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,47 @@
# 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/>.
"""Manage references to external libraries
An external library is any system which uses a key to store information. This
allows you to associate IFC entities with any arbitrary external database, API,
system, and so on. This is typically useful in smart building systems.
"""
from .. import wrap_usecases
from .add_library import add_library
from .add_reference import add_reference
from .assign_reference import assign_reference
from .edit_library import edit_library
from .edit_reference import edit_reference
from .remove_library import remove_library
from .remove_reference import remove_reference
from .unassign_reference import unassign_reference
wrap_usecases(__path__, __name__)
__all__ = [
"add_library",
"add_reference",
"assign_reference",
"edit_library",
"edit_reference",
"remove_library",
"remove_reference",
"unassign_reference",
]
@@ -0,0 +1,59 @@
# 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_library(file: ifcopenshell.file, name: str) -> ifcopenshell.entity_instance:
"""Adds a new library to the project
A library is an external data source that is related to the project. It
may be a database, a spreadsheet, an API, or even a stack of papers in a
filing cabinet. This allows IFC data to store relationships to these
external data sources.
For example, you may have a list of laser scans of a site stored in an
online platform, which can be queried using an API. Or, you might have a
database of live building sensor data. So long as there is a clear
identifier you can use to link the two datasets together, you can create
a relationship.
Note that IFC does not store any instructions on how to access the
library. It does not specify whether a HTTP request or database
connection needs to be made or what protocol the library operates with.
Until this is fleshed out further, it is the users responsibility to
name the libraries consistently and use appropriate identifiers. For
example, if you are linking IFC data and Brickschema data, use a full
URI for the identifier with no abbreviation (e.g.
'http://example.org/digitaltwin#AHU01', not 'digitaltwin:AHU01').
A library will then contain a list of references within that library.
These references will then be related to IFC elements. For example, a
library will represent an external database, and a reference will point
to a particular table and row within that database.
:param name: The name of the library
:return: The newly created IfcLibraryInformation
Example:
.. code:: python
ifcopenshell.api.library.add_library(model, name="Brickschema")
"""
return file.create_entity("IfcLibraryInformation", Name=name)
@@ -0,0 +1,57 @@
# 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_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Adds a new reference to a library
A library represents an external data source, such as a database,
spreadsheet, API, or something else that contains information related to
the IFC project. Within a library, there will be one or more references,
such as reference to a particular table or row in a database, or a sheet
and row or column in a spreadsheet, a URI in a linked data Brickschema
file, 32-bit decimal BACnetObjectIdentifier in a BACnet system, IP
address in a network, and so on.
These references can then be related to IFC elements. You cannot relate
an IFC element directly to a library, it must be related to one of the
library's references.
:param library: The IfcLibraryInformation element to add a reference to
:return: The newly created IfcLibraryReference element
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.library.add_reference(model, library=library)
ifcopenshell.api.library.edit_reference(model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
if file.schema == "IFC2X3":
reference = file.createIfcLibraryReference()
references = list(library.LibraryReference or [])
references.append(reference)
library.LibraryReference = references
return reference
return file.createIfcLibraryReference(ReferencedLibrary=library)
@@ -0,0 +1,90 @@
# 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
import ifcopenshell.util.element
def assign_reference(
file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, None]:
"""Associates a list products with a library reference
A product may be associated with zero, one, or many references across
multiple libraries. See ifcopenshell.api.library.add_reference for more
detail about how references work.
:param products: The list of IfcProducts you want to associate with the reference
:param reference: The IfcLibraryReference you want the product to be
associated with.
:return: The IfcRelAssociatesLibrary relationship entity
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.library.add_reference(model, library=library)
ifcopenshell.api.library.edit_reference(model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.root.create_entity(model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.library.assign_reference(model, reference=reference, products=[ahu])
"""
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
referenced_elements = ifcopenshell.util.element.get_referenced_elements(reference)
products_set: set[ifcopenshell.entity_instance] = set(products)
products_set = products_set - referenced_elements
if not products_set:
return
if file.schema == "IFC2X3":
rel = next(
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == reference),
None,
)
else:
rel = next(iter(reference.LibraryRefForObjects), None)
if not rel:
return file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
RelatedObjects=list(products_set),
RelatingLibrary=reference,
)
related_objects = set(rel.RelatedObjects) | products_set
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
return rel
@@ -0,0 +1,56 @@
# 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 datetime
from typing import Any
import ifcopenshell
import ifcopenshell.util.date
def edit_library(file: ifcopenshell.file, library: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
"""Edits the attributes of an IfcLibraryInformation
For more information about the attributes and data types of an
IfcLibraryInformation, consult the IFC documentation.
:param library: The IfcLibraryInformation entity you want to edit
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
ifcopenshell.api.library.edit_library(model, library=library,
attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."})
"""
if "VersionDate" in attributes:
dt = attributes["VersionDate"]
if isinstance(dt, datetime.datetime):
if file.schema != "IFC2X3":
dt = ifcopenshell.util.date.datetime2ifc(dt, "IfcDateTime")
else:
calendar_date = ifcopenshell.util.date.datetime2ifc(dt, "IfcCalendarDate")
dt = file.create_entity("IfcCalendarDate", **calendar_date)
attributes = attributes.copy()
attributes["VersionDate"] = dt
for name, value in attributes.items():
setattr(library, name, value)
@@ -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/>.
from typing import Any
import ifcopenshell
def edit_reference(
file: ifcopenshell.file, reference: ifcopenshell.entity_instance, attributes: dict[str, Any]
) -> None:
"""Edits the attributes of an IfcLibraryReference
For more information about the attributes and data types of an
IfcLibraryReference, consult the IFC documentation.
:param reference: The IfcLibraryReference entity you want to edit
:param attributes: a dictionary of attribute names and values.
:return: None
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.library.add_reference(model, library=library)
ifcopenshell.api.library.edit_reference(model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
"""
for name, value in attributes.items():
setattr(reference, name, value)
@@ -0,0 +1,58 @@
# 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_library(file: ifcopenshell.file, library: ifcopenshell.entity_instance) -> None:
"""Removes a library
All references along with their relationships will also be removed. Any
products which have relationships to this library will not be removed.
:param library: The IfcLibraryInformation entity you want to remove
:return: None
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
ifcopenshell.api.library.remove_library(model, library=library)
"""
if file.schema != "IFC2X3":
rels = []
for reference in set(library.HasLibraryReferences):
rels.extend(reference.LibraryRefForObjects)
file.remove(reference)
rels.extend(library.LibraryInfoForObjects)
file.remove(library)
else:
for reference in set(library.LibraryReference or []):
file.remove(reference)
file.remove(library)
# RelatingLibrary could either be library itself or library reference we removed
rels = [rel for rel in file.by_type("IfcRelAssociatesLibrary") if rel.RelatingLibrary is None]
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -0,0 +1,51 @@
# 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_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None:
"""Removes a library reference
Any products which have relationships to this reference will not be
removed.
:param reference: The IfcLibraryReference entity you want to remove
:return: None
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
reference = ifcopenshell.api.library.add_reference(model, library=library)
# Let's change our mind and remove it.
ifcopenshell.api.library.remove_reference(model, reference=reference)
"""
if file.schema != "IFC2X3":
rels = reference.LibraryRefForObjects
else:
rels = [rel for rel in file.by_type("IfcRelAssociatesLibrary") if rel.RelatingLibrary == reference]
for rel in rels:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
file.remove(reference)
@@ -0,0 +1,78 @@
# 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_reference(
file: ifcopenshell.file,
reference: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
) -> None:
"""Unassigns a product of products from a reference
If the product isn't assigned to the reference, nothing will happen.
:param reference: The IfcLibraryReference to unassign from
:param products: A list of IfcProduct elements to unassign from the reference
:return: None
Example:
.. code:: python
library = ifcopenshell.api.library.add_library(model, name="Brickschema")
# Let's create a reference to a single AHU in our Brickschema dataset
reference = ifcopenshell.api.library.add_reference(model, library=library)
ifcopenshell.api.library.edit_reference(model,
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
# Let's assume we have an AHU in our model.
ahu = ifcopenshell.api.root.create_entity(model,
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.library.assign_reference(model, reference=reference, products=[ahu])
# Let's change our mind and unassign it.
ifcopenshell.api.library.unassign_reference(model, reference=reference, products=[ahu])
"""
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
reference_rels: set[ifcopenshell.entity_instance] = set()
products_set = set(products)
for product in products_set:
reference_rels.update(product.HasAssociations)
reference_rels = {
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == reference
}
for rel in reference_rels:
related_objects = set(rel.RelatedObjects) - products_set
if related_objects:
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.owner.update_owner_history(file, element=rel)
else:
history = rel.OwnerHistory
file.remove(rel)
if history:
ifcopenshell.util.element.remove_deep2(file, history)