mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Initial attempt at a qto module in Ifc5D
This is a more flexible, non-Blender approach to doing QTO which allows: - Doing QTO without Blender - Doing QTO using query filters - Using config files for QTOs - Choosing your calculation engine, either built-in or creating your own
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "IFC4 Base Quantities",
|
||||
"description": "This ruleset quantifies every single possible standardised base quantity in IFC4",
|
||||
"calculators": {
|
||||
"IOSTriangulation": {
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"Length": "net_get_x",
|
||||
"Width": "net_get_y",
|
||||
"Height": "net_get_z",
|
||||
"NetSideArea": "net_get_side_area",
|
||||
"NetVolume": "net_get_volume"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
# Ifc5D - IFC costing utility
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Ifc5D.
|
||||
#
|
||||
# Ifc5D is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ifc5D is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Ifc5D. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.selector
|
||||
import multiprocessing
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def get_rules(name: str):
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(os.path.join(cwd, name + ".json"), "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], rules: dict):
|
||||
results = {}
|
||||
for calculator, queries in rules["calculators"].items():
|
||||
calculator = calculators[calculator]
|
||||
for query, qtos in queries.items():
|
||||
filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements)
|
||||
calculator.calculate(ifc_file, filtered_elements, qtos, results)
|
||||
return results
|
||||
|
||||
|
||||
def edit_qtos(ifc_file, results):
|
||||
for element, qtos in results.items():
|
||||
for name, quantities in qtos.items():
|
||||
qto = ifcopenshell.util.element.get_pset(element, name, should_inherit=False)
|
||||
if qto:
|
||||
qto = ifc_file.by_id(qto["id"])
|
||||
else:
|
||||
qto = ifcopenshell.api.pset.add_qto(ifc_file, element, name)
|
||||
ifcopenshell.api.pset.edit_qto(ifc_file, qto=qto, properties=quantities)
|
||||
|
||||
|
||||
class IOSTriangulation:
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict,
|
||||
results: Optional[dict] = None,
|
||||
):
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.shape
|
||||
|
||||
if results is None:
|
||||
results = {}
|
||||
|
||||
formula_functions = {}
|
||||
|
||||
gross_settings = ifcopenshell.geom.settings()
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
net_settings = ifcopenshell.geom.settings()
|
||||
|
||||
gross_qtos = {}
|
||||
net_qtos = {}
|
||||
|
||||
for name, quantities in qtos.items():
|
||||
for quantity, formula in quantities.items():
|
||||
if formula.startswith("gross_"):
|
||||
formula = formula[6:]
|
||||
gross_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
elif formula.startswith("net_"):
|
||||
formula = formula[4:]
|
||||
net_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
|
||||
tasks = []
|
||||
|
||||
if gross_qtos:
|
||||
tasks.append((IOSTriangulation.create_iterator(ifc_file, gross_settings, elements), gross_qtos))
|
||||
|
||||
if net_qtos:
|
||||
tasks.append((IOSTriangulation.create_iterator(ifc_file, net_settings, elements), net_qtos))
|
||||
|
||||
for iterator, qtos in tasks:
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
shape = iterator.get()
|
||||
element = ifc_file.by_id(shape.id)
|
||||
results.setdefault(element, {})
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
for quantity, formula in quantities.items():
|
||||
results[element][name][quantity] = formula_functions[formula](shape.geometry)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def create_iterator(ifc_file, settings, elements):
|
||||
return ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
|
||||
|
||||
class Blender:
|
||||
@staticmethod
|
||||
def calculate(ifc_file, elements, qtos):
|
||||
import blenderbim.tool as tool
|
||||
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj:
|
||||
continue
|
||||
|
||||
for name, quantities in qtos.items():
|
||||
for quantity, formula in quantities.items():
|
||||
getattr(tool.Qto, formula)
|
||||
# TODO
|
||||
|
||||
|
||||
calculators = {"Blender": Blender, "IOSTriangulation": IOSTriangulation}
|
||||
Reference in New Issue
Block a user