Common Pitfalls β
This document explains problems frequently encountered during STEP implementation and conversion, along with countermeasures, detection methods, and implementation examples.
π Table of Contents β
- Unit Mismatches
- Precision (Tolerance) Issues
- Face Orientation and Consistency
- Loss of Assembly Structure
- Dangling PMI
- Missing Colors and Layers
- Encoding Issues
- Improper Forward Reference Handling
- Persistence and Topology Risks
1. Unit Mismatches β
Difficulty: β
β
β (Intermediate)
Frequency: β
β
β
(Very Common)
Impact: π΄ High (Drastic changes in geometry size)
β The Problem β
- Conversion errors between
SI_UNITandCONVERSION_BASED_UNIT(e.g., Inches). - Definitions like "length in mm, angles in Radians" are not correctly specified in the file.
- Implementer's Blind Spot: Missing the interpretation of SI prefixes (kilo, milli).
The Unit Mismatch Disaster β
Examples:
- Interpreting mm as meters β Geometry becomes 1000x larger.
- Misidentifying an Inch file as mm β ~4% error (or 25.4x depending on the direction).
β Solutions β
Unit Normalization Flow β
Implementers should normalize all incoming data to a single internal unit system (typically mm).
1. Always Verify UNIT_CONTEXT β
#500 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
#510 = ( PLANE_ANGLE_UNIT() NAMED_UNIT(*) SI_UNIT($,.RADIAN.) );.MILLI.,.METRE.= mm.CENTI.,.METRE.= cm- None (
$) or.METRE.= m
2. Implement an SI Prefix Conversion Table β
| PREFIX | Factor | Example |
|---|---|---|
.PICO. | 10^-12 | |
.NANO. | 10^-9 | |
.MICRO. | 10^-6 | ΞΌm |
.MILLI. | 10^-3 | mm |
.CENTI. | 10^-2 | cm |
None or $ | 1 | m |
.KILO. | 10^3 | km |
π Detection Methods β
Validation Code (Python):
def validate_and_get_unit_scale(step_file):
"""
Retrieve units and return the conversion scale to mm.
"""
# Search for GEOMETRIC_REPRESENTATION_CONTEXT
contexts = find_all_by_type(step_file, 'GEOMETRIC_REPRESENTATION_CONTEXT')
for ctx in contexts:
units = ctx.units if hasattr(ctx, 'units') else []
for unit in units:
if 'LENGTH_UNIT' in str(unit.entity_types):
# Retrieve prefix from SI_UNIT
prefix = unit.prefix if hasattr(unit, 'prefix') else None
# Calculate scale from prefix
scale = get_si_prefix_scale(prefix) # .MILLI. β 0.001
return scale # Factor to meters (0.001 if mm)
# Default: assume meters
logger.warning("LENGTH_UNIT not found, assuming meters")
return 1.0
def get_si_prefix_scale(prefix):
"""Get factor from SI prefix"""
prefix_map = {
'PICO': 1e-12, 'NANO': 1e-9, 'MICRO': 1e-6,
'MILLI': 1e-3, 'CENTI': 1e-2,
'DECI': 1e-1,
'DECA': 1e1, 'HECTO': 1e2, 'KILO': 1e3
}
if prefix is None or prefix == '$':
return 1.0
return prefix_map.get(prefix.upper().strip('.'), 1.0)π‘ Implementation Best Practices β
- Pre-calculate and cache unit conversion factors.
- Automatically convert all coordinates upon retrieval (e.g., unifying internal data to mm).
- Explicitly specify units during export (e.g.,
SI_UNIT(.MILLI.,.METRE.)).
Ansys Notes (Workbench / SpaceClaim / Fluent / Meshing) β
- Ansys geometry workflows commonly behave as if the geometry kernel is meter-based internally, so a βmm β mβ mismatch can look like a ( \times 1000 ) scale error.
- Treat βdisplay unitsβ as separate from actual imported scale; always validate by measuring a known dimension right after import.
- If you must standardize: standardize in the CAD export (consistent units + AP) and validate on the receiving side.
2. Precision (Tolerance) Issues β
Difficulty: β
β
β (Intermediate)
Frequency: β
β
β (Common)
Impact: π‘ Medium (Gaps appearing during geometric operations)
β The Problem β
The UNCERTAINTY_MEASURE_WITH_UNIT value differs between sender and receiver, causing mismatches in geometric operation tolerances.
Example:
- Sender: Precision 10^-6 mm
- Receiver: Evaluates with 10^-2 mm precision
- Result: Incorrectly determines that "there is a gap between faces."
β Solutions β
#600 = UNCERTAINTY_MEASURE_WITH_UNIT(1.0E-6,(#500),'distance_accuracy_value','');β Precision is 10^-6 (If the unit system is meters, 10^-6 m = 1 ΞΌm).
Recommended Values:
- mm-based:
1.0E-3(0.001 mm = 1 ΞΌm) - m-based:
1.0E-6(1 ΞΌm)
π Detection Methods β
def get_geometric_tolerance(step_file):
"""Retrieve geometric precision"""
uncertainties = find_all_by_type(step_file, 'UNCERTAINTY_MEASURE_WITH_UNIT')
for unc in uncertainties:
if 'distance_accuracy' in unc.description.lower():
value = unc.value_component
unit = unc.unit_component
# Account for unit conversion
scale = get_unit_scale(unit)
tolerance_in_mm = value * scale * 1000 # Convert to mm
return tolerance_in_mm
# Default
return 0.001 # 1 ΞΌmπ‘ Implementation Best Practices β
- Use this value to set tolerances in geometric libraries (like OpenCascade).
- Always set an appropriate precision value during export.
- Warn if the precision is excessively large (e.g., > 1mm).
Ansys Notes (Import / Healing / Stitching) β
- Import βheal/stitchβ tolerances are often absolute values in meters. If your model is authored in mm, a tolerance like (1\times10^{-7},\text{m}) is (1\times10^{-4},\text{mm}).
- Too-large tolerance can remove small features (slivers, thin gaps); too-small tolerance can cause sewing/stitching to fail.
- For CAE robustness, record and control the import tolerance used for each dataset (so results are reproducible).
3. Face Orientation and Consistency β
Difficulty: β
β
β
(Advanced)
Frequency: β
β
β (Common)
Impact: π΄ High (Broken solids)
β The Problem β
Orientation flags (.T. / .F.) for FACE_BOUND and ORIENTED_EDGE are inverted, leading to the conclusion that a solid is not "closed."
Face Definition in STEP:
#100 = ADVANCED_FACE('',(#110),#120,.T.); β .T. = Face normal direction
#110 = FACE_OUTER_BOUND('',(#111,#112,#113,#114),.T.);
#111 = ORIENTED_EDGE('',*,*,#115,.F.); β .F. = Edge used in reverse directionβ Solutions β
1. Check Face Orientation Consistency β
def validate_face_orientation(face):
"""
Verify orientation consistency of an ADVANCED_FACE.
"""
face_orientation = face.same_sense # .T. or .F.
face_surface = face.face_geometry
for bound in face.bounds:
bound_orientation = bound.orientation
for oriented_edge in bound.bound:
edge_sense = oriented_edge.orientation
# Check if the combination of orientations is valid
# (Detailed geometric validation is usually deferred to libraries like OpenCascade)
return True2. Verify Normal Vectors β
Traverse the edge loops to calculate the area vector and verify it matches the face normal.
π Detection Methods β
- Verify by opening in CAD.
- Use
BRepCheck_Analyzerin OpenCascade. - If implementing manually, verify Euler's polyhedral formula (V - E + F = 2).
π‘ Implementation Best Practices β
- Do not change face orientations; use the export result from CAD as-is.
- Use CAD "repair" functions during export.
- Implement automatic orientation correction on the receiving side (e.g., OpenCascade's
ShapeFix).
4. Loss of Assembly Structure β
Difficulty: β
β
β
(Advanced)
Frequency: β
β
β (Common)
Impact: π‘ Medium (Loss of structure)
β The Problem β
- Mismatches in linking
NEXT_ASSEMBLY_USAGE_OCCURRENCE(NAUO) with transformation matrices. - Broken links between
PRODUCT_DEFINITIONs of parent and child parts. - The transformation matrix (
ITEM_DEFINED_TRANSFORMATION) is incorrectly set to identity.
β Solutions β
1. Validate NAUO β
def validate_assembly_structure(step_file):
"""
Validate assembly structure.
"""
nauos = find_all_by_type(step_file, 'NEXT_ASSEMBLY_USAGE_OCCURRENCE')
issues = []
for nauo in nauos:
# Check for existence of parent/child PRODUCT_DEFINITIONs
parent_pd = nauo.relating_product_definition
child_pd = nauo.related_product_definition
if parent_pd is None or child_pd is None:
issues.append(f"NAUO {nauo.id}: Broken PD reference")
continue
# Verify existence of placement transformation
cdsrs = find_referencing(nauo, 'CONTEXT_DEPENDENT_SHAPE_REPRESENTATION')
if not cdsrs:
issues.append(f"NAUO {nauo.id}: No placement transform")
# Check if transformation is identity
for cdsr in cdsrs:
transform = extract_transform_matrix(cdsr)
if is_identity_matrix(transform):
logger.warning(f"NAUO {nauo.id}: Identity transform (may be intentional)")
return issues2. Extract Transformation Matrix β
def extract_transform_matrix(cdsr):
"""
Extract 4x4 matrix from CONTEXT_DEPENDENT_SHAPE_REPRESENTATION.
"""
rep_rel = cdsr.representation_relation
if hasattr(rep_rel, 'transformation_operator'):
item_transform = rep_rel.transformation_operator
# Build matrix from AXIS2_PLACEMENT_3D
origin = item_transform.location.coordinates # (x, y, z)
axis = item_transform.axis.direction_ratios if hasattr(item_transform, 'axis') else (0, 0, 1)
ref_direction = item_transform.ref_direction.direction_ratios if hasattr(item_transform, 'ref_direction') else (1, 0, 0)
# Build 4x4 matrix (requires linear algebra knowledge)
matrix = build_transformation_matrix(origin, axis, ref_direction)
return matrix
return identity_matrix_4x4()π Detection Methods β
- Tree View: List
PRODUCTentities in a tree to visualize parent-child relations. - Isolation Detection: Detect
PRODUCT_DEFINITIONs not referenced by any NAUO. - Placement Verification: Check if all matrices are identity by mistake.
π‘ Implementation Best Practices β
- Set default values: Use an identity matrix if no transformation is found.
- Check for cyclic references: Detect instances where a parent references a child that eventually references the parent.
- Support multiple instances: Correctness when the same child part is used multiple times.
5. Dangling PMI β
Difficulty: β
β
β
(Advanced)
Frequency: β
ββ (AP242 only)
Impact: π‘ Medium (Loss of PMI)
β The Problem β
The tolerance (GEOMETRIC_TOLERANCE) exists, but the reference to the target face (SHAPE_ASPECT) is broken.
Causes:
- Complexity and redundancy of PMI links in AP242.
- Differences in interpretation between CAD systems.
- Incomplete export implementations.
β Solutions β
def validate_pmi_linkage(step_file):
"""
Validate PMI linkage consistency.
"""
tolerances = find_all_by_type(step_file, 'GEOMETRIC_TOLERANCE')
for tol in tolerances:
# 1. Check link to Shape Aspect
shape_aspects = find_all_referencing(tol, 'SHAPE_ASPECT')
if not shape_aspects:
logger.warning(f"Tolerance {tol.id}: No SHAPE_ASPECT reference (dangling PMI)")
continue
# 2. Check link from Shape Aspect to geometric elements
for sa in shape_aspects:
if not hasattr(sa, 'of_shape') or sa.of_shape is None:
logger.warning(f"SHAPE_ASPECT {sa.id}: No geometry reference")π Detection Methods β
- Check references from every
GEOMETRIC_TOLERANCEtoSHAPE_ASPECT. - Check references from
SHAPE_ASPECTto actual geometry (likeADVANCED_FACE).
π‘ Implementation Best Practices β
- Implement PMI loading as an optional feature (so geometry still loads if PMI fails).
- Follow CAx-IF Recommended Practices.
- Continue with a warning if PMI is incomplete.
6. Missing Colors and Layers β
Difficulty: β
β
β (Intermediate)
Frequency: β
β
β
(Very Common)
Impact: π’ Low (Visual only)
β The Problem β
- Color information defined on an inappropriate layer (e.g., Shell vs. Face).
PRESENTATION_STYLE_ASSIGNMENTis not directly linked to geometry.- AP limitations (AP203 does not support colors).
π Details: Styling and Colors
β Solutions β
1. Verify AP β
AP203 does not support colors/layers β Use AP214 or later.
2. Correct STYLED_ITEM Creation β
# Correct Example: Link STYLED_ITEM directly to a FACE
#100 = ADVANCED_FACE(...);
#200 = STYLED_ITEM('',(#210),#100); # Item is a FACE
#210 = PRESENTATION_STYLE_ASSIGNMENT((#220));
#220 = SURFACE_STYLE_USAGE(.BOTH.,#230);
#230 = SURFACE_SIDE_STYLE('',(#240));
#240 = SURFACE_STYLE_RENDERING(#250,.MATTE.);
#250 = COLOUR_RGB('Red',1.0,0.0,0.0);Incorrect Example:
# STYLED_ITEM references a Shell (ignored by some CAD)
#100 = CLOSED_SHELL(...);
#200 = STYLED_ITEM('',(#210),#100); # Some CAD will not recognize thisπ Detection Methods β
def validate_color_assignment(step_file):
"""
Validate color assignment correctness.
"""
styled_items = find_all_by_type(step_file, 'STYLED_ITEM')
for si in styled_items:
item = si.item
# Check what the color is attached to
if item.entity_type not in ['ADVANCED_FACE', 'MANIFOLD_SOLID_BREP', 'SHELL_BASED_SURFACE_MODEL']:
logger.warning(f"STYLED_ITEM {si.id}: Attached to {item.entity_type} (may not be supported)")
# Check if it reaches COLOUR_RGB
color = extract_color_from_styled_item(si)
if color is None:
logger.warning(f"STYLED_ITEM {si.id}: No COLOUR_RGB found")π‘ Implementation Best Practices β
- Attach colors at the FACE level (highest compatibility).
- Color values are 0.0 to 1.0 (convert to 0-255:
int(value * 255)). - Set default colors (e.g., gray if no color is defined).
7. Encoding Issues β
Difficulty: β
ββ (Beginner)
Frequency: β
ββ (Occasional)
Impact: π‘ Medium (Mangled characters)
β The Problem β
Improper handling of non-ASCII characters (Japanese, Chinese, etc.).
Specification: STEP Part 21 is fundamentally ISO 8859-1 (Latin-1); non-ASCII characters must be escaped using \X2\...\X0\.
β Solutions β
# Correct Example: Unicode Escape
#10 = PRODUCT('\X2\30D130FC30C8\X0\','Part A',...);
β Unicode hex (UTF-16BE)
# \X2\30D130FC30C8\X0\ = γγγΌγγ (Part in Katakana)Parser Implementation:
def decode_step_string(s):
"""
Decode STEP strings (Handle \X2\...\X0\ escapes).
"""
import re
def replace_unicode_escape(match):
hex_str = match.group(1)
# Interpret as UTF-16BE
bytes_data = bytes.fromhex(hex_str)
return bytes_data.decode('utf-16-be')
# Replace \X2\...\X0\ patterns
result = re.sub(r'\\X2\\([0-9A-F]+)\\X0\\', replace_unicode_escape, s)
return resultπ Detection Methods β
- Check if the file contains
\X2\. - Warn if non-ASCII characters (0x80 or higher) are included directly.
π‘ Implementation Best Practices β
- Input: Correctly decode escape sequences.
- Output: Always escape non-ASCII characters.
- Read the file as ASCII, not UTF-8.
8. Improper Forward Reference Handling β
Difficulty: β
β
β (Intermediate)
Frequency: β
β
β (Common)
Impact: π΄ High (Parsing error)
β The Problem β
Handling cases where the reference target (#number) is defined after the reference source.
#10 = PRODUCT(..., (#20, #30), ...); β References #20, #30
#20 = PRODUCT_CONTEXT(...); β Defined after #10
#30 = APPLICATION_CONTEXT(...);Incorrect Implementation:
# β Error: Sequential processing
for line in step_file:
inst = parse_instance(line)
instance_map[inst.id] = inst
# Trying to resolve reference here β #20 doesn't exist yet!
resolve_references(inst) # ERROR!β Solutions β
Two-Pass Parser Strategy β
To handle forward references correctly, a "Two-Pass" approach is required.
Two-Pass Parser Implementation:
# β
Correct: Two-pass processing
# Pass 1: Load all instances
instance_map = {}
for line in step_file:
inst = parse_instance(line)
instance_map[inst.id] = inst # References remain unresolved
# Pass 2: Resolve all references
for inst in instance_map.values():
resolve_references(inst, instance_map)π Detection Methods β
- Verify no unresolved references remain after parsing.
- Detect references to non-existent
#numbers.
def validate_all_references(instance_map):
"""Confirm all references are resolved"""
for inst in instance_map.values():
for ref in inst.get_all_references():
if ref.target_id not in instance_map:
logger.error(f"Instance {inst.id}: Reference to non-existent #{ref.target_id}")π‘ Implementation Best Practices β
- Always implement two-pass processing.
- Check for duplicate instance IDs.
- Detect cyclic references (rare but possible).
9. Persistence and Topology Risks β
Difficulty: β
β
β
(Advanced)
Frequency: β
β
β (Common in Simulation)
Impact: π΄ High (Breakdown of automated simulation pipelines)
β The Problem β
"Persistent IDs" (face names) assigned in CAD are lost or shifted when the model is modified and re-exported.
Example:
- You name a face "Inlet" in Rhino and set up an Ansys simulation.
- You change the geometry (e.g., move a hole) and re-export the STEP.
- The new STEP file labels a different face as "Inlet" or the label disappears entirely.
β Solutions β
1. Use Semantic Labeling (SHAPE_ASPECT) β
Use AP242 where possible, and prefer exporter/tooling combinations that follow CAx-IF practices.
In many CAE pipelines (including Ansys), practical βface namingβ often comes through as attributes (e.g., layers/colors/user-defined attributes) that are mapped into the solver/pre-processor as βNamed Selections,β rather than a guaranteed, direct consumption of raw SHAPE_ASPECT names.
2. Avoid Reliance on Instance IDs (#) β
Never write simulation scripts that rely on #10, #500, etc. These numbers are recalculated every time a file is saved.
3. Best Practices for Stability β
- Name faces as late as possible in the design process.
- Use CAD-specific Named Selection / Attribute features that your toolchain demonstrably preserves (and keep the import mapping key consistent in Ansys).
π Detection Methods β
- Compare two versions of the model by behavior: re-import and confirm that the same Named Selections still scope the intended faces (area/centroid checks are often more robust than entity-level assumptions).
Summary β
The reality is not that "STEP is fragile," but rather that "CAD vendor implementations can be loose relative to the strictness of the standard, leading to discrepancies."
Advice for Implementers:
- Be thorough with validation: Always check units, precision, and reference integrity.
- Robust error handling: Load as much data as possible, even if it's incomplete.
- Follow CAx-IF Recommended Practices: This will significantly improve interoperability.
- Leverage test cases: Validate your implementation using CAx-IF benchmark files.
π Next Steps β
- Validation and CAx-IF - Ensuring quality and improving interoperability.