diff --git a/src/ifc5d/Ex8 - BoQ with formula.csv b/src/ifc5d/Ex8 - BoQ with formula.csv new file mode 100644 index 0000000000..a3e8d77b60 --- /dev/null +++ b/src/ifc5d/Ex8 - BoQ with formula.csv @@ -0,0 +1,14 @@ +Index,Identification,Name,Unit,Value,Quantity,Query,Property,Formula +1,E.01,Walls,m3,,,,, +2,E.01.01,Ground floor walls,m3,100,,"IfcWall, location=""Ground Floor""",GrossVolume, +2,E.01.02,First floor walls,m3,200,,"IfcWall, location=""First Floor""",GrossVolume, +1,A.02,Paintings,m2,,,,, +2,A.03,Paintings with water,m2,,,,, +3,B.05,White paintings,m2,25,,IfcWall,GrossVolume, +3,B.06,Colored paintings,m2,32,33,,, +3,B.07,Double paintings,m,,,IfcWall,,NetSideArea*2 +2,C-01,Paintings with machine,m2,17,133,,, +2,C-02,Decorated paintings,m2,40,8,,, +1,D,Reinforcements,,,,,, +2,D.1,Walls reinforcements weight,kg,,,IfcWall,,Pset_ConcreteElementGeneral.ReinforcementVolumeRatio * GrossVolume +2,D.2,Beams reinforcements weight,kg,,,,, diff --git a/src/ifc5d/README.md b/src/ifc5d/README.md index ba37664a88..0c57785236 100644 --- a/src/ifc5d/README.md +++ b/src/ifc5d/README.md @@ -39,6 +39,7 @@ See example files as a CSV file format reference: - Ex5 - SoR_with_description.csv (a simple SoR with description column) - Ex6 - BoQ with categories.csv (a simple BoQ with categories columns) - Ex7 - BoQ with Rates.csv (a simple BoQ that connect to an existing SoR. It needs an already loaded SoR.) +- Ex8 - Boq with formula.csv (a simple BoQ with formula field used to calculate quantities when specified) - `sample_cost_schedule_house_FR.csv` / `.ods` - `schedule.csv`, `rates.csv` (schedule of rates example) diff --git a/src/ifc5d/ifc5d/csv2ifc.py b/src/ifc5d/ifc5d/csv2ifc.py index aaab6c7ab1..d43eb907e9 100644 --- a/src/ifc5d/ifc5d/csv2ifc.py +++ b/src/ifc5d/ifc5d/csv2ifc.py @@ -412,16 +412,13 @@ class Csv2Ifc: results = ifcopenshell.util.selector.filter_elements(self.file, cost_item["Query"]) results = [r for r in results] ifc_quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["Unit"]) - try: - quantity = ifcopenshell.api.cost.assign_cost_item_quantity( - self.file, - cost_item=cost_item["ifc"], - products=results, - formula=cost_item["Formula"], - ifc_class=ifc_quantity_class, + quantity = ifcopenshell.api.cost.assign_cost_item_quantity( + self.file, + cost_item=cost_item["ifc"], + products=results, + formula=cost_item["Formula"], + ifc_class=ifc_quantity_class, ) - except: - quantity=0 self.create_cost_items(cost_item["children"], cost_item["ifc"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index 7a57372e8b..6eb752aef2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -23,6 +23,7 @@ from typing import Any import ifcopenshell.api.control import ifcopenshell.api.cost +import ifcopenshell.util.element def assign_cost_item_quantity( @@ -142,15 +143,18 @@ class Usecase: variables_modified[v] = self.get_value_from_pset(product, v, separator) else: variables_modified[v] = self.get_value_from_qset(product, v) + if variables_modified[v] == 0: + print(f"WARNING: {product.Name} hasn't the variable {v.replace(separator, '.')}") formula_modified = self.settings["formula"].replace(".", separator) if any(v is None for v in variables_modified.values()): for k, v in variables_modified.items(): if v == None: print(f"Property {k.replace(separator, '.')} not found") - raise ValueError("Formula contains variables that are not found in properties. Please check the output") - - result = eval(formula_modified, {}, variables_modified) + print("Formula contains variables that are not found in Psets or Qsets. Please check the output") + result = 0 + else: + result = eval(formula_modified, {}, variables_modified) new_quantity = None for quantity in self.quantities: @@ -231,26 +235,19 @@ class Usecase: v: str, separator: str, ) -> float: - for relationship in product.IsDefinedBy: - if relationship.is_a("IfcRelDefinesByProperties"): - pset = relationship.RelatingPropertyDefinition - if pset.Name == v.split(separator)[0]: - for prop in pset.HasProperties: - if prop.Name.lower() == v.split(separator)[1].lower(): - return prop[2].wrappedValue + pset_name = v.split(separator)[0] + pset = ifcopenshell.util.element.get_pset(product, pset_name) + pset_property_name = v.split(separator)[1] + return (pset or {}).get(pset_property_name,0) def get_value_from_qset( self, product:ifcopenshell.entity_instance, v: str, ) -> float: - for relationship in product.IsDefinedBy: - if relationship.is_a("IfcRelDefinesByProperties"): - qto = relationship.RelatingPropertyDefinition - if qto.is_a("IfcElementQuantity"): - for prop in qto.Quantities: - if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == v.lower(): - return prop[3] + qtos = ifcopenshell.util.element.get_psets(product, qtos_only = True) + quantities = next(iter(qtos.values()), {}) + return (quantities or {}).get(v,0) class VariableExtractor(ast.NodeVisitor): def __init__(self):