SolidCode/SolidPython

Variable price in BOM

lyndametref opened this issue · 3 comments

I am using SolidPython to design a piece of furniture made from plates of wood of different sizes.
It would be nice to have a list of all the pieces of wood I will need to order to fulfill the project and have an estimate price.
As those wood sheet price are given by square meter, I would like to know if it is possible to calculate the price of a part as function of its argument? Is something like that possible?

@bom_part("15mm walnut laminate", 0.0002*width*length, currency="€")
def wood_sheet(width,length)
    return linear_extrude(15)(square([width,length]))

This would have to create a "new part" for each dimension, I have in the project and calculate a price for it.

Hi Lynda - That's a good question. The answer (for now) is that the BOM decorator only counts units of something, not rates like length, area, or volume, which would all be useful for linear lumber, sheet materials, or 3D printing pricing. I can imagine some changes to the @bom_part() function that would enable these, but I think it could be pretty finicky to make something that's generally applicable. If you're buying sheet materials for instance, you probably buy in units of one sheet at a time, so the shape of what's being created might determine the depending on how many copies you can fit into a single sheet of material more than the actual area. There are some similar issues with linear or volume-based costing that seem pretty thorny to me -- do you have any ideas about how that feature could work in general?

For your particular purpose, I think you might try either:
A) defining separate functions for each separate piece you're modeling, e.g.

L_PANEL_W = 30
L_PANEL_H = 75
T_PANEL_W = 30
T_PANEL_H = 18

@bom_part('Left side panel, 15mm walnut laminate', 0.0002*L_PANEL_H*L_PANEL_W, currency="€")
def left_side_panel():
    return wood_sheet(L_PANEL_W, L_PANEL_H)

@bom_part('Top panel, 15mm walnut laminate', 0.0002*T_PANEL_H*T_PANEL_W, currency="€")
def top_panel():
    return wood_sheet(T_PANEL_W, T_PANEL_H)

You would then get a BOM which contained several different line items for the same walnut paneling, but which might allow you to optimize how your cuts and patterning go for buying sheet goods.

Or B) Just return area from wood_sheet() and keep a running total of area as well as your SolidPython objects:

@bom_part("15mm walnut laminate", 0.0002*width*length, currency="€")
def wood_sheet(width,length)
    obj =  linear_extrude(15)(square([width,length]))
    area = width* length
    return obj, area

total_area = 0
part_a, area_a = wood_sheet(width_a, height_a)
total_area += area_a
part_b, area_b = wood_sheet(width_b, height_b)
total_area += area_b

Hi Evan!

Thanks for your answer. In the end I have used the following approach: Defining a "subunit" for a material. This is the unit to which the material is priced, in my case m^2 (m for threaded shaft, kg for sand, tiles for tiling floor etc.). Then I have included this subunit calculation in the wood_sheet function and added the resulting item with add_bom_item(part_name: str, subunits_price: float, subunits_count: float = 1). This function adds a line to a csv file which is at the end of the model generation read and consolidated by looking for duplicate, counting and aggregating them (currently I am using pandas to do it, but there is probably a solution with lighter dependencies). Here under are some spinets of my code for it. I had also added some more bells and whistles (look up material in a list, add description), but those are very specific to my application.

import pandas as pd

def init_bom_list() :
    df = pd.DataFrame({'Part': [], 'Subunits': [], 'Price_per_subunit': [], 'Unit_price': []})
    df.to_csv( 'BOM.csv', mode='w', header=True)


def add_bom_item(part_name: str, subunits_price: float, subunits_count: float = 1) :
    df = pd.DataFrame({''Part': [part_name], 'Subunits': [subunits_count], 'Price_per_subunit': [subunits_price], 'Unit_price': [subunits_count * subunits_price]})
    df.to_csv(os.path.join(get_path('rendered_model_path'), 'BOM.csv'), mode='a', header=False)


def consolidate_bom_list():
    df = pd.read_csv('BOM.csv')
    print(df.sort_values(['Provider', 'Description']).reset_index(drop=True).groupby(
        ['Provider', 'Description', 'Part', 'Subunits', 'Price_per_subunit', 'Unit_price'], dropna=False).count())


def wood_sheet(width: float, length: float, thickness: float, subunits_price: float):
    part_name = "wood " + str(width)  + "x" +str(length) 
    subunits_count = width / 1000 * length / 1000
    add_bom_item(part_name, subunits_price=subunits_price, subunits_count=subunits_count)
    return sheet(width, length, thickness) # function returning the scad object with the proper dimensions