Sheet metal feature iteration
Sheet metal shops are a fantastic fit for our platform because of the formulaic nature of sheet metal costing. We provide values like cut length, punch hit count, hole setups, thickness, etc. that allow for effective formulaic high level pricing. Now with feature iteration, you can use the information stored within each bend, feature, and manufacturability feedback instance to calculate runtimes, prices, part complexity, etc.
In this article, we will break down each feature and feedback type extracted from our sheet metal interrogation, as well as provide examples of how to use them in your pricing formulas. The cheat sheet below contains all the high level information you will need to get started. For more specifics on what each feature and feedback instance represents, check out our sheet metal interrogation article.
Cheat Sheet
sheet_metal = analyze_sheet_metal() # request interrogation for bend in get_features(sheet_metal, name='bend'): # iterate thru bends # the below four lines demonstrate how to access bend information bend.properties.angle bend.properties.radius bend.properties.length bend.properties.k_factor if is_close(bend.properties.angle, 90): # check bend angle using is_close helper do_something = 1 else: do_something_else = 1 # iterate through other forming features for offset in get_features(sheet_metal, name='offset'): # has same properties as bend, but also offset height offset.properties.offset_height do_something_with_offsets = 1 for open_hem in get_features(sheet_metal, name='open_hem'): do_something_with_hems = 1 for feature in get_features(sheet_metal): # iterate thru features feature.name feature.properties if feature.name == 'countersink': # access information with dot notation into properties feature.properties.depth feature.properties.major_diameter do_something_with_countersink = 1 for feedback in get_feedback(sheet_metal): # iterate thru feedback feedback.name feedback.level feedback.properties if feedback.name == 'cut_near_bend': # access information with dot notation into properties feedback.properties.dist feedback.properties.distance_ratio do_something_with_cut_near_bend = 1 # get count of feature type by using len() and specifying name num_countersinks = len(get_features(sheet_metal, name='countersink')) # get count of feedback type by using len() and specifying name num_cut_near_bend = len(get_feedback(sheet_metal, name='cut_near_bend')) # interact with features and feedback directly from dot operator, returning copies of p3l list objects # can manipulate these with p3l list functions and methods features = sheet_metal.features.filter(lambda x: x.name == 'bend').filter(lambda x: x.properties.length > 10).sort(lambda x: x.properties.length) for bend in iterate(features): do_something = 1 for feedback in iterate(sheet_metal.feedback): do_something = 1
Feature Reference
Name | Properties |
bend | area, angle, length, k_factor, radius |
bend_relief | area, width, length |
curl | area, angle, length, k_factor, radius |
counterbore | area, depth, diameter, volume |
countersink | area, depth, major_diameter, minor_diameter, semi_angle, volume |
cutout_feature | area, cut_length, requires_piercing Note: We currently do not count the external cut as requiring a pierce. |
drilled_hole | area, depth, diameter, volume |
embossment | area, depth |
etching | edge_length |
offset | area, angle, length, offset_height, k_factor, radius |
open_hem | area, angle, length, k_factor, radius |
punch_multi_hit_feature | area, cut_length |
circular_punch | cut_length, radius |
obround_punch | cut_length, side_length, side_width, radius |
rectangular_punch | cut_length, side_length, side_width |
slot_punch | cut_length, side_length, radius |
square_punch | cut_length, side_length |
tear_drop | area, angle, length, k_factor, radius |
Feedback Reference
Name | Level | Properties (short description) |
captive_part | complete_exclusion | n/a |
close_countersink_bores | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
close_cutouts | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
countersink_bore_to_edge | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
curl_near_bend | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
cut_near_bend | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
cutout_to_edge | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
different_bend_directions | cost_driver | n/a |
exceeds_press_length | complete_exclusion | n/a |
hem_near_bend | manufacturing_issue | dist, distance_ratio (traversal/thickness) |
large_bend_radius | manufacturing_issue | ratio (radius/thickness) |
machining_required | manufacturing_issue | n/a |
neg_bend_allowance | complete_exclusion | radius, thickness |
no_bend_relief | manufacturing_issue | n/a |
no_corner_relief | manufacturing_issue | n/a |
non_standard_bend_angle | cost_driver | angle |
shallow_bend_relief | manufacturing_issue | ratio ((depth-radius)/thickness) |
sheet_metal_large_part | complete_exclusion | length, width, height, max_length, max_width, max_height |
short_bend | manufacturing_issue | ratio (length/radius) |
short_hem_return | manufacturing_issue | ratio (distance/thickness) |
small_bend_radius | manufacturing_issue | ratio (radius/thickness) |
small_cut | manufacturing_issue | dist (size of cut) |
small_radius | manufacturing_issue | radius |
split_bend | cost_driver | stroke_length |
tight_corner | manufacturing_issue | angle |
tight_curl | manufacturing_issue | ratio (radius/thickness) |
tight_hem | manufacturing_issue | ratio (diameter/thickness |
thin_bend_relief | manufacturing_issue | ratio (width/thickness) |
unfolded_sheet_metal_large_part | complete_exclusion | length, width, max_length, max_width |
unfolding_issue | complete_exclusion | n/a |
Examples
Basic
Below describes a way to add price for the appearance of certain types of features and feedback on a part. This operation could serve as a supplement to your standard marking and forming operations to encapsulate added costs for specific features. Checkout our custom interrogations article to find out how to customize your manufacturability feedback outputs.
units_in() sheet_metal = analyze_sheet_metal() close_cutout_cost = var('Close cutout cost', 0.25, '$ per close cutout found', currency) cut_near_bend_cost = var('Cut near bend cost', 2.50, '$ per cut near bend found', currency) curl_cost = var('Curl Cost', 5.0, '$ per curl found', currency) hem_cost = var('Hem Cost', 5.0, '$ per hem found', currency) total_close_cutout_cost = len(get_feedback(sheet_metal, name='close_cutouts')) * close_cutout_cost total_cut_near_bend_cost = len(get_feedback(sheet_metal, name='cut_near_bend')) * cut_near_bend_cost total_curl_cost = len(get_features(sheet_metal, name='curl')) * curl_cost hem_count = len(get_features(sheet_metal, name='open_hem')) + len(get_features(sheet_metal, name='tear_drop')) total_hem_cost = hem_count * hem_cost PRICE = (total_close_cutout_cost + total_cut_near_bend_cost + total_curl_cost + total_hem_cost) * part.qty DAYS = 0
Advanced
Below describes an advanced method to cost out a forming operation based on the characteristics of bends in the part. The operation below will start each bend with a base setup time and runtime, and will then increment these times based on added geometric complexity, such as non-90 degree angle bends, long bends, or large radius bends. This uses a lot of list operations. Check out the article on P3L Lists for more info.
units_in() sheet_metal = analyze_sheet_metal() setup_time = var('setup_time', 0, 'Setup time in hours', number, frozen=False) runtime = var('runtime', 0, 'Runtime in hours', number, frozen=False) # establish base setup time and time increments in minutes base_bend_setup_time = 5 st_increment = 2.5 # establish base runtime and time increments in seconds base_bend_runtime = 30 rt_increment = 10 # establish geometric thresholds for incrementing times # in inches medium_radius_thresh = 2.0 large_radius_thresh = 4.0 medium_length_thresh = 20.0 large_length_thresh = 40.0 # establish our variables that we will use as pricing levers in the operation # also establish their local tracking variables base_bends = sheet_metal.features.filter(lambda x: x.name == 'bend') base_bend_count = var('Bend Count', 0, 'Number of bends detected', number, frozen=False) base_bend_count.update(len(base_bends)) base_bend_count.freeze() non_ninety_bends = var('Non-90 Bend Count', 0, 'Number of non-90 degree bends', number, frozen=False) non_ninety_bends.update(len( base_bends.copy().filter(lambda x: not is_close(x.properties.angle, 90)) )) non_ninety_bends.freeze() med_rads = var('Medium Rads', 0, 'Number of bends with medium rads', number, frozen=False) med_rads.update(len( base_bends.copy().filter( lambda x: medium_radius_thresh <= x.properties.radius < large_radius_thresh or is_close(x.properties.radius, medium_radius_thresh) ) )) med_rads.freeze() large_rads = var('Large Rads', 0, 'Number of bends with large rads', number, frozen=False) large_rads.update(len( base_bends.copy().filter( lambda x: x.properties.radius >= large_radius_thresh or is_close(x.properties.radius, large_radius_thresh) ) )) large_rads.freeze() med_lengths = var('Medium Lengths', 0, 'Number of bends with medium length', number, frozen=False) med_lengths.update(len( base_bends.copy().filter( lambda x: medium_length_thresh <= x.properties.length < large_length_thresh or is_close(x.properties.length, medium_length_thresh)) )) med_lengths.freeze() large_lengths = var('Large Lengths', 0, 'Number of bends with large length', number, frozen=False) large_lengths.update(len( base_bends.copy().filter( lambda x: x.properties.length >= large_length_thresh or is_close(x.properties.length, large_length_thresh)) )) large_lengths.freeze() # now collect contributions total_bend_setup = base_bend_setup_time * base_bend_setup_time total_bend_run = base_bend_count * base_bend_runtime total_bend_setup += non_ninety_bends * st_increment total_bend_run += non_ninety_bends * rt_increment total_bend_setup += med_rads * st_increment total_bend_run += med_rads * rt_increment total_bend_setup += large_rads * 2 * st_increment total_bend_run += large_rads * 2 * rt_increment total_bend_setup += med_lengths * st_increment total_bend_run += med_lengths * rt_increment total_bend_setup += large_lengths * 2 * st_increment total_bend_run += large_lengths * 2 * rt_increment # establish final times as a sum of all above # convert times to hours setup_time.update(total_bend_setup / 60) setup_time.freeze() runtime.update(total_bend_run / 3600) runtime.freeze() setup_labor_rate = var('Setup Labor Rate', 50, 'Labor cost per hour to set up work center', currency) run_labor_rate = var('Run Labor Rate', 25, 'Labor cost per hour to attend work center machine', currency) setup_cost = setup_time * setup_labor_rate run_cost = runtime * run_labor_rate * part.qty PRICE = setup_cost + run_cost DAYS = 0