# Requirements and conformity A requirement says what a result must achieve: a limit, in a unit, with an ID and a statement. noodlelab checks every requirement on every run and reports its **margin**, how far inside the limit the result is, rather than just pass or fail. When the result has an uncertainty, it also reports how likely the true value is to meet the requirement, and a **decision rule** can decide with that probability. The tutorial's chapter *Requirements and checks* walks through all of this on a small graph (Help › Tutorial). ## Writing requirements One requirement per line, in a **Requirements** node, a CSV or YAML file (**Load Requirements**), or `nv.requirements(...)` in Python: ```text COM-001 link_margin >= 3 dB [Test] # The link shall close with 3 dB margin PWR-001 dc_power <= 40 W THM-001 temperature between -20 and 60 degC ORB-001 altitude == 550 ± 5 km STR-001 stress <= 250 MPa fs=1.5 SPD-001 speed <= 15 km/h risk=2.5% ``` | Part | Meaning | |---|---| | `COM-001` | The ID (a colon after it is allowed) | | `link_margin` | The quantity it constrains | | `>=`, `<=`, `>`, `<`, `==`, `between … and …` | The comparison. `>` and `<` are strict: a value exactly at the limit meets `>=` but not `>` | | `3 dB` | The limit and its unit. A value in another unit is converted; a plain number is taken to be in this unit | | `± 5` | With `==`: the tolerance either way | | `fs=1.5` | A factor of safety: the check also reports the margin of safety | | `risk=2.5%`, `guard=1`, `rule=simple` | A decision rule (below) | | `[Test]` | How it is verified: Analysis (the default), Test, Inspection or Demonstration | | `# …` | The statement itself, shown in the compliance matrix | **Verify Requirement** checks one result against one requirement; chain its `log` from node to node and **Add Compliance Matrix** puts them all in a report. **Requirement Value** feeds a limit into the design, and **Check Candidates** scores every row of a trade table. ## Verdicts and margins A check has one of three verdicts: | Verdict | When | |---|---| | **Passed** | The result meets the limit. With an uncertainty: by more than its expanded uncertainty U = k·u (k = 2 unless set) | | **Inconclusive** | The nominal result meets the limit, but by no more than U: the design may meet the requirement, but the calculation cannot show it | | **Failed** | The nominal result misses the limit, whatever its uncertainty. Also when the margin of safety is negative | Inconclusive is not a pass: `noodlelab test` and `noodlelab verify` fail on it, as on a failed check. Reduce the uncertainty, change the design, or (when the specification says how) decide with a rule; never drop the uncertainty. Every check reports: - the **margin**, in the requirement's unit (negative: outside the limit), and as a percentage of the limit where that means something (not for °C, dB or a range); - for an uncertain result, **U** and the coverage factor k. *From dof* on Verify Requirement (or `coverage=0.95`) takes k from Student's t and the result's effective degrees of freedom, as a mean of few readings needs; - with a factor of safety, the **margin of safety** MS = allowable / (fs × applied) − 1 (achieved / (fs × required) − 1 for a minimum); - for an uncertain result, the **probability of conformity**. ## The probability of conformity P(conform) is the probability that the *true* value meets the requirement, given what the calculation knows about it (JCGM 106, 7). noodlelab works it out in one of two ways: - **From the uncertainty.** The true value is taken as normally distributed around the result with its standard uncertainty u (Student's t when the effective degrees of freedom are finite), and P is the share of that distribution inside the limits. A range or a tolerance counts both tails. - **From Monte Carlo trials.** Link the **Monte Carlo** node's `samples` into Verify Requirement's `samples` (or pass a `nv.monte_carlo(...)` result to `nv.verify`), and P is the share of trials that meet the requirement. This follows a skewed result, where a normal distribution would not: for 1/x with x = 1.0 ± 0.4, P(1/x ≤ 1.5) is about 80 % from the trials, but about 50 % from a normal distribution with the same mean and standard deviation. **A worked example** (the tutorial's): 5 ± 0.5 km walked in 23 min is 13.0 ± 1.3 km/h. Against `SPD-001 speed <= 15 km/h` the margin is 1.96 km/h, less than U = 2.6 km/h, so the check is inconclusive. P(conform) = Φ(1.96 / 1.30) = **93.3 %**: likely, but not certain, to meet the limit. An array is judged by its least conforming element; the probability that every element conforms at once is not worked out. ## Decision rules Without a rule, the verdict is the one above: inconclusive whenever the margin is within U. A specification or standard often says how to decide instead (JCGM 106, 8). Write the rule in the requirement's line, or choose it on Verify Requirement (which then overrides the line): | Rule | In a line | Passes when | Fails when | Otherwise | |---|---|---|---|---| | **Simple acceptance** | `rule=simple` | the nominal value meets the limit | it does not | (never inconclusive) | | **Guard band** | `guard=1` | the value is inside the limit by more than w = 1·U | it is not | (never inconclusive) | | **Specific risk** | `risk=2.5%` | P(conform) ≥ 100 % − 2.5 % | P(conform) ≤ 2.5 % | inconclusive | - *Simple acceptance* leaves up to a 50 % chance of a wrong verdict near the limit, which the reported P(conform) shows. - A *guard band* trades false passes for false fails: with w = U at k = 2, a pass is wrong at most about 2.3 % of the time (Φ(−2)). - *Specific risk* states the acceptable chance of a wrong pass directly. In the example above, `risk=10%` passes (93.3 % ≥ 90 %) and `risk=2.5%` stays inconclusive. Use the rule your standard or customer names. A rule chosen to turn an inconclusive verdict into a pass hides exactly what the uncertainty is there to show. ## In Python and on the command line ```python import noodlelab.verify as nv speed = nv.measure("13.04 ± 1.30 km/h") check = nv.verify("SPD-001 speed <= 15 km/h", speed) check.status, check.p_conform # ('inconclusive', 0.934...) nv.verify("SPD-001 speed <= 15 km/h", speed, rule="risk=10%").status # 'passed' mc = nv.monte_carlo(lambda: 1 / nv.measure("1 ± 0.4"), 40_000) nv.verify("Y y <= 1.5", mc).p_conform # from the trials ``` `noodlelab verify --json` gives each check's `status`, `margin`, `uncertainty`, `p_conform` and `rule`, and the run records, the Requirements tab's history and its CSV export carry them too. The compliance matrix in a report gains a **P(conform)** column whenever a check has one. See also {doc}`reference/concepts` (`noodlelab.core.requirements`) for the full reference.