<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yujin hu</title>
    <description>The latest articles on DEV Community by yujin hu (@yujin_hu_7465bb61324e71cc).</description>
    <link>https://dev.to/yujin_hu_7465bb61324e71cc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4076519%2Fb9d3bb8f-0747-428a-99da-33d5ea0ebf72.png</url>
      <title>DEV Community: yujin hu</title>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yujin_hu_7465bb61324e71cc"/>
    <language>en</language>
    <item>
      <title>air=true Is Not a Diagnosis: A Testable Python Evidence Gate for Fluidics AI Agents</title>
      <dc:creator>yujin hu</dc:creator>
      <pubDate>Tue, 25 Aug 2026 07:40:22 +0000</pubDate>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc/airtrue-is-not-a-diagnosis-a-testable-python-evidence-gate-for-fluidics-ai-agents-2h5n</link>
      <guid>https://dev.to/yujin_hu_7465bb61324e71cc/airtrue-is-not-a-diagnosis-a-testable-python-evidence-gate-for-fluidics-ai-agents-2h5n</guid>
      <description>&lt;p&gt;When an instrument reads &lt;code&gt;air=true&lt;/code&gt; from a Modbus register, it is tempting to treat the bit as a fault verdict. That shortcut is unsafe.&lt;/p&gt;

&lt;p&gt;The bit tells us what the sensor adapter observed. It does &lt;strong&gt;not&lt;/strong&gt; tell us whether the event was expected during priming, caused by an empty source, produced by a suction-side leak, replayed from an old frame, or invalidated by the wrong tube setup.&lt;/p&gt;

&lt;p&gt;This article shows a small, dependency-free Python pattern for keeping that distinction explicit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv2pvknzgaghvcpixzhyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv2pvknzgaghvcpixzhyv.png" alt="Evidence gate flow" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The real engineering question
&lt;/h2&gt;

&lt;p&gt;Consider two events that contain the same raw value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"air"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first occurs 120 ms after a reagent switch. The pump is following a validated priming recipe, and the interface lasts 80 ms.&lt;/p&gt;

&lt;p&gt;The second occurs during forward aspiration from a reagent bottle. It persists for 900 ms while the valve route is unchanged.&lt;/p&gt;

&lt;p&gt;The raw observation is identical, but the engineering meaning is not. A controller therefore needs an &lt;strong&gt;evidence contract&lt;/strong&gt;, not only a decoded bit.&lt;/p&gt;

&lt;p&gt;At minimum, the contract should carry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sample timestamp and monotonic sequence number;&lt;/li&gt;
&lt;li&gt;communication, frame and sensor-readiness status;&lt;/li&gt;
&lt;li&gt;configuration identity and tube-setup verification;&lt;/li&gt;
&lt;li&gt;process phase and time since phase entry;&lt;/li&gt;
&lt;li&gt;pump state, direction and valve route;&lt;/li&gt;
&lt;li&gt;observed fluid state and event duration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI layer may help generate the protocol adapter, analyse logs or explain a result. The final state transition should remain deterministic and testable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A deliberately conservative state model
&lt;/h2&gt;

&lt;p&gt;The reference implementation returns one of six verdicts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;th&gt;Interpretation&lt;/th&gt;
&lt;th&gt;Example controller action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIQUID_CONFIRMED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current evidence supports a liquid observation&lt;/td&gt;
&lt;td&gt;Continue under existing interlocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPECTED_INTERFACE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A short interface is inside an allowed process window&lt;/td&gt;
&lt;td&gt;Log and continue under phase rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POSSIBLE_AIR_INGRESS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Air persists during forward aspiration&lt;/td&gt;
&lt;td&gt;Pause fluid motion and diagnose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SENSOR_SETUP_SUSPECT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configuration, installation or residue evidence is inconsistent&lt;/td&gt;
&lt;td&gt;Pause and inspect setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMUNICATION_INVALID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Frame, readiness, time or sequence evidence is invalid&lt;/td&gt;
&lt;td&gt;Safe hold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NEEDS_HUMAN_REVIEW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Context is insufficient for a deterministic interpretation&lt;/td&gt;
&lt;td&gt;Safe hold and review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice what is intentionally missing: there is no &lt;code&gt;air_detected == emergency_stop&lt;/code&gt; rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faom0gm5akjcvrhi5j7yi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faom0gm5akjcvrhi5j7yi.png" alt="Phase and context matrix" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Put data validity before process interpretation
&lt;/h2&gt;

&lt;p&gt;The evaluator first rejects stale, malformed or replayed observations. Only then does it reason about physical context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;communication_ok&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame_valid&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sensor_ready&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;age_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;age_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_sample_age_ms&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;sequence_invalid&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EvaluationResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COMMUNICATION_INVALID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Communication, frame, readiness, timestamp, or sequence evidence is invalid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SAFE_HOLD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ordering prevents a common AI-agent failure mode: confidently explaining a number that should never have entered the decision path.&lt;/p&gt;

&lt;p&gt;The next gate checks configuration identity, tube setup and residue evidence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config_revision&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_config_revision&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tube_setup_verified&lt;/span&gt;
    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;residue_suspected&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EvaluationResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SENSOR_SETUP_SUSPECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Configuration, tube setup, or residue evidence invalidates interpretation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAUSE_AND_INSPECT_SETUP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only a validated observation reaches the process-phase rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distinguish an expected interface from possible ingress
&lt;/h2&gt;

&lt;p&gt;An air event may be expected during priming, reagent switching or rinsing, but only inside a validated time window and below a validated event duration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;expected_phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ProcessPhase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PRIMING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ProcessPhase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REAGENT_SWITCH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ProcessPhase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RINSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;expected_phase&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phase_elapsed_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected_interface_window_ms&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_duration_ms&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_expected_air_event_ms&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EvaluationResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXPECTED_INTERFACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The air event falls inside an allowed interface window&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LOG_AND_CONTINUE_UNDER_PHASE_RULES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By contrast, persistent air evidence during forward aspiration should trigger a different path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ProcessPhase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASPIRATION&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pump_running&lt;/span&gt;
    &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pump_direction&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;PumpDirection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FORWARD&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EvaluationResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Verdict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POSSIBLE_AIR_INGRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Persistent air evidence during aspiration requires source and suction-side checks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAUSE_FLUID_MOTION_AND_DIAGNOSE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The word &lt;strong&gt;possible&lt;/strong&gt; matters. The signal still does not identify root cause. Diagnosis may require checking the source container, fittings, tubing, valve route, mounting, medium and recent maintenance history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the boundary executable
&lt;/h2&gt;

&lt;p&gt;The complete reference project includes nine regression tests. They cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;normal liquid evidence;&lt;/li&gt;
&lt;li&gt;an expected short interface;&lt;/li&gt;
&lt;li&gt;possible air ingress during aspiration;&lt;/li&gt;
&lt;li&gt;stale samples;&lt;/li&gt;
&lt;li&gt;invalid frames;&lt;/li&gt;
&lt;li&gt;configuration mismatch;&lt;/li&gt;
&lt;li&gt;residue suspicion;&lt;/li&gt;
&lt;li&gt;unknown physical state;&lt;/li&gt;
&lt;li&gt;replayed or out-of-order sequences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run everything with standard Python 3.9+:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/blmdxiao/fluidics-bubble-evidence-gate.git
&lt;span class="nb"&gt;cd &lt;/span&gt;fluidics-bubble-evidence-gate
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; tests &lt;span class="nt"&gt;-v&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; examples.evaluate_event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository is available here: &lt;a href="https://github.com/blmdxiao/fluidics-bubble-evidence-gate" rel="noopener noreferrer"&gt;Fluidics Bubble Evidence Gate on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI helps—and where it should stop
&lt;/h2&gt;

&lt;p&gt;Claude Code, Codex, ChatGPT, Gemini or another coding agent can be useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating UART, TTL or Modbus adapter scaffolding;&lt;/li&gt;
&lt;li&gt;producing test fixtures from a written evidence contract;&lt;/li&gt;
&lt;li&gt;checking unit conversions and schema completeness;&lt;/li&gt;
&lt;li&gt;clustering logs and proposing diagnostic hypotheses;&lt;/li&gt;
&lt;li&gt;reviewing whether every state has an explicit safe action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model should not silently invent register definitions, validated limits or tube-specific thresholds. It should also not replace deterministic interlocks with a natural-language confidence score.&lt;/p&gt;

&lt;p&gt;A useful division of labour is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sensor/protocol adapter
        ↓
normalized evidence package
        ↓
deterministic evidence gate
        ↓
safe controller action + auditable log
        ↓
AI explanation or diagnostic assistance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Engineering limits
&lt;/h2&gt;

&lt;p&gt;The timing values in the example are demonstrations, not release limits. Real values must be validated for tube material, wall thickness, inner diameter, medium, flow rate, mounting, sensor response and the instrument risk analysis.&lt;/p&gt;

&lt;p&gt;For public detector specifications and application context, see the &lt;a href="https://www.foreachtek.com/en/products/control/abd-air-bubble-detector/" rel="noopener noreferrer"&gt;FOREACH ABD air-bubble detector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The broader lesson is simple: &lt;strong&gt;a sensor bit is evidence, not a diagnosis&lt;/strong&gt;. Giving an AI agent more context is useful, but putting an explicit, tested state contract between the agent and the hardware is what makes the workflow auditable.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>iot</category>
      <category>testing</category>
    </item>
    <item>
      <title>An AI Agent Can Switch a Rotary Valve—But Only After These 7 Checks</title>
      <dc:creator>yujin hu</dc:creator>
      <pubDate>Tue, 18 Aug 2026 12:11:09 +0000</pubDate>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc/an-ai-agent-can-switch-a-rotary-valve-but-only-after-these-7-checks-5do2</link>
      <guid>https://dev.to/yujin_hu_7465bb61324e71cc/an-ai-agent-can-switch-a-rotary-valve-but-only-after-these-7-checks-5do2</guid>
      <description>&lt;p&gt;An AI agent can generate a valve command in milliseconds. That does &lt;strong&gt;not&lt;/strong&gt; mean the intended fluid path exists in the instrument.&lt;/p&gt;

&lt;p&gt;In an IVD analyzer or laboratory-automation system, a successful register write proves only that software sent a value. It does not prove that the valve was homed, that the requested port is physically connected to the intended reagent, that the previous liquid has been displaced, or that the pressure is safe for switching.&lt;/p&gt;

&lt;p&gt;That distinction matters as teams begin using ChatGPT, Codex, Claude Code, Gemini, DeepSeek, and other AI tools to generate control code, test sequences, and troubleshooting workflows.&lt;/p&gt;

&lt;p&gt;This article proposes a bounded control pattern for a future AI-agent layer. It uses the FOREACH MRV3 product page as a concrete engineering reference, but &lt;strong&gt;FOREACH does not currently claim that the MRV3 contains autonomous AI control&lt;/strong&gt;. The AI workflow below is a design recommendation, not a description of a shipping AI feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Resolve route intent to a physical port map
&lt;/h2&gt;

&lt;p&gt;An agent should never act on a vague instruction such as &lt;code&gt;switch to wash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It needs a versioned route map that resolves the semantic intent to a physical port, destination, tube, and allowed sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wash_to_waste"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valve_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"V1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requested_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wash_buffer_A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"waste_2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"map_revision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fluidics-2026-08-18"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the route-map revision in the controller does not match the instrument configuration, the safe action is to stop—not to guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Verify home and current position
&lt;/h2&gt;

&lt;p&gt;Power-up reset is useful, but a supervisory controller still needs evidence that the expected reference sequence completed and that the reported position is credible.&lt;/p&gt;

&lt;p&gt;Before switching, check at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the valve has completed its reset or homing sequence;&lt;/li&gt;
&lt;li&gt;the current position is known;&lt;/li&gt;
&lt;li&gt;the requested port is valid for the installed valve configuration;&lt;/li&gt;
&lt;li&gt;the last movement did not end in a timeout or position fault.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The FOREACH MRV3 page lists automatic reset on power-up. In an agent-managed system, that event should become an explicit verified state—not an assumption hidden inside startup code.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check fluid and wetted-material compatibility
&lt;/h2&gt;

&lt;p&gt;The agent needs more than a list of port names. It needs fluid metadata: chemistry, concentration, temperature, cleaning requirements, and compatibility with every wetted material in the route.&lt;/p&gt;

&lt;p&gt;For example, the MRV3 page lists PCTFE, zirconia ceramic, and sapphire among the wetted materials. An engineering database can use that information as an input, but compatibility still has to be evaluated for the actual formulation and operating conditions.&lt;/p&gt;

&lt;p&gt;If fluid identity is unknown or its compatibility record is missing, the route should be unavailable to autonomous execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Budget internal volume, carryover, and flush volume
&lt;/h2&gt;

&lt;p&gt;A route can be mechanically correct and analytically wrong.&lt;/p&gt;

&lt;p&gt;Internal volume changes the amount of liquid retained in a path and therefore affects flush demand, mixing, and carryover risk. The MRV3 reference page lists different channel diameters and corresponding internal volumes, including 15.8 uL, 10 uL, and 2.9 uL configurations.&lt;/p&gt;

&lt;p&gt;The controller should combine valve volume with tube volume and downstream cell volume, then require a validated displacement factor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;required_flush = retained_route_volume x validated_displacement_factor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The factor is not universal. It should come from instrument testing with the real fluid pair, tubing, flow rate, and acceptance criterion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkh9e6b4xwaxy5npka04h.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkh9e6b4xwaxy5npka04h.webp" alt="FOREACH MRV3 ceramic multiport rotary valve — official product image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Match pressure, bore, and interface constraints
&lt;/h2&gt;

&lt;p&gt;The command is unsafe if the requested route violates the operating envelope.&lt;/p&gt;

&lt;p&gt;The agent should compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expected pressure against the permitted pressure range;&lt;/li&gt;
&lt;li&gt;selected channel bore against required flow and pressure-drop limits;&lt;/li&gt;
&lt;li&gt;connection type against the installed fittings;&lt;/li&gt;
&lt;li&gt;pump behavior against the transient created by switching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official MRV3 page lists a 0.7 MPa pressure rating, multiple channel diameters, and both 1/4-28UNF and 6-40UNF interfaces. Those are configuration inputs, not values an AI model should infer from a photo or product family name.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Enforce a no-flow switching sequence
&lt;/h2&gt;

&lt;p&gt;An agent should not issue a port change while the pump is still pressurizing the line unless the complete fluidic design explicitly permits it.&lt;/p&gt;

&lt;p&gt;A conservative sequence is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;stop or decelerate the pump;&lt;/li&gt;
&lt;li&gt;confirm pressure is below the switching threshold;&lt;/li&gt;
&lt;li&gt;command the valve;&lt;/li&gt;
&lt;li&gt;wait for verified position or timeout;&lt;/li&gt;
&lt;li&gt;begin the validated flush sequence;&lt;/li&gt;
&lt;li&gt;restart process flow only after postconditions pass.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The MRV3 page lists switching within two seconds per revolution and under 100 milliseconds for adjacent ports. These times can inform a timeout budget, but they do not replace instrument-level validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Require feedback, fault handling, and a safe fallback
&lt;/h2&gt;

&lt;p&gt;The agent must distinguish three states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;requested&lt;/strong&gt;: software asked for a position;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;reported&lt;/strong&gt;: the controller returned a value;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;verified&lt;/strong&gt;: the instrument satisfied the defined physical and analytical checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A compact command contract could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wash_to_waste"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requested_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"preconditions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pump_stopped"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pressure_kPa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"route_map_current"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verified_state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"homed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reported_port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fault"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postcondition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"flush_then_measure"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fallback should be deterministic: stop the pump, preserve the fault context, prevent retries beyond a defined limit, and require operator recovery when physical state cannot be verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small software guardrail
&lt;/h2&gt;

&lt;p&gt;AI can propose an action, but a deterministic function should decide whether the action is eligible for execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValveContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;homed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;pump_stopped&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;pressure_kpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;route_map_current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;fluid_compatible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;requested_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;allowed_ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;may_switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ValveContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_switch_pressure_kpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;homed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pump_stopped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pressure_kpa&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_switch_pressure_kpa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route_map_current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fluid_compatible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requested_port&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allowed_ports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important architecture choice is separation of concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the AI layer interprets intent and proposes a plan;&lt;/li&gt;
&lt;li&gt;deterministic guardrails validate eligibility;&lt;/li&gt;
&lt;li&gt;the controller executes the command;&lt;/li&gt;
&lt;li&gt;sensors and state feedback verify the result;&lt;/li&gt;
&lt;li&gt;validated instrument logic decides whether processing can continue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Product reality: what the reference valve actually provides
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.foreachtek.com/en/products/valves/rotary-valves/" rel="noopener noreferrer"&gt;FOREACH MRV3 ceramic multiport rotary valve&lt;/a&gt; is presented for fluid-path selection in automated analytical instruments. The official page lists 10-, 16-, and 24-channel options, several channel diameters and internal volumes, automatic power-up reset, optional drivers, and RS232/RS485 communication.&lt;/p&gt;

&lt;p&gt;Those features make the product page a useful reference for discussing configuration-aware control. They do not, by themselves, turn a valve into an autonomous system. Safe operation still depends on the surrounding mechanics, electronics, firmware, tubing, fluids, sensors, test evidence, and application requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical takeaway
&lt;/h2&gt;

&lt;p&gt;The near-term value of an AI agent is not to bypass control engineering. It is to make the engineering state more explicit: route intent, configuration revision, compatibility evidence, carryover budget, interlocks, and recovery steps.&lt;/p&gt;

&lt;p&gt;When those facts are machine-readable, AI can help generate test cases, review route plans, explain faults, and propose sequences. The final permission to move fluid should remain bounded by deterministic checks and verified physical state.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I am a member of the FOREACH team. The product facts in this article are paraphrased from the official MRV3 page linked above. The AI-agent architecture is an engineering proposal and is not a claim that the current product provides autonomous AI control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://www.foreachtek.com/en/products/valves/rotary-valves/" rel="noopener noreferrer"&gt;FOREACH — MRV3 Ceramic Multiport Rotary Valve&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>iot</category>
    </item>
    <item>
      <title>Claude Code, Codex, or Gemini Can Suggest a Pump — But Never Skip These 5 Fluidic Checks</title>
      <dc:creator>yujin hu</dc:creator>
      <pubDate>Mon, 17 Aug 2026 15:20:53 +0000</pubDate>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc/claude-code-codex-or-gemini-can-suggest-a-pump-but-never-skip-these-5-fluidic-checks-1ep</link>
      <guid>https://dev.to/yujin_hu_7465bb61324e71cc/claude-code-codex-or-gemini-can-suggest-a-pump-but-never-skip-these-5-fluidic-checks-1ep</guid>
      <description>&lt;p&gt;AI coding agents are becoming useful engineering assistants. Claude Code, Codex, and Gemini can read specifications, normalize tables, generate calculation scripts, and prepare comparison matrices much faster than a manual copy-and-paste workflow.&lt;/p&gt;

&lt;p&gt;But a language model should not be the final authority for a microfluidic design.&lt;/p&gt;

&lt;p&gt;If an agent confuses mL/min with μL/min, treats a free-flow pump rating as the actual operating point, or invents a missing material-compatibility value, the output can look convincing while being physically wrong.&lt;/p&gt;

&lt;p&gt;The safer pattern is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let the LLM propose. Let deterministic code validate. Let an engineer approve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frc05n9techuuifg9spco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frc05n9techuuifg9spco.png" alt="Guardrailed AI Agent workflow for microfluidic design" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The five-layer workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Convert the request into an explicit engineering schema
&lt;/h3&gt;

&lt;p&gt;Do not start with: “Choose a pump for my analyzer.” Start with structured requirements that make missing data visible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"application"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"automated reagent dispensing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fluid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aqueous buffer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"temperature_c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target_flow_ml_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tubing_id_mm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tubing_length_mm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;650&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max_pressure_kpa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"wetted_materials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PTFE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PEEK"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"control"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PWM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"missing_fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"target_lifetime_cycles"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An agent may help extract these fields from a requirements document, but it must not silently fill &lt;code&gt;missing_fields&lt;/code&gt; with guesses.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Retrieve candidates, not conclusions
&lt;/h3&gt;

&lt;p&gt;The retrieval layer should return candidate components and the evidence behind each field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source datasheet and revision;&lt;/li&gt;
&lt;li&gt;rated flow and the test conditions;&lt;/li&gt;
&lt;li&gt;pressure range;&lt;/li&gt;
&lt;li&gt;wetted materials;&lt;/li&gt;
&lt;li&gt;connector geometry;&lt;/li&gt;
&lt;li&gt;control interface;&lt;/li&gt;
&lt;li&gt;temperature and lifetime limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number needs provenance. “The model remembers this value” is not provenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Run deterministic unit and range checks
&lt;/h3&gt;

&lt;p&gt;Before any physics calculation, reject inconsistent units, nonphysical values, and missing constraints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FluidPathInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;flow_ml_min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;tube_id_mm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;tube_length_mm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;viscosity_pa_s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;max_pressure_kpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FluidPathInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flow_ml_min&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flow is outside the configured engineering range&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tube_id_mm&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tube ID is missing or implausible&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tube_length_mm&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tube length must be positive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viscosity_pa_s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamic viscosity must be positive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_pressure_kpa&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pressure limit must be positive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These ranges are project configuration, not universal laws. The important point is that they are explicit, testable, version-controlled, and independent of the model’s prose.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Call a physics calculator as a tool
&lt;/h3&gt;

&lt;p&gt;The agent should pass validated inputs to a deterministic calculator rather than estimate pressure loss in free-form text.&lt;/p&gt;

&lt;p&gt;For laminar flow in a straight circular tube, the Hagen–Poiseuille relationship highlights an important sensitivity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔP = 128 μ L Q / (π d⁴)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because pressure drop scales with the inverse fourth power of internal diameter, changing a tube ID from 1.0 mm to 0.5 mm can increase the theoretical straight-tube pressure drop by 16× when the other variables are unchanged.&lt;/p&gt;

&lt;p&gt;Real fluid paths also contain valves, fittings, bends, filters, probes, entrances, contractions, and possibly non-Newtonian fluids. The model must preserve those limitations instead of presenting one equation as a complete system model.&lt;/p&gt;

&lt;p&gt;For a practical check, the &lt;a href="https://www.foreachtek.com/en/resources/calculators/fluid-resistance/" rel="noopener noreferrer"&gt;FOREACH Fluid Resistance Calculator&lt;/a&gt; supports known-flow or known-pressure-drop modes, fluid properties, tube segments, local resistance, and Cv-based elements. It is useful for early engineering estimates—not a replacement for bench validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Require human approval before execution
&lt;/h3&gt;

&lt;p&gt;The final report should separate facts, calculations, assumptions, and unresolved questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  Units are consistent
PASS  Candidate pressure rating exceeds calculated steady-state loss
WARN  Startup pressure spike was not modeled
WARN  Material compatibility requires supplier confirmation
FAIL  Target lifetime is missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only after an engineer resolves every &lt;code&gt;FAIL&lt;/code&gt; and accepts each documented &lt;code&gt;WARN&lt;/code&gt; should the design proceed to procurement, prototype testing, or device control.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI agents are genuinely good at here
&lt;/h2&gt;

&lt;p&gt;Used with these guardrails, AI tools can provide real leverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract parameters from multiple datasheets;&lt;/li&gt;
&lt;li&gt;normalize naming and units;&lt;/li&gt;
&lt;li&gt;generate test matrices and Python notebooks;&lt;/li&gt;
&lt;li&gt;identify missing requirements;&lt;/li&gt;
&lt;li&gt;compare calculated results with logged bench data;&lt;/li&gt;
&lt;li&gt;draft a traceable engineering summary;&lt;/li&gt;
&lt;li&gt;prepare regression tests when the fluid path changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are much less reliable as an unreviewed source of product limits, chemical compatibility, safety decisions, or “best component” claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  A useful rule for engineering agents
&lt;/h2&gt;

&lt;p&gt;Do not ask whether an AI agent can select a pump or valve. Ask whether the workflow can prove where every requirement, value, calculation, and approval came from.&lt;/p&gt;

&lt;p&gt;That shift—from autonomous-sounding answers to traceable engineering evidence—is what makes an AI-assisted fluidic workflow useful.&lt;/p&gt;




&lt;p&gt;Disclosure: I work with FOREACH, which develops microfluidic pumps, valves, sensors, tubing, fittings, and related components. The linked calculator is a free engineering resource from our website. Product selection and final operating limits should always be verified against the relevant datasheet and physical tests.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>agents</category>
      <category>science</category>
    </item>
    <item>
      <title>Why Fluid Carryover Remains After a Solenoid Valve Switch</title>
      <dc:creator>yujin hu</dc:creator>
      <pubDate>Sun, 16 Aug 2026 13:42:40 +0000</pubDate>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc/why-fluid-carryover-remains-after-a-solenoid-valve-switch-pj9</link>
      <guid>https://dev.to/yujin_hu_7465bb61324e71cc/why-fluid-carryover-remains-after-a-solenoid-valve-switch-pj9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxnvr15cs02elt53o3bwr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxnvr15cs02elt53o3bwr.webp" alt="FOREACH 6010 Series Solenoid Valve" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;FOREACH 6010 Series Solenoid Valve. Product image from the official FOREACH website.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In an IVD instrument, analytical system, or laboratory-automation platform, a solenoid valve can complete its mechanical motion long before the fluid path is actually clean. A valve may switch in milliseconds, yet the first packet of the next reagent can still contain a measurable amount of the previous fluid.&lt;/p&gt;

&lt;p&gt;This is not automatically a valve failure. Carryover can remain in the valve chamber, fittings, manifold passages, probe, and nearby tubing. A useful troubleshooting approach is to separate three time scales:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the mechanical switching time of the valve;&lt;/li&gt;
&lt;li&gt;the displacement time required for the new fluid to pass through the local path;&lt;/li&gt;
&lt;li&gt;the slower decay caused by mixing, diffusion, adsorption, recirculation, and low-velocity zones.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The electrical command mainly controls the first time scale. Assay purity and wash performance are often governed by the other two.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple mixed-volume estimate
&lt;/h2&gt;

&lt;p&gt;As an early engineering approximation, treat the valve chamber and adjacent short passages as one well-mixed volume, &lt;code&gt;Vd&lt;/code&gt;. If the accumulated volume of new wash fluid is &lt;code&gt;Vf&lt;/code&gt;, the remaining fraction of the old fluid can be approximated by:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;C / C0 = exp(-Vf / Vd)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is not a complete CFD model. It is a screening equation that converts a vague delay into a measurable flush-volume target.&lt;/p&gt;

&lt;p&gt;To reduce the old-fluid concentration to 1%:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Vf ≈ 4.6 × Vd&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To reduce it to 0.1%:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Vf ≈ 6.9 × Vd&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For a 20 µL valve chamber, the ideal mixed-volume estimate gives approximately 92 µL for 1% residual and 138 µL for 0.1% residual. The volume of fittings, manifold channels, tubing, and probes must still be added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast valve response does not mean a clean path
&lt;/h2&gt;

&lt;p&gt;Consider a valve that responds in roughly tens of milliseconds. At a wash flow of 1 mL/min, passing 92 µL through the path requires about 5.5 seconds. In this example, the fluid-displacement time is far longer than the mechanical switching time.&lt;/p&gt;

&lt;p&gt;This distinction explains a common bench observation: the valve and driver work correctly during an unloaded test, but the first sample or reagent packet remains unstable after the complete fluid path is installed.&lt;/p&gt;

&lt;p&gt;During troubleshooting, record at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valve drive command and actual switching time;&lt;/li&gt;
&lt;li&gt;pump speed or measured flow rate;&lt;/li&gt;
&lt;li&gt;accumulated flush volume after the switch;&lt;/li&gt;
&lt;li&gt;conductivity, absorbance, fluorescence, or another signal that reveals carryover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fixed delay alone is not a robust wash specification. When flow changes, the same delay corresponds to a different displaced volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why real systems often need more wash volume
&lt;/h2&gt;

&lt;p&gt;The well-mixed approximation is usually optimistic. Actual fluid paths can contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;low-velocity regions inside valve cavities and fittings;&lt;/li&gt;
&lt;li&gt;recirculation zones at steps, bends, and manifold intersections;&lt;/li&gt;
&lt;li&gt;adsorption and desorption on flexible tubing or polymer surfaces;&lt;/li&gt;
&lt;li&gt;trapped bubbles that create bypass flow;&lt;/li&gt;
&lt;li&gt;fluids with different viscosity, density, or surface tension;&lt;/li&gt;
&lt;li&gt;transient backflow caused by pump pulsation or pressure imbalance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, 4.6 dead volumes should be treated as a theoretical starting point rather than a final cleaning requirement. A practical validation can test 1, 2, 4, 6, and 8 equivalent dead volumes, measure the residual signal at each point, and build an empirical decay curve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check during valve selection
&lt;/h2&gt;

&lt;p&gt;Port diameter and maximum pressure are not enough for a switching application. The design review should also include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valve-chamber volume and internal passage geometry;&lt;/li&gt;
&lt;li&gt;2-way or 3-way configuration and normally open or normally closed behavior;&lt;/li&gt;
&lt;li&gt;diaphragm material and reagent compatibility;&lt;/li&gt;
&lt;li&gt;threaded, barbed, or manifold interface;&lt;/li&gt;
&lt;li&gt;operating pressure, backpressure, and possible negative-pressure aspiration;&lt;/li&gt;
&lt;li&gt;response time, long energized periods, coil heating, and energy-saving drive requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official FOREACH 6010 Series Solenoid Valve page publishes the 1.4 mm orifice, 20 µL valve-chamber volume, pressure range, diaphragm options, response time, and interface choices used in the example above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.foreachtek.com/en/products/valves/solenoid-valves/" rel="noopener noreferrer"&gt;FOREACH 6010 Series Solenoid Valve — specifications and selection information&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The purpose of referencing a product page is not to replace representative testing. It shows why valve-chamber volume belongs in the same selection table as pressure, material, interface, and response time.&lt;/p&gt;

&lt;h2&gt;
  
  
  A repeatable validation sequence
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Estimate the total retained volume of the valve chamber, fittings, manifold, probe, and adjacent tubing.&lt;/li&gt;
&lt;li&gt;Convert 1–8 equivalent volumes into actual flush volume and time at the expected flow rate.&lt;/li&gt;
&lt;li&gt;Measure residual concentration with conductivity, color, fluorescence, absorbance, or the actual assay signal.&lt;/li&gt;
&lt;li&gt;Repeat at different flow rates and backpressures.&lt;/li&gt;
&lt;li&gt;Confirm the final setting under worst-case temperature, viscosity, assembly tolerance, aging, and contamination conditions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Separating “the valve has switched” from “the fluid path has been displaced and cleaned” turns a vague software delay into an engineering requirement that can be calculated, measured, and verified.&lt;/p&gt;

&lt;p&gt;Disclosure: This article was prepared by the FOREACH engineering team. The linked product page is an official FOREACH resource. The equations are intended for early engineering estimates; final designs require representative testing of the complete fluid path.&lt;/p&gt;

</description>
      <category>science</category>
      <category>tutorial</category>
      <category>iot</category>
      <category>tools</category>
    </item>
    <item>
      <title>Why a Pump's Maximum Flow Is Not Its Operating Flow (with Python)</title>
      <dc:creator>yujin hu</dc:creator>
      <pubDate>Thu, 13 Aug 2026 15:16:43 +0000</pubDate>
      <link>https://dev.to/yujin_hu_7465bb61324e71cc/why-a-pumps-maximum-flow-is-not-its-operating-flow-with-python-12ga</link>
      <guid>https://dev.to/yujin_hu_7465bb61324e71cc/why-a-pumps-maximum-flow-is-not-its-operating-flow-with-python-12ga</guid>
      <description>&lt;p&gt;Choosing a pump from its maximum-flow number is an easy mistake to make.&lt;/p&gt;

&lt;p&gt;That headline value is normally measured with little or no backpressure. At the other end of the curve, maximum pressure occurs near shutoff, where flow approaches zero. Once tubing, valves, filters, needles, and a fluidic chip are connected, the pump works somewhere between those two endpoints.&lt;/p&gt;

&lt;p&gt;That real condition is the &lt;strong&gt;operating point&lt;/strong&gt;: the intersection of the pump curve and the system curve.&lt;/p&gt;

&lt;p&gt;This post builds a small, dependency-free Python model to estimate that intersection during early design.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Approximate the pump curve
&lt;/h2&gt;

&lt;p&gt;Measured pressure-flow data is always preferable. But when a datasheet provides only maximum flow and shutoff pressure, a linear approximation is a useful first screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔP_pump(Q) = ΔP_max × (1 - Q / Q_max)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Q_max&lt;/code&gt; is the low-backpressure flow rate and &lt;code&gt;ΔP_max&lt;/code&gt; is the pressure at approximately zero flow.&lt;/p&gt;

&lt;p&gt;This is deliberately simple. A diaphragm, peristaltic, piston, or gear pump may have a nonlinear curve, and the curve can change with voltage, speed, fluid, and control mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Model the tube as a resistance
&lt;/h2&gt;

&lt;p&gt;For fully developed laminar flow of an incompressible Newtonian liquid through a circular tube, Hagen-Poiseuille gives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔP_system = R × Q
R = 128 μ L / (π D⁴)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fourth-power dependence on inner diameter is the detail worth remembering. Changing the ID from 0.8 mm to 0.7 mm raises theoretical tube resistance by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(0.8 / 0.7)⁴ ≈ 1.71
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is about 71% more resistance, before adding valves, filters, connectors, needles, bends, or manifolds.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Solve the intersection
&lt;/h2&gt;

&lt;p&gt;Set the pressure available from the pump equal to the pressure required by the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ΔP_max × (1 - Q / Q_max) = R × Q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this simplified linear case, the solution is direct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q = ΔP_max / (R + ΔP_max / Q_max)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a compact implementation with explicit unit conversion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pump&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;max_flow_ml_min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;max_pressure_kpa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tube&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;inner_diameter_mm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;length_m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;viscosity_mpa_s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;operating_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pump&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tube&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tube&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;q_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_flow_ml_min&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e-6&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt;  &lt;span class="c1"&gt;# m³/s
&lt;/span&gt;    &lt;span class="n"&gt;p_max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_pressure_kpa&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e3&lt;/span&gt;         &lt;span class="c1"&gt;# Pa
&lt;/span&gt;    &lt;span class="n"&gt;diameter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inner_diameter_mm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e-3&lt;/span&gt;    &lt;span class="c1"&gt;# m
&lt;/span&gt;    &lt;span class="n"&gt;viscosity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;viscosity_mpa_s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e-3&lt;/span&gt;     &lt;span class="c1"&gt;# Pa·s
&lt;/span&gt;
    &lt;span class="n"&gt;resistance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mf"&gt;128.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;viscosity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;tube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length_m&lt;/span&gt;
        &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;diameter&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;flow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p_max&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resistance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p_max&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;q_max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pressure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resistance&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;flow&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;flow&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1e6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pressure&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;1e3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Run a practical example
&lt;/h2&gt;

&lt;p&gt;Suppose the early design inputs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maximum flow: 120 mL/min;&lt;/li&gt;
&lt;li&gt;maximum pressure: 80 kPa;&lt;/li&gt;
&lt;li&gt;tube length: 1 m;&lt;/li&gt;
&lt;li&gt;tube ID: 0.8 mm;&lt;/li&gt;
&lt;li&gt;viscosity: 1 mPa·s.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_flow_ml_min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;120.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_pressure_kpa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;80.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tube&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Tube&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inner_diameter_mm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length_m&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pressure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;operating_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pump&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tube&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Operating flow: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; mL/min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Pressure drop: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pressure&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; kPa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Operating flow: 34.42 mL/min
Pressure drop: 57.06 kPa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pump is advertised at 120 mL/min, but the estimated flow in this system is only about 34 mL/min.&lt;/p&gt;

&lt;p&gt;Now change only the tube ID to 0.7 mm. The estimated operating flow falls again, to about 22.89 mL/min, while the pressure drop rises to about 64.74 kPa. A tolerance that looks small on a drawing can therefore move the actual operating point substantially.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What should be added next?
&lt;/h2&gt;

&lt;p&gt;The single-tube model is a screening tool, not a system qualification. A realistic pressure budget may also include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valves, filters, restrictors, and fittings;&lt;/li&gt;
&lt;li&gt;sampling or dispensing needles;&lt;/li&gt;
&lt;li&gt;microchannels and sudden contractions;&lt;/li&gt;
&lt;li&gt;inlet/outlet pressure and elevation differences;&lt;/li&gt;
&lt;li&gt;temperature-dependent viscosity;&lt;/li&gt;
&lt;li&gt;pump-curve tolerance and tube-ID tolerance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extra care is needed for gases, bubbles, non-Newtonian fluids, two-phase flow, compliant tubing, and pulsatile pumping.&lt;/p&gt;

&lt;p&gt;The full implementation, input validation, and tests are available in the &lt;a href="https://github.com/blmdxiao/fluid-system-operating-point" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For a quick independent check of circular-tube resistance and pressure drop, I also use the &lt;a href="https://www.foreachtek.com/en/resources/calculators/fluid-resistance/" rel="noopener noreferrer"&gt;FOREACH Fluid Resistance and Pressure Drop Calculator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: I work with the FOREACH team that develops this calculator. Both the code and calculator are intended for early engineering estimates; measured pump curves and prototype validation should be used before design release.&lt;/p&gt;

&lt;p&gt;The useful selection question is not "What is the pump's maximum flow?" It is: &lt;strong&gt;Where will this pump operate in my actual fluidic system?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>opensource</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
