See #6570. Formula column minor improvements and documentation

This commit is contained in:
Massimo Fabbro
2026-01-11 18:06:43 +01:00
committed by Massimo Fabbro
parent 528964ca56
commit f0b5ab860f
4 changed files with 35 additions and 26 deletions
+14
View File
@@ -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,,,,,
1 Index Identification Name Unit Value Quantity Query Property Formula
2 1 E.01 Walls m3
3 2 E.01.01 Ground floor walls m3 100 IfcWall, location="Ground Floor" GrossVolume
4 2 E.01.02 First floor walls m3 200 IfcWall, location="First Floor" GrossVolume
5 1 A.02 Paintings m2
6 2 A.03 Paintings with water m2
7 3 B.05 White paintings m2 25 IfcWall GrossVolume
8 3 B.06 Colored paintings m2 32 33
9 3 B.07 Double paintings m IfcWall NetSideArea*2
10 2 C-01 Paintings with machine m2 17 133
11 2 C-02 Decorated paintings m2 40 8
12 1 D Reinforcements
13 2 D.1 Walls reinforcements weight kg IfcWall Pset_ConcreteElementGeneral.ReinforcementVolumeRatio * GrossVolume
14 2 D.2 Beams reinforcements weight kg
+1
View File
@@ -39,6 +39,7 @@ See example files as a CSV file format reference:
- Ex5 - SoR_with_description.csv (a simple SoR with description column) - Ex5 - SoR_with_description.csv (a simple SoR with description column)
- Ex6 - BoQ with categories.csv (a simple BoQ with categories columns) - 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.) - 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` - `sample_cost_schedule_house_FR.csv` / `.ods`
- `schedule.csv`, `rates.csv` (schedule of rates example) - `schedule.csv`, `rates.csv` (schedule of rates example)
+6 -9
View File
@@ -412,16 +412,13 @@ class Csv2Ifc:
results = ifcopenshell.util.selector.filter_elements(self.file, cost_item["Query"]) results = ifcopenshell.util.selector.filter_elements(self.file, cost_item["Query"])
results = [r for r in results] results = [r for r in results]
ifc_quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["Unit"]) ifc_quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["Unit"])
try: quantity = ifcopenshell.api.cost.assign_cost_item_quantity(
quantity = ifcopenshell.api.cost.assign_cost_item_quantity( self.file,
self.file, cost_item=cost_item["ifc"],
cost_item=cost_item["ifc"], products=results,
products=results, formula=cost_item["Formula"],
formula=cost_item["Formula"], ifc_class=ifc_quantity_class,
ifc_class=ifc_quantity_class,
) )
except:
quantity=0
self.create_cost_items(cost_item["children"], cost_item["ifc"]) self.create_cost_items(cost_item["children"], cost_item["ifc"])
@@ -23,6 +23,7 @@ from typing import Any
import ifcopenshell.api.control import ifcopenshell.api.control
import ifcopenshell.api.cost import ifcopenshell.api.cost
import ifcopenshell.util.element
def assign_cost_item_quantity( def assign_cost_item_quantity(
@@ -142,15 +143,18 @@ class Usecase:
variables_modified[v] = self.get_value_from_pset(product, v, separator) variables_modified[v] = self.get_value_from_pset(product, v, separator)
else: else:
variables_modified[v] = self.get_value_from_qset(product, v) 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) formula_modified = self.settings["formula"].replace(".", separator)
if any(v is None for v in variables_modified.values()): if any(v is None for v in variables_modified.values()):
for k, v in variables_modified.items(): for k, v in variables_modified.items():
if v == None: if v == None:
print(f"Property {k.replace(separator, '.')} not found") print(f"Property {k.replace(separator, '.')} not found")
raise ValueError("Formula contains variables that are not found in properties. Please check the output") print("Formula contains variables that are not found in Psets or Qsets. Please check the output")
result = 0
result = eval(formula_modified, {}, variables_modified) else:
result = eval(formula_modified, {}, variables_modified)
new_quantity = None new_quantity = None
for quantity in self.quantities: for quantity in self.quantities:
@@ -231,26 +235,19 @@ class Usecase:
v: str, v: str,
separator: str, separator: str,
) -> float: ) -> float:
for relationship in product.IsDefinedBy: pset_name = v.split(separator)[0]
if relationship.is_a("IfcRelDefinesByProperties"): pset = ifcopenshell.util.element.get_pset(product, pset_name)
pset = relationship.RelatingPropertyDefinition pset_property_name = v.split(separator)[1]
if pset.Name == v.split(separator)[0]: return (pset or {}).get(pset_property_name,0)
for prop in pset.HasProperties:
if prop.Name.lower() == v.split(separator)[1].lower():
return prop[2].wrappedValue
def get_value_from_qset( def get_value_from_qset(
self, self,
product:ifcopenshell.entity_instance, product:ifcopenshell.entity_instance,
v: str, v: str,
) -> float: ) -> float:
for relationship in product.IsDefinedBy: qtos = ifcopenshell.util.element.get_psets(product, qtos_only = True)
if relationship.is_a("IfcRelDefinesByProperties"): quantities = next(iter(qtos.values()), {})
qto = relationship.RelatingPropertyDefinition return (quantities or {}).get(v,0)
if qto.is_a("IfcElementQuantity"):
for prop in qto.Quantities:
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == v.lower():
return prop[3]
class VariableExtractor(ast.NodeVisitor): class VariableExtractor(ast.NodeVisitor):
def __init__(self): def __init__(self):