mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Quantification to consider Pset_ProfileMechanical.MassPerLength #6344
This commit is contained in:
@@ -24,6 +24,7 @@ import bonsai.tool as tool
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import ifc5d.qto
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils.bvhtree import BVHTree
|
||||
from shapely.geometry import Polygon
|
||||
@@ -565,7 +566,7 @@ def has_openings(obj: bpy.types.Object) -> list[ifcopenshell.entity_instance]:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return []
|
||||
return [o for o in tool.Geometry.get_openings(element)]
|
||||
return [o for o in ifcopenshell.util.element.get_openings(element)]
|
||||
|
||||
|
||||
def get_obj_decompositions(obj: bpy.types.Object) -> set[ifcopenshell.entity_instance]:
|
||||
@@ -576,20 +577,42 @@ def get_obj_decompositions(obj: bpy.types.Object) -> set[ifcopenshell.entity_ins
|
||||
|
||||
|
||||
def get_gross_weight(obj: bpy.types.Object) -> Union[float, None]:
|
||||
"""Get gross weight of the object (based on gross volume and Pset_MaterialCommon.MassDensity)"""
|
||||
"""Get gross weight of the object.
|
||||
|
||||
Based on gross volume and Pset_MaterialCommon.MassDensity
|
||||
or Pset_ProfileMechanical.MassPerLength and extrusion depth if it's profile based.
|
||||
"""
|
||||
|
||||
weight = get_profile_obj_weight(obj)
|
||||
if weight is not None:
|
||||
return weight
|
||||
|
||||
obj_mass_density = get_obj_mass_density(obj)
|
||||
if not obj_mass_density:
|
||||
return
|
||||
|
||||
gross_volume = get_gross_volume(obj)
|
||||
gross_weight = obj_mass_density * gross_volume
|
||||
return gross_weight
|
||||
|
||||
|
||||
def get_net_weight(obj: bpy.types.Object) -> Union[float, None]:
|
||||
"""Get net weight of the object (based on net volume and Pset_MaterialCommon.MassDensity)"""
|
||||
"""Get net weight of the object.
|
||||
|
||||
Based on Pset_ProfileMechanical.MassPerLength and extrusion depth
|
||||
(for profile based objects, though objects with openings are not supported)
|
||||
or object's net volume and Pset_MaterialCommon.MassDensity.
|
||||
"""
|
||||
|
||||
if not has_openings(obj):
|
||||
weight = get_profile_obj_weight(obj)
|
||||
if weight is not None:
|
||||
return weight
|
||||
|
||||
obj_mass_density = get_obj_mass_density(obj)
|
||||
if not obj_mass_density:
|
||||
return
|
||||
|
||||
net_volume = get_net_volume(obj)
|
||||
net_weight = obj_mass_density * net_volume
|
||||
return net_weight
|
||||
@@ -602,6 +625,13 @@ def get_obj_mass_density(obj: bpy.types.Object) -> Union[float, None]:
|
||||
return ifcopenshell.util.element.get_element_mass_density(entity)
|
||||
|
||||
|
||||
def get_profile_obj_weight(obj: bpy.types.Object) -> Union[float, None]:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
weight = ifc5d.qto.IfcOpenShell.get_weight_profile_based(element)
|
||||
return weight
|
||||
|
||||
|
||||
def get_opening_type(opening: bpy.types.Object, obj: bpy.types.Object) -> Literal["OPENING", "RECESS"]:
|
||||
"""_summary_: Returns the opening type - OPENING / RECESS
|
||||
|
||||
|
||||
@@ -1660,15 +1660,13 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
Use `.RelatedOpeningElement` to get the opening element.
|
||||
"""
|
||||
for element_rel in getattr(element, "HasOpenings", ()):
|
||||
yield element_rel
|
||||
|
||||
if aggregate := ifcopenshell.util.element.get_aggregate(element):
|
||||
yield from cls.get_openings(aggregate)
|
||||
# TODO: replace everywhere with util method.
|
||||
return ifcopenshell.util.element.get_openings(element)
|
||||
|
||||
@classmethod
|
||||
def has_openings(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return bool(next(cls.get_openings(element), False))
|
||||
# TODO: replace everywhere with util method.
|
||||
return ifcopenshell.util.element.has_openings(element)
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_representation(
|
||||
|
||||
+41
-3
@@ -238,7 +238,9 @@ class IfcOpenShell(QtoCalculator):
|
||||
"get_weight": Function(
|
||||
"IfcMassMeasure",
|
||||
"Weight",
|
||||
"The weight of the object based on it's volume and material density (from Pset_MaterialCommon.MassDensity).",
|
||||
"The weight of the object based on it's length and Pset_ProfileMechanical.MassPerLength "
|
||||
"(for profile based objects, though objects with openings are not supported for net calculations)"
|
||||
"or it's volume and material density (from Pset_MaterialCommon.MassDensity).",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -308,7 +310,8 @@ class IfcOpenShell(QtoCalculator):
|
||||
if value is None:
|
||||
continue
|
||||
elif formula == "get_weight":
|
||||
value = cls.get_weight(element, geometry)
|
||||
calculation_type = "GROSS" if iterator.settings is cls.gross_settings else "NET"
|
||||
value = cls.get_weight(element, geometry, calculation_type)
|
||||
if value is None:
|
||||
continue
|
||||
else:
|
||||
@@ -367,7 +370,10 @@ class IfcOpenShell(QtoCalculator):
|
||||
|
||||
@classmethod
|
||||
def get_weight(
|
||||
cls, element: ifcopenshell.entity_instance, geometry: ifcopenshell.geom.ShapeType
|
||||
cls,
|
||||
element: ifcopenshell.entity_instance,
|
||||
geometry: ifcopenshell.geom.ShapeType,
|
||||
calculation_type: Literal["GROSS", "NET"],
|
||||
) -> Union[float, None]:
|
||||
"""Get element's weight.
|
||||
|
||||
@@ -376,12 +382,44 @@ class IfcOpenShell(QtoCalculator):
|
||||
or ``None`` if mass density calculation for this element is not supported.
|
||||
"""
|
||||
|
||||
if calculation_type == "gross" or not ifcopenshell.util.element.has_openings(element):
|
||||
weight = cls.get_weight_profile_based(element)
|
||||
if weight is not None:
|
||||
return weight
|
||||
|
||||
density = ifcopenshell.util.element.get_element_mass_density(element)
|
||||
if density is None:
|
||||
return
|
||||
volume = ifcopenshell.util.shape.get_volume(geometry)
|
||||
return volume * density
|
||||
|
||||
@classmethod
|
||||
def get_weight_profile_based(cls, element: ifcopenshell.entity_instance) -> Union[float, None]:
|
||||
"""Get weight of the profile based element.
|
||||
|
||||
:return: A float weight value if calculation was successful
|
||||
or ``None`` if it's either not profile based object
|
||||
or it's not supported.
|
||||
"""
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return None
|
||||
items = representation.Items
|
||||
if not all(item.is_a("IfcExtrudedAreaSolid") for item in items):
|
||||
return None
|
||||
mass = 0.0
|
||||
for item in items:
|
||||
profile = item.SweptArea
|
||||
# TODO: there are also bunch of other similar props we will need to consider in the future.
|
||||
# Examples:
|
||||
# - Pset_CableSegmentTypeBusBarSegment.MassPerLength
|
||||
# - Pset_CableCarrierSegmentTypeCatenaryWire.MassPerLength
|
||||
mass_per_length = ifcopenshell.util.element.get_pset(profile, "Pset_ProfileMechanical", "MassPerLength")
|
||||
if not isinstance(mass_per_length, float):
|
||||
return None
|
||||
mass += mass_per_length * item.Depth
|
||||
return mass
|
||||
|
||||
|
||||
class Blender(QtoCalculator):
|
||||
"""Calculates geometry based on currently loaded Blender objects."""
|
||||
|
||||
@@ -20,7 +20,7 @@ import ifcopenshell
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
from typing import Any, Callable, Optional, Union, Literal, overload, Sequence
|
||||
from typing import Any, Callable, Optional, Union, Literal, overload, Sequence, Generator
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
@@ -1720,3 +1720,27 @@ def has_property(product: ifcopenshell.entity_instance, property_name: str) -> b
|
||||
return True
|
||||
qtos = get_psets(product, qtos_only=True)
|
||||
return any(property_name in quantities.keys() for quantities in qtos.values())
|
||||
|
||||
|
||||
def get_openings(element: ifcopenshell.entity_instance) -> Generator[ifcopenshell.entity_instance, None, None]:
|
||||
"""Get element openings as IfcRelVoidsElements.
|
||||
|
||||
Use `.RelatedOpeningElement` to get the opening element.
|
||||
|
||||
:param element: IfcElement.
|
||||
:return: Generator of IfcRelVoidsElements.
|
||||
"""
|
||||
for element_rel in getattr(element, "HasOpenings", ()):
|
||||
yield element_rel
|
||||
|
||||
if aggregate := get_aggregate(element):
|
||||
yield from get_openings(aggregate)
|
||||
|
||||
|
||||
def has_openings(element: ifcopenshell.entity_instance) -> bool:
|
||||
"""Check if the element has openings.
|
||||
|
||||
:param element: IfcElement.
|
||||
:return: True if element has openings.
|
||||
"""
|
||||
return bool(next(get_openings(element), False))
|
||||
|
||||
Reference in New Issue
Block a user