Files
cadSet/SimpleCADAPI/docs/core/dimension-tolerance-chains.md
T

257 lines
9.7 KiB
Markdown

# Dimension Tolerance Chains
SimpleCADAPI can attach units and manufacturing tolerances to declared dimension
variables, infer dimensions through the expression DAG, propagate source
variation, and verify derived dimensions against design requirements.
This feature is separate from sketch-solver residual tolerances, boolean fuzzy tolerances, mesh resolution, and geometric fitting tolerances. A dimension tolerance describes permitted manufacturing variation around a nominal design value; it never changes a CAD operation's numerical robustness settings.
## Declare Source Dimensions
Every variable that participates in a tolerance chain must declare a tolerance:
```python
import simplecadapi as scad
width = scad.var("width", 10.0, unit="mm", tolerance=0.1)
shaft = scad.var(
"shaft",
0.315,
unit="in",
tolerance=(-0.05, 0.0),
tolerance_unit="mm",
)
```
A scalar tolerance is symmetric. `tolerance=0.1` means `-0.1/+0.1`
around the nominal value. A two-value sequence contains signed
`(lower_deviation, upper_deviation)` values. The lower deviation must be at most
zero, the upper deviation must be at least zero, and all values must be finite.
`tolerance_unit` defaults to `unit`. It may differ from the nominal unit, but the
dimensions must match. Geometry and tolerance propagation use canonical
millimeters for length and degrees for angle. Declaration-space values remain on
the `Var` for display and serialization.
The same values can be represented explicitly:
```python
tolerance = scad.DimensionTolerance(
lower_deviation=-0.05,
upper_deviation=0.0,
)
shaft = scad.var("shaft", 8.0, unit="mm", tolerance=tolerance)
```
Tolerance identity follows `expr_id`, not the human-readable variable name. Two variables with the same name remain separate tolerance sources.
## Propagate A Chain
```python
housing = scad.var("housing", 100.0, unit="mm", tolerance=0.15)
bearing = scad.var(
"bearing", 2.0, unit="cm", tolerance=(-0.04, 0.05), tolerance_unit="mm"
)
spacer = scad.var("spacer", 79.4, unit="mm", tolerance=0.05)
clearance = housing - bearing - spacer
result = scad.analyze_tolerance(clearance, method="worst_case")
print(result.nominal)
print(result.lower_bound, result.upper_bound)
print(result.lower_deviation, result.upper_deviation)
for contribution in result.contributions:
print(contribution.variable_name, contribution.lower_deviation, contribution.upper_deviation)
```
`ToleranceAnalysis` contains absolute bounds, deviations from nominal, inferred
`dimension`, canonical `unit`, and one contribution record per source variable.
Each contribution reports its nominal and source tolerance in canonical units.
The unit system validates the expression before propagation. This permits
physically meaningful nonlinear chains such as:
```python
width = scad.var("width", 30.0, unit="mm", tolerance=0.1)
height = scad.var("height", 40.0, unit="mm", tolerance=0.2)
diagonal = scad.sqrt(width**2 + height**2)
analysis = scad.analyze_tolerance(diagonal)
assert analysis.dimension == scad.LENGTH
assert analysis.unit == scad.MM
```
Area and volume expressions can be inferred and analyzed. Persisted manufacturing
requirements currently accept final Length and Angle results only.
## Propagation Methods
### Worst Case
`method="worst_case"` is the default and the safety-oriented validation method.
- Affine chains preserve variable identity and combine coefficients exactly. Repeated use is not treated as an independent source, so `x - x` has zero propagated tolerance.
- Nonlinear chains use conservative interval propagation.
- Multiplication and division consider all endpoint sign combinations.
- Integer, negative, fractional, and varying powers validate their mathematical domains.
- `sin` and `cos` include interior extrema; `tan` rejects intervals crossing a discontinuity.
- `sqrt`, `asin`, and `acos` validate the entire input interval.
- Division rejects denominator intervals containing zero.
- `atan2` rejects tolerance regions containing its undefined origin.
Conservative interval propagation can intentionally overestimate a strongly correlated nonlinear expression. It must not underestimate a safety bound.
### RSS
`method="rss"` uses first-order analytic sensitivities and root-sum-square combination:
```python
result = scad.analyze_tolerance(clearance, method="rss")
```
Distinct variables are assumed independent. Repeated occurrences of the same variable are merged before RSS, so `x - x` still has zero sensitivity and `x + x` has twice the sensitivity of `x`.
RSS validates the full declared tolerance interval before calculating the first-order estimate. A nominal point cannot hide a division singularity, trigonometric discontinuity, or invalid function domain elsewhere in the source range.
Covariance matrices, correlation groups, probability distributions, and Monte Carlo analysis are not represented by the current API. Use `worst_case` when the independence assumption is unavailable or when a guaranteed envelope is required.
## Check One Requirement
`check_tolerance()` returns a result without raising when the derived tolerance exceeds the permitted deviations:
```python
check = scad.check_tolerance(
clearance,
tolerance=(-0.25, 0.24),
method="worst_case",
name="axial_clearance",
tolerance_unit="mm",
)
print(check.passed)
print(check.lower_margin, check.upper_margin)
```
A non-negative lower and upper margin means the requirement passes, subject to a
small floating-point comparison epsilon. Requirement deviations are converted
from `tolerance_unit` to the target's canonical unit before comparison.
## Session Requirements And Automatic Validation
Use `GraphSession.require_tolerance()` for design requirements that must travel with the model:
```python
with scad.GraphSession() as session:
body = scad.make_box_rsolid(housing, 10.0, 10.0)
session.require_tolerance(
clearance,
(-0.25, 0.24),
method="worst_case",
name="axial_clearance",
requirement_id="req.axial_clearance",
tolerance_unit="mm",
)
report = session.validate_tolerances(raise_on_failure=True)
model_json = scad.export_model_json(session)
```
Automatic validation occurs when:
1. `validate_tolerances(raise_on_failure=True)` is called.
2. A session or model JSON payload is exported.
3. A model JSON payload is imported or replayed.
4. A model is translated to FreeCAD through the model importer.
A failed requirement raises `ToleranceValidationError` at the tolerance layer. Model import, export, and replay expose it through the existing structured `SimpleCADError` harness where applicable.
Declaring a requirement validates that the chain is complete and mathematically defined, but it still records a failing requirement so callers can inspect its margins. Export and replay are the enforcement boundaries.
## Serialization
Variable tolerances are stored on variable nodes in `expression_graph`:
```json
{
"expr_id": "var_width",
"kind": "var",
"name": "width",
"default": 1.0,
"unit": "in",
"tolerance": {
"lower_deviation": -0.1,
"upper_deviation": 0.1
},
"tolerance_unit": "mm"
}
```
Design requirements and their latest validation evidence are stored in the top-level `tolerance_graph`:
```json
{
"requirements": [
{
"requirement_id": "req.axial_clearance",
"target_expr_id": "expr_clearance",
"tolerance": {
"lower_deviation": -0.25,
"upper_deviation": 0.24
},
"method": "worst_case",
"name": "axial_clearance",
"tolerance_unit": "mm",
"target_dimension": {
"length": 1,
"angle": 0
}
}
],
"validation": {
"passed": true,
"checks": []
}
}
```
Validation evidence is recomputed during import; serialized evidence is not
trusted as an authority. The target expression dimension is inferred again and
must match `target_dimension`; `tolerance_unit` must have the same dimension.
Payloads created before units or `tolerance_graph` existed remain valid as legacy
unitless expressions and import with an empty tolerance graph when it is absent.
Nominal geometry replay still uses the numeric snapshots in operation-node `params`. Tolerance validation does not sample or regenerate worst-case geometry.
## FreeCAD Translation
FreeCAD translation keeps the full tolerance graph as document metadata. The
`SimpleCADExpressions` spreadsheet stores lower/upper deviations in columns E/F,
nominal unit in G, tolerance unit in H, and inferred dimension in I. Spreadsheet
values and formulas use canonical CAD values so inch/radian declarations remain
consistent with operation-node snapshots. The translator preserves tolerance
intent but does not convert it into FreeCAD geometric-tolerance objects or
statistical solvers.
## Failure Conditions
Tolerance analysis fails explicitly for:
- a source variable without a declared tolerance
- non-finite nominal values or deviations
- malformed signed lower/upper deviations
- unknown, malformed, non-finite, incompatible, overflowing, or underflowing units
- addition/subtraction or `atan2` with incompatible dimensions
- invalid powers, square roots, or trigonometric dimensions
- mixing unit-declared and legacy variables in one expression
- a requirement unit or persisted target dimension that disagrees with the target
- an Area, Volume, Dimensionless, or compound-dimension requirement target
- duplicate requirement IDs
- unknown or dangling expression references
- malformed, cyclic, duplicate-ID, or unsupported expression nodes
- an undefined expression anywhere in the declared tolerance interval
- an RSS derivative at a non-differentiable nominal point
- an unsupported propagation method
See [Physical Units And Dimension Inference](physical-units.md) for the complete
unit registry, dimension algebra, custom-unit payload, and legacy behavior.