First Commit

This commit is contained in:
2026-05-31 10:17:09 +07:00
commit 17a9c69379
4547 changed files with 1170384 additions and 0 deletions
@@ -0,0 +1,48 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
"""Constraints are an advanced feature allowing you to specify parametric
limits on properties
Warning: usage of constraints are mostly untested in real life applications.
"""
from .. import wrap_usecases
from .add_metric import add_metric
from .add_metric_reference import add_metric_reference
from .add_objective import add_objective
from .assign_constraint import assign_constraint
from .edit_metric import edit_metric
from .edit_objective import edit_objective
from .remove_constraint import remove_constraint
from .remove_metric import remove_metric
from .unassign_constraint import unassign_constraint
wrap_usecases(__path__, __name__)
__all__ = [
"add_metric",
"add_metric_reference",
"add_objective",
"assign_constraint",
"edit_metric",
"edit_objective",
"remove_constraint",
"remove_metric",
"unassign_constraint",
]
@@ -0,0 +1,54 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Add a new metric benchmark
Qualitative constraints may have a series of quantitative benchmarks
linked to it known as metrics. Metrics may be parametrically linked to
computed model properties or quantities. Metrics need to be satisfied
to meet the objective of the constraint.
:param objective: The IfcObjective that this metric is a benchmark of.
:return: The newly created IfcMetric entity
Example:
.. code:: python
objective = ifcopenshell.api.constraint.add_objective(model)
metric = ifcopenshell.api.constraint.add_metric(model,
objective=objective)
"""
metric = file.create_entity(
"IfcMetric",
**{
"Name": "Unnamed",
"ConstraintGrade": "NOTDEFINED",
"Benchmark": "EQUALTO",
},
)
if objective:
benchmark_values: list[ifcopenshell.entity_instance]
benchmark_values = list(objective.BenchmarkValues or [])
benchmark_values.append(metric)
objective.BenchmarkValues = benchmark_values
return metric
@@ -0,0 +1,43 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_metric_reference(
file: ifcopenshell.file, metric: ifcopenshell.entity_instance, reference_path: str
) -> list[ifcopenshell.entity_instance]:
"""
Adds a chain of references to a metric. The reference path is a string of the form "attribute.attribute.attribute"
Used to reference a value of an attribute of an instance through a metric objective entity.
"""
references_created = []
if reference_path:
attributes = reference_path.split(".")
for i in range(len(attributes)):
if i == 0:
reference = file.create_entity("IfcReference")
reference.AttributeIdentifier = attributes[i]
metric.ReferencePath = reference
references_created.append(reference)
else:
reference = file.create_entity("IfcReference")
reference.AttributeIdentifier = attributes[i]
references_created[i - 1].InnerReference = reference
references_created.append(reference)
return references_created
@@ -0,0 +1,46 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
"""Add a new objective constraint
Parametric constraints may be defined by the user. The constraint is defined
by first creating an objective describing the purpose of the constraint and
whether it is a hard or soft constraint. Later on, metrics may be added to
check whether the constraint has been met by connecting it to properties and
quantities. See ifcopenshell.api.constraint.add_metric for more information.
:return: The newly created IfcObjective entity
Example:
.. code:: python
# Create a new objective for code compliance requirements
objective = ifcopenshell.api.constraint.add_objective(model)
objective.ConstraintGrade = "ADVISORY"
objective.ObjectiveQualifier = "CODECOMPLIANCE"
# Note: the objective right now is purely qualitative and for
# information purposes. You may wish to add quantiative metrics.
"""
return file.create_entity(
"IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
)
@@ -0,0 +1,92 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.guid
def assign_constraint(
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance,
) -> Union[ifcopenshell.entity_instance, None]:
"""Assigns a constraint to a list of products
This assigns a relationship between a product and a constraint, so that
when a product's properties and quantities do not match the requirements
of the constraint's metrics, results can be flagged.
It is assumed (but not explicit in the IFC documentation) that
constraints are inherited from the type. This way, it is not necessary
to create lots of constraint assignments.
:param products: The list of products the constraint applies to. This is anything
which can have properties or quantities.
:param constraint: The IfcObjective constraint
:return: The new or updated IfcRelAssociatesConstraint relationship
or `None` if `products` was an empty list.
"""
usecase = Usecase()
usecase.file = file
return usecase.execute(products, constraint)
class Usecase:
file: ifcopenshell.file
def execute(self, products: list[ifcopenshell.entity_instance], constraint: ifcopenshell.entity_instance):
if not products:
return
products_set = set(products)
rels = self.get_constraint_rels(constraint)
related_objects = set()
for rel in rels:
related_objects.update(rel.RelatedObjects)
products_to_assign = products_set - related_objects
if not products_to_assign:
return rels[0]
rel = next(iter(rels), None)
if rel:
related_objects = set(rel.RelatedObjects) | products_to_assign
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
return rel
return self.file.create_entity(
"IfcRelAssociatesConstraint",
**{
"GlobalId": ifcopenshell.guid.new(),
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(self.file),
"RelatingConstraint": constraint,
"RelatedObjects": list(products_to_assign),
}
)
def get_constraint_rels(self, constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
rels = []
for rel in self.file.get_inverse(constraint):
if rel.is_a("IfcRelAssociatesConstraint"):
rels.append(rel)
return rels
@@ -0,0 +1,44 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
import ifcopenshell
def edit_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edit the attributes of a metric
For more information about the attributes and data types of an
IfcMetric, consult the IFC documentation.
:param metric: The IfcMetric you want to edit.
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
objective = ifcopenshell.api.constraint.add_objective(model)
metric = ifcopenshell.api.constraint.add_metric(model,
objective=objective)
ifcopenshell.api.constraint.edit_metric(model,
metric=metric, attributes={"ConstraintGrade": "HARD"})
"""
for name, value in attributes.items():
setattr(metric, name, value)
@@ -0,0 +1,44 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Any
import ifcopenshell
def edit_objective(
file: ifcopenshell.file, objective: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edit the attributes of a objective
For more information about the attributes and data types of an
IfcObjective, consult the IFC documentation.
:param objective: The IfcObjective you want to edit.
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
objective = ifcopenshell.api.constraint.add_objective(model)
ifcopenshell.api.constraint.edit_objective(model,
objective=objective, attributes={"ConstraintGrade": "HARD"})
"""
for name, value in attributes.items():
setattr(objective, name, value)
@@ -0,0 +1,48 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
def remove_constraint(file: ifcopenshell.file, constraint: ifcopenshell.entity_instance) -> None:
"""Remove a constraint (typically an objective)
Removes a constraint definition and all of its associations to any
products. Typically this would be an IfcObjective, although technically
you can associate IfcMetrics ith products too, though the meaning may be
unclear.
:param constraint: The IfcObjective you want to remove.
:return: None
Example:
.. code:: python
objective = ifcopenshell.api.constraint.add_objective(model)
ifcopenshell.api.constraint.remove_constraint(model,
constraint=objective)
"""
file.remove(constraint)
for rel in file.by_type("IfcRelAssociatesConstraint"):
if not rel.RelatingConstraint:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -0,0 +1,68 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.util.element
def remove_metric(file: ifcopenshell.file, metric: ifcopenshell.entity_instance) -> None:
"""Remove a metric benchmark
Removes a metric benchmark and all of its associations to any products
and objectives.
:param metric: The IfcMetric you want to remove.
:return: None
Example:
.. code:: python
objective = ifcopenshell.api.constraint.add_objective(model)
metric = ifcopenshell.api.constraint.add_metric(model,
objective=objective)
ifcopenshell.api.constraint.remove_metric(model,
metric=metric)
"""
usecase = Usecase()
usecase.file = file
return usecase.execute(metric)
class Usecase:
file: ifcopenshell.file
def execute(self, metric: ifcopenshell.entity_instance) -> None:
if metric.ReferencePath:
reference = metric.ReferencePath
self.delete_reference(reference)
self.file.remove(metric)
for rel in self.file.by_type("IfcRelAssociatesConstraint"):
if not rel.RelatingConstraint:
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
for resource_rel in self.file.by_type("IfcResourceConstraintRelationship"):
if not resource_rel.RelatingConstraint:
self.file.remove(resource_rel)
def delete_reference(self, reference: ifcopenshell.entity_instance) -> None:
if reference.InnerReference:
self.delete_reference(reference.InnerReference)
self.file.remove(reference)
@@ -0,0 +1,79 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell 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.
#
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api.owner
import ifcopenshell.util.element
def unassign_constraint(
file: ifcopenshell.file,
products: list[ifcopenshell.entity_instance],
constraint: ifcopenshell.entity_instance,
) -> None:
"""Unassigns a constraint from a list of products
The constraint will not be deleted and is available to be assigned to
other products.
:param products: The list of products the constraint applies to.
:param constraint: The IfcObjective constraint
:return: None
"""
usecase = Usecase()
usecase.file = file
return usecase.execute(products, constraint)
class Usecase:
file: ifcopenshell.file
def execute(self, products_: list[ifcopenshell.entity_instance], constraint: ifcopenshell.entity_instance):
if not products_:
return
products_set = set(products_)
rels = self.get_constraint_rels(constraint)
related_objects = set()
for rel in rels:
related_objects.update(rel.RelatedObjects)
if not related_objects.intersection(products_set):
return
for rel in rels:
related_objects = set(rel.RelatedObjects)
if not related_objects.intersection(products_set):
continue
related_objects -= products_set
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(self.file, element=rel)
continue
history = rel.OwnerHistory
self.file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
def get_constraint_rels(self, cosntraint: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
rels = []
for rel in self.file.get_inverse(cosntraint):
if rel.is_a("IfcRelAssociatesConstraint"):
rels.append(rel)
return rels