diff --git a/CMakeLists.txt b/CMakeLists.txt
index 92dfd42a..380eddbc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,6 +62,8 @@ configure_file(include/reactor-cpp/config.hh.in include/reactor-cpp/config.hh @O
 include(GNUInstallDirs)
 
 add_subdirectory(lib)
+add_subdirectory(reactor-sdk)
+
 if(NOT DEFINED LF_REACTOR_CPP_SUFFIX)
   add_subdirectory(examples)
 endif()
@@ -71,3 +73,13 @@ if (DEFINED LF_REACTOR_CPP_SUFFIX)
 else()
   install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
 endif()
+
+if(NOT TARGET uninstall)
+  configure_file(
+    "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
+    "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+    IMMEDIATE @ONLY)
+
+  add_custom_target(uninstall
+    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
+endif()
\ No newline at end of file
diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
new file mode 100644
index 00000000..c2d34d47
--- /dev/null
+++ b/cmake_uninstall.cmake.in
@@ -0,0 +1,21 @@
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif()
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif()
+  else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif()
+endforeach()
diff --git a/examples/sdk-SrcSink-Fanout/.gitignore b/examples/sdk-SrcSink-Fanout/.gitignore
new file mode 100644
index 00000000..c809c12d
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/.gitignore
@@ -0,0 +1 @@
+*build
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/CMakeLists.txt b/examples/sdk-SrcSink-Fanout/CMakeLists.txt
new file mode 100644
index 00000000..39213d0c
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.9)
+project(src_sink_fanout VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET src_sink_fanout)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
+
+include(Sink/SinkReactor.cmake)
+include(Source/SourceReactor.cmake)
+include(Main/MainReactor.cmake)
+include(Config-a/Config-a.cmake)
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cc b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cc
new file mode 100644
index 00000000..48e621d0
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cc
@@ -0,0 +1,17 @@
+#include "Config-a.hh"
+
+UserParameters cfg_parameters;
+
+ConfigParameter<int, uint32_t>::ParametersMap UserParameters::homogeneous_config() {
+    return {
+            {"Main.Source.iterations", ConfigParameterMetadata<int> { 5 } }
+    };
+}
+
+ConfigParameter<int, uint32_t>::ParametersMap UserParameters::heterogeneous_config() {
+    return {
+            {"Main.Source.iterations", ConfigParameterMetadata<int> { 20 } },
+            {"Main.Sink.n_ports", ConfigParameterMetadata<int> { 2 } }
+
+    };
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cmake b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cmake
new file mode 100644
index 00000000..bd7049e4
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Config-a/Config-a.hh b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.hh
new file mode 100644
index 00000000..7d57d334
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Config-a/Config-a.hh
@@ -0,0 +1,18 @@
+#ifndef USER_PARAMETERS_H
+#define USER_PARAMETERS_H
+
+#include <reactor-sdk/reactor-sdk.hh>
+#include <map>
+#include <variant>
+#include <string>
+
+using namespace sdk;
+
+struct UserParameters : public ConfigParameter<int, uint32_t> {
+    ConfigParameter<int, uint32_t>::ParametersMap homogeneous_config();
+    ConfigParameter<int, uint32_t>::ParametersMap heterogeneous_config();
+};
+
+extern UserParameters cfg_parameters;
+
+#endif // USER_PARAMETERS_H
diff --git a/examples/sdk-SrcSink-Fanout/Main/MainReactor.cc b/examples/sdk-SrcSink-Fanout/Main/MainReactor.cc
new file mode 100644
index 00000000..fc841b8c
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Main/MainReactor.cc
@@ -0,0 +1,29 @@
+#include "MainReactor.hh"
+
+void MainReactor::construction() {
+
+    cout << "Construction Main\n";
+
+    src = std::make_unique<SourceReactor>("Source", this);
+    snk = std::make_unique<SinkReactor>("Sink", this);
+}
+
+void MainReactor::wiring() {
+    cout << "Wiring Main\n";
+
+    src->req -->> snk->req;
+    snk->rsp --> src->rsp;
+}
+
+void REACTION_SCOPE(MainReactor)::add_reactions(MainReactor *reactor) {
+    reaction("reaction_1").
+        triggers(&reactor->startup).
+        dependencies().
+        effects().
+        function(
+            [this](Startup& startup) {
+                cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                "Starting up reaction\n" << "Bank:" << bank_index << " name:" << parameters.alias.value << " fqn:" << fqn() << endl;
+            }
+    );
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Main/MainReactor.cmake b/examples/sdk-SrcSink-Fanout/Main/MainReactor.cmake
new file mode 100644
index 00000000..4c6cc870
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Main/MainReactor.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Main/MainReactor.hh b/examples/sdk-SrcSink-Fanout/Main/MainReactor.hh
new file mode 100644
index 00000000..a945643b
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Main/MainReactor.hh
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Source/SourceReactor.hh"
+#include "Sink/SinkReactor.hh"
+
+using namespace sdk;
+
+class MainReactor: public Reactor {
+public:
+    struct Parameters {
+        string alias = "Src-Sink-Fanout-Example";
+    };
+private:
+    struct PublishParameters : public SystemParameters<Parameters, string> {
+        REACTOR_PARAMETER(string, alias, "Alternate name", "another", "another", defaults.alias);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, string>(container, std::forward<Parameters>(param)) {
+            register_parameters (alias);
+        }
+    };
+    PublishParameters parameters;
+
+    REACTION_SCOPE_START(MainReactor, PublishParameters)
+        void add_reactions(MainReactor *reactor);
+    REACTION_SCOPE_END(this, parameters)
+
+    std::unique_ptr<SourceReactor> src;
+    std::unique_ptr<SinkReactor> snk;
+
+public:
+    MainReactor(const std::string &name, Environment *env)
+    : Reactor(name, env), parameters{this, Parameters{}} {}
+    MainReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container), parameters{this, Parameters{}} {}
+
+    MainReactor(const std::string &name, Environment *env, Parameters && param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    MainReactor(const std::string &name, Reactor *container, Parameters && param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+  
+    void construction() override;
+    void wiring() override;
+};
+        
diff --git a/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cc b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cc
new file mode 100644
index 00000000..b4e8e125
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cc
@@ -0,0 +1,45 @@
+#include "SinkReactor.hh"
+using namespace std;
+    
+void SinkReactor::construction() {
+    cout << "Construction Sink n_ports:" << parameters.n_ports.value << "\n";
+    req.set_width (parameters.n_ports.value);
+    rsp.set_width (parameters.n_ports.value);
+}
+
+void SinkReactor::wiring() {
+    cout << "Wiring Sink\n";
+}
+
+void REACTION_SCOPE(SinkReactor)::add_reactions (SinkReactor *reactor) {
+    reaction("startup_reaction").
+        triggers(&reactor->startup).
+        dependencies().
+        effects().
+        function(pass_function(startup_reaction)
+    );
+
+    reaction("process_request").
+        triggers(&reactor->req).
+        dependencies().
+        effects(&reactor->rsp).
+        function(pass_function(process_request)
+    );
+}
+
+
+
+void REACTION_SCOPE(SinkReactor)::startup_reaction (Startup& startup) {
+    cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+    "Starting up reaction\n" << "Bank:" << bank_index << " name:" << parameters.name.value << " fqn:" << fqn() << endl;
+}
+
+void REACTION_SCOPE(SinkReactor)::process_request (MultiportInput<int>& req, MultiportOutput<int>& rsp) {
+    for (int i = 0; i < parameters.n_ports.value; ++i) {
+        if (req[i].is_present()) {
+            cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+            "Received input:" << *req[i].get() << " port:" << i << endl;
+            rsp[i].set (*req[i].get());
+        }
+    }
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cmake b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cmake
new file mode 100644
index 00000000..8402349d
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.cmake
@@ -0,0 +1,14 @@
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SinkReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SinkReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.hh b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.hh
new file mode 100644
index 00000000..63cb7ddc
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Sink/SinkReactor.hh
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+using namespace std;
+using namespace sdk;
+
+class SinkReactor : public Reactor {
+public:
+
+    struct Parameters {
+        string name = "Sink";
+        int n_ports = 1;
+    };
+private:
+    struct PublishParameters : public SystemParameters<Parameters, string, int> {
+        REACTOR_PARAMETER(string, name, "Alternate name", "Sink", "Sink", defaults.name);
+        REACTOR_PARAMETER (int, n_ports, "Size of multiports", 1, 10, defaults.n_ports);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, string, int>(container, std::forward<Parameters>(param)) {
+            register_parameters (name, n_ports);
+        }
+    };
+    PublishParameters parameters;
+
+    REACTION_SCOPE_START(SinkReactor, PublishParameters)
+        void add_reactions(SinkReactor *reactor);
+        
+        void startup_reaction (Startup &startup);
+        void process_request (MultiportInput<int>& req, MultiportOutput<int>& rsp);
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    SinkReactor(const std::string &name, Environment *env)
+    : Reactor(name, env), parameters{this, Parameters{}} {}
+    SinkReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container), parameters{this, Parameters{}} {}
+
+    SinkReactor(const std::string &name, Environment *env, Parameters && param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    SinkReactor(const std::string &name, Reactor *container, Parameters && param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+
+    MultiportInput<int> req{"req", this};
+    MultiportOutput<int> rsp{"rsp", this};
+    
+    void construction() override;
+    void wiring() override;
+};
diff --git a/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cc b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cc
new file mode 100644
index 00000000..b4ca3dba
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cc
@@ -0,0 +1,61 @@
+
+#include "SourceReactor.hh"
+using namespace std;
+
+void SourceReactor::construction() {
+
+    cout << "Construction Source iterations:" << parameters.iterations.value << "\n";
+}
+
+void SourceReactor::wiring() {
+    cout << "Wiring Source iterations:" << parameters.iterations.value << "\n";
+}
+
+void REACTION_SCOPE(SourceReactor)::add_reactions(SourceReactor *reactor) {
+    reaction("reaction_1").
+        triggers(&reactor->startup).
+        dependencies().
+        effects(&reactor->sch).
+        function(
+            [this](Startup& startup, LogicalAction<int>& sched) {
+                cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                "Starting up reaction\n" << "Bank:" << bank_index << " name:" << name << " fqn:" << fqn() << " iterations:" << parameters.iterations.value << endl;
+                if (itr < parameters.iterations.value) {
+                    sched.schedule (itr, 0ms);
+                    ++itr;
+                }
+            }
+        );
+
+    reaction("reaction_2").
+        triggers(&reactor->sch).
+        dependencies().
+        effects(&reactor->req).
+        function(
+            [this](LogicalAction<int>& sch, Output<int>& req) {
+                cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                "Scheduling iteration:" << *sch.get() << endl;
+                req.set (*sch.get());
+            }
+        );
+
+    reaction("reaction_3").
+        triggers(&reactor->rsp).
+        dependencies().
+        effects(&reactor->sch).
+        function(
+            [this](Input<int>& rsp, LogicalAction<int>& sch) {
+                if (rsp.is_present()) {
+                    cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                    "Recevied response:" << *rsp.get() << endl;
+                }
+                
+                if (itr < parameters.iterations.value) {
+                    sch.schedule (itr, 0ms);
+                    ++itr;
+                } else {
+                    request_stop();
+                }
+            }
+        );
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cmake b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cmake
new file mode 100644
index 00000000..a6071900
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.cmake
@@ -0,0 +1,13 @@
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SourceReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SourceReactor.cc"
+)
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/Source/SourceReactor.hh b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.hh
new file mode 100644
index 00000000..248f77ec
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/Source/SourceReactor.hh
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+using namespace std;
+using namespace sdk;
+
+class SourceReactor : public Reactor {
+public:
+    struct Parameters : public SystemParametersStandalone<int> {
+        REACTOR_PARAMETER(int, iterations, "Number of iterations", 1, 100, 10);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<int>(container) {
+            register_parameters (iterations);
+        }
+    };
+private:
+    LogicalAction<int> sch{"sch", this};
+    
+    Parameters parameters{this};
+
+    REACTION_SCOPE_START(SourceReactor, Parameters)
+        std::string name = "Source";
+        int itr = 0;
+
+        void add_reactions(SourceReactor *reactor);
+    REACTION_SCOPE_END(this, parameters)
+
+public:                                                         
+    SourceReactor(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    SourceReactor(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Input<int> rsp{"rsp", this};
+    Output<int> req{"req", this};
+
+    void construction() override;
+    void wiring() override;
+};
\ No newline at end of file
diff --git a/examples/sdk-SrcSink-Fanout/example.png b/examples/sdk-SrcSink-Fanout/example.png
new file mode 100644
index 00000000..ba059554
Binary files /dev/null and b/examples/sdk-SrcSink-Fanout/example.png differ
diff --git a/examples/sdk-SrcSink-Fanout/graph.dot b/examples/sdk-SrcSink-Fanout/graph.dot
new file mode 100644
index 00000000..03cc57c6
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/graph.dot
@@ -0,0 +1,44 @@
+digraph {
+rankdir=LR;
+subgraph {
+rank=same;
+Main_reaction_1 [label="Main.reaction_1"];
+Main_Source_reaction_1 [label="Main.Source.reaction_1"];
+Main_Sink_0_startup_reaction [label="Main.Sink_0.startup_reaction"];
+Main_Sink_1_startup_reaction [label="Main.Sink_1.startup_reaction"];
+Main_Sink_2_startup_reaction [label="Main.Sink_2.startup_reaction"];
+Main_Sink_3_startup_reaction [label="Main.Sink_3.startup_reaction"];
+}
+subgraph {
+rank=same;
+Main_Source_reaction_2 [label="Main.Source.reaction_2"];
+}
+subgraph {
+rank=same;
+Main_Sink_0_process_request [label="Main.Sink_0.process_request"];
+Main_Sink_1_process_request [label="Main.Sink_1.process_request"];
+Main_Sink_2_process_request [label="Main.Sink_2.process_request"];
+Main_Sink_3_process_request [label="Main.Sink_3.process_request"];
+}
+subgraph {
+rank=same;
+Main_Source_reaction_3 [label="Main.Source.reaction_3"];
+}
+Main_reaction_1 -> Main_Source_reaction_2 [style=invis];
+Main_Source_reaction_2 -> Main_Sink_0_process_request [style=invis];
+Main_Sink_0_process_request -> Main_Source_reaction_3 [style=invis];
+Main_Source_reaction_3 -> Main_Sink_0_process_request
+Main_Source_reaction_3 -> Main_Sink_1_process_request
+Main_Source_reaction_3 -> Main_Sink_2_process_request
+Main_Source_reaction_3 -> Main_Sink_3_process_request
+Main_Source_reaction_2 -> Main_Source_reaction_1
+Main_Source_reaction_3 -> Main_Source_reaction_2
+Main_Sink_0_process_request -> Main_Source_reaction_2
+Main_Sink_0_process_request -> Main_Sink_0_startup_reaction
+Main_Sink_1_process_request -> Main_Source_reaction_2
+Main_Sink_1_process_request -> Main_Sink_1_startup_reaction
+Main_Sink_2_process_request -> Main_Source_reaction_2
+Main_Sink_2_process_request -> Main_Sink_2_startup_reaction
+Main_Sink_3_process_request -> Main_Source_reaction_2
+Main_Sink_3_process_request -> Main_Sink_3_startup_reaction
+}
diff --git a/examples/sdk-SrcSink-Fanout/main.cc b/examples/sdk-SrcSink-Fanout/main.cc
new file mode 100644
index 00000000..2ed281e9
--- /dev/null
+++ b/examples/sdk-SrcSink-Fanout/main.cc
@@ -0,0 +1,51 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Config-a/Config-a.hh"
+#include "Main/MainReactor.hh"
+
+using namespace std;
+using namespace sdk;
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("sdk-SrcSink-Fanout", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {&cfg_parameters, workers, fast, timeout, cfg_gen};
+    auto main = new MainReactor("Main", &sim);
+
+    sim.run();
+    return 0;
+}
diff --git a/examples/sdk-SrcSink/.gitignore b/examples/sdk-SrcSink/.gitignore
new file mode 100644
index 00000000..c809c12d
--- /dev/null
+++ b/examples/sdk-SrcSink/.gitignore
@@ -0,0 +1 @@
+*build
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/CMakeLists.txt b/examples/sdk-SrcSink/CMakeLists.txt
new file mode 100644
index 00000000..71c3b16d
--- /dev/null
+++ b/examples/sdk-SrcSink/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.9)
+project(src_sink VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET src_sink)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
+
+include(Sink/SinkReactor.cmake)
+include(Source/SourceReactor.cmake)
+include(Main/MainReactor.cmake)
+include(Config-a/Config-a.cmake)
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Config-a/Config-a.cc b/examples/sdk-SrcSink/Config-a/Config-a.cc
new file mode 100644
index 00000000..77b906d0
--- /dev/null
+++ b/examples/sdk-SrcSink/Config-a/Config-a.cc
@@ -0,0 +1,23 @@
+#include "Config-a.hh"
+
+UserParameters cfg_parameters;
+
+ConfigParameter<int, uint32_t, string>::ParametersMap UserParameters::homogeneous_config() {
+    return {
+            {"Main.Source.iterations", ConfigParameterMetadata<int> { 5 } },
+            // {"Main.Sink.name", ConfigParameterMetadata<string> { "Homog Name" } },
+    };
+}
+
+ConfigParameter<int, uint32_t, string>::ParametersMap UserParameters::heterogeneous_config() {
+    return {
+            {"Main.Source.iterations", ConfigParameterMetadata<int> { 20 } },
+            {"Main.Source.n_ports", ConfigParameterMetadata<int> { 4 } },
+            {"Main.n_sinks", ConfigParameterMetadata<int> { 4 } },
+            // {"Main.Sink_0.name", ConfigParameterMetadata<string> { "Hetero Name 0" } },
+            // {"Main.Sink_1.name", ConfigParameterMetadata<string> { "Hetero Name 1" } },
+            // {"Main.Sink_2.name", ConfigParameterMetadata<string> { "Hetero Name 2" } },
+            // {"Main.Sink_3.name", ConfigParameterMetadata<string> { "Hetero Name 3" } },
+
+    };
+}
diff --git a/examples/sdk-SrcSink/Config-a/Config-a.cmake b/examples/sdk-SrcSink/Config-a/Config-a.cmake
new file mode 100644
index 00000000..bd7049e4
--- /dev/null
+++ b/examples/sdk-SrcSink/Config-a/Config-a.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Config-a/Config-a.hh b/examples/sdk-SrcSink/Config-a/Config-a.hh
new file mode 100644
index 00000000..129beb99
--- /dev/null
+++ b/examples/sdk-SrcSink/Config-a/Config-a.hh
@@ -0,0 +1,18 @@
+#ifndef USER_PARAMETERS_H
+#define USER_PARAMETERS_H
+
+#include <reactor-sdk/reactor-sdk.hh>
+#include <map>
+#include <variant>
+#include <string>
+
+using namespace sdk;
+
+struct UserParameters : public ConfigParameter<int, uint32_t, string> {
+    ConfigParameter<int, uint32_t, string>::ParametersMap homogeneous_config();
+    ConfigParameter<int, uint32_t, string>::ParametersMap heterogeneous_config();
+};
+
+extern UserParameters cfg_parameters;
+
+#endif // USER_PARAMETERS_H
diff --git a/examples/sdk-SrcSink/Main/MainReactor.cc b/examples/sdk-SrcSink/Main/MainReactor.cc
new file mode 100644
index 00000000..ffd48410
--- /dev/null
+++ b/examples/sdk-SrcSink/Main/MainReactor.cc
@@ -0,0 +1,41 @@
+#include "MainReactor.hh"
+
+void MainReactor::construction() {
+
+    cout << "Construction Main n_sinks:" << parameters.n_sinks.value << " default n_sinks:" << parameters.defaults.n_sinks << "\n";
+
+    src = std::make_unique<SourceReactor>("Source", this);
+
+    for (int i = 0; i < parameters.n_sinks.value; i++) {
+        snk.create_reactor(SinkReactor::Parameters{.name = "Default Sink Name"});
+    }
+}
+
+void MainReactor::wiring() {
+    cout << "Wiring Main n_sinks:" << parameters.n_sinks.value << " default n_sinks:" << parameters.defaults.n_sinks << "\n";
+
+    src->req --> snk.for_each(select_default(snk).req);
+    // src->req --> snk.for_each(&SinkReactor::req);         // alternative
+    // src->req --> snk.for_each(&snk[0].req);              // alternative
+    // src->req --> snk->*(select_default(snk).req);        // alternative
+    // src->req --> snk->*(&SinkReactor::req);              //  alternative
+
+    snk.for_each(select_default(snk).rsp) --> src->rsp;
+    // snk.for_each(&SinkReactor::rsp) --> src->rsp;        // alternative
+    // snk.for_each(&snk[0].rsp) --> src->rsp;              // alternative
+    // (snk->*(select_default(snk).rsp)) --> src->rsp;      // alternative
+    // (snk->*(&SinkReactor::rsp)) --> src->rsp;            // alternative
+}
+
+void REACTION_SCOPE(MainReactor)::add_reactions(MainReactor *reactor) {
+    reaction("reaction_1").
+        triggers(&reactor->startup).
+        dependencies().
+        effects().
+        function(
+            [this](Startup& startup) {
+                cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                "Starting up reaction\n" << "Bank:" << bank_index << " name:" << parameters.alias.value << " fqn:" << fqn() << " n_sinks:" << parameters.n_sinks.value << endl;
+            }
+    );
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Main/MainReactor.cmake b/examples/sdk-SrcSink/Main/MainReactor.cmake
new file mode 100644
index 00000000..4c6cc870
--- /dev/null
+++ b/examples/sdk-SrcSink/Main/MainReactor.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Main/MainReactor.hh b/examples/sdk-SrcSink/Main/MainReactor.hh
new file mode 100644
index 00000000..ef5d875f
--- /dev/null
+++ b/examples/sdk-SrcSink/Main/MainReactor.hh
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Source/SourceReactor.hh"
+#include "Sink/SinkReactor.hh"
+
+using namespace sdk;
+
+class MainReactor: public Reactor {
+public:
+    struct Parameters {
+        string alias = "Src-Sink-Example";
+        int n_sinks = 2;
+    };
+private:
+    struct PublishParameters : public SystemParameters<Parameters, string, int> {
+        REACTOR_PARAMETER(string, alias, "Alternate name", "another", "another", defaults.alias);
+        REACTOR_PARAMETER(int, n_sinks, "Sink reactors bank width", 1, 10, defaults.n_sinks);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, string, int>(container, std::forward<Parameters>(param)) {
+            register_parameters (alias, n_sinks);
+        }
+    };
+    PublishParameters parameters;
+
+    REACTION_SCOPE_START(MainReactor, PublishParameters)
+        void add_reactions(MainReactor *reactor);
+    REACTION_SCOPE_END(this, parameters)
+
+    std::unique_ptr<SourceReactor> src;
+    ReactorBank<SinkReactor> snk{"Sink", this};
+
+public:
+    MainReactor(const std::string &name, Environment *env)
+    : Reactor(name, env), parameters{this, Parameters{}} {}
+    MainReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container), parameters{this, Parameters{}} {}
+
+    MainReactor(const std::string &name, Environment *env, Parameters && param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    MainReactor(const std::string &name, Reactor *container, Parameters && param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+  
+    void construction() override;
+    void wiring() override;
+};
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Sink/SinkReactor.cc b/examples/sdk-SrcSink/Sink/SinkReactor.cc
new file mode 100644
index 00000000..74a8c69b
--- /dev/null
+++ b/examples/sdk-SrcSink/Sink/SinkReactor.cc
@@ -0,0 +1,40 @@
+#include "SinkReactor.hh"
+using namespace std;
+    
+void SinkReactor::construction() {
+    cout << "Construction Sink\n";
+}
+
+void SinkReactor::wiring() {
+
+    cout << "Wiring Sink\n";
+}
+
+void REACTION_SCOPE(SinkReactor)::add_reactions(SinkReactor *reactor) {
+    reaction("startup_reaction").
+        triggers(&reactor->startup).
+        dependencies().
+        effects().
+        function(pass_function(startup_reaction)
+    );
+
+    reaction("process_request").
+        triggers(&reactor->req).
+        dependencies().
+        effects(&reactor->rsp).
+        function(pass_function(process_request)
+    );
+}
+
+
+
+void REACTION_SCOPE(SinkReactor)::startup_reaction (Startup& startup) {
+    cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+    "Starting up reaction\n" << "Bank:" << bank_index << " name:" << parameters.name.value << " fqn:" << fqn() << endl;
+}
+
+void REACTION_SCOPE(SinkReactor)::process_request (Input<int>& req, Output<int>& rsp) {
+    cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+    "Received input:" << *req.get() << " bank:" << bank_index << endl;
+    rsp.set (*req.get());
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Sink/SinkReactor.cmake b/examples/sdk-SrcSink/Sink/SinkReactor.cmake
new file mode 100644
index 00000000..8402349d
--- /dev/null
+++ b/examples/sdk-SrcSink/Sink/SinkReactor.cmake
@@ -0,0 +1,14 @@
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SinkReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SinkReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Sink/SinkReactor.hh b/examples/sdk-SrcSink/Sink/SinkReactor.hh
new file mode 100644
index 00000000..db709382
--- /dev/null
+++ b/examples/sdk-SrcSink/Sink/SinkReactor.hh
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+using namespace std;
+using namespace sdk;
+
+class SinkReactor : public Reactor {
+public:
+
+    struct Parameters {
+        string name = "Sink";
+    };
+private:
+    struct PublishParameters : public SystemParameters<Parameters, string> {
+        REACTOR_PARAMETER(string, name, "Alternate name", "Sink", "Sink", defaults.name);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, string>(container, std::forward<Parameters>(param)) {
+            register_parameters (name);
+        }
+    };
+    PublishParameters parameters;
+
+    REACTION_SCOPE_START(SinkReactor, PublishParameters)
+        void add_reactions(SinkReactor *reactor);
+        
+        void startup_reaction (Startup &startup);
+        void process_request (Input<int>& req, Output<int>& rsp);
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    SinkReactor(const std::string &name, Environment *env)
+    : Reactor(name, env), parameters{this, Parameters{}} {}
+    SinkReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container), parameters{this, Parameters{}} {}
+
+    SinkReactor(const std::string &name, Environment *env, Parameters && param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    SinkReactor(const std::string &name, Reactor *container, Parameters && param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+
+    Input<int> req{"req", this};
+    Output<int> rsp{"rsp", this};
+    
+    void construction() override;
+    void wiring() override;
+};
diff --git a/examples/sdk-SrcSink/Source/SourceReactor.cc b/examples/sdk-SrcSink/Source/SourceReactor.cc
new file mode 100644
index 00000000..25042cc3
--- /dev/null
+++ b/examples/sdk-SrcSink/Source/SourceReactor.cc
@@ -0,0 +1,75 @@
+
+#include "SourceReactor.hh"
+using namespace std;
+
+void SourceReactor::construction() {
+
+    cout << "Construction Source n_ports:" << parameters.n_ports.value << "\n";
+
+    req.set_width (parameters.n_ports.value);
+    rsp.set_width (parameters.n_ports.value);
+}
+
+void SourceReactor::wiring() {
+    cout << "Wiring Source n_ports:" << parameters.n_ports.value << "\n";
+}
+
+void REACTION_SCOPE(SourceReactor)::add_reactions(SourceReactor *reactor) {
+    reaction("reaction_1").
+        triggers(&reactor->startup).
+        dependencies().
+        effects(&reactor->sch).
+        function(
+            [this](Startup& startup, LogicalAction<int>& sched) {
+                cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                "Starting up reaction\n" << "Bank:" << bank_index << " name:" << name << " fqn:" << fqn() << " iterations:" << parameters.iterations.value << endl;
+                if (itr < parameters.iterations.value) {
+                    sched.schedule (itr, 0ms);
+                    ++itr;
+                }
+            }
+        );
+
+    reaction("reaction_2").
+        triggers(&reactor->sch).
+        dependencies().
+        effects(&reactor->req).
+        function(
+            [this](LogicalAction<int>& sch, MultiportOutput<int>& req) {
+                for (int i = 0; i < parameters.n_ports.value; ++i) {
+                    cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                    "Scheduling iteration:" << *sch.get() << " out_port:" << i << endl;
+                    req[i].set (*sch.get());
+                }
+            }
+        );
+
+    reaction("reaction_3").
+        triggers(&reactor->rsp).
+        dependencies().
+        effects(&reactor->sch).
+        function(
+                [this](MultiportInput<int>& rsp, LogicalAction<int>& sch) {
+                    for (int i = 0; i < parameters.n_ports.value; ++i) {
+                        if (rsp[i].is_present()) {
+                            cout << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " <<
+                            "Recevied response:" << *rsp[i].get() << " in_port:" << i << endl;
+                            ++rsp_itr;
+                        }
+                    }
+
+                    if (rsp_itr < parameters.n_ports.value) {
+                        return;
+                    }
+
+                    rsp_itr = 0;
+                    
+                    if (itr < parameters.iterations.value) {
+                        sch.schedule (itr, 0ms);
+                        ++itr;
+                    } else {
+                        request_stop();
+                    }
+                }
+        );
+}
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Source/SourceReactor.cmake b/examples/sdk-SrcSink/Source/SourceReactor.cmake
new file mode 100644
index 00000000..a6071900
--- /dev/null
+++ b/examples/sdk-SrcSink/Source/SourceReactor.cmake
@@ -0,0 +1,13 @@
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SourceReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/SourceReactor.cc"
+)
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/Source/SourceReactor.hh b/examples/sdk-SrcSink/Source/SourceReactor.hh
new file mode 100644
index 00000000..9a7a3b53
--- /dev/null
+++ b/examples/sdk-SrcSink/Source/SourceReactor.hh
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+using namespace std;
+using namespace sdk;
+
+class SourceReactor : public Reactor {
+public:
+    struct Parameters : public SystemParametersStandalone<int> {
+        REACTOR_PARAMETER(int, iterations, "Number of iterations", 1, 100, 10);
+        REACTOR_PARAMETER(int, n_ports, "Size of multiports", 1, 10, 1);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<int>(container) {
+            register_parameters (iterations, n_ports);
+        }
+    };
+private:
+    LogicalAction<int> sch{"sch", this};
+    
+    Parameters parameters{this};
+
+    REACTION_SCOPE_START(SourceReactor, Parameters)
+        std::string name = "Source";
+        int itr = 0;
+        int rsp_itr = 0;
+
+        void add_reactions(SourceReactor *reactor);
+    REACTION_SCOPE_END(this, parameters)
+
+public:                                                         
+    SourceReactor(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    SourceReactor(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    MultiportInput<int> rsp{"rsp", this};
+    MultiportOutput<int> req{"req", this};
+
+    void construction() override;
+    void wiring() override;
+};
\ No newline at end of file
diff --git a/examples/sdk-SrcSink/example.png b/examples/sdk-SrcSink/example.png
new file mode 100644
index 00000000..ba059554
Binary files /dev/null and b/examples/sdk-SrcSink/example.png differ
diff --git a/examples/sdk-SrcSink/graph.dot b/examples/sdk-SrcSink/graph.dot
new file mode 100644
index 00000000..03cc57c6
--- /dev/null
+++ b/examples/sdk-SrcSink/graph.dot
@@ -0,0 +1,44 @@
+digraph {
+rankdir=LR;
+subgraph {
+rank=same;
+Main_reaction_1 [label="Main.reaction_1"];
+Main_Source_reaction_1 [label="Main.Source.reaction_1"];
+Main_Sink_0_startup_reaction [label="Main.Sink_0.startup_reaction"];
+Main_Sink_1_startup_reaction [label="Main.Sink_1.startup_reaction"];
+Main_Sink_2_startup_reaction [label="Main.Sink_2.startup_reaction"];
+Main_Sink_3_startup_reaction [label="Main.Sink_3.startup_reaction"];
+}
+subgraph {
+rank=same;
+Main_Source_reaction_2 [label="Main.Source.reaction_2"];
+}
+subgraph {
+rank=same;
+Main_Sink_0_process_request [label="Main.Sink_0.process_request"];
+Main_Sink_1_process_request [label="Main.Sink_1.process_request"];
+Main_Sink_2_process_request [label="Main.Sink_2.process_request"];
+Main_Sink_3_process_request [label="Main.Sink_3.process_request"];
+}
+subgraph {
+rank=same;
+Main_Source_reaction_3 [label="Main.Source.reaction_3"];
+}
+Main_reaction_1 -> Main_Source_reaction_2 [style=invis];
+Main_Source_reaction_2 -> Main_Sink_0_process_request [style=invis];
+Main_Sink_0_process_request -> Main_Source_reaction_3 [style=invis];
+Main_Source_reaction_3 -> Main_Sink_0_process_request
+Main_Source_reaction_3 -> Main_Sink_1_process_request
+Main_Source_reaction_3 -> Main_Sink_2_process_request
+Main_Source_reaction_3 -> Main_Sink_3_process_request
+Main_Source_reaction_2 -> Main_Source_reaction_1
+Main_Source_reaction_3 -> Main_Source_reaction_2
+Main_Sink_0_process_request -> Main_Source_reaction_2
+Main_Sink_0_process_request -> Main_Sink_0_startup_reaction
+Main_Sink_1_process_request -> Main_Source_reaction_2
+Main_Sink_1_process_request -> Main_Sink_1_startup_reaction
+Main_Sink_2_process_request -> Main_Source_reaction_2
+Main_Sink_2_process_request -> Main_Sink_2_startup_reaction
+Main_Sink_3_process_request -> Main_Source_reaction_2
+Main_Sink_3_process_request -> Main_Sink_3_startup_reaction
+}
diff --git a/examples/sdk-SrcSink/main.cc b/examples/sdk-SrcSink/main.cc
new file mode 100644
index 00000000..ecf28acf
--- /dev/null
+++ b/examples/sdk-SrcSink/main.cc
@@ -0,0 +1,51 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Config-a/Config-a.hh"
+#include "Main/MainReactor.hh"
+
+using namespace std;
+using namespace sdk;
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("sdk-SrcSink", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {&cfg_parameters, workers, fast, timeout, cfg_gen};
+    auto main = new MainReactor("Main", &sim, MainReactor::Parameters{.alias = "Test Param", .n_sinks = 3});
+
+    sim.run();
+    return 0;
+}
diff --git a/examples/sdk-Workers/CMakeLists.txt b/examples/sdk-Workers/CMakeLists.txt
new file mode 100644
index 00000000..65b13793
--- /dev/null
+++ b/examples/sdk-Workers/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(workers VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET workers)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/examples/sdk-Workers/main.cc b/examples/sdk-Workers/main.cc
new file mode 100644
index 00000000..07b080ff
--- /dev/null
+++ b/examples/sdk-Workers/main.cc
@@ -0,0 +1,463 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace sdk;
+
+class Relay : public Reactor {
+public:
+    struct Parameters {
+        int n_outputs;
+    };
+
+private:
+    Parameters parameters;
+    const int &n_outputs = parameters.n_outputs;
+
+    REACTION_SCOPE_START(Relay, Parameters)
+        // parameters
+        const int &n_outputs = parameters.n_outputs;
+
+        // state variables
+        int index = 0;
+        int *busy = 0;
+
+        void add_reactions(Relay *reactor) {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Startup\n";
+                        busy = (int*) calloc (n_outputs, sizeof(int));
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->in_req).
+                dependencies().
+                effects(&reactor->all_workers_busy, &reactor->out_req).
+                function(
+                    [this](Input<int> &in_req, Output<bool> &all_workers_busy, MultiportOutput<int> &out_req) {
+                        for (int i = 0; i < n_outputs; ++i, index = (index + 1) % n_outputs) {
+                            if (busy[index] == 0) {
+                                out_req[index].set(*in_req.get());
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Sending task_id:" << *in_req.get() << " to worker:" << index << std::endl;
+                                busy[index] = 1;
+                                index = (index + 1) % n_outputs;
+                                break;
+                            }
+                        }
+                        int busy_count = 0;
+                        for (int i = 0; i < n_outputs; ++i) {
+                            busy_count = busy[i] ? (busy_count + 1) : busy_count;
+                        }
+
+                        if (busy_count == n_outputs) {
+                            all_workers_busy.set(true);
+                        }
+                    }
+                );
+
+            reaction("reaction_3").
+                triggers(&reactor->in_rsp).
+                dependencies().
+                effects(&reactor->out_rsp).
+                function(
+                    [this](MultiportInput<int> &in_rsp, Output<int> &out_rsp) {
+                        for (int i = 0; i < n_outputs; ++i) {
+                            if (in_rsp[i].is_present()) {
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Receiving task_id:" << *in_rsp[i].get() << " from worker:" << i << std::endl;
+                                busy[i] = 0;
+                                out_rsp.set(*in_rsp[i].get());
+                            }
+                        }
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+
+public:
+    Input<int> in_req{"in_req", this};
+    Output<int> out_rsp{"out_rsp", this};
+    MultiportOutput<int> out_req{"out_req", this};
+    MultiportInput<int> in_rsp{"in_rsp", this};
+
+    Output<bool> all_workers_busy{"all_workers_busy", this};
+
+    Relay(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Relay(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    void construction() override {
+        out_req.set_width(n_outputs);
+        in_rsp.set_width(n_outputs);
+    }
+
+    void wiring() override {}
+};
+
+class Worker : public Reactor {
+public:
+    struct Parameters {
+        Duration processing_delay = 2s;
+    };
+
+private:
+    LogicalAction<int> sch_rsp{"sch_rsp", this};
+
+    Parameters parameters;
+    const Duration &processing_delay = parameters.processing_delay;
+
+    REACTION_SCOPE_START(Worker, Parameters)
+        const Duration &processing_delay = parameters.processing_delay;
+
+        void add_reactions(Worker *reactor) {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Startup\n";
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->req).
+                dependencies().
+                effects(&reactor->sch_rsp).
+                function(
+                    [this](Input<int> &req, LogicalAction<int> &sch_rsp) {
+                        auto req_ref = *req.get();
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Receiving task_id:" << req_ref << std::endl;
+                        sch_rsp.schedule (req_ref, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(processing_delay)));
+                    }
+                );
+
+            reaction("reaction_3").
+                triggers(&reactor->sch_rsp).
+                dependencies().
+                effects(&reactor->rsp).
+                function(
+                    [this](LogicalAction<int> &sch_rsp, Output<int> &rsp) {
+                        auto req_ref = *sch_rsp.get();
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Sending task_id:" << req_ref << std::endl;
+                        rsp.set(req_ref);
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    Worker(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Worker(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    Input<int> req{"req", this};
+    Output<int> rsp{"rsp", this};
+
+    void construction() override {}
+    void wiring() override {}
+};
+
+
+class Pool : public Reactor {
+public:
+    struct Parameters {
+        int n_workers = 1;
+    };
+
+private:
+    Parameters parameters;
+    const int &n_workers = parameters.n_workers;
+
+    LogicalAction<int> sch_rsp{"sch_rsp", this};
+
+    ReactorBank<Worker> workers{"workers", this};
+    std::unique_ptr<Relay> relay;
+
+    REACTION_SCOPE_START(Pool, Parameters)
+        const int &n_workers = parameters.n_workers;
+
+        void add_reactions(Pool *reactor) {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Startup\n";
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    Pool(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Pool(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    Input<int> req{"req", this};
+    Output<int> rsp{"rsp", this};
+
+    Output<bool> all_workers_busy{"all_workers_busy", this};
+
+    void construction() override {
+        for (int i = 0; i < n_workers; ++i) {
+            workers.create_reactor(Worker::Parameters{.processing_delay = 1s});
+        }
+        relay = std::make_unique<Relay>("relay", this, Relay::Parameters{.n_outputs = n_workers});
+    }
+
+    void wiring() override {
+        req --> relay->in_req;
+        relay->out_req --> workers.for_each(select_default(workers).req);
+        workers.for_each(select_default(workers).rsp) --> relay->in_rsp;
+        relay->out_rsp --> rsp;
+
+        relay->all_workers_busy --> all_workers_busy;
+    }
+};
+
+class Tasks : public Reactor {
+public:
+    struct Parameters {
+        int n_tasks = 10;
+        int n_pools = 1;
+    };
+
+private:
+    Parameters parameters;
+    const int &n_tasks = parameters.n_tasks;
+    const int &n_pools = parameters.n_pools;
+    
+    LogicalAction<int> sch{"sch", this};
+
+    REACTION_SCOPE_START(Tasks, Parameters)
+        const int &n_tasks = parameters.n_tasks;
+        const int &n_pools = parameters.n_pools;
+
+        int req_itr = 0;
+        int rsp_itr = 0;
+        bool *busy = 0;
+
+        void add_reactions(Tasks *reactor) {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects(&reactor->sch).
+                function(
+                    [this](Startup& startup, LogicalAction<int> &sch) {
+                        sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Startup n_pools:" << n_pools << "\n";
+                        busy = (bool*) calloc (n_pools, sizeof(bool));
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->sch).
+                dependencies().
+                effects(&reactor->req).
+                function(
+                    [this](LogicalAction<int> &sch, MultiportOutput<int> &req) {
+                        if (req_itr == n_tasks) {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Tasks queue empty" << std::endl;
+                            return;
+                        }
+                        auto index = *sch.get();
+                        if (index < 0) {
+                            for (int i = 0; i < n_pools; ++i) {
+                                if (busy[i]) {
+                                    std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Busy Pool:" << i << std::endl;
+                                    continue;
+                                }
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Sending task_id:" << req_itr << " to pool:" << i << std::endl;
+                                req[i].set (req_itr++);
+                            }
+
+                            int busy_count = 0;
+                            for (int i = 0; i < n_pools; ++i) {
+                                busy_count = busy[i] ? (busy_count + 1) : busy_count;
+                            }
+
+                            if (busy_count == n_pools) {
+                                return;
+                            }
+                            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        } else {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << fqn() << " Sending task_id:" << req_itr << " to pool:" << index << std::endl;
+                            req[index].set (req_itr++);
+                        }
+                    }
+                );
+
+                reaction("reaction_3").
+                triggers(&reactor->rsp).
+                dependencies().
+                effects(&reactor->sch).
+                function(
+                    [this](MultiportInput<int> &rsp, LogicalAction<int> &sch) {
+                        for (int i = 0; i < n_pools; ++i) {
+                            if (rsp[i].is_present()) {
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << "Received response of task:" << *rsp[i].get() << "\n";
+                                ++rsp_itr;
+                                busy[i] = 0;
+                            }
+                        }
+                        if (rsp_itr == n_tasks) {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << "Terminating Run\n";
+                            request_stop();
+                        } else {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << fqn() << " Scheduling tasks\n";
+                            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        }
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->hybernate).
+                dependencies().
+                effects().
+                function(
+                    [this](MultiportInput<bool> &hybernate) {
+                        for (int i = 0; i < n_pools; ++i) {
+                            if (hybernate[i].is_present()) {
+                                busy[i] = *hybernate[i].get();
+                            }
+                        }
+                    }
+                );
+
+            reaction("reaction_5").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    Tasks(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Tasks(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    MultiportOutput<int> req{"req", this};
+    MultiportInput<int> rsp{"rsp", this};
+    MultiportInput<bool> hybernate{"hybernate", this};
+
+    void construction() override {
+        req.set_width (n_pools);
+        rsp.set_width (n_pools);
+        hybernate.set_width (n_pools);
+    }
+
+    void wiring() override {}
+};
+
+class Main : public Reactor {
+public:
+    struct Parameters {
+        int n_tasks = 10;
+        int n_pools = 2;
+        int n_workers = 4;
+    };
+
+    Main(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Main(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+private:
+    Parameters parameters;
+    const int &n_tasks = parameters.n_tasks;
+    const int &n_pools = parameters.n_pools;
+    const int &n_workers = parameters.n_workers;
+
+    std::unique_ptr<Tasks> tasks;
+    ReactorBank<Pool> pool{"pool", this};
+
+public:
+    void construction() override {
+        tasks = std::make_unique<Tasks>("tasks", this, Tasks::Parameters{.n_tasks = n_tasks, .n_pools = n_pools});
+        for (int i = 0; i < n_pools; ++i) {
+            pool.create_reactor(Pool::Parameters{.n_workers = n_workers});
+        }
+    }
+
+    void wiring() override {
+        tasks->req --> pool.for_each(select_default(pool).req);
+        pool.for_each(select_default(pool).rsp) --> tasks->rsp;
+        pool.for_each(select_default(pool).all_workers_busy) --> tasks->hybernate;
+    }
+};
+
+int main(int argc, char **argv) {
+    Environment env {nullptr, 4, false, reactor::Duration::max(), false};
+
+    int n_tasks = 10;
+    int n_pools = 2;
+    int n_workers = 4;
+
+    auto main = new Main("Main", &env, Main::Parameters{.n_tasks = n_tasks, .n_pools = n_pools, .n_workers = n_workers});
+
+    env.run();
+    return 0;
+}
diff --git a/examples/sdk-Workers/src/Workers.lf b/examples/sdk-Workers/src/Workers.lf
new file mode 100644
index 00000000..d0db26e4
--- /dev/null
+++ b/examples/sdk-Workers/src/Workers.lf
@@ -0,0 +1,219 @@
+target Cpp {
+    fast: false,
+    // logging: debug
+}
+
+// lf_set_destructor(alloc_rsp, cache_entry_destructor);
+
+public preamble {=
+#include <stdint.h>
+=}
+
+reactor Relay (bank_index:size_t = 0, n_outputs:int = 1) {
+    input in_req:int;
+    output out_rsp:int;
+    output [n_outputs] out_req:int;
+    input [n_outputs] in_rsp:int;
+    
+    output all_workers_busy:bool;
+
+    state index:int = 0;
+    state busy:int* = 0;
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+        busy = (int*) calloc (n_outputs, sizeof(int));
+    =}
+
+    reaction (in_req) -> all_workers_busy, out_req {=
+        for (int i = 0; i < n_outputs; ++i, index = (index + 1) % n_outputs) {
+            if (busy[index] == 0) {
+                out_req[index].set(*in_req.get());
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Sending task_id:" << *in_req.get() << " to worker:" << index << std::endl;
+                busy[index] = 1;
+                index = (index + 1) % n_outputs;
+                break;
+            }
+        }
+        int busy_count = 0;
+        for (int i = 0; i < n_outputs; ++i) {
+            busy_count = busy[i] ? (busy_count + 1) : busy_count;
+        }
+
+        if (busy_count == n_outputs) {
+            all_workers_busy.set(true);
+        }
+    =}
+
+    reaction (in_rsp) -> out_rsp {=
+        for (int i = 0; i < n_outputs; ++i) {
+            if (in_rsp[i].is_present()) {
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Receiving task_id:" << *in_rsp[i].get() << " from worker:" << i << std::endl;
+                busy[i] = 0;
+                out_rsp.set(*in_rsp[i].get());
+            }
+        }
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+reactor Worker (bank_index:size_t = 0, processing_delay:time = 2s) {
+    input req:int;
+    output rsp:int;
+
+    logical action sch_rsp(0):int;
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+    =}
+
+    reaction (req) -> sch_rsp {=
+        auto req_ref = *req.get();
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Receiving task_id:" << req_ref << std::endl;
+        sch_rsp.schedule (req_ref, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(processing_delay)));
+    =}
+
+    reaction (sch_rsp) -> rsp {=
+        auto req_ref = *sch_rsp.get();
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Sending task_id:" << req_ref << std::endl;
+        rsp.set(req_ref);
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+
+reactor Pool (bank_index:size_t = 0, n_workers:int = 1) {
+    
+    input req:int;
+    output rsp:int;
+
+    output all_workers_busy:bool;
+
+    logical action sch_rsp(0):int;
+
+    workers = new [n_workers] Worker(processing_delay = 1s);
+    relay = new Relay(n_outputs = n_workers);
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+
+    req -> relay.in_req;
+    relay.out_req -> workers.req;
+    workers.rsp -> relay.in_rsp;
+    relay.out_rsp -> rsp;
+
+    relay.all_workers_busy -> all_workers_busy;
+
+}
+
+reactor Tasks (bank_index:size_t = 0, n_tasks:int = 10, n_pools:int = 1) {
+
+    output[n_pools] req:int;
+    input[n_pools] rsp:int;
+
+    input[n_pools] hybernate:bool;
+
+    state req_itr:int = 0;
+    state rsp_itr:int = 0;
+    state busy:bool* = 0;
+    
+    logical action sch(0):int;
+
+    reaction (startup) -> sch {=
+        sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+        busy = (bool*) calloc (n_pools, sizeof(bool));
+    =}
+
+    reaction (sch) -> sch, req {=
+        auto index = *sch.get();
+        if (index < 0) {
+            for (int i = 0; i < n_pools; ++i) {
+                if (busy[i]) {
+                    std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Busy Pool:" << i << std::endl;
+                    continue;
+                }
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Sending task_id:" << req_itr << " to pool:" << i << std::endl;
+                req[i].set (req_itr++);
+            }
+
+            int busy_count = 0;
+            for (int i = 0; i < n_pools; ++i) {
+                busy_count = busy[i] ? (busy_count + 1) : busy_count;
+            }
+
+            if (busy_count == n_pools) {
+                return;
+            }
+            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+        } else {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Sending task_id:" << req_itr << " to pool:" << index << std::endl;
+            req[index].set (req_itr++);
+        }
+    =}
+
+    reaction (rsp) -> sch {=
+        for (int i = 0; i < n_pools; ++i) {
+            if (rsp[i].is_present()) {
+                ++rsp_itr;
+                if (req_itr < n_tasks) {
+                    std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                << fqn() << " Scheduling task_id:" << req_itr << " to pool:" << i << std::endl;
+                    sch.schedule (i, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                }
+            }
+        }
+        if (rsp_itr == n_tasks) {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << "Terminating Run\n";
+            request_stop();
+        }
+    =}
+
+    reaction (hybernate) {=
+        for (int i = 0; i < n_pools; ++i) {
+            if (hybernate[i].is_present()) {
+                busy[i] = *hybernate[i].get();
+            }
+        }
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+main reactor (n_tasks:int = 10, n_pools:int = 2, n_workers:int = 4) {
+    tasks = new Tasks(n_tasks = n_tasks, n_pools = n_pools);
+    pool = new [n_pools] Pool(n_workers = n_workers);
+
+    tasks.req -> pool.req;
+    pool.rsp -> tasks.rsp;
+    pool.all_workers_busy -> tasks.hybernate;
+}
\ No newline at end of file
diff --git a/examples/sdk-WorkersParams/CMakeLists.txt b/examples/sdk-WorkersParams/CMakeLists.txt
new file mode 100644
index 00000000..159ed788
--- /dev/null
+++ b/examples/sdk-WorkersParams/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(workers_with_params VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET workers_with_params)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/examples/sdk-WorkersParams/main.cc b/examples/sdk-WorkersParams/main.cc
new file mode 100644
index 00000000..88056e72
--- /dev/null
+++ b/examples/sdk-WorkersParams/main.cc
@@ -0,0 +1,567 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace sdk;
+
+struct UserParameters : public ConfigParameter<Duration, int> {
+	ConfigParameter<Duration, int>::ParametersMap homogeneous_config();
+	ConfigParameter<Duration, int>::ParametersMap heterogeneous_config();
+};
+
+UserParameters cfg_parameters;
+
+ConfigParameter<Duration, int>::ParametersMap UserParameters::homogeneous_config() {
+	return {
+		{ "Main.n_pools", ConfigParameterMetadata<int> {2} },
+		{ "Main.n_tasks", ConfigParameterMetadata<int> {10} },
+		{ "Main.pool.n_workers", ConfigParameterMetadata<int> {4} },
+		{ "Main.pool.workers.processing_delay", ConfigParameterMetadata<Duration> {1000000000ns} }
+	};
+}
+
+ConfigParameter<Duration, int>::ParametersMap UserParameters::heterogeneous_config() {
+	return {
+		{ "Main.n_pools", ConfigParameterMetadata<int> {2} },
+		{ "Main.n_tasks", ConfigParameterMetadata<int> {10} },
+		{ "Main.pool_0.n_workers", ConfigParameterMetadata<int> {2} },
+		{ "Main.pool_0.workers_0.processing_delay", ConfigParameterMetadata<Duration> {100000000ns} },
+		{ "Main.pool_0.workers_1.processing_delay", ConfigParameterMetadata<Duration> {1000000000ns} },
+		{ "Main.pool_1.n_workers", ConfigParameterMetadata<int> {3} },
+		{ "Main.pool_1.workers_0.processing_delay", ConfigParameterMetadata<Duration> {1000000000ns} },
+		{ "Main.pool_1.workers_1.processing_delay", ConfigParameterMetadata<Duration> {100000000ns} },
+		{ "Main.pool_1.workers_2.processing_delay", ConfigParameterMetadata<Duration> {1000000000ns} },
+	};
+}
+
+class Relay : public Reactor {
+public:
+    struct Parameters {
+        int n_outputs;
+    };
+
+private:
+    Parameters parameters;
+    const int &n_outputs = parameters.n_outputs;
+
+    REACTION_SCOPE_START(Relay, Parameters)
+        const int &n_outputs = parameters.n_outputs;
+
+        int index = 0;
+        int *busy = 0;
+
+        void add_reactions(Relay *reactor) override {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Startup\n";
+                        busy = (int*) calloc (n_outputs, sizeof(int));
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->in_req).
+                dependencies().
+                effects(&reactor->all_workers_busy, &reactor->out_req).
+                function(
+                    [this](Input<int> &in_req, Output<bool> &all_workers_busy, MultiportOutput<int> &out_req) {
+                        for (int i = 0; i < n_outputs; ++i, index = (index + 1) % n_outputs) {
+                            if (busy[index] == 0) {
+                                out_req[index].set(*in_req.get());
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Sending task_id:" << *in_req.get() << " to worker:" << index << std::endl;
+                                busy[index] = 1;
+                                index = (index + 1) % n_outputs;
+                                break;
+                            }
+                        }
+                        int busy_count = 0;
+                        for (int i = 0; i < n_outputs; ++i) {
+                            busy_count = busy[i] ? (busy_count + 1) : busy_count;
+                        }
+
+                        if (busy_count == n_outputs) {
+                            all_workers_busy.set(true);
+                        }
+                    }
+                );
+
+            reaction("reaction_3").
+                triggers(&reactor->in_rsp).
+                dependencies().
+                effects(&reactor->out_rsp).
+                function(
+                    [this](MultiportInput<int> &in_rsp, Output<int> &out_rsp) {
+                        for (int i = 0; i < n_outputs; ++i) {
+                            if (in_rsp[i].is_present()) {
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Receiving task_id:" << *in_rsp[i].get() << " from worker:" << i << std::endl;
+                                busy[i] = 0;
+                                out_rsp.set(*in_rsp[i].get());
+                            }
+                        }
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+public:
+    Input<int> in_req{"in_req", this};
+    Output<int> out_rsp{"out_rsp", this};
+    MultiportOutput<int> out_req{"out_req", this};
+    MultiportInput<int> in_rsp{"in_rsp", this};
+
+    Output<bool> all_workers_busy{"all_workers_busy", this};
+
+    Relay(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Relay(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    void construction() override {
+        out_req.set_width(n_outputs);
+        in_rsp.set_width(n_outputs);
+    }
+
+    void wiring() override {
+    }
+};
+
+class Worker : public Reactor {
+public:
+    struct Parameters {
+        Duration processing_delay = 2s;
+    };
+
+private:
+    LogicalAction<int> sch_rsp{"sch_rsp", this};
+
+    struct PublishParameters : public SystemParameters<Parameters, Duration> {
+        REACTOR_PARAMETER(Duration, processing_delay, "Worker's processing delay", 1ms, 2s, defaults.processing_delay);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, Duration>(container, std::forward<Parameters>(param)) {
+            register_parameters (processing_delay);
+        }
+    };
+
+    PublishParameters parameters;
+    class Chamber : public ReactionChamber<Worker, PublishParameters> {
+        const Duration &processing_delay = parameters.processing_delay.value;
+    public:
+        Chamber(Reactor *reactor, PublishParameters &params)
+            : ReactionChamber<Worker, PublishParameters>(reactor, params) {}
+        
+        void add_reactions(Worker *reactor) override {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Startup\n";
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->req).
+                dependencies().
+                effects(&reactor->sch_rsp).
+                function(
+                    [this](Input<int> &req, LogicalAction<int> &sch_rsp) {
+                        auto req_ref = *req.get();
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Receiving task_id:" << req_ref << std::endl;
+                        sch_rsp.schedule (req_ref, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(processing_delay)));
+                    }
+                );
+
+            reaction("reaction_3").
+                triggers(&reactor->sch_rsp).
+                dependencies().
+                effects(&reactor->rsp).
+                function(
+                    [this](LogicalAction<int> &sch_rsp, Output<int> &rsp) {
+                        auto req_ref = *sch_rsp.get();
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Sending task_id:" << req_ref << std::endl;
+                        rsp.set(req_ref);
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+
+
+    };
+    Chamber reaction_chamber{this, parameters};
+
+public:
+    Worker(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    Worker(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+
+    Input<int> req{"req", this};
+    Output<int> rsp{"rsp", this};
+
+    void construction() override {}
+    void wiring() override {
+    }
+};
+
+
+class Pool : public Reactor {
+public:
+    struct Parameters {
+        int n_workers = 1;
+    };
+
+private:
+    struct PublishParameters : public SystemParameters<Parameters, int> {
+        REACTOR_PARAMETER(int, n_workers, "Number of workers in the pool", 1, 10, defaults.n_workers);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, int>(container, std::forward<Parameters>(param)) {
+            register_parameters (n_workers);
+        }
+    };
+    PublishParameters parameters;
+    const int &n_workers = parameters.n_workers.value;
+
+    LogicalAction<int> sch_rsp{"sch_rsp", this};
+
+    ReactorBank<Worker> workers{"workers", this};
+    std::unique_ptr<Relay> relay;
+
+    class Chamber : public ReactionChamber<Pool, PublishParameters> {
+    public:
+        Chamber(Reactor *reactor, PublishParameters &params)
+            : ReactionChamber<Pool, PublishParameters>(reactor, params) {}
+        
+        void add_reactions(Pool *reactor) override {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects().
+                function(
+                    [this](Startup& startup) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Startup\n";
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+
+
+    };
+
+    Chamber reaction_chamber{this, parameters};
+
+public:
+    Pool(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    Pool(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+
+    Input<int> req{"req", this};
+    Output<int> rsp{"rsp", this};
+
+    Output<bool> all_workers_busy{"all_workers_busy", this};
+
+    void construction() override {
+        for (int i = 0; i < n_workers; ++i) {
+            workers.create_reactor(Worker::Parameters{.processing_delay = 1s});
+        }
+        relay = std::make_unique<Relay>("relay", this, Relay::Parameters{.n_outputs = n_workers});
+
+    }
+
+    void wiring() override {
+        req --> relay->in_req;
+        relay->out_req --> workers.for_each(select_default(workers).req);
+        workers.for_each(select_default(workers).rsp) --> relay->in_rsp;
+        relay->out_rsp --> rsp;
+
+        relay->all_workers_busy --> all_workers_busy;
+    }
+};
+
+class Tasks : public Reactor {
+public:
+    struct Parameters {
+        int n_tasks = 10;
+        int n_pools = 1;
+    };
+
+private:
+    Parameters parameters;
+    const int &n_pools = parameters.n_pools;
+    
+    LogicalAction<int> sch{"sch", this};
+
+    class Chamber : public ReactionChamber<Tasks, Parameters> {
+        const int &n_tasks = parameters.n_tasks;
+        const int &n_pools = parameters.n_pools;
+
+        int req_itr = 0;
+        int rsp_itr = 0;
+        bool *busy = 0;
+    public:
+        Chamber(Reactor *reactor, Parameters &params)
+            : ReactionChamber<Tasks, Parameters>(reactor, params) {}
+        
+        void add_reactions(Tasks *reactor) override {
+            reaction("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects(&reactor->sch).
+                function(
+                    [this](Startup& startup, LogicalAction<int> &sch) {
+                        sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Startup n_pools:" << n_pools << "\n";
+                        busy = (bool*) calloc (n_pools, sizeof(bool));
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->sch).
+                dependencies().
+                effects(&reactor->req).
+                function(
+                    [this](LogicalAction<int> &sch, MultiportOutput<int> &req) {
+                        if (req_itr == n_tasks) {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Tasks queue empty" << std::endl;
+                            return;
+                        }
+                        auto index = *sch.get();
+                        if (index < 0) {
+                            for (int i = 0; i < n_pools; ++i) {
+                                if (busy[i]) {
+                                    std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Busy Pool:" << i << std::endl;
+                                    continue;
+                                }
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << fqn() << " Sending task_id:" << req_itr << " to pool:" << i << std::endl;
+                                req[i].set (req_itr++);
+                            }
+
+                            int busy_count = 0;
+                            for (int i = 0; i < n_pools; ++i) {
+                                busy_count = busy[i] ? (busy_count + 1) : busy_count;
+                            }
+
+                            if (busy_count == n_pools) {
+                                return;
+                            }
+                            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        } else {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << fqn() << " Sending task_id:" << req_itr << " to pool:" << index << std::endl;
+                            req[index].set (req_itr++);
+                        }
+                    }
+                );
+
+                reaction("reaction_3").
+                triggers(&reactor->rsp).
+                dependencies().
+                effects(&reactor->sch).
+                function(
+                    [this](MultiportInput<int> &rsp, LogicalAction<int> &sch) {
+                        for (int i = 0; i < n_pools; ++i) {
+                            if (rsp[i].is_present()) {
+                                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                            << "Received response of task:" << *rsp[i].get() << "\n";
+                                ++rsp_itr;
+                                busy[i] = 0;
+                            }
+                        }
+                        if (rsp_itr == n_tasks) {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << "Terminating Run\n";
+                            request_stop();
+                        } else {
+                            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                        << fqn() << " Scheduling tasks\n";
+                            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+                        }
+                    }
+                );
+
+            reaction("reaction_4").
+                triggers(&reactor->hybernate).
+                dependencies().
+                effects().
+                function(
+                    [this](MultiportInput<bool> &hybernate) {
+                        for (int i = 0; i < n_pools; ++i) {
+                            if (hybernate[i].is_present()) {
+                                busy[i] = *hybernate[i].get();
+                            }
+                        }
+                    }
+                );
+
+            reaction("reaction_5").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(
+                    [this](Shutdown &shutdown) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                                    << fqn() << " Shutdown\n";
+                    }
+                );
+        }
+    };
+
+    Chamber reaction_chamber{this, parameters};
+
+public:
+    Tasks(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{std::forward<Parameters>(param)} {}
+    Tasks(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{std::forward<Parameters>(param)} {}
+
+    MultiportOutput<int> req{"req", this};
+    MultiportInput<int> rsp{"rsp", this};
+    MultiportInput<bool> hybernate{"hybernate", this};
+
+    void construction() override {
+        req.set_width (n_pools);
+        rsp.set_width (n_pools);
+        hybernate.set_width (n_pools);
+    }
+
+    void wiring() override {
+    }
+};
+
+class Main : public Reactor {
+public:
+    struct Parameters {
+        int n_tasks = 10;
+        int n_pools = 2;
+        int n_workers = 4;
+    };
+
+    Main(const std::string &name, Environment *env, Parameters &&param)
+    : Reactor(name, env), parameters{this, std::forward<Parameters>(param)} {}
+    Main(const std::string &name, Reactor *container, Parameters &&param)
+    : Reactor(name, container), parameters{this, std::forward<Parameters>(param)} {}
+private:
+    struct PublishParameters : public SystemParameters<Parameters, int> {
+        REACTOR_PARAMETER(int, n_tasks, "Number of tasks", 1, 100, defaults.n_tasks);
+        REACTOR_PARAMETER(int, n_pools, "Number of pools", 1, 10, defaults.n_pools);
+
+        PublishParameters(Reactor *container, Parameters &&param)
+            :   SystemParameters<Parameters, int>(container, std::forward<Parameters>(param)) {
+            register_parameters (n_tasks, n_pools);
+        }
+    };
+    PublishParameters parameters;
+    const int &n_tasks = parameters.n_tasks.value;
+    const int &n_pools = parameters.n_pools.value;
+    const int &n_workers = parameters.defaults.n_workers;
+
+    std::unique_ptr<Tasks> tasks;
+    ReactorBank<Pool> pool{"pool", this};
+
+public:
+
+    void construction() override {
+        tasks = std::make_unique<Tasks>("tasks", this, Tasks::Parameters{.n_tasks = n_tasks, .n_pools = n_pools});
+        for (int i = 0; i < n_pools; ++i) {
+            pool.create_reactor(Pool::Parameters{.n_workers = n_workers});
+        }
+    }
+
+    void wiring() override {
+        tasks->req --> pool.for_each(select_default(pool).req);
+        pool.for_each(select_default(pool).rsp) --> tasks->rsp;
+        pool.for_each(select_default(pool).all_workers_busy) --> tasks->hybernate;
+    }
+};
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("Workers-Example", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+    Environment env {nullptr, workers, fast, timeout, cfg_gen};
+
+    int n_tasks = 10;
+    int n_pools = 2;
+    int n_workers = 4;
+
+    auto main = new Main("Main", &env, Main::Parameters{.n_tasks = n_tasks, .n_pools = n_pools, .n_workers = n_workers});
+
+    env.run();
+    return 0;
+}
diff --git a/examples/sdk-WorkersParams/src/Workers.lf b/examples/sdk-WorkersParams/src/Workers.lf
new file mode 100644
index 00000000..6afebc9e
--- /dev/null
+++ b/examples/sdk-WorkersParams/src/Workers.lf
@@ -0,0 +1,226 @@
+target Cpp {
+    fast: false,
+    // logging: debug
+}
+
+// lf_set_destructor(alloc_rsp, cache_entry_destructor);
+
+public preamble {=
+#include <stdint.h>
+=}
+
+reactor Relay (bank_index:size_t = 0, n_outputs:int = 1) {
+    input in_req:int;
+    output out_rsp:int;
+    output [n_outputs] out_req:int;
+    input [n_outputs] in_rsp:int;
+    
+    output all_workers_busy:bool;
+
+    state index:int = 0;
+    state busy:int* = 0;
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+        busy = (int*) calloc (n_outputs, sizeof(int));
+    =}
+
+    reaction (in_req) -> all_workers_busy, out_req {=
+        for (int i = 0; i < n_outputs; ++i, index = (index + 1) % n_outputs) {
+            if (busy[index] == 0) {
+                out_req[index].set(*in_req.get());
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Sending task_id:" << *in_req.get() << " to worker:" << index << std::endl;
+                busy[index] = 1;
+                index = (index + 1) % n_outputs;
+                break;
+            }
+        }
+        int busy_count = 0;
+        for (int i = 0; i < n_outputs; ++i) {
+            busy_count = busy[i] ? (busy_count + 1) : busy_count;
+        }
+
+        if (busy_count == n_outputs) {
+            all_workers_busy.set(true);
+        }
+    =}
+
+    reaction (in_rsp) -> out_rsp {=
+        for (int i = 0; i < n_outputs; ++i) {
+            if (in_rsp[i].is_present()) {
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Receiving task_id:" << *in_rsp[i].get() << " from worker:" << i << std::endl;
+                busy[i] = 0;
+                out_rsp.set(*in_rsp[i].get());
+            }
+        }
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+reactor Worker (bank_index:size_t = 0, processing_delay:time = 2s) {
+    input req:int;
+    output rsp:int;
+
+    logical action sch_rsp(0):int;
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+    =}
+
+    reaction (req) -> sch_rsp {=
+        auto req_ref = *req.get();
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Receiving task_id:" << req_ref << std::endl;
+        sch_rsp.schedule (req_ref, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(processing_delay)));
+    =}
+
+    reaction (sch_rsp) -> rsp {=
+        auto req_ref = *sch_rsp.get();
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Sending task_id:" << req_ref << std::endl;
+        rsp.set(req_ref);
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+
+reactor Pool (bank_index:size_t = 0, n_workers:int = 1) {
+    
+    input req:int;
+    output rsp:int;
+
+    output all_workers_busy:bool;
+
+    logical action sch_rsp(0):int;
+
+    workers = new [n_workers] Worker(processing_delay = 1s);
+    relay = new Relay(n_outputs = n_workers);
+
+    reaction (startup) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+
+    req -> relay.in_req;
+    relay.out_req -> workers.req;
+    workers.rsp -> relay.in_rsp;
+    relay.out_rsp -> rsp;
+
+    relay.all_workers_busy -> all_workers_busy;
+
+}
+
+reactor Tasks (bank_index:size_t = 0, n_tasks:int = 10, n_pools:int = 1) {
+
+    output[n_pools] req:int;
+    input[n_pools] rsp:int;
+
+    input[n_pools] hybernate:bool;
+
+    state req_itr:int = 0;
+    state rsp_itr:int = 0;
+    state busy:bool* = 0;
+    
+    logical action sch(0):int;
+
+    reaction (startup) -> sch {=
+        sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Startup\n";
+        busy = (bool*) calloc (n_pools, sizeof(bool));
+    =}
+
+    reaction (sch) -> sch, req {=
+        if (req_itr == n_tasks) {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Tasks queue empty" << std::endl;
+            return;
+        }
+        auto index = *sch.get();
+        if (index < 0) {
+            for (int i = 0; i < n_pools; ++i) {
+                if (busy[i]) {
+                    std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Busy Pool:" << i << std::endl;
+                    continue;
+                }
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << fqn() << " Sending task_id:" << req_itr << " to pool:" << i << std::endl;
+                req[i].set (req_itr++);
+            }
+
+            int busy_count = 0;
+            for (int i = 0; i < n_pools; ++i) {
+                busy_count = busy[i] ? (busy_count + 1) : busy_count;
+            }
+
+            if (busy_count == n_pools) {
+                return;
+            }
+            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+        } else {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Sending task_id:" << req_itr << " to pool:" << index << std::endl;
+            req[index].set (req_itr++);
+        }
+    =}
+
+    reaction (rsp) -> sch {=
+        for (int i = 0; i < n_pools; ++i) {
+            if (rsp[i].is_present()) {
+                std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                            << "Received response of task:" << *rsp[i].get() << "\n";
+                ++rsp_itr;
+                busy[i] = 0;
+            }
+        }
+        if (rsp_itr == n_tasks) {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << "Terminating Run\n";
+            request_stop();
+        } else {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Scheduling tasks\n";
+            sch.schedule (-1, std::chrono::duration_cast<reactor::Duration>(std::chrono::nanoseconds(0)));
+        }
+    =}
+
+    reaction (hybernate) {=
+        for (int i = 0; i < n_pools; ++i) {
+            if (hybernate[i].is_present()) {
+                busy[i] = *hybernate[i].get();
+            }
+        }
+    =}
+
+    reaction (shutdown) {=
+        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                    << fqn() << " Shutdown\n";
+    =}
+}
+
+main reactor (n_tasks:int = 10, n_pools:int = 2, n_workers:int = 4) {
+    tasks = new Tasks(n_tasks = n_tasks, n_pools = n_pools);
+    pool = new [n_pools] Pool(n_workers = n_workers);
+
+    tasks.req -> pool.req;
+    pool.rsp -> tasks.rsp;
+    pool.all_workers_busy -> tasks.hybernate;
+}
\ No newline at end of file
diff --git a/examples/sdk-deadlines/CMakeLists.txt b/examples/sdk-deadlines/CMakeLists.txt
new file mode 100644
index 00000000..b6035a02
--- /dev/null
+++ b/examples/sdk-deadlines/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.9)
+project(node VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET node)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
+
+include(Node/NodeReactor.cmake)
+include(Main/MainReactor.cmake)
+include(Config-a/Config-a.cmake)
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Config-a/Config-a.cc b/examples/sdk-deadlines/Config-a/Config-a.cc
new file mode 100644
index 00000000..0aa1a421
--- /dev/null
+++ b/examples/sdk-deadlines/Config-a/Config-a.cc
@@ -0,0 +1,18 @@
+#include "Config-a.hh"
+
+UserParameters cfg_parameters;
+
+ConfigParameter<int, Duration>::ParametersMap UserParameters::homogeneous_config() {
+    return {
+    };
+}
+
+ConfigParameter<int, Duration>::ParametersMap UserParameters::heterogeneous_config() {
+    return {
+            {"Main.slow.period", ConfigParameterMetadata<Duration> { 1s } },
+            {"Main.slow.duration", ConfigParameterMetadata<Duration> { 5s } },
+            {"Main.n_fast", ConfigParameterMetadata<int> { 3 } },
+            {"Main.fast_0.period", ConfigParameterMetadata<Duration> { 500ms } },
+            {"Main.fast_0.duration", ConfigParameterMetadata<Duration> { 10ms } }
+    };
+}
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Config-a/Config-a.cmake b/examples/sdk-deadlines/Config-a/Config-a.cmake
new file mode 100644
index 00000000..bd7049e4
--- /dev/null
+++ b/examples/sdk-deadlines/Config-a/Config-a.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/Config-a.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Config-a/Config-a.hh b/examples/sdk-deadlines/Config-a/Config-a.hh
new file mode 100644
index 00000000..e23e5e2d
--- /dev/null
+++ b/examples/sdk-deadlines/Config-a/Config-a.hh
@@ -0,0 +1,18 @@
+#ifndef USER_PARAMETERS_H
+#define USER_PARAMETERS_H
+
+#include <reactor-sdk/reactor-sdk.hh>
+#include <map>
+#include <variant>
+#include <string>
+
+using namespace sdk;
+
+struct UserParameters : public ConfigParameter<int, Duration> {
+    ConfigParameter<int, Duration>::ParametersMap homogeneous_config();
+    ConfigParameter<int, Duration>::ParametersMap heterogeneous_config();
+};
+
+extern UserParameters cfg_parameters;
+
+#endif // USER_PARAMETERS_H
diff --git a/examples/sdk-deadlines/Main/MainReactor.cc b/examples/sdk-deadlines/Main/MainReactor.cc
new file mode 100644
index 00000000..0f6ef5ab
--- /dev/null
+++ b/examples/sdk-deadlines/Main/MainReactor.cc
@@ -0,0 +1,16 @@
+#include "MainReactor.hh"
+
+void MainReactor::construction() {
+
+    std::cout << "Construction Main n_fast:" << parameters.n_fast.value << "\n";
+
+    slow = std::make_unique<NodeReactor>("slow", this);
+
+    for (int i = 0; i < parameters.n_fast.value; i++) {
+        fast.create_reactor();
+    }
+}
+
+void MainReactor::wiring() {
+    std::cout << "Wiring Main n_sinks:" << parameters.n_fast.value << "\n";
+}
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Main/MainReactor.cmake b/examples/sdk-deadlines/Main/MainReactor.cmake
new file mode 100644
index 00000000..4c6cc870
--- /dev/null
+++ b/examples/sdk-deadlines/Main/MainReactor.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/MainReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Main/MainReactor.hh b/examples/sdk-deadlines/Main/MainReactor.hh
new file mode 100644
index 00000000..62031b9c
--- /dev/null
+++ b/examples/sdk-deadlines/Main/MainReactor.hh
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Node/NodeReactor.hh"
+
+using namespace sdk;
+
+class MainReactor: public Reactor {
+public:
+    struct Parameters : public SystemParametersStandalone<int> {
+        REACTOR_PARAMETER(int, n_fast, "Number of fast nodes", 1, 10, 2);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<int>(container) {
+            register_parameters (n_fast);
+        }
+    };
+private:
+    Parameters parameters{this};
+    std::unique_ptr<NodeReactor> slow;
+    ReactorBank<NodeReactor> fast{"fast", this};
+
+public:
+    MainReactor(const std::string &name, Environment *env)
+    : Reactor(name, env) {}
+    MainReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container) {}
+  
+    void construction() override;
+    void wiring() override;
+};
+
+        
diff --git a/examples/sdk-deadlines/Node/NodeReactor.cc b/examples/sdk-deadlines/Node/NodeReactor.cc
new file mode 100644
index 00000000..d23d8bab
--- /dev/null
+++ b/examples/sdk-deadlines/Node/NodeReactor.cc
@@ -0,0 +1,29 @@
+#include "NodeReactor.hh"
+
+void NodeReactor::construction() {
+    std::cout << "Construction:" << fqn() << " period:" << parameters.period.value << " duration:" << parameters.duration.value << "\n";
+}
+
+void NodeReactor::wiring() {
+    std::cout << "Assembling Node\n";
+}
+
+void NodeReactor::Internals::add_reactions(NodeReactor *reactor) {
+    reaction("reaction_1").
+        triggers(&reactor->startup, &reactor->a).
+        dependencies().
+        effects().
+        function(
+            [this](Startup& startup, LogicalAction<void> &a) {
+                reactor::log::Info() << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " << fqn() << " reaction executes.";
+                std::this_thread::sleep_for(parameters.duration.value);
+                reactor::log::Info() << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " << fqn() << " reaction done.";
+                a.schedule(parameters.period.value);
+            }
+        ).deadline (parameters.period.value, 
+            [this](Startup& startup, LogicalAction<void> &a) {
+                reactor::log::Error() << "(" << get_elapsed_logical_time() << ", " << get_microstep() << "), physical_time: " << get_elapsed_physical_time() << " " << fqn() << " deadline was violated!";
+                exit(1);
+            }
+        );
+}
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Node/NodeReactor.cmake b/examples/sdk-deadlines/Node/NodeReactor.cmake
new file mode 100644
index 00000000..7468f97c
--- /dev/null
+++ b/examples/sdk-deadlines/Node/NodeReactor.cmake
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+set(INCLUDE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/NodeReactor.hh"
+)
+
+set(SOURCE_FILES 
+    "${CMAKE_CURRENT_LIST_DIR}/NodeReactor.cc"
+)
+
+
+foreach(file IN LISTS INCLUDE_FILES)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include ${file}")
+endforeach()
+
+target_sources(${LF_MAIN_TARGET} PRIVATE ${SOURCE_FILES})
\ No newline at end of file
diff --git a/examples/sdk-deadlines/Node/NodeReactor.hh b/examples/sdk-deadlines/Node/NodeReactor.hh
new file mode 100644
index 00000000..8b5b017f
--- /dev/null
+++ b/examples/sdk-deadlines/Node/NodeReactor.hh
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace sdk;
+
+class NodeReactor: public Reactor {
+public:
+    struct Parameters : public SystemParametersStandalone<Duration> {
+        REACTOR_PARAMETER(Duration, period, "Schedule and deadline period", 10ms, 10s, 500ms);
+        REACTOR_PARAMETER(Duration, duration, "Sleep duration", 5ms, 5s, 10ms);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<Duration>(container) {
+            register_parameters (period, duration);
+        }
+  };
+
+private:
+    Parameters parameters{this};
+    LogicalAction<void> a{"a", this};
+
+    REACTION_SCOPE_START(NodeReactor, Parameters)
+        void add_reactions(NodeReactor *reactor);
+    REACTION_SCOPE_END(this, parameters)
+
+
+public:
+    NodeReactor(const std::string &name, Environment *env)
+    : Reactor(name, env) {}
+    NodeReactor(const std::string &name, Reactor *container)
+    : Reactor(name, container) {}
+  
+    void construction() override;
+    void wiring() override;
+};
+
+        
diff --git a/examples/sdk-deadlines/main.cc b/examples/sdk-deadlines/main.cc
new file mode 100644
index 00000000..cb3d106b
--- /dev/null
+++ b/examples/sdk-deadlines/main.cc
@@ -0,0 +1,51 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+#include "Config-a/Config-a.hh"
+#include "Main/MainReactor.hh"
+
+using namespace std;
+using namespace sdk;
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("sdk-deadlines", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {&cfg_parameters, workers, fast, timeout, cfg_gen};
+    auto main = new MainReactor("Main", &sim);
+
+    sim.run();
+    return 0;
+}
diff --git a/examples/sdk-hello/CMakeLists.txt b/examples/sdk-hello/CMakeLists.txt
new file mode 100644
index 00000000..77875f90
--- /dev/null
+++ b/examples/sdk-hello/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(hello VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET hello)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/examples/sdk-hello/main.cc b/examples/sdk-hello/main.cc
new file mode 100644
index 00000000..6c2c904b
--- /dev/null
+++ b/examples/sdk-hello/main.cc
@@ -0,0 +1,65 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace sdk;
+
+class Hello : public Reactor {
+private:
+    struct Parameters {
+    };
+    Parameters parameters;
+
+    Timer timer{"timer", this};
+
+    class Chamber : public ReactionChamber<Hello, Parameters> {
+    public:
+        Chamber(Reactor *reactor, Parameters &params)
+            : ReactionChamber<Hello, Parameters>(reactor, params) {}
+    private:
+
+        void terminate(Shutdown& shutdown) {
+            std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Good Bye!\n";
+        }
+
+        void add_reactions (Hello *reactor) {
+            reaction("reaction_1").
+                triggers(&reactor->timer).
+                dependencies().
+                effects().
+                function(
+                    [this](Timer& timer) {
+                        std::cout   << "(" << get_elapsed_logical_time().count() << ", " << get_microstep() << ") physical_time:" << get_elapsed_physical_time().count()
+                        << fqn() << " Bank:" << bank_index << " Hello World!\n";
+                    }
+                );
+
+            reaction("reaction_2").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function(pass_function(terminate));
+        }
+    };
+    Chamber reaction_chamber{this, parameters};
+
+public:
+    Hello(const std::string &name, Environment *env)
+    : Reactor(name, env) {}
+
+    void construction() {
+        timer.set_timer (1s, 2s);
+    }
+
+    void wiring() override {
+        
+    }
+};
+
+int main(int argc, char **argv) {
+    Environment env {nullptr, 4, false, 4s, false};
+    auto main = new Hello("Hello", &env);
+
+    env.run();
+    return 0;
+}
diff --git a/examples/sdk-hello/src/HelloWorld.lf b/examples/sdk-hello/src/HelloWorld.lf
new file mode 100644
index 00000000..f55ccc56
--- /dev/null
+++ b/examples/sdk-hello/src/HelloWorld.lf
@@ -0,0 +1,19 @@
+target Cpp {
+    timeout: 4s
+}
+
+reactor Hello (bank_index: size_t = 0) {
+    timer Timer(1s, 2s)
+
+    reaction(Timer) {=
+        std::cout << "Bank:" << bank_index << " Hello World!\n";
+    =}
+        
+    reaction(shutdown) {=
+        std::cout << "Good Bye!\n";
+    =}
+}
+    
+main reactor {
+    hello = new Hello();
+}
\ No newline at end of file
diff --git a/include/reactor-cpp/action.hh b/include/reactor-cpp/action.hh
index bb43c8c8..7f7be9d1 100644
--- a/include/reactor-cpp/action.hh
+++ b/include/reactor-cpp/action.hh
@@ -152,7 +152,7 @@ public:
 };
 
 class Timer : public BaseAction {
-private:
+protected:
   Duration offset_{0};
   Duration period_{0};
 
diff --git a/include/reactor-cpp/environment.hh b/include/reactor-cpp/environment.hh
index 825b3bba..59234d3d 100644
--- a/include/reactor-cpp/environment.hh
+++ b/include/reactor-cpp/environment.hh
@@ -30,10 +30,11 @@ constexpr bool default_fast_fwd_execution = false;
 enum class Phase : std::uint8_t {
   Construction = 0,
   Assembly = 1,
-  Startup = 2,
-  Execution = 3,
-  Shutdown = 4,
-  Deconstruction = 5
+  Indexing = 2,
+  Startup = 3,
+  Execution = 4,
+  Shutdown = 5,
+  Deconstruction = 6
 };
 
 class Environment {
@@ -107,7 +108,9 @@ public:
   void register_reactor(Reactor* reactor);
   void register_port(BasePort* port) noexcept;
   void register_input_action(BaseAction* action);
+  void construct();
   void assemble();
+  void dependency_graph_and_indexes();
   auto startup() -> std::thread;
   void sync_shutdown();
   void async_shutdown();
diff --git a/include/reactor-cpp/reactor.hh b/include/reactor-cpp/reactor.hh
index 02b698dd..a93cedb0 100644
--- a/include/reactor-cpp/reactor.hh
+++ b/include/reactor-cpp/reactor.hh
@@ -52,6 +52,7 @@ public:
   void shutdown() final;
 
   virtual void assemble() = 0;
+  virtual void construct() {}
 
   [[nodiscard]] static auto get_physical_time() noexcept -> TimePoint;
   [[nodiscard]] auto get_logical_time() const noexcept -> TimePoint;
diff --git a/include/reactor-sdk/ConfigParameters.hh b/include/reactor-sdk/ConfigParameters.hh
new file mode 100644
index 00000000..84eaccaf
--- /dev/null
+++ b/include/reactor-sdk/ConfigParameters.hh
@@ -0,0 +1,135 @@
+#pragma once
+
+#include <map>
+#include <string>
+#include <variant>
+
+namespace sdk
+{
+
+template <typename T, typename = void>
+struct is_comparable : std::false_type {};
+
+template <typename T>
+struct is_comparable<
+    T,
+    std::void_t<decltype(std::declval<T>() < std::declval<T>())>
+> : std::true_type {};
+
+extern std::map<std::string, std::string> type_convert;
+template <typename T>
+struct ParameterMetadata;
+
+class ConfigParameterBase {
+protected:
+    virtual void pull_config() = 0;
+    virtual void display() = 0;
+    virtual int validate() = 0;
+
+public:
+    virtual ~ConfigParameterBase() = default;
+    virtual int pull_config_parameter(const bool &is_heterogeneous, const std::string &key, void *user_param, const std::type_info& ti) = 0;
+
+    template<typename T>
+    int PullConfigParameter(const bool &is_heterogeneous, const std::string &key, ParameterMetadata<T>* user_param) {
+        return pull_config_parameter(is_heterogeneous, key, static_cast<void*>(user_param), typeid(T));
+    }
+    friend class Environment;
+};
+
+template <typename T>
+struct ConfigParameterMetadata {
+    std::vector<T> values;
+
+    ConfigParameterMetadata(std::initializer_list<T> val) : values(val) {}
+};
+
+template <typename... ParameterValueType>
+class ConfigParameter : public ConfigParameterBase {
+public:
+    using ParameterValue = std::variant<ConfigParameterMetadata<ParameterValueType>...>;
+    using ParametersMap = std::map<std::string, ParameterValue>;
+
+    virtual ParametersMap homogeneous_config() = 0;
+    virtual ParametersMap heterogeneous_config() = 0;
+    int pull_config_parameter(const bool &is_heterogeneous, const std::string &key, void *user_param, const std::type_info& ti) override {
+        std::map<std::string, ParameterValue> *param_map = is_heterogeneous ? &hetero_param_map : &homoge_param_map;
+        std::set<std::string> *invalid_keys = is_heterogeneous ? &hetero_invalid_keys : &homoge_invalid_keys;
+        auto itr_system = param_map->find(key);
+        if (itr_system != param_map->end()) {
+            auto v_it = invalid_keys->find(key);
+            if (v_it != invalid_keys->end()) {
+                invalid_keys->erase(v_it);
+            }
+            std::visit([is_heterogeneous, user_param, &ti, key](auto&& system_param) {
+                using ContainerType = std::decay_t<decltype(system_param.values)>;
+                using U = typename ContainerType::value_type;
+
+                if (ti == typeid(U)) {
+                    ParameterMetadata<U>* param = static_cast<ParameterMetadata<U>*>(user_param);
+                    if constexpr (is_comparable<U>::value && !std::is_same<U, std::string>::value) {
+                        if ((system_param.values[0] < param->min_value) ||
+                            (system_param.values[0] > param->max_value)) {
+                            reactor::log::Error() << "Error: " << ((is_heterogeneous) ? "Heterogeneous Map" : "Homogeneous Map") << " -- Range mismatch for parameter name: " << key << " value:" << system_param.values[0] <<
+                                " min_value:" << param->min_value << " max_value:" << param->max_value;
+                            std::exit(EXIT_FAILURE);
+                        }
+                    }
+                    param->value = system_param.values[0];
+
+                } else {
+                    reactor::log::Error() << "Error: Type mismatch for parameter name: " << key << "\n"
+                          << "Expected type: " << type_convert[ti.name()]
+                          << ", Provided type: " << type_convert[typeid(U).name()];
+                    std::exit(EXIT_FAILURE);
+                }
+            }, itr_system->second);
+            return 0;
+        }
+        return -1;
+    }
+
+protected:
+    std::map<std::string, ParameterValue> homoge_param_map;
+    std::set<std::string> homoge_invalid_keys;
+    std::map<std::string, ParameterValue> hetero_param_map;
+    std::set<std::string> hetero_invalid_keys;
+    void pull_config() override {
+        homoge_param_map = homogeneous_config();
+        for (const auto& entry : homoge_param_map) {
+            bool result = homoge_invalid_keys.insert(entry.first).second;
+            assert(result);
+        }
+
+        hetero_param_map = heterogeneous_config();
+        for (const auto& entry : hetero_param_map) {
+            bool result = hetero_invalid_keys.insert(entry.first).second;
+            assert(result);
+        }
+    }
+
+    int validate() override {
+        for (const auto &key : hetero_invalid_keys) {
+            reactor::log::Error() << "Heterogeneous Invalid key:" << key << "\n";
+        }
+
+        for (const auto &key : homoge_invalid_keys) {
+            reactor::log::Error() << "Homogeneous Invalid key:" << key << "\n";
+        }
+        return (hetero_invalid_keys.size() + homoge_invalid_keys.size());
+    }
+
+    void display() override {
+        for (const auto& entry : hetero_param_map) {
+            reactor::log::Debug() << "Parameter: " << entry.first;
+
+            std::visit([](auto&& param) {
+                for (auto val : param.values) {
+                    reactor::log::Debug() << "Value: " << val;
+                }
+            }, entry.second);
+        }
+    }
+};
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/Environment.hh b/include/reactor-sdk/Environment.hh
new file mode 100644
index 00000000..2653766b
--- /dev/null
+++ b/include/reactor-sdk/Environment.hh
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include "ConfigParameters.hh"
+
+namespace sdk
+{
+
+class Reactor;
+class Environment: public reactor::Environment {
+private:
+    std::set<Reactor*> top_tier_reactors;
+    ConfigParameterBase *config_parameters;
+    bool cfg_gen = false;
+    
+public:
+    Environment(ConfigParameterBase *sys_param = nullptr, unsigned int num_workers = 1, bool fast_fwd_execution = true,
+                       const reactor::Duration& timeout = reactor::Duration::max(), bool cfg_gen = false);
+
+    Environment(const Environment&) = delete;
+    Environment& operator=(const Environment&) = delete;
+    void run();
+
+    void add_reactor (Reactor* reactor) {
+        bool result = top_tier_reactors.insert(reactor).second;
+        reactor_assert(result);
+    }
+
+    ConfigParameterBase *get_config_params() { return config_parameters; }
+
+    friend class SystemParameterBase;
+};
+    
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/InputPort.hh b/include/reactor-sdk/InputPort.hh
new file mode 100644
index 00000000..e7891799
--- /dev/null
+++ b/include/reactor-sdk/InputPort.hh
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+
+namespace sdk
+{
+
+template <typename T>
+class Input : public reactor::Input<T> {
+    class WiringProxy {
+        public:
+            WiringProxy(Input& origin) : origin(origin) {}
+
+            void operator>(Input<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>>(MultiportInput<T>& input) {
+                origin.connect_fanout (input);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect(std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect_fanout(std::move(other_bank_ports));
+            }
+
+        private:
+            Input& origin;
+    };
+
+    void connect(Input<T>& input);
+    void connect(MultiportInput<T>& input);
+    void connect_fanout(MultiportInput<T>& input);
+
+    template <typename ReactorType>
+    void connect(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports);
+
+    template <typename ReactorType>
+    void connect_fanout(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports);
+
+public:
+    using value_type = T;
+    Input(const std::string& name, reactor::Reactor* container)
+      : reactor::Input<T>(name, container) {}
+
+    Input(Input&&) noexcept = default;
+    ~Input() {}
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+};
+    
+} // namespace sdk
+
+#include "impl/InputPort_wiring_impl.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/Misc.hh b/include/reactor-sdk/Misc.hh
new file mode 100644
index 00000000..55289d1a
--- /dev/null
+++ b/include/reactor-sdk/Misc.hh
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include <iomanip>
+
+namespace sdk
+{
+
+class Reactor;
+
+template<typename T>
+using LogicalAction = reactor::LogicalAction<T>;
+
+using Startup = reactor::StartupTrigger;
+using Shutdown = reactor::ShutdownTrigger;
+
+using Duration = reactor::Duration;
+using TimePoint = reactor::TimePoint;
+
+#define select_default(obj) &obj[0]
+
+template <typename T>
+struct inspect_function_args;
+
+template <typename Ret, typename Class, typename... Args>
+struct inspect_function_args<Ret(Class::*)(Args...)> {
+    static constexpr size_t nargs = sizeof...(Args);
+};
+
+template <typename Func, typename Object>
+auto bind_function(Object* obj, Func&& func) {
+    constexpr size_t nargs = inspect_function_args<Func>::nargs;
+
+    if constexpr (nargs == 0) {
+        static_assert(nargs > 0, "Reactors must have one or more parameters");
+        return nullptr;
+    } else if constexpr (nargs == 1) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1);
+    } else if constexpr (nargs == 2) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2);
+    } else if constexpr (nargs == 3) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+    } else if constexpr (nargs == 4) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
+    } else if constexpr (nargs == 5) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5);
+    } else if constexpr (nargs == 6) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6);
+    } else if constexpr (nargs == 7) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7);
+    } else if constexpr (nargs == 8) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8);
+    } else if constexpr (nargs == 9) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9);
+    } else if constexpr (nargs == 10) {
+        return std::bind(std::forward<Func>(func), obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9, std::placeholders::_10);
+    } else {
+        static_assert(nargs <= 10, "This needs to be extended as per requirement of more parameters");
+        return nullptr;
+    }
+
+}
+
+#define pass_function(func) \
+    bind_function(this, &std::decay_t<decltype(*this)>::func)
+
+class Timer : public reactor::Timer {
+    std::string name;
+    Reactor *reactor;
+public:
+    Timer(const std::string& name, Reactor* container)
+        : reactor::Timer(name, (reactor::Reactor *) container), name (name), reactor (container){}
+
+    void set_timer (Duration period = Duration::zero(), Duration offset = Duration::zero()) {
+        period_ = period;
+        offset_ = offset;
+    }
+
+    Timer(Timer&&) noexcept = default;
+};
+
+inline auto operator<<(std::ostream& os, Duration dur) -> std::ostream& {
+  os << dur.count() << " nsecs";
+  return os;
+}
+
+constexpr std::size_t TIME_TO_STR_BUFFER_SIZE_{20};
+constexpr std::size_t NANOSECONDS_IN_ONE_SECOND_{1'000'000'000UL};
+constexpr std::size_t NANOSECOND_DIGITS_{9};
+
+inline auto operator<<(std::ostream& os, TimePoint tp) -> std::ostream& {
+  std::array<char, TIME_TO_STR_BUFFER_SIZE_> buf{};
+  time_t time =
+      std::chrono::system_clock::to_time_t(std::chrono::time_point_cast<std::chrono::system_clock::duration>(tp));
+  auto res = std::strftime(buf.data(), sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&time));
+  auto epoch = std::chrono::duration_cast<Duration>(tp.time_since_epoch());
+
+  if (res != 0) {
+    os << buf.data() << '.' << std::setw(NANOSECOND_DIGITS_) << std::setfill('0')
+       << epoch.count() % NANOSECONDS_IN_ONE_SECOND_;
+  } else {
+    os << "[INVALID TIME]";
+  }
+
+  return os;
+}
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/MultiportInput.hh b/include/reactor-sdk/MultiportInput.hh
new file mode 100644
index 00000000..3af18c49
--- /dev/null
+++ b/include/reactor-sdk/MultiportInput.hh
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <string>
+#include "reactor-cpp/reactor-cpp.hh"
+
+namespace sdk
+{
+
+template<typename T>
+class Input;
+
+class Reactor;
+
+template<typename T>
+class MultiportInput : public reactor::ModifableMultiport<Input<T>> {
+    size_t n_inputs;
+    std::string name;
+    Reactor *reactor;
+
+    class WiringProxy {
+        public:
+            WiringProxy(MultiportInput& origin) : origin(origin) {}
+
+            void operator>(Input<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+        private:
+            MultiportInput& origin;
+    };
+
+    void connect(Input<T>& input);
+    void connect(MultiportInput<T>& input);
+
+public:
+    using value_type = T;
+    MultiportInput(const std::string& name, Reactor* container)
+        : name (name), reactor (container) {}
+
+    void set_width (int width)
+    {
+        this->reserve(width);
+        n_inputs = width;
+        for (int idx = 0; idx < width; idx++) {
+            std::string input_name = name + "_" + std::to_string(idx);
+            this->emplace_back(input_name, reactor);
+        }
+    }
+
+    MultiportInput(MultiportInput&&) noexcept = default;
+    auto get_nports() -> int { return n_inputs; }
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+};
+
+    
+} // namespace sdk
+
+#include "impl/InputMultiport_wiring_impl.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/MultiportOutput.hh b/include/reactor-sdk/MultiportOutput.hh
new file mode 100644
index 00000000..6d076fcf
--- /dev/null
+++ b/include/reactor-sdk/MultiportOutput.hh
@@ -0,0 +1,108 @@
+#pragma once
+
+#include <string>
+#include "reactor-cpp/reactor-cpp.hh"
+
+namespace sdk
+{
+
+template<typename T>
+class Input;
+
+template<typename T>
+class Output;
+
+template<typename T>
+class MultiportOutput;
+
+template<typename T>
+class MultiportInput;
+
+class Reactor;
+
+template<typename T>
+class MultiportOutput : public reactor::ModifableMultiport<Output<T>> {
+    size_t n_inputs;
+    std::string name;
+    Reactor *reactor;
+    class WiringProxy {
+    public:
+        WiringProxy(MultiportOutput& origin) : origin(origin) {}
+
+        void operator>(Input<T>& input) {
+            origin.connect (input);
+        }
+
+        void operator>(Output<T>& input) {
+            origin.connect (input);
+        }
+
+        void operator>(MultiportInput<T>& input) {
+            origin.connect (input);
+        }
+
+        void operator>(MultiportOutput<T>& input) {
+            origin.connect (input);
+        }
+
+        template <typename ReactorType>
+        void operator>(std::pair<std::vector<std::unique_ptr<ReactorType>>*, Input<T> ReactorType::*> connections)
+        {
+            origin.connect (connections.first, connections.second);
+        }
+
+        template <typename OtherReactorType>
+        void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+            origin.connect(std::move(other_bank_ports));
+        }
+
+        template <typename OtherReactorType>
+        void operator>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+            origin.connect(std::move(other_bank_ports));
+        }
+
+    private:
+        MultiportOutput& origin;
+    };
+
+    void connect(Input<T>& input);
+    void connect(Output<T>& input);
+    void connect(MultiportInput<T>& input);
+    void connect(MultiportOutput<T>& input);
+
+    template <typename ReactorType>
+    void connect(std::vector<std::unique_ptr<ReactorType>>* reactors, Input<T> ReactorType::*member);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports);
+
+public:
+    using value_type = T;
+    MultiportOutput(const std::string& name, Reactor* container)
+        : name (name), reactor (container) {}
+
+    void set_width (int width)
+    {
+        this->reserve(width);
+        n_inputs = width;
+        for (int idx = 0; idx < width; idx++) {
+            std::string input_name = name + "_" + std::to_string(idx);
+            this->emplace_back(input_name, reactor);
+        }
+    }
+
+    MultiportOutput(MultiportOutput&&) noexcept = default;
+    auto get_nports() -> int { return n_inputs; }
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+};
+
+    
+} // namespace sdk
+
+#include "impl/OutputMultiport_wiring_impl.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/OutputPort.hh b/include/reactor-sdk/OutputPort.hh
new file mode 100644
index 00000000..40e01b55
--- /dev/null
+++ b/include/reactor-sdk/OutputPort.hh
@@ -0,0 +1,113 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+
+namespace sdk
+{
+
+template<typename T>
+class Input;
+
+template<typename T>
+class Output;
+
+template<typename T>
+class MultiportOutput;
+
+template<typename T>
+class MultiportInput;
+
+class Reactor;
+
+template <typename T>
+class Output : public reactor::Output<T> {
+    std::set<Output<T>*> accumulated;
+    bool is_accumulated = false;
+
+    class WiringProxy {
+        public:
+            WiringProxy(Output& origin) : origin(origin) {}
+
+            void operator>(Input<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(Output<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>>(MultiportInput<T>& input) {
+                origin.connect_fanout (input);
+            }
+
+            template <typename ReactorType>
+            void operator>(std::pair<std::vector<std::unique_ptr<ReactorType>>*, Input<T> ReactorType::*> connections)
+            {
+                origin.connect (connections.first, connections.second);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect(std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect(std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect_fanout(std::move(other_bank_ports));
+            }
+
+        private:
+            Output& origin;
+    };
+
+    void connect(Input<T>& input);
+    void connect(Output<T>& input);
+    void connect(MultiportInput<T>& input);
+    void connect_fanout(MultiportInput<T>& input);
+
+    template <typename ReactorType>
+    void connect(std::vector<std::unique_ptr<ReactorType>>* reactors, Input<T> ReactorType::*member);
+
+    template <typename ReactorType>
+    void connect(ReactorBankInputPort<ReactorType, T> &&other_bank_ports);
+
+    template <typename ReactorType>
+    void connect(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports);
+
+    template <typename ReactorType>
+    void connect_fanout(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports);
+
+public:
+    using value_type = T;
+    Output(const std::string& name, reactor::Reactor* container)
+      : reactor::Output<T>(name, container) {}
+    
+    ~Output() {}
+
+    Output(Output&&) noexcept = default;
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+
+    Output<T>& operator+(Output<T> &output) {
+        [[maybe_unused]] bool result = accumulated.insert(&output).second;
+        reactor_assert(result);
+        is_accumulated = true;
+        return *this;
+    }
+};
+
+    
+} // namespace sdk
+
+#include "impl/OutputPort_wiring_impl.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/Reaction.hh b/include/reactor-sdk/Reaction.hh
new file mode 100644
index 00000000..54da081e
--- /dev/null
+++ b/include/reactor-sdk/Reaction.hh
@@ -0,0 +1,365 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include "ReactionBase.hh"
+#include "Reactor.hh"
+
+namespace sdk
+{
+
+template <typename Fn, typename InputTuple, typename DependencyTuple, typename OutputTuple>
+class Reaction;
+
+template <typename T, template <typename...> class Template>
+struct is_specialization : std::false_type
+{
+};
+
+template <typename... Args, template <typename...> class Template>
+struct is_specialization<Template<Args...>, Template> : std::true_type
+{
+};
+
+template <typename T, template <typename...> class Template>
+inline constexpr bool is_specialization_v = is_specialization<T, Template>::value;
+
+// fix for gcc < 13
+template <typename T>
+constexpr bool templated_false = false;
+
+template <typename InputTuple, typename DependencyTuple, typename OutputTuple>
+class ReactionOutput: public ReactionBase
+{
+private:
+    InputTuple input_triggers;
+    DependencyTuple dependencies;
+    OutputTuple output_triggers;
+
+public:
+    explicit ReactionOutput(std::string name, Reactor *parent, InputTuple inputs, DependencyTuple deps, OutputTuple outputs)
+        : ReactionBase (name, parent), input_triggers(std::move(inputs)), dependencies(std::move(deps)), output_triggers(std::move(outputs)) {}
+    ~ReactionOutput() {}
+
+    template <typename Fn>
+    Reaction<Fn, InputTuple, DependencyTuple, OutputTuple> &function(Fn func)
+    {
+        if constexpr (std::is_bind_expression<Fn>::value) {
+        }
+        else if (sizeof(func) != sizeof(void*)) {
+            reactor::log::Error() << "Reactor: " << reactor->fqn() << " Reaction: " << name << " Accesses variables outside of its scope";
+            exit(EXIT_FAILURE);
+        }
+
+        auto ReactionRef = std::make_shared<Reaction<Fn, InputTuple, DependencyTuple, OutputTuple>> (name, reactor, std::move(input_triggers), std::move(dependencies), std::move(output_triggers), std::forward<Fn>(func));
+        ReactionRef->execute();
+        return *ReactionRef;
+    }
+};
+
+template <typename InputTuple, typename DependencyTuple>
+class ReactionDependency: public ReactionBase
+{
+private:
+    InputTuple input_triggers;
+    DependencyTuple dependencies;
+
+public:
+    explicit ReactionDependency(std::string name, Reactor *parent, InputTuple inputs, DependencyTuple deps)
+        : ReactionBase (name, parent), input_triggers(inputs), dependencies(std::move(deps)) {}
+    ~ReactionDependency() {}
+
+    template <typename... Outputs>
+    ReactionOutput<InputTuple, DependencyTuple, std::tuple<Outputs...>> &effects(Outputs&&... outputs)
+    {
+        auto output_tuple = std::make_tuple(outputs...);
+        auto ReactionOutputRef = std::make_shared<ReactionOutput<InputTuple, DependencyTuple, std::tuple<Outputs...>>> (name, reactor, std::move(input_triggers), std::move(dependencies), std::move(output_tuple));
+        next = ReactionOutputRef;
+        return *ReactionOutputRef;
+    }
+};
+
+template <typename InputTuple>
+class ReactionInput: public ReactionBase
+{
+private:
+    InputTuple input_triggers;
+
+public:
+    explicit ReactionInput(std::string name, Reactor *parent, InputTuple inputs)
+        : ReactionBase (name, parent), input_triggers(std::move(inputs)) {}
+    ~ReactionInput() {}
+
+    template <typename... Dependencies>
+    ReactionDependency<InputTuple, std::tuple<Dependencies...>> &dependencies(Dependencies&&... deps)
+    {
+        auto deps_tuple = std::make_tuple(deps...);
+        auto ReactionDependenciesRef = std::make_shared<ReactionDependency<InputTuple, std::tuple<Dependencies...>>> (name, reactor, std::move(input_triggers), std::move(deps_tuple));
+        next = ReactionDependenciesRef;
+        return *ReactionDependenciesRef;
+    }
+};
+
+class ReactionName: public ReactionBase {
+public:
+    explicit ReactionName(std::string name, Reactor *parent)
+        : ReactionBase (name, parent) {}
+    ~ReactionName() = default;
+
+    template <typename... Inputs>
+    ReactionInput<std::tuple<Inputs...>> &triggers(Inputs&&... inputs)
+    {
+        auto input_tuple = std::make_tuple(inputs...);
+        auto ReactionInputRef = std::make_shared<ReactionInput<std::tuple<Inputs...>>> (name, reactor, std::move(input_tuple));
+        next = ReactionInputRef;
+        return *ReactionInputRef;
+    }
+};
+
+template <typename Fn, typename InputTuple, typename DependencyTuple, typename OutputTuple>
+class Reaction: public ReactionBase
+{
+private:
+    InputTuple input_triggers;
+    DependencyTuple dependencies;
+    OutputTuple output_triggers;
+    Fn user_function;
+    std::unique_ptr<reactor::Reaction> reaction;
+
+    template <typename Reaction, typename Trigger>
+    void set_input_trigger(Reaction &reaction, Trigger &&trigger)
+    {
+        if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, MultiportInput>)
+        {
+            for (auto& port : *trigger) {
+                reaction.declare_trigger(&port);
+            }
+        }
+        else if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, MultiportOutput>)
+        {
+            for (auto& port : *trigger) {
+                reaction.declare_trigger(&port);
+            }
+        }
+        else {
+            reaction.declare_trigger(trigger);
+        }
+    }
+
+    template <typename Reaction, typename... Triggers>
+    void set_input_triggers(std::unique_ptr<Reaction> &reaction, const std::tuple<Triggers...> &inputs)
+    {
+        std::apply([this, &reaction](auto &&...input)
+            {
+                (void)this;
+                (..., set_input_trigger(*reaction, std::forward<decltype(input)>(input)));
+            },
+            inputs);
+    }
+
+    template <typename Reaction, typename Trigger>
+    void set_dependency(Reaction &reaction, Trigger &&trigger)
+    {
+        if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, MultiportInput>)
+        {
+            for (auto& port : *trigger) {
+                reaction.declare_dependency(&port);
+            }
+        }
+        else {
+            reaction.declare_dependency(trigger);
+        }
+    }
+
+    template <typename Reaction, typename... Dependencies>
+    void set_dependencies(std::unique_ptr<Reaction> &reaction, const std::tuple<Dependencies...> &deps)
+    {
+        std::apply([this, &reaction](auto &&...dep)
+            {
+                (void)this;
+                (..., set_dependency(*reaction, std::forward<decltype(dep)>(dep)));
+            },
+            deps);
+    }
+
+    template <typename Reaction, typename Trigger>
+    void set_output_trigger(Reaction &reaction, Trigger &&trigger)
+    {
+        if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, Output>)
+        {
+            reaction.declare_antidependency(trigger);
+        } else if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, Input>)
+        {
+            reaction.declare_antidependency(trigger);
+        }
+        else if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, reactor::LogicalAction>)
+        {
+            reaction.declare_schedulable_action(trigger);
+        }
+        else if constexpr (is_specialization_v<std::remove_pointer_t<std::decay_t<Trigger>>, MultiportOutput>)
+        {
+            for (auto& port : *trigger) {
+                reaction.declare_antidependency(&port);
+            }
+        }
+        else
+        {
+            static_assert(templated_false<Trigger>, "Unsupported trigger type");
+        }
+    }
+
+    template <typename Reaction, typename... Triggers>
+    void set_output_triggers(std::unique_ptr<Reaction> &reaction, const std::tuple<Triggers...> &outputs)
+    {
+        std::apply([this, &reaction](auto &&...output)
+            {
+                (void)this;
+                (..., set_output_trigger(*reaction, std::forward<decltype(output)>(output)));
+            },
+            outputs);
+    }
+
+public:
+    Reaction(std::string name, Reactor *parent, InputTuple inputs, DependencyTuple deps, OutputTuple outputs, Fn func)
+        : ReactionBase(name, parent), input_triggers(std::move(inputs)), dependencies(std::move(deps)), output_triggers(std::move(outputs)), user_function(std::forward<Fn>(func)) { /* std::cout << "Creating Reaction\n"; */ }
+    ~Reaction() {}
+
+    void execute () {
+        int priority = reactor->get_priority();
+        reactor->add_to_reaction_map(name, shared_from_this());
+        reactor->validate_reaction (user_function, input_triggers, dependencies, output_triggers);
+
+        auto reactor_func = [func = std::move(user_function), this]()
+        {
+            (void)this;
+            auto apply_to_dereferenced = [](auto&& func, auto&& tuple) {
+                return std::apply(
+                    [&](auto*... ptrs) {
+                        return std::invoke(std::forward<decltype(func)>(func), (*ptrs)...);
+                    },
+                    std::forward<decltype(tuple)>(tuple));
+            };
+
+            apply_to_dereferenced(func, std::tuple_cat(this->input_triggers, this->dependencies, this->output_triggers));
+        };
+
+        reaction = std::make_unique<reactor::Reaction>(name, priority, reactor, reactor_func);
+
+        set_input_triggers(reaction, input_triggers);
+        set_dependencies(reaction, dependencies);
+        set_output_triggers(reaction, output_triggers);
+    }
+
+    template <typename Dfn>
+    void deadline(reactor::Duration deadline_period, Dfn fn)
+    {
+        reactor->validate_reaction (fn, input_triggers, dependencies, output_triggers);
+
+        auto deadline_func = [func = std::move(fn), this]()
+        {
+            (void)this;
+            auto apply_to_dereferenced = [](auto&& func, auto&& tuple) {
+                return std::apply(
+                    [&](auto*... ptrs) {
+                        return std::invoke(std::forward<decltype(func)>(func), (*ptrs)...);
+                    },
+                    std::forward<decltype(tuple)>(tuple));
+            };
+
+            apply_to_dereferenced(func, std::tuple_cat(this->input_triggers, this->dependencies, this->output_triggers));
+        };
+
+        reaction->set_deadline(deadline_period, deadline_func);
+    }
+};
+
+template <typename ReactorType>
+class ReactionChamberParameterless : public ReactionBase {
+    ReactorType *reactor_;
+protected:
+    const size_t &bank_index = reactor_->bank_index;
+public:
+    ReactionChamberParameterless(Reactor *owner)
+        : ReactionBase("reaction-internals-parameterless", owner), reactor_((ReactorType*) owner) {
+        reactor_->add_reaction_internals(this);
+    }
+
+    ReactionName &reaction (const std::string name) {
+        auto ReactionNameRef = std::make_shared<ReactionName>(name, reactor);
+        next = ReactionNameRef;
+        return *ReactionNameRef;
+    }
+
+    virtual void add_reactions(ReactorType *reactor) = 0;
+    virtual void assemble() override {
+        add_reactions(reactor_);
+    }
+
+    auto fqn() const noexcept -> const std::string& { return reactor_->fqn(); }
+    auto get_elapsed_logical_time() const noexcept -> Duration { return reactor_->get_elapsed_logical_time(); }
+    auto get_microstep() const noexcept -> reactor::mstep_t { return reactor_->get_microstep(); }
+    auto get_elapsed_physical_time() const noexcept -> Duration { return reactor_->get_elapsed_physical_time(); }
+    auto get_physical_time() noexcept -> reactor::TimePoint { return reactor_->get_physical_time(); }
+    auto get_logical_time() const noexcept -> reactor::TimePoint { return reactor_->get_logical_time(); }
+    auto get_tag() const noexcept -> reactor::Tag { return reactor_->get_tag(); }
+    void request_stop() { reactor_->environment()->sync_shutdown(); }
+};
+
+template <typename ReactorType, typename ParameterType>
+class ReactionChamber : public ReactionBase {
+    ReactorType *reactor_;
+
+protected:
+    const ParameterType &parameters;
+    const size_t &bank_index = reactor_->bank_index;
+public:
+    ReactionChamber(Reactor *owner, ParameterType &param)
+        : ReactionBase("reaction-internals", owner), reactor_((ReactorType*) owner), parameters(param) {
+        reactor_->add_reaction_internals(this);
+    }
+
+    ReactionName &reaction (const std::string name) {
+        auto ReactionNameRef = std::make_shared<ReactionName>(name, reactor);
+        next = ReactionNameRef;
+        return *ReactionNameRef;
+    }
+
+    virtual void add_reactions(ReactorType *reactor) = 0;
+    virtual void assemble() override {
+        add_reactions(reactor_);
+    }
+
+    auto fqn() const noexcept -> const std::string& { return reactor_->fqn(); }
+    auto get_elapsed_logical_time() const noexcept -> Duration { return reactor_->get_elapsed_logical_time(); }
+    auto get_microstep() const noexcept -> reactor::mstep_t { return reactor_->get_microstep(); }
+    auto get_elapsed_physical_time() const noexcept -> Duration { return reactor_->get_elapsed_physical_time(); }
+    auto get_physical_time() noexcept -> reactor::TimePoint { return reactor_->get_physical_time(); }
+    auto get_logical_time() const noexcept -> reactor::TimePoint { return reactor_->get_logical_time(); }
+    auto get_tag() const noexcept -> reactor::Tag { return reactor_->get_tag(); }
+    void request_stop() { reactor_->environment()->sync_shutdown(); }
+};
+
+#define REACTION_SCOPE_START(ReactorType, ParamType) \
+class Internals : public ReactionChamber<ReactorType, ParamType> { \
+public: \
+    Internals(Reactor *reactor, ParamType &params) \
+        : ReactionChamber<ReactorType, ParamType>(reactor, params) {} \
+private:
+
+#define REACTION_SCOPE_END(reactor, param) \
+}; \
+Internals reaction_internals{reactor, param};
+
+#define REACTION_SCOPE_START_NO_PARAMS(ReactorType) \
+class Internals : public ReactionChamberParameterless<ReactorType> { \
+public: \
+    Internals(Reactor *reactor) \
+        : ReactionChamberParameterless<ReactorType>(reactor) {} \
+private:
+
+#define REACTION_SCOPE_END_NO_PARAMS(reactor) \
+}; \
+Internals reaction_internals{reactor};
+
+#define REACTION_SCOPE(ReactorType) ReactorType::Internals
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/ReactionBase.hh b/include/reactor-sdk/ReactionBase.hh
new file mode 100644
index 00000000..2701c67a
--- /dev/null
+++ b/include/reactor-sdk/ReactionBase.hh
@@ -0,0 +1,22 @@
+#pragma once
+
+namespace sdk
+{
+class Reactor;
+
+class ReactionBase : public std::enable_shared_from_this<ReactionBase>
+{
+public:
+    Reactor *reactor;
+    std::shared_ptr<ReactionBase> next;
+    std::string name;
+
+public:
+    ReactionBase(std::string name, Reactor *parent)
+    : reactor(parent), next(nullptr), name(name) {}
+    virtual ~ReactionBase() = default;
+
+    virtual void assemble() {}
+};
+    
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/Reactor.hh b/include/reactor-sdk/Reactor.hh
new file mode 100644
index 00000000..f4354b59
--- /dev/null
+++ b/include/reactor-sdk/Reactor.hh
@@ -0,0 +1,155 @@
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include <string.h>
+#include "ReactionBase.hh"
+#include "SystemParameterBase.hh"
+#include "Environment.hh"
+
+namespace sdk
+{
+
+template <typename InputTuple>
+class ReactionInput;
+
+template <typename Fn, typename InputTuple, typename DependencyTuple, typename OutputTuple>
+class Reaction;
+
+template<typename T>
+class Input;
+
+template<typename T>
+class Output;
+
+template<typename T>
+class MultiportOutput;
+
+template<typename T>
+class MultiportInput;
+
+class Timer;
+
+template <typename T>
+struct trigger_value_type;
+
+template <typename T>
+struct trigger_value_type<reactor::LogicalAction<T> *>
+{
+    using type = reactor::LogicalAction<T>&;
+};
+
+template <typename T>
+struct trigger_value_type<Input<T> *>
+{
+    using type = Input<T>&;
+};
+
+template <typename T>
+struct trigger_value_type<MultiportInput<T> *>
+{
+    using type = MultiportInput<T>&;
+};
+
+template <typename T>
+struct trigger_value_type<MultiportOutput<T> *>
+{
+    using type = MultiportOutput<T>&;
+};
+
+template <typename T>
+struct trigger_value_type<Output<T> *>
+{
+    using type = Output<T>&;
+};
+
+template <>
+struct trigger_value_type<reactor::StartupTrigger *>
+{
+    using type = reactor::StartupTrigger&;
+};
+
+template <>
+struct trigger_value_type<reactor::ShutdownTrigger *>
+{
+    using type = reactor::ShutdownTrigger&;
+};
+
+template <>
+struct trigger_value_type<Timer *>
+{
+    using type = Timer&;
+};
+
+class Reactor : public reactor::Reactor
+{
+protected:
+    reactor::StartupTrigger startup{"startup", this};
+    reactor::ShutdownTrigger shutdown{"shutdown", this};
+
+private:
+    size_t bank_index_ = 0;
+    SystemParameterBase *p_param = nullptr;
+    Environment *env{nullptr};
+    Reactor *parent{nullptr};
+    std::unordered_map<std::string, std::shared_ptr<ReactionBase>> reaction_map;
+    ReactionBase *reaction_internals_;
+    int priority = 1;
+    std::set<Reactor*> child_reactors;
+    std::string homog_name = "";
+
+    void add_child(Reactor* reactor);
+    void add_to_reaction_map (std::string &name, std::shared_ptr<ReactionBase> reaction);
+    int get_priority() { return priority++;}
+
+    template <typename Fn, typename... InputTriggers, typename... Dependencies, typename... OutputTriggers>
+    void validate_reaction(Fn func, std::tuple<InputTriggers...> inputs, std::tuple<Dependencies...> deps, std::tuple<OutputTriggers...> outputs) {
+        (void)func;
+        (void)inputs;
+        (void)deps;
+        (void)outputs;
+        static_assert(
+            std::is_invocable_v<
+                Fn,
+                typename trigger_value_type<InputTriggers>::type...,
+                typename trigger_value_type<Dependencies>::type...,
+                typename trigger_value_type<OutputTriggers>::type...
+                >,
+                "Reaction function parameters must match the declared input and output types.");
+    }
+
+    void populate_params(std::set<std::string> &types, std::map<std::string, std::string> &homog_map_entries, std::map<std::string, std::string> &hetero_map_entries);
+
+public:
+    const size_t &bank_index = bank_index_;
+
+    Reactor(const std::string &name, Environment *env);
+    Reactor(const std::string &name, Reactor *container);
+
+    void add_reaction_internals (ReactionBase* internals) {
+        reaction_internals_ = internals;
+    }
+
+    static std::string BankName(const std::string& name);
+    static std::string HomogName(const std::string& name);
+
+    void set_param (SystemParameterBase *param) { p_param = param; }
+
+    Environment *get_env() { return env; }
+
+    auto homog_fqn() const noexcept -> const std::string& { return homog_name; }
+
+    virtual void construction() = 0;
+    virtual void wiring() = 0;
+    void construct() override;
+    void assemble() override;
+
+    template <typename Fn, typename InputTuple, typename DependencyTuple, typename OutputTuple>
+    friend class Reaction;
+
+    template <typename ReactorType>
+    friend class ReactorBank;
+
+    friend class Environment;
+};
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/ReactorBank.hh b/include/reactor-sdk/ReactorBank.hh
new file mode 100644
index 00000000..afeee39c
--- /dev/null
+++ b/include/reactor-sdk/ReactorBank.hh
@@ -0,0 +1,699 @@
+#pragma once
+
+#include "Reactor.hh"
+
+namespace sdk
+{
+
+template<typename T>
+class Input;
+
+template<typename T>
+class Output;
+
+template<typename T>
+class MultiportOutput;
+
+template<typename T>
+class MultiportInput;
+
+template <typename ReactorType, typename T>
+class ReactorBankInputPort {
+public:
+    ReactorBankInputPort(std::vector<std::unique_ptr<ReactorType>>& reactors, Input<T> ReactorType::*member)
+        : reactors(reactors), member(member) {}
+    
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+    
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    Input<T> ReactorType::* get_member() { return member; }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    Input<T> ReactorType::*member;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankInputPortOffset {
+public:
+    ReactorBankInputPortOffset(std::vector<std::unique_ptr<ReactorType>>& reactors, std::ptrdiff_t offset)
+        : reactors(reactors), offset(offset) {}
+    
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+    
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    std::ptrdiff_t get_offset() { return offset; }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    std::ptrdiff_t offset;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankInputMultiPort {
+public:
+    ReactorBankInputMultiPort(std::vector<std::unique_ptr<ReactorType>> &reactors, MultiportInput<T> ReactorType::*member)
+        : reactors(reactors), member(member) {}
+
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    MultiportInput<T> ReactorType::*member;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankInputMultiPortOffset {
+public:
+    ReactorBankInputMultiPortOffset(std::vector<std::unique_ptr<ReactorType>>& reactors, std::ptrdiff_t offset)
+        : reactors(reactors), offset(offset) {}
+    
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+    
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    std::ptrdiff_t get_offset() { return offset; }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    std::ptrdiff_t offset;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankOutputPort {
+    class WiringProxy {
+        public:
+            WiringProxy(ReactorBankOutputPort& origin) : origin(origin) {}
+
+            void operator>(Input<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+        private:
+            ReactorBankOutputPort& origin;
+    };
+
+    void connect(Input<T>& input);
+
+    void connect(MultiportInput<T>& input);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports);
+
+public:
+    ReactorBankOutputPort(std::vector<std::unique_ptr<ReactorType>>& reactors, Output<T> ReactorType::*member)
+    : reactors(reactors), member(member) {}
+
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    Output<T> ReactorType::*member;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankOutputPortOffset {
+    class WiringProxy {
+        public:
+            WiringProxy(ReactorBankOutputPortOffset& origin) : origin(origin) {}
+
+            void operator>(Input<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            void operator>>(MultiportInput<T>& input) {
+                origin.connect_fanout (input);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+        private:
+            ReactorBankOutputPortOffset& origin;
+    };
+
+    void connect(Input<T>& input);
+    void connect(MultiportInput<T>& input);
+    void connect_fanout(MultiportInput<T>& input);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports);
+
+public:
+    ReactorBankOutputPortOffset(std::vector<std::unique_ptr<ReactorType>>& reactors, std::ptrdiff_t offset)
+        : reactors(reactors), offset(offset) {}
+
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    std::ptrdiff_t offset;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankOutputMultiPort {
+    class WiringProxy {
+        public:
+            WiringProxy(ReactorBankOutputMultiPort& origin) : origin(origin) {}
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+        private:
+            ReactorBankOutputMultiPort& origin;
+    };
+
+    void connect(MultiportInput<T>& input);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports);
+
+public:
+    ReactorBankOutputMultiPort(std::vector<std::unique_ptr<ReactorType>>& reactors, MultiportOutput<T> ReactorType::*member)
+        : reactors(reactors), member(member) {}
+
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    MultiportOutput<T> ReactorType::*member;
+};
+
+template <typename ReactorType, typename T>
+class ReactorBankOutputMultiPortOffset {
+    class WiringProxy {
+        public:
+            WiringProxy(ReactorBankOutputMultiPortOffset& origin) : origin(origin) {}
+
+            void operator>(MultiportInput<T>& input) {
+                origin.connect (input);
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+            template <typename OtherReactorType>
+            void operator>(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports) {
+                origin.connect (std::move(other_bank_ports));
+            }
+
+        private:
+            ReactorBankOutputMultiPortOffset& origin;
+    };
+
+    void connect(MultiportInput<T>& input);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports);
+
+    template <typename OtherReactorType>
+    void connect(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports);
+
+public:
+    ReactorBankOutputMultiPortOffset(std::vector<std::unique_ptr<ReactorType>>& reactors, std::ptrdiff_t offset)
+        : reactors(reactors), offset(offset) {}
+
+    using iterator = typename std::vector<std::unique_ptr<ReactorType>>::iterator;
+    using const_iterator = typename std::vector<std::unique_ptr<ReactorType>>::const_iterator;
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { return *reactors[index]->get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { return *reactors[index]->get(); }
+
+    auto begin() noexcept -> iterator { return reactors.begin(); };
+    auto begin() const noexcept -> const_iterator { return reactors.begin(); };
+    auto cbegin() const noexcept -> const_iterator { return reactors.cbegin(); };
+    auto end() noexcept -> iterator { return reactors.end(); };
+    auto end() const noexcept -> const_iterator { return reactors.end(); };
+    auto cend() const noexcept -> const_iterator { return reactors.cend(); };
+
+    auto size() const noexcept -> size_t { return reactors.size(); };
+    [[nodiscard]] auto empty() const noexcept -> bool { return reactors.empty(); };
+
+    WiringProxy operator--(int) {
+        return WiringProxy(*this);
+    }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>>& reactors;
+    std::ptrdiff_t offset;
+};
+
+template <typename ReactorType>
+class ReactorBank {
+public:
+    ReactorBank(const std::string &_name, Environment *env)
+        : name(_name), e_parent(env) {}
+
+    ReactorBank(const std::string &_name, Reactor *container)
+        : name(_name), r_parent(container) {}
+
+    void reserve(std::size_t size) noexcept {
+        reactors.reserve(size);
+    }
+
+    void create_reactor() {
+        assert (e_parent || r_parent);
+        std::string bank_name = name + "\r\n" + std::to_string(index);
+        if (e_parent) {
+            reactors.emplace_back(std::make_unique<ReactorType>(bank_name, e_parent));
+        } else {
+            reactors.emplace_back(std::make_unique<ReactorType>(bank_name, r_parent));
+        }
+        reactors.back()->bank_index_ = index++;
+    }
+
+    template <class... Args> void create_reactor(Args&&... args) noexcept {
+        assert (e_parent || r_parent);
+        std::string bank_name = name + "\r\n" + std::to_string(index);
+        if (e_parent) {
+            reactors.emplace_back(std::make_unique<ReactorType>(bank_name, e_parent, std::forward<Args>(args)...));
+        } else {
+            reactors.emplace_back(std::make_unique<ReactorType>(bank_name, r_parent, std::forward<Args>(args)...));
+        }
+        reactors.back()->bank_index_ = index++;
+    }
+
+    template <class... Args> void emplace_back(Args&&... args) noexcept {
+        reactors.emplace_back(std::forward<Args>(args)...);
+        reactors.back()->bank_index_ = index++;
+        reactors.back()->homog_name = reactors.back()->parent ? (reactors.back()->parent->homog_name + "." + name) : name;
+    }
+
+    template <typename T>
+    std::pair<std::vector<std::unique_ptr<ReactorType>>*, Input<T> ReactorType::*> operator()(Input<T> ReactorType::*member) {
+        return std::make_pair(&reactors, static_cast<Input<T> ReactorType::*>(member));
+    }
+
+    template <typename T>
+    ReactorBankInputPort<ReactorType, T> operator->*(Input<T> ReactorType::*member) {
+        return ReactorBankInputPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankInputPortOffset<ReactorType, T> operator->*(Input<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+        
+        return ReactorBankInputPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankInputPort<ReactorType, T> for_each(Input<T> ReactorType::*member) {
+        return ReactorBankInputPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankInputPortOffset<ReactorType, T> for_each(Input<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+        
+        return ReactorBankInputPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankInputMultiPort<ReactorType, T> operator->*(MultiportInput<T> ReactorType::*member) {
+        return ReactorBankInputMultiPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankInputMultiPortOffset<ReactorType, T> operator->*(MultiportInput<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankInputMultiPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankInputMultiPort<ReactorType, T> for_each(MultiportInput<T> ReactorType::*member) {
+        return ReactorBankInputMultiPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankInputMultiPortOffset<ReactorType, T> for_each(MultiportInput<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankInputMultiPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankOutputPort<ReactorType, T> operator->*(Output<T> ReactorType::*member) {
+        return ReactorBankOutputPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankOutputPortOffset<ReactorType, T> operator->*(Output<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankOutputPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankOutputPort<ReactorType, T> for_each(Output<T> ReactorType::*member) {
+        return ReactorBankOutputPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankOutputPortOffset<ReactorType, T> for_each(Output<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankOutputPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankOutputMultiPort<ReactorType, T> operator->*(MultiportOutput<T> ReactorType::*member) {
+        return ReactorBankOutputMultiPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankOutputMultiPortOffset<ReactorType, T> operator->*(MultiportOutput<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankOutputMultiPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    template <typename T>
+    ReactorBankOutputMultiPort<ReactorType, T> for_each(MultiportOutput<T> ReactorType::*member) {
+        return ReactorBankOutputMultiPort<ReactorType, T>(reactors, member);
+    }
+
+    template <typename T>
+    ReactorBankOutputMultiPortOffset<ReactorType, T> for_each(MultiportOutput<T> *member) {
+        std::size_t object_size = sizeof(ReactorType);
+        std::ptrdiff_t offset = -1;
+
+        for (auto &reactor : reactors) {
+            const char* base_ptr = reinterpret_cast<const char*>(reactor.get());
+            if ((reinterpret_cast<const char*>(member) >= base_ptr) && (reinterpret_cast<const char*>(member) < base_ptr + object_size)) {
+                offset = reinterpret_cast<const char*>(member) - base_ptr;
+                break;
+            }
+        }
+
+        if (offset < 0) {
+            std::cerr << "Member passed in is not a valid member\n";
+            reactor_assert(false);
+        }
+
+        return ReactorBankOutputMultiPortOffset<ReactorType, T>(reactors, offset);
+    }
+
+    ReactorBank& operator->() {
+        return *this;
+    }
+
+    auto operator[](std::size_t index) noexcept -> ReactorType& { assert (index < reactors.size()); return *reactors[index].get(); }
+    auto operator[](std::size_t index) const noexcept -> const ReactorType& { assert (index < reactors.size()); return *reactors[index].get(); }
+
+private:
+    std::vector<std::unique_ptr<ReactorType>> reactors;
+    size_t index = 0;
+    std::string name = "";
+    Reactor *r_parent{nullptr};
+    Environment *e_parent{nullptr};
+};
+
+    
+} // namespace sdk
+
+#include "impl/ReactorBankOutputPort_wiring_impl.hh"
+#include "impl/ReactorBankOutputMultiport_wiring_impl.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/SystemParameterBase.hh b/include/reactor-sdk/SystemParameterBase.hh
new file mode 100644
index 00000000..248b5802
--- /dev/null
+++ b/include/reactor-sdk/SystemParameterBase.hh
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <set>
+#include <map>
+namespace sdk
+{
+
+class SystemParameterBase {
+public:
+    virtual ~SystemParameterBase() = default;
+    virtual void fetch_config() = 0;
+    virtual void print() = 0;
+    virtual void populate_params(std::set<std::string> &types, std::map<std::string, std::string> &homog_map_entries, std::map<std::string, std::string> &hetero_map_entries) = 0;
+};
+    
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/SystemParameters.hh b/include/reactor-sdk/SystemParameters.hh
new file mode 100644
index 00000000..93cee5c6
--- /dev/null
+++ b/include/reactor-sdk/SystemParameters.hh
@@ -0,0 +1,152 @@
+#pragma once
+
+#include <string>
+#include <cxxabi.h>
+#include "SystemParameterBase.hh"
+#include "Environment.hh"
+#include "time_parser.hh"
+namespace sdk
+{
+
+inline std::string convertToString(const std::string& s) {
+    std::string annotated = "\"" + s + "\"";
+    return annotated;
+}
+
+template <typename T>
+typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type
+convertToString(const T& value) {
+    return std::to_string(value);
+}
+
+inline std::string time_to_string_(const reactor::Duration& dur) {
+  if (dur == reactor::Duration::max()) {
+    return "forever";
+  }
+
+  std::stringstream ss;
+  ss << dur.count() << "ns";
+  return ss.str();
+}
+
+inline std::string convertToString(const reactor::Duration& dur) {
+  return time_to_string_ (dur);
+}
+
+template <typename T>
+struct ParameterMetadata {
+    std::string name;
+    std::string description;
+    T min_value;
+    T max_value;
+    T value;
+    std::string type_name;
+};
+
+#define REACTOR_PARAMETER(Type, variable_name, description, min_value, max_value, default_value) \
+    ParameterMetadata<Type> variable_name = ParameterMetadata<Type>{ #variable_name, description, min_value, max_value, default_value, #Type }
+
+template <typename... ParameterValueType>
+class SystemParametersStandalone : public SystemParameterBase {
+public:
+    using ParameterValue = std::variant<ParameterMetadata<ParameterValueType>*...>;
+
+    struct MapEntry {
+        std::string alt_name;
+        ParameterValue param;
+    };
+
+    SystemParametersStandalone(Reactor *owner)
+    : reactor(owner), env(owner->get_env()) {
+        reactor->set_param (this);
+    }
+
+    void fetch_config() override {
+        if (env->get_config_params()) {
+            for (auto& entry : param_map) {
+                std::visit([&](auto* paramMetadataPtr) {
+                    env->get_config_params()->PullConfigParameter(false, entry.second.alt_name, paramMetadataPtr);
+                    env->get_config_params()->PullConfigParameter(true, entry.first, paramMetadataPtr);
+                }, entry.second.param);
+            }
+        }
+    }
+
+    void print() override {
+        for (const auto& entry : param_map) {
+            std::cout << "Parameter: " << entry.first << ", alt_name:" << entry.second.alt_name << ", ";
+
+            std::visit([](auto&& param) {
+                std::cout << "Description: " << param->description 
+                          << ", Value: " << param->value 
+                          << ", Value Type Key: " << typeid(param->value).name()
+                          << ", Value Type: " << param->type_name
+                          << std::endl;
+            }, entry.second.param);
+        }
+    }
+
+    template <typename... Args>
+    void register_parameters(Args&... args) {
+        register_parameters_(reactor->fqn(), reactor->homog_fqn(), args...);
+        // print();
+    }
+
+    void populate_params(std::set<std::string> &types, std::map<std::string, std::string> &homog_map_entries, std::map<std::string, std::string> &hetero_map_entries) {
+        for (const auto& entry : param_map) {
+            std::cout << "POPULATING: " << entry.first << ", alt_name:" << entry.second.alt_name << std::endl;            
+
+            std::visit([&](auto&& param) {
+                bool result = types.insert(param->type_name).second;
+                std::cout << "type:" <<  param->type_name << (result ? " PUSHED" : " SKIPPED") << std::endl;
+                if (homog_map_entries.find (entry.second.alt_name) == homog_map_entries.end()) {
+                    std::string homog_entry_str = "{ \"" + entry.second.alt_name + "\", ConfigParameterMetadata<" + param->type_name + "> {" + convertToString(param->value) + "} }";
+                    homog_map_entries[entry.second.alt_name] = homog_entry_str;
+                    std::cout << "homog-entry:" <<  homog_entry_str << " PUSHED" << std::endl;
+                } else {
+                    std::cout << "homog-entry:" <<  entry.second.alt_name << " SKIPPED" << std::endl;
+                }
+
+                if (hetero_map_entries.find (entry.first) == hetero_map_entries.end()) {
+                    std::string hetero_entry_str = "{ \"" + entry.first + "\", ConfigParameterMetadata<" + param->type_name + "> {" + convertToString(param->value) + "} }";
+                    hetero_map_entries[entry.first] = hetero_entry_str;
+                    std::cout << "hetero-entry:" <<  hetero_entry_str << " PUSHED" << std::endl;
+                } else {
+                    std::cout << "hetero-entry:" <<  entry.first << " SKIPPED" << std::endl;
+                }
+            }, entry.second.param);
+        }
+    }
+
+private:
+    std::map<std::string, MapEntry> param_map;
+    Reactor *reactor;
+    Environment *env;
+
+    template <typename T>
+    void register_parameter(const std::string& hetero_name, const std::string& homog_name, ParameterMetadata<T>& param) {
+        MapEntry entry = MapEntry {
+            .alt_name = homog_name,
+            .param = &param
+        };
+        param_map[hetero_name] = std::move(entry);
+    }
+
+    template <typename T, typename... Args>
+    void register_parameters_(const std::string& hetero_name, const std::string& homog_name, ParameterMetadata<T>& first, Args&... args) {
+        register_parameter(hetero_name + "." + first.name, homog_name + "." + first.name, first);
+        if constexpr (sizeof...(args) > 0) {
+            register_parameters_(hetero_name, homog_name, args...);
+        }
+    }
+};
+
+template <typename Defaults, typename... ParameterValueType>
+class SystemParameters : public SystemParametersStandalone<ParameterValueType...> {
+public:
+    Defaults defaults;
+    SystemParameters(Reactor *owner, Defaults &&param)
+    : SystemParametersStandalone<ParameterValueType...>(owner), defaults(std::forward<Defaults>(param)) {}
+};
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/cxxopts.hpp b/include/reactor-sdk/cxxopts.hpp
new file mode 100644
index 00000000..870755a2
--- /dev/null
+++ b/include/reactor-sdk/cxxopts.hpp
@@ -0,0 +1,2610 @@
+/*
+
+Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+#ifndef CXXOPTS_HPP_INCLUDED
+#define CXXOPTS_HPP_INCLUDED
+
+#include <cctype>
+#include <cstring>
+#include <exception>
+#include <iostream>
+#include <limits>
+#include <list>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+#include <algorithm>
+
+#if defined(__GNUC__) && !defined(__clang__)
+#  if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
+#    define CXXOPTS_NO_REGEX true
+#  endif
+#endif
+
+#ifndef CXXOPTS_NO_REGEX
+#  include <regex>
+#endif  // CXXOPTS_NO_REGEX
+
+// Nonstandard before C++17, which is coincidentally what we also need for <optional>
+#ifdef __has_include
+#  if __has_include(<optional>)
+#    include <optional>
+#    ifdef __cpp_lib_optional
+#      define CXXOPTS_HAS_OPTIONAL
+#    endif
+#  endif
+#endif
+
+#if __cplusplus >= 201603L
+#define CXXOPTS_NODISCARD [[nodiscard]]
+#else
+#define CXXOPTS_NODISCARD
+#endif
+
+#ifndef CXXOPTS_VECTOR_DELIMITER
+#define CXXOPTS_VECTOR_DELIMITER ','
+#endif
+
+#define CXXOPTS__VERSION_MAJOR 3
+#define CXXOPTS__VERSION_MINOR 0
+#define CXXOPTS__VERSION_PATCH 0
+
+#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6
+  #define CXXOPTS_NULL_DEREF_IGNORE
+#endif
+
+namespace cxxopts
+{
+  static constexpr struct {
+    uint8_t major, minor, patch;
+  } version = {
+    CXXOPTS__VERSION_MAJOR,
+    CXXOPTS__VERSION_MINOR,
+    CXXOPTS__VERSION_PATCH
+  };
+} // namespace cxxopts
+
+//when we ask cxxopts to use Unicode, help strings are processed using ICU,
+//which results in the correct lengths being computed for strings when they
+//are formatted for the help output
+//it is necessary to make sure that <unicode/unistr.h> can be found by the
+//compiler, and that icu-uc is linked in to the binary.
+
+#ifdef CXXOPTS_USE_UNICODE
+#include <unicode/unistr.h>
+
+namespace cxxopts
+{
+  using String = icu::UnicodeString;
+
+  inline
+  String
+  toLocalString(std::string s)
+  {
+    return icu::UnicodeString::fromUTF8(std::move(s));
+  }
+
+#if defined(__GNUC__)
+// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
+// warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#pragma GCC diagnostic ignored "-Weffc++"
+// This will be ignored under other compilers like LLVM clang.
+#endif
+  class UnicodeStringIterator : public
+    std::iterator<std::forward_iterator_tag, int32_t>
+  {
+    public:
+
+    UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
+    : s(string)
+    , i(pos)
+    {
+    }
+
+    value_type
+    operator*() const
+    {
+      return s->char32At(i);
+    }
+
+    bool
+    operator==(const UnicodeStringIterator& rhs) const
+    {
+      return s == rhs.s && i == rhs.i;
+    }
+
+    bool
+    operator!=(const UnicodeStringIterator& rhs) const
+    {
+      return !(*this == rhs);
+    }
+
+    UnicodeStringIterator&
+    operator++()
+    {
+      ++i;
+      return *this;
+    }
+
+    UnicodeStringIterator
+    operator+(int32_t v)
+    {
+      return UnicodeStringIterator(s, i + v);
+    }
+
+    private:
+    const icu::UnicodeString* s;
+    int32_t i;
+  };
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+  inline
+  String&
+  stringAppend(String&s, String a)
+  {
+    return s.append(std::move(a));
+  }
+
+  inline
+  String&
+  stringAppend(String& s, size_t n, UChar32 c)
+  {
+    for (size_t i = 0; i != n; ++i)
+    {
+      s.append(c);
+    }
+
+    return s;
+  }
+
+  template <typename Iterator>
+  String&
+  stringAppend(String& s, Iterator begin, Iterator end)
+  {
+    while (begin != end)
+    {
+      s.append(*begin);
+      ++begin;
+    }
+
+    return s;
+  }
+
+  inline
+  size_t
+  stringLength(const String& s)
+  {
+    return s.length();
+  }
+
+  inline
+  std::string
+  toUTF8String(const String& s)
+  {
+    std::string result;
+    s.toUTF8String(result);
+
+    return result;
+  }
+
+  inline
+  bool
+  empty(const String& s)
+  {
+    return s.isEmpty();
+  }
+}
+
+namespace std
+{
+  inline
+  cxxopts::UnicodeStringIterator
+  begin(const icu::UnicodeString& s)
+  {
+    return cxxopts::UnicodeStringIterator(&s, 0);
+  }
+
+  inline
+  cxxopts::UnicodeStringIterator
+  end(const icu::UnicodeString& s)
+  {
+    return cxxopts::UnicodeStringIterator(&s, s.length());
+  }
+}
+
+//ifdef CXXOPTS_USE_UNICODE
+#else
+
+namespace cxxopts
+{
+  using String = std::string;
+
+  template <typename T>
+  T
+  toLocalString(T&& t)
+  {
+    return std::forward<T>(t);
+  }
+
+  inline
+  size_t
+  stringLength(const String& s)
+  {
+    return s.length();
+  }
+
+  inline
+  String&
+  stringAppend(String&s, const String& a)
+  {
+    return s.append(a);
+  }
+
+  inline
+  String&
+  stringAppend(String& s, size_t n, char c)
+  {
+    return s.append(n, c);
+  }
+
+  template <typename Iterator>
+  String&
+  stringAppend(String& s, Iterator begin, Iterator end)
+  {
+    return s.append(begin, end);
+  }
+
+  template <typename T>
+  std::string
+  toUTF8String(T&& t)
+  {
+    return std::forward<T>(t);
+  }
+
+  inline
+  bool
+  empty(const std::string& s)
+  {
+    return s.empty();
+  }
+} // namespace cxxopts
+
+//ifdef CXXOPTS_USE_UNICODE
+#endif
+
+namespace cxxopts
+{
+  namespace
+  {
+#ifdef _WIN32
+    const std::string LQUOTE("\'");
+    const std::string RQUOTE("\'");
+#else
+    const std::string LQUOTE("‘");
+    const std::string RQUOTE("’");
+#endif
+  } // namespace
+
+#if defined(__GNUC__)
+// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it:
+// warning: base class 'class std::enable_shared_from_this<cxxopts::Value>' has accessible non-virtual destructor
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#pragma GCC diagnostic ignored "-Weffc++"
+// This will be ignored under other compilers like LLVM clang.
+#endif
+  class Value : public std::enable_shared_from_this<Value>
+  {
+    public:
+
+    virtual ~Value() = default;
+
+    virtual
+    std::shared_ptr<Value>
+    clone() const = 0;
+
+    virtual void
+    parse(const std::string& text) const = 0;
+
+    virtual void
+    parse() const = 0;
+
+    virtual bool
+    has_default() const = 0;
+
+    virtual bool
+    is_container() const = 0;
+
+    virtual bool
+    has_implicit() const = 0;
+
+    virtual std::string
+    get_default_value() const = 0;
+
+    virtual std::string
+    get_implicit_value() const = 0;
+
+    virtual std::shared_ptr<Value>
+    default_value(const std::string& value) = 0;
+
+    virtual std::shared_ptr<Value>
+    implicit_value(const std::string& value) = 0;
+
+    virtual std::shared_ptr<Value>
+    no_implicit_value() = 0;
+
+    virtual bool
+    is_boolean() const = 0;
+  };
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+  class OptionException : public std::exception
+  {
+    public:
+    explicit OptionException(std::string  message)
+    : m_message(std::move(message))
+    {
+    }
+
+    CXXOPTS_NODISCARD
+    const char*
+    what() const noexcept override
+    {
+      return m_message.c_str();
+    }
+
+    private:
+    std::string m_message;
+  };
+
+  class OptionSpecException : public OptionException
+  {
+    public:
+
+    explicit OptionSpecException(const std::string& message)
+    : OptionException(message)
+    {
+    }
+  };
+
+  class OptionParseException : public OptionException
+  {
+    public:
+    explicit OptionParseException(const std::string& message)
+    : OptionException(message)
+    {
+    }
+  };
+
+  class option_exists_error : public OptionSpecException
+  {
+    public:
+    explicit option_exists_error(const std::string& option)
+    : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
+    {
+    }
+  };
+
+  class invalid_option_format_error : public OptionSpecException
+  {
+    public:
+    explicit invalid_option_format_error(const std::string& format)
+    : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
+    {
+    }
+  };
+
+  class option_syntax_exception : public OptionParseException {
+    public:
+    explicit option_syntax_exception(const std::string& text)
+    : OptionParseException("Argument " + LQUOTE + text + RQUOTE +
+        " starts with a - but has incorrect syntax")
+    {
+    }
+  };
+
+  class option_not_exists_exception : public OptionParseException
+  {
+    public:
+    explicit option_not_exists_exception(const std::string& option)
+    : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
+    {
+    }
+  };
+
+  class missing_argument_exception : public OptionParseException
+  {
+    public:
+    explicit missing_argument_exception(const std::string& option)
+    : OptionParseException(
+        "Option " + LQUOTE + option + RQUOTE + " is missing an argument"
+      )
+    {
+    }
+  };
+
+  class option_requires_argument_exception : public OptionParseException
+  {
+    public:
+    explicit option_requires_argument_exception(const std::string& option)
+    : OptionParseException(
+        "Option " + LQUOTE + option + RQUOTE + " requires an argument"
+      )
+    {
+    }
+  };
+
+  class option_not_has_argument_exception : public OptionParseException
+  {
+    public:
+    option_not_has_argument_exception
+    (
+      const std::string& option,
+      const std::string& arg
+    )
+    : OptionParseException(
+        "Option " + LQUOTE + option + RQUOTE +
+        " does not take an argument, but argument " +
+        LQUOTE + arg + RQUOTE + " given"
+      )
+    {
+    }
+  };
+
+  class option_not_present_exception : public OptionParseException
+  {
+    public:
+    explicit option_not_present_exception(const std::string& option)
+    : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
+    {
+    }
+  };
+
+  class option_has_no_value_exception : public OptionException
+  {
+    public:
+    explicit option_has_no_value_exception(const std::string& option)
+    : OptionException(
+        !option.empty() ?
+        ("Option " + LQUOTE + option + RQUOTE + " has no value") :
+        "Option has no value")
+    {
+    }
+  };
+
+  class argument_incorrect_type : public OptionParseException
+  {
+    public:
+    explicit argument_incorrect_type
+    (
+      const std::string& arg
+    )
+    : OptionParseException(
+        "Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
+      )
+    {
+    }
+  };
+
+  class option_required_exception : public OptionParseException
+  {
+    public:
+    explicit option_required_exception(const std::string& option)
+    : OptionParseException(
+        "Option " + LQUOTE + option + RQUOTE + " is required but not present"
+      )
+    {
+    }
+  };
+
+  template <typename T>
+  void throw_or_mimic(const std::string& text)
+  {
+    static_assert(std::is_base_of<std::exception, T>::value,
+                  "throw_or_mimic only works on std::exception and "
+                  "deriving classes");
+
+#ifndef CXXOPTS_NO_EXCEPTIONS
+    // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw
+    throw T{text};
+#else
+    // Otherwise manually instantiate the exception, print what() to stderr,
+    // and exit
+    T exception{text};
+    std::cerr << exception.what() << std::endl;
+    std::exit(EXIT_FAILURE);
+#endif
+  }
+
+  namespace values
+  {
+    namespace parser_tool
+    {
+      struct IntegerDesc
+      {
+        std::string negative = "";
+        std::string base     = "";
+        std::string value    = "";
+      };
+      struct ArguDesc {
+        std::string arg_name  = "";
+        bool        grouping  = false;
+        bool        set_value = false;
+        std::string value     = "";
+      };
+#ifdef CXXOPTS_NO_REGEX
+      inline IntegerDesc SplitInteger(const std::string &text)
+      {
+        if (text.empty())
+        {
+          throw_or_mimic<argument_incorrect_type>(text);
+        }
+        IntegerDesc desc;
+        const char *pdata = text.c_str();
+        if (*pdata == '-')
+        {
+          pdata += 1;
+          desc.negative = "-";
+        }
+        if (strncmp(pdata, "0x", 2) == 0)
+        {
+          pdata += 2;
+          desc.base = "0x";
+        }
+        if (*pdata != '\0')
+        {
+          desc.value = std::string(pdata);
+        }
+        else
+        {
+          throw_or_mimic<argument_incorrect_type>(text);
+        }
+        return desc;
+      }
+
+      inline bool IsTrueText(const std::string &text)
+      {
+        const char *pdata = text.c_str();
+        if (*pdata == 't' || *pdata == 'T')
+        {
+          pdata += 1;
+          if (strncmp(pdata, "rue\0", 4) == 0)
+          {
+            return true;
+          }
+        }
+        else if (strncmp(pdata, "1\0", 2) == 0)
+        {
+          return true;
+        }
+        return false;
+      }
+
+      inline bool IsFalseText(const std::string &text)
+      {
+        const char *pdata = text.c_str();
+        if (*pdata == 'f' || *pdata == 'F')
+        {
+          pdata += 1;
+          if (strncmp(pdata, "alse\0", 5) == 0)
+          {
+            return true;
+          }
+        }
+        else if (strncmp(pdata, "0\0", 2) == 0)
+        {
+          return true;
+        }
+        return false;
+      }
+
+      inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
+      {
+        std::string short_sw, long_sw;
+        const char *pdata = text.c_str();
+        if (isalnum(*pdata) && *(pdata + 1) == ',') {
+          short_sw = std::string(1, *pdata);
+          pdata += 2;
+        }
+        while (*pdata == ' ') { pdata += 1; }
+        if (isalnum(*pdata)) {
+          const char *store = pdata;
+          pdata += 1;
+          while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') {
+            pdata += 1;
+          }
+          if (*pdata == '\0') {
+            long_sw = std::string(store, pdata - store);
+          } else {
+            throw_or_mimic<invalid_option_format_error>(text);
+          }
+        }
+        return std::pair<std::string, std::string>(short_sw, long_sw);
+      }
+
+      inline ArguDesc ParseArgument(const char *arg, bool &matched)
+      {
+        ArguDesc argu_desc;
+        const char *pdata = arg;
+        matched = false;
+        if (strncmp(pdata, "--", 2) == 0)
+        {
+          pdata += 2;
+          if (isalnum(*pdata))
+          {
+            argu_desc.arg_name.push_back(*pdata);
+            pdata += 1;
+            while (isalnum(*pdata) || *pdata == '-' || *pdata == '_')
+            {
+              argu_desc.arg_name.push_back(*pdata);
+              pdata += 1;
+            }
+            if (argu_desc.arg_name.length() > 1)
+            {
+              if (*pdata == '=')
+              {
+                argu_desc.set_value = true;
+                pdata += 1;
+                if (*pdata != '\0')
+                {
+                  argu_desc.value = std::string(pdata);
+                }
+                matched = true;
+              }
+              else if (*pdata == '\0')
+              {
+                matched = true;
+              }
+            }
+          }
+        }
+        else if (strncmp(pdata, "-", 1) == 0)
+        {
+          pdata += 1;
+          argu_desc.grouping = true;
+          while (isalnum(*pdata))
+          {
+            argu_desc.arg_name.push_back(*pdata);
+            pdata += 1;
+          }
+          matched = !argu_desc.arg_name.empty() && *pdata == '\0';
+        }
+        return argu_desc;
+      }
+
+#else  // CXXOPTS_NO_REGEX
+
+      namespace
+      {
+
+        std::basic_regex<char> integer_pattern
+          ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
+        std::basic_regex<char> truthy_pattern
+          ("(t|T)(rue)?|1");
+        std::basic_regex<char> falsy_pattern
+          ("(f|F)(alse)?|0");
+
+        std::basic_regex<char> option_matcher
+          ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
+        std::basic_regex<char> option_specifier
+          ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
+
+      } // namespace
+
+      inline IntegerDesc SplitInteger(const std::string &text)
+      {
+        std::smatch match;
+        std::regex_match(text, match, integer_pattern);
+
+        if (match.length() == 0)
+        {
+          throw_or_mimic<argument_incorrect_type>(text);
+        }
+
+        IntegerDesc desc;
+        desc.negative = match[1];
+        desc.base = match[2];
+        desc.value = match[3];
+
+        if (match.length(4) > 0)
+        {
+          desc.base = match[5];
+          desc.value = "0";
+          return desc;
+        }
+
+        return desc;
+      }
+
+      inline bool IsTrueText(const std::string &text)
+      {
+        std::smatch result;
+        std::regex_match(text, result, truthy_pattern);
+        return !result.empty();
+      }
+
+      inline bool IsFalseText(const std::string &text)
+      {
+        std::smatch result;
+        std::regex_match(text, result, falsy_pattern);
+        return !result.empty();
+      }
+
+      inline std::pair<std::string, std::string> SplitSwitchDef(const std::string &text)
+      {
+        std::match_results<const char*> result;
+        std::regex_match(text.c_str(), result, option_specifier);
+        if (result.empty())
+        {
+          throw_or_mimic<invalid_option_format_error>(text);
+        }
+
+        const std::string& short_sw = result[2];
+        const std::string& long_sw = result[3];
+
+        return std::pair<std::string, std::string>(short_sw, long_sw);
+      }
+
+      inline ArguDesc ParseArgument(const char *arg, bool &matched)
+      {
+        std::match_results<const char*> result;
+        std::regex_match(arg, result, option_matcher);
+        matched = !result.empty();
+
+        ArguDesc argu_desc;
+        if (matched) {
+          argu_desc.arg_name = result[1].str();
+          argu_desc.set_value = result[2].length() > 0;
+          argu_desc.value = result[3].str();
+          if (result[4].length() > 0)
+          {
+            argu_desc.grouping = true;
+            argu_desc.arg_name = result[4].str();
+          }
+        }
+
+        return argu_desc;
+      }
+
+#endif  // CXXOPTS_NO_REGEX
+#undef CXXOPTS_NO_REGEX
+  }
+
+    namespace detail
+    {
+      template <typename T, bool B>
+      struct SignedCheck;
+
+      template <typename T>
+      struct SignedCheck<T, true>
+      {
+        template <typename U>
+        void
+        operator()(bool negative, U u, const std::string& text)
+        {
+          if (negative)
+          {
+            if (u > static_cast<U>((std::numeric_limits<T>::min)()))
+            {
+              throw_or_mimic<argument_incorrect_type>(text);
+            }
+          }
+          else
+          {
+            if (u > static_cast<U>((std::numeric_limits<T>::max)()))
+            {
+              throw_or_mimic<argument_incorrect_type>(text);
+            }
+          }
+        }
+      };
+
+      template <typename T>
+      struct SignedCheck<T, false>
+      {
+        template <typename U>
+        void
+        operator()(bool, U, const std::string&) const {}
+      };
+
+      template <typename T, typename U>
+      void
+      check_signed_range(bool negative, U value, const std::string& text)
+      {
+        SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
+      }
+    } // namespace detail
+
+    template <typename R, typename T>
+    void
+    checked_negate(R& r, T&& t, const std::string&, std::true_type)
+    {
+      // if we got to here, then `t` is a positive number that fits into
+      // `R`. So to avoid MSVC C4146, we first cast it to `R`.
+      // See https://github.com/jarro2783/cxxopts/issues/62 for more details.
+      r = static_cast<R>(-static_cast<R>(t-1)-1);
+    }
+
+    template <typename R, typename T>
+    void
+    checked_negate(R&, T&&, const std::string& text, std::false_type)
+    {
+      throw_or_mimic<argument_incorrect_type>(text);
+    }
+
+    template <typename T>
+    void
+    integer_parser(const std::string& text, T& value)
+    {
+      parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text);
+
+      using US = typename std::make_unsigned<T>::type;
+      constexpr bool is_signed = std::numeric_limits<T>::is_signed;
+
+      const bool          negative    = int_desc.negative.length() > 0;
+      const uint8_t       base        = int_desc.base.length() > 0 ? 16 : 10;
+      const std::string & value_match = int_desc.value;
+
+      US result = 0;
+
+      for (char ch : value_match)
+      {
+        US digit = 0;
+
+        if (ch >= '0' && ch <= '9')
+        {
+          digit = static_cast<US>(ch - '0');
+        }
+        else if (base == 16 && ch >= 'a' && ch <= 'f')
+        {
+          digit = static_cast<US>(ch - 'a' + 10);
+        }
+        else if (base == 16 && ch >= 'A' && ch <= 'F')
+        {
+          digit = static_cast<US>(ch - 'A' + 10);
+        }
+        else
+        {
+          throw_or_mimic<argument_incorrect_type>(text);
+        }
+
+        const US next = static_cast<US>(result * base + digit);
+        if (result > next)
+        {
+          throw_or_mimic<argument_incorrect_type>(text);
+        }
+
+        result = next;
+      }
+
+      detail::check_signed_range<T>(negative, result, text);
+
+      if (negative)
+      {
+        checked_negate<T>(value, result, text, std::integral_constant<bool, is_signed>());
+      }
+      else
+      {
+        value = static_cast<T>(result);
+      }
+    }
+
+    template <typename T>
+    void stringstream_parser(const std::string& text, T& value)
+    {
+      std::stringstream in(text);
+      in >> value;
+      if (!in) {
+        throw_or_mimic<argument_incorrect_type>(text);
+      }
+    }
+
+    template <typename T,
+             typename std::enable_if<std::is_integral<T>::value>::type* = nullptr
+             >
+    void parse_value(const std::string& text, T& value)
+    {
+        integer_parser(text, value);
+    }
+
+    inline
+    void
+    parse_value(const std::string& text, bool& value)
+    {
+      if (parser_tool::IsTrueText(text))
+      {
+        value = true;
+        return;
+      }
+
+      if (parser_tool::IsFalseText(text))
+      {
+        value = false;
+        return;
+      }
+
+      throw_or_mimic<argument_incorrect_type>(text);
+    }
+
+    inline
+    void
+    parse_value(const std::string& text, std::string& value)
+    {
+      value = text;
+    }
+
+    // The fallback parser. It uses the stringstream parser to parse all types
+    // that have not been overloaded explicitly.  It has to be placed in the
+    // source code before all other more specialized templates.
+    template <typename T,
+             typename std::enable_if<!std::is_integral<T>::value>::type* = nullptr
+             >
+    void
+    parse_value(const std::string& text, T& value) {
+      stringstream_parser(text, value);
+    }
+
+    template <typename T>
+    void
+    parse_value(const std::string& text, std::vector<T>& value)
+    {
+      if (text.empty()) {
+        T v;
+        parse_value(text, v);
+        value.emplace_back(std::move(v));
+        return;
+      }
+      std::stringstream in(text);
+      std::string token;
+      while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
+        T v;
+        parse_value(token, v);
+        value.emplace_back(std::move(v));
+      }
+    }
+
+#ifdef CXXOPTS_HAS_OPTIONAL
+    template <typename T>
+    void
+    parse_value(const std::string& text, std::optional<T>& value)
+    {
+      T result;
+      parse_value(text, result);
+      value = std::move(result);
+    }
+#endif
+
+    inline
+    void parse_value(const std::string& text, char& c)
+    {
+      if (text.length() != 1)
+      {
+        throw_or_mimic<argument_incorrect_type>(text);
+      }
+
+      c = text[0];
+    }
+
+    template <typename T>
+    struct type_is_container
+    {
+      static constexpr bool value = false;
+    };
+
+    template <typename T>
+    struct type_is_container<std::vector<T>>
+    {
+      static constexpr bool value = true;
+    };
+
+    template <typename T>
+    class abstract_value : public Value
+    {
+      using Self = abstract_value<T>;
+
+      public:
+      abstract_value()
+      : m_result(std::make_shared<T>())
+      , m_store(m_result.get())
+      {
+      }
+
+      explicit abstract_value(T* t)
+      : m_store(t)
+      {
+      }
+
+      ~abstract_value() override = default;
+
+      abstract_value& operator=(const abstract_value&) = default;
+
+      abstract_value(const abstract_value& rhs)
+      {
+        if (rhs.m_result)
+        {
+          m_result = std::make_shared<T>();
+          m_store = m_result.get();
+        }
+        else
+        {
+          m_store = rhs.m_store;
+        }
+
+        m_default = rhs.m_default;
+        m_implicit = rhs.m_implicit;
+        m_default_value = rhs.m_default_value;
+        m_implicit_value = rhs.m_implicit_value;
+      }
+
+      void
+      parse(const std::string& text) const override
+      {
+        parse_value(text, *m_store);
+      }
+
+      bool
+      is_container() const override
+      {
+        return type_is_container<T>::value;
+      }
+
+      void
+      parse() const override
+      {
+        parse_value(m_default_value, *m_store);
+      }
+
+      bool
+      has_default() const override
+      {
+        return m_default;
+      }
+
+      bool
+      has_implicit() const override
+      {
+        return m_implicit;
+      }
+
+      std::shared_ptr<Value>
+      default_value(const std::string& value) override
+      {
+        m_default = true;
+        m_default_value = value;
+        return shared_from_this();
+      }
+
+      std::shared_ptr<Value>
+      implicit_value(const std::string& value) override
+      {
+        m_implicit = true;
+        m_implicit_value = value;
+        return shared_from_this();
+      }
+
+      std::shared_ptr<Value>
+      no_implicit_value() override
+      {
+        m_implicit = false;
+        return shared_from_this();
+      }
+
+      std::string
+      get_default_value() const override
+      {
+        return m_default_value;
+      }
+
+      std::string
+      get_implicit_value() const override
+      {
+        return m_implicit_value;
+      }
+
+      bool
+      is_boolean() const override
+      {
+        return std::is_same<T, bool>::value;
+      }
+
+      const T&
+      get() const
+      {
+        if (m_store == nullptr)
+        {
+          return *m_result;
+        }
+        return *m_store;
+      }
+
+      protected:
+      std::shared_ptr<T> m_result{};
+      T* m_store{};
+
+      bool m_default = false;
+      bool m_implicit = false;
+
+      std::string m_default_value{};
+      std::string m_implicit_value{};
+    };
+
+    template <typename T>
+    class standard_value : public abstract_value<T>
+    {
+      public:
+      using abstract_value<T>::abstract_value;
+
+      CXXOPTS_NODISCARD
+      std::shared_ptr<Value>
+      clone() const override
+      {
+        return std::make_shared<standard_value<T>>(*this);
+      }
+    };
+
+    template <>
+    class standard_value<bool> : public abstract_value<bool>
+    {
+      public:
+      ~standard_value() override = default;
+
+      standard_value()
+      {
+        set_default_and_implicit();
+      }
+
+      explicit standard_value(bool* b)
+      : abstract_value(b)
+      {
+        set_default_and_implicit();
+      }
+
+      std::shared_ptr<Value>
+      clone() const override
+      {
+        return std::make_shared<standard_value<bool>>(*this);
+      }
+
+      private:
+
+      void
+      set_default_and_implicit()
+      {
+        m_default = true;
+        m_default_value = "false";
+        m_implicit = true;
+        m_implicit_value = "true";
+      }
+    };
+  } // namespace values
+
+  template <typename T>
+  std::shared_ptr<Value>
+  value()
+  {
+    return std::make_shared<values::standard_value<T>>();
+  }
+
+  template <typename T>
+  std::shared_ptr<Value>
+  value(T& t)
+  {
+    return std::make_shared<values::standard_value<T>>(&t);
+  }
+
+  class OptionAdder;
+
+  class OptionDetails
+  {
+    public:
+    OptionDetails
+    (
+      std::string short_,
+      std::string long_,
+      String desc,
+      std::shared_ptr<const Value> val
+    )
+    : m_short(std::move(short_))
+    , m_long(std::move(long_))
+    , m_desc(std::move(desc))
+    , m_value(std::move(val))
+    , m_count(0)
+    {
+      m_hash = std::hash<std::string>{}(m_long + m_short);
+    }
+
+    OptionDetails(const OptionDetails& rhs)
+    : m_desc(rhs.m_desc)
+    , m_value(rhs.m_value->clone())
+    , m_count(rhs.m_count)
+    {
+    }
+
+    OptionDetails(OptionDetails&& rhs) = default;
+
+    CXXOPTS_NODISCARD
+    const String&
+    description() const
+    {
+      return m_desc;
+    }
+
+    CXXOPTS_NODISCARD
+    const Value&
+    value() const {
+        return *m_value;
+    }
+
+    CXXOPTS_NODISCARD
+    std::shared_ptr<Value>
+    make_storage() const
+    {
+      return m_value->clone();
+    }
+
+    CXXOPTS_NODISCARD
+    const std::string&
+    short_name() const
+    {
+      return m_short;
+    }
+
+    CXXOPTS_NODISCARD
+    const std::string&
+    long_name() const
+    {
+      return m_long;
+    }
+
+    size_t
+    hash() const
+    {
+      return m_hash;
+    }
+
+    private:
+    std::string m_short{};
+    std::string m_long{};
+    String m_desc{};
+    std::shared_ptr<const Value> m_value{};
+    int m_count;
+
+    size_t m_hash{};
+  };
+
+  struct HelpOptionDetails
+  {
+    std::string s;
+    std::string l;
+    String desc;
+    bool has_default;
+    std::string default_value;
+    bool has_implicit;
+    std::string implicit_value;
+    std::string arg_help;
+    bool is_container;
+    bool is_boolean;
+  };
+
+  struct HelpGroupDetails
+  {
+    std::string name{};
+    std::string description{};
+    std::vector<HelpOptionDetails> options{};
+  };
+
+  class OptionValue
+  {
+    public:
+    void
+    parse
+    (
+      const std::shared_ptr<const OptionDetails>& details,
+      const std::string& text
+    )
+    {
+      ensure_value(details);
+      ++m_count;
+      m_value->parse(text);
+      m_long_name = &details->long_name();
+    }
+
+    void
+    parse_default(const std::shared_ptr<const OptionDetails>& details)
+    {
+      ensure_value(details);
+      m_default = true;
+      m_long_name = &details->long_name();
+      m_value->parse();
+    }
+
+    void
+    parse_no_value(const std::shared_ptr<const OptionDetails>& details)
+    {
+      m_long_name = &details->long_name();
+    }
+
+#if defined(CXXOPTS_NULL_DEREF_IGNORE)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnull-dereference"
+#endif
+
+    CXXOPTS_NODISCARD
+    size_t
+    count() const noexcept
+    {
+      return m_count;
+    }
+
+#if defined(CXXOPTS_NULL_DEREF_IGNORE)
+#pragma GCC diagnostic pop
+#endif
+
+    // TODO: maybe default options should count towards the number of arguments
+    CXXOPTS_NODISCARD
+    bool
+    has_default() const noexcept
+    {
+      return m_default;
+    }
+
+    template <typename T>
+    const T&
+    as() const
+    {
+      if (m_value == nullptr) {
+          throw_or_mimic<option_has_no_value_exception>(
+              m_long_name == nullptr ? "" : *m_long_name);
+      }
+
+#ifdef CXXOPTS_NO_RTTI
+      return static_cast<const values::standard_value<T>&>(*m_value).get();
+#else
+      return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
+#endif
+    }
+
+    private:
+    void
+    ensure_value(const std::shared_ptr<const OptionDetails>& details)
+    {
+      if (m_value == nullptr)
+      {
+        m_value = details->make_storage();
+      }
+    }
+
+
+    const std::string* m_long_name = nullptr;
+    // Holding this pointer is safe, since OptionValue's only exist in key-value pairs,
+    // where the key has the string we point to.
+    std::shared_ptr<Value> m_value{};
+    size_t m_count = 0;
+    bool m_default = false;
+  };
+
+  class KeyValue
+  {
+    public:
+    KeyValue(std::string key_, std::string value_)
+    : m_key(std::move(key_))
+    , m_value(std::move(value_))
+    {
+    }
+
+    CXXOPTS_NODISCARD
+    const std::string&
+    key() const
+    {
+      return m_key;
+    }
+
+    CXXOPTS_NODISCARD
+    const std::string&
+    value() const
+    {
+      return m_value;
+    }
+
+    template <typename T>
+    T
+    as() const
+    {
+      T result;
+      values::parse_value(m_value, result);
+      return result;
+    }
+
+    private:
+    std::string m_key;
+    std::string m_value;
+  };
+
+  using ParsedHashMap = std::unordered_map<size_t, OptionValue>;
+  using NameHashMap = std::unordered_map<std::string, size_t>;
+
+  class ParseResult
+  {
+    public:
+
+    ParseResult() = default;
+    ParseResult(const ParseResult&) = default;
+
+    ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector<KeyValue> sequential, std::vector<std::string>&& unmatched_args)
+    : m_keys(std::move(keys))
+    , m_values(std::move(values))
+    , m_sequential(std::move(sequential))
+    , m_unmatched(std::move(unmatched_args))
+    {
+    }
+
+    ParseResult& operator=(ParseResult&&) = default;
+    ParseResult& operator=(const ParseResult&) = default;
+
+    size_t
+    count(const std::string& o) const
+    {
+      auto iter = m_keys.find(o);
+      if (iter == m_keys.end())
+      {
+        return 0;
+      }
+
+      auto viter = m_values.find(iter->second);
+
+      if (viter == m_values.end())
+      {
+        return 0;
+      }
+
+      return viter->second.count();
+    }
+
+    const OptionValue&
+    operator[](const std::string& option) const
+    {
+      auto iter = m_keys.find(option);
+
+      if (iter == m_keys.end())
+      {
+        throw_or_mimic<option_not_present_exception>(option);
+      }
+
+      auto viter = m_values.find(iter->second);
+
+      if (viter == m_values.end())
+      {
+        throw_or_mimic<option_not_present_exception>(option);
+      }
+
+      return viter->second;
+    }
+
+    const std::vector<KeyValue>&
+    arguments() const
+    {
+      return m_sequential;
+    }
+
+    const std::vector<std::string>&
+    unmatched() const
+    {
+      return m_unmatched;
+    }
+
+    private:
+    NameHashMap m_keys{};
+    ParsedHashMap m_values{};
+    std::vector<KeyValue> m_sequential{};
+    std::vector<std::string> m_unmatched{};
+  };
+
+  struct Option
+  {
+    Option
+    (
+      std::string opts,
+      std::string desc,
+      std::shared_ptr<const Value>  value = ::cxxopts::value<bool>(),
+      std::string arg_help = ""
+    )
+    : opts_(std::move(opts))
+    , desc_(std::move(desc))
+    , value_(std::move(value))
+    , arg_help_(std::move(arg_help))
+    {
+    }
+
+    std::string opts_;
+    std::string desc_;
+    std::shared_ptr<const Value> value_;
+    std::string arg_help_;
+  };
+
+  using OptionMap = std::unordered_map<std::string, std::shared_ptr<OptionDetails>>;
+  using PositionalList = std::vector<std::string>;
+  using PositionalListIterator = PositionalList::const_iterator;
+
+  class OptionParser
+  {
+    public:
+    OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised)
+    : m_options(options)
+    , m_positional(positional)
+    , m_allow_unrecognised(allow_unrecognised)
+    {
+    }
+
+    ParseResult
+    parse(int argc, const char* const* argv);
+
+    bool
+    consume_positional(const std::string& a, PositionalListIterator& next);
+
+    void
+    checked_parse_arg
+    (
+      int argc,
+      const char* const* argv,
+      int& current,
+      const std::shared_ptr<OptionDetails>& value,
+      const std::string& name
+    );
+
+    void
+    add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg);
+
+    void
+    parse_option
+    (
+      const std::shared_ptr<OptionDetails>& value,
+      const std::string& name,
+      const std::string& arg = ""
+    );
+
+    void
+    parse_default(const std::shared_ptr<OptionDetails>& details);
+
+    void
+    parse_no_value(const std::shared_ptr<OptionDetails>& details);
+
+    private:
+
+    void finalise_aliases();
+
+    const OptionMap& m_options;
+    const PositionalList& m_positional;
+
+    std::vector<KeyValue> m_sequential{};
+    bool m_allow_unrecognised;
+
+    ParsedHashMap m_parsed{};
+    NameHashMap m_keys{};
+  };
+
+  class Options
+  {
+    public:
+
+    explicit Options(std::string program, std::string help_string = "")
+    : m_program(std::move(program))
+    , m_help_string(toLocalString(std::move(help_string)))
+    , m_custom_help("[OPTION...]")
+    , m_positional_help("positional parameters")
+    , m_show_positional(false)
+    , m_allow_unrecognised(false)
+    , m_width(76)
+    , m_tab_expansion(false)
+    , m_options(std::make_shared<OptionMap>())
+    {
+    }
+
+    Options&
+    positional_help(std::string help_text)
+    {
+      m_positional_help = std::move(help_text);
+      return *this;
+    }
+
+    Options&
+    custom_help(std::string help_text)
+    {
+      m_custom_help = std::move(help_text);
+      return *this;
+    }
+
+    Options&
+    show_positional_help()
+    {
+      m_show_positional = true;
+      return *this;
+    }
+
+    Options&
+    allow_unrecognised_options()
+    {
+      m_allow_unrecognised = true;
+      return *this;
+    }
+
+    Options&
+    set_width(size_t width)
+    {
+      m_width = width;
+      return *this;
+    }
+
+    Options&
+    set_tab_expansion(bool expansion=true)
+    {
+      m_tab_expansion = expansion;
+      return *this;
+    }
+
+    ParseResult
+    parse(int argc, const char* const* argv);
+
+    OptionAdder
+    add_options(std::string group = "");
+
+    void
+    add_options
+    (
+      const std::string& group,
+      std::initializer_list<Option> options
+    );
+
+    void
+    add_option
+    (
+      const std::string& group,
+      const Option& option
+    );
+
+    void
+    add_option
+    (
+      const std::string& group,
+      const std::string& s,
+      const std::string& l,
+      std::string desc,
+      const std::shared_ptr<const Value>& value,
+      std::string arg_help
+    );
+
+    //parse positional arguments into the given option
+    void
+    parse_positional(std::string option);
+
+    void
+    parse_positional(std::vector<std::string> options);
+
+    void
+    parse_positional(std::initializer_list<std::string> options);
+
+    template <typename Iterator>
+    void
+    parse_positional(Iterator begin, Iterator end) {
+      parse_positional(std::vector<std::string>{begin, end});
+    }
+
+    std::string
+    help(const std::vector<std::string>& groups = {}) const;
+
+    std::vector<std::string>
+    groups() const;
+
+    const HelpGroupDetails&
+    group_help(const std::string& group) const;
+
+    private:
+
+    void
+    add_one_option
+    (
+      const std::string& option,
+      const std::shared_ptr<OptionDetails>& details
+    );
+
+    String
+    help_one_group(const std::string& group) const;
+
+    void
+    generate_group_help
+    (
+      String& result,
+      const std::vector<std::string>& groups
+    ) const;
+
+    void
+    generate_all_groups_help(String& result) const;
+
+    std::string m_program{};
+    String m_help_string{};
+    std::string m_custom_help{};
+    std::string m_positional_help{};
+    bool m_show_positional;
+    bool m_allow_unrecognised;
+    size_t m_width;
+    bool m_tab_expansion;
+
+    std::shared_ptr<OptionMap> m_options;
+    std::vector<std::string> m_positional{};
+    std::unordered_set<std::string> m_positional_set{};
+
+    //mapping from groups to help options
+    std::map<std::string, HelpGroupDetails> m_help{};
+
+    std::list<OptionDetails> m_option_list{};
+    std::unordered_map<std::string, decltype(m_option_list)::iterator> m_option_map{};
+  };
+
+  class OptionAdder
+  {
+    public:
+
+    OptionAdder(Options& options, std::string group)
+    : m_options(options), m_group(std::move(group))
+    {
+    }
+
+    OptionAdder&
+    operator()
+    (
+      const std::string& opts,
+      const std::string& desc,
+      const std::shared_ptr<const Value>& value
+        = ::cxxopts::value<bool>(),
+      std::string arg_help = ""
+    );
+
+    private:
+    Options& m_options;
+    std::string m_group;
+  };
+
+  namespace
+  {
+    constexpr size_t OPTION_LONGEST = 30;
+    constexpr size_t OPTION_DESC_GAP = 2;
+
+    String
+    format_option
+    (
+      const HelpOptionDetails& o
+    )
+    {
+      const auto& s = o.s;
+      const auto& l = o.l;
+
+      String result = "  ";
+
+      if (!s.empty())
+      {
+        result += "-" + toLocalString(s);
+        if (!l.empty())
+        {
+          result += ",";
+        }
+      }
+      else
+      {
+        result += "   ";
+      }
+
+      if (!l.empty())
+      {
+        result += " --" + toLocalString(l);
+      }
+
+      auto arg = !o.arg_help.empty() ? toLocalString(o.arg_help) : "arg";
+
+      if (!o.is_boolean)
+      {
+        if (o.has_implicit)
+        {
+          result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
+        }
+        else
+        {
+          result += " " + arg;
+        }
+      }
+
+      return result;
+    }
+
+    String
+    format_description
+    (
+      const HelpOptionDetails& o,
+      size_t start,
+      size_t allowed,
+      bool tab_expansion
+    )
+    {
+      auto desc = o.desc;
+
+      if (o.has_default && (!o.is_boolean || o.default_value != "false"))
+      {
+        if(!o.default_value.empty())
+        {
+          desc += toLocalString(" (default: " + o.default_value + ")");
+        }
+        else
+        {
+          desc += toLocalString(" (default: \"\")");
+        }
+      }
+
+      String result;
+
+      if (tab_expansion)
+      {
+        String desc2;
+        auto size = size_t{ 0 };
+        for (auto c = std::begin(desc); c != std::end(desc); ++c)
+        {
+          if (*c == '\n')
+          {
+            desc2 += *c;
+            size = 0;
+          }
+          else if (*c == '\t')
+          {
+            auto skip = 8 - size % 8;
+            stringAppend(desc2, skip, ' ');
+            size += skip;
+          }
+          else
+          {
+            desc2 += *c;
+            ++size;
+          }
+        }
+        desc = desc2;
+      }
+
+      desc += " ";
+
+      auto current = std::begin(desc);
+      auto previous = current;
+      auto startLine = current;
+      auto lastSpace = current;
+
+      auto size = size_t{};
+
+      bool appendNewLine;
+      bool onlyWhiteSpace = true;
+
+      while (current != std::end(desc))
+      {
+        appendNewLine = false;
+
+        if (std::isblank(*previous))
+        {
+          lastSpace = current;
+        }
+
+        if (!std::isblank(*current))
+        {
+          onlyWhiteSpace = false;
+        }
+
+        while (*current == '\n')
+        {
+          previous = current;
+          ++current;
+          appendNewLine = true;
+        }
+
+        if (!appendNewLine && size >= allowed)
+        {
+          if (lastSpace != startLine)
+          {
+            current = lastSpace;
+            previous = current;
+          }
+          appendNewLine = true;
+        }
+
+        if (appendNewLine)
+        {
+          stringAppend(result, startLine, current);
+          startLine = current;
+          lastSpace = current;
+
+          if (*previous != '\n')
+          {
+            stringAppend(result, "\n");
+          }
+
+          stringAppend(result, start, ' ');
+
+          if (*previous != '\n')
+          {
+            stringAppend(result, lastSpace, current);
+          }
+
+          onlyWhiteSpace = true;
+          size = 0;
+        }
+
+        previous = current;
+        ++current;
+        ++size;
+      }
+
+      //append whatever is left but ignore whitespace
+      if (!onlyWhiteSpace)
+      {
+        stringAppend(result, startLine, previous);
+      }
+
+      return result;
+    }
+  } // namespace
+
+inline
+void
+Options::add_options
+(
+  const std::string &group,
+  std::initializer_list<Option> options
+)
+{
+ OptionAdder option_adder(*this, group);
+ for (const auto &option: options)
+ {
+   option_adder(option.opts_, option.desc_, option.value_, option.arg_help_);
+ }
+}
+
+inline
+OptionAdder
+Options::add_options(std::string group)
+{
+  return OptionAdder(*this, std::move(group));
+}
+
+inline
+OptionAdder&
+OptionAdder::operator()
+(
+  const std::string& opts,
+  const std::string& desc,
+  const std::shared_ptr<const Value>& value,
+  std::string arg_help
+)
+{
+  std::string short_sw, long_sw;
+  std::tie(short_sw, long_sw) = values::parser_tool::SplitSwitchDef(opts);
+
+  if (!short_sw.length() && !long_sw.length())
+  {
+    throw_or_mimic<invalid_option_format_error>(opts);
+  }
+  else if (long_sw.length() == 1 && short_sw.length())
+  {
+    throw_or_mimic<invalid_option_format_error>(opts);
+  }
+
+  auto option_names = []
+  (
+    const std::string &short_,
+    const std::string &long_
+  )
+  {
+    if (long_.length() == 1)
+    {
+      return std::make_tuple(long_, short_);
+    }
+    return std::make_tuple(short_, long_);
+  }(short_sw, long_sw);
+
+  m_options.add_option
+  (
+    m_group,
+    std::get<0>(option_names),
+    std::get<1>(option_names),
+    desc,
+    value,
+    std::move(arg_help)
+  );
+
+  return *this;
+}
+
+inline
+void
+OptionParser::parse_default(const std::shared_ptr<OptionDetails>& details)
+{
+  // TODO: remove the duplicate code here
+  auto& store = m_parsed[details->hash()];
+  store.parse_default(details);
+}
+
+inline
+void
+OptionParser::parse_no_value(const std::shared_ptr<OptionDetails>& details)
+{
+  auto& store = m_parsed[details->hash()];
+  store.parse_no_value(details);
+}
+
+inline
+void
+OptionParser::parse_option
+(
+  const std::shared_ptr<OptionDetails>& value,
+  const std::string& /*name*/,
+  const std::string& arg
+)
+{
+  auto hash = value->hash();
+  auto& result = m_parsed[hash];
+  result.parse(value, arg);
+
+  m_sequential.emplace_back(value->long_name(), arg);
+}
+
+inline
+void
+OptionParser::checked_parse_arg
+(
+  int argc,
+  const char* const* argv,
+  int& current,
+  const std::shared_ptr<OptionDetails>& value,
+  const std::string& name
+)
+{
+  if (current + 1 >= argc)
+  {
+    if (value->value().has_implicit())
+    {
+      parse_option(value, name, value->value().get_implicit_value());
+    }
+    else
+    {
+      throw_or_mimic<missing_argument_exception>(name);
+    }
+  }
+  else
+  {
+    if (value->value().has_implicit())
+    {
+      parse_option(value, name, value->value().get_implicit_value());
+    }
+    else
+    {
+      parse_option(value, name, argv[current + 1]);
+      ++current;
+    }
+  }
+}
+
+inline
+void
+OptionParser::add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg)
+{
+  parse_option(iter->second, option, arg);
+}
+
+inline
+bool
+OptionParser::consume_positional(const std::string& a, PositionalListIterator& next)
+{
+  while (next != m_positional.end())
+  {
+    auto iter = m_options.find(*next);
+    if (iter != m_options.end())
+    {
+      if (!iter->second->value().is_container())
+      {
+        auto& result = m_parsed[iter->second->hash()];
+        if (result.count() == 0)
+        {
+          add_to_option(iter, *next, a);
+          ++next;
+          return true;
+        }
+        ++next;
+        continue;
+      }
+      add_to_option(iter, *next, a);
+      return true;
+    }
+    throw_or_mimic<option_not_exists_exception>(*next);
+  }
+
+  return false;
+}
+
+inline
+void
+Options::parse_positional(std::string option)
+{
+  parse_positional(std::vector<std::string>{std::move(option)});
+}
+
+inline
+void
+Options::parse_positional(std::vector<std::string> options)
+{
+  m_positional = std::move(options);
+
+  m_positional_set.insert(m_positional.begin(), m_positional.end());
+}
+
+inline
+void
+Options::parse_positional(std::initializer_list<std::string> options)
+{
+  parse_positional(std::vector<std::string>(options));
+}
+
+inline
+ParseResult
+Options::parse(int argc, const char* const* argv)
+{
+  OptionParser parser(*m_options, m_positional, m_allow_unrecognised);
+
+  return parser.parse(argc, argv);
+}
+
+inline ParseResult
+OptionParser::parse(int argc, const char* const* argv)
+{
+  int current = 1;
+  bool consume_remaining = false;
+  auto next_positional = m_positional.begin();
+
+  std::vector<std::string> unmatched;
+
+  while (current != argc)
+  {
+    if (strcmp(argv[current], "--") == 0)
+    {
+      consume_remaining = true;
+      ++current;
+      break;
+    }
+    bool matched = false;
+    values::parser_tool::ArguDesc argu_desc =
+        values::parser_tool::ParseArgument(argv[current], matched);
+
+    if (!matched)
+    {
+      //not a flag
+
+      // but if it starts with a `-`, then it's an error
+      if (argv[current][0] == '-' && argv[current][1] != '\0') {
+        if (!m_allow_unrecognised) {
+          throw_or_mimic<option_syntax_exception>(argv[current]);
+        }
+      }
+
+      //if true is returned here then it was consumed, otherwise it is
+      //ignored
+      if (consume_positional(argv[current], next_positional))
+      {
+      }
+      else
+      {
+        unmatched.emplace_back(argv[current]);
+      }
+      //if we return from here then it was parsed successfully, so continue
+    }
+    else
+    {
+      //short or long option?
+      if (argu_desc.grouping)
+      {
+        const std::string& s = argu_desc.arg_name;
+
+        for (std::size_t i = 0; i != s.size(); ++i)
+        {
+          std::string name(1, s[i]);
+          auto iter = m_options.find(name);
+
+          if (iter == m_options.end())
+          {
+            if (m_allow_unrecognised)
+            {
+              continue;
+            }
+            //error
+            throw_or_mimic<option_not_exists_exception>(name);
+          }
+
+          auto value = iter->second;
+
+          if (i + 1 == s.size())
+          {
+            //it must be the last argument
+            checked_parse_arg(argc, argv, current, value, name);
+          }
+          else if (value->value().has_implicit())
+          {
+            parse_option(value, name, value->value().get_implicit_value());
+          }
+          else if (i + 1 < s.size())
+          {
+            std::string arg_value = s.substr(i + 1);
+            parse_option(value, name, arg_value);
+            break;
+          }
+          else
+          {
+            //error
+            throw_or_mimic<option_requires_argument_exception>(name);
+          }
+        }
+      }
+      else if (argu_desc.arg_name.length() != 0)
+      {
+        const std::string& name = argu_desc.arg_name;
+
+        auto iter = m_options.find(name);
+
+        if (iter == m_options.end())
+        {
+          if (m_allow_unrecognised)
+          {
+            // keep unrecognised options in argument list, skip to next argument
+            unmatched.emplace_back(argv[current]);
+            ++current;
+            continue;
+          }
+          //error
+          throw_or_mimic<option_not_exists_exception>(name);
+        }
+
+        auto opt = iter->second;
+
+        //equals provided for long option?
+        if (argu_desc.set_value)
+        {
+          //parse the option given
+
+          parse_option(opt, name, argu_desc.value);
+        }
+        else
+        {
+          //parse the next argument
+          checked_parse_arg(argc, argv, current, opt, name);
+        }
+      }
+
+    }
+
+    ++current;
+  }
+
+  for (auto& opt : m_options)
+  {
+    auto& detail = opt.second;
+    const auto& value = detail->value();
+
+    auto& store = m_parsed[detail->hash()];
+
+    if (value.has_default()) {
+      if (!store.count() && !store.has_default()) {
+        parse_default(detail);
+      }
+    }
+    else {
+      parse_no_value(detail);
+    }
+  }
+
+  if (consume_remaining)
+  {
+    while (current < argc)
+    {
+      if (!consume_positional(argv[current], next_positional)) {
+        break;
+      }
+      ++current;
+    }
+
+    //adjust argv for any that couldn't be swallowed
+    while (current != argc) {
+      unmatched.emplace_back(argv[current]);
+      ++current;
+    }
+  }
+
+  finalise_aliases();
+
+  ParseResult parsed(std::move(m_keys), std::move(m_parsed), std::move(m_sequential), std::move(unmatched));
+  return parsed;
+}
+
+inline
+void
+OptionParser::finalise_aliases()
+{
+  for (auto& option: m_options)
+  {
+    auto& detail = *option.second;
+    auto hash = detail.hash();
+    m_keys[detail.short_name()] = hash;
+    m_keys[detail.long_name()] = hash;
+
+    m_parsed.emplace(hash, OptionValue());
+  }
+}
+
+inline
+void
+Options::add_option
+(
+  const std::string& group,
+  const Option& option
+)
+{
+    add_options(group, {option});
+}
+
+inline
+void
+Options::add_option
+(
+  const std::string& group,
+  const std::string& s,
+  const std::string& l,
+  std::string desc,
+  const std::shared_ptr<const Value>& value,
+  std::string arg_help
+)
+{
+  auto stringDesc = toLocalString(std::move(desc));
+  auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
+
+  if (!s.empty())
+  {
+    add_one_option(s, option);
+  }
+
+  if (!l.empty())
+  {
+    add_one_option(l, option);
+  }
+
+  m_option_list.push_front(*option.get());
+  auto iter = m_option_list.begin();
+  m_option_map[s] = iter;
+  m_option_map[l] = iter;
+
+  //add the help details
+  auto& options = m_help[group];
+
+  options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
+      value->has_default(), value->get_default_value(),
+      value->has_implicit(), value->get_implicit_value(),
+      std::move(arg_help),
+      value->is_container(),
+      value->is_boolean()});
+}
+
+inline
+void
+Options::add_one_option
+(
+  const std::string& option,
+  const std::shared_ptr<OptionDetails>& details
+)
+{
+  auto in = m_options->emplace(option, details);
+
+  if (!in.second)
+  {
+    throw_or_mimic<option_exists_error>(option);
+  }
+}
+
+inline
+String
+Options::help_one_group(const std::string& g) const
+{
+  using OptionHelp = std::vector<std::pair<String, String>>;
+
+  auto group = m_help.find(g);
+  if (group == m_help.end())
+  {
+    return "";
+  }
+
+  OptionHelp format;
+
+  size_t longest = 0;
+
+  String result;
+
+  if (!g.empty())
+  {
+    result += toLocalString(" " + g + " options:\n");
+  }
+
+  for (const auto& o : group->second.options)
+  {
+    if (m_positional_set.find(o.l) != m_positional_set.end() &&
+        !m_show_positional)
+    {
+      continue;
+    }
+
+    auto s = format_option(o);
+    longest = (std::max)(longest, stringLength(s));
+    format.push_back(std::make_pair(s, String()));
+  }
+  longest = (std::min)(longest, OPTION_LONGEST);
+
+  //widest allowed description -- min 10 chars for helptext/line
+  size_t allowed = 10;
+  if (m_width > allowed + longest + OPTION_DESC_GAP)
+  {
+    allowed = m_width - longest - OPTION_DESC_GAP;
+  }
+
+  auto fiter = format.begin();
+  for (const auto& o : group->second.options)
+  {
+    if (m_positional_set.find(o.l) != m_positional_set.end() &&
+        !m_show_positional)
+    {
+      continue;
+    }
+
+    auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion);
+
+    result += fiter->first;
+    if (stringLength(fiter->first) > longest)
+    {
+      result += '\n';
+      result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
+    }
+    else
+    {
+      result += toLocalString(std::string(longest + OPTION_DESC_GAP -
+        stringLength(fiter->first),
+        ' '));
+    }
+    result += d;
+    result += '\n';
+
+    ++fiter;
+  }
+
+  return result;
+}
+
+inline
+void
+Options::generate_group_help
+(
+  String& result,
+  const std::vector<std::string>& print_groups
+) const
+{
+  for (size_t i = 0; i != print_groups.size(); ++i)
+  {
+    const String& group_help_text = help_one_group(print_groups[i]);
+    if (empty(group_help_text))
+    {
+      continue;
+    }
+    result += group_help_text;
+    if (i < print_groups.size() - 1)
+    {
+      result += '\n';
+    }
+  }
+}
+
+inline
+void
+Options::generate_all_groups_help(String& result) const
+{
+  std::vector<std::string> all_groups;
+
+  std::transform(
+    m_help.begin(),
+    m_help.end(),
+    std::back_inserter(all_groups),
+    [] (const std::map<std::string, HelpGroupDetails>::value_type& group)
+    {
+      return group.first;
+    }
+  );
+
+  generate_group_help(result, all_groups);
+}
+
+inline
+std::string
+Options::help(const std::vector<std::string>& help_groups) const
+{
+  String result = m_help_string + "\nUsage:\n  " +
+    toLocalString(m_program) + " " + toLocalString(m_custom_help);
+
+  if (!m_positional.empty() && !m_positional_help.empty()) {
+    result += " " + toLocalString(m_positional_help);
+  }
+
+  result += "\n\n";
+
+  if (help_groups.empty())
+  {
+    generate_all_groups_help(result);
+  }
+  else
+  {
+    generate_group_help(result, help_groups);
+  }
+
+  return toUTF8String(result);
+}
+
+inline
+std::vector<std::string>
+Options::groups() const
+{
+  std::vector<std::string> g;
+
+  std::transform(
+    m_help.begin(),
+    m_help.end(),
+    std::back_inserter(g),
+    [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
+    {
+      return pair.first;
+    }
+  );
+
+  return g;
+}
+
+inline
+const HelpGroupDetails&
+Options::group_help(const std::string& group) const
+{
+  return m_help.at(group);
+}
+
+} // namespace cxxopts
+
+#endif //CXXOPTS_HPP_INCLUDED
diff --git a/include/reactor-sdk/impl/Connection.hh b/include/reactor-sdk/impl/Connection.hh
new file mode 100644
index 00000000..a238da8a
--- /dev/null
+++ b/include/reactor-sdk/impl/Connection.hh
@@ -0,0 +1,108 @@
+#pragma once
+
+#include "reactor-cpp/port.hh"
+
+/*
+Input
+MultiportInput
+ReactorBank_Input
+ReactorBank_MultiportInput
+
+Output
+MultiportOutput
+ReactorBank_Output
+ReactorBank_MultiportOutput
+
+Input -> Input
+Input -> MultiportInput             *
+Input -> ReactorBank_Input          *
+Input -> ReactorBank_MultiportInput *
+
+Output -> Input
+Output -> MultiportInput             *
+Output -> ReactorBank_Input          *
+Output -> ReactorBank_MultiportInput *
+Output -> Output
+Output -> MultiportOutput
+
+MultiportInput -> Input
+MultiportInput -> MultiportInput
+MultiportInput -> ReactorBank_Input
+MultiportInput -> ReactorBank_MultiportInput
+
+MultiportOutput -> Input
+MultiportOutput -> MultiportInput
+MultiportOutput -> ReactorBank_Input
+MultiportOutput -> ReactorBank_MultiportInput
+MultiportOutput -> Output
+MultiportOutput -> MultiportOutput
+
+ReactorBank_Output -> Input
+ReactorBank_Output -> MultiportInput
+ReactorBank_Output -> ReactorBank_Input
+ReactorBank_Output -> ReactorBank_MultiportInput
+ReactorBank_Output -> Output
+ReactorBank_Output -> MultiportOutput
+
+ReactorBank_MultiportOutput -> Input
+ReactorBank_MultiportOutput -> MultiportInput
+ReactorBank_MultiportOutput -> ReactorBank_Input
+ReactorBank_MultiportOutput -> ReactorBank_MultiportInput
+ReactorBank_MultiportOutput -> Output
+ReactorBank_MultiportOutput -> MultiportOutput
+
+*/
+
+template <typename T>
+void display_(std::set<reactor::Port<T>*> &left_ports, std::set<reactor::Port<T>*> &right_ports) {
+    reactor::log::Warn() << "Left Ports:";
+    for (auto *left_port : left_ports) {
+        reactor::log::Warn() << "\t" << left_port->fqn();
+    }
+    reactor::log::Warn() << "Right Ports:";
+    for (auto *right_port : right_ports) {
+        reactor::log::Warn() << "\t" << right_port->fqn();
+    }
+}
+
+template <typename T>
+void connect_(  std::set<reactor::Port<T>*> &left_ports, std::set<reactor::Port<T>*> &right_ports,
+                reactor::ConnectionProperties &&property) {
+    if (left_ports.size() < right_ports.size()) {
+        reactor::log::Warn() << "There are more right ports (" << right_ports.size() << ") than left ports (" << left_ports.size() << ")";
+        display_ (left_ports, right_ports);
+    } else if (left_ports.size() > right_ports.size()) {
+        reactor::log::Warn() << "There are more left ports (" << left_ports.size() << ") than right ports (" << right_ports.size() << ")";
+        display_ (left_ports, right_ports);
+    }
+
+    auto right_port_itr = right_ports.begin();
+    for (auto *left_port : left_ports) {
+        if (right_port_itr == right_ports.end()) {
+            break;
+        }
+        left_port->environment()->draw_connection(left_port, (*right_port_itr), reactor::ConnectionProperties{});
+        ++right_port_itr;
+    }
+}
+
+template <typename T>
+void connect_fanout_(std::set<reactor::Port<T>*> &left_ports, std::set<reactor::Port<T>*> &right_ports,
+                    reactor::ConnectionProperties &&property) {
+    assert (left_ports.size() == 1);
+    if (left_ports.size() < right_ports.size()) {
+        reactor::log::Warn() << "There are more right ports (" << right_ports.size() << ") than left ports (" << left_ports.size() << ")";
+        display_ (left_ports, right_ports);
+        reactor::log::Warn() << "Fanning out left port to all right ports";
+    } else if (left_ports.size() > right_ports.size()) {
+        reactor::log::Warn() << "There are more left ports (" << left_ports.size() << ") than right ports (" << right_ports.size() << ")";
+        display_ (left_ports, right_ports);
+        reactor::log::Warn() << "Fanning out left port to all right ports";
+    }
+
+    auto left_port_itr = left_ports.begin();
+    for (auto *right_port : right_ports) {
+        (*left_port_itr)->environment()->draw_connection((*left_port_itr), right_port, reactor::ConnectionProperties{});
+    }
+
+}
\ No newline at end of file
diff --git a/include/reactor-sdk/impl/InputMultiport_wiring_impl.hh b/include/reactor-sdk/impl/InputMultiport_wiring_impl.hh
new file mode 100644
index 00000000..b5095368
--- /dev/null
+++ b/include/reactor-sdk/impl/InputMultiport_wiring_impl.hh
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "Connection.hh"
+
+namespace sdk
+{
+template <typename T>
+void MultiportInput<T>::connect(Input<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+    for (auto& output_port : *this) {
+        result = left_ports.insert(&output_port).second;
+        reactor_assert(result);
+    }
+    result = right_ports.insert(&input).second;
+    reactor_assert(result);
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void MultiportInput<T>::connect(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+    for (auto& left_port : *this) {
+        result = left_ports.insert(&left_port).second;
+        reactor_assert(result);
+    }
+
+    for (auto& right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+    
+} // namespace sdk
diff --git a/include/reactor-sdk/impl/InputPort_wiring_impl.hh b/include/reactor-sdk/impl/InputPort_wiring_impl.hh
new file mode 100644
index 00000000..18e2dd5f
--- /dev/null
+++ b/include/reactor-sdk/impl/InputPort_wiring_impl.hh
@@ -0,0 +1,80 @@
+#pragma once
+
+#include "Connection.hh"
+
+namespace sdk
+{
+
+template <typename T>
+void Input<T>::connect(Input<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    result = right_ports.insert(&input).second;
+    reactor_assert(result);
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void Input<T>::connect(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+
+    for (auto& right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void Input<T>::connect_fanout(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    for (auto &right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_fanout_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+template <typename ReactorType>
+void Input<T>::connect(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    for (auto &p_reactor : other_bank_ports) {
+        auto *reactor = p_reactor.get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Input<T>* port = reinterpret_cast<Input<T>*>(reactor_base + other_bank_ports.get_offset());
+        result = right_ports.insert(port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+template <typename ReactorType>
+void Input<T>::connect_fanout(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    for (auto &p_reactor : other_bank_ports) {
+        auto *reactor = p_reactor.get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Input<T>* port = reinterpret_cast<Input<T>*>(reactor_base + other_bank_ports.get_offset());
+        result = right_ports.insert(port).second;
+        reactor_assert(result);
+    }
+    connect_fanout_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+    
+} // namespace sdk
diff --git a/include/reactor-sdk/impl/OutputMultiport_wiring_impl.hh b/include/reactor-sdk/impl/OutputMultiport_wiring_impl.hh
new file mode 100644
index 00000000..45ad89e8
--- /dev/null
+++ b/include/reactor-sdk/impl/OutputMultiport_wiring_impl.hh
@@ -0,0 +1,132 @@
+#pragma once
+
+#include "Connection.hh"
+namespace sdk
+{
+template <typename T>
+void MultiportOutput<T>::connect(Input<T>& input) {
+    if (n_inputs > 1) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& output_port : *this) {
+        output_port.environment()->draw_connection(output_port, input, reactor::ConnectionProperties{});
+        break;
+    }
+}
+
+template <typename T>
+void MultiportOutput<T>::connect(Output<T>& input) {
+    if (n_inputs > 1) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& output_port : *this) {
+        output_port.environment()->draw_connection(output_port, input, reactor::ConnectionProperties{});
+        break;
+    }
+}
+
+template <typename T>
+void MultiportOutput<T>::connect(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+    for (auto& left_port : *this) {
+        result = left_ports.insert(&left_port).second;
+        reactor_assert(result);
+    }
+
+    for (auto& right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void MultiportOutput<T>::connect(MultiportOutput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+    for (auto& left_port : *this) {
+        result = left_ports.insert(&left_port).second;
+        reactor_assert(result);
+    }
+
+    for (auto& right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+template <typename ReactorType>
+void MultiportOutput<T>::connect(std::vector<std::unique_ptr<ReactorType>>* reactors, Input<T> ReactorType::*member) {
+    auto reactor_itr = reactors->begin();
+
+    if (n_inputs < reactors->size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (n_inputs > reactors->size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& output_port : *this) {
+        auto *reactor = (*reactor_itr).get();
+        output_port.environment()->draw_connection(output_port, reactor->*member, reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors->end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename T>
+template <typename OtherReactorType>
+void MultiportOutput<T>::connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = other_bank_ports.begin();
+
+    if (n_inputs < other_bank_ports.size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (n_inputs > other_bank_ports.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& output_port : *this) {
+        auto *reactor = (*reactor_itr).get();
+        output_port.environment()->draw_connection(output_port, reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == other_bank_ports.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename T>
+template <typename OtherReactorType>
+void MultiportOutput<T>::connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result;
+
+    for (auto& left_port : *this) {
+        result = left_ports.insert(&left_port).second;
+        reactor_assert(result);
+    }
+
+    for (auto &reactor : other_bank_ports) {
+        char* reactor_base = reinterpret_cast<char*>(reactor.get());
+        Input<T>* port = reinterpret_cast<Input<T>*>(reactor_base + other_bank_ports.get_offset());
+        result = right_ports.insert(port).second;
+        reactor_assert(result);
+    }
+
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+    
+} // namespace sdk
diff --git a/include/reactor-sdk/impl/OutputPort_wiring_impl.hh b/include/reactor-sdk/impl/OutputPort_wiring_impl.hh
new file mode 100644
index 00000000..c929cc28
--- /dev/null
+++ b/include/reactor-sdk/impl/OutputPort_wiring_impl.hh
@@ -0,0 +1,147 @@
+#pragma once
+
+#include "Connection.hh"
+
+namespace sdk
+{
+
+template <typename T>
+void Output<T>::connect(Input<T>& input) {
+    this->environment()->draw_connection(*this, input, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void Output<T>::connect(Output<T>& input) {
+    this->environment()->draw_connection(*this, input, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+void Output<T>::connect(MultiportInput<T>& input) {
+    if (is_accumulated) {
+        std::set<reactor::Port<T>*> left_ports;
+        std::set<reactor::Port<T>*> right_ports;
+        bool result = false;
+        result = left_ports.insert(this).second;
+        reactor_assert(result);
+        for (auto *l_port : accumulated) {
+            result = left_ports.insert(l_port).second;
+            reactor_assert(result);
+        }
+
+        for (auto& right_port : input) {
+            result = right_ports.insert(&right_port).second;
+            reactor_assert(result);
+        }
+        connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+        is_accumulated = false;
+        accumulated.clear();
+    } else {
+        std::set<reactor::Port<T>*> left_ports;
+        std::set<reactor::Port<T>*> right_ports;
+        bool result = left_ports.insert(this).second;
+        reactor_assert(result);
+        for (auto &right_port : input) {
+            result = right_ports.insert(&right_port).second;
+            reactor_assert(result);
+        }
+        connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+    }
+}
+
+template <typename T>
+void Output<T>::connect_fanout(MultiportInput<T>& input) {
+    if (is_accumulated) {
+        std::set<reactor::Port<T>*> left_ports;
+        std::set<reactor::Port<T>*> right_ports;
+        bool result = false;
+        result = left_ports.insert(this).second;
+        reactor_assert(result);
+        for (auto *l_port : accumulated) {
+            result = left_ports.insert(l_port).second;
+            reactor_assert(result);
+        }
+
+        for (auto& right_port : input) {
+            result = right_ports.insert(&right_port).second;
+            reactor_assert(result);
+        }
+        connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+        is_accumulated = false;
+        accumulated.clear();
+    } else {
+        std::set<reactor::Port<T>*> left_ports;
+        std::set<reactor::Port<T>*> right_ports;
+        bool result = left_ports.insert(this).second;
+        reactor_assert(result);
+        for (auto &right_port : input) {
+            result = right_ports.insert(&right_port).second;
+            reactor_assert(result);
+        }
+        connect_fanout_ (left_ports, right_ports, reactor::ConnectionProperties{});
+    }
+}
+
+template <typename T>
+template <typename ReactorType>
+void Output<T>::connect(std::vector<std::unique_ptr<ReactorType>>* reactors, Input<T> ReactorType::*member) {
+
+    if (1 < reactors->size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto &p_reactor : *reactors) {
+        auto *reactor = p_reactor.get();
+        this->environment()->draw_connection(*this, reactor->*member, reactor::ConnectionProperties{});
+    }
+}
+
+template <typename T>
+template <typename ReactorType>
+void Output<T>::connect(ReactorBankInputPort<ReactorType, T> &&other_bank_ports) {
+
+    if (1 < other_bank_ports.size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto &p_reactor : other_bank_ports) {
+        auto *reactor = p_reactor.get();
+        this->environment()->draw_connection(*this, reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+        break;
+    }
+}
+
+template <typename T>
+template <typename ReactorType>
+void Output<T>::connect(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    for (auto &p_reactor : other_bank_ports) {
+        auto *reactor = p_reactor.get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Input<T>* port = reinterpret_cast<Input<T>*>(reactor_base + other_bank_ports.get_offset());
+        result = right_ports.insert(port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename T>
+template <typename ReactorType>
+void Output<T>::connect_fanout(ReactorBankInputPortOffset<ReactorType, T> &&other_bank_ports) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = left_ports.insert(this).second;
+    reactor_assert(result);
+    for (auto &p_reactor : other_bank_ports) {
+        auto *reactor = p_reactor.get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Input<T>* port = reinterpret_cast<Input<T>*>(reactor_base + other_bank_ports.get_offset());
+        result = right_ports.insert(port).second;
+        reactor_assert(result);
+    }
+    connect_fanout_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+    
+} // namespace sdk
diff --git a/include/reactor-sdk/impl/ReactorBankOutputMultiport_wiring_impl.hh b/include/reactor-sdk/impl/ReactorBankOutputMultiport_wiring_impl.hh
new file mode 100644
index 00000000..1b2ba8e0
--- /dev/null
+++ b/include/reactor-sdk/impl/ReactorBankOutputMultiport_wiring_impl.hh
@@ -0,0 +1,354 @@
+#pragma once
+
+namespace sdk
+{
+template <typename ReactorType, typename T>
+void ReactorBankOutputMultiPort<ReactorType, T>::connect(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        for (auto &l_port : left_reactor->*member) {
+            result = left_ports.insert(&l_port).second;
+            reactor_assert(result);
+        }
+    }
+
+    for (auto &right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPort<ReactorType, T>::connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = other_bank_ports.size();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        left_ports += (left_reactor->*member).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_reactor_itr == other_bank_ports.end()) {
+            break;
+        }
+
+        for (auto &l_port : left_reactor->*member) {
+            auto *right_reactor = (*right_reactor_itr).get();
+            l_port.environment()->draw_connection(l_port, right_reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+            ++right_reactor_itr;
+            if (right_reactor_itr == other_bank_ports.end()) {
+                break;
+            }
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPort<ReactorType, T>::connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        left_ports += (left_reactor->*member).get_nports();
+    }
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        right_ports += (right_reactor->*(other_bank_ports.get_member())).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+    auto right_port_itr =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).begin();
+    auto right_port_itr_end =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).end();
+    size_t right_port_count = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_port_count == right_ports) {
+            break;
+        }
+
+        for (auto &l_port : left_reactor->*member) {
+            l_port.environment()->draw_connection(l_port, *right_port_itr, reactor::ConnectionProperties{});
+            ++right_port_count;
+            if (right_port_count == right_ports) {
+                break;
+            }
+            ++right_port_itr;
+            if (right_port_itr == right_port_itr_end) {
+                ++right_reactor_itr;
+                right_port_itr =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).begin();
+                right_port_itr_end =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).end();
+            }
+        }
+    }
+}
+
+
+template <typename ReactorType, typename T>
+void ReactorBankOutputMultiPortOffset<ReactorType, T>::connect(MultiportInput<T>& input) {
+    std::set<reactor::Port<T>*> left_ports;
+    std::set<reactor::Port<T>*> right_ports;
+    bool result = false;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        for (auto &l_port : *l_ports) {
+            result = left_ports.insert(&l_port).second;
+            reactor_assert(result);
+        }
+    }
+
+    for (auto &right_port : input) {
+        result = right_ports.insert(&right_port).second;
+        reactor_assert(result);
+    }
+    connect_ (left_ports, right_ports, reactor::ConnectionProperties{});
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPortOffset<ReactorType, T>::connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = other_bank_ports.size();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        left_ports += (*l_ports).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_reactor_itr == other_bank_ports.end()) {
+            break;
+        }
+
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        for (auto &l_port : *l_ports) {
+            auto *right_reactor = (*right_reactor_itr).get();
+            l_port.environment()->draw_connection(l_port, right_reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+            ++right_reactor_itr;
+            if (right_reactor_itr == other_bank_ports.end()) {
+                break;
+            }
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPortOffset<ReactorType, T>::connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        left_ports += (*l_ports).get_nports();
+    }
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        right_ports += (right_reactor->*(other_bank_ports.get_member())).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+    auto right_port_itr =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).begin();
+    auto right_port_itr_end =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).end();
+    size_t right_port_count = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_port_count == right_ports) {
+            break;
+        }
+
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        for (auto &l_port : *l_ports) {
+            l_port.environment()->draw_connection(l_port, *right_port_itr, reactor::ConnectionProperties{});
+            ++right_port_count;
+            if (right_port_count == right_ports) {
+                break;
+            }
+            ++right_port_itr;
+            if (right_port_itr == right_port_itr_end) {
+                ++right_reactor_itr;
+                right_port_itr =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).begin();
+                right_port_itr_end =  (*right_reactor_itr).get()->*(other_bank_ports.get_member()).end();
+            }
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPortOffset<ReactorType, T>::connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = other_bank_ports.size();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        left_ports += (*l_ports).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_reactor_itr == other_bank_ports.end()) {
+            break;
+        }
+
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        for (auto &l_port : *l_ports) {
+            auto *right_reactor = (*right_reactor_itr).get();
+            char* r_reactor_base = reinterpret_cast<char*>(right_reactor);
+            Input<T>* r_port= reinterpret_cast<Input<T>*>(r_reactor_base + other_bank_ports.get_offset());
+            l_port.environment()->draw_connection(l_port, *r_port, reactor::ConnectionProperties{});
+            ++right_reactor_itr;
+            if (right_reactor_itr == other_bank_ports.end()) {
+                break;
+            }
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputMultiPortOffset<ReactorType, T>::connect(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = 0;
+    size_t right_ports = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        left_ports += (*l_ports).get_nports();
+    }
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        char* r_reactor_base = reinterpret_cast<char*>(right_reactor);
+        MultiportInput<T>* r_ports = reinterpret_cast<MultiportInput<T>*>(r_reactor_base + other_bank_ports.get_offset());
+        right_ports += (*r_ports).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto right_reactor_itr = other_bank_ports.begin();
+    char* r_reactor_base = reinterpret_cast<char*>(*right_reactor_itr);
+    MultiportInput<T>* r_ports = reinterpret_cast<MultiportInput<T>*>(r_reactor_base + other_bank_ports.get_offset());
+    auto right_port_itr =  (*r_ports).begin();
+    auto right_port_itr_end =  (*r_ports).end();
+    size_t right_port_count = 0;
+
+    for (auto& p_left_reactor : reactors) {
+        auto *left_reactor = p_left_reactor.get();
+
+        if (right_port_count == right_ports) {
+            break;
+        }
+
+        char* l_reactor_base = reinterpret_cast<char*>(left_reactor);
+        MultiportOutput<T>* l_ports= reinterpret_cast<MultiportOutput<T>*>(l_reactor_base + offset);
+        for (auto &l_port : *l_ports) {
+            l_port.environment()->draw_connection(l_port, *right_port_itr, reactor::ConnectionProperties{});
+            ++right_port_count;
+            if (right_port_count == right_ports) {
+                break;
+            }
+            ++right_port_itr;
+            if (right_port_itr == right_port_itr_end) {
+                ++right_reactor_itr;
+                r_reactor_base = reinterpret_cast<char*>(*right_reactor_itr);
+                r_ports = reinterpret_cast<MultiportInput<T>*>(r_reactor_base + other_bank_ports.get_offset());
+                right_port_itr =  (*r_ports).begin();
+                right_port_itr_end =  (*r_ports).end();
+            }
+        }
+    }
+}
+    
+} // namespace sdk
diff --git a/include/reactor-sdk/impl/ReactorBankOutputPort_wiring_impl.hh b/include/reactor-sdk/impl/ReactorBankOutputPort_wiring_impl.hh
new file mode 100644
index 00000000..33d4779d
--- /dev/null
+++ b/include/reactor-sdk/impl/ReactorBankOutputPort_wiring_impl.hh
@@ -0,0 +1,313 @@
+#pragma once
+
+namespace sdk
+{
+
+template <typename ReactorType, typename T>
+void ReactorBankOutputPort<ReactorType, T>::connect(Input<T>& input) {
+    auto reactor_itr = reactors.begin();
+
+    if (1 < reactors.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto *reactor = (*reactor_itr).get();
+    input.environment()->draw_connection(reactor->*member, input, reactor::ConnectionProperties{});
+}
+
+template <typename ReactorType, typename T>
+void ReactorBankOutputPort<ReactorType, T>::connect(MultiportInput<T>& input) {
+    auto reactor_itr = reactors.begin();
+
+    if (input.get_nports() > reactors.size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (input.get_nports() < reactors.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& input_port : input) {
+        auto *reactor = (*reactor_itr).get();
+        input_port.environment()->draw_connection(reactor->*member, input_port, reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPort<ReactorType, T>::connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = other_bank_ports.size();
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *reactor = (*reactor_itr).get();
+        auto *right_reactor = p_right_reactor.get();
+        (reactor->*member).environment()->draw_connection(reactor->*member, right_reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPort<ReactorType, T>::connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = 0;
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        right_ports += (right_reactor->*(other_bank_ports.get_member())).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+        
+        for (auto& right_port : right_reactor->*(other_bank_ports.get_member())) {
+            auto *reactor = (*reactor_itr).get();
+            (reactor->*member).environment()->draw_connection(reactor->*member, right_port, reactor::ConnectionProperties{});
+            ++reactor_itr;
+            if (reactor_itr == reactors.end())
+            {
+                break;
+            }
+        }
+    }
+}
+
+
+// ReactorBankOutputPortOffset
+template <typename ReactorType, typename T>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(Input<T>& input) {
+    auto reactor_itr = reactors.begin();
+
+    if (1 < reactors.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+
+    auto *reactor = (*reactor_itr).get();
+    char* reactor_base = reinterpret_cast<char*>(reactor);
+    Output<T>* port = reinterpret_cast<Output<T>*>(reactor_base + offset);
+    input.environment()->draw_connection(*port, input, reactor::ConnectionProperties{});
+}
+
+template <typename ReactorType, typename T>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(MultiportInput<T>& input) {
+    auto reactor_itr = reactors.begin();
+
+    if (input.get_nports() > reactors.size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (input.get_nports() < reactors.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& input_port : input) {
+        auto *reactor = (*reactor_itr).get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Output<T>* port = reinterpret_cast<Output<T>*>(reactor_base + offset);
+        input_port.environment()->draw_connection(*port, input_port, reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect_fanout(MultiportInput<T>& input) {
+    auto reactor_itr = reactors.begin();
+
+    if (input.get_nports() > reactors.size()) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Fanning Out!";
+    } else if (input.get_nports() < reactors.size()) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& input_port : input) {
+        auto *reactor = (*reactor_itr).get();
+        char* reactor_base = reinterpret_cast<char*>(reactor);
+        Output<T>* port = reinterpret_cast<Output<T>*>(reactor_base + offset);
+        input_port.environment()->draw_connection(*port, input_port, reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            reactor_itr = reactors.begin();
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(ReactorBankInputPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = other_bank_ports.size();
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *reactor = (*reactor_itr).get();
+        char* l_reactor_base = reinterpret_cast<char*>(reactor);
+        Output<T>* l_port = reinterpret_cast<Output<T>*>(l_reactor_base + offset);
+        auto *right_reactor = p_right_reactor.get();
+        (*l_port).environment()->draw_connection(*l_port, right_reactor->*(other_bank_ports.get_member()), reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(ReactorBankInputMultiPort<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = 0;
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        right_ports += (right_reactor->*(other_bank_ports.get_member())).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+        
+        for (auto& right_port : right_reactor->*(other_bank_ports.get_member())) {
+            auto *reactor = (*reactor_itr).get();
+            char* l_reactor_base = reinterpret_cast<char*>(reactor);
+            Output<T>* l_port = reinterpret_cast<Output<T>*>(l_reactor_base + offset);
+            (*l_port).environment()->draw_connection(*l_port, right_port, reactor::ConnectionProperties{});
+            ++reactor_itr;
+            if (reactor_itr == reactors.end())
+            {
+                break;
+            }
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(ReactorBankInputPortOffset<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = other_bank_ports.size();
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *reactor = (*reactor_itr).get();
+        char* l_reactor_base = reinterpret_cast<char*>(reactor);
+        Output<T>* l_port = reinterpret_cast<Output<T>*>(l_reactor_base + offset);
+        auto *right_reactor = p_right_reactor.get();
+        char* r_reactor_base = reinterpret_cast<char*>(right_reactor);
+        Input<T>* r_port = reinterpret_cast<Input<T>*>(r_reactor_base + other_bank_ports.get_offset());
+        (*l_port).environment()->draw_connection(*l_port, *r_port, reactor::ConnectionProperties{});
+        ++reactor_itr;
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+    }
+}
+
+template <typename ReactorType, typename T>
+template <typename OtherReactorType>
+void ReactorBankOutputPortOffset<ReactorType, T>::connect(ReactorBankInputMultiPortOffset<OtherReactorType, T> &&other_bank_ports) {
+    auto reactor_itr = reactors.begin();
+    size_t left_ports = reactors.size();
+    size_t right_ports = 0;
+
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        char* r_reactor_base = reinterpret_cast<char*>(right_reactor);
+        MultiportInput<T>* r_port = reinterpret_cast<MultiportInput<T>*>(r_reactor_base + other_bank_ports.get_offset());
+        right_ports += (*r_port).get_nports();
+    }
+
+    if (left_ports < right_ports) {
+        reactor::log::Warn() << "There are more right ports than left ports. "
+                            << "Not all ports will be connected!";
+    } else if (left_ports > right_ports) {
+        reactor::log::Warn() << "There are more left ports than right ports. "
+                            << "Not all ports will be connected!";
+    }
+    for (auto& p_right_reactor : other_bank_ports) {
+        auto *right_reactor = p_right_reactor.get();
+        if (reactor_itr == reactors.end())
+        {
+            break;
+        }
+        
+        char* r_reactor_base = reinterpret_cast<char*>(right_reactor);
+        MultiportInput<T>* r_port = reinterpret_cast<MultiportInput<T>*>(r_reactor_base + other_bank_ports.get_offset());
+        for (auto& right_port : *r_port) {
+            auto *reactor = (*reactor_itr).get();
+            char* l_reactor_base = reinterpret_cast<char*>(reactor);
+            Output<T>* l_port = reinterpret_cast<Output<T>*>(l_reactor_base + offset);
+            (*l_port).environment()->draw_connection(*l_port, right_port, reactor::ConnectionProperties{});
+            ++reactor_itr;
+            if (reactor_itr == reactors.end())
+            {
+                break;
+            }
+        }
+    }
+}
+
+} // namespace sdk
\ No newline at end of file
diff --git a/include/reactor-sdk/reactor-sdk.hh b/include/reactor-sdk/reactor-sdk.hh
new file mode 100644
index 00000000..fa19129b
--- /dev/null
+++ b/include/reactor-sdk/reactor-sdk.hh
@@ -0,0 +1,17 @@
+#pragma once
+
+using namespace std;
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include "time_parser.hh"
+#include "Misc.hh"
+#include "ReactorBank.hh"
+#include "MultiportInput.hh"
+#include "MultiportOutput.hh"
+#include "OutputPort.hh"
+#include "InputPort.hh"
+#include "Environment.hh"
+#include "Reaction.hh"
+#include "Reactor.hh"
+#include "ConfigParameters.hh"
+#include "SystemParameters.hh"
\ No newline at end of file
diff --git a/include/reactor-sdk/time_parser.hh b/include/reactor-sdk/time_parser.hh
new file mode 100644
index 00000000..dfe872fe
--- /dev/null
+++ b/include/reactor-sdk/time_parser.hh
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2020, TU Dresden.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "reactor-cpp/reactor-cpp.hh"
+#include <sstream>
+
+std::stringstream& operator>>(std::stringstream& in, reactor::Duration& dur);
+#include "cxxopts.hpp"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <regex>
+#include <string>
+
+inline bool iequals(const std::string& a, const std::string& b) {
+  return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
+}
+
+class argument_incorrect_type_with_reason : public cxxopts::OptionParseException {
+public:
+  explicit argument_incorrect_type_with_reason(const std::string& arg, const std::string& reason)
+      : cxxopts::OptionParseException("Argument " + cxxopts::LQUOTE + arg + cxxopts::RQUOTE + " failed to parse (" +
+                                      reason + ")") {}
+};
+
+std::string validate_time_string(const std::string& time);
+
+/**
+ * converts a reactor::Duration to a string with ns as unit
+ */
+inline std::string time_to_string(const reactor::Duration& dur) {
+  if (dur == reactor::Duration::max()) {
+    return "forever";
+  }
+
+  std::stringstream ss;
+  ss << dur.count() << " ns";
+  return ss.str();
+}
+
+template <typename T> std::string any_to_string(const T val) {
+  std::stringstream ss;
+  ss << val;
+  return ss.str();
+}
+
+inline std::stringstream& operator>>(std::stringstream& in, reactor::Duration& dur) {
+  double value;
+  std::string unit;
+
+  const std::string validation_msg = validate_time_string(in.str());
+  if (!validation_msg.empty()) {
+    // throw cxxopts error
+    throw argument_incorrect_type_with_reason(in.str(), validation_msg);
+  }
+
+  if (iequals(in.str(), "forever")) {
+    in >> unit; // parse the entire string
+    dur = reactor::Duration::max();
+    return in;
+  }
+
+  // try to read as double
+  in >> value;
+
+  if (value == 0.0) {
+    dur = reactor::Duration::zero();
+    if (!in.eof()) {
+      // parse whatever remains
+      in >> unit;
+    }
+  } else {
+    in >> unit;
+    if (unit == "nsec" || unit == "nsecs" || unit == "ns") {
+      std::chrono::duration<double, std::nano> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "usec" || unit == "usecs" || unit == "us") {
+      std::chrono::duration<double, std::micro> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "msec" || unit == "msecs" || unit == "ms") {
+      std::chrono::duration<double, std::milli> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "sec" || unit == "secs" || unit == "second" || unit == "seconds" || unit == "s") {
+      std::chrono::duration<double, std::ratio<1, 1>> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "min" || unit == "mins" || unit == "minute" || unit == "minutes" || unit == "m") {
+      std::chrono::duration<double, std::ratio<60, 1>> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "hour" || unit == "hours" || unit == "h") {
+      std::chrono::duration<double, std::ratio<3600, 1>> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "day" || unit == "days" || unit == "d") {
+      std::chrono::duration<double, std::ratio<24 * 3600, 1>> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else if (unit == "week" || unit == "weeks" || unit == "w") {
+      std::chrono::duration<double, std::ratio<7 * 24 * 3600, 1>> tmp{value};
+      dur = std::chrono::duration_cast<reactor::Duration>(tmp);
+    } else {
+      // mark as error
+      in.setstate(std::ifstream::failbit);
+    }
+  }
+
+  return in;
+}
+
+/**
+ *   Tests for correct syntax in unit usage for time strings
+ **/
+inline std::string validate_time_string(const std::string& time) {
+  auto trimmed = std::regex_replace(time, std::regex("^ +| +$|( ) +"), "$1");
+  if (trimmed.size() == 0) {
+    return "The empty string is not a valid time!";
+  } else if (trimmed[0] == '-') {
+    return "Negative values are not a valid time!";
+  } else if (iequals("forever", time)) {
+    return ""; // "forever" is a valid value
+  } else if (trimmed.find_first_not_of("0.") == std::string::npos) {
+    return "";
+  } else {
+    auto pos = trimmed.find_first_not_of("0123456789. \n\r\t");
+    if (pos == std::string::npos) {
+      return "No unit given!";
+    } else {
+      auto unit = trimmed.substr(pos);
+      if (unit == "nsec" || unit == "nsecs" || unit == "ns" || unit == "usec" || unit == "usecs" || unit == "us" ||
+          unit == "msec" || unit == "msecs" || unit == "ms" || unit == "sec" || unit == "secs" || unit == "second" ||
+          unit == "seconds" || unit == "s" || unit == "min" || unit == "mins" || unit == "minute" ||
+          unit == "minutes" || unit == "m" || unit == "hour" || unit == "hours" || unit == "h" || unit == "day" ||
+          unit == "days" || unit == "d" || unit == "week" || unit == "weeks" || unit == "w") {
+        return "";
+      } else {
+        std::stringstream ss;
+        ss << "Not a valid unit: " << unit;
+        return ss.str();
+      }
+    }
+  }
+  return "Unexpected error!";
+}
diff --git a/lib/environment.cc b/lib/environment.cc
index 52c0b6bf..937afe3f 100644
--- a/lib/environment.cc
+++ b/lib/environment.cc
@@ -67,6 +67,24 @@ void Environment::optimize() {
   optimized_graph_ = graph_;
 }
 
+void recursive_construct(Reactor* container) {
+  container->construct();
+  for (auto* reactor : container->reactors()) {
+    recursive_construct(reactor);
+  }
+}
+
+void Environment::construct() {
+    log::Debug() << "Start Contruction of reactors";
+    for (auto* reactor : top_level_reactors_) {
+        recursive_construct(reactor);
+    }
+
+    for (auto* env : contained_environments_) {
+        env->construct();
+    }
+}
+
 void recursive_assemble(Reactor* container) {
   container->assemble();
   for (auto* reactor : container->reactors()) {
@@ -112,6 +130,8 @@ void Environment::assemble() { // NOLINT(readability-function-cognitive-complexi
           source_port->add_outward_binding(destination_port);
           log::Debug() << "from: " << source_port->fqn() << "(" << source_port << ")"
                        << " --> to: " << destination_port->fqn() << "(" << destination_port << ")";
+          reactor::validate(source_port != destination_port,
+                            "Self wiring detected; from " + source_port->fqn() + " --> " + destination_port->fqn());
         }
       } else {
         if (properties.type_ == ConnectionType::Enclaved || properties.type_ == ConnectionType::PhysicalEnclaved ||
@@ -221,6 +241,8 @@ void Environment::export_dependency_graph(const std::string& path) {
   std::ofstream dot;
   dot.open(path);
 
+  dependency_graph_and_indexes();
+
   // sort all reactions_ by their index
   std::map<unsigned int, std::vector<Reaction*>> reactions_by_index;
   for (auto* reaction : reactions_) {
@@ -259,7 +281,7 @@ void Environment::export_dependency_graph(const std::string& path) {
 
   dot.close();
 
-  log_.info() << "Reaction graph was written to " << path;
+  log_.debug() << "Reaction graph was written to " << path;
 }
 
 void Environment::calculate_indexes() {
@@ -317,7 +339,7 @@ auto Environment::startup() -> std::thread {
   return startup(get_physical_time());
 }
 
-auto Environment::startup(const TimePoint& start_time) -> std::thread {
+void Environment::dependency_graph_and_indexes() {
   validate(this->phase() == Phase::Assembly, "startup() may only be called during assembly phase!");
 
   log::Debug() << "Building the Dependency-Graph";
@@ -327,6 +349,16 @@ auto Environment::startup(const TimePoint& start_time) -> std::thread {
 
   calculate_indexes();
 
+  phase_ = Phase::Indexing;
+}
+
+auto Environment::startup(const TimePoint& start_time) -> std::thread {
+  if (phase_ == Phase::Assembly) {
+    dependency_graph_and_indexes();
+  }
+
+  validate(this->phase() == Phase::Indexing, "startup() may only be called during Indexing phase!");
+
   log_.debug() << "Starting the execution";
   phase_ = Phase::Startup;
 
diff --git a/lib/reactor.cc b/lib/reactor.cc
index f42488b0..ae9bdb57 100644
--- a/lib/reactor.cc
+++ b/lib/reactor.cc
@@ -47,7 +47,7 @@ void Reactor::register_output(BasePort* port) {
   reactor_assert(port != nullptr);
   reactor::validate(this->environment()->phase() == Phase::Construction,
                     "Ports can only be registered during construction phase!");
-  [[maybe_unused]] bool result = inputs_.insert(port).second;
+  [[maybe_unused]] bool result = outputs_.insert(port).second;
   reactor_assert(result);
   Statistics::increment_ports();
 }
@@ -55,8 +55,9 @@ void Reactor::register_output(BasePort* port) {
 void Reactor::register_reaction([[maybe_unused]] Reaction* reaction) {
   reactor_assert(reaction != nullptr);
 
-  validate(this->environment()->phase() == Phase::Construction,
-           "Reactions can only be registered during construction phase!");
+  validate((this->environment()->phase() == Phase::Construction) ||
+            (this->environment()->phase() == Phase::Assembly),
+           "Reactions can only be registered during construction or assembly phase!");
   [[maybe_unused]] bool result = reactions_.insert(reaction).second;
   reactor_assert(result);
   Statistics::increment_reactions();
diff --git a/lib/reactor_element.cc b/lib/reactor_element.cc
index 32db319f..b0faae83 100644
--- a/lib/reactor_element.cc
+++ b/lib/reactor_element.cc
@@ -25,7 +25,8 @@ ReactorElement::ReactorElement(const std::string& name, ReactorElement::Type typ
   this->environment_ = container->environment();
   reactor_assert(this->environment_ != nullptr);
   validate(this->environment_->phase() == Phase::Construction ||
-               (type == Type::Action && this->environment_->phase() == Phase::Assembly),
+               (type == Type::Action && this->environment_->phase() == Phase::Assembly) ||
+               (type == Type::Reaction && this->environment_->phase() == Phase::Assembly),
            "Reactor elements can only be created during construction phase!");
   // We need a reinterpret_cast here as the derived class is not yet created
   // when this constructor is executed. dynamic_cast only works for
diff --git a/reactor-sdk/CMakeLists.txt b/reactor-sdk/CMakeLists.txt
new file mode 100644
index 00000000..bf3f9021
--- /dev/null
+++ b/reactor-sdk/CMakeLists.txt
@@ -0,0 +1,20 @@
+set (ReactorSDK_LIB reactor-sdk)
+
+include(GNUInstallDirs)
+
+find_package(reactor-cpp PATHS )
+
+set(REACTOR_CPP_SDK_INCLUDE "include")
+
+add_library(${ReactorSDK_LIB} SHARED Environment.cc Reactor.cc)
+
+target_link_libraries(${ReactorSDK_LIB} PUBLIC reactor-cpp)
+
+install(TARGETS ${ReactorSDK_LIB} EXPORT ${ReactorSDK_LIB}Config
+      ARCHIVE  DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
+      LIBRARY  DESTINATION "${CMAKE_INSTALL_LIBDIR}" OPTIONAL
+      RUNTIME  DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
+
+install(EXPORT ${ReactorSDK_LIB}Config DESTINATION share/${ReactorSDK_LIB}/cmake)
+
+export(TARGETS ${ReactorSDK_LIB} FILE ${ReactorSDK_LIB}Config.cmake)
\ No newline at end of file
diff --git a/reactor-sdk/Environment.cc b/reactor-sdk/Environment.cc
new file mode 100644
index 00000000..f0718b93
--- /dev/null
+++ b/reactor-sdk/Environment.cc
@@ -0,0 +1,127 @@
+#include <fstream>
+#include <unordered_map>
+#include <map>
+#include <cstdlib>
+
+#include "reactor-sdk/Environment.hh"
+#include "reactor-sdk/Reactor.hh"
+
+using namespace std;
+
+namespace sdk
+{
+
+std::map<std::string, std::string> type_convert = {
+    {"i", "int"},
+    {"j", "uint32_t"},
+    {"b", "bool"},
+    {"f", "float"},
+    {"d", "double"},
+    {"c", "char"},
+    {"s", "std::string"},
+    {"ll", "long long"},
+    {"ul", "unsigned long"},
+    {"uc", "unsigned char"},
+    {"ld", "long double"},
+    {"uint", "unsigned int"}
+};
+
+Environment::Environment( ConfigParameterBase *cfg_param, unsigned int num_workers, bool fast_fwd_execution,
+                                        const reactor::Duration& timeout, bool _cfg_gen)
+    : reactor::Environment (num_workers, fast_fwd_execution, timeout), config_parameters(cfg_param), cfg_gen(_cfg_gen) {
+}
+
+
+void Environment::run()
+{
+    if (this->config_parameters) {
+        this->config_parameters->pull_config();
+        // instance->config_parameters->display();
+    }
+
+    this->construct();
+    this->assemble();
+
+    if (this->config_parameters) {
+        if (this->config_parameters->validate() != 0) {
+            reactor::log::Error() << "INVALID CONFIGURATION!";
+            return;
+        }
+    }
+
+    this->export_dependency_graph("graph.dot");
+    int ret = std::system("dot -Tpng graph.dot -o graph.png");
+    if (ret != 0) {
+        reactor::log::Error() << "Error: Failed to generate graph diagram from dot file";
+    }
+    
+    if (cfg_gen) {
+        std::set<std::string> types;
+        std::map<std::string, std::string> homog_map_entries;
+        std::map<std::string, std::string> hetero_map_entries;
+        for (auto *reactor : top_tier_reactors) {
+            reactor->populate_params (types, homog_map_entries, hetero_map_entries);
+        }
+        std::string type_str = "";
+        bool first = true;
+        for (auto &type : types) {
+            type_str += first ? type : (", " + type);
+            first = false;
+        }
+        std::cout << "TYPE_STR:" << type_str << std::endl;
+
+        std::string homog_entry_str = "";
+        first = true;
+        for (auto &entry : homog_map_entries) {
+            homog_entry_str += first ? ("\t\t" + entry.second) : (",\n\t\t" + entry.second);
+            first = false;
+        }
+        std::cout << "HOMOG_ENTRY_STR:" << homog_entry_str << std::endl;
+
+        std::string hetero_entry_str = "";
+        first = true;
+        for (auto &entry : hetero_map_entries) {
+            hetero_entry_str += first ? ("\t\t" + entry.second) : (",\n\t\t" + entry.second);
+            first = false;
+        }
+        std::cout << "HETERO_ENTRY_STR:" << hetero_entry_str << std::endl;
+
+        std::string header_file_str =    std::string("#pragma once\n") +
+                                        "#include <reactor-sdk/reactor-sdk.hh>\n" +
+                                        "#include <map>\n" +
+                                        "#include <variant>\n" +
+                                        "#include <string>\n\n" +
+                                        "using namespace sdk;\n\n" +
+                                        "struct UserParameters : public ConfigParameter<" + type_str + "> {\n" +
+                                        "\tConfigParameter<" + type_str + ">::ParametersMap homogeneous_config();\n" +
+                                        "\tConfigParameter<" + type_str + ">::ParametersMap heterogeneous_config();\n" +
+                                        "};\n" +
+                                        "extern UserParameters cfg_parameters;";
+
+        std::ofstream header("GeneratedConfig.hh");
+        if (!header) {
+            cout << "ERROR: Failed to open header file\n";
+        } else {
+            header << header_file_str;
+        }
+
+        std::string source_file_str =   std::string("#include \"GeneratedConfig.hh\"\n\n") +
+                                        "UserParameters cfg_parameters;\n\n" +
+                                        "ConfigParameter<" + type_str + ">::ParametersMap UserParameters::homogeneous_config() {\n" +
+                                        "\treturn {\n" + homog_entry_str + "\n\t};\n}\n\n" +
+                                        "ConfigParameter<" + type_str + ">::ParametersMap UserParameters::heterogeneous_config() {\n" +
+                                        "\treturn {\n" + hetero_entry_str + "\n\t};\n}";
+
+        std::ofstream source("GeneratedConfig.cc");
+        if (!source) {
+            cout << "ERROR: Failed to open source file\n";
+        } else {
+            source << source_file_str;
+        }
+        return;
+    }
+    auto thread = this->startup();
+    thread.join();
+}
+
+} // namespace sdk
\ No newline at end of file
diff --git a/reactor-sdk/Reactor.cc b/reactor-sdk/Reactor.cc
new file mode 100644
index 00000000..d2406cc8
--- /dev/null
+++ b/reactor-sdk/Reactor.cc
@@ -0,0 +1,75 @@
+#include <fstream>
+#include <unordered_map>
+#include <map>
+#include "reactor-sdk/Reactor.hh"
+#include "reactor-sdk/Environment.hh"
+
+using namespace std;
+
+namespace sdk
+{
+
+std::string Reactor::BankName(const std::string& name) {
+    std::string bank_name = name;
+    size_t index = bank_name.rfind("\r\n");
+    if (index != std::string::npos) {
+        bank_name.replace(index, strlen("\r\n"), "_");
+    }
+    return bank_name;
+}
+
+std::string Reactor::HomogName(const std::string& name) {
+    std::string h_name = name;
+    size_t index = h_name.rfind("\r\n");
+    if (index != std::string::npos) {
+        return h_name.substr(0, index);
+    }
+    return name;
+}
+
+Reactor::Reactor(const std::string &name, Environment *env)
+    : reactor::Reactor(BankName(name), (reactor::Environment*)env), env(env) {
+    env->add_reactor(this);
+    homog_name = HomogName(name);
+}
+
+Reactor::Reactor(const std::string &name, Reactor *container)
+    : reactor::Reactor(BankName(name), container), env(container->env), parent(container) {
+    container->add_child (this);
+    homog_name = container->homog_name + "." + HomogName(name);
+}
+
+void Reactor::add_child(Reactor* reactor) {
+    [[maybe_unused]] bool result = child_reactors.insert(reactor).second;
+    reactor_assert(result);
+}
+
+void Reactor::add_to_reaction_map (std::string &name, std::shared_ptr<ReactionBase> reaction) {
+    reaction_map[name] = reaction;
+}
+
+void Reactor::construct() {
+    if (p_param) {
+        p_param->fetch_config();
+    }
+    construction();
+}
+void Reactor::assemble() {
+    if (reaction_internals_) {
+        reaction_internals_->assemble();
+    }
+    wiring();
+}
+
+void Reactor::populate_params(std::set<std::string> &types, std::map<std::string, std::string> &homog_map_entries, std::map<std::string, std::string> &hetero_map_entries) {
+    if (p_param) {
+        p_param->populate_params (types, homog_map_entries, hetero_map_entries);
+    }
+
+    for (auto *reactor : child_reactors) {
+        reactor->populate_params(types, homog_map_entries, hetero_map_entries);
+    }
+
+}
+
+} // namespace sdk
\ No newline at end of file
diff --git a/test/ActionDelay/CMakeLists.txt b/test/ActionDelay/CMakeLists.txt
new file mode 100644
index 00000000..31bb9c9a
--- /dev/null
+++ b/test/ActionDelay/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(ActionDelay VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET ActionDelay)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/test/ActionDelay/main.cc b/test/ActionDelay/main.cc
new file mode 100644
index 00000000..12a6b6b6
--- /dev/null
+++ b/test/ActionDelay/main.cc
@@ -0,0 +1,176 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace std;
+using namespace sdk;
+
+class GeneratedDelay : public Reactor {
+    LogicalAction<void> act{"act", this, 100ms};
+
+    REACTION_SCOPE_START_NO_PARAMS(GeneratedDelay)
+        int y_state = 0;
+
+        void add_reactions (GeneratedDelay *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->y_in).
+                dependencies().
+                effects(&reactor->act).
+                function (
+                    [this](Input<int> &y_in, LogicalAction<void> &act) {
+                        y_state = *y_in.get();
+                        act.schedule();
+                    }
+                );
+            
+            reaction ("reaction_2").
+                triggers(&reactor->act).
+                dependencies().
+                effects(&reactor->y_out).
+                function (
+                    [this](LogicalAction<void> &act, Output<int> &y_out) {
+                        y_out.set(y_state);
+                    }
+                );
+        }
+    REACTION_SCOPE_END_NO_PARAMS(this)
+public:
+    GeneratedDelay(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    GeneratedDelay(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Input<int> y_in{"y_in", this};
+    Output<int> y_out{"y_out", this};
+
+    void construction() {}
+
+    void wiring() {}
+};
+
+class Source : public Reactor {
+    REACTION_SCOPE_START_NO_PARAMS(Source)
+        void add_reactions (Source *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects(&reactor->out).
+                function (
+                    [this](Startup &startup, Output<int> &out) {
+                        out.set(1);
+                    }
+                );
+        }
+    REACTION_SCOPE_END_NO_PARAMS(this)
+public:
+    Source(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Source(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Output<int> out{"out", this};
+
+    void construction() {}
+    void wiring() {}
+};
+
+class Sink : public Reactor {
+    REACTION_SCOPE_START_NO_PARAMS(Sink)
+        void add_reactions (Sink *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->in).
+                dependencies().
+                effects().
+                function (
+                    [this](Input<int> &in) {
+                        auto elapsed_logical = get_elapsed_logical_time();
+                        auto logical = get_logical_time();
+                        auto physical = get_physical_time();
+                        std::cout << "logical time: " << logical << '\n';
+                        std::cout << "physical time: " << physical << '\n';
+                        std::cout << "elapsed logical time: " << elapsed_logical << '\n';
+                        if (elapsed_logical != 100ms) {
+                        std::cerr << "ERROR: Expected 100 msecs but got " << elapsed_logical << '\n';
+                        exit(1);
+                        } else {
+                        std::cout << "SUCCESS. Elapsed logical time is 100 msec.\n";
+                        }
+                    }
+                );
+        }
+    REACTION_SCOPE_END_NO_PARAMS(this)
+public:
+    Sink(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Sink(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Input<int> in{"in", this};
+
+    void construction() {}
+    void wiring() {}
+};
+
+class ActionDelay : public Reactor {
+    std::unique_ptr<Source> source;
+    std::unique_ptr<Sink> sink;
+    std::unique_ptr<GeneratedDelay> g;
+public:
+    ActionDelay(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    ActionDelay(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+    
+    void construction() {
+        source = std::make_unique<Source>("Source", this);
+        sink = std::make_unique<Sink>("Sink", this);
+        g = std::make_unique<GeneratedDelay>("GeneratedDelay", this);
+    }
+
+    void wiring() {
+        source->out --> g->y_in;
+        g->y_out --> sink->in;
+    }
+};
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("ActionDelay", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {nullptr, workers, fast, timeout, cfg_gen};
+    auto action_delay = new ActionDelay("ActionDelay", &sim);
+
+    sim.run();
+    return 0;
+}
diff --git a/test/ActionIsPresent/CMakeLists.txt b/test/ActionIsPresent/CMakeLists.txt
new file mode 100644
index 00000000..0adc43de
--- /dev/null
+++ b/test/ActionIsPresent/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(ActionIsPresent VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET ActionIsPresent)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/test/ActionIsPresent/main.cc b/test/ActionIsPresent/main.cc
new file mode 100644
index 00000000..c065299d
--- /dev/null
+++ b/test/ActionIsPresent/main.cc
@@ -0,0 +1,110 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace std;
+using namespace sdk;
+
+class ActionIsPresent : public Reactor {
+    struct Parameters : public SystemParametersStandalone<Duration> {
+        REACTOR_PARAMETER (Duration, offset, "offset", 1ns, 10ns, 1ns);
+        REACTOR_PARAMETER (Duration, period, "period", 500ms, 1s, 500ms);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<Duration>(container) {
+            register_parameters (offset, period);
+        }
+    };
+    Parameters parameters{this};
+
+    REACTION_SCOPE_START(ActionIsPresent, Parameters)
+        bool success = false;
+        Duration zero = 0ns;
+        void add_reactions(ActionIsPresent *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->startup, &reactor->a).
+                dependencies().
+                effects().
+                function (
+                    [this](Startup &startup, LogicalAction<void> &a) {
+                        if (!a.is_present()) {
+                            if (parameters.offset.value == zero) {
+                                std::cout << "Hello World!" << '\n';
+                                success = true;
+                            } else {
+                                a.schedule(parameters.offset.value);
+                            }
+                        } else {
+                            std::cout << "Hello World 2!" << '\n';
+                            success = true;
+                        }
+                    }
+                );
+            
+            reaction ("reaction_2").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function (
+                    [this](Shutdown &shutdown) {
+                        if (!success) {
+                            std::cerr << "Failed to print 'Hello World!'" << '\n';
+                            exit(1);
+                        }
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+    LogicalAction<void> a{"a", this};
+public:
+    ActionIsPresent(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    ActionIsPresent(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+    
+    void construction() {}
+    void wiring() {}
+};
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("ActionIsPresent", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {nullptr, workers, fast, timeout, cfg_gen};
+    auto action_delay = new ActionIsPresent("ActionIsPresent", &sim);
+
+    sim.run();
+    return 0;
+}
diff --git a/test/Deadlines/CMakeLists.txt b/test/Deadlines/CMakeLists.txt
new file mode 100644
index 00000000..aef0a597
--- /dev/null
+++ b/test/Deadlines/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(Deadlines VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET Deadlines)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/test/Deadlines/main.cc b/test/Deadlines/main.cc
new file mode 100644
index 00000000..ee938cb7
--- /dev/null
+++ b/test/Deadlines/main.cc
@@ -0,0 +1,174 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace std;
+using namespace sdk;
+
+class Source : public Reactor {
+    struct Parameters : public SystemParametersStandalone<Duration> {
+        REACTOR_PARAMETER (Duration, period, "period", 1s, 5s, 2s);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<Duration>(container) {
+            register_parameters (period);
+        }
+    };
+    Parameters parameters{this};
+
+    REACTION_SCOPE_START(Source, Parameters)
+        int count = 0;
+        void add_reactions(Source *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->t).
+                dependencies().
+                effects(&reactor->y).
+                function (
+                    [this](Timer &t, Output<int> &y) {
+                        if (count % 2 == 1) {
+                            // The count variable is odd.
+                            // Take time to cause a deadline violation.
+                            std::this_thread::sleep_for(400ms);
+                        }
+                        std::cout << "Source sends: " << count << std::endl;
+                        y.set(count);
+                        count++;
+                    }
+                );
+        }
+    REACTION_SCOPE_END(this, parameters)
+
+    Timer t{"t", this};
+public:
+    Source(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Source(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Output<int> y{"y", this};
+
+    void construction() {
+        t.set_timer (parameters.period.value, 0ns);
+    }
+
+    void wiring() {}
+};
+
+class Destination : public Reactor {
+    struct Parameters : public SystemParametersStandalone<Duration> {
+        REACTOR_PARAMETER (Duration, timeout, "timeout", 100ms, 1s, 200ms);
+
+        Parameters(Reactor *container)
+            :   SystemParametersStandalone<Duration>(container) {
+            register_parameters (timeout);
+        }
+    };
+    Parameters parameters{this};
+
+    REACTION_SCOPE_START(Destination, Parameters)
+        int count = 0;
+        void add_reactions(Destination *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->x).
+                dependencies().
+                effects().
+                function (
+                    [this](Input<int> &x) {
+                        std::cout << "Destination receives: " << *x.get() << std::endl;
+                        if (count % 2 == 1) {
+                            // The count variable is odd, so the deadline should have been
+                            // violated
+                            std::cerr << "ERROR: Failed to detect deadline." << std::endl;
+                            exit(1);
+                        }
+                        count++;
+                    }
+                ).deadline (parameters.timeout.value,
+                    [this](Input<int> &x) {
+                        std::cout << "Destination deadline handler receives: "
+                            << *x.get() << std::endl;
+                        if (count % 2 == 0) {
+                            // The count variable is even, so the deadline should not have
+                            // been violated.
+                            std::cerr << "ERROR: Deadline handler invoked without deadline "
+                                    << "violation." << std::endl;
+                            exit(2);
+                        }
+                        count++;
+                    });
+        }
+    REACTION_SCOPE_END(this, parameters)
+public:
+    Destination(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Destination(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    Input<int> x{"x", this};
+
+    void construction() {}
+
+    void wiring() {
+    }
+};
+
+class Deadline : public Reactor {
+    std::unique_ptr<Source> s;
+    std::unique_ptr<Destination> d;
+public:
+    Deadline(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Deadline(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+    
+    void construction() {
+        s = std::make_unique<Source>("Source", this);
+        d = std::make_unique<Destination>("Destination", this);
+    }
+
+    void wiring() {
+        s->y --> d->x;
+    }
+};
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("Deadlines", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = 4s;
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {nullptr, workers, fast, timeout, cfg_gen};
+    auto dl = new Deadline("Deadline", &sim);
+
+    sim.run();
+    return 0;
+}
diff --git a/test/ReactionOrder/CMakeLists.txt b/test/ReactionOrder/CMakeLists.txt
new file mode 100644
index 00000000..73c3a1d6
--- /dev/null
+++ b/test/ReactionOrder/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.9)
+project(ReactionOrder VERSION 0.0.0 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard is cached for visibility in external tools." FORCE)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
+
+set(LF_MAIN_TARGET ReactionOrder)
+
+find_package(reactor-cpp PATHS )
+find_package(reactor-sdk PATHS )
+
+add_executable(${LF_MAIN_TARGET}
+    main.cc
+)
+
+include_directories(${CMAKE_CURRENT_LIST_DIR})
+
+target_link_libraries(${LF_MAIN_TARGET} reactor-cpp)
+target_link_libraries(${LF_MAIN_TARGET} reactor-sdk)
+
+target_compile_options(${LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
\ No newline at end of file
diff --git a/test/ReactionOrder/main.cc b/test/ReactionOrder/main.cc
new file mode 100644
index 00000000..8cae5e35
--- /dev/null
+++ b/test/ReactionOrder/main.cc
@@ -0,0 +1,120 @@
+
+#include <reactor-sdk/reactor-sdk.hh>
+
+using namespace std;
+using namespace sdk;
+
+class Src : public Reactor {
+    REACTION_SCOPE_START_NO_PARAMS(Src)
+        void add_reactions (Src *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->startup).
+                dependencies().
+                effects(&reactor->out).
+                function (
+                    [this](Startup &startup, Output<unsigned> &out) {
+                        out.set(42);
+                    }
+                );
+        }
+    REACTION_SCOPE_END_NO_PARAMS(this)
+public:
+    Output<unsigned> out{"out", this};
+
+    Src(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Src(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+    
+    void construction() {}
+    void wiring() {}
+};
+
+class Sink : public Reactor {
+    REACTION_SCOPE_START_NO_PARAMS(Sink)
+        void add_reactions (Sink *reactor) {
+            reaction ("reaction_1").
+                triggers(&reactor->startup).
+                dependencies(&reactor->in).
+                effects().
+                function (
+                    [this](Startup &startup, Input<unsigned> &in) {
+                        if (!in.is_present()) {
+                            reactor::log::Error() << "Received no value";
+                            exit(1);
+                        }
+                        if(*in.get() != 42) {
+                            reactor::log::Error() << "Received an unexpected value";
+                            exit(1);
+                        }
+                    }
+                );
+
+            reaction ("reaction_2").
+                triggers(&reactor->shutdown).
+                dependencies().
+                effects().
+                function (
+                    [this](Shutdown &shutdown) {
+                        reactor::log::Info() << "Success!";
+                    }
+                );
+        }
+    REACTION_SCOPE_END_NO_PARAMS(this)
+public:
+    Input<unsigned> in{"in", this};
+
+    Sink(const std::string &name, Environment *env)
+        : Reactor(name, env) {}
+    Sink(const std::string &name, Reactor *container)
+        : Reactor(name, container) {}
+
+    void construction() {}
+    void wiring() {}
+};
+
+int main(int argc, char **argv) {
+    cxxopts::Options options("ReactionOrder", "Multiport source connecting to banked sink reactors");
+
+    unsigned workers = std::thread::hardware_concurrency();
+    bool fast{false};
+    reactor::Duration timeout = reactor::Duration::max();
+    bool cfg_gen{false};
+
+    // the timeout variable needs to be tested beyond fitting the Duration-type 
+    options
+    .set_width(120)
+    .add_options()
+        ("w,workers", "the number of worker threads used by the scheduler", cxxopts::value<unsigned>(workers)->default_value(std::to_string(workers)), "'unsigned'")
+        ("o,timeout", "Time after which the execution is aborted.", cxxopts::value<reactor::Duration>(timeout)->default_value(time_to_string(timeout)), "'FLOAT UNIT'")
+        ("f,fast", "Allow logical time to run faster than physical time.", cxxopts::value<bool>(fast)->default_value("false"))
+        ("c,config-gen", "Generate configuration files for the topology.", cxxopts::value<bool>(cfg_gen)->default_value("false"))
+        ("help", "Print help");
+
+    cxxopts::ParseResult result{};
+    bool parse_error{false};
+    try {
+    result = options.parse(argc, argv);
+    } catch (const cxxopts::OptionException& e) {
+    reactor::log::Error() << e.what();
+    parse_error = true;
+    }
+
+    // if parameter --help was used or there was a parse error, print help
+    if (parse_error || result.count("help"))
+    {
+        std::cout << options.help({""});
+        return parse_error ? -1 : 0;
+    }
+
+    std::cout << "parameters - workers:" << workers << " fast:" << (fast ? "True" : "False") << " timeout:" << timeout << " cfg_gen:" << (cfg_gen ? "True" : "False") << std::endl;
+
+    Environment sim {nullptr, workers, fast, timeout, cfg_gen};
+    
+    auto src = new Src("src", &sim);
+    auto sink = new Sink("sink", &sim);
+    src->out --> sink->in;
+
+    sim.run();
+    return 0;
+}