Overview
A step.Object is one STEP data instance, while a step.Design is the collection of all instances in a file.
An EXPRESS schema describes the type of instances, which can be an ENTITY with attributes, or an AGGREGATE such as a list, set, or bag. An ENTITY instance has a file identifier (#123) while AGGREGATEs have no identifier because they only appear as values of attributes.
STEP instances often describe low-level atomic values, like a direction vector, or a property name, which are then combined in well-defined patterns to describe higher-level concepts like a workpiece, part feature, or machining workplan. The patterns are described in the "Application Reference Model (ARM)" section of the STEP standard.
The STEP Python interface recognizes these patterns when reading data and associates the graph of objects surrounding a root STEP instance as ARM attributes that are accessed exactly like the basic EXPRESS attributes. This greatly simplifies understanding and operations on STEP data sets.
An STEP file example is shown below. Each of the instances #50 to
#62 below is a step.Object
with attribute values shown in
parenthesis. Instance #50 is the root of the higher
level MILLING_MACHINE_FUNCTIONS
ARM concept, so it will also have a through spindle coolant attribute
with a value of 'through spindle coolant off', built from instances
#51-#54, and similarly for chip removal and coolant.
/************************************************ * Application object: MILLING_MACHINE_FUNCTIONS (#50) * THROUGH_SPINDLE_COOLANT: #50, #51, #52, #53, #54: [through spindle coolant off] * CHIP_REMOVAL: #50, #55, #56, #57, #58: [chip removal off] * COOLANT: #50, #59, #60, #61, #62: [coolant on] */ #50=MACHINING_FUNCTIONS('','milling',' ',' '); #51=ACTION_PROPERTY('through spindle coolant','',#50); #52=ACTION_PROPERTY_REPRESENTATION('','',#51,#53); #53=REPRESENTATION('',(#54),#31); #54=DESCRIPTIVE_REPRESENTATION_ITEM('constant','through spindle coolant off'); #55=ACTION_PROPERTY('chip removal','',#50); #56=ACTION_PROPERTY_REPRESENTATION('','',#55,#57); #57=REPRESENTATION('',(#58),#31); #58=DESCRIPTIVE_REPRESENTATION_ITEM('constant','chip removal off'); #59=ACTION_PROPERTY('coolant','',#50); #60=ACTION_PROPERTY_REPRESENTATION('','',#59,#61); #61=REPRESENTATION('',(#62),#31); #62=DESCRIPTIVE_REPRESENTATION_ITEM('constant','coolant on');
Basic Operations
The Python repr()
gives basic information about
the step.Object
. An ENTITY has the file identifier
(#123) and the EXPRESS type. If it is the root of an ARM concept,
that appears first. Some common entities (measures, units, points)
include a summary of the underlying data rather than the EXPRESS type.
AGGREGATEs include EXPRESS type and size. They have no file
identifier because they only appear as values of attributes.
print (val)
# ENTITY instances
<step.Object #200 polyline>
<step.Object #20 product_definition_shape>
# ENTITY instances, root of an ARM concept
<step.Object ARM WORKPLAN #1035 machining_workplan>
<step.Object ARM WORKPIECE #19 product_definition>
# ENTITY instances for points, units, measures
<step.Object #897 MEASURE 25.4 mm>
<step.Object #683 UNIT deg>
<step.Object #198 POINT -0.109698, -0.0599282, 1>
# AGGREGATE lists, bags, sets. Similar sequence for ARM data
<step.Object SetOfrepresentation_item size 1>
<step.ArmCollection size 10>
Attributes of an ENTITY are available as a dictionary or by the
usual dot attribute syntax.
The entity_id() function returns
the file identifier. The step.type()
and step.isinstance() functions
follow the similarly-named Python builtins, but operate on the EXPRESS
type of an object.
The step.arm_type() function
returns the ARM type for objects that are the root of an ARM concept
or None
otherwise.
Unset attribute values are None
. Integer, floating
point, and string values are all handled as their normal Python
values. The EXPRESS Logical
type can have the usual
Python True
or False
values, as well as
a step.Logical.UNKNOWN
value.
print (LINE) pprint (dict(LINE)) # pprint sorts keys <step.Object #200 polyline> {'name': 'starting line', 'points': <step.Object ListOfcartesian_point size 3>} print (exp type:, step.type(LINE)) print (is rep item:, step.isinstance(LINE,representation_item)) print (file id: #%d% LINE.entity_id()) print (name as att:, LINE.name) print (name as sub:, LINE['name']) exp type: polyline is rep item: True file id: #200 name as att: starting line name as sub: starting line
Aggregates are Python sequences, and accept the len()
and usual iterator and index operators. In the example below, the
polyline has a list of cartesian point objects, and each point has a
list of floating point coordinate values.
ARM attributes with multiple values use
a ArmCollection
sequence object that accepts
the len()
and usual iterator and index operators.
PT = LINE.points[0] pprint (dict(PT)) {'coordinates': <step.Object ListOfdouble size 3>, 'name': 'w'} print (length:, len(PT.coordinates)) print (vals as tuple:, *PT.coordinates) print (vals by index:, PT.coordinates[0], PT.coordinates[1]) length: 3 vals as tuple: 13412.0 -1486.0 0.0 vals by index: 13412.0 -1486.0
The EXPRESS SELECT union type is not used. Only the underlying
value of the union appears. When the union holds a string or number,
it appears as a tuple with a type name, like (2.0,
'length_measure')
.
STEP Files
A step.Design object is a containier for all instances in a STEP file. The step.open_project() function reads a file, recognizes all ARM concepts, prepares indexes for the high-level API, and returns a design object that has been set as the current project. The step.save_project() function writes the current project out to a STEP file.
You can find object with the high level functions in AptAPI, ToleranceAPI or FinderAPI. You can also iterate over objects by EXPRESS type with a DesignCursor. The design can also be used as a dictionary with with the names (usually UUIDs) given to objects by the ANCHOR section in newer STEP files.
D = step.open_project(part123.stp) # iterate over all entities for obj in step.DesignCursor(D): print (entity #%d% obj.entity_id()) # iterate over just the representation items for obj in step.DesignCursor(D,representation_item): print (rep item #%d, name %s% (obj.entity_id(), obj.name)) # get object by ANCHOR value from STEP file (P21e3) obj = D['6db46031-4fab-4838-824b-91cea43922e4'] # print all of the UUID ANCHORs from STEP file PP.pprint (dict(D)) { '0098e447-15ab-44ac-b360-3d5fd4f7dcd3': <step.Object #7978 dimensional_location>, '04d1431a-9535-4ef6-a615-f00315a87548': <step.Object #8003 dimensional_location>, '09a905e5-da9c-44ab-aa9b-48689aafcd59': <step.Object #8018 dimensional_location>, '09c07753-38cf-4928-928f-4acd0e636247': <step.Object #7928 datum>, ...
Making STEP Data
When creating STEP data, you first need a Design to hold the objects. You can read an existing file and add to it, or create a new Design. The new_project() function creates a new STEP-NC design with a project and empty main workplan. You can also use the normal Python constructor to create a completely empty Design with a name that will be the default value for the filename when it is saved.
D = step.new_project(step_proj) print(D) ==> <step.Design 'step_proj' containing 24 entities> # create some data step.save_project(step_proj.stpnc)
You can also use the normal Python constructor to create a completely empty Design.
D = step.Design(step_new) print(D) ==> <step.Design 'step_new' containing 0 entities>
Next, you can create STEP data objects, by passing in the Design that will own the object plus the EXPRESS ENTITY type name. EXPRESS selects and aggregates are generally second-class objects that are created automatically by assigning to an entity attribute.
The example below creates a cartesian point object and populates its name and coordinates list. Newly created objects have a zero entity_id() value until they are saved to a file.
OBJ = step.Object(D,cartesian_point) OBJ.name =my pointOBJ.coordinates = [ 1, 2, 3 ] print(dict(OBJ)) ==>{ 'coordinates': <step.Object ListOfdouble size 3>, 'name': 'my point' }
You can create STEP AND/OR complex instances by passing a sequence of ENTITY names instead of a single one. This is occasionally used in STEP data sets for things like NURBS curves/surfaces.
NURBS = step.Object(D, (rational_b_spline_curve,b_spline_curve_with_knots)) print (nurb curve:, NURBS) print (dict(NURBS)) ==> nurb curve: <step.Object #0 b_spline_curve_with_knots_and_rational_b_spline_curve> {'closed_curve': False, 'control_points_list': None, 'curve_form': None, 'degree': 0, 'knot_multiplicities': None, 'knot_spec': None, 'knots': None, 'name': None, 'self_intersect': False, 'weights_data': None}
Aggregate attributes support direct assignment and will
automatically append when assigning one beyond the size. They also
support the standard Python append()
, extend()
,
insert()
, pop()
, and
clear()
functions.
OBJ = step.Object(D,cartesian_point) OBJ.coordinates = [ ] OBJ.coordinates[0] = 1 OBJ.coordinates[1] = 2 OBJ.coordinates[2] = 3 print (*OBJ.coordinates) ==> 1.0 2.0 3.0 OBJ.coordinates.append(4) print (*OBJ.coordinates) ==> 1.0 2.0 3.0 4.0 OBJ.coordinates.extend([5,6]) print (*OBJ.coordinates) ==> 1.0 2.0 3.0 4.0 5.0 6.0 OBJ.coordinates.insert(3, 7.0) print (*OBJ.coordinates) ==> 1.0 2.0 3.0 7.0 4.0 5.0 6.0 print(OBJ.coordinates.pop(3)) print (*OBJ.coordinates) ==> 7.0 ==> 1.0 2.0 3.0 4.0 5.0 6.0 print(OBJ.coordinates.pop()) print (*OBJ.coordinates) ==> 6.0 ==> 1.0 2.0 3.0 4.0 5.0 OBJ.coordinates.clear() print (*OBJ.coordinates) ==>
You can assign an ENTITY object directly to an EXPRESS SELECT attribute, but a primitive or aggregate value must be wrapped in a tuple with the EXPRESS type name in the same way that it is returned by the get behavior.
OBJ = step.Object(D,measure_with_unit) OBJ.value_component = ( 5,positive_length_measure) print(dict(OBJ)) ==> {'value_component': (5.0, 'positive_length_measure'), 'unit_component': None}
Making STEP ARM Concepts
Newly made step.Object
are only treated as low-level
STEP AIM instances. To work with the higher-level ARM concepts, you
can create instances of step.ArmObject
. This will create
the root AIM instance, any other supporting objects, and make sure
that it is recognized as the appropriate ARM concept so that the ARM
properties are available. After creating the ArmObject, you can get
its root()
and use it as normal.
The code below just creates a product definition object. The result has no other ARM properties:
# low level AIM create OBJ = step.Object(D,product_definition) print(basic instance, OBJ) pprint(dict(OBJ)) ==> basic instance <step.Object #0 product_definition> {'description': None, 'formation': None, 'frame_of_reference': None, 'id': None}
The code below creates a complete Workpiece ARM concept. The root object is still a product definition, but it is recognized as the root of a Workpiece and has many additional ARM properties available:
# high level ARM create OBJ = step.ArmObject(D,WORKPIECE).root() print(part of ARM concept, OBJ) pprint(dict(OBJ)) ==> part of ARM concept <step.Object ARM WORKPIECE #0 product_definition> {'clamping_positions': <step.ArmCollection size 0>, 'description': '', 'formation': <step.Object #0 product_definition_formation>, 'frame_of_reference': <step.Object #0 product_definition_context>, 'global_tolerance': None, 'id': '', 'its_approvals': <step.ArmCollection size 0>, 'its_bounding_geometry': None, 'its_categories': <step.ArmCollection size 0>, 'its_components': <step.ArmCollection size 0>, 'its_constructive_models': <step.ArmCollection size 0>, 'its_datestamps': <step.ArmCollection size 0>, 'its_geometry': None, 'its_id': None, 'its_material': None, 'its_orgs': <step.ArmCollection size 0>, 'its_people': <step.ArmCollection size 0>, 'its_rawpiece': None, 'its_related_geometry': <step.ArmCollection size 0>, 'its_security_classification': <step.ArmCollection size 0>, 'its_styled_models': <step.ArmCollection size 0>, 'its_timestamps': <step.ArmCollection size 0>, 'product_approvals': <step.ArmCollection size 0>, 'product_datestamps': <step.ArmCollection size 0>, 'product_orgs': <step.ArmCollection size 0>, 'product_people': <step.ArmCollection size 0>, 'product_timestamps': <step.ArmCollection size 0>, 'revision_approvals': <step.ArmCollection size 0>, 'revision_datestamps': <step.ArmCollection size 0>, 'revision_id': '', 'revision_orgs': <step.ArmCollection size 0>, 'revision_people': <step.ArmCollection size 0>, 'revision_security_classification': <step.ArmCollection size 0>, 'revision_timestamps': <step.ArmCollection size 0>, 'security_classification': <step.ArmCollection size 0>, 'shape_definition': None}
ARM properties can be assigned in the same way as AIM attributes, but behind the scenes a chain of AIM objects may be created and linked together to represent the property.
As with AIM aggregates, ARM properties that are collections can be
assigned by a Python sequence and will automatically append when
assigning one beyond the size. They also support the standard
Python append()
, extend()
, and
clear()
functions.
# Set ARM property OBJ.its_geometry = step.Object(D,shape_representation) print (OBJ.its_geometry) ==> <step.Object #0 shape_representation> # Set ARM collection property OBJ.its_categories = [foo,bar] OBJ.its_categories += [baz] print (*OBJ.its_categories) ==> foo bar baz
Common STEP Objects
The following EXPRESS types and ARM concepts are a good starting
place for understanding the contents of a STEP file. The sections
below show an example of the repr()
you might see from
printing an object with print(OBJ)
, plus the contents of
the dict(OBJ)
of the object.
Find ARM ConceptFind EXPRESS Entity
ARM Workpiece
The root of an ARM Workpiece is a product_definition instance. This is the backbone of a STEP file and describes the individual products in an assembly.
Shape is largest and most complex property of a workpiece and is
typically found through the its_geometry
and its_related_geometry
ARM attributes. Other ARM properties
give information about material and administrative information like
approvals and signoffs.
<step.Object ARM WORKPIECE #21 product_definition> {'clamping_positions': <step.ArmCollection size 0>, 'description': ' ', 'formation': <step.Object #20 product_definition_formation_with_specified_source>, 'frame_of_reference': <step.Object #3 product_definition_context>, 'global_tolerance': None, 'id': '7AJS9999-0001A_FOR_NC', 'its_approvals': <step.ArmCollection size 0>, 'its_bounding_geometry': None, 'its_categories': <step.ArmCollection size 1>, 'its_components': <step.ArmCollection size 0>, 'its_constructive_models': <step.ArmCollection size 1>, 'its_datestamps': <step.ArmCollection size 0>, 'its_geometry': <step.Object #16 shape_representation>, 'its_id': '7AJS9999-0001A_FOR_NC', 'its_material': None, 'its_orgs': <step.ArmCollection size 0>, 'its_people': <step.ArmCollection size 0>, 'its_rawpiece': None, 'its_related_geometry': <step.ArmCollection size 0>, 'its_security_classification': <step.ArmCollection size 0>, 'its_styled_models': <step.ArmCollection size 0>, 'its_timestamps': <step.ArmCollection size 0>, 'product_approvals': <step.ArmCollection size 0>, 'product_datestamps': <step.ArmCollection size 0>, 'product_orgs': <step.ArmCollection size 0>, 'product_people': <step.ArmCollection size 0>, 'product_timestamps': <step.ArmCollection size 0>, 'revision_approvals': <step.ArmCollection size 0>, 'revision_datestamps': <step.ArmCollection size 0>, 'revision_id': '', 'revision_orgs': <step.ArmCollection size 0>, 'revision_people': <step.ArmCollection size 0>, 'revision_security_classification': <step.ArmCollection size 0>, 'revision_timestamps': <step.ArmCollection size 0>, 'security_classification': <step.ArmCollection size 0>, 'shape_definition': <step.Object #22 product_definition_shape>}
ARM Geometric_tolerance
The root of an ARM Geometric_tolerance is a geometric_tolerance instance. There are subtypes in both ARM and EXPRESS for position, flatness, surface profile, and many other kinds of tolerance. The actual EXPRESS instance can get rather complex as seen in the example below. The ToleranceAPI has a variety of high-level functions for working with tolerances.
<step.Object ARM SURFACE_PROFILE_TOLERANCE_WITH_DATUM #5449 geometric_tolerance_with_datum_reference_and_surface_profile_tolerance> {'applied_to': <step.Object ARM COMPOSITE_CALLOUT #5417 all_around_shape_aspect>, 'associated_draughting': <step.ArmCollection size 1>, 'datum_system': <step.Object SetOfdatum_system_or_reference size 1>, 'description': '', 'id': None, 'magnitude': <step.Object #5447 MEASURE 0.5 mm>, 'name': 'Position surface profile.2', 'qualifying_note': '', 'reference_datum': <step.ArmCollection size 0>, 'related_tolerances': <step.ArmCollection size 0>, 'significant_digits': None, 'system_datum': <step.Object ARM DATUM_SYSTEM #5448 datum_system>, 'tolerance_value': <step.Object #5447 MEASURE 0.5 mm>, 'toleranced_shape_aspect': <step.Object ARM COMPOSITE_CALLOUT #5417 all_around_shape_aspect>} <step.Object ARM POSITION_TOLERANCE_WITH_DATUM #1554 geometric_tolerance_with_datum_reference_and_geometric_tolerance_with_modifiers_and_position_tolerance> {'affected_plane': None, 'applied_to': <step.Object ARM CENTER_OF_SYMMETRY_CALLOUT #1533 centre_of_symmetry>, 'associated_draughting': <step.ArmCollection size 1>, 'datum_system': <step.Object SetOfdatum_system_or_reference size 1>, 'description': '', 'id': None, 'magnitude': <step.Object ARM QUALIFIED_PLUS_MINUS_VALUE #1552 length_measure_with_unit_and_measure_representation_item_and_qualified_representation_item>, 'modifiers': <step.Object SetOfgeometric_tolerance_modifier size 1>, 'name': 'Position.1', 'qualifying_note': '', 'reference_datum': <step.ArmCollection size 0>, 'related_tolerances': <step.ArmCollection size 0>, 'significant_digits': None, 'system_datum': <step.Object ARM DATUM_SYSTEM #1438 datum_system>, 'tolerance_value': <step.Object ARM QUALIFIED_PLUS_MINUS_VALUE #1552 length_measure_with_unit_and_measure_representation_item_and_qualified_representation_item>, 'toleranced_shape_aspect': <step.Object ARM CENTER_OF_SYMMETRY_CALLOUT #1533 centre_of_symmetry>}
ARM Material
The root of an ARM
material is
a material_designation
instance. It generally appears as the its_material
property of
a workpiece.
<step.Object ARM MATERIAL #17348 material_designation> {'definitions': <step.Object SetOfcharacterized_definition size 1>, 'material_identifier': 'EN AW-7075 (3.4365 or AlZn5,5MgCu)', 'material_property': <step.ArmCollection size 0>, 'name': 'EN AW-7075 (3.4365 or AlZn5,5MgCu)', 'standard_identifier': 'ASM'}
ARM Project
The root of an ARM Project is a product_definition instance. Every STEP-NC program contains one project, and the "main_workplan" gives the starting point for the manufacturing process. The AptAPI.get_current_project() function is an easy way to find this.
<step.Object ARM PROJECT #10 product_definition> {'description': '', 'formation': <step.Object #12 product_definition_formation>, 'frame_of_reference': <step.Object #16 product_definition_context>, 'id': '', 'its_id': 'imts_ashtray_v1', 'its_manufacturer': None, 'its_manufacturer_organization': None, 'its_owner': None, 'its_owner_organization': None, 'its_release': None, 'its_security_classification': <step.ArmCollection size 0>, 'its_status': None, 'its_workpieces': <step.ArmCollection size 1>, 'main_workplan': <step.Object ARM WORKPLAN #19 machining_workplan>}
ARM Workplan and Workingstep
The ARM Workplan and Workingstep concepts are the two most commonly used subtypes of the ARM Executable concept, which describes the control flow in an STEP-NC process. The root is a machining_process_executable instance, or subtypes machining_workplan and machining_workingstep.
The Adaptive class is the easiest way
to walk through a STEP-NC process.
The as_is_geometry
, to_be_geometry
,
and removal_geometry
properties describe the shape at that
location in the process.
<step.Object ARM WORKPLAN #19 machining_workplan> {'as_is_geometry': <step.Object ARM WORKPIECE #2054 product_definition>, 'consequence': '', 'description': '', 'enabled': None, 'fixture_geometry': None, 'its_channel': None, 'its_elements': <step.ArmCollection size 1>, 'its_id': 'Aerospace', 'its_minimum_machine_params': None, 'its_security_classification': <step.ArmCollection size 0>, 'its_setup': <step.Object ARM SETUP #6267 product_definition_formation>, 'machine_used': None, 'name': 'Aerospace', 'planning_operation': None, 'process_properties': <step.ArmCollection size 0>, 'purpose': '', 'removal_geometry': None, 'to_be_geometry': <step.Object ARM WORKPIECE #1648 product_definition>, 'toolpath_orientation': None, 'twin_end': None, 'twin_exception': None, 'twin_plan': None, 'twin_source': None, 'twin_start': None, 'twin_worktime': None} <step.Object ARM MACHINING_WORKINGSTEP #905 machining_workingstep> {'as_is_geometry': None, 'consequence': '', 'description': 'machining', 'enabled': None, 'final_features': <step.ArmCollection size 0>, 'fixture_geometry': None, 'its_feature': <step.Object ARM TOOLPATH_FEATURE #994 instanced_feature>, 'its_id': '2D 90deg arcs and lines WS 2', 'its_operation': <step.Object ARM FREEFORM_OPERATION #863 freeform_milling_operation>, 'its_secplane': None, 'its_secplane_rep': None, 'its_security_classification': <step.ArmCollection size 0>, 'machine_used': None, 'name': '2D 90deg arcs and lines WS 2', 'process_properties': <step.ArmCollection size 0>, 'purpose': '', 'removal_geometry': None, 'to_be_geometry': None, 'toolpath_orientation': None, 'twin_end': None, 'twin_exception': None, 'twin_plan': None, 'twin_source': None, 'twin_start': None, 'twin_worktime': None}