Data Model Map β
NOTE
Target Audience: Intermediate users
Prerequisites: Please read the Glossary and Getting Started first.
STEP entities are deeply and complexly nested. To prevent implementers from getting lost, this page illustrates the primary hierarchies and navigation paths, accompanied by implementation examples.
πΊοΈ Overall Map β
The four primary hierarchies in a STEP file:
- Core Hierarchy: From Product to Geometry.
- Assembly Structure: Parent-child relationships.
- PMI Hierarchy: Dimensions and tolerances.
- Styling and Colors: Colors, transparency, and layers.
1. Core Hierarchy: From Product to Geometry β
Difficulty: β
β
β (Intermediate)
Importance: β
β
β
(Essential)
The link from "Management Data" to "Geometry" that forms the foundation of every STEP file.
B-rep Topology Hierarchy β
Once you reach the REPRESENTATION_ITEM level, the geometry is organized as a nested hierarchy of topological elements.
Entity Details β
| Entity | Role | Required/Optional | Frequency |
|---|---|---|---|
| PRODUCT | The part itself | Required | 1 |
| PRODUCT_DEFINITION_FORMATION | Version management | Required | 1 |
| PRODUCT_DEFINITION | Design context | Required | 1+ |
| PRODUCT_DEFINITION_SHAPE | Bridge between Admin and Shape | Required | 1 |
| SHAPE_REPRESENTATION | Geometry container | Required | 1+ |
| REPRESENTATION_ITEM | Actual geometry elements | Required | Many |
Example in a Real File β
Excerpt from Minimal STEP Analysis:
#10 = PRODUCT('Part_A','Part_A','Part_A description',(#20));
#20 = PRODUCT_CONTEXT('',#30,'design');
#30 = APPLICATION_CONTEXT('managed model based 3d engineering');
#40 = PRODUCT_DEFINITION_FORMATION('1','first version',#10);
#50 = PRODUCT_DEFINITION('design','',#40,#60);
#60 = PRODUCT_DEFINITION_CONTEXT('part definition',#30,'design');
#70 = PRODUCT_DEFINITION_SHAPE('','',#50);
#80 = SHAPE_DEFINITION_REPRESENTATION(#70,#90);
#90 = SHAPE_REPRESENTATION('',(#100,#110,#120),#130);
#100 = ADVANCED_FACE(...); β Geometry data starts hereπ Details: Geometry and Topology
π Details: Anatomy of Product Entities
Basic Traversal Pattern (Python-style Pseudocode):
def get_all_faces_from_product(product_instance):
"""
Retrieve all ADVANCED_FACE entities from a PRODUCT instance.
"""
# 1. PRODUCT β PRODUCT_DEFINITION
prod_def_formation = find_referencing(product_instance, 'PRODUCT_DEFINITION_FORMATION', 'of_product')
prod_def = find_referencing(prod_def_formation, 'PRODUCT_DEFINITION', 'formation')
# 2. PRODUCT_DEFINITION β SHAPE_REPRESENTATION
prod_def_shape = find_referencing(prod_def, 'PRODUCT_DEFINITION_SHAPE', 'definition')
shape_def_rep = find_referencing(prod_def_shape, 'SHAPE_DEFINITION_REPRESENTATION', 'definition')
shape_rep = shape_def_rep.used_representation # Direct attribute reference
# 3. SHAPE_REPRESENTATION β ADVANCED_FACE
faces = []
for item in shape_rep.items: # List attribute
if item.entity_type == 'ADVANCED_FACE':
faces.append(item)
elif item.entity_type == 'MANIFOLD_SOLID_BREP':
# For Solids, traverse deeper
for face in item.outer.cfs_faces:
faces.append(face)
return facesImplementation Considerations:
find_referencing(): Reverse lookups for instances that reference the current one.- Beware of forward references (the target ID might not be parsed yet).
- Speed up processing with caching (memoization).
Efficient Implementation:
# Build a hash map for direct access by instance ID
instance_map = {} # {id: instance}
reference_map = {} # {referenced_id: [referencing_instances]}
# Build during parsing
for inst in instances:
instance_map[inst.id] = inst
for attr_value in inst.attributes:
if isinstance(attr_value, Reference):
reference_map.setdefault(attr_value.id, []).append(inst)π Details: Anatomy of Product Entities
2. Assembly Structure β
Difficulty: β
β
β
(Advanced)
Importance: β
β
β (Frequent)
Assemblies are defined as "usage relationships" between product definitions.
Entity Hierarchy Diagram β
Entity Details β
| Entity | Role | Attributes |
|---|---|---|
| NEXT_ASSEMBLY_USAGE_OCCURRENCE | Defines parent-child relation | relating_PD, related_PD |
| CONTEXT_DEPENDENT_SHAPE_REPRESENTATION | Placement information | representation_relation |
| ITEM_DEFINED_TRANSFORMATION | Transformation matrix | transform_item_1, transform_item_2 |
Example in a Real File β
# Parent Assembly
#100 = PRODUCT('Assembly_A',...);
#110 = PRODUCT_DEFINITION(..., #100, ...);
# Child Part
#200 = PRODUCT('Part_B',...);
#210 = PRODUCT_DEFINITION(..., #200, ...);
# Usage Relationship (Assembly_A uses Part_B)
#300 = NEXT_ASSEMBLY_USAGE_OCCURRENCE('1','Part B Instance','',#110,#210,$);
# Placement Info (Transformation)
#310 = CONTEXT_DEPENDENT_SHAPE_REPRESENTATION(#320,#330);
#320 = ( REPRESENTATION_RELATIONSHIP('','',#340,#350)
REPRESENTATION_RELATIONSHIP_WITH_TRANSFORMATION(#360) );
#360 = ITEM_DEFINED_TRANSFORMATION('','',#370,#380);
#370 = AXIS2_PLACEMENT_3D(...); # Source
#380 = AXIS2_PLACEMENT_3D(...); # Target (Placement Location)Tips for Parser Implementation β
Building an Assembly Tree (Python-style):
def build_assembly_tree(root_product_def):
"""
Build an assembly tree starting from a PRODUCT_DEFINITION.
"""
tree = {
'product_def': root_product_def,
'children': []
}
# Search for NAUOs where this PRODUCT_DEFINITION is the parent
nauos = find_all_by_type('NEXT_ASSEMBLY_USAGE_OCCURRENCE')
for nauo in nauos:
if nauo.relating_product_definition == root_product_def:
child_pd = nauo.related_product_definition
# Retrieve the placement transformation
transform = get_placement_transform(nauo)
# Recursively build the child tree
child_tree = build_assembly_tree(child_pd)
child_tree['transform'] = transform
child_tree['nauo'] = nauo
tree['children'].append(child_tree)
return tree
def get_placement_transform(nauo):
"""
Retrieve the transformation matrix from an NAUO.
"""
# Search for CONTEXT_DEPENDENT_SHAPE_REPRESENTATION
cdsrs = find_referencing(nauo, 'CONTEXT_DEPENDENT_SHAPE_REPRESENTATION')
for cdsr in cdsrs:
rep_rel = cdsr.representation_relation
if hasattr(rep_rel, 'transformation_operator'):
item_transform = rep_rel.transformation_operator
# Build a 4x4 matrix from AXIS2_PLACEMENT_3D
return build_4x4_matrix(item_transform)
return identity_matrix() # Default to identityHow Coordinate Transformation Works:
Transformation Process:
- Parent Coordinate System: The assembly's origin and orientation
- NAUO: Defines the parent-child relationship
- Transformation Matrix: Calculated from
AXIS2_PLACEMENT_3D(source and target) - Child Geometry: All child part coordinates are transformed using this matrix
- Result: Child part appears in the correct position within the assembly
Example: If a child part is defined at origin (0,0,0) but needs to be placed at (100, 50, 0) in the assembly, the transformation matrix translates all child coordinates by (100, 50, 0).
Implementation Considerations:
- Cyclic References: Watch for invalid files where a parent references a child and vice-versa.
- Multiple Instances: The same child part can be used multiple times (multiple NAUOs).
- Missing Matrices: Assume an identity matrix if CDSR is absent.
- Matrix Calculation: Convert
AXIS2_PLACEMENT_3Dto a 4x4 transformation matrix for rendering.
π Details: Assembly Support (Comparison Page)
3. PMI (Product and Manufacturing Information) Hierarchy β
Difficulty: β
β
β
(Advanced)
Importance: β
ββ (AP242 only)
PMI gives βtechnical meaningβ to a productβs shape and links that meaning to tolerances and annotations.
Important: although itβs common to talk about βPMI on a face,β the ISO schemas typically define SHAPE_ASPECT on the productβs shape definition (PRODUCT_DEFINITION_SHAPE). Exporters then use additional associativity patterns to connect a given semantic element to specific faces/edges. Do not assume SHAPE_ASPECT.of_shape points directly to ADVANCED_FACE.
Entity Hierarchy Diagram β
Tips for Parser Implementation β
Extracting PMI (Python-style, high-level):
def extract_pmi(step_file):
"""
Extract PMI information.
Note: The exact mapping between semantic elements (e.g., SHAPE_ASPECT)
and specific faces/edges is exporter-dependent (CAx-IF patterns vary).
"""
pmi = []
# 1) Collect semantic handles (shape aspects) and tolerances/datums
shape_aspects = find_all_by_type(step_file, 'SHAPE_ASPECT')
tolerances = find_all_by_type(step_file, 'GEOMETRIC_TOLERANCE')
# 2) Link tolerances/datums to SHAPE_ASPECT (schema-dependent; may require reverse lookups)
for sa in shape_aspects:
sa_tols = find_all_referencing(sa, 'GEOMETRIC_TOLERANCE')
sa_datums = find_all_referencing(sa, 'DATUM_FEATURE') + find_all_referencing(sa, 'DATUM')
# 3) Resolve targets (faces/edges) using exporter-specific associativity
targets = resolve_shape_targets(sa) # exporter/CAx-IF pattern specific
pmi.append({
'shape_aspect': sa,
'tolerances': sa_tols,
'datums': sa_datums,
'targets': targets,
})
return pmiImplementation Considerations:
- PMI is exclusive to AP242 (limited support in AP214).
SHAPE_ASPECThandling can be complex (multiple faces might define a single datum).- Interoperability between CAD systems is not always perfect.
4. Presentation (Colors and Layers) β
Difficulty: β
β
β (Intermediate)
Importance: β
β
β (AP214 and later)
Styles are assigned to geometry elements to represent colors, transparency, and layers.
π Details: Styling and Colors
Quick Reference Diagram β
π‘ Implementation Best Practices β
1. Phased Parsing β
Recommended Order:
- Phase 1: HEADER Analysis β Confirm AP Version.
- Phase 2: Build Instance Map (All instances in a hash map).
- Phase 3: Core Path (PRODUCT β SHAPE_REPRESENTATION).
- Phase 4: Build Assembly Tree (As needed).
- Phase 5: Colors & PMI (Optional).
2. Error Handling β
def safe_traverse(instance, target_type, attribute_name=None):
"""
Safe traversal (Returns None if reference does not exist).
"""
try:
if attribute_name:
ref = getattr(instance, attribute_name)
else:
ref = find_referencing(instance, target_type)
if ref is None:
logger.warning(f"{target_type} not found for {instance.id}")
return None
return ref
except Exception as e:
logger.error(f"Error traversing from {instance.id}: {e}")
return None3. Performance Optimization β
| Method | Effect | Complexity |
|---|---|---|
| Instance Map (Hash Map) | Best | Low |
| Reverse Reference Map | Best | Medium |
| Memoization (Caching) | Good | Low |
| Streaming Parsing | Fair | High |
Why is it so complex? β
STEP is designed to strictly separate "What (Product)", "In what context (Definition)", and "What shape (Shape)" it has, rather than just being "geometric data."
Benefits:
- Revisions can be updated without changing the 3D geometry.
- The same part can be placed in multiple locations in different assemblies.
- The same model can be used across design, analysis, and manufacturing with different contexts.
Impact on Implementers:
- It feels complex at first, but it's consistent once you understand the patterns.
- Traversal functions developed for one case can be reused for others.
π Next Steps β
- EXPRESS Language Basics - Learn how to read schemas.
- Common Pitfalls - Implementation warnings and solutions.