<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://cppmodel.com/blog</id>
    <title>CppModel Blog</title>
    <updated>2026-08-03T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://cppmodel.com/blog"/>
    <subtitle>CppModel Blog</subtitle>
    <icon>https://cppmodel.com/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Add a simulation CI to your process-based code]]></title>
        <id>https://cppmodel.com/blog/simulation-ci-flow</id>
        <link href="https://cppmodel.com/blog/simulation-ci-flow"/>
        <updated>2026-08-03T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to add a simulation CI to your process-based code]]></summary>
        <content type="html"><![CDATA[<p>Continuous integration (CI) is a crucial practice in modern software development, helping teams maintain code quality and catch issues early. In the control systems world, concepts like model-in-the-loop (MIL), software-in-the-loop (SIL), and hardware-in-the-loop (HIL) are widely used to validate and verify control algorithms before deploying them to real hardware. In this blog post, we will explore how to integrate simulation-based testing into your CI pipeline for process-based code.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-simulation-ci">Why Simulation CI?<a href="https://cppmodel.com/blog/simulation-ci-flow#why-simulation-ci" class="hash-link" aria-label="Direct link to Why Simulation CI?" title="Direct link to Why Simulation CI?" translate="no">​</a></h2>
<p>Traditional CI pipelines focus on unit tests and integration tests, which are essential for ensuring that your code behaves as expected. However, these tests are usually static and focus on a binary result (pass/fail), often lacking the ability to capture the dynamic behavior of your system. MIL, SIL, and HIL testing, on the other hand, let you simulate the behavior of your system under various conditions, providing a more comprehensive validation of your algorithms.</p>
<p>With CppModel, the code you write is the model itself, so you can get significantly closer to MIL and SIL testing — and even HIL testing, if you have access to the hardware — while still staying in the code domain. This makes it easy to integrate simulation-based testing into your existing software pipelines and workflows, so you can catch issues early and break down test results into specific pass and fail time intervals, which is crucial for understanding how your system behaves over time.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-simulation-ci">Setting Up Simulation CI<a href="https://cppmodel.com/blog/simulation-ci-flow#setting-up-simulation-ci" class="hash-link" aria-label="Direct link to Setting Up Simulation CI" title="Direct link to Setting Up Simulation CI" translate="no">​</a></h2>
<p>For this example, we will follow the process described in the previous blog post <a href="https://www.cppmodel.com/blog/glass-of-water" target="_blank" rel="noopener noreferrer" class="">How do you fill a glass of water?</a> which is already modeled in CppModel. As the code is already hosted on GitHub, we will use GitHub Actions to set up our CI pipeline.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="code-setup">Code setup<a href="https://cppmodel.com/blog/simulation-ci-flow#code-setup" class="hash-link" aria-label="Direct link to Code setup" title="Direct link to Code setup" translate="no">​</a></h3>
<p>We will focus on the Glass of Water simulation and prepare a short verification test that checks whether the simulation behaves as expected. To track the simulation result at every step of time, we will use <code>CppModel.StepResult</code>, which captures the current step result and lets us summarize the overall pass/fail status of the simulation. This gives us a clear, easy-to-integrate view of simulation performance in the CI pipeline. To make sure the executable runs as a test in the CI pipeline, we will use CMake's <code>enable_testing()</code> and <code>add_test()</code> functions, which let us run the simulation as part of our CI pipeline and check the results.</p>
<p>Here is the <code>CMakeLists.txt</code> file for the Glass of Water simulation, with the verification source and test setup added:</p>
<div class="language-cmake codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cmake codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cmake_minimum_required(VERSION 3.12)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">project(GlassOfWater_Project LANGUAGES C CXX)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set(CMAKE_CXX_STANDARD 17)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">enable_testing()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_definitions(-DCPPHTTPLIB_OPENSSL_SUPPORT)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">include_directories(dependencies/include)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">link_directories(dependencies/lib)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_executable(GlassOfWaterSim GlassOfWaterSim.c GlassOfWaterLib.c GlassOfWaterVerification.c)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">set_target_properties(GlassOfWaterSim PROPERTIES LINKER_LANGUAGE CXX)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">if (WIN32)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_link_libraries(GlassOfWaterSim CppModelBase ixwebsocket crypto ssl z crypt32 ws2_32)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">else()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    target_link_libraries(GlassOfWaterSim CppModelBase ixwebsocket crypto ssl z)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endif()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_test(NAME GlassOfWaterSim_Test COMMAND GlassOfWaterSim)</span><br></span></code></pre></div></div>
<p>I'll break the verification of the process down into three steps:</p>
<ul>
<li class=""><strong>Pre-filling</strong> - The simulation will not fail, as we have not yet initialized the inputs and started the process. Since we may be adding water to an already-filled glass, the glass's initial state is not important for this simulation.</li>
<li class=""><strong>Filling</strong> - The simulation will fail if the tap is open and the level does not change (for this example we will ignore any transport delay that may be present in the system).</li>
<li class=""><strong>Post-filling</strong> - To keep it simple, we will consider the simulation successful if the level is equal to or greater than the desired level.</li>
</ul>
<p>The goal of this article is to present the CI workflow, so we won't get into the specifics of testing against exact time intervals or specific interface values. Instead, we'll break the steps down based on tap opening and closing. With this in mind, we can implement a simple state machine that initializes in the Pre-filling state and transitions to Filling when the tap opens, then to Post-filling when the tap closes.</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token macro property directive-hash" style="color:#36acaa">#</span><span class="token macro property directive keyword" style="color:#00009f">include</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property string" style="color:#e3116c">"GlassOfWaterVerification.h"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">typedef</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">enum</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    PRE_FILLING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    FILLING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    POST_FILLING</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> FillingState_t</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> FillingState_t fillingState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> PRE_FILLING</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PreFillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> delta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Before we start filling, the result is always satisfactory, as we haven't started yet.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">FillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> delta</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> previousLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actualLevel </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> previousLevel </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> actualLevel </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PostFillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> delta</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actualLevel </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">VerifyGlassFilling</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> cycleTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> previousLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">fillingState</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> PRE_FILLING</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tapOpening </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            fillingState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> FILLING</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PreFillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cycleTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> FILLING</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tapOpening </span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            fillingState </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> POST_FILLING</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">FillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cycleTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> previousLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> POST_FILLING</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">PostFillingCheck</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cycleTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">default</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Now we can use the <code>CppModel.StepResult</code> interface in our simulation code to capture the verification test's status at every point in time. I have also added a <code>previousLevel</code> variable to track the level from the previous step, which is used to check whether the level is increasing during the filling state. Here is how the <code>RunCyclic</code> function looks with the added verification logic:</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RunCyclic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CModelSimulation_ts </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> cycleTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> dt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">stepTime_ms </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Convert milliseconds to seconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> plumbingFlow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"plumbingFlow [l/min]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Default plumbing flow is 15 liters per minute converted to liters per second</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> glassCapacity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"glassCapacity [ml]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">250.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Default glass capacity is 250 ml converted to liters</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> openingSlope </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"openingSlope [%/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Default opening slope is 100% per second converted to a fraction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> closingSlope </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"closingSlope [%/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">400.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Default closing slope is 400% per second converted to a fraction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> desiredLevel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getInputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"desiredLevel [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80.0f</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Default desired level is 80%</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Control loop</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> desiredLevel </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tapOpening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">control</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> openingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> closingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualFlow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">actuator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plumbingFlow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualVolume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">glass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> glassCapacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualLevel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">glassCapacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tapOpening [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualFlow [l/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualVolume [l]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualLevel [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">uint8_t</span><span class="token plain"> stepResult </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">VerifyGlassFilling</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cycleTime</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputI8</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"CppModel.StepResult"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> stepResult</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>As you may have guessed, the simulation was expected to fail in the post-filling state, since the actual level won't exactly equal the desired level once we account for the closing slope on the tap opening — in this case we significantly overshoot the desired level.</p>
<p><img decoding="async" loading="lazy" alt="Failed simulation" src="https://cppmodel.com/assets/images/sim-desired-level-fail-2e71d4ff0cd5888b82b088073ad2eb0a.png" width="1224" height="638" class="img_ev3q"></p>
<p>The overall test result is shown as a failure in the terminal where the executable ran — in my case, a red circle with a cross in it. That's the signal our CI workflow will use to determine pass or fail. Here is what the terminal output looks like when the simulation fails:</p>
<p><img decoding="async" loading="lazy" alt="Failed simulation terminal" src="https://cppmodel.com/assets/images/desired-level-fail-7726c5f8180db86180f489fe87f9c7a7.png" width="747" height="34" class="img_ev3q"></p>
<p>In real life, nobody complains about a few extra drops of water in the glass, so let's relax the post-filling check to consider the simulation successful if the actual level is equal to or greater than the desired level. This way, the simulation will pass in the post-filling state too.</p>
<div class="language-cpp codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cpp codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actualLevel </span><span class="token operator" style="color:#393A34">&gt;=</span><span class="token plain"> desiredLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre></div></div>
<p>Here are the simulation results after the modification:</p>
<p><img decoding="async" loading="lazy" alt="Successful simulation" src="https://cppmodel.com/assets/images/sim-desired-level-success-939dcd272a53209b4f2ed2b0fecbe05e.png" width="1224" height="638" class="img_ev3q"></p>
<p>And just for completeness, here is what the terminal output looks like when the simulation passes (a blue dot in my case):</p>
<p><img decoding="async" loading="lazy" alt="Successful simulation terminal" src="https://cppmodel.com/assets/images/desired-level-success-8e645aa88997a79889d26be7cef6517b.png" width="754" height="35" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="integrating-with-github-actions">Integrating with GitHub Actions<a href="https://cppmodel.com/blog/simulation-ci-flow#integrating-with-github-actions" class="hash-link" aria-label="Direct link to Integrating with GitHub Actions" title="Direct link to Integrating with GitHub Actions" translate="no">​</a></h3>
<p>Before we jump into the workflow implementation, let's make sure our simulation won't try to run interactively and prompt for credentials. To do this, we'll store the credentials as GitHub secrets and pass them to the simulation as environment variables, keeping them secure while letting the simulation run non-interactively. By default, CppModel checks whether the <code>CPPMODEL_USERNAME</code> and <code>CPPMODEL_PASSWORD</code> environment variables are set and uses them to authenticate with the CppModel server. If they aren't set, the simulation falls back to interactive mode and prompts for credentials.</p>
<p>Next, we'll create a GitHub Actions workflow that runs our simulation and checks the results. If the CppModel credentials are set, the workflow will automatically submit the results to the CppModel workspace, where they can be observed and analyzed. Since we're focused on simulation-based CI and expect to see those results, we'll add a credential check to the workflow so it fails outright if the credentials aren't set.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Verify CppModel credentials are set</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">CPPMODEL_USERNAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> secrets.CPPMODEL_USERNAME </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">CPPMODEL_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> secrets.CPPMODEL_PASSWORD </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          if [ -z "$CPPMODEL_USERNAME" ] || [ -z "$CPPMODEL_PASSWORD" ]; then</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">            echo "::error::CPPMODEL_USERNAME and CPPMODEL_PASSWORD secrets must be configured for this workflow"</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">            exit 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          fi</span><br></span></code></pre></div></div>
<p>The next step is to prepare the build environment. For this example we use the gcc16 container image, to which we additionally add <code>git</code> (required for the checkout action) and <code>curl</code> (to download and install the CppModel libraries). For CMake, we'll use the <code>lukka/get-cmake</code> action to install the latest version of CMake and Ninja. Here is how the build environment setup looks:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Install dependencies</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apt</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">get update </span><span class="token important">&amp;&amp;</span><span class="token plain"> apt</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">get install </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">y git curl zlib1g</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dev libssl</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">dev</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Get latest CMake and ninja</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">uses</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> lukka/get</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cmake@v3.27.7</span><br></span></code></pre></div></div>
<p>Downloading and installing the CppModel libraries is straightforward: we use <code>curl</code> to grab the latest release and extract it into the <code>./dependencies</code> directory, matching what the <code>CMakeLists.txt</code> configuration expects. Here is how that step looks:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Download and extract CppModel libraries</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          curl -fsSL https://download.cppmodel.com/CppModel-latest-Linux-gcc16.tar.gz -o cppmodel.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          mkdir -p dependencies</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          tar -xzf cppmodel.tar.gz -C dependencies --strip-components=1</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          rm cppmodel.tar.gz</span><br></span></code></pre></div></div>
<p>The final part of the workflow builds and runs the simulation. We'll also add a timeout to this step in case the credentials are wrong or the simulation gets stuck.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Run CMake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cmake </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">S . </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">B build </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">DCMAKE_BUILD_TYPE=RelWithDebInfo</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Build</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cmake </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">build build </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">config RelWithDebInfo </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">target all</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Test</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">timeout-minutes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">working-directory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> build</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">env</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">CPPMODEL_USERNAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> secrets.CPPMODEL_USERNAME </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">CPPMODEL_PASSWORD</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> secrets.CPPMODEL_PASSWORD </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">run</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ctest </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">output</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">on</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">failure</span><br></span></code></pre></div></div>
<p>Note that the <code>CPPMODEL_USERNAME</code> and <code>CPPMODEL_PASSWORD</code> secrets need to be set in your GitHub repository for this workflow to run: go to <strong>Settings &gt; Secrets and variables &gt; Actions &gt; New repository secret</strong> and add both.</p>
<p><img decoding="async" loading="lazy" alt="GitHub secrets" src="https://cppmodel.com/assets/images/github-credentials-07d387733f9967d6d2c776805c457cfc.png" width="639" height="303" class="img_ev3q"></p>
<p>All that's left is to commit the workflow file and push it to GitHub. Once triggered, the workflow runs the simulation and checks the results: if the simulation fails, the workflow fails and you're notified. If it succeeds, you can view the results under the corresponding user in the CppModel workspace, just as we did earlier.</p>
<p>Here is how the workflow looks in GitHub Actions:</p>
<p><img decoding="async" loading="lazy" alt="GitHub Actions workflow" src="https://cppmodel.com/assets/images/ci-results-87aab526709829532535d0ce02548e13.png" width="1292" height="785" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-thoughts">Final Thoughts<a href="https://cppmodel.com/blog/simulation-ci-flow#final-thoughts" class="hash-link" aria-label="Direct link to Final Thoughts" title="Direct link to Final Thoughts" translate="no">​</a></h2>
<p>Bringing simulation into CI closes a real gap in how process-based code typically gets tested. A conventional unit test tells you a function returned the right value; it doesn't tell you whether your control loop overshoots, oscillates, or settles cleanly over time. By running the actual model as the test and capturing <code>CppModel.StepResult</code> at every step, you get that dynamic, time-resolved picture for free, in the same pipeline that already gates your merges.</p>
<p>The Glass of Water example is deliberately simple, but the pattern holds for anything process-based: define the states your system moves through, decide what "correct" means in each one, and let the simulation tell you when reality drifts from that definition. The state-machine approach we used here only checks pass/fail per phase — it doesn't yet look at <em>how</em> a value evolves against a reference trajectory, which is often where the more interesting bugs hide, like quantization-induced oscillation, which capturing <code>CppModel.StepResult</code> at every step already puts you in a position to catch.</p>
<p>The bigger win in this approach is structural: simulation results are no longer something you eyeball locally and forget. They run on every push, fail the build when the system misbehaves, and land in the CppModel workspace where the whole team can inspect them.</p>
<p>If you try wiring this into a process-based project of your own — or think the pass/fail checks should be structured differently — I'd love to hear about it. Reach out or leave a comment below.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="links">Links<a href="https://cppmodel.com/blog/simulation-ci-flow#links" class="hash-link" aria-label="Direct link to Links" title="Direct link to Links" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://github.com/hristoskov/glass-of-water/tree/v0.0.2" target="_blank" rel="noopener noreferrer" class="">Simulation code</a></li>
</ul>]]></content>
        <author>
            <name>Hristo Hristoskov</name>
            <uri>https://cedge.se</uri>
        </author>
        <category label="CppModel" term="CppModel"/>
        <category label="Continuous Integration" term="Continuous Integration"/>
        <category label="Simulation" term="Simulation"/>
        <category label="Control Systems" term="Control Systems"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[So, how do you fill a glass of water?]]></title>
        <id>https://cppmodel.com/blog/glass-of-water</id>
        <link href="https://cppmodel.com/blog/glass-of-water"/>
        <updated>2026-07-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A glass of water is a simple example of a process that can be modeled and controlled]]></summary>
        <content type="html"><![CDATA[<p>I am starting this blog series with one of the simplest processes I could imagine. We do it every day and rarely stop to think about how it actually happens.</p>
<p>Take a glass, open the tap, and when the water level is high enough, close the tap. It sounds simple. It feels simple. Yet if we try to explain this process precisely enough for a machine to do it automatically, it turns out to have real structure underneath — the same structure engineers use to control much bigger things, like a thermostat keeping a room at the right temperature or a cruise control keeping a car at the right speed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-glass-itself">The glass itself<a href="https://cppmodel.com/blog/glass-of-water#the-glass-itself" class="hash-link" aria-label="Direct link to The glass itself" title="Direct link to The glass itself" translate="no">​</a></h2>
<p>Let's start with the thing being controlled — the glass. In engineering, we'd call this "the plant," but really it's just whatever we're trying to influence.</p>
<p>The idea is simple: the more water flows in, the fuller the glass gets, until it's full — after that, no matter how much more water flows, the glass doesn't get any fuller (it just overflows). So the glass's behavior can be described as a simple rule: given how much water is flowing in, and how full the glass already is, figure out how full it will be a moment later.</p>
<p>Written as a function, that rule looks like this:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">volume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">glass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">capacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> filledVolume</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> waterFlow</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Breaking this into the four parts we'll use consistently throughout this post:</p>
<p><strong>Settings</strong>, decided ahead of time and fixed while the process runs:</p>
<ul>
<li class=""><code>capacity</code> — the size of the glass</li>
</ul>
<p><strong>State</strong>, what the process remembers from one check to the next:</p>
<ul>
<li class=""><code>filledVolume</code> — how full the glass currently is</li>
</ul>
<p><strong>Input</strong>, what changes moment to moment from outside the process:</p>
<ul>
<li class=""><code>waterFlow</code> — how fast water is pouring in right now</li>
</ul>
<p><strong>Output</strong>, the result handed back:</p>
<ul>
<li class=""><code>volume</code> — the new fill level, after this moment's flow has had a chance to add water; this becomes the State going into the next check</li>
</ul>
<p>Since a computer checks on things at discrete moments in time rather than continuously, we also need to say how much time has passed since the last check, <code>dt</code>:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">volume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">glass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> filledVolume</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> waterFlow</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>And here's what that looks like as actual code — you don't need to be a programmer to follow the gist: add the water that flowed in during this brief moment, then cap the result at the glass's capacity so it doesn't "overflow" mathematically:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">glass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> filledVolume</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> waterFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> deltaVolume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> waterFlow </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> dt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    filledVolume </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> deltaVolume</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filledVolume </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        filledVolume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> filledVolume</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Now that we have a model of the glass, we can simulate it with different steady flows. Here I fill a 250 ml glass three times, with the tap running at 5, 10, and then 10 liters per minute. Here's how each fill plays out over time:
<img decoding="async" loading="lazy" alt="Glasses filling simulation" src="https://cppmodel.com/assets/images/glass-fills-76c4bfc745863014af8f4c880da4fbf5.png" width="868" height="508" class="img_ev3q"></p>
<p>Unsurprisingly, each run is a straight line climbing up to the glass's capacity, with a steeper slope for a faster flow. If you tried pouring at different, changing speeds — the way you probably experimented as a kid — you'd get more interesting, curvier lines. That's actually a common way engineers test a system: run it under different conditions and watch what happens.</p>
<p>This little piece of code is about as small as a model gets, and it happens to be written in the same programming language (C) that's often used for the real control logic driving mobile or even industrial machines. The same code would run just as well on a hobbyist kit like an Arduino or Raspberry Pi.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-controller-deciding-how-far-to-open-the-tap">The controller: deciding how far to open the tap<a href="https://cppmodel.com/blog/glass-of-water#the-controller-deciding-how-far-to-open-the-tap" class="hash-link" aria-label="Direct link to The controller: deciding how far to open the tap" title="Direct link to The controller: deciding how far to open the tap" translate="no">​</a></h2>
<p>The controller is the part that decides what to do next. It looks at the gap between "how full we want the glass to be" and "how full it actually is" (we'll call that gap the error), and decides how far open the tap valve should be, as a percentage.</p>
<p>Think about how you actually do this by hand: when you start filling a glass, you open the tap a little, then gradually more, to avoid splashing — and as you get close to the level you want, you start closing it, finishing with a firm close right at the target. Often you close much faster than you opened: spilling is a concern with the first drops of water, not the last, so once you're near the target there's no need to stay gentle. In engineering terms, this is a controller whose behavior is different depending on whether it's opening or closing — a very ordinary thing for real controllers to do.</p>
<p>Here's the shape of that idea, again as a function:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">opening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">control</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">openingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> closingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualOpening</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Breaking this into the same four parts:</p>
<p><strong>Settings</strong>, decided ahead of time:</p>
<ul>
<li class=""><code>openingSlope</code> — how quickly the valve opens, in percent per second</li>
<li class=""><code>closingSlope</code> — how quickly the valve closes, in percent per second</li>
</ul>
<p><strong>State</strong>, what the controller remembers between checks:</p>
<ul>
<li class=""><code>actualOpening</code> — how far open the valve currently is</li>
</ul>
<p><strong>Input</strong>, what changes moment to moment:</p>
<ul>
<li class=""><code>error</code> — the gap between the desired and actual water level</li>
</ul>
<p><strong>Output</strong>, the result handed back:</p>
<ul>
<li class=""><code>opening</code> — the updated valve position; here it's the same value as the State above, since the valve position is both what the controller needs to remember and what it reports back to the rest of the system</li>
</ul>
<p>Turning this into code, the controller nudges the valve open a bit if the glass isn't full enough yet, or closed a bit if it's gone past the target — always keeping the valve position between fully closed (0%) and fully open (100%):</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">control</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> openingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> closingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualOpening</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// we need to open the tap</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        actualOpening </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> openingSlope </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> dt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actualOpening </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// limit to 100%</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            actualOpening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// we need to close the tap</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        actualOpening </span><span class="token operator" style="color:#393A34">-=</span><span class="token plain"> closingSlope </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> dt</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// error is negative, so this will decrease actualOpening</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">actualOpening </span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// limit to 0%</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            actualOpening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> actualOpening</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>To check that this behaves sensibly, I simulated a 2-second burst where the error is pinned at 100% (as if the glass were completely empty and we wanted it completely full), using:</p>
<ul>
<li class="">opening speed: 100% per second</li>
<li class="">closing speed: 200% per second</li>
</ul>
<p>Here's the result:
<img decoding="async" loading="lazy" alt="Tap controller simulation" src="https://cppmodel.com/assets/images/tap-control-5365820d19648443259ffae0cec96d0a.png" width="868" height="508" class="img_ev3q"></p>
<p>With both pieces defined — the glass and the controller — we can connect them into a complete, self-correcting loop: the controller looks at the gap between the desired and actual level and adjusts the valve; the valve's opening determines the flow rate; and that flow rate is what fills the glass.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-go-betweens-sensor-and-actuator">The go-betweens: sensor and actuator<a href="https://cppmodel.com/blog/glass-of-water#the-go-betweens-sensor-and-actuator" class="hash-link" aria-label="Direct link to The go-betweens: sensor and actuator" title="Direct link to The go-betweens: sensor and actuator" translate="no">​</a></h3>
<p>Two more small pieces connect the controller to the glass: a sensor and an actuator. They're not part of the "thinking" — they just translate between units.</p>
<p>The sensor converts the glass's water volume into a fill percentage (relative to its capacity) — like your eyes glancing at the glass and judging "it's about 80% full." The actuator converts a desired valve-opening percentage into an actual flow rate — like your hand turning the tap. In real life, sensors and actuators have their own quirks and delays, but for this example we'll treat them as perfect.</p>
<p>Fitting these into the same four parts, both are simpler than the glass or the controller: neither needs State, since neither has to remember anything between checks — each just turns its Input into an Output using a fixed Setting:</p>
<ul>
<li class="">sensor — Settings: <code>capacity</code>; Input: <code>filledVolume</code>; Output: fill percentage</li>
<li class="">actuator — Settings: <code>maxFlow</code>; Input: <code>valveOpening</code>; Output: flow rate</li>
</ul>
<p>Here's what they look like in code — each is just a one-line conversion:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> filledVolume</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> filledVolume </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">actuator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> maxFlow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> valveOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> maxFlow </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> valveOpening</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="putting-it-all-together">Putting it all together<a href="https://cppmodel.com/blog/glass-of-water#putting-it-all-together" class="hash-link" aria-label="Direct link to Putting it all together" title="Direct link to Putting it all together" translate="no">​</a></h2>
<p>Before wiring everything up, let's be clear about the goal: when you walk up to the tap, your intention is usually to fill the glass to some target, say 80% full. That target is called the setpoint.</p>
<p>In real life, your eyes are the sensor, judging the fill level, and your hand is the actuator, opening and closing the valve to some flow rate between zero and the maximum the plumbing can deliver. You probably don't think about that maximum flow much, but it's usually enough to fill a small glass in about a second — here I'll use 15 liters per minute. Put together, the controller's job is: look at the gap between the desired and actual level, and output how far open the valve should be. Here's what that whole system looks like as a block diagram:
<img decoding="async" loading="lazy" alt="Control system block diagram" src="https://cppmodel.com/assets/images/glass-control-80f1620c839d0653ebdbe0645c327dc9.png" width="732" height="191" class="img_ev3q"></p>
<p>I simulated this full system aiming for 80% full, with:</p>
<ul>
<li class="">Glass capacity: 250 ml</li>
<li class="">Opening speed: 100% per second</li>
<li class="">Closing speed: 400% per second</li>
<li class="">Maximum tap flow: 15 liters per minute</li>
</ul>
<p>With the rest of the functions already sitting in a file called <code>GlassOfWaterLib.c</code> (and its header, <code>GlassOfWaterLib.h</code>), here's the simulation code that wires them together using the CppModel framework — all in under 50 lines:</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token macro property directive-hash" style="color:#36acaa">#</span><span class="token macro property directive keyword" style="color:#00009f">include</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property string" style="color:#e3116c">"GlassOfWaterLib.h"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token macro property directive-hash" style="color:#36acaa">#</span><span class="token macro property directive keyword" style="color:#00009f">include</span><span class="token macro property" style="color:#36acaa"> </span><span class="token macro property string" style="color:#e3116c">"cppmodel/CModel.h"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> tapOpening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualVolume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualLevel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> actualFlow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RunCyclic</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">CModelSimulation_ts </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">long</span><span class="token plain"> cycleTime</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> dt </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">stepTime_ms </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Convert milliseconds to seconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> plumbingFlow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"plumbingFlow [l/min]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">15.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// Default plumbing flow is 15 liters per minute converted to liters per second</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> glassCapacity </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"glassCapacity [ml]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">250.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000.0f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Default glass capacity is 250 ml converted to liters</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> openingSlope </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"openingSlope [%/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Default opening slope is 100% per second converted to a fraction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> closingSlope </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getParameterF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"closingSlope [%/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">400.0f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Default closing slope is 400% per second converted to a fraction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> desiredLevel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getInputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"desiredLevel [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80.0f</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.01f</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Default desired level is 80%</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Control loop</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">float</span><span class="token plain"> error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> desiredLevel </span><span class="token operator" style="color:#393A34">-</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tapOpening </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">control</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> openingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> closingSlope</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualFlow </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">actuator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">plumbingFlow</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualVolume </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">glass</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dt</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> glassCapacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    actualLevel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sensor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">glassCapacity</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tapOpening [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> tapOpening</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualFlow [l/s]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualFlow</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualVolume [l]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualVolume</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setOutputF32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"actualLevel [0.01%]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> actualLevel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    CModelSimulation_ts </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">sim </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"Glass of Water"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_setRunStepFunction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sim</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> RunCyclic</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">CppModel_Simulate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">CppModel_getSimulationResult</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sim</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>Here's how it behaves over time:
<img decoding="async" loading="lazy" alt="Closed-loop system simulation" src="https://cppmodel.com/assets/images/glass-tap-system-17eb814fbdfea64f92f7b3c3506bd669.png" width="868" height="508" class="img_ev3q"></p>
<p>You can see the glass filling as soon as the tap opens, and as it nears the 80% target, the tap starts closing. Because it closes faster than it opened, the system still slightly overshoots the target before settling. Tuning those opening and closing speeds can reduce that overshoot and make the fill smoother.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-notes">Final notes<a href="https://cppmodel.com/blog/glass-of-water#final-notes" class="hash-link" aria-label="Direct link to Final notes" title="Direct link to Final notes" translate="no">​</a></h2>
<p>I chose to simulate this entire system directly in a general-purpose programming language, without any specialized physics or simulation software, to show just how little machinery is needed to model and test a real process — even one as ordinary as filling a glass. The same approach scales up to far more complex systems; the underlying ideas don't change. By understanding how the thing you're controlling behaves, and designing a controller that responds sensibly to it, you can get useful, predictable behavior out of all kinds of automated systems.</p>
<p>Keeping the simulation this lightweight also means it can double as the real production code and be run automatically as part of a software testing pipeline. And as AI tools become part of more engineering workflows, keeping the simulation and the real control logic written in the same language makes it easier to bring those tools to bear on implementing and improving the system.</p>
<p>If you enjoyed this and want to run your own simulations, please consider subscribing to one of our plans. I've prepared an archive with the code and data used in this post, ready to run all the simulations in this article. I'll be continuing this series with more processes and control systems, so let me know your thoughts — or how you'd have approached this differently.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="links">Links<a href="https://cppmodel.com/blog/glass-of-water#links" class="hash-link" aria-label="Direct link to Links" title="Direct link to Links" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://github.com/hristoskov/glass-of-water/tree/v0.0.1" target="_blank" rel="noopener noreferrer" class="">Simulation code</a></li>
</ul>]]></content>
        <author>
            <name>Hristo Hristoskov</name>
            <uri>https://cedge.se</uri>
        </author>
        <category label="Simulation" term="Simulation"/>
        <category label="Control Systems" term="Control Systems"/>
        <category label="Process" term="Process"/>
        <category label="CppModel" term="CppModel"/>
    </entry>
</feed>