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)
- 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)
+6 -9
View File
@@ -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"])
@@ -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):