{
    "version": "https://jsonfeed.org/version/1",
    "title": "CppModel Blog",
    "home_page_url": "https://www.cppmodel.com/blog",
    "description": "CppModel Blog",
    "items": [
        {
            "id": "https://www.cppmodel.com/blog/when-ai-writes-the-code",
            "content_html": "<p>Picture a mobile machine during testing. An experienced operator makes a movement and notices something immediately. It feels a little too aggressive. Perhaps it overshoots slightly before settling, or the transition does not feel as smooth or predictable as expected. A control system engineer looks at the implementation.</p>\n<p>Not long ago, trying another approach could require a significant amount of work: study the problem, modify the algorithm, review the change, compile it, deploy it, find another machine test slot, and ask the operator to reproduce the situation. Now the first part of that process can look quite different. The engineer may ask an AI coding assistant to propose a modification that reduces the overshoot, smooths the transition, or takes a different structural approach. A plausible alternative can appear quickly.</p>\n<p>That implementation is not automatically correct. It still needs to be understood, reviewed, and verified. But producing the next candidate may no longer be the slowest part of the loop. AI assistance increasingly extends beyond implementation as well. Tools can help create tests, suggest edge cases, analyze failures, and work with test results. So the interesting change is not simply that we can write code faster. <strong>More of the engineering experiment can potentially become faster.</strong></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"faster-code-does-not-tell-us-which-code-is-better\">Faster code does not tell us which code is better<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#faster-code-does-not-tell-us-which-code-is-better\" class=\"hash-link\" aria-label=\"Direct link to Faster code does not tell us which code is better\" title=\"Direct link to Faster code does not tell us which code is better\" translate=\"no\">​</a></h2>\n<p>Suppose we produce three alternative implementations. One reduces the overshoot but makes the machine feel sluggish. Another settles quickly but introduces more aggressive acceleration. A third stays close to the original behavior while removing the sharpest part of the transition. Which one is better? There is no answer hidden in the source code. The answer depends on intent: performance targets, safety constraints, mechanical limitations, energy consumption, operator comfort, and probably several other considerations.</p>\n<p>Some of that intent is captured in requirements. Some may be represented in models and tests. Some exists as engineering knowledge that has never been formally written down. And sometimes an experienced operator notices something that none of them captured. If an operator says, <em>\"This doesn't feel right,\"</em> even though the existing checks pass, that observation should not simply be dismissed. It may expose a missing requirement, an incomplete test, or behavior that the current model does not represent sufficiently well.</p>\n<p>Code is executable, but that does not make code the definition of correct behavior.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"short-feedback-loops-are-not-new\">Short feedback loops are not new<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#short-feedback-loops-are-not-new\" class=\"hash-link\" aria-label=\"Direct link to Short feedback loops are not new\" title=\"Direct link to Short feedback loops are not new\" translate=\"no\">​</a></h2>\n<p>Control system engineers already have mature ways of learning about behavior before or alongside testing the final machine. Model-Based Development, Software-in-the-Loop, Hardware-in-the-Loop, virtual controllers, and many other forms of simulation exist precisely because experimenting directly on the physical system can be expensive, slow, or difficult to repeat.</p>\n<p>There are also development environments where the feedback loop can already be very short. Engineers working with PLC environments such as CODESYS are often accustomed to changing logic, downloading it, and observing what happens on the machine during commissioning. That immediacy can be extremely useful. Teams moving more functionality into C or C++ can lose some of that feeling if the development process becomes edit, compile, deploy, and wait for access to the machine. Simulation offers another possibility: move some of that exploration away from scarce machine time while keeping a short feedback loop around the implementation. AI does not invent that idea, and it does not make established simulation and testing methods obsolete. What it may change is how frequently and cheaply we can move around the loop.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"but-changing-the-code-is-not-the-only-experiment\">But changing the code is not the only experiment<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#but-changing-the-code-is-not-the-only-experiment\" class=\"hash-link\" aria-label=\"Direct link to But changing the code is not the only experiment\" title=\"Direct link to But changing the code is not the only experiment\" translate=\"no\">​</a></h2>\n<p>We do not necessarily need to modify and recompile the control algorithm every time we want to learn something. If inputs, parameters, and test conditions can be supplied externally, the same snapshot of the implementation can be executed repeatedly under different conditions. Instead of asking only <em>\"Can I simulate this implementation?\"</em> we can start asking <em>\"Which parameter values produce the behavior we want?\"</em></p>\n<p>We can run parameter sweeps, try different input sequences, compare results, and optimize against measurable objectives without rebuilding the control algorithm for every experiment. The implementation remains fixed while the experiment around it changes. That can make a surprisingly large difference. Once an experiment becomes cheap enough to repeat, we naturally start asking questions that would have been impractical if every attempt required another build, deployment, and machine test.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"real-machine-data-can-make-the-experiment-more-meaningful\">Real machine data can make the experiment more meaningful<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#real-machine-data-can-make-the-experiment-more-meaningful\" class=\"hash-link\" aria-label=\"Direct link to Real machine data can make the experiment more meaningful\" title=\"Direct link to Real machine data can make the experiment more meaningful\" translate=\"no\">​</a></h2>\n<p>Recorded machine data gives us another way to tighten the loop. Suppose we have logs from a real machine showing inputs and measured responses during a particular movement. Instead of immediately modifying the controller until the simulated result looks better, we can first ask a different question: <strong>does our simulation reproduce what actually happened on the machine?</strong> If it does not, the limiting factor may not be the control algorithm. Perhaps the representation of the plant is too simple. Perhaps a delay is missing. Perhaps an assumption about the dynamics is wrong.</p>\n<p>Measured behavior can provide an anchor while we improve the simulation context until it represents the behavior we care about well enough for the engineering question in front of us. It does not need to become a perfect representation of the entire machine. And a model fitted to one set of logged data should still be challenged against other operating conditions and independent data rather than assumed to be universally valid.</p>\n<p>Once the simulation context is credible enough for the question we are asking, we can start changing the controller and exploring alternatives with more confidence that the observed differences are meaningful. This creates another iteration loop. We are not only iterating over code. We can iterate over parameters, inputs, assumptions, and the simulation context surrounding the code.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"different-questions-need-different-evidence\">Different questions need different evidence<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#different-questions-need-different-evidence\" class=\"hash-link\" aria-label=\"Direct link to Different questions need different evidence\" title=\"Direct link to Different questions need different evidence\" translate=\"no\">​</a></h2>\n<p>None of this means every control problem can be solved with a simple simulation. Sometimes a generated input sequence and a plot are enough. Sometimes recorded machine data provides the missing context. Sometimes we need a plant model, Software-in-the-Loop, Hardware-in-the-Loop, or a more detailed simulation. And for many questions, we still need the real machine.</p>\n<p>Real-machine testing also depends on something that is easy to underestimate: access to the right people. An experienced operator may be able to reproduce or evaluate behavior that a control system engineer cannot. The operator knows how the machine feels under load, which combinations of movements expose a problem, and when a response that looks reasonable in a graph simply does not feel right from the cab. The objective is not to replace that stage. It is to arrive there having already learned as much as we reasonably can, so that valuable machine and operator time can be used for the questions that genuinely require them.</p>\n<p>These approaches are not competitors. They provide different kinds of evidence. The engineering task is to understand what question we are trying to answer and what evidence is sufficient to answer it.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-experiment-can-become-cheaper\">The experiment can become cheaper<a href=\"https://www.cppmodel.com/blog/when-ai-writes-the-code#the-experiment-can-become-cheaper\" class=\"hash-link\" aria-label=\"Direct link to The experiment can become cheaper\" title=\"Direct link to The experiment can become cheaper\" translate=\"no\">​</a></h2>\n<p>That may be the more consequential change. AI can make producing another implementation cheaper. External parameters and inputs can make another simulation run cheaper. Recorded data can make a real situation easier to reproduce. A simulation context can let us investigate that situation repeatedly without occupying the machine. AI can increasingly help create experiments, explore alternatives, and interpret the resulting data. None of these things determines what the machine <em>should</em> do.</p>\n<p>Requirements still matter. Models still matter. Tests still matter. Engineering judgment still matters. And the operator who says, <em>\"No, that still doesn't feel right,\"</em> may still provide the most valuable piece of information in the entire experiment.</p>\n<p>What changes is the cost of asking the next question.</p>\n<p>The opportunity is not merely to write control system software faster. It is to shorten the loop between <strong>intent, implementation, behavior, and evidence</strong>—and to learn faster about the system we are actually building.</p>",
            "url": "https://www.cppmodel.com/blog/when-ai-writes-the-code",
            "title": "When AI Writes the Code, What Becomes the Model?",
            "summary": "AI can make the next implementation cheaper to produce. For control system engineers, the bigger opportunity may be making the next experiment cheaper to run.",
            "image": "https://www.cppmodel.com/img/blog/when-ai-writes-the-code.png",
            "date_modified": "2026-10-01T00:00:00.000Z",
            "author": {
                "name": "Hristo Hristoskov",
                "url": "https://cedge.se"
            },
            "tags": [
                "AI",
                "Simulation",
                "Control Systems",
                "Embedded Systems"
            ]
        },
        {
            "id": "https://www.cppmodel.com/blog/what-level-of-simulation",
            "content_html": "<p>If you're developing control system software today, you have no shortage of ways to simulate and test it. You can write unit tests, build a higher-level engineering model, run an application in a simulated controller environment, model the physics of a machine, place that machine in a virtual environment, emulate an embedded target, connect a real controller to HIL, or test on the machine itself.</p>\n<p>All of these approaches can be useful. The more interesting question is not which simulator is the most capable, but:</p>\n<blockquote>\n<p><strong>What are you actually trying to understand?</strong></p>\n</blockquote>\n<p>That question matters because simulation is not only about how much of the system we reproduce. It is also about the abstraction at which we choose to represent the problem. A unit test may be enough to verify a calculation. A higher-level model may be the right place to design a control concept. A physical model may be necessary when hydraulics or mechanics dominate the behavior. And sometimes the production implementation already contains the behavior we need to investigate.</p>\n<p>The table below is not a ranking, and the products listed are not necessarily direct competitors. It is a map of different starting points and the kinds of questions they help answer.</p>\n<table><thead><tr><th>Approach</th><th>Examples</th><th>What it helps you understand</th></tr></thead><tbody><tr><td><strong>Unit testing</strong></td><td>GoogleTest, Catch2</td><td>Individual calculations, conditions and module behavior</td></tr><tr><td><strong>Code-first control system simulation</strong></td><td>CppModel</td><td>How the production implementation behaves over time</td></tr><tr><td><strong>PLC engineering &amp; simulation</strong></td><td>CODESYS, Beckhoff TwinCAT, Siemens PLCSIM</td><td>Running, debugging and observing controller applications in a simulated controller environment</td></tr><tr><td><strong>Model-Based Design</strong></td><td>Simulink, SCADE</td><td>Control design and behavior represented at a higher modeling abstraction, including model-based development and code-generation workflows</td></tr><tr><td><strong>Physical/system modeling</strong></td><td>Simscape, OpenModelica</td><td>Interaction between control and physical processes</td></tr><tr><td><strong>Robotics/environment simulation</strong></td><td>Gazebo, Webots, CoppeliaSim</td><td>Motion, sensors, physics and interaction with a virtual environment</td></tr><tr><td><strong>Embedded platform simulation/emulation</strong></td><td>Renode, QEMU</td><td>Software behavior in the context of simulated or emulated target hardware</td></tr><tr><td><strong>Distributed embedded-system simulation &amp; test</strong></td><td>Vector CANoe</td><td>Software, ECU, communication and distributed-system behavior</td></tr><tr><td><strong>Hardware-in-the-loop</strong></td><td>HIL platforms</td><td>Real controller hardware interacting with a simulated process</td></tr><tr><td><strong>Real-machine testing</strong></td><td>The machine itself</td><td>Actual machine and process behavior</td></tr></tbody></table>\n<p>The part of this landscape I want to focus on here is control system software and the behavior it creates in processes, machines and mechanisms.</p>\n<p>CppModel starts from a deliberately simple premise: <strong>the code is the model.</strong> Instead of first creating a separate representation of the control system, it works directly with the production implementation—which in embedded and machine control is often C or C++—and observes its behavior over time. CppModel currently supports this approach for production C and C++ code.</p>\n<p>This does not mean that the production implementation is always the only model we need. It means that when the behavior we are interested in is already expressed there, we can start there rather than first rebuilding the same logic at another abstraction level.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"from-code-to-behavior\">From Code to Behavior<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#from-code-to-behavior\" class=\"hash-link\" aria-label=\"Direct link to From Code to Behavior\" title=\"Direct link to From Code to Behavior\" translate=\"no\">​</a></h2>\n<p>Consider a simple velocity ramp. A machine receives a desired velocity, but we don't want the value sent toward the actuator to jump immediately to the requested value. Instead, we limit how quickly the current value can move toward the target.</p>\n<p>The core logic can be remarkably small:</p>\n<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\">static</span><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\">RampStep</span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token keyword\" style=\"color:#00009f\">float</span><span class=\"token plain\"> current</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\"> target</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\"> maxDelta</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\"> delta </span><span class=\"token operator\" style=\"color:#393A34\">=</span><span class=\"token plain\"> target </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> current</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\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">delta </span><span class=\"token operator\" style=\"color:#393A34\">&gt;</span><span class=\"token plain\"> maxDelta</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\"> current </span><span class=\"token operator\" style=\"color:#393A34\">+</span><span class=\"token plain\"> maxDelta</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\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:#393A34\">(</span><span class=\"token plain\">delta </span><span class=\"token operator\" style=\"color:#393A34\">&lt;</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\">maxDelta</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\"> current </span><span class=\"token operator\" style=\"color:#393A34\">-</span><span class=\"token plain\"> maxDelta</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\">return</span><span class=\"token plain\"> target</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>\n<p>There is deliberately nothing CppModel-specific in this function. It is intentionally minimal so the behavior is easy to follow. Production embedded code may use explicit project types, diagnostics, additional checks, saturation handling, and project-specific coding rules such as MISRA C.</p>\n<p>The function itself has no concept of time. <code>maxDelta</code> is simply the maximum velocity change allowed during one call. In this experiment the function is executed every <strong>1 ms</strong>, so the configured acceleration limit is converted into the corresponding velocity change for each execution step. For example, <strong>2 m/s²</strong> corresponds to a maximum change of <strong>2 mm/s per 1 ms step</strong>.</p>\n<p>Reading the function tells us what happens during one call. What it doesn't immediately show is what happens after hundreds of calls while the requested velocity changes. To see that, let's give it a simple velocity schedule:</p>\n<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\">static</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">const</span><span class=\"token plain\"> VelocityLevel_ts VELOCITY_SCHEDULE</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 operator\" style=\"color:#393A34\">=</span><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 number\" style=\"color:#36acaa\">0</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\">   </span><span class=\"token number\" style=\"color:#36acaa\">50.0f</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 number\" style=\"color:#36acaa\">300</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">150.0f</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 number\" style=\"color:#36acaa\">600</span><span class=\"token punctuation\" style=\"color:#393A34\">,</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">30.0f</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 number\" style=\"color:#36acaa\">900</span><span class=\"token punctuation\" 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 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 punctuation\" style=\"color:#393A34\">;</span><br></span></code></pre></div></div>\n<p>The requested velocity starts at <strong>50 mm/s</strong>, steps to <strong>150 mm/s after 300 ms</strong>, drops to <strong>30 mm/s after 600 ms</strong>, and finally goes to <strong>0 mm/s after 900 ms</strong>. In a short experiment we get acceleration from standstill, another acceleration step, a large deceleration, and braking to zero.</p>\n<p>The experiment has three parameters:</p>\n<table><thead><tr><th>Parameter</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Max Acceleration [m/s²]</code></td><td>Limits how quickly the actual velocity can change</td></tr><tr><td><code>Acceleration Window [ms]</code></td><td>Defines the allowed time to reach a higher desired velocity</td></tr><tr><td><code>Braking Window [ms]</code></td><td>Defines the allowed time to reach a lower desired velocity</td></tr></tbody></table>\n<p>For the runs below, both timing windows are set to <strong>100 ms</strong>. The simulation runs for <strong>1.2 seconds</strong> with an execution step of <strong>1 ms</strong>. Velocity is expressed in <strong>mm/s</strong>, while acceleration is configured in <strong>m/s²</strong>.</p>\n<p>The blue and orange traces mark when the acceleration and braking windows are active. Their height is only used to make the windows visible on the same graph; their duration is represented by the time axis. The validation result is shown below the response, so we can see not only whether a run passes, but where a requirement is violated.</p>\n<p>This is the transition from code to behavior that matters here. The function calculates one control step. Execute it every millisecond through a defined scenario and those individual steps become a time-domain response that we can inspect and validate.</p>\n<p>Now keep the code, input schedule, execution step and timing windows identical and change only <code>Max Acceleration</code>.</p>\n<p>With <code>Max Acceleration</code> set to <strong>4 m/s²</strong>, the controller reaches each requested velocity quickly and comfortably satisfies the configured timing requirements. On a real machine, however, we might decide that such a rapid response is unnecessarily aggressive.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"CppModel velocity ramp simulation with Max Acceleration set to 4 m/s²\" src=\"https://www.cppmodel.com/assets/images/velocity-ramp-max-acceleration-4-ab3947874b696b24521fc7816c87f0e5.png\" width=\"1258\" height=\"747\" class=\"img_ev3q\"></p>\n<p>So let's halve the acceleration limit to <strong>2 m/s²</strong>.</p>\n<p>The response is now visibly slower, while the same validation requirements still pass. If this produces the machine behavior we want, we have reduced the aggressiveness without violating the requirement.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"CppModel velocity ramp simulation with Max Acceleration set to 2 m/s²\" src=\"https://www.cppmodel.com/assets/images/velocity-ramp-max-acceleration-2-b6a46d74b3677d3846ebf6d3107b4cac.png\" width=\"1251\" height=\"745\" class=\"img_ev3q\"></p>\n<p>Both simulations pass, yet they clearly do not behave in the same way. That is an important distinction. A parameter tells us what we configured; <strong>the graph shows us what that configuration means for behavior over time.</strong></p>\n<p>On a real control system, the same idea extends beyond a simple ramp. We may be interested in filtering, settling, overshoot, oscillation, sequencing, state transitions, or the interaction between several pieces of logic. These are all things that can be difficult to understand from isolated function calls or individual values.</p>\n<p>What happens if we halve the acceleration limit once more?</p>\n<p>At <strong>1 m/s²</strong>, we have gone too far for the requirement we defined.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"CppModel velocity ramp simulation with Max Acceleration set to 1 m/s², showing a failed validation region\" src=\"https://www.cppmodel.com/assets/images/velocity-ramp-max-acceleration-1-failure-0bc90fcc7b0e2dd72c4d353154002e15.png\" width=\"1259\" height=\"749\" class=\"img_ev3q\"></p>\n<p>At the <strong>600 ms</strong> transition, <code>Desired Velocity</code> drops from <strong>150 mm/s to 30 mm/s</strong>. With <code>Max Acceleration</code> limited to <strong>1 m/s²</strong>, <code>Actual Velocity</code> cannot reach the new target within the configured <strong>100 ms braking window</strong>, so the validation fails. The red region below the graph shows where the requirement was violated.</p>\n<p>This is where simulation and testing start to complement each other. The test answers:</p>\n<blockquote>\n<p><strong>Did the behavior satisfy the requirement?</strong></p>\n</blockquote>\n<p>The graph helps answer:</p>\n<blockquote>\n<p><strong>Why didn't it?</strong></p>\n</blockquote>\n<p>That becomes particularly useful in continuous integration. The same scenario can be executed after a change and the requirements checked automatically, but the result does not have to stop at pass or fail. The behavior that produced the result can be preserved as evidence.</p>\n<p>The earlier runs show the other side of the same idea. <strong>4 m/s²</strong> and <strong>2 m/s²</strong> both pass, while their responses are visibly different. Tests tell us whether the behavior satisfies the requirements we thought to encode. Looking at the behavior can also expose differences we never thought to express as assertions.</p>\n<p>For control system software, where important behavior develops over time, that context can be as useful as the final test result.</p>\n<blockquote>\n<p><strong>The function gives us one step. The simulation gives us the behavior. The test tells us whether that behavior meets the requirements.</strong></p>\n</blockquote>\n<p>The complete simulation setup, including input handling, parameters, validation windows and the CI result, is available in the <a href=\"https://github.com/hristoskov/velocity-ramp\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">simulation code</a>.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"where-the-model-lives\">Where the Model Lives<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#where-the-model-lives\" class=\"hash-link\" aria-label=\"Direct link to Where the Model Lives\" title=\"Direct link to Where the Model Lives\" translate=\"no\">​</a></h2>\n<p>The example above treats the production implementation itself as the model. That is one possible abstraction, but it is not the only one. The same control system can be represented at a higher level, surrounded by a model of the physical process, placed in a simulated environment, or eventually exercised together with real hardware.</p>\n<p>In CppModel, that choice is explicit: <strong>the production implementation is the executable model.</strong> Model-Based Design makes a different choice about where the model lives and at what abstraction level.</p>\n<p>In a Model-Based Design environment such as Simulink, the engineering model typically exists at a higher abstraction level. Control behavior can be described using blocks, signals, state machines and other modeling constructs. That representation can be simulated and validated and, depending on the workflow, used to generate production code.</p>\n<p>This can be a powerful approach when the engineering model itself is intended to be a primary design artifact. CppModel starts from the other direction: when the behavior is already expressed in the production implementation, simulation, visualization and validation are built around that implementation rather than around a separate representation of the same algorithm.</p>\n<p>Neither abstraction is inherently the right one. They answer somewhat different development needs. If you want to design and reason about the control system through a higher-level engineering representation, Model-Based Design may be the natural starting point. If the production implementation is the artifact you want to develop, observe and validate, working directly with it can be more direct.</p>\n<p>PLC engineering environments add another variation. Tools such as CODESYS provide simulation and debugging workflows that are often centered on running the controller application and observing or modifying it much as an engineer would with a live controller. The approach discussed here has a different emphasis: execute a defined scenario over a defined time interval, preserve the resulting behavior, validate it, and make the experiment repeatable in CI.</p>\n<p>That distinction is not fundamentally C/C++ versus IEC 61131-3. It is also about <strong>how the control system implementation is exercised and how its behavior becomes part of the development and verification workflow</strong>. CppModel currently supports C and C++, but the idea of treating the production implementation as the model is not tied to those languages.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"when-the-model-needs-to-grow\">When the Model Needs to Grow<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#when-the-model-needs-to-grow\" class=\"hash-link\" aria-label=\"Direct link to When the Model Needs to Grow\" title=\"Direct link to When the Model Needs to Grow\" translate=\"no\">​</a></h2>\n<p>There is, of course, a limit to what the production implementation can tell us by itself.</p>\n<p>Suppose the question is no longer whether the velocity algorithm reaches its target within 100 ms, but how a hydraulic cylinder, flexible structure, or loaded crane actually responds to that command. The missing behavior now lives in the physical system. Hydraulics, mechanics, electrical systems, loads, and other dynamics have to become part of the model. This is where tools such as Simscape or Modelica-based environments such as OpenModelica become appropriate.</p>\n<p>The model may need to grow in another direction as well. If we want to understand how a controlled machine interacts with its environment—its motion, sensors, collisions, geometry, or surroundings—we need to represent that environment too. Robotics simulation environments such as Gazebo can provide this context, often as part of a ROS-based development workflow. At that point the question is no longer only what the control system implementation does over time, but what happens when that behavior is placed inside a simulated machine and environment.</p>\n<p>Eventually, some questions depend on the real controller. When the behavior depends on the actual controller hardware, its I/O and electrical interfaces, or sufficiently realistic machine dynamics, HIL can bring the real controller into the experiment. And some behavior can only be established with confidence on the real machine.</p>\n<p>Other tools extend the engineering context in different directions rather than simply making the physical model larger. Embedded simulation and emulation frameworks such as Renode and QEMU move the focus toward executing software in a representation of the target hardware. Their scope differs—Renode can also model sensors, environments and multi-node embedded systems—but the engineering question is generally closer to how software behaves in its target-platform context. CANoe extends the context around software components, ECUs, communication and distributed systems. Those can be essential engineering problems, but they are different from the process, machine and mechanism behavior considered here.</p>\n<p>This is why the approaches in the opening table should not be interpreted as one ladder from simple to advanced. They add different information and operate at different abstractions. The useful question is what information the current model needs in order to answer the engineering question in front of us.</p>\n<p>CppModel fits when the <strong>production implementation contains the model you want to investigate</strong> and the question is primarily about its behavior over time. When the answer depends on information the implementation does not contain, we need to add that information or move to another simulation approach.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"building-the-same-workflow-yourself\">Building the Same Workflow Yourself<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#building-the-same-workflow-yourself\" class=\"hash-link\" aria-label=\"Direct link to Building the Same Workflow Yourself\" title=\"Direct link to Building the Same Workflow Yourself\" translate=\"no\">​</a></h2>\n<p>There is also a much closer alternative to CppModel than many of the tools above: build the workflow yourself.</p>\n<p>Compile the controller as a library. Write a test harness. Feed it input data. Export CSV files. Plot the results using Python. Add GoogleTest or another testing framework. Connect everything to GitHub Actions, GitLab CI, or another CI system.</p>\n<p>For a capable software team, this is entirely possible, and many teams already have some variation of it. The question is not whether it can be done. It is whether building and maintaining the execution, visualization, result comparison and CI infrastructure is part of the engineering problem the team wants to solve.</p>\n<p>CppModel packages that workflow around the production implementation so the engineering effort can stay focused on developing and validating behavior.</p>\n<p>This feedback loop is becoming more relevant for another reason. Modern AI coding tools can generate and modify implementation code increasingly quickly. Asking for another ramp implementation, a different filter, or an additional limit may take seconds. Generating an alternative is becoming cheaper; understanding what that alternative actually does remains important.</p>\n<p>That makes the loop increasingly useful:</p>\n<p><strong>production code → change → simulation → behavioral evidence → tests → CI</strong></p>\n<p>AI can help produce alternatives. Simulation and testing provide a way to understand and validate the behavior those alternatives create.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"choosing-the-right-abstraction\">Choosing the Right Abstraction<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#choosing-the-right-abstraction\" class=\"hash-link\" aria-label=\"Direct link to Choosing the Right Abstraction\" title=\"Direct link to Choosing the Right Abstraction\" translate=\"no\">​</a></h2>\n<p>None of these approaches replaces all the others. A control system may begin with unit tests and code-level simulation, later be exercised together with a physical model, continue through HIL, and eventually be validated on the real machine. Another project may begin with a higher-level Model-Based Design workflow. PLC engineering, robotics simulation, embedded platform emulation and network simulation answer still other questions along the way.</p>\n<p>The useful boundary is not a particular tool or language. It is whether the representation we have contains enough information to answer the engineering question in front of us.</p>\n<blockquote>\n<p><strong>Use code-first simulation when the production implementation contains enough of the behavior you need to understand. Move to another abstraction when the answer depends on something the implementation alone cannot represent.</strong></p>\n</blockquote>\n<p>So perhaps the useful question isn't:</p>\n<blockquote>\n<p><strong>Which simulator is the most powerful?</strong></p>\n</blockquote>\n<p>It is:</p>\n<blockquote>\n<p><strong>What's the smallest simulation, at the right abstraction level, that gives me the answer I need right now?</strong></p>\n</blockquote>\n<p>Sometimes that means a higher-level engineering model. Sometimes the production implementation is already enough. Sometimes it means adding a physical system or a virtual environment around it. Sometimes it means HIL or the real machine.</p>\n<p>The goal is not to simulate everything. It is to model enough of the right things to answer the question.</p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"links\">Links<a href=\"https://www.cppmodel.com/blog/what-level-of-simulation#links\" class=\"hash-link\" aria-label=\"Direct link to Links\" title=\"Direct link to Links\" translate=\"no\">​</a></h2>\n<ul>\n<li class=\"\"><a href=\"https://github.com/hristoskov/velocity-ramp\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Simulation code</a></li>\n</ul>",
            "url": "https://www.cppmodel.com/blog/what-level-of-simulation",
            "title": "What Level of Simulation Do You Really Need?",
            "summary": "A practical look at choosing the right simulation abstraction for control system software, from code-first simulation and Model-Based Design to physical modeling, HIL, and real-machine testing.",
            "image": "https://www.cppmodel.com/img/blog/what-level-of-simulation.png",
            "date_modified": "2026-09-21T00:00:00.000Z",
            "author": {
                "name": "Hristo Hristoskov",
                "url": "https://cedge.se"
            },
            "tags": [
                "CppModel",
                "Simulation",
                "Control Systems",
                "Continuous Integration"
            ]
        },
        {
            "id": "https://www.cppmodel.com/blog/simulation-ci-flow",
            "content_html": "<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>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>note</div><div class=\"admonitionContent_BuS1\"><p>Updated in October 2026 for CppModel 0.9: the build now uses the CppModel CMake package, and the CI workflow no longer installs OpenSSL or zlib.</p></div></div>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"why-simulation-ci\">Why Simulation CI?<a href=\"https://www.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>\n<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>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"setting-up-simulation-ci\">Setting Up Simulation CI<a href=\"https://www.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>\n<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>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"code-setup\">Code setup<a href=\"https://www.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>\n<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>\n<p>Here is the <code>CMakeLists.txt</code> file for the Glass of Water simulation, with the verification source and test setup added. It expects the CppModel library to be extracted into a <code>dependencies</code> folder, which the CI workflow below takes care of:</p>\n<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.21)</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)</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\">list(APPEND CMAKE_PREFIX_PATH \"${CMAKE_SOURCE_DIR}/dependencies\")</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">find_package(cppmodel REQUIRED)</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\">target_link_libraries(GlassOfWaterSim PRIVATE cppmodel::cppmodel)</span><br></span><span class=\"token-line\" style=\"color:#393A34\"><span class=\"token plain\">cppmodel_deploy_runtime(GlassOfWaterSim)</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>\n<p>I'll break the verification of the process down into three steps:</p>\n<ul>\n<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>\n<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>\n<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>\n</ul>\n<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>\n<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\"></span><span class=\"token keyword\" style=\"color:#00009f\">static</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:#00009f\">float</span><span class=\"token plain\"> previousLevel </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\">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><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\">uint8_t</span><span class=\"token plain\"> result </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 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 punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><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\">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\">break</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\"> 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 punctuation\" style=\"color:#393A34\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#393A34\"><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\">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\">break</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\"> 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\">        result </span><span class=\"token operator\" style=\"color:#393A34\">=</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\">break</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\">        result </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 keyword\" style=\"color:#00009f\">break</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\">    previousLevel </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\">    </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>\n<p>The verification keeps its own <code>previousLevel</code> variable with the level from the previous step, which is used to check whether the level is increasing during the filling state.</p>\n<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. Here is how the cyclic function looks with the added verification logic:</p>\n<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 function\" style=\"color:#d73a49\">CMODEL_CYCLIC</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\">    </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\"> self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">currentTime_ms</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\">self</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>\n<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>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Failed simulation\" src=\"https://www.cppmodel.com/assets/images/sim-desired-level-fail-2e71d4ff0cd5888b82b088073ad2eb0a.png\" width=\"1224\" height=\"638\" class=\"img_ev3q\"></p>\n<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>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Failed simulation terminal\" src=\"https://www.cppmodel.com/assets/images/desired-level-fail-7726c5f8180db86180f489fe87f9c7a7.png\" width=\"747\" height=\"34\" class=\"img_ev3q\"></p>\n<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>\n<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>\n<p>Here are the simulation results after the modification:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Successful simulation\" src=\"https://www.cppmodel.com/assets/images/sim-desired-level-success-939dcd272a53209b4f2ed2b0fecbe05e.png\" width=\"1224\" height=\"638\" class=\"img_ev3q\"></p>\n<p>And just for completeness, here is what the terminal output looks like when the simulation passes (a blue dot in my case):</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Successful simulation terminal\" src=\"https://www.cppmodel.com/assets/images/desired-level-success-8e645aa88997a79889d26be7cef6517b.png\" width=\"754\" height=\"35\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"integrating-with-github-actions\">Integrating with GitHub Actions<a href=\"https://www.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>\n<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: it prints a sign-in link and waits for someone to sign in. Setting <code>CPPMODEL_LOGIN_TIMEOUT=0</code> turns that off, so a missing or wrong login fails the job right away instead of waiting.</p>\n<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>\n<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>\n<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 the CppModel library). The library has everything else it needs built in, so there are no other packages to install. 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>\n<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</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>\n<p>Downloading and installing the CppModel library is straightforward: we use <code>curl</code> to grab the latest Linux x86_64 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>\n<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 the CppModel library</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-x86_64.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>\n<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>\n<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\">CPPMODEL_LOGIN_TIMEOUT</span><span class=\"token punctuation\" style=\"color:#393A34\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:#36acaa\">0</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>\n<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>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"GitHub secrets\" src=\"https://www.cppmodel.com/assets/images/github-credentials-07d387733f9967d6d2c776805c457cfc.png\" width=\"639\" height=\"303\" class=\"img_ev3q\"></p>\n<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>\n<p>Here is how the workflow looks in GitHub Actions:</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"GitHub Actions workflow\" src=\"https://www.cppmodel.com/assets/images/ci-results-87aab526709829532535d0ce02548e13.png\" width=\"1292\" height=\"785\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"final-thoughts\">Final Thoughts<a href=\"https://www.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>\n<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>\n<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>\n<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>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"links\">Links<a href=\"https://www.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>\n<ul>\n<li class=\"\"><a href=\"https://github.com/hristoskov/glass-of-water/tree/v0.0.3\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Simulation code</a></li>\n</ul>",
            "url": "https://www.cppmodel.com/blog/simulation-ci-flow",
            "title": "Add a simulation CI to your process-based code",
            "summary": "How to add a simulation CI to your process-based code",
            "image": "https://www.cppmodel.com/img/blog/glass-of-water-ci.png",
            "date_modified": "2026-08-03T00:00:00.000Z",
            "author": {
                "name": "Hristo Hristoskov",
                "url": "https://cedge.se"
            },
            "tags": [
                "CppModel",
                "Continuous Integration",
                "Simulation",
                "Control Systems"
            ]
        },
        {
            "id": "https://www.cppmodel.com/blog/glass-of-water",
            "content_html": "<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>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-glass-itself\">The glass itself<a href=\"https://www.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>\n<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>\n<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>\n<p>Written as a function, that rule looks like this:</p>\n<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>\n<p>Breaking this into the four parts we'll use consistently throughout this post:</p>\n<p><strong>Settings</strong>, decided ahead of time and fixed while the process runs:</p>\n<ul>\n<li class=\"\"><code>capacity</code> — the size of the glass</li>\n</ul>\n<p><strong>State</strong>, what the process remembers from one check to the next:</p>\n<ul>\n<li class=\"\"><code>filledVolume</code> — how full the glass currently is</li>\n</ul>\n<p><strong>Input</strong>, what changes moment to moment from outside the process:</p>\n<ul>\n<li class=\"\"><code>waterFlow</code> — how fast water is pouring in right now</li>\n</ul>\n<p><strong>Output</strong>, the result handed back:</p>\n<ul>\n<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>\n</ul>\n<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>\n<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>\n<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>\n<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>\n<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:\n<img decoding=\"async\" loading=\"lazy\" alt=\"Glasses filling simulation\" src=\"https://www.cppmodel.com/assets/images/glass-fills-76c4bfc745863014af8f4c880da4fbf5.png\" width=\"868\" height=\"508\" class=\"img_ev3q\"></p>\n<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>\n<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>\n<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://www.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>\n<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>\n<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>\n<p>Here's the shape of that idea, again as a function:</p>\n<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>\n<p>Breaking this into the same four parts:</p>\n<p><strong>Settings</strong>, decided ahead of time:</p>\n<ul>\n<li class=\"\"><code>openingSlope</code> — how quickly the valve opens, in percent per second</li>\n<li class=\"\"><code>closingSlope</code> — how quickly the valve closes, in percent per second</li>\n</ul>\n<p><strong>State</strong>, what the controller remembers between checks:</p>\n<ul>\n<li class=\"\"><code>actualOpening</code> — how far open the valve currently is</li>\n</ul>\n<p><strong>Input</strong>, what changes moment to moment:</p>\n<ul>\n<li class=\"\"><code>error</code> — the gap between the desired and actual water level</li>\n</ul>\n<p><strong>Output</strong>, the result handed back:</p>\n<ul>\n<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>\n</ul>\n<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>\n<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>\n<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>\n<ul>\n<li class=\"\">opening speed: 100% per second</li>\n<li class=\"\">closing speed: 200% per second</li>\n</ul>\n<p>Here's the result:\n<img decoding=\"async\" loading=\"lazy\" alt=\"Tap controller simulation\" src=\"https://www.cppmodel.com/assets/images/tap-control-5365820d19648443259ffae0cec96d0a.png\" width=\"868\" height=\"508\" class=\"img_ev3q\"></p>\n<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>\n<h3 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"the-go-betweens-sensor-and-actuator\">The go-betweens: sensor and actuator<a href=\"https://www.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>\n<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>\n<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>\n<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>\n<ul>\n<li class=\"\">sensor — Settings: <code>capacity</code>; Input: <code>filledVolume</code>; Output: fill percentage</li>\n<li class=\"\">actuator — Settings: <code>maxFlow</code>; Input: <code>valveOpening</code>; Output: flow rate</li>\n</ul>\n<p>Here's what they look like in code — each is just a one-line conversion:</p>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"putting-it-all-together\">Putting it all together<a href=\"https://www.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>\n<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>\n<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:\n<img decoding=\"async\" loading=\"lazy\" alt=\"Control system block diagram\" src=\"https://www.cppmodel.com/assets/images/glass-control-80f1620c839d0653ebdbe0645c327dc9.png\" width=\"732\" height=\"191\" class=\"img_ev3q\"></p>\n<p>I simulated this full system aiming for 80% full, with:</p>\n<ul>\n<li class=\"\">Glass capacity: 250 ml</li>\n<li class=\"\">Opening speed: 100% per second</li>\n<li class=\"\">Closing speed: 400% per second</li>\n<li class=\"\">Maximum tap flow: 15 liters per minute</li>\n</ul>\n<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>\n<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 function\" style=\"color:#d73a49\">CMODEL_CYCLIC</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\">    </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\"> self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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\">self</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 function\" style=\"color:#d73a49\">CMODEL_SIMULATE</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><br></span></code></pre></div></div>\n<p>Here's how it behaves over time:\n<img decoding=\"async\" loading=\"lazy\" alt=\"Closed-loop system simulation\" src=\"https://www.cppmodel.com/assets/images/glass-tap-system-17eb814fbdfea64f92f7b3c3506bd669.png\" width=\"868\" height=\"508\" class=\"img_ev3q\"></p>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"final-notes\">Final notes<a href=\"https://www.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>\n<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>\n<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>\n<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>\n<h2 class=\"anchor anchorTargetStickyNavbar_Vzrq\" id=\"links\">Links<a href=\"https://www.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>\n<ul>\n<li class=\"\"><a href=\"https://github.com/hristoskov/glass-of-water/tree/v0.0.3\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"\">Simulation code</a></li>\n</ul>",
            "url": "https://www.cppmodel.com/blog/glass-of-water",
            "title": "So, how do you fill a glass of water?",
            "summary": "A glass of water is a simple example of a process that can be modeled and controlled",
            "image": "https://www.cppmodel.com/img/blog/glass-of-water.png",
            "date_modified": "2026-07-09T00:00:00.000Z",
            "author": {
                "name": "Hristo Hristoskov",
                "url": "https://cedge.se"
            },
            "tags": [
                "Simulation",
                "Control Systems",
                "Process",
                "CppModel"
            ]
        }
    ]
}