-
-
Save Moult/d25e4fe1c295d5fdafe1c3b2f1adeab1 to your computer and use it in GitHub Desktop.
import ifcopenshell | |
import ifcopenshell.api | |
import ifcopenshell.geom | |
import ifcopenshell.util.unit | |
import ifcopenshell.util.shape | |
class QtoFix: | |
def execute(self): | |
self.file = ifcopenshell.open("model.ifc") | |
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) | |
gross_settings = ifcopenshell.geom.settings() | |
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True) | |
net_settings = ifcopenshell.geom.settings() | |
for element in self.file.by_type("IfcDoor"): | |
ifcopenshell.api.run("root.remove_product", self.file, product=element) | |
for element in self.file.by_type("IfcWall"): | |
self.quantities = {} | |
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) | |
qto = qtos["Qto_WallBaseQuantities"] | |
gross_shape = ifcopenshell.geom.create_shape(gross_settings, element) | |
net_shape = ifcopenshell.geom.create_shape(net_settings, element) | |
length = ifcopenshell.util.shape.get_x(gross_shape.geometry) / unit_scale | |
width = ifcopenshell.util.shape.get_y(gross_shape.geometry) / unit_scale | |
height = ifcopenshell.util.shape.get_z(gross_shape.geometry) / unit_scale | |
gross_side_area = ifcopenshell.util.shape.get_side_area(gross_shape.geometry) | |
net_volume = ifcopenshell.util.shape.get_volume(net_shape.geometry) | |
self.verify_quantity(element, qto, "Length", length) | |
self.verify_quantity(element, qto, "Width", width) | |
self.verify_quantity(element, qto, "Height", height) | |
self.verify_quantity(element, qto, "GrossSideArea", gross_side_area) | |
self.verify_quantity(element, qto, "NetVolume", net_volume) | |
if self.quantities: | |
ifcopenshell.api.run("pset.edit_qto", self.file, qto=self.file.by_id(qto["id"]), properties=self.quantities) | |
for element in self.file.by_type("IfcSlab"): | |
self.quantities = {} | |
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) | |
qto = qtos["Qto_SlabBaseQuantities"] | |
gross_shape = ifcopenshell.geom.create_shape(gross_settings, element) | |
length = ifcopenshell.util.shape.get_x(gross_shape.geometry) / unit_scale | |
width = ifcopenshell.util.shape.get_y(gross_shape.geometry) / unit_scale | |
depth = ifcopenshell.util.shape.get_z(gross_shape.geometry) / unit_scale | |
perimeter = ifcopenshell.util.shape.get_footprint_perimeter(gross_shape.geometry) / unit_scale | |
gross_area = ifcopenshell.util.shape.get_footprint_area(gross_shape.geometry) | |
gross_volume = ifcopenshell.util.shape.get_volume(gross_shape.geometry) | |
self.verify_quantity(element, qto, "Length", length) | |
self.verify_quantity(element, qto, "Width", width) | |
self.verify_quantity(element, qto, "Depth", depth) | |
self.verify_quantity(element, qto, "Perimeter", perimeter) | |
self.verify_quantity(element, qto, "GrossArea", gross_area) | |
self.verify_quantity(element, qto, "GrossVolume", gross_volume) | |
if self.quantities: | |
ifcopenshell.api.run("pset.edit_qto", self.file, qto=self.file.by_id(qto["id"]), properties=self.quantities) | |
for element in self.file.by_type("IfcColumn"): | |
self.quantities = {} | |
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) | |
qto = qtos["Qto_ColumnBaseQuantities"] | |
gross_shape = ifcopenshell.geom.create_shape(gross_settings, element) | |
length = ifcopenshell.util.shape.get_z(gross_shape.geometry) / unit_scale | |
outer_surface_area = ifcopenshell.util.shape.get_outer_surface_area(gross_shape.geometry) | |
gross_volume = ifcopenshell.util.shape.get_volume(gross_shape.geometry) | |
self.verify_quantity(element, qto, "Length", length) | |
self.verify_quantity(element, qto, "OuterSurfaceArea", outer_surface_area) | |
self.verify_quantity(element, qto, "GrossVolume", gross_volume) | |
if self.quantities: | |
ifcopenshell.api.run("pset.edit_qto", self.file, qto=self.file.by_id(qto["id"]), properties=self.quantities) | |
for element in self.file.by_type("IfcBeam"): | |
self.quantities = {} | |
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) | |
qto = qtos["Qto_BeamBaseQuantities"] | |
gross_shape = ifcopenshell.geom.create_shape(gross_settings, element) | |
length = ifcopenshell.util.shape.get_z(gross_shape.geometry) / unit_scale | |
outer_surface_area = ifcopenshell.util.shape.get_outer_surface_area(gross_shape.geometry) | |
gross_volume = ifcopenshell.util.shape.get_volume(gross_shape.geometry) | |
self.verify_quantity(element, qto, "Length", length) | |
self.verify_quantity(element, qto, "OuterSurfaceArea", outer_surface_area) | |
self.verify_quantity(element, qto, "GrossVolume", gross_volume) | |
if self.quantities: | |
ifcopenshell.api.run("pset.edit_qto", self.file, qto=self.file.by_id(qto["id"]), properties=self.quantities) | |
for element in self.file.by_type("IfcOpeningElement"): | |
self.quantities = {} | |
qto = self.get_qto(element, "Qto_OpeningElementBaseQuantities") | |
gross_shape = ifcopenshell.geom.create_shape(gross_settings, element) | |
height = ifcopenshell.util.shape.get_x(gross_shape.geometry) / unit_scale | |
width = ifcopenshell.util.shape.get_y(gross_shape.geometry) / unit_scale | |
depth = ifcopenshell.util.shape.get_z(gross_shape.geometry) / unit_scale | |
area = ifcopenshell.util.shape.get_side_area(gross_shape.geometry) | |
volume = ifcopenshell.util.shape.get_volume(gross_shape.geometry) | |
self.verify_quantity(element, qto, "Height", height) | |
self.verify_quantity(element, qto, "Width", width) | |
self.verify_quantity(element, qto, "Depth", depth) | |
self.verify_quantity(element, qto, "Area", area) | |
self.verify_quantity(element, qto, "Volume", volume) | |
if self.quantities: | |
ifcopenshell.api.run("pset.edit_qto", self.file, qto=self.file.by_id(qto["id"]), properties=self.quantities) | |
self.file.write("model-qto.ifc") | |
def get_qto(self, element, name): | |
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True) | |
if name in qtos: | |
return qtos[name] | |
qto = ifcopenshell.api.run("pset.add_qto", self.file, product=element, name=name) | |
return {"id": qto.id()} | |
def verify_quantity(self, element, qto, name, value): | |
if name not in qto: | |
self.quantities[name] = value | |
print(f"Quantity {name} added: {value} for {element}") | |
elif not ifcopenshell.util.shape.is_x(qto[name], value): | |
print(f"Quantity {name} fixed: {qto[name]} -> {value} for {element}") | |
self.quantities[name] = value | |
QtoFix().execute() |
Okei thanks, that may work!
Just so I don't misunderstand, it is more correct to work further with the lengths provided by the get_z (or x) functions, instead of the pset function?
I am also struggling to find the profiles of the elements, do you know how I can find them?
Also, if I wish to have the cross section areas, can I just calculate this by multiplying the width and height (the y and z in the output over)? (This of course will only work if the profile is a rectangle)
Many questions at once, but I am a little lost. Thank you again!
Using the shape util created shapes will get you exactly what the geometry is, which may or may not be more correct than what already exists in the existing qtos. In my opinion, it is almost always more correct because the qto method is known as opposed to a black box. But your project may be special and have special calculation rules.
The cross section area may be calculated either by using get_side_area and specifying the axis of the side you want (which you can work out via analysis of the get_x/y/z function you used to get the length axis), which is a generic way to get it for any tessellated shape. Otherwise, you can (and should) first check for the representation type, which you'd use the util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") to get the body representation, resolve mapped representations using util.representation.resolve_representation then if it is a representationtype of sweptsolid, you can check the extrusion profile then use get_area.
Hi, great code!
This code works to find the lengths for some of my IFC files, but for others the length may be found as get_x instead of get_z. Do you have a suggestion as to what this may be due and if it is a way to fix it so the code works generically?
For example is you see the output below, the length of the beam corresponds to x and not to z for the specific file:
psets BEAM: {'BaseQuantities': {'Length': 105.000000001222, 'OuterSurfaceArea': 0.00621171657618607, 'NetVolume': 2.10148755905985e-05, 'NetWeight': 0.164966773386198, 'id': 2900}}
x 104.99999726451682
y: 23.9999999999709
z: 23.9999999991269
Thank you!