From d11ec671290ccdd6c3022925bd817e91c857b8d1 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 6 May 2024 14:35:39 +1000 Subject: [PATCH] Generate functions for all API usecases for better static code features. See #2693. --- .../ifcopenshell/api/aggregate/__init__.py | 3 + .../api/aggregate/assign_object.py | 236 ++++++----- .../api/aggregate/unassign_object.py | 93 +++-- .../ifcopenshell/api/attribute/__init__.py | 2 + .../api/attribute/edit_attributes.py | 95 ++--- .../ifcopenshell/api/boundary/__init__.py | 5 + .../boundary/assign_connection_geometry.py | 134 ++++--- .../api/boundary/copy_boundary.py | 39 +- .../api/boundary/edit_attributes.py | 82 ++-- .../api/boundary/remove_boundary.py | 51 ++- .../api/classification/__init__.py | 7 + .../api/classification/add_classification.py | 125 +++--- .../api/classification/add_reference.py | 222 +++++------ .../api/classification/edit_classification.py | 43 +- .../api/classification/edit_reference.py | 43 +- .../classification/remove_classification.py | 49 +-- .../api/classification/remove_reference.py | 175 ++++----- .../ifcopenshell/api/constraint/__init__.py | 10 + .../ifcopenshell/api/constraint/add_metric.py | 67 ++-- .../api/constraint/add_metric_reference.py | 46 ++- .../api/constraint/add_objective.py | 47 ++- .../api/constraint/assign_constraint.py | 66 ++-- .../api/constraint/edit_metric.py | 45 +-- .../api/constraint/edit_objective.py | 41 +- .../api/constraint/remove_constraint.py | 51 ++- .../api/constraint/remove_metric.py | 51 +-- .../api/constraint/unassign_constraint.py | 50 +-- .../ifcopenshell/api/context/__init__.py | 4 + .../ifcopenshell/api/context/add_context.py | 325 +++++++-------- .../ifcopenshell/api/context/edit_context.py | 51 ++- .../api/context/remove_context.py | 73 ++-- .../ifcopenshell/api/control/__init__.py | 3 + .../api/control/assign_control.py | 142 ++++--- .../api/control/unassign_control.py | 85 ++-- .../ifcopenshell/api/cost/__init__.py | 20 + .../ifcopenshell/api/cost/add_cost_item.py | 83 ++-- .../api/cost/add_cost_item_quantity.py | 113 +++--- .../api/cost/add_cost_schedule.py | 71 ++-- .../ifcopenshell/api/cost/add_cost_value.py | 151 ++++--- .../api/cost/assign_cost_item_quantity.py | 141 ++++--- .../api/cost/assign_cost_value.py | 93 +++-- .../calculate_cost_item_resource_value.py | 163 ++++---- .../ifcopenshell/api/cost/copy_cost_item.py | 59 +-- .../api/cost/copy_cost_item_values.py | 65 ++- .../ifcopenshell/api/cost/edit_cost_item.py | 41 +- .../api/cost/edit_cost_item_quantity.py | 55 ++- .../api/cost/edit_cost_schedule.py | 41 +- .../ifcopenshell/api/cost/edit_cost_value.py | 73 ++-- .../api/cost/edit_cost_value_formula.py | 63 +-- .../ifcopenshell/api/cost/remove_cost_item.py | 71 ++-- .../api/cost/remove_cost_item_quantity.py | 59 ++- .../api/cost/remove_cost_schedule.py | 59 ++- .../api/cost/remove_cost_value.py | 77 ++-- .../api/cost/unassign_cost_item_quantity.py | 101 ++--- .../ifcopenshell/api/document/__init__.py | 9 + .../api/document/add_information.py | 107 +++-- .../api/document/add_reference.py | 95 +++-- .../api/document/assign_document.py | 148 ++++--- .../api/document/edit_information.py | 54 ++- .../api/document/edit_reference.py | 60 ++- .../api/document/remove_information.py | 63 ++- .../api/document/remove_reference.py | 43 +- .../api/document/unassign_document.py | 108 +++-- .../ifcopenshell/api/drawing/__init__.py | 4 + .../api/drawing/assign_product.py | 151 ++++--- .../api/drawing/edit_text_literal.py | 41 +- .../api/drawing/unassign_product.py | 85 ++-- .../ifcopenshell/api/geometry/__init__.py | 27 ++ .../api/geometry/add_axis_representation.py | 119 +++--- .../ifcopenshell/api/geometry/add_boolean.py | 37 +- .../api/geometry/add_door_representation.py | 145 ++++--- .../geometry/add_footprint_representation.py | 29 +- .../api/geometry/add_mesh_representation.py | 39 +- .../geometry/add_profile_representation.py | 33 +- .../geometry/add_railing_representation.py | 67 ++-- .../api/geometry/add_representation.py | 73 ++-- .../api/geometry/add_slab_representation.py | 29 +- .../api/geometry/add_wall_representation.py | 39 +- .../api/geometry/add_window_representation.py | 143 ++++--- .../api/geometry/assign_representation.py | 15 +- .../api/geometry/connect_element.py | 67 ++-- .../ifcopenshell/api/geometry/connect_path.py | 131 +++---- .../api/geometry/create_2pt_wall.py | 37 +- .../api/geometry/disconnect_element.py | 55 ++- .../api/geometry/disconnect_path.py | 63 ++- .../api/geometry/edit_object_placement.py | 36 +- .../api/geometry/map_representation.py | 17 +- .../api/geometry/remove_boolean.py | 15 +- .../api/geometry/remove_representation.py | 101 +++-- .../api/geometry/unassign_representation.py | 15 +- .../ifcopenshell/api/georeference/__init__.py | 4 + .../api/georeference/add_georeferencing.py | 73 ++-- .../api/georeference/edit_georeferencing.py | 143 +++---- .../api/georeference/remove_georeferencing.py | 39 +- .../ifcopenshell/api/grid/__init__.py | 4 + .../api/grid/create_axis_curve.py | 81 ++-- .../ifcopenshell/api/grid/create_grid_axis.py | 113 +++--- .../ifcopenshell/api/grid/remove_grid_axis.py | 51 ++- .../ifcopenshell/api/group/__init__.py | 7 + .../ifcopenshell/api/group/add_group.py | 67 ++-- .../ifcopenshell/api/group/assign_group.py | 87 ++-- .../ifcopenshell/api/group/edit_group.py | 41 +- .../ifcopenshell/api/group/remove_group.py | 85 ++-- .../ifcopenshell/api/group/unassign_group.py | 75 ++-- .../api/group/update_group_products.py | 79 ++-- .../ifcopenshell/api/layer/__init__.py | 6 + .../ifcopenshell/api/layer/add_layer.py | 43 +- .../ifcopenshell/api/layer/assign_layer.py | 101 +++-- .../ifcopenshell/api/layer/edit_layer.py | 41 +- .../ifcopenshell/api/layer/remove_layer.py | 33 +- .../ifcopenshell/api/layer/unassign_layer.py | 105 +++-- .../ifcopenshell/api/library/__init__.py | 9 + .../ifcopenshell/api/library/add_library.py | 69 ++-- .../ifcopenshell/api/library/add_reference.py | 71 ++-- .../api/library/assign_reference.py | 123 +++--- .../ifcopenshell/api/library/edit_library.py | 41 +- .../api/library/edit_reference.py | 45 +-- .../api/library/remove_library.py | 49 ++- .../api/library/remove_reference.py | 47 ++- .../api/library/unassign_reference.py | 102 +++-- .../ifcopenshell/api/material/__init__.py | 25 ++ .../api/material/add_constituent.py | 119 +++--- .../ifcopenshell/api/material/add_layer.py | 117 +++--- .../api/material/add_list_item.py | 107 +++-- .../ifcopenshell/api/material/add_material.py | 97 +++-- .../api/material/add_material_set.py | 159 ++++---- .../ifcopenshell/api/material/add_profile.py | 136 ++++--- .../api/material/assign_material.py | 254 ++++++------ .../api/material/assign_profile.py | 145 +++---- .../api/material/copy_material.py | 67 ++-- .../api/material/edit_assigned_material.py | 41 +- .../api/material/edit_constituent.py | 75 ++-- .../ifcopenshell/api/material/edit_layer.py | 77 ++-- .../api/material/edit_layer_usage.py | 97 +++-- .../api/material/edit_material.py | 15 +- .../ifcopenshell/api/material/edit_profile.py | 115 +++--- .../api/material/edit_profile_usage.py | 147 +++---- .../api/material/remove_constituent.py | 57 ++- .../ifcopenshell/api/material/remove_layer.py | 63 ++- .../api/material/remove_list_item.py | 61 ++- .../api/material/remove_material.py | 91 +++-- .../api/material/remove_material_set.py | 101 +++-- .../api/material/remove_profile.py | 83 ++-- .../api/material/reorder_set_item.py | 85 ++-- .../api/material/unassign_material.py | 71 ++-- .../ifcopenshell/api/nest/__init__.py | 5 + .../ifcopenshell/api/nest/assign_object.py | 260 ++++++------ .../ifcopenshell/api/nest/change_nest.py | 47 ++- .../ifcopenshell/api/nest/reorder_nesting.py | 29 +- .../ifcopenshell/api/nest/unassign_object.py | 89 ++--- .../ifcopenshell/api/owner/__init__.py | 24 ++ .../ifcopenshell/api/owner/add_actor.py | 71 ++-- .../ifcopenshell/api/owner/add_address.py | 87 ++-- .../ifcopenshell/api/owner/add_application.py | 88 +++-- .../api/owner/add_organisation.py | 57 ++- .../ifcopenshell/api/owner/add_person.py | 72 ++-- .../api/owner/add_person_and_organisation.py | 56 ++- .../ifcopenshell/api/owner/add_role.py | 71 ++-- .../ifcopenshell/api/owner/assign_actor.py | 139 ++++--- .../api/owner/create_owner_history.py | 165 ++++---- .../ifcopenshell/api/owner/edit_actor.py | 55 ++- .../ifcopenshell/api/owner/edit_address.py | 61 ++- .../api/owner/edit_organisation.py | 43 +- .../ifcopenshell/api/owner/edit_person.py | 43 +- .../ifcopenshell/api/owner/edit_role.py | 47 ++- .../ifcopenshell/api/owner/remove_actor.py | 49 ++- .../ifcopenshell/api/owner/remove_address.py | 47 ++- .../api/owner/remove_application.py | 33 +- .../api/owner/remove_organisation.py | 83 ++-- .../ifcopenshell/api/owner/remove_person.py | 81 ++-- .../owner/remove_person_and_organisation.py | 65 ++- .../ifcopenshell/api/owner/remove_role.py | 53 ++- .../ifcopenshell/api/owner/unassign_actor.py | 89 ++--- .../api/owner/update_owner_history.py | 115 +++--- .../ifcopenshell/api/profile/__init__.py | 6 + .../api/profile/add_arbitrary_profile.py | 67 ++-- .../add_arbitrary_profile_with_voids.py | 85 ++-- .../api/profile/add_parameterized_profile.py | 43 +- .../ifcopenshell/api/profile/edit_profile.py | 45 +-- .../api/profile/remove_profile.py | 45 +-- .../ifcopenshell/api/project/__init__.py | 5 + .../ifcopenshell/api/project/append_asset.py | 189 ++++----- .../api/project/assign_declaration.py | 210 +++++----- .../ifcopenshell/api/project/create_file.py | 74 ++-- .../api/project/unassign_declaration.py | 90 ++--- .../ifcopenshell/api/pset/__init__.py | 6 + .../ifcopenshell/api/pset/add_pset.py | 222 +++++------ .../ifcopenshell/api/pset/add_qto.py | 119 +++--- .../ifcopenshell/api/pset/edit_pset.py | 271 ++++++------- .../ifcopenshell/api/pset/edit_qto.py | 183 ++++----- .../ifcopenshell/api/pset/remove_pset.py | 113 +++--- .../api/pset_template/__init__.py | 7 + .../api/pset_template/add_prop_template.py | 160 ++++---- .../api/pset_template/add_pset_template.py | 152 ++++--- .../api/pset_template/edit_prop_template.py | 47 ++- .../api/pset_template/edit_pset_template.py | 45 +-- .../api/pset_template/remove_prop_template.py | 57 ++- .../api/pset_template/remove_pset_template.py | 37 +- .../ifcopenshell/api/resource/__init__.py | 13 + .../ifcopenshell/api/resource/add_resource.py | 172 ++++---- .../api/resource/add_resource_quantity.py | 87 ++-- .../api/resource/add_resource_time.py | 71 ++-- .../api/resource/assign_resource.py | 156 ++++---- .../api/resource/calculate_resource_usage.py | 53 +-- .../api/resource/calculate_resource_work.py | 79 ++-- .../api/resource/edit_resource.py | 43 +- .../api/resource/edit_resource_quantity.py | 63 ++- .../api/resource/edit_resource_time.py | 115 +++--- .../api/resource/remove_resource.py | 123 +++--- .../api/resource/remove_resource_quantity.py | 45 +-- .../api/resource/unassign_resource.py | 98 +++-- .../ifcopenshell/api/root/__init__.py | 5 + .../ifcopenshell/api/root/copy_class.py | 93 ++--- .../ifcopenshell/api/root/create_entity.py | 114 +++--- .../ifcopenshell/api/root/reassign_class.py | 110 +++--- .../ifcopenshell/api/root/remove_product.py | 371 +++++++++--------- .../ifcopenshell/api/sequence/__init__.py | 44 +++ .../ifcopenshell/api/sequence/add_task.py | 318 ++++++++------- .../api/sequence/add_task_time.py | 83 ++-- .../api/sequence/add_time_period.py | 119 +++--- .../api/sequence/add_work_calendar.py | 125 +++--- .../api/sequence/add_work_plan.py | 111 +++--- .../api/sequence/add_work_schedule.py | 188 +++++---- .../api/sequence/add_work_time.py | 105 +++-- .../api/sequence/assign_lag_time.py | 136 +++---- .../api/sequence/assign_process.py | 168 ++++---- .../api/sequence/assign_product.py | 128 +++--- .../api/sequence/assign_recurrence_pattern.py | 167 ++++---- .../api/sequence/assign_sequence.py | 194 +++++---- .../api/sequence/assign_workplan.py | 79 ++-- .../api/sequence/calculate_task_duration.py | 149 ++++--- .../api/sequence/cascade_schedule.py | 245 ++++++------ .../api/sequence/create_baseline.py | 73 ++-- .../api/sequence/duplicate_task.py | 55 +-- .../api/sequence/edit_lag_time.py | 117 +++--- .../api/sequence/edit_recurrence_pattern.py | 67 ++-- .../api/sequence/edit_sequence.py | 81 ++-- .../ifcopenshell/api/sequence/edit_task.py | 53 ++- .../api/sequence/edit_task_time.py | 147 +++---- .../api/sequence/edit_work_calendar.py | 45 +-- .../api/sequence/edit_work_plan.py | 55 ++- .../api/sequence/edit_work_schedule.py | 61 ++- .../api/sequence/edit_work_time.py | 82 ++-- .../api/sequence/get_related_products.py | 93 +++-- .../api/sequence/recalculate_schedule.py | 184 ++++----- .../ifcopenshell/api/sequence/remove_task.py | 209 +++++----- .../api/sequence/remove_time_period.py | 61 ++- .../api/sequence/remove_work_calendar.py | 75 ++-- .../api/sequence/remove_work_plan.py | 57 ++- .../api/sequence/remove_work_schedule.py | 111 +++--- .../api/sequence/remove_work_time.py | 39 +- .../api/sequence/unassign_lag_time.py | 83 ++-- .../api/sequence/unassign_process.py | 89 ++--- .../api/sequence/unassign_product.py | 89 ++--- .../sequence/unassign_recurrence_pattern.py | 53 ++- .../api/sequence/unassign_sequence.py | 79 ++-- .../ifcopenshell/api/spatial/__init__.py | 5 + .../api/spatial/assign_container.py | 262 ++++++------- .../api/spatial/dereference_structure.py | 108 +++-- .../api/spatial/reference_structure.py | 162 ++++---- .../api/spatial/unassign_container.py | 81 ++-- .../ifcopenshell/api/structural/__init__.py | 22 ++ .../api/structural/add_structural_activity.py | 116 +++--- .../add_structural_analysis_model.py | 37 +- .../add_structural_boundary_condition.py | 85 ++-- .../api/structural/add_structural_load.py | 49 ++- .../structural/add_structural_load_case.py | 63 ++- .../structural/add_structural_load_group.py | 63 ++- .../add_structural_member_connection.py | 47 ++- .../assign_structural_analysis_model.py | 61 ++- .../edit_structural_analysis_model.py | 33 +- .../edit_structural_boundary_condition.py | 43 +- .../edit_structural_connection_cs.py | 61 ++- .../structural/edit_structural_item_axis.py | 31 +- .../api/structural/edit_structural_load.py | 31 +- .../structural/edit_structural_load_case.py | 31 +- .../remove_structural_analysis_model.py | 39 +- .../remove_structural_boundary_condition.py | 49 ++- .../remove_structural_connection_condition.py | 41 +- .../api/structural/remove_structural_load.py | 21 +- .../structural/remove_structural_load_case.py | 37 +- .../remove_structural_load_group.py | 41 +- .../unassign_structural_analysis_model.py | 57 ++- .../ifcopenshell/api/style/__init__.py | 13 + .../ifcopenshell/api/style/add_style.py | 71 ++-- .../api/style/add_surface_style.py | 193 +++++---- .../api/style/add_surface_textures.py | 67 ++-- .../api/style/assign_material_style.py | 173 ++++---- .../api/style/assign_representation_styles.py | 184 ++++----- .../api/style/edit_presentation_style.py | 43 +- .../api/style/edit_surface_style.py | 111 +++--- .../ifcopenshell/api/style/remove_style.py | 49 +-- .../api/style/remove_styled_representation.py | 51 ++- .../api/style/remove_surface_style.py | 71 ++-- .../api/style/unassign_material_style.py | 131 +++---- .../style/unassign_representation_styles.py | 77 ++-- .../ifcopenshell/api/system/__init__.py | 13 + .../ifcopenshell/api/system/add_port.py | 63 ++- .../ifcopenshell/api/system/add_system.py | 67 ++-- .../api/system/assign_flow_control.py | 103 +++-- .../ifcopenshell/api/system/assign_port.py | 81 ++-- .../ifcopenshell/api/system/assign_system.py | 72 ++-- .../ifcopenshell/api/system/connect_port.py | 157 ++++---- .../api/system/disconnect_port.py | 93 +++-- .../ifcopenshell/api/system/edit_system.py | 43 +- .../ifcopenshell/api/system/remove_system.py | 87 ++-- .../api/system/unassign_flow_control.py | 91 +++-- .../ifcopenshell/api/system/unassign_port.py | 73 ++-- .../api/system/unassign_system.py | 64 ++- .../ifcopenshell/api/type/__init__.py | 5 + .../ifcopenshell/api/type/assign_type.py | 320 +++++++-------- .../api/type/get_related_objects.py | 71 ++-- .../api/type/map_type_representations.py | 160 ++++---- .../ifcopenshell/api/type/unassign_type.py | 85 ++-- .../ifcopenshell/api/unit/__init__.py | 11 + .../api/unit/add_context_dependent_unit.py | 75 ++-- .../api/unit/add_conversion_based_unit.py | 107 +++-- .../api/unit/add_monetary_unit.py | 41 +- .../ifcopenshell/api/unit/add_si_unit.py | 71 ++-- .../ifcopenshell/api/unit/assign_unit.py | 110 +++--- .../api/unit/edit_derived_unit.py | 31 +- .../api/unit/edit_monetary_unit.py | 45 +-- .../ifcopenshell/api/unit/edit_named_unit.py | 63 ++- .../ifcopenshell/api/unit/remove_unit.py | 53 ++- .../ifcopenshell/api/unit/unassign_unit.py | 61 ++- .../ifcopenshell/api/void/__init__.py | 5 + .../ifcopenshell/api/void/add_filling.py | 159 ++++---- .../ifcopenshell/api/void/add_opening.py | 189 +++++---- .../ifcopenshell/api/void/remove_filling.py | 65 ++- .../ifcopenshell/api/void/remove_opening.py | 61 ++- 330 files changed, 13283 insertions(+), 13751 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py index 18731bf443..bf452c8e2b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/__init__.py @@ -22,3 +22,6 @@ One common use is spatial elements, such as how a site has multiple buildings, and a building has multiple storeys. Another is for regular elements, such as how a wall is made out of members and coverings. """ + +from .assign_object import assign_object +from .unassign_object import unassign_object diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py index 3e9f866435..c1ffdc5a16 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py @@ -23,148 +23,144 @@ import ifcopenshell.util.placement from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_object: ifcopenshell.entity_instance, - ): - """Assigns object as an aggregate to the products +def assign_object( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_object: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns object as an aggregate to the products - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. - Typically used when you want to describe how large spaces are made up of - smaller spaces. For example large spatial elements (e.g. sites, - buidings) can be made out of smaller spatial elements (e.g. storeys, - spaces). + Typically used when you want to describe how large spaces are made up of + smaller spaces. For example large spatial elements (e.g. sites, + buidings) can be made out of smaller spatial elements (e.g. storeys, + spaces). - The largest space (typically the IfcSite) can then be aggregated in a - project. It is requirement for all spatial structures to be directly or - indirectly aggregated back to the IfcProject to create a hierarchy of - spaces. + The largest space (typically the IfcSite) can then be aggregated in a + project. It is requirement for all spatial structures to be directly or + indirectly aggregated back to the IfcProject to create a hierarchy of + spaces. - The other common usecase is when larger physical products are made up of - smaller physical products. For example, a stair might be made out of a - flight, a landing, a railing and so on. Or a wall might be made out of - stud members, and coverings. + The other common usecase is when larger physical products are made up of + smaller physical products. For example, a stair might be made out of a + flight, a landing, a railing and so on. Or a wall might be made out of + stud members, and coverings. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an aggregate relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an aggregate relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - IFC placements follow a convention where the placement is relative to - its parent in the spatial hierarchy. If your product has a placement, - its placement will be recalculated to follow this convention. + IFC placements follow a convention where the placement is relative to + its parent in the spatial hierarchy. If your product has a placement, + its placement will be recalculated to follow this convention. - :param products: The list of parts of the aggregate, typically of IfcElement or - IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance] - :param relating_object: The whole of the aggregate, typically an - IfcElement or IfcSpatialStructureElement subclass - :type relating_object: ifcopenshell.entity_instance - :return: The IfcRelAggregate relationship instance - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of parts of the aggregate, typically of IfcElement or + IfcSpatialStructureElement subclass + :type product: list[ifcopenshell.entity_instance] + :param relating_object: The whole of the aggregate, typically an + IfcElement or IfcSpatialStructureElement subclass + :type relating_object: ifcopenshell.entity_instance + :return: The IfcRelAggregate relationship instance + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + subelement = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[element], relating_object=project) - # The site has a building - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) - """ - self.file = file - self.settings = { - "products": products, - "relating_object": relating_object, - } + # The site has a building + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement], relating_object=element) + """ + settings = { + "products": products, + "relating_object": relating_object, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - products = set(self.settings["products"]) - relating_object = self.settings["relating_object"] - is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) + products = set(settings["products"]) + relating_object = settings["relating_object"] + is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None) - previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() - products_without_aggregates: list[ifcopenshell.entity_instance] = [] - products_with_aggregates: list[ifcopenshell.entity_instance] = [] + previous_aggregates_rels: set[ifcopenshell.entity_instance] = set() + products_without_aggregates: list[ifcopenshell.entity_instance] = [] + products_with_aggregates: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for product in products: - product_rel = next(iter(product.Decomposes), None) + # check if there is anything to change + for product in products: + product_rel = next(iter(product.Decomposes), None) - if product_rel is None: - products_without_aggregates.append(product) - continue + if product_rel is None: + products_without_aggregates.append(product) + continue - # either is_decomposed_by is None or product is part of different rel - if product_rel != is_decomposed_by: - previous_aggregates_rels.add(product_rel) - products_with_aggregates.append(product) + # either is_decomposed_by is None or product is part of different rel + if product_rel != is_decomposed_by: + previous_aggregates_rels.add(product_rel) + products_with_aggregates.append(product) - # products with already assigned aggregates will be skipped + # products with already assigned aggregates will be skipped - products_to_change = products_without_aggregates + products_with_aggregates - # nothing to change - if not products_to_change: - return is_decomposed_by + products_to_change = products_without_aggregates + products_with_aggregates + # nothing to change + if not products_to_change: + return is_decomposed_by - # can be either only aggregated or only contained at the same time - # some product might not be able to have a container - possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] - ifcopenshell.api.run("spatial.unassign_container", self.file, products=possibly_contained_products) + # can be either only aggregated or only contained at the same time + # some product might not be able to have a container + possibly_contained_products = [p for p in products_without_aggregates if hasattr(p, "ContainedInStructure")] + ifcopenshell.api.run("spatial.unassign_container", file, products=possibly_contained_products) - # unassign elements from previous aggregates - for decomposes in previous_aggregates_rels: - related_objects = set(decomposes.RelatedObjects) - products - if related_objects: - decomposes.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": decomposes}) - else: - history = decomposes.OwnerHistory - self.file.remove(decomposes) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new aggregate - if is_decomposed_by: - is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by}) + # unassign elements from previous aggregates + for decomposes in previous_aggregates_rels: + related_objects = set(decomposes.RelatedObjects) - products + if related_objects: + decomposes.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": decomposes}) else: - is_decomposed_by = self.file.create_entity( - "IfcRelAggregates", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": list(products), - "RelatingObject": relating_object, - } + history = decomposes.OwnerHistory + file.remove(decomposes) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new aggregate + if is_decomposed_by: + is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_decomposed_by}) + else: + is_decomposed_by = file.create_entity( + "IfcRelAggregates", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": list(products), + "RelatingObject": relating_object, + } + ) + + # localize placement relative to a new aggregate for affected products + for product in products_to_change: + placement = getattr(product, "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=product, + matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), + is_si=False, ) - # localize placement relative to a new aggregate for affected products - for product in products_to_change: - placement = getattr(product, "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=product, - matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), - is_si=False, - ) - - return is_decomposed_by + return is_decomposed_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py index c766a4f019..8a8e35a948 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/unassign_object.py @@ -21,60 +21,57 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Unassigns products from their aggregate +def unassign_object(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Unassigns products from their aggregate - A product (i.e. a smaller part of a whole) may be aggregated into zero - or one larger space or element. This function will remove that - aggregation relationship. + A product (i.e. a smaller part of a whole) may be aggregated into zero + or one larger space or element. This function will remove that + aggregation relationship. - As all physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", using this function will remove the - product from that tree. This is a dangerous operation and may result in - the product no longer being visible in IFC applications. + As all physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", using this function will remove the + product from that tree. This is a dangerous operation and may result in + the product no longer being visible in IFC applications. - If the product is not part of an aggregation relationship, nothing will - happen. + If the product is not part of an aggregation relationship, nothing will + happen. - :param products: The list of parts of the aggregate, typically of IfcElements or - IfcSpatialStructureElement subclass - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param products: The list of parts of the aggregate, typically of IfcElements or + IfcSpatialStructureElement subclass + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - subelement1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - subelement2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement1], relating_object=element) - ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element) - # nothing is returned - ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1]) - # nothing is returned, relationship is removed - ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2]) - """ - self.file = file - self.settings = {"products": products} + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + subelement1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + subelement2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement1], relating_object=element) + ifcopenshell.api.run("aggregate.assign_object", model, products=[subelement2], relating_object=element) + # nothing is returned + ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement1]) + # nothing is returned, relationship is removed + ifcopenshell.api.run("aggregate.unassign_object", model, products=[subelement2]) + """ + settings = {"products": products} - def execute(self) -> None: - products = set(self.settings["products"]) - rels = set( - rel - for product in products - if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None)) - ) + products = set(settings["products"]) + rels = set( + rel + for product in products + if (rel := next((rel for rel in product.Decomposes if rel.is_a("IfcRelAggregates")), None)) + ) - for rel in rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py index e0caddbe3c..31a605de5d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/__init__.py @@ -15,3 +15,5 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .edit_attributes import edit_attributes diff --git a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py index 1e2cd98bc5..05dbff5a5c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/attribute/edit_attributes.py @@ -19,64 +19,49 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, product=None, attributes=None): - """Edit the attributes of a product +def edit_attributes(file, product=None, attributes=None) -> None: + """Edit the attributes of a product - All IFC entities have attributes. Normally they can be edited directly, - by simply assigning a new value to them. In some scenarios, you may wish - to also ensure that ownership history is updated. This function provides - that convenience. + All IFC entities have attributes. Normally they can be edited directly, + by simply assigning a new value to them. In some scenarios, you may wish + to also ensure that ownership history is updated. This function provides + that convenience. - :param product: The product you want to edit. This may be any rooted IFC - entity. - :type product: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param product: The product you want to edit. This may be any rooted IFC + entity. + :type product: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("attribute.edit_attributes", model, - product=element, attributes={"Name": "Waldo"}) - """ - self.file = file - self.settings = {"product": product, "attributes": attributes or {}} + element = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("attribute.edit_attributes", model, + product=element, attributes={"Name": "Waldo"}) + """ + settings = {"product": product, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["product"], name, value) - if hasattr(self.settings["product"], "PredefinedType"): - if hasattr(self.settings["product"], "ElementType"): - if ( - self.settings["product"].ElementType is None - and self.settings["product"].PredefinedType == "USERDEFINED" - ): - self.settings["product"].PredefinedType = "NOTDEFINED" - elif ( - self.settings["product"].ElementType - and self.settings["product"].PredefinedType != "USERDEFINED" - ): - self.settings["product"].PredefinedType = "USERDEFINED" - elif hasattr(self.settings["product"], "ObjectType"): - relating_type = ifcopenshell.util.element.get_type(self.settings["product"]) - # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 - if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None): - self.settings["product"].ObjectType = None - self.settings["product"].PredefinedType = None - elif ( - self.settings["product"].ObjectType is None - and self.settings["product"].PredefinedType == "USERDEFINED" - ): - self.settings["product"].PredefinedType = "NOTDEFINED" - elif ( - self.settings["product"].ObjectType - and self.settings["product"].PredefinedType != "USERDEFINED" - ): - self.settings["product"].PredefinedType = "USERDEFINED" - if hasattr(self.settings["product"], "OwnerHistory"): - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": self.settings["product"]}) + for name, value in settings["attributes"].items(): + setattr(settings["product"], name, value) + if hasattr(settings["product"], "PredefinedType"): + if hasattr(settings["product"], "ElementType"): + if settings["product"].ElementType is None and settings["product"].PredefinedType == "USERDEFINED": + settings["product"].PredefinedType = "NOTDEFINED" + elif settings["product"].ElementType and settings["product"].PredefinedType != "USERDEFINED": + settings["product"].PredefinedType = "USERDEFINED" + elif hasattr(settings["product"], "ObjectType"): + relating_type = ifcopenshell.util.element.get_type(settings["product"]) + # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 + if relating_type and relating_type.PredefinedType not in ("NOTDEFINED", None): + settings["product"].ObjectType = None + settings["product"].PredefinedType = None + elif settings["product"].ObjectType is None and settings["product"].PredefinedType == "USERDEFINED": + settings["product"].PredefinedType = "NOTDEFINED" + elif settings["product"].ObjectType and settings["product"].PredefinedType != "USERDEFINED": + settings["product"].PredefinedType = "USERDEFINED" + if hasattr(settings["product"], "OwnerHistory"): + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": settings["product"]}) diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py index c2a0c1900d..fff4c4e7f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/__init__.py @@ -19,3 +19,8 @@ """Boundaries are primarily used for representing virtual interfaces between spaces for energy analysis. """ + +from .assign_connection_geometry import assign_connection_geometry +from .copy_boundary import copy_boundary +from .edit_attributes import edit_attributes +from .remove_boundary import remove_boundary diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py index 8f23a60bb6..b184810642 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/assign_connection_geometry.py @@ -19,68 +19,80 @@ import ifcopenshell.util.unit +def assign_connection_geometry( + file, + rel_space_boundary=None, + outer_boundary=None, + inner_boundaries=None, + location=None, + axis=None, + ref_direction=None, + unit_scale=None, +) -> None: + """Create and assign a connection geometry to a space boundary relationship + + A space boundary may optionally have a plane that represents how that + space is adjacent to another space, known as the connection geometry. + You may specify this plane in terms of an outer boundary polyline, zero + or more inner boundaries (such as for windows), and a positional matrix + for the orientation of the plane. + + :param rel_space_boundary: The space boundary relationship to assign the + connection geometry to. + :type rel_space_boundary: ifcopenshell.entity_instance + :param outer_boundary: A list of 2D points representing an open + polyline. The last point will connect to the first point. Each + point is represented by an interable of 2 floats. The coordinates of + the points are relative to the positional matrix arguments. + :type outer_boundary: list[list[float]] + :param inner_boundaries: A list of zero or more inner boundaries to use + for the plane. Each boundary is represented by an open polyline, as + defined by the outer_boundary argument. + :type inner_boundaries: list[list[list[float]]], optional + :param location: The local origin of the connection geometry, defined as + an XYZ coordinate relative to the placement of the space that is + being bounded. + :type location: list[float] + :param axis: The local X axis of the connection geometry, defined as an + XYZ vector relative to the placement of the space that is being + bounded. + :type axis: list[float] + :param ref_direction: The local Z axis of the connection geometry, + defined as an XYZ vector relative to the placement of the space that + is being bounded. The Y vector is automatically derived using the + right hand rule. + :type ref_direction: list[float] + :param unit_scale: The unit scale as calculated by + ifcopenshell.util.unit.calculate_unit_scale. If not provided, it + will be automatically calculated for you. + :type unit_scale: float, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("boundary.assign_connection_geometry", model, + rel_space_boundary=element, + outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)], + location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.], + ) + """ + usecase = Usecase() + usecase.file = file + usecase.rel_space_boundary = rel_space_boundary + usecase.outer_boundary = outer_boundary + usecase.inner_boundaries = inner_boundaries or () + usecase.location = location + usecase.axis = axis + usecase.ref_direction = ref_direction + usecase.unit_scale = unit_scale + usecase.ifc_vertices = [] + return usecase.execute() + + class Usecase: - def __init__(self, file, rel_space_boundary=None, outer_boundary=None, inner_boundaries=None, location=None, axis=None, ref_direction=None, unit_scale=None): - """Create and assign a connection geometry to a space boundary relationship - - A space boundary may optionally have a plane that represents how that - space is adjacent to another space, known as the connection geometry. - You may specify this plane in terms of an outer boundary polyline, zero - or more inner boundaries (such as for windows), and a positional matrix - for the orientation of the plane. - - :param rel_space_boundary: The space boundary relationship to assign the - connection geometry to. - :type rel_space_boundary: ifcopenshell.entity_instance - :param outer_boundary: A list of 2D points representing an open - polyline. The last point will connect to the first point. Each - point is represented by an interable of 2 floats. The coordinates of - the points are relative to the positional matrix arguments. - :type outer_boundary: list[list[float]] - :param inner_boundaries: A list of zero or more inner boundaries to use - for the plane. Each boundary is represented by an open polyline, as - defined by the outer_boundary argument. - :type inner_boundaries: list[list[list[float]]], optional - :param location: The local origin of the connection geometry, defined as - an XYZ coordinate relative to the placement of the space that is - being bounded. - :type location: list[float] - :param axis: The local X axis of the connection geometry, defined as an - XYZ vector relative to the placement of the space that is being - bounded. - :type axis: list[float] - :param ref_direction: The local Z axis of the connection geometry, - defined as an XYZ vector relative to the placement of the space that - is being bounded. The Y vector is automatically derived using the - right hand rule. - :type ref_direction: list[float] - :param unit_scale: The unit scale as calculated by - ifcopenshell.util.unit.calculate_unit_scale. If not provided, it - will be automatically calculated for you. - :type unit_scale: float, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("boundary.assign_connection_geometry", model, - rel_space_boundary=element, - outer_boundary=[(0., 0.), (1., 0.), (1., 1.), (0., 1.)], - location=[0., 0., 0.], axis=[1., 0., 0.], ref_direction=[0., 0., 1.], - ) - """ - self.file = file - self.rel_space_boundary = rel_space_boundary - self.outer_boundary = outer_boundary - self.inner_boundaries = inner_boundaries or () - self.location = location - self.axis = axis - self.ref_direction = ref_direction - self.unit_scale = unit_scale - self.ifc_vertices = [] - def execute(self): if self.unit_scale is None: self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py index 2f8b092c51..b051bae828 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/copy_boundary.py @@ -19,29 +19,26 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, boundary=None): - """Copies a space boundary +def copy_boundary(file, boundary=None) -> None: + """Copies a space boundary - :param boundary: The IfcRelSpaceBoundary you want to copy. - :type boundary: ifcopenshell.entity_instance - :return: None - :rtype: None + :param boundary: The IfcRelSpaceBoundary you want to copy. + :type boundary: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A boring boundary with no geometry. Note that this boundary is - # invalid and does not relate to any space or building element. - boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") + # A boring boundary with no geometry. Note that this boundary is + # invalid and does not relate to any space or building element. + boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") - # And now we have two - boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary) - """ - self.file = file - self.settings = {"boundary": boundary} + # And now we have two + boundary_copy = ifcopenshell.api.run("boundary.copy_boundary", model, boundary=boundary) + """ + settings = {"boundary": boundary} - def execute(self): - result = ifcopenshell.util.element.copy(self.file, self.settings["boundary"]) - if result.ConnectionGeometry: - result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(self.file, result.ConnectionGeometry) - return result + result = ifcopenshell.util.element.copy(file, settings["boundary"]) + if result.ConnectionGeometry: + result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry) + return result diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py index 4be540f7a2..663c656dbb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/edit_attributes.py @@ -17,45 +17,49 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, entity=None, relating_space=None, related_building_element=None, parent_boundary=None, corresponding_boundary=None): - """Modify the relationships of a space boundary relationship +def edit_attributes( + file, + entity=None, + relating_space=None, + related_building_element=None, + parent_boundary=None, + corresponding_boundary=None, +) -> None: + """Modify the relationships of a space boundary relationship - Currently this function is quite minimal and offers no advantage to - manual assignment of the space boundary attributes. + Currently this function is quite minimal and offers no advantage to + manual assignment of the space boundary attributes. - :param entity: The IfcRelSpaceBoundary to modify - :type entity: ifcopenshell.entity_instance - :param relating_space: The IfcSpace or IfcExternalSpatialElement that - the space boundary is related to. - :type relating_space: ifcopenshell.entity_instance - :param related_building_element: The IfcElement that defines the - boundary, typically an IfcWall. - :type relating_space: ifcopenshell.entity_instance - :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if - this is an inner boundary. This can apply to 1st and 2nd level - boundaries. - :type parent_boundary: ifcopenshell.entity_instance, - optional - :param corresponding_boundary: The other IfcRelSpaceBoundary on the - other side of the related element. The pair together represents a - thermal boundary. This only applies to 2nd level boundaries. - :type corresponding_boundary: ifcopenshell.entity_instance, - optional - :return: None - :rtype: None - """ - self.file = file - self.entity = entity - self.relating_space = relating_space - self.related_building_element = related_building_element - self.parent_boundary = parent_boundary - self.corresponding_boundary = corresponding_boundary + :param entity: The IfcRelSpaceBoundary to modify + :type entity: ifcopenshell.entity_instance + :param relating_space: The IfcSpace or IfcExternalSpatialElement that + the space boundary is related to. + :type relating_space: ifcopenshell.entity_instance + :param related_building_element: The IfcElement that defines the + boundary, typically an IfcWall. + :type relating_space: ifcopenshell.entity_instance + :param parent_boundary: A parent IfcRelSpaceBoundary, only provided if + this is an inner boundary. This can apply to 1st and 2nd level + boundaries. + :type parent_boundary: ifcopenshell.entity_instance, + optional + :param corresponding_boundary: The other IfcRelSpaceBoundary on the + other side of the related element. The pair together represents a + thermal boundary. This only applies to 2nd level boundaries. + :type corresponding_boundary: ifcopenshell.entity_instance, + optional + :return: None + :rtype: None + """ + entity = entity + relating_space = relating_space + related_building_element = related_building_element + parent_boundary = parent_boundary + corresponding_boundary = corresponding_boundary - def execute(self): - self.entity.RelatingSpace = self.relating_space - self.entity.RelatedBuildingElement = self.related_building_element - if hasattr(self.entity, "ParentBoundary"): - self.entity.ParentBoundary = self.parent_boundary - if hasattr(self.entity, "CorrespondingBoundary"): - self.entity.CorrespondingBoundary = self.corresponding_boundary + entity.RelatingSpace = relating_space + entity.RelatedBuildingElement = related_building_element + if hasattr(entity, "ParentBoundary"): + entity.ParentBoundary = parent_boundary + if hasattr(entity, "CorrespondingBoundary"): + entity.CorrespondingBoundary = corresponding_boundary diff --git a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py index 6744da820c..dadf44e3c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py +++ b/src/ifcopenshell-python/ifcopenshell/api/boundary/remove_boundary.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, boundary=None): - """Removes a space boundary +def remove_boundary(file, boundary=None) -> None: + """Removes a space boundary - The relating space or related building element is untouched. Only the - boundary and its connection geometry is removed. + The relating space or related building element is untouched. Only the + boundary and its connection geometry is removed. - :param boundary: The IfcRelSpaceBoundary you want to remove. - :type boundary: ifcopenshell.entity_instance - :return: None - :rtype: None + :param boundary: The IfcRelSpaceBoundary you want to remove. + :type boundary: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A boring boundary with no geometry. Note that this boundary is - # invalid and does not relate to any space or building element. - boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") + # A boring boundary with no geometry. Note that this boundary is + # invalid and does not relate to any space or building element. + boundary = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcRelSpaceBoundary") - # Let's remove it! - ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary) - """ - self.file = file - self.settings = {"boundary": boundary} + # Let's remove it! + ifcopenshell.api.run("boundary.remove_boundary", model, boundary=boundary) + """ + settings = {"boundary": boundary} - def execute(self): - geometry = self.settings["boundary"].ConnectionGeometry - if geometry: - self.settings["boundary"].ConnectionGeometry = None - ifcopenshell.util.element.remove_deep2(self.file, geometry) - history = self.settings["boundary"].OwnerHistory - self.file.remove(self.settings["boundary"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + geometry = settings["boundary"].ConnectionGeometry + if geometry: + settings["boundary"].ConnectionGeometry = None + ifcopenshell.util.element.remove_deep2(file, geometry) + history = settings["boundary"].OwnerHistory + file.remove(settings["boundary"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py index e0caddbe3c..6616ff6f89 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_classification import add_classification +from .add_reference import add_reference +from .edit_classification import edit_classification +from .edit_reference import edit_reference +from .remove_classification import remove_classification +from .remove_reference import remove_reference diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py index 580c036a5f..a6e251fcf2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_classification.py @@ -22,67 +22,72 @@ import ifcopenshell.util.date from typing import Union +def add_classification( + file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance] +) -> ifcopenshell.entity_instance: + """Adds a new classification system to the project + + External classification systems such as Uniclass or Omniclass are + ways of categorising elements in the AEC industry, typically + standardised or nominated by governments or companies. A system + typically contains a series of hierarchical reference codes and labels + like Pr_12_23_34. + + Classifications may be applied to many things, not just physical + elements, such as doors and windows, spatial elements, tasks, cost + items, or even resources. + + Prior to assigning classificaion references, you need to add the name + and metadata of the classification system that you will use in your + project. Classification systems may be revised over time, so this + metadata includes the edition date. + + Common classification systems are provided as an IFC library which may + be downloaded from https://github.com/Moult/IfcClassification for your + convenience. It is advised to use these to ensure that the + classification metadata is standardised. + + Adding a classification system will not add the entire hierarchy of + references available in the classification. References need to be added + separately. Typically, you'd only add the references that you use in + your project, see ifcopenshell.api.classification.add_reference for more + information. + + :param classification: If a string is provided, it is assumed to be the + name of your classification system. This is necessary if you are + creating your own custom classification system. Alternatively, you + may provide an entity_instance of an IfcClassification from an IFC + classification library. The latter approach is preferred if you are + using a commonly known system such as Uniclass, as this will ensure + all metadata is added correctly. + :type classification: str,ifcopenshell.entity_instance + :return: The added IfcClassification element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Option 1: adding a custom clasification from scratch + ifcopenshell.api.run("classification.add_classification", model, + classification="MyCustomClassification") + + # Option 2: adding a popular classification from a library + library = ifcopenshell.open("/path/to/Uniclass.ifc") + classification = library.by_type("IfcClassification")[0] + ifcopenshell.api.run("classification.add_classification", model, + classification=classification) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "classification": classification, + } + return usecase.execute() + + class Usecase: - def __init__(self, file: ifcopenshell.file, classification: Union[str, ifcopenshell.entity_instance]): - """Adds a new classification system to the project - - External classification systems such as Uniclass or Omniclass are - ways of categorising elements in the AEC industry, typically - standardised or nominated by governments or companies. A system - typically contains a series of hierarchical reference codes and labels - like Pr_12_23_34. - - Classifications may be applied to many things, not just physical - elements, such as doors and windows, spatial elements, tasks, cost - items, or even resources. - - Prior to assigning classificaion references, you need to add the name - and metadata of the classification system that you will use in your - project. Classification systems may be revised over time, so this - metadata includes the edition date. - - Common classification systems are provided as an IFC library which may - be downloaded from https://github.com/Moult/IfcClassification for your - convenience. It is advised to use these to ensure that the - classification metadata is standardised. - - Adding a classification system will not add the entire hierarchy of - references available in the classification. References need to be added - separately. Typically, you'd only add the references that you use in - your project, see ifcopenshell.api.classification.add_reference for more - information. - - :param classification: If a string is provided, it is assumed to be the - name of your classification system. This is necessary if you are - creating your own custom classification system. Alternatively, you - may provide an entity_instance of an IfcClassification from an IFC - classification library. The latter approach is preferred if you are - using a commonly known system such as Uniclass, as this will ensure - all metadata is added correctly. - :type classification: str,ifcopenshell.entity_instance - :return: The added IfcClassification element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Option 1: adding a custom clasification from scratch - ifcopenshell.api.run("classification.add_classification", model, - classification="MyCustomClassification") - - # Option 2: adding a popular classification from a library - library = ifcopenshell.open("/path/to/Uniclass.ifc") - classification = library.by_type("IfcClassification")[0] - ifcopenshell.api.run("classification.add_classification", model, - classification=classification) - """ - self.file = file - self.settings = { - "classification": classification, - } - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): if isinstance(self.settings["classification"], str): classification = self.file.createIfcClassification(Name=self.settings["classification"]) self.relate_to_project(classification) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py index db1bab41bf..979bfc40dd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/add_reference.py @@ -23,117 +23,119 @@ import ifcopenshell.util.schema from typing import Optional, Union +def add_reference( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + reference: Optional[ifcopenshell.entity_instance] = None, + identification: Optional[str] = None, + name: Optional[str] = None, + classification: Optional[ifcopenshell.entity_instance] = None, + is_lightweight=True, +) -> Union[ifcopenshell.entity_instance, None]: + """Adds a new classification reference and assigns it to the list of products + + A classification reference is a single entry such as "Pr_12_23_34" that + is part of an external classification system (such as Uniclass or + Omniclass). + + References can be added to almost any object in IFC, including physical + objects, object types, properties, tasks, costs, resources, or even + resources such as profiles, documents, libraries, and so on. + + Classification references can be added in two ways. Option 1) specify a + custom arbitrary reference, where you have to manually specify the + identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products"). + Option 2) add a reference from an IFC classification library. The latter + is preferred if you are using a common classification system such as + Uniclass, as the library will be prepopulated with all the valid + classifications already. + + Objects are allowed to have multiple classification references from + multiple classification systems. This means that adding a new reference + will not remove existing references. + + References can be inherited from types. This means that if an + IfcWallType has a classification reference of Pr_12_23_34, then all + IfcWall occurrences of that type automatically get the same + classification of Pr_12_23_34. This means that it is more efficient to + assign to types where possible. If a classification reference is + assigned to both the type and an occurrence, then the assignment at the + occurrence will override the type classification. + + :param product: The list of IFC objects, properties, or resources you want to + associate the classification reference to. + :type product: list[ifcopenshell.entity_instance] + :param reference: The classification reference entity taken from an + IFC classification library. If you supply this parameter, you will + use option 2. + :type reference: ifcopenshell.entity_instance, optional + :param identification: If you choose option 1 and do not specify a + reference, you may manually specify an identification code. The code + is typically a short identifier and may have punctuation to separate + the levels of hierarchy in the classificaion (e.g. Pr_12_23_34). + :type identification: str, optional + :param name: If you choose option 1 and do not specify a reference, you + may manually specify a name. The name is typically human readable. + :type name: str, optional + :param classification: The IfcClassification entity in your IFC model + (not the library, if you are doing option 2) that the reference is + part of. + :type classification: ifcopenshell.entity_instance + :param is_lightweight: If you are doing option 2, choose whether or not + to only add that particular reference (lighweight) or also add all + of its parent references in the classification hierarchy (not + lighweight). For example, adding a lightweight reference to + Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference + to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent + references merely help describe the "tree" of classifications, but + is generally unnecessary. Using lightweight classifications are + recommended and is the default. + :type is_lightweight: bool, optional + + :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. + + :return: The newly added IfcClassificationReference + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] + + Example: + + .. code:: python + + # Option 1: adding and assigning a new reference from scratch + wall_type = model.by_type("IfcWallType")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification="MyCustomClassification") + ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + identification="W_01", name="Interior Walls") + + # Option 2: adding a popular classification from a library + library = ifcopenshell.open("/path/to/Uniclass.ifc") + lib_classification = library.by_type("IfcClassification")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification=lib_classification) + reference = [r for r in library.by_type("IfcClassificationReference") + if r.Identification == "XYZ"][0] + ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + reference=reference) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "reference": reference, + "identification": identification, + "name": name, + "classification": classification, + "is_lightweight": is_lightweight, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - reference: Optional[ifcopenshell.entity_instance] = None, - identification: Optional[str] = None, - name: Optional[str] = None, - classification: Optional[ifcopenshell.entity_instance] = None, - is_lightweight=True, - ): - """Adds a new classification reference and assigns it to the list of products - - A classification reference is a single entry such as "Pr_12_23_34" that - is part of an external classification system (such as Uniclass or - Omniclass). - - References can be added to almost any object in IFC, including physical - objects, object types, properties, tasks, costs, resources, or even - resources such as profiles, documents, libraries, and so on. - - Classification references can be added in two ways. Option 1) specify a - custom arbitrary reference, where you have to manually specify the - identification (e.g. "Pr_12_23_45") and name (e.g. "Door Products"). - Option 2) add a reference from an IFC classification library. The latter - is preferred if you are using a common classification system such as - Uniclass, as the library will be prepopulated with all the valid - classifications already. - - Objects are allowed to have multiple classification references from - multiple classification systems. This means that adding a new reference - will not remove existing references. - - References can be inherited from types. This means that if an - IfcWallType has a classification reference of Pr_12_23_34, then all - IfcWall occurrences of that type automatically get the same - classification of Pr_12_23_34. This means that it is more efficient to - assign to types where possible. If a classification reference is - assigned to both the type and an occurrence, then the assignment at the - occurrence will override the type classification. - - :param product: The list of IFC objects, properties, or resources you want to - associate the classification reference to. - :type product: list[ifcopenshell.entity_instance] - :param reference: The classification reference entity taken from an - IFC classification library. If you supply this parameter, you will - use option 2. - :type reference: ifcopenshell.entity_instance, optional - :param identification: If you choose option 1 and do not specify a - reference, you may manually specify an identification code. The code - is typically a short identifier and may have punctuation to separate - the levels of hierarchy in the classificaion (e.g. Pr_12_23_34). - :type identification: str, optional - :param name: If you choose option 1 and do not specify a reference, you - may manually specify a name. The name is typically human readable. - :type name: str, optional - :param classification: The IfcClassification entity in your IFC model - (not the library, if you are doing option 2) that the reference is - part of. - :type classification: ifcopenshell.entity_instance - :param is_lightweight: If you are doing option 2, choose whether or not - to only add that particular reference (lighweight) or also add all - of its parent references in the classification hierarchy (not - lighweight). For example, adding a lightweight reference to - Pr_12_23_34 will only add Pr_12_23_34, but adding a heavy reference - to Pr_12_23_34 will also add Pr_12_23 and Pr_12. These parent - references merely help describe the "tree" of classifications, but - is generally unnecessary. Using lightweight classifications are - recommended and is the default. - :type is_lightweight: bool, optional - - :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. - - :return: The newly added IfcClassificationReference - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] - - Example: - - .. code:: python - - # Option 1: adding and assigning a new reference from scratch - wall_type = model.by_type("IfcWallType")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification="MyCustomClassification") - ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - identification="W_01", name="Interior Walls") - - # Option 2: adding a popular classification from a library - library = ifcopenshell.open("/path/to/Uniclass.ifc") - lib_classification = library.by_type("IfcClassification")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification=lib_classification) - reference = [r for r in library.by_type("IfcClassificationReference") - if r.Identification == "XYZ"][0] - ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - reference=reference) - """ - self.file = file - self.settings = { - "products": products, - "reference": reference, - "identification": identification, - "name": name, - "classification": classification, - "is_lightweight": is_lightweight, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): if not self.settings["products"]: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py index 9925c59f54..7568a11d5e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_classification.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, classification=None, attributes=None): - """Edits the attributes of an IfcClassification +def edit_classification(file, classification=None, attributes=None) -> None: + """Edits the attributes of an IfcClassification - For more information about the attributes and data types of an - IfcClassification, consult the IFC documentation. + For more information about the attributes and data types of an + IfcClassification, consult the IFC documentation. - :param classification: The IfcClassification entity you want to edit - :type classification: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param classification: The IfcClassification entity you want to edit + :type classification: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - classification = model.by_type("IfcClassification")[0] - # Change the name of the classification system to "Foo" - ifcopenshell.api.run("classification.edit_classification", model, - classification=classification, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"classification": classification, "attributes": attributes or {}} + classification = model.by_type("IfcClassification")[0] + # Change the name of the classification system to "Foo" + ifcopenshell.api.run("classification.edit_classification", model, + classification=classification, attributes={"Name": "Foo"}) + """ + settings = {"classification": classification, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["classification"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["classification"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py index 4acf396adb..dc5096f38c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/edit_reference.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, reference=None, attributes=None): - """Edits the attributes of an IfcClassificationReference +def edit_reference(file, reference=None, attributes=None) -> None: + """Edits the attributes of an IfcClassificationReference - For more information about the attributes and data types of an - IfcClassificationReference, consult the IFC documentation. + For more information about the attributes and data types of an + IfcClassificationReference, consult the IFC documentation. - :param reference: The IfcClassificationReference entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcClassificationReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - reference = model.by_type("IfcClassification")[0] - # Change the name of the reference to "Foo" - ifcopenshell.api.run("classification.edit_reference", model, - reference=reference, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + reference = model.by_type("IfcClassification")[0] + # Change the name of the reference to "Foo" + ifcopenshell.api.run("classification.edit_reference", model, + reference=reference, attributes={"Name": "Foo"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py index 42a5dcacd0..42ec050d61 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_classification.py @@ -20,30 +20,33 @@ import ifcopenshell import ifcopenshell.util.element +def remove_classification(file, classification=None) -> None: + """Removes an IfcClassification from the project and all references + + The classification and all of its relationships, children references, + and relationships between objectse and child references are completely + removed from a project. + + :param classification: The IfcClassification entity you want to remove + :type classification: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + classification = model.by_type("IfcClassification")[0] + ifcopenshell.api.run("classification.remove_classification", model, + classification=classification) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"classification": classification} + return usecase.execute() + + class Usecase: - def __init__(self, file, classification=None): - """Removes an IfcClassification from the project and all references - - The classification and all of its relationships, children references, - and relationships between objectse and child references are completely - removed from a project. - - :param classification: The IfcClassification entity you want to remove - :type classification: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - classification = model.by_type("IfcClassification")[0] - ifcopenshell.api.run("classification.remove_classification", model, - classification=classification) - """ - self.file = file - self.settings = {"classification": classification} - def execute(self): references = self.get_references(self.settings["classification"]) for reference in references: diff --git a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py index ea61fb002d..bad8406582 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/classification/remove_reference.py @@ -21,107 +21,102 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - products: list[ifcopenshell.entity_instance], - ): - """Removes a classification reference from the list of products +def remove_reference( + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + products: list[ifcopenshell.entity_instance], +) -> None: + """Removes a classification reference from the list of products - If the classification reference is no longer associated to any products, - the classification reference itself is also removed. + If the classification reference is no longer associated to any products, + the classification reference itself is also removed. - :param reference: The IfcClassificationReference entity of the - relationship you want to remove. - :type reference: ifcopenshell.entity_instance - :param product: The list fo object entities of the relationship you want to - remove. - :type product: list[ifcopenshell.entity_instance] + :param reference: The IfcClassificationReference entity of the + relationship you want to remove. + :type reference: ifcopenshell.entity_instance + :param product: The list fo object entities of the relationship you want to + remove. + :type product: list[ifcopenshell.entity_instance] - :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. + :raises TypeError: If file is IFC2X3 and `products` has non-IfcRoot elements. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - wall_type = model.by_type("IfcWallType")[0] - classification = ifcopenshell.api.run("classification.add_classification", - model, classification="MyCustomClassification") - reference = ifcopenshell.api.run("classification.add_reference", model, - products=[wall_type], classification=classification, - identification="W_01", name="Interior Walls") - ifcopenshell.api.run("classification.remove_reference", model, - reference=reference, products=[wall_type]) - """ - self.file = file - self.settings = {"reference": reference, "products": products} + wall_type = model.by_type("IfcWallType")[0] + classification = ifcopenshell.api.run("classification.add_classification", + model, classification="MyCustomClassification") + reference = ifcopenshell.api.run("classification.add_reference", model, + products=[wall_type], classification=classification, + identification="W_01", name="Interior Walls") + ifcopenshell.api.run("classification.remove_reference", model, + reference=reference, products=[wall_type]) + """ + settings = {"reference": reference, "products": products} - def execute(self) -> None: - is_ifc2x3 = self.file.schema == "IFC2X3" - products = set(self.settings["products"]) - referenced = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - products -= products.difference(referenced) + is_ifc2x3 = file.schema == "IFC2X3" + products = set(settings["products"]) + referenced = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + products -= products.difference(referenced) - # all products are already unassigned from a reference - if not products: - return + # all products are already unassigned from a reference + if not products: + return - rooted_products: set[ifcopenshell.entity_instance] = set() - non_rooted_products: set[ifcopenshell.entity_instance] = set() - for product in self.settings["products"]: - if product.is_a("IfcRoot"): - rooted_products.add(product) + rooted_products: set[ifcopenshell.entity_instance] = set() + non_rooted_products: set[ifcopenshell.entity_instance] = set() + for product in settings["products"]: + if product.is_a("IfcRoot"): + rooted_products.add(product) + else: + non_rooted_products.add(product) + + if non_rooted_products and is_ifc2x3: + raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.") + + if rooted_products: + reference_rels: set[ifcopenshell.entity_instance] = set() + for product in rooted_products: + reference_rels.update(product.HasAssociations) + + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesClassification") and rel.RelatingClassification == settings["reference"] + } + + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - rooted_products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - non_rooted_products.add(product) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - if non_rooted_products and is_ifc2x3: - raise TypeError(f"Cannot add reference to non-IfcRoot element in IFC2X3: {non_rooted_products}.") + if non_rooted_products: + reference_rels: set[ifcopenshell.entity_instance] = set() + for product in non_rooted_products: + rels = getattr(product, "HasExternalReferences", None) + if rels is None: + rels = getattr(product, "HasExternalReference", []) + reference_rels.update(rels) - if rooted_products: - reference_rels: set[ifcopenshell.entity_instance] = set() - for product in rooted_products: - reference_rels.update(product.HasAssociations) + reference_rels = {rel for rel in reference_rels if rel.RelatingReference == settings["reference"]} + for rel in reference_rels: + related_objects = set(rel.RelatedResourceObjects) - non_rooted_products + if related_objects: + rel.RelatedResourceObjects = list(related_objects) + else: + file.remove(rel) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesClassification") - and rel.RelatingClassification == self.settings["reference"] - } - - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - rooted_products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - if non_rooted_products: - reference_rels: set[ifcopenshell.entity_instance] = set() - for product in non_rooted_products: - rels = getattr(product, "HasExternalReferences", None) - if rels is None: - rels = getattr(product, "HasExternalReference", []) - reference_rels.update(rels) - - reference_rels = {rel for rel in reference_rels if rel.RelatingReference == self.settings["reference"]} - for rel in reference_rels: - related_objects = set(rel.RelatedResourceObjects) - non_rooted_products - if related_objects: - rel.RelatedResourceObjects = list(related_objects) - else: - self.file.remove(rel) - - # TODO: we only handle lightweight classifications here - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - if not referenced_elements: - self.file.remove(self.settings["reference"]) + # TODO: we only handle lightweight classifications here + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + if not referenced_elements: + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py index e0caddbe3c..7309050851 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/__init__.py @@ -15,3 +15,13 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py index b84e2f3cc9..ab0870b528 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric.py @@ -19,44 +19,41 @@ import ifcopenshell -class Usecase: - def __init__(self, file, objective=None): - """Add a new metric benchmark +def add_metric(file, objective=None) -> None: + """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. + 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. - :type objective: ifcopenshell.entity_instance - :return: The newly created IfcMetric entity - :rtype: ifcopenshell.entity_instance + :param objective: The IfcObjective that this metric is a benchmark of. + :type objective: ifcopenshell.entity_instance + :return: The newly created IfcMetric entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - """ - self.file = file - self.settings = { - "objective": objective, + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + """ + settings = { + "objective": objective, + } + + metric = file.create_entity( + "IfcMetric", + **{ + "Name": "Unnamed", + "ConstraintGrade": "NOTDEFINED", + "Benchmark": "EQUALTO", } - - def execute(self): - metric = self.file.create_entity( - "IfcMetric", - **{ - "Name": "Unnamed", - "ConstraintGrade": "NOTDEFINED", - "Benchmark": "EQUALTO", - } - ) - if self.settings["objective"]: - benchmark_values = list(self.settings["objective"].BenchmarkValues or []) - benchmark_values.append(metric) - self.settings["objective"].BenchmarkValues = benchmark_values - return metric + ) + if settings["objective"]: + benchmark_values = list(settings["objective"].BenchmarkValues or []) + benchmark_values.append(metric) + settings["objective"].BenchmarkValues = benchmark_values + return metric diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py index 072c71ecb0..a3c37392e2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_metric_reference.py @@ -18,28 +18,26 @@ import ifcopenshell -class Usecase: - def __init__(self, file, metric=None, reference_path=None): - """ - 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. - """ - self.file = file - self.settings = {"metric": metric, "reference_path": reference_path} - def execute(self): - if self.settings["reference_path"]: - attributes = self.settings["reference_path"].split(".") - references_created = [] - for i in range(len(attributes)): - if i == 0: - reference = self.file.create_entity("IfcReference") - reference.AttributeIdentifier = attributes[i] - self.settings["metric"].ReferencePath = reference - references_created.append(reference) - else: - reference = self.file.create_entity("IfcReference") - reference.AttributeIdentifier = attributes[i] - references_created[i-1].InnerReference = reference - references_created.append(reference) - return references_created \ No newline at end of file +def add_metric_reference(file, metric=None, reference_path=None) -> None: + """ + 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. + """ + settings = {"metric": metric, "reference_path": reference_path} + + if settings["reference_path"]: + attributes = settings["reference_path"].split(".") + references_created = [] + for i in range(len(attributes)): + if i == 0: + reference = file.create_entity("IfcReference") + reference.AttributeIdentifier = attributes[i] + settings["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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py index 40fb46dfd2..efce0bc080 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/add_objective.py @@ -19,34 +19,31 @@ import ifcopenshell -class Usecase: - def __init__(self, file): - """Add a new objective constraint +def add_objective(file) -> None: + """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. + 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 - :rtype: ifcopenshell.entity_instance + :return: The newly created IfcObjective entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new objective for code compliance requirements - objective = ifcopenshell.api.run("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. - """ - self.file = file - self.settings = {} + # Create a new objective for code compliance requirements + objective = ifcopenshell.api.run("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. + """ + settings = {} - def execute(self): - return self.file.create_entity( - "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"} - ) + return file.create_entity( + "IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"} + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py index dfc826faf8..89a80d9ab3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/assign_constraint.py @@ -21,39 +21,41 @@ import ifcopenshell.api from typing import Union +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. + :type products: list[ifcopenshell.entity_instance] + :param constraint: The IfcObjective constraint + :type constraint: ifcopenshell.entity_instance + :return: The new or updated IfcRelAssociatesConstraint relationship + or `None` if `products` was an empty list. + :rtype: ifcopenshell.entity_instance + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "constraint": constraint, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - constraint: ifcopenshell.entity_instance, - ): - """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. - :type products: list[ifcopenshell.entity_instance] - :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance - :return: The new or updated IfcRelAssociatesConstraint relationship - or `None` if `products` was an empty list. - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "products": products, - "constraint": constraint, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): products = set(self.settings["products"]) if not products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py index 72fead7d88..b1ba5699ca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_metric.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, metric=None, attributes=None): - """Edit the attributes of a metric +def edit_metric(file, metric=None, attributes=None) -> None: + """Edit the attributes of a metric - For more information about the attributes and data types of an - IfcMetric, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMetric, consult the IFC documentation. - :param metric: The IfcMetric you want to edit. - :type metric: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param metric: The IfcMetric you want to edit. + :type metric: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - ifcopenshell.api.run("constraint.edit_metric", model, - metric=metric, attributes={"ConstraintGrade": "HARD"}) - """ - self.file = file - self.settings = {"metric": metric, "attributes": attributes or {}} + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + ifcopenshell.api.run("constraint.edit_metric", model, + metric=metric, attributes={"ConstraintGrade": "HARD"}) + """ + settings = {"metric": metric, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["metric"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["metric"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py index dff4985539..6ce5c597e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/edit_objective.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, objective=None, attributes=None): - """Edit the attributes of a objective +def edit_objective(file, objective=None, attributes=None) -> None: + """Edit the attributes of a objective - For more information about the attributes and data types of an - IfcObjective, consult the IFC documentation. + For more information about the attributes and data types of an + IfcObjective, consult the IFC documentation. - :param objective: The IfcObjective you want to edit. - :type objective: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param objective: The IfcObjective you want to edit. + :type objective: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - ifcopenshell.api.run("constraint.edit_objective", model, - objective=objective, attributes={"ConstraintGrade": "HARD"}) - """ - self.file = file - self.settings = {"objective": objective, "attributes": attributes or {}} + objective = ifcopenshell.api.run("constraint.add_objective", model) + ifcopenshell.api.run("constraint.edit_objective", model, + objective=objective, attributes={"ConstraintGrade": "HARD"}) + """ + settings = {"objective": objective, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["objective"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["objective"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py index e7dab1afb0..30b61fb5c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_constraint.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, constraint=None): - """Remove a constraint (typically an objective) +def remove_constraint(file, constraint=None) -> 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. + 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. - :type constraint: ifcopenshell.entity_instance - :return: None - :rtype: None + :param constraint: The IfcObjective you want to remove. + :type constraint: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - objective = ifcopenshell.api.run("constraint.add_objective", model) - ifcopenshell.api.run("constraint.remove_constraint", model, - constraint=objective) - """ - self.file = file - self.settings = {"constraint": constraint} + objective = ifcopenshell.api.run("constraint.add_objective", model) + ifcopenshell.api.run("constraint.remove_constraint", model, + constraint=objective) + """ + settings = {"constraint": constraint} - def execute(self): - self.file.remove(self.settings["constraint"]) - 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) + file.remove(settings["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) diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py index 6eaf012fa2..49203da3c5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/remove_metric.py @@ -17,31 +17,34 @@ # along with IfcOpenShell. If not, see . +def remove_metric(file, metric=None) -> 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. + :type metric: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + objective = ifcopenshell.api.run("constraint.add_objective", model) + metric = ifcopenshell.api.run("constraint.add_metric", model, + objective=objective) + ifcopenshell.api.run("constraint.remove_metric", model, + metric=metric) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"metric": metric} + return usecase.execute() + + class Usecase: - def __init__(self, file, metric=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. - :type metric: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - objective = ifcopenshell.api.run("constraint.add_objective", model) - metric = ifcopenshell.api.run("constraint.add_metric", model, - objective=objective) - ifcopenshell.api.run("constraint.remove_metric", model, - metric=metric) - """ - self.file = file - self.settings = {"metric": metric} - def execute(self): if self.settings["metric"].ReferencePath: reference = self.settings["metric"].ReferencePath diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py index 3b6471713e..138964e265 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py @@ -21,31 +21,33 @@ import ifcopenshell.api 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. + :type products: list[ifcopenshell.entity_instance] + :param constraint: The IfcObjective constraint + :type constraint: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "products": products, + "constraint": constraint, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - constraint: ifcopenshell.entity_instance, - ): - """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. - :type products: list[ifcopenshell.entity_instance] - :param constraint: The IfcObjective constraint - :type constraint: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "products": products, - "constraint": constraint, - } - def execute(self): products = set(self.settings["products"]) if not products: diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py index e0caddbe3c..1edb3ee252 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_context import add_context +from .edit_context import edit_context +from .remove_context import remove_context diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py index 02156daf0b..95a0929ab6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py @@ -17,168 +17,171 @@ # along with IfcOpenShell. If not, see . +def add_context(file, context_type=None, context_identifier=None, target_view=None, parent=None) -> None: + """Adds a new geometric representation context + + In IFC, physical objects may have zero, one, or multiple geometric + representations associated with it. For example, a building storey might + not have any geometry, but simply be a coordinate in space. + Alternatively, a wall might have a 3D body representation in the form of + a cuboid. As a final example, a door might also have a 3D body + representation of a 3D door panel and door frame, but may additionally + have a 2D door plan view representation of the door swing, and even a 2D + elevation view of the door, a 3D box representing the disabled clearance + zone of the door, a 2D profile representing the profile of the door to + cut out in a wall, and so on. In this situation, a door will have + multiple geometric representations. + + To distinguish between the different purposes of multiple geometric + representations, each geometric representation must belong to a + geometric representation "context". There are typically always 2 + contexts, one for 3D representations and one for 2D representations. + These 2 contexts then have subcontexts for things like the 3D body + representation, clearance representations, annotation representations, + and so on. Each representation of a physical IFC product (e.g. a door) + must be assigned to one of these subcontexts. Therefore setting up + appropriate contexts is critical prior to authoring any IFC model which + contains geometry. + + There are two steps to setting up appropriate subcontexts. First, a 2D + and/or 3D context must be added. These must be always called the "Model" + context for 3D and the "Plan" context for 2D (even if the 2D geometry is + not a plan view). Then, one or more subcontexts are added using either + the "Model" or "Plan" as their parent. These subcontexts are further + distinguished using an "identifier" and "target view". The "identifier" + describes the purpose of the representation, and the "target view" + describes the typical diagrammatic presentation that context's geometry + should be viewed in. The most common identifiers you might use are: + + - Body: for the actual shape of the object + - Box: the bounding box of the object (useful for shape analytics) + - Axis: the parametric line determining the shape of the object + - Profile: the elevation silhouette of the object, useful for cutting + out holes for the object to fit into host elements + - Footprint: the plan view silhouette of the object, useful for certain + quantity take-off rules + - Clearance: the clearance zone of the object + - Annotation: symbolic annotations typically used in diagrams or + drawings + + The most common "target views" you might use are: + + - MODEL_VIEW: for 3D geometry you might see in a BIM viewer + - PLAN_VIEW: for 2D geometry you might see in a plan representation + - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation + - SECTION_VIEW: for 2D geometry you might see in a section representation + - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams + you might use for structural frame analysis, axis-based parametric + modeling + - SKETCH_VIEW: for viewing abstract high-level representations such as + in bubble diagrams of spatial topology + + This may sound like a lot, but after a few typical contexts are set up + at the beginning, it becomes easy to navigate and isolate geometry for + different purposes. There is also the concept of a target scale, which + represents the zoom level detail of geometry, but this is not currently + supported by this API. Setting up all these contexts are also optional, + and you may only use a single Model context and Body subcontext for + simple models, but this simplification sacrifices the ability of more + parametric or analytical usecases. + + :param context_type: The type of the context, must be one of "Model" or + "Plan" only. + :type context_type: str + :param context_identifier: The identifier of the context, chosen from + one of the common identifiers above or consult the IFC documentation + (under the IfcShapeRepresentation page) for more details. Optional + for contexts, but mandatory for subcontexts. + :type context_identifier: str, optional + :param target_view: the target view of the context, chosen from one of + the common target views above or consult the IFC documentation + (under the IfcShapeRepresentation page) for more details. Optional + for contexts, but mandatory for subcontexts. + :type target_view: str, optional + :param parent: the parent context. Must be left as None (the default) + for contexts, and only set for subcontexts. Note that there are only + contexts and subcontexts, a subcontext cannot have any children. + :type parent: ifcopenshell.entity_instance, optional + :return: the newly created IfcGeometricRepresentationContext or + IfcGeometricRepresentationSubContext entity + :rtype: ifcopenshell.entity_instance, optional + + Example: + + .. code:: python + + # If we plan to store 3D geometry in our IFC model, we have to setup + # a "Model" context. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # And/Or, if we plan to store 2D geometry, we need a "Plan" context + plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan") + + # Now we setup the subcontexts with each of the geometric "purposes" + # we plan to store in our model. "Body" is by far the most important + # and common context, as most IFC models are assumed to be viewable + # in 3D. + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # The 3D Axis subcontext is important if any "axis-based" parametric + # geometry is going to be created. For example, a beam, or column + # may be drawn using a single 3D axis line, and for this we need an + # Axis subcontext. + ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d) + + # The 3D Box subcontext is useful for clash detection or shape + # analysis, or even lazy-loading of large models. + ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d) + + # It's also important to have a 2D Axis subcontext for things like + # walls and claddings which can be drawn using a 2D axis line. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan) + + # A 2D annotation subcontext for plan views are important for door + # swings, window cuts, and symbols for equipment like GPOs, fire + # extinguishers, and so on. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan) + + # You may also create 2D annotation subcontexts for sections and + # elevation views. + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan) + ifcopenshell.api.run("context.add_context", model, + context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context_type": context_type, + "parent": parent, + "context_identifier": context_identifier, + "target_view": target_view, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, context_type=None, context_identifier=None, target_view=None, parent=None): - """Adds a new geometric representation context - - In IFC, physical objects may have zero, one, or multiple geometric - representations associated with it. For example, a building storey might - not have any geometry, but simply be a coordinate in space. - Alternatively, a wall might have a 3D body representation in the form of - a cuboid. As a final example, a door might also have a 3D body - representation of a 3D door panel and door frame, but may additionally - have a 2D door plan view representation of the door swing, and even a 2D - elevation view of the door, a 3D box representing the disabled clearance - zone of the door, a 2D profile representing the profile of the door to - cut out in a wall, and so on. In this situation, a door will have - multiple geometric representations. - - To distinguish between the different purposes of multiple geometric - representations, each geometric representation must belong to a - geometric representation "context". There are typically always 2 - contexts, one for 3D representations and one for 2D representations. - These 2 contexts then have subcontexts for things like the 3D body - representation, clearance representations, annotation representations, - and so on. Each representation of a physical IFC product (e.g. a door) - must be assigned to one of these subcontexts. Therefore setting up - appropriate contexts is critical prior to authoring any IFC model which - contains geometry. - - There are two steps to setting up appropriate subcontexts. First, a 2D - and/or 3D context must be added. These must be always called the "Model" - context for 3D and the "Plan" context for 2D (even if the 2D geometry is - not a plan view). Then, one or more subcontexts are added using either - the "Model" or "Plan" as their parent. These subcontexts are further - distinguished using an "identifier" and "target view". The "identifier" - describes the purpose of the representation, and the "target view" - describes the typical diagrammatic presentation that context's geometry - should be viewed in. The most common identifiers you might use are: - - - Body: for the actual shape of the object - - Box: the bounding box of the object (useful for shape analytics) - - Axis: the parametric line determining the shape of the object - - Profile: the elevation silhouette of the object, useful for cutting - out holes for the object to fit into host elements - - Footprint: the plan view silhouette of the object, useful for certain - quantity take-off rules - - Clearance: the clearance zone of the object - - Annotation: symbolic annotations typically used in diagrams or - drawings - - The most common "target views" you might use are: - - - MODEL_VIEW: for 3D geometry you might see in a BIM viewer - - PLAN_VIEW: for 2D geometry you might see in a plan representation - - ELEVATION_VIEW: for 2D geometry you might see in an elevation representation - - SECTION_VIEW: for 2D geometry you might see in a section representation - - GRAPH_VIEW: for 2D or 3D line or frame or path connectivity diagrams - you might use for structural frame analysis, axis-based parametric - modeling - - SKETCH_VIEW: for viewing abstract high-level representations such as - in bubble diagrams of spatial topology - - This may sound like a lot, but after a few typical contexts are set up - at the beginning, it becomes easy to navigate and isolate geometry for - different purposes. There is also the concept of a target scale, which - represents the zoom level detail of geometry, but this is not currently - supported by this API. Setting up all these contexts are also optional, - and you may only use a single Model context and Body subcontext for - simple models, but this simplification sacrifices the ability of more - parametric or analytical usecases. - - :param context_type: The type of the context, must be one of "Model" or - "Plan" only. - :type context_type: str - :param context_identifier: The identifier of the context, chosen from - one of the common identifiers above or consult the IFC documentation - (under the IfcShapeRepresentation page) for more details. Optional - for contexts, but mandatory for subcontexts. - :type context_identifier: str, optional - :param target_view: the target view of the context, chosen from one of - the common target views above or consult the IFC documentation - (under the IfcShapeRepresentation page) for more details. Optional - for contexts, but mandatory for subcontexts. - :type target_view: str, optional - :param parent: the parent context. Must be left as None (the default) - for contexts, and only set for subcontexts. Note that there are only - contexts and subcontexts, a subcontext cannot have any children. - :type parent: ifcopenshell.entity_instance, optional - :return: the newly created IfcGeometricRepresentationContext or - IfcGeometricRepresentationSubContext entity - :rtype: ifcopenshell.entity_instance, optional - - Example: - - .. code:: python - - # If we plan to store 3D geometry in our IFC model, we have to setup - # a "Model" context. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # And/Or, if we plan to store 2D geometry, we need a "Plan" context - plan = ifcopenshell.api.run("context.add_context", model, context_type="Plan") - - # Now we setup the subcontexts with each of the geometric "purposes" - # we plan to store in our model. "Body" is by far the most important - # and common context, as most IFC models are assumed to be viewable - # in 3D. - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # The 3D Axis subcontext is important if any "axis-based" parametric - # geometry is going to be created. For example, a beam, or column - # may be drawn using a single 3D axis line, and for this we need an - # Axis subcontext. - ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=model3d) - - # The 3D Box subcontext is useful for clash detection or shape - # analysis, or even lazy-loading of large models. - ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model3d) - - # It's also important to have a 2D Axis subcontext for things like - # walls and claddings which can be drawn using a 2D axis line. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Axis", target_view="GRAPH_VIEW", parent=plan) - - # A 2D annotation subcontext for plan views are important for door - # swings, window cuts, and symbols for equipment like GPOs, fire - # extinguishers, and so on. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan) - - # You may also create 2D annotation subcontexts for sections and - # elevation views. - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="SECTION_VIEW", parent=plan) - ifcopenshell.api.run("context.add_context", model, - context_type="Plan", context_identifier="Annotation", target_view="ELEVATION_VIEW", parent=plan) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = { - "context_type": context_type, - "parent": parent, - "context_identifier": context_identifier, - "target_view": target_view, - } - def execute(self): if not self.settings["parent"]: if self.settings["context_type"] == "Plan": diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py index 50f4612c75..30f6d642b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/edit_context.py @@ -17,37 +17,34 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, context, attributes): - """Edits the attributes of an IfcGeometricRepresentationContext +def edit_context(file, context, attributes) -> None: + """Edits the attributes of an IfcGeometricRepresentationContext - For more information about the attributes and data types of an - IfcGeometricRepresentationContext, consult the IFC documentation. + For more information about the attributes and data types of an + IfcGeometricRepresentationContext, consult the IFC documentation. - :param context: The IfcGeometricRepresentationContext entity you want to edit - :type context: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param context: The IfcGeometricRepresentationContext entity you want to edit + :type context: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - # Revit had a bug where they incorrectly called the body representation a "Facetation" - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model - ) + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + # Revit had a bug where they incorrectly called the body representation a "Facetation" + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model + ) - # Let's fix it! - ifcopenshell.api.run("context.edit_context", model, - context=body, attributes={"ContextIdentifier": "Body"}) - """ - self.file = file - self.settings = {"context": context, "attributes": attributes or {}} + # Let's fix it! + ifcopenshell.api.run("context.edit_context", model, + context=body, attributes={"ContextIdentifier": "Body"}) + """ + settings = {"context": context, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["context"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["context"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py index b0025efbdb..9ac30483cf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py +++ b/src/ifcopenshell-python/ifcopenshell/api/context/remove_context.py @@ -19,49 +19,46 @@ import ifcopenshell -class Usecase: - def __init__(self, file, context=None): - """Removes an IfcGeometricRepresentationContext +def remove_context(file, context=None) -> None: + """Removes an IfcGeometricRepresentationContext - Any representation geometry that is assigned to the context is also - removed. If a context is removed, then any subcontexts are also removed. + Any representation geometry that is assigned to the context is also + removed. If a context is removed, then any subcontexts are also removed. - :param context: The IfcGeometricRepresentationContext entity to remove - :type context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param context: The IfcGeometricRepresentationContext entity to remove + :type context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - # Revit had a bug where they incorrectly called the body representation a "Facetation" - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model - ) + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + # Revit had a bug where they incorrectly called the body representation a "Facetation" + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Facetation", target_view="MODEL_VIEW", parent=model + ) - # Let's just get rid of it completely - ifcopenshell.api.run("context.remove_context", model, context=body) - """ - self.file = file - self.settings = {"context": context} + # Let's just get rid of it completely + ifcopenshell.api.run("context.remove_context", model, context=body) + """ + settings = {"context": context} - def execute(self): - for subcontext in self.settings["context"].HasSubContexts: - ifcopenshell.api.run("context.remove_context", self.file, context=subcontext) + for subcontext in settings["context"].HasSubContexts: + ifcopenshell.api.run("context.remove_context", file, context=subcontext) - if getattr(self.settings["context"], "ParentContext", None): - new = self.settings["context"].ParentContext - for inverse in self.file.get_inverse(self.settings["context"]): - if inverse.is_a("IfcCoordinateOperation"): - inverse.SourceCRS = inverse.TargetCRS - ifcopenshell.util.element.remove_deep(self.file, inverse) - else: - ifcopenshell.util.element.replace_attribute(inverse, self.settings["context"], new) - self.file.remove(self.settings["context"]) - else: - representations_in_context = self.settings["context"].RepresentationsInContext - self.file.remove(self.settings["context"]) - for element in representations_in_context: - ifcopenshell.api.run("geometry.remove_representation", self.file, representation=element) + if getattr(settings["context"], "ParentContext", None): + new = settings["context"].ParentContext + for inverse in file.get_inverse(settings["context"]): + if inverse.is_a("IfcCoordinateOperation"): + inverse.SourceCRS = inverse.TargetCRS + ifcopenshell.util.element.remove_deep(file, inverse) + else: + ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new) + file.remove(settings["context"]) + else: + representations_in_context = settings["context"].RepresentationsInContext + file.remove(settings["context"]) + for element in representations_in_context: + ifcopenshell.api.run("geometry.remove_representation", file, representation=element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py index e0caddbe3c..792f5eec35 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/__init__.py @@ -15,3 +15,6 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_control import assign_control +from .unassign_control import unassign_control diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py index 4d1ecb128d..93a4fe2f35 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -20,87 +20,81 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_control=None, related_object=None): - """Assigns a planning control or constraint to an object +def assign_control(file, relating_control=None, related_object=None) -> None: + """Assigns a planning control or constraint to an object - IFC can describe concepts that control other objects. For example, a - planning calendar controls the availability of working days for - construction planning. As another example, a cost item might constrain - or limit the ability to procure and build a product. + IFC can describe concepts that control other objects. For example, a + planning calendar controls the availability of working days for + construction planning. As another example, a cost item might constrain + or limit the ability to procure and build a product. - This usecase lets you assign controls following the rules of the IFC - specification. This is an advanced topic and assumes knowledge of the - IFC concepts to determine what is allowed to control what. In the - future, this API will likely be deprecated in favour of multiple usecase - specific APIs. + This usecase lets you assign controls following the rules of the IFC + specification. This is an advanced topic and assumes knowledge of the + IFC concepts to determine what is allowed to control what. In the + future, this API will likely be deprecated in favour of multiple usecase + specific APIs. - :param relating_control: The IfcControl entity that is creating the - control or constraint - :type relating_control: ifcopenshell.entity_instance - :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToControl. If relationship already - existed before and wasn't changed then returns None. - :rtype: ifcopenshell.entity_instance, None + :param relating_control: The IfcControl entity that is creating the + control or constraint + :type relating_control: ifcopenshell.entity_instance + :param related_object: The IfcObjectDefinition that is being controlled + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToControl. If relationship already + existed before and wasn't changed then returns None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # One common usecase is to assign a calendar to a task - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model) - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule) + # One common usecase is to assign a calendar to a task + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model) + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule) - # All subtasks will inherit this calendar, so assigning a single - # calendar to the root task effectively defines a "default" calendar - ifcopenshell.api.run("control.assign_control", model, - relating_control=calendar, related_object=task) + # All subtasks will inherit this calendar, so assigning a single + # calendar to the root task effectively defines a "default" calendar + ifcopenshell.api.run("control.assign_control", model, + relating_control=calendar, related_object=task) - # Another common example might be relating a cost item and a product - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - cost_item = ifcopenshell.api.run("cost.add_cost_item", model, - cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=wall) - """ - self.file = file - self.settings = { - "relating_control": relating_control, - "related_object": related_object, - } + # Another common example might be relating a cost item and a product + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + cost_item = ifcopenshell.api.run("cost.add_cost_item", model, + cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=wall) + """ + settings = { + "relating_control": relating_control, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToControl") - and assignment.RelatingControl == self.settings["relating_control"] - ): - return - - controls = None - if self.settings["relating_control"].Controls: - controls = self.settings["relating_control"].Controls[0] - - if controls: - if self.settings["related_object"] in controls.RelatedObjects: + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]: return - related_objects = set(controls.RelatedObjects) - related_objects.add(self.settings["related_object"]) - controls.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": controls}) - else: - controls = self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingControl": self.settings["relating_control"], - }, - ) - return controls + + controls = None + if settings["relating_control"].Controls: + controls = settings["relating_control"].Controls[0] + + if controls: + if settings["related_object"] in controls.RelatedObjects: + return + related_objects = set(controls.RelatedObjects) + related_objects.add(settings["related_object"]) + controls.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": controls}) + else: + controls = file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingControl": settings["relating_control"], + }, + ) + return controls diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py index 72996ad62a..0463689c5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py @@ -21,54 +21,51 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_control=None, related_object=None): - """Unassigns a planning control or constraint to an object +def unassign_control(file, relating_control=None, related_object=None) -> None: + """Unassigns a planning control or constraint to an object - :param relating_control: The IfcControl entity that is creating the - control or constraint - :type relating_control: ifcopenshell.entity_instance - :param related_object: The IfcObjectDefinition that is being controlled - :type related_object: ifcopenshell.entity_instance - :return: If the control still is related to other objects, the - IfcRelAssignsToControl is returned, otherwise None. - :rtype: ifcopenshell.entity_instance, None + :param relating_control: The IfcControl entity that is creating the + control or constraint + :type relating_control: ifcopenshell.entity_instance + :param related_object: The IfcObjectDefinition that is being controlled + :type related_object: ifcopenshell.entity_instance + :return: If the control still is related to other objects, the + IfcRelAssignsToControl is returned, otherwise None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # Let's relate a cost item and a product - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - cost_item = ifcopenshell.api.run("cost.add_cost_item", model, - cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=wall) + # Let's relate a cost item and a product + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + cost_item = ifcopenshell.api.run("cost.add_cost_item", model, + cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=wall) - # And now let's change our mind - ifcopenshell.api.run("control.unassign_control", model, - relating_control=cost_item, related_object=wall) - """ + # And now let's change our mind + ifcopenshell.api.run("control.unassign_control", model, + relating_control=cost_item, related_object=wall) + """ - self.file = file - self.settings = { - "relating_control": relating_control, - "related_object": related_object, - } + settings = { + "relating_control": relating_control, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != self.settings["relating_control"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py index e0caddbe3c..4cf5fc63c6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/__init__.py @@ -15,3 +15,23 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_cost_item import add_cost_item +from .add_cost_item_quantity import add_cost_item_quantity +from .add_cost_schedule import add_cost_schedule +from .add_cost_value import add_cost_value +from .assign_cost_item_quantity import assign_cost_item_quantity +from .assign_cost_value import assign_cost_value +from .calculate_cost_item_resource_value import calculate_cost_item_resource_value +from .copy_cost_item import copy_cost_item +from .copy_cost_item_values import copy_cost_item_values +from .edit_cost_item import edit_cost_item +from .edit_cost_item_quantity import edit_cost_item_quantity +from .edit_cost_schedule import edit_cost_schedule +from .edit_cost_value import edit_cost_value +from .edit_cost_value_formula import edit_cost_value_formula +from .remove_cost_item import remove_cost_item +from .remove_cost_item_quantity import remove_cost_item_quantity +from .remove_cost_schedule import remove_cost_schedule +from .remove_cost_value import remove_cost_value +from .unassign_cost_item_quantity import unassign_cost_item_quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py index 26a0bba442..f270446cfd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item.py @@ -19,55 +19,52 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_schedule=None, cost_item=None): - """Add a new cost item +def add_cost_item(file, cost_schedule=None, cost_item=None) -> None: + """Add a new cost item - A cost item represents a single line item in a cost schedule. Cost items - may then be broken down into cost subitems. + A cost item represents a single line item in a cost schedule. Cost items + may then be broken down into cost subitems. - :param cost_schedule: If the cost item is to be added as a root or top - level cost item to a cost schedule, the IfcCostSchedule may be - specified. This is mutually exlclusive to the cost_item parameter. - :type cost_schedule: ifcopenshell.entity_instance - :param cost_item: If the cost item is to be added as a subitem to an - existing cost item, the parent IfcCostItem may be specified. This is - mutually exclusive to the cost_schedule parameter. - :type cost_item: ifcopenshell.entity_instance - :return: The newly created IfcCostItem - :rtype: ifcopenshell.entity_instance + :param cost_schedule: If the cost item is to be added as a root or top + level cost item to a cost schedule, the IfcCostSchedule may be + specified. This is mutually exlclusive to the cost_item parameter. + :type cost_schedule: ifcopenshell.entity_instance + :param cost_item: If the cost item is to be added as a subitem to an + existing cost item, the parent IfcCostItem may be specified. This is + mutually exclusive to the cost_schedule parameter. + :type cost_item: ifcopenshell.entity_instance + :return: The newly created IfcCostItem + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # The very first cost item must be in a cost schedule - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # The very first cost item must be in a cost schedule + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # You may add cost items as top level item in the schedule - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # You may add cost items as top level item in the schedule + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Alternatively you may add them as subitems - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1) - """ - self.file = file - self.settings = {"cost_schedule": cost_schedule, "cost_item": cost_item} + # Alternatively you may add them as subitems + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_item=item1) + """ + settings = {"cost_schedule": cost_schedule, "cost_item": cost_item} - def execute(self): - cost_item = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCostItem") + cost_item = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcCostItem") - if self.settings["cost_schedule"]: - self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [cost_item], - "RelatingControl": self.settings["cost_schedule"], - } - ) - elif self.settings["cost_item"]: - ifcopenshell.api.run( - "nest.assign_object", self.file, related_objects=[cost_item], relating_object=self.settings["cost_item"] - ) - return cost_item + if settings["cost_schedule"]: + file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [cost_item], + "RelatingControl": settings["cost_schedule"], + } + ) + elif settings["cost_item"]: + ifcopenshell.api.run( + "nest.assign_object", file, related_objects=[cost_item], relating_object=settings["cost_item"] + ) + return cost_item diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index fabe443460..47a9b3efb9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -19,73 +19,70 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_item=None, ifc_class="IfcQuantityCount"): - """Adds a new quantity associated with a cost item +def add_cost_item_quantity(file, cost_item=None, ifc_class="IfcQuantityCount") -> None: + """Adds a new quantity associated with a cost item - Cost items calculate their subtotal by multiplying the sum of the cost - item's "values" by the sum of the cost item's "quantities". The - quantities may be either parametrically linked to quantities measured on - physical product, or manually specified. + Cost items calculate their subtotal by multiplying the sum of the cost + item's "values" by the sum of the cost item's "quantities". The + quantities may be either parametrically linked to quantities measured on + physical product, or manually specified. - The quantity must be of a particular type, common examples are: + The quantity must be of a particular type, common examples are: - - IfcQuantityCount: to count the total occurrences of a product, useful - for things like doors, windows, and furniture - - IfcQuantityNumber: any other generic numeric quantity - - IfcQuantityLength - - IfcQuantityArea - - IfcQuantityVolume - - IfcQuantityWeight - - IfcQuantityTime + - IfcQuantityCount: to count the total occurrences of a product, useful + for things like doors, windows, and furniture + - IfcQuantityNumber: any other generic numeric quantity + - IfcQuantityLength + - IfcQuantityArea + - IfcQuantityVolume + - IfcQuantityWeight + - IfcQuantityTime - A cost item must not mix quantities of different types. + A cost item must not mix quantities of different types. - If an IfcQuantityCount is used, then this API will automatically count - all products that this cost item controls (see - ifcopenshell.api.controls.assign_control) and prefill that quantity. + If an IfcQuantityCount is used, then this API will automatically count + all products that this cost item controls (see + ifcopenshell.api.controls.assign_control) and prefill that quantity. - For all other quantity types, the quantity is left as zero and the user - must either manually specify the quantity or parametrically link it - using another API call. + For all other quantity types, the quantity is left as zero and the user + must either manually specify the quantity or parametrically link it + using another API call. - :param cost_item: The IfcCostItem to add the quantity to - :type cost_item: ifcopenshell.entity_instance - :param ifc_class: The type of quantity to add - :type ifc_class: str, optional - :return: The newly created quantity entity, chosen from the ifc_class - parameter - :rtype: ifcopenshell.entity_instance + :param cost_item: The IfcCostItem to add the quantity to + :type cost_item: ifcopenshell.entity_instance + :param ifc_class: The type of quantity to add + :type ifc_class: str, optional + :return: The newly created quantity entity, chosen from the ifc_class + parameter + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("control.assign_control", model, - relating_control=cost_item, related_object=chair) + chair = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("control.assign_control", model, + relating_control=cost_item, related_object=chair) - # Let's assume we want to count the amount of chairs to calculate our cost item - # Because this is an IfcQuantityCount the count will be automatically set to "1" chair - ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityCount") - """ - self.file = file - self.settings = {"cost_item": cost_item, "ifc_class": ifc_class} + # Let's assume we want to count the amount of chairs to calculate our cost item + # Because this is an IfcQuantityCount the count will be automatically set to "1" chair + ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityCount") + """ + settings = {"cost_item": cost_item, "ifc_class": ifc_class} - def execute(self): - quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") - quantity[3] = 0.0 - # This is a bold assumption - # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 - if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls: - count = 0 - for rel in self.settings["cost_item"].Controls: - count += len(rel.RelatedObjects) - quantity[3] = count - quantities = list(self.settings["cost_item"].CostQuantities or []) - quantities.append(quantity) - self.settings["cost_item"].CostQuantities = quantities - return quantity + quantity = file.create_entity(settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if settings["ifc_class"] == "IfcQuantityCount" and settings["cost_item"].Controls: + count = 0 + for rel in settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count + quantities = list(settings["cost_item"].CostQuantities or []) + quantities.append(quantity) + settings["cost_item"].CostQuantities = quantities + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py index fa97a893f3..d72566ae1f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_schedule.py @@ -21,48 +21,45 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__(self, file, name=None, predefined_type="NOTDEFINED"): - """Add a new cost schedule +def add_cost_schedule(file, name=None, predefined_type="NOTDEFINED") -> None: + """Add a new cost schedule - A cost schedule is a group of cost items which typically represent a - cost plan or breakdown of the project. This may be used as an estimate, - bid, or actual cost. + A cost schedule is a group of cost items which typically represent a + cost plan or breakdown of the project. This may be used as an estimate, + bid, or actual cost. - Alternatively, a cost schedule may also represent a schedule of rates, - which include cost items which capture unit rates for different elements - or processes. + Alternatively, a cost schedule may also represent a schedule of rates, + which include cost items which capture unit rates for different elements + or processes. - As such, creating a cost schedule is necessary prior to creating and - managing any cost items. + As such, creating a cost schedule is necessary prior to creating and + managing any cost items. - :param name: The name of the cost schedule. - :type name: str, optional - :param predefined_type: The predefined type of the cost schedule, chosen - from a valid type in the IFC documentation for - IfcCostScheduleTypeEnum - :type predefined_type: str, optional - :return: The newly created IfcCostSchedule entity - :rtype: ifcopenshell.entity_instance + :param name: The name of the cost schedule. + :type name: str, optional + :param predefined_type: The predefined type of the cost schedule, chosen + from a valid type in the IFC documentation for + IfcCostScheduleTypeEnum + :type predefined_type: str, optional + :return: The newly created IfcCostSchedule entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # Now that we have a cost schedule, we may add cost items to it - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - """ - self.file = file - self.settings = {"name": name, "predefined_type": predefined_type} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # Now that we have a cost schedule, we may add cost items to it + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + """ + settings = {"name": name, "predefined_type": predefined_type} - def execute(self): - cost_schedule = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcCostSchedule", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") - return cost_schedule + cost_schedule = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcCostSchedule", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + cost_schedule.UpdateDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + return cost_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py index e7a481e8b2..b6fe5f5698 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_value.py @@ -17,95 +17,92 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None): - """Adds a new value or subvalue to a cost item +def add_cost_value(file, parent=None) -> None: + """Adds a new value or subvalue to a cost item - A cost item's subtotal can be specified in two ways. + A cost item's subtotal can be specified in two ways. - Option 1 is by simply manually specifying the subtotal value, which - represents the full cost of that cost item. This option occurs when a - cost item has no quantities associated with it. + Option 1 is by simply manually specifying the subtotal value, which + represents the full cost of that cost item. This option occurs when a + cost item has no quantities associated with it. - Option 2 is by specifying a unit cost value of the cost item, which is - then multiplied by the associated quantity of the cost item, to give us - the subtotal. This option occurs when a cost item has quantities - associated with it. + Option 2 is by specifying a unit cost value of the cost item, which is + then multiplied by the associated quantity of the cost item, to give us + the subtotal. This option occurs when a cost item has quantities + associated with it. - For either option 1 (full cost value) or option 2 (unit cost value), the - cost value may be specified as a single number, or as a sum of - subcomponents or formulas (e.g. multiplication by wastage factor, or - adding taxes or other adjustments). + For either option 1 (full cost value) or option 2 (unit cost value), the + cost value may be specified as a single number, or as a sum of + subcomponents or formulas (e.g. multiplication by wastage factor, or + adding taxes or other adjustments). - This function lets you add a single top level unit value to a cost item, - or alternatively price subcomponents by using the "parent" parameter. + This function lets you add a single top level unit value to a cost item, + or alternatively price subcomponents by using the "parent" parameter. - More advanced usage, which involves summing, subcategory-filtered costs, - and formulas are possible but not yet documented. + More advanced usage, which involves summing, subcategory-filtered costs, + and formulas are possible but not yet documented. - :param parent: A parent IfcCostItem, if specifying a price directly to a - cost item, or a top-level price component. Alternatively, this can - be set to a IfcCostValue, if specifying price subcomponents. - :type parent: ifcopenshell.entity_instance - :return: The newly created IfcCostValue - :rtype: ifcopenshell.entity_instance + :param parent: A parent IfcCostItem, if specifying a price directly to a + cost item, or a top-level price component. Alternatively, this can + be set to a IfcCostValue, if specifying price subcomponents. + :type parent: ifcopenshell.entity_instance + :return: The newly created IfcCostValue + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We always need a schedule first prior to adding any cost items - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + # We always need a schedule first prior to adding any cost items + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - # Option 1: This cost item will have a full cost of 42.0 - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) + # Option 1: This cost item will have a full cost of 42.0 + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) - # Option 2: This cost item will have a unit cost of 5.0 per unit - # area, multiplied by the quantity of area specified explicitly as - # 3.0, giving us a subtotal cost of 15.0. - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item2, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("cost.edit_cost_item_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) + # Option 2: This cost item will have a unit cost of 5.0 per unit + # area, multiplied by the quantity of area specified explicitly as + # 3.0, giving us a subtotal cost of 15.0. + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item2) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item2, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("cost.edit_cost_item_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) - # A cost value may also be specified in terms of the sum of its - # subcomponents. In this case, it's broken down into 2 subvalues. - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) - subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) - subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) + # A cost value may also be specified in terms of the sum of its + # subcomponents. In this case, it's broken down into 2 subvalues. + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item1) + subvalue1 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) + subvalue2 = ifcopenshell.api.run("cost.add_cost_value", model, parent=value) - # This specifies that the value is the sum of all subitems - # regardless of their cost category. The first subvalue is 2.0 and - # the second is 3.0, giving a total value of 5.0. - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"}) - ifcopenshell.api.run("cost.edit_cost_value", model, - cost_value=subvalue1, attributes={"AppliedValue": 2.0}) - ifcopenshell.api.run("cost.edit_cost_value", model, - cost_value=subvalue2, attributes={"AppliedValue": 3.0}) - """ - self.file = file - self.settings = {"parent": parent} + # This specifies that the value is the sum of all subitems + # regardless of their cost category. The first subvalue is 2.0 and + # the second is 3.0, giving a total value of 5.0. + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, attributes={"Category": "*"}) + ifcopenshell.api.run("cost.edit_cost_value", model, + cost_value=subvalue1, attributes={"AppliedValue": 2.0}) + ifcopenshell.api.run("cost.edit_cost_value", model, + cost_value=subvalue2, attributes={"AppliedValue": 3.0}) + """ + settings = {"parent": parent} - def execute(self): - value = self.file.create_entity("IfcCostValue") - if self.settings["parent"].is_a("IfcCostItem"): - values = list(self.settings["parent"].CostValues or []) - values.append(value) - self.settings["parent"].CostValues = values - elif self.settings["parent"].is_a("IfcConstructionResource"): - values = list(self.settings["parent"].BaseCosts or []) - values.append(value) - self.settings["parent"].BaseCosts = values - elif self.settings["parent"].is_a("IfcCostValue"): - values = list(self.settings["parent"].Components or []) - values.append(value) - self.settings["parent"].Components = values - return value + value = file.create_entity("IfcCostValue") + if settings["parent"].is_a("IfcCostItem"): + values = list(settings["parent"].CostValues or []) + values.append(value) + settings["parent"].CostValues = values + elif settings["parent"].is_a("IfcConstructionResource"): + values = list(settings["parent"].BaseCosts or []) + values.append(value) + settings["parent"].BaseCosts = values + elif settings["parent"].is_a("IfcCostValue"): + values = list(settings["parent"].Components or []) + values.append(value) + settings["parent"].Components = values + return value diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index d4c6b6be69..6c13162ed7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -19,82 +19,82 @@ import ifcopenshell.api +def assign_cost_item_quantity(file, cost_item=None, products=None, prop_name="") -> None: + """Adds a cost item quantity that is parametrically connected to a product + + A cost item may have its subtotal calculated by multiplying a unit value + by a quantity associated with the cost item. That quantity may be either + manually specified or parametrically connected to a quantity on a + product. This API function lets you create that parametric connection. + + For example, you may wish to have a cost item linked to the "NetVolume" + quantity on all IfcSlabs. Each quantity has a name which you can + specify. If the quantity is updated in-place (which should occur for + Native IFC applications) then the quantity for the cost item will + automatically update as well. If the quantity is deleted and then + re-added, then the parametric relationship is also lost. + + This API also automatically assigns a control relationship between the + cost item and the product, so it is not necessary to use + ifcopenshell.api.control.assign_control. + + :param cost_item: The IfcCostItem to assign parametric quantities to + :type cost_item: ifcopenshell.entity_instance + :param products: The IfcObjects to assign parametric quantities to + :type products: list[ifcopenshell.entity_instance] + :param prop_name: The name of the quantity. If this is not specified, + then it is assumed that there is no calculated quantity, and the + number of objects are counted instead. + :type prop_name: str, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + # Let's imagine a unit cost of 5.0 per unit volume + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + + slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") + # Usually the quantity would be automatically calculated via a + # graphical authoring application but let's assign a manual quantity + # for now. + qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) + + # Now let's parametrically link the slab's quantity to the cost + # item. If the slab is edited in the future and 42.0 changes, then + # the updated value will also automatically be applied to the cost + # item. + ifcopenshell.api.run("cost.assign_cost_item_quantity", model, + cost_item=item, products=[slab], prop_name="NetVolume") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "cost_item": cost_item, + "products": products or [], + "prop_name": prop_name, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None, products=None, prop_name=""): - """Adds a cost item quantity that is parametrically connected to a product - - A cost item may have its subtotal calculated by multiplying a unit value - by a quantity associated with the cost item. That quantity may be either - manually specified or parametrically connected to a quantity on a - product. This API function lets you create that parametric connection. - - For example, you may wish to have a cost item linked to the "NetVolume" - quantity on all IfcSlabs. Each quantity has a name which you can - specify. If the quantity is updated in-place (which should occur for - Native IFC applications) then the quantity for the cost item will - automatically update as well. If the quantity is deleted and then - re-added, then the parametric relationship is also lost. - - This API also automatically assigns a control relationship between the - cost item and the product, so it is not necessary to use - ifcopenshell.api.control.assign_control. - - :param cost_item: The IfcCostItem to assign parametric quantities to - :type cost_item: ifcopenshell.entity_instance - :param products: The IfcObjects to assign parametric quantities to - :type products: list[ifcopenshell.entity_instance] - :param prop_name: The name of the quantity. If this is not specified, - then it is assumed that there is no calculated quantity, and the - number of objects are counted instead. - :type prop_name: str, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - # Let's imagine a unit cost of 5.0 per unit volume - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - - slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") - # Usually the quantity would be automatically calculated via a - # graphical authoring application but let's assign a manual quantity - # for now. - qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) - - # Now let's parametrically link the slab's quantity to the cost - # item. If the slab is edited in the future and 42.0 changes, then - # the updated value will also automatically be applied to the cost - # item. - ifcopenshell.api.run("cost.assign_cost_item_quantity", model, - cost_item=item, products=[slab], prop_name="NetVolume") - """ - self.file = file - self.settings = { - "cost_item": cost_item, - "products": products or [], - "prop_name": prop_name, - } - def execute(self): if self.settings["prop_name"]: self.quantities = set(self.settings["cost_item"].CostQuantities or []) for product in self.settings["products"]: - self.assign_cost_control( - related_object=product, cost_item=self.settings["cost_item"] - ) + self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"]) if self.settings["prop_name"]: if ( self.settings["cost_item"].CostQuantities - and self.settings["cost_item"].CostQuantities[0].Name.lower() - != self.settings["prop_name"].lower() + and self.settings["cost_item"].CostQuantities[0].Name.lower() != self.settings["prop_name"].lower() ) or not product.is_a("IfcObject"): continue self.add_quantity_from_related_object(product) @@ -120,10 +120,7 @@ class Usecase: if not qto.is_a("IfcElementQuantity"): return for prop in qto.Quantities: - if ( - prop.is_a("IfcPhysicalSimpleQuantity") - and prop.Name.lower() == self.settings["prop_name"].lower() - ): + if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): self.quantities.add(prop) def update_cost_item_count(self): diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py index fb89fe7432..18bb05694f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_value.py @@ -19,60 +19,57 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, cost_item=None, cost_rate=None): - """Assigns a cost value to a cost item from a schedule of rates +def assign_cost_value(file, cost_item=None, cost_rate=None) -> None: + """Assigns a cost value to a cost item from a schedule of rates - Instead of assigning cost values from scratch for each cost item in a - cost schedule, the cost values may instead be assigned from a schedule - of rates. + Instead of assigning cost values from scratch for each cost item in a + cost schedule, the cost values may instead be assigned from a schedule + of rates. - A schedule of rates is just another cost schedule which have cost values - but no quantities. This API will allow you to "copy" the values from a - cost item in the schedule of rates into another cost item in your own - cost schedule. When the schedule of rates value is updated, then your - cost item values will also be updated. You can think of the schedule of - rates as a "template" to quickly populate your rates from. + A schedule of rates is just another cost schedule which have cost values + but no quantities. This API will allow you to "copy" the values from a + cost item in the schedule of rates into another cost item in your own + cost schedule. When the schedule of rates value is updated, then your + cost item values will also be updated. You can think of the schedule of + rates as a "template" to quickly populate your rates from. - :param cost_item: The IfcCostItem that you want to copy the values to - :type cost_item: ifcopenshell.entity_instance - :param cost_rate: The IfcCostItem that you want to copy the values from - :type cost_rate: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem that you want to copy the values to + :type cost_item: ifcopenshell.entity_instance + :param cost_rate: The IfcCostItem that you want to copy the values from + :type cost_rate: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a schedule of rates with a single rate in it of 5.0 - rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model, - predefined_type="SCHEDULEOFRATES") - rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) + # Let's create a schedule of rates with a single rate in it of 5.0 + rate_tables = ifcopenshell.api.run("cost.add_cost_schedule", model, + predefined_type="SCHEDULEOFRATES") + rate = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=rate) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) - # And this schedule will be for our actual cost plan / estimate / etc - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # And this schedule will be for our actual cost plan / estimate / etc + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Now the cost item has the same rate as the one from the schedule of rate's item - ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate) - """ - self.file = file - self.settings = {"cost_item": cost_item, "cost_rate": cost_rate} + # Now the cost item has the same rate as the one from the schedule of rate's item + ifcopenshell.api.run("cost.assign_cost_value", model, cost_item=item, cost_rate=rate) + """ + settings = {"cost_item": cost_item, "cost_rate": cost_rate} - def execute(self): - if self.settings["cost_item"].CostValues: - [ - ifcopenshell.api.run( - "cost.remove_cost_value", - self.file, - parent=self.settings["cost_item"], - cost_value=cost_value, - ) - for cost_value in self.settings["cost_item"].CostValues - ] - # This is an assumption, and not part of the official IFC documentation - self.settings["cost_item"].CostValues = self.settings["cost_rate"].CostValues + if settings["cost_item"].CostValues: + [ + ifcopenshell.api.run( + "cost.remove_cost_value", + file, + parent=settings["cost_item"], + cost_value=cost_value, + ) + for cost_value in settings["cost_item"].CostValues + ] + # This is an assumption, and not part of the official IFC documentation + settings["cost_item"].CostValues = settings["cost_rate"].CostValues diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py index b82e5948a6..c977be1b19 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/calculate_cost_item_resource_value.py @@ -21,100 +21,97 @@ import ifcopenshell.util.date import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, cost_item=None): - """Calculates the total cost of all resources associated with a cost item +def calculate_cost_item_resource_value(file, cost_item=None) -> None: + """Calculates the total cost of all resources associated with a cost item - A cost item may have construction resources (e.g. equipment, material, - etc) assigned to it. Construction resources may be assigned directly to - the cost item, or assigned first to a task, and the task is then - assigned to the cost item. + A cost item may have construction resources (e.g. equipment, material, + etc) assigned to it. Construction resources may be assigned directly to + the cost item, or assigned first to a task, and the task is then + assigned to the cost item. - The cost of a resource is calculated by the total sum of all of its base - costs. If no quantity is provided, that sum is considered to be the - total cost. Otherwise, it is considered to be a unit cost, and is then - multiplied by the resource quantity. The quantity is either stored as a - base quantity (such as a volume) for a things like material resources, - or as a duration as a daily rate for labour resources. + The cost of a resource is calculated by the total sum of all of its base + costs. If no quantity is provided, that sum is considered to be the + total cost. Otherwise, it is considered to be a unit cost, and is then + multiplied by the resource quantity. The quantity is either stored as a + base quantity (such as a volume) for a things like material resources, + or as a duration as a daily rate for labour resources. - The final calculated cost is set as the cost item's value. Any - previously existing values are removed. + The final calculated cost is set as the cost item's value. Any + previously existing values are removed. - :param cost_item: The IfcCostItem to calculate - :type cost_item: ifccopenshell.entity_instance.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem to calculate + :type cost_item: ifccopenshell.entity_instance.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # First, we need a cost schedule and item - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # First, we need a cost schedule and item + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # Let's imagine we have our own formworking crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Let's imagine we have our own formworking crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # ... and they need concrete - concrete = ifcopenshell.api.run("resource.add_resource", model, - ifc_class="IfcConstructionMaterialResource", parent_resource=crew) - ifcopenshell.api.run("control.assign_control", model, - relating_control=item, related_object=concrete) - # ... which has a unit price of 42.0 per m3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) - # ... and a volume of 200m3 - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=concrete, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 200.0}) + # ... and they need concrete + concrete = ifcopenshell.api.run("resource.add_resource", model, + ifc_class="IfcConstructionMaterialResource", parent_resource=crew) + ifcopenshell.api.run("control.assign_control", model, + relating_control=item, related_object=concrete) + # ... which has a unit price of 42.0 per m3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) + # ... and a volume of 200m3 + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=concrete, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 200.0}) - # Let's say they also need some equipment - equipment = ifcopenshell.api.run("resource.add_resource", model, - ifc_class="IfcConstructionEquipmentResource", parent_resource=crew) - ifcopenshell.api.run("control.assign_control", model, - relating_control=item, related_object=equipment) - # ... with a fixed price of 50,000 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) + # Let's say they also need some equipment + equipment = ifcopenshell.api.run("resource.add_resource", model, + ifc_class="IfcConstructionEquipmentResource", parent_resource=crew) + ifcopenshell.api.run("control.assign_control", model, + relating_control=item, related_object=equipment) + # ... with a fixed price of 50,000 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=concrete) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) - # (42 * 200) + 50000 = 58400 is our calculated cost - ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item) - """ - self.file = file - self.settings = {"cost_item": cost_item} + # (42 * 200) + 50000 = 58400 is our calculated cost + ifcopenshell.api.run("cost.calculate_cost_item_resource_value", model, cost_item=item) + """ + settings = {"cost_item": cost_item} - def execute(self): - for cost_value in self.settings["cost_item"].CostValues or []: - ifcopenshell.api.run( - "cost.remove_cost_value", self.file, parent=self.settings["cost_item"], cost_value=cost_value - ) + for cost_value in settings["cost_item"].CostValues or []: + ifcopenshell.api.run("cost.remove_cost_value", file, parent=settings["cost_item"], cost_value=cost_value) - resources = [] - for rel in self.settings["cost_item"].Controls or []: - for related_object in rel.RelatedObjects: - if related_object.is_a("IfcConstructionResource"): - resources.append(related_object) - elif related_object.is_a("IfcTask"): - for rel2 in related_object.OperatesOn or []: - for related_object2 in rel2.RelatedObjects: - if related_object2.is_a("IfcConstructionResource"): - resources.append(related_object2) + resources = [] + for rel in settings["cost_item"].Controls or []: + for related_object in rel.RelatedObjects: + if related_object.is_a("IfcConstructionResource"): + resources.append(related_object) + elif related_object.is_a("IfcTask"): + for rel2 in related_object.OperatesOn or []: + for related_object2 in rel2.RelatedObjects: + if related_object2.is_a("IfcConstructionResource"): + resources.append(related_object2) - for resource in resources: - cost, unit = ifcopenshell.util.resource.get_cost(resource) - if not cost: - cost, unit = ifcopenshell.util.resource.get_parent_cost(resource) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data. - quantity = ifcopenshell.util.resource.get_quantity(resource) - if not cost or not quantity: - continue - if unit and "day" in unit: - quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar - quantity = round(quantity, 2) - formula = "{}*{}".format(cost, quantity) - cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.settings["cost_item"]) - cost_value.Name = resource.Name - ifcopenshell.api.run("cost.edit_cost_value_formula", self.file, cost_value=cost_value, formula=formula) \ No newline at end of file + for resource in resources: + cost, unit = ifcopenshell.util.resource.get_cost(resource) + if not cost: + cost, unit = ifcopenshell.util.resource.get_parent_cost( + resource + ) # Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data. + quantity = ifcopenshell.util.resource.get_quantity(resource) + if not cost or not quantity: + continue + if unit and "day" in unit: + quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar + quantity = round(quantity, 2) + formula = "{}*{}".format(cost, quantity) + cost_value = ifcopenshell.api.run("cost.add_cost_value", file, parent=settings["cost_item"]) + cost_value.Name = resource.Name + ifcopenshell.api.run("cost.edit_cost_value_formula", file, cost_value=cost_value, formula=formula) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py index ec2d147731..13927088ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item.py @@ -21,35 +21,38 @@ import ifcopenshell.api import ifcopenshell.util.element +def copy_cost_item(file, cost_item=None) -> None: + """Copies all cost items and related relationships + + The following relationships are also duplicated: + + * The copy will have the same attributes and property sets as the original cost item + * The copy will be assigned to the parent cost schedule + * The copy will have duplicated nested cost items + + :param cost_item: The cost item to be duplicated + :type cost_item: ifcopenshell.entity_instance + :return: The duplicated cost item or the list of duplicated cost items if the latter has children + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a cost item + cost_item = CostItem(name="Design new feature", deadline="2023-03-01") + + # And now we have two + duplicated_cost_item = project.duplicate_cost_item(cost_item) + + + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_item": cost_item} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None): - """Copies all cost items and related relationships - - The following relationships are also duplicated: - - * The copy will have the same attributes and property sets as the original cost item - * The copy will be assigned to the parent cost schedule - * The copy will have duplicated nested cost items - - :param cost_item: The cost item to be duplicated - :type cost_item: ifcopenshell.entity_instance - :return: The duplicated cost item or the list of duplicated cost items if the latter has children - :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a cost item - cost_item = CostItem(name="Design new feature", deadline="2023-03-01") - - # And now we have two - duplicated_cost_item = project.duplicate_cost_item(cost_item) - - - """ - self.file = file - self.settings = {"cost_item": cost_item} - def execute(self): self.new_cost_items = [] self.duplicate_cost_item(self.settings["cost_item"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py index 6bc0677b28..8ccb0a4158 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/copy_cost_item_values.py @@ -20,45 +20,42 @@ import ifcopenshell.util.element import ifcopenshell.api -class Usecase: - def __init__(self, file, source=None, destination=None): - """Copies all cost values from one cost item to another +def copy_cost_item_values(file, source=None, destination=None) -> None: + """Copies all cost values from one cost item to another - Any previously existing values will be removed. The entire value is - copied, including all components and formulas. However they are not - parametrically linked, so if one value changes, the other will not. + Any previously existing values will be removed. The entire value is + copied, including all components and formulas. However they are not + parametrically linked, so if one value changes, the other will not. - :param source: The IfcCostItem to copy cost values from - :type source: ifcopenshell.entity_instance - :param destination: The IfcCostItem to copy cost values from - :type destination: ifcopenshell.entity_instance - :return: None - :rtype: None + :param source: The IfcCostItem to copy cost values from + :type source: ifcopenshell.entity_instance + :param destination: The IfcCostItem to copy cost values from + :type destination: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Assume we have a schedule with multiple items in it - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + # Assume we have a schedule with multiple items in it + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item1 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + item2 = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # One of the items has a value - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5000.0}) + # One of the items has a value + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5000.0}) - # Let's copy the value from one item to another - ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2) - """ - self.file = file - self.settings = {"source": source, "destination": destination} + # Let's copy the value from one item to another + ifcopenshell.api.run("cost.copy_cost_item_values", model, source=item1, destination=item2) + """ + settings = {"source": source, "destination": destination} - def execute(self): - for cost_value in self.settings["destination"].CostValues or []: - ifcopenshell.api.run("cost.remove_cost_item_value", self.file, cost_value=cost_value) - copied_cost_values = [] - for cost_value in self.settings["source"].CostValues or []: - copied_cost_values.append(ifcopenshell.util.element.copy_deep(self.file, cost_value)) - self.settings["destination"].CostValues = copied_cost_values + for cost_value in settings["destination"].CostValues or []: + ifcopenshell.api.run("cost.remove_cost_item_value", file, cost_value=cost_value) + copied_cost_values = [] + for cost_value in settings["source"].CostValues or []: + copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value)) + settings["destination"].CostValues = copied_cost_values diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py index cc0a187177..2bf72a5d57 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_item=None, attributes=None): - """Edits the attributes of an IfcCostItem +def edit_cost_item(file, cost_item=None, attributes=None) -> None: + """Edits the attributes of an IfcCostItem - For more information about the attributes and data types of an - IfcCostItem, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostItem, consult the IFC documentation. - :param cost_item: The IfcCostItem entity you want to edit - :type cost_item: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_item: The IfcCostItem entity you want to edit + :type cost_item: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"cost_item": cost_item, "attributes": attributes or {}} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.edit_cost_item", model, cost_item=item, attributes={"Name": "Foo"}) + """ + settings = {"cost_item": cost_item, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["cost_item"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["cost_item"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py index 3ba4e9f498..178816a593 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item_quantity.py @@ -17,39 +17,36 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, physical_quantity=None, attributes=None): - """Edits the attributes of an IfcPhysicalQuantity +def edit_cost_item_quantity(file, physical_quantity=None, attributes=None) -> None: + """Edits the attributes of an IfcPhysicalQuantity - For more information about the attributes and data types of an - IfcPhysicalQuantity, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPhysicalQuantity, consult the IFC documentation. - :param physical_quantity: The IfcPhysicalQuantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param physical_quantity: The IfcPhysicalQuantity entity you want to edit + :type physical_quantity: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a unit cost of 5 and a volume of 3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityVolume") - ifcopenshell.api.run("cost.edit_cost_item_quantity", model, - physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) - """ - self.file = file - self.settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}} + # This cost item will have a unit cost of 5 and a volume of 3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityVolume") + ifcopenshell.api.run("cost.edit_cost_item_quantity", model, + physical_quantity=quantity, "attributes": {"VolumeValue": 3.0}) + """ + settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["physical_quantity"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py index bdfb856cc1..3e47f3a430 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_schedule.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_schedule=None, attributes=None): - """Edits the attributes of an IfcCostSchedule +def edit_cost_schedule(file, cost_schedule=None, attributes=None) -> None: + """Edits the attributes of an IfcCostSchedule - For more information about the attributes and data types of an - IfcCostSchedule, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostSchedule, consult the IFC documentation. - :param cost_schedule: The IfcCostSchedule entity you want to edit - :type cost_schedule: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_schedule: The IfcCostSchedule entity you want to edit + :type cost_schedule: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - ifcopenshell.api.run("cost.edit_cost_schedule", model, - cost_schedule=schedule, attributes={"Name": "Foo"}) - """ + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + ifcopenshell.api.run("cost.edit_cost_schedule", model, + cost_schedule=schedule, attributes={"Name": "Foo"}) + """ - self.file = file - self.settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}} + settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["cost_schedule"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["cost_schedule"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py index 75ce055eb1..430b4272aa 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value.py @@ -21,48 +21,45 @@ import ifcopenshell.util.unit import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_value=None, attributes=None): - """Edits the attributes of an IfcCostValue +def edit_cost_value(file, cost_value=None, attributes=None) -> None: + """Edits the attributes of an IfcCostValue - For more information about the attributes and data types of an - IfcCostValue, consult the IFC documentation. + For more information about the attributes and data types of an + IfcCostValue, consult the IFC documentation. - :param cost_value: The IfcCostValue entity you want to edit - :type cost_value: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param cost_value: The IfcCostValue entity you want to edit + :type cost_value: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a total cost of 42 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 42.0}) - """ - self.file = file - self.settings = {"cost_value": cost_value, "attributes": attributes or {}} + # This cost item will have a total cost of 42 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 42.0}) + """ + settings = {"cost_value": cost_value, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "AppliedValue" and value is not None: - # TODO: support all applied value select types - value = self.file.createIfcMonetaryMeasure(value) - elif name == "UnitBasis": - old_unit_basis = self.settings["cost_value"].UnitBasis - if value: - value_component = self.file.create_entity( - ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), - value["ValueComponent"], - ) - value = self.file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"]) - if old_unit_basis and len(self.file.get_inverse(old_unit_basis)) == 0: - ifcopenshell.util.element.remove_deep(self.file, old_unit_basis) - setattr(self.settings["cost_value"], name, value) + for name, value in settings["attributes"].items(): + if name == "AppliedValue" and value is not None: + # TODO: support all applied value select types + value = file.createIfcMonetaryMeasure(value) + elif name == "UnitBasis": + old_unit_basis = settings["cost_value"].UnitBasis + if value: + value_component = file.create_entity( + ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType), + value["ValueComponent"], + ) + value = file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"]) + if old_unit_basis and len(file.get_inverse(old_unit_basis)) == 0: + ifcopenshell.util.element.remove_deep(file, old_unit_basis) + setattr(settings["cost_value"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py index 8dada5dc98..eac443ba40 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py @@ -22,37 +22,40 @@ import ifcopenshell.util.unit import ifcopenshell.util.element +def edit_cost_value_formula(file, cost_value=None, formula=None) -> None: + """Sets a cost value based on a formula, similar to formulas in spreadsheets + + Costs may be made up of many components (e.g. labour, material, waste + factor, taxes, etc). This can be easily represented in the form of a + formula similar thta would be used in spreadsheet applications. + + For more information, see ifcopenshell.util.cost + + :param cost_value: The IfcCostValue to set the values of + :type cost_value: ifcopenshell.entity_instance + :param formula: The formula following the language of ifcopenshell.util.cost + :type formula: str + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value, + formula="5000 * 1.19") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_value": cost_value, "formula": formula or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_value=None, formula=None): - """Sets a cost value based on a formula, similar to formulas in spreadsheets - - Costs may be made up of many components (e.g. labour, material, waste - factor, taxes, etc). This can be easily represented in the form of a - formula similar thta would be used in spreadsheet applications. - - For more information, see ifcopenshell.util.cost - - :param cost_value: The IfcCostValue to set the values of - :type cost_value: ifcopenshell.entity_instance - :param formula: The formula following the language of ifcopenshell.util.cost - :type formula: str - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value_formula", model, cost_value=value, - formula="5000 * 1.19") - """ - self.file = file - self.settings = {"cost_value": cost_value, "formula": formula or {}} - def execute(self): try: data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py index 5596ceff13..e52fd655cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item.py @@ -21,48 +21,45 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_item=None): - """Removes a cost item +def remove_cost_item(file, cost_item=None) -> None: + """Removes a cost item - All associated relationships with the cost item are also removed, - however the related resources, products, and tasks themselves are - retained. + All associated relationships with the cost item are also removed, + however the related resources, products, and tasks themselves are + retained. - :param cost_item: The IfcCostItem entity you want to remove - :type cost_item: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem entity you want to remove + :type cost_item: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item) - """ - self.file = file - self.settings = {"cost_item": cost_item} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.remove_cost_item", model, cost_item=item) + """ + settings = {"cost_item": cost_item} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["cost_item"]): - if inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["cost_item"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run("cost.remove_cost_item", self.file, cost_item=related_object) - elif inverse.RelatedObjects == (self.settings["cost_item"],): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToControl"): + # TODO: do a deep purge + for inverse in file.get_inverse(settings["cost_item"]): + if inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["cost_item"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object) + elif inverse.RelatedObjects == (settings["cost_item"],): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["cost_item"].OwnerHistory - self.file.remove(self.settings["cost_item"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToControl"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["cost_item"].OwnerHistory + file.remove(settings["cost_item"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py index fae8e1cd37..eed3a7adb3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_item_quantity.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, cost_item=None, physical_quantity=None): - """Removes a quantity assigned to a cost item +def remove_cost_item_quantity(file, cost_item=None, physical_quantity=None) -> None: + """Removes a quantity assigned to a cost item - If the quantity is part of a product (e.g. wall), then the quantity will - still exist and merely the relationship to the cost item will be - removed. + If the quantity is part of a product (e.g. wall), then the quantity will + still exist and merely the relationship to the cost item will be + removed. - :param cost_item: The IfcCostItem that the quantity is assigned to - :type cost_item: ifcopenshell.entity_instance - :param physical_quantity: The IfcPhysicalQuantity to remove - :type physical_quantity: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_item: The IfcCostItem that the quantity is assigned to + :type cost_item: ifcopenshell.entity_instance + :param physical_quantity: The IfcPhysicalQuantity to remove + :type physical_quantity: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, - cost_item=item, ifc_class="IfcQuantityVolume") - # Let's change our mind and delete it - ifcopenshell.api.run("cost.remove_cost_item", model, - cost_item=item, physical_quantity=quantity) - """ - self.file = file - self.settings = {"cost_item": cost_item, "physical_quantity": physical_quantity} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + quantity = ifcopenshell.api.run("cost.add_cost_item_quantity", model, + cost_item=item, ifc_class="IfcQuantityVolume") + # Let's change our mind and delete it + ifcopenshell.api.run("cost.remove_cost_item", model, + cost_item=item, physical_quantity=quantity) + """ + settings = {"cost_item": cost_item, "physical_quantity": physical_quantity} - def execute(self): - if len(self.file.get_inverse(self.settings["physical_quantity"])) == 1: - self.file.remove(self.settings["physical_quantity"]) - return - quantities = list(self.settings["cost_item"].CostQuantities or []) - quantities.remove(self.settings["physical_quantity"]) - self.settings["cost_item"].CostQuantities = quantities + if len(file.get_inverse(settings["physical_quantity"])) == 1: + file.remove(settings["physical_quantity"]) + return + quantities = list(settings["cost_item"].CostQuantities or []) + quantities.remove(settings["physical_quantity"]) + settings["cost_item"].CostQuantities = quantities diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py index 51feebb76e..7b73859bb0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_schedule.py @@ -21,41 +21,36 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, cost_schedule=None): - """Removes a cost schedule +def remove_cost_schedule(file, cost_schedule=None) -> None: + """Removes a cost schedule - All associated relationships with the cost schedule are also removed, - including all cost items. + All associated relationships with the cost schedule are also removed, + including all cost items. - :param cost_schedule: The IfcCostSchedule entity you want to remove - :type cost_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None + :param cost_schedule: The IfcCostSchedule entity you want to remove + :type cost_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule) - """ - self.file = file - self.settings = {"cost_schedule": cost_schedule} + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + ifcopenshell.api.run("cost.remove_cost_schedule", model, cost_schedule=schedule) + """ + settings = {"cost_schedule": cost_schedule} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["cost_schedule"]): - if inverse.is_a("IfcRelAssignsToControl"): - [ - ifcopenshell.api.run( - "cost.remove_cost_item", self.file, cost_item=related_object - ) - for related_object in inverse.RelatedObjects - if related_object.is_a("IfcCostItem") - ] - history = self.settings["cost_schedule"].OwnerHistory - self.file.remove(self.settings["cost_schedule"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for inverse in file.get_inverse(settings["cost_schedule"]): + if inverse.is_a("IfcRelAssignsToControl"): + [ + ifcopenshell.api.run("cost.remove_cost_item", file, cost_item=related_object) + for related_object in inverse.RelatedObjects + if related_object.is_a("IfcCostItem") + ] + history = settings["cost_schedule"].OwnerHistory + file.remove(settings["cost_schedule"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py index 4af7322899..757877bf9d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/remove_cost_value.py @@ -17,51 +17,48 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None, cost_value=None): - """Removes a cost value +def remove_cost_value(file, parent=None, cost_value=None) -> None: + """Removes a cost value - The cost value may be assigned either to a cost item, a construction - resource, or another cost value (i.e. it is a subcomponent of a cost) + The cost value may be assigned either to a cost item, a construction + resource, or another cost value (i.e. it is a subcomponent of a cost) - :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue - that the IfcCostValue is assigned to. - :type parent: ifcopenshell.entity_instance - :param cost_value: The IfcCostValue that you want to remove - :type parent: ifcopenshell.entity_instance - :return: None - :rtype: None + :param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue + that the IfcCostValue is assigned to. + :type parent: ifcopenshell.entity_instance + :param cost_value: The IfcCostValue that you want to remove + :type parent: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - # This cost item will have a unit cost of 5 and a volume of 3 - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) + # This cost item will have a unit cost of 5 and a volume of 3 + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) - ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value) - """ - self.file = file - self.settings = {"parent": parent, "cost_value": cost_value} + ifcopenshell.api.run("cost.remove_cost_value", model, parent=item, cost_value=value) + """ + settings = {"parent": parent, "cost_value": cost_value} - def execute(self): - if len(self.file.get_inverse(self.settings["cost_value"])) == 1: - self.file.remove(self.settings["cost_value"]) - # TODO deep purge - elif self.settings["parent"].is_a("IfcCostItem"): - values = list(self.settings["parent"].CostValues) - values.remove(self.settings["cost_value"]) - self.settings["parent"].CostValues = values if values else None - elif self.settings["parent"].is_a("IfcConstructionResource"): - values = list(self.settings["parent"].BaseCosts) - values.remove(self.settings["cost_value"]) - self.settings["parent"].BaseCosts = values if values else None - elif self.settings["parent"].is_a("IfcCostValue"): - components = list(self.settings["parent"].Components) - components.remove(self.settings["cost_value"]) - self.settings["parent"].Components = components if components else None + if len(file.get_inverse(settings["cost_value"])) == 1: + file.remove(settings["cost_value"]) + # TODO deep purge + elif settings["parent"].is_a("IfcCostItem"): + values = list(settings["parent"].CostValues) + values.remove(settings["cost_value"]) + settings["parent"].CostValues = values if values else None + elif settings["parent"].is_a("IfcConstructionResource"): + values = list(settings["parent"].BaseCosts) + values.remove(settings["cost_value"]) + settings["parent"].BaseCosts = values if values else None + elif settings["parent"].is_a("IfcCostValue"): + components = list(settings["parent"].Components) + components.remove(settings["cost_value"]) + settings["parent"].Components = components if components else None diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py index 091029f594..c7c5fc69fd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py @@ -19,56 +19,59 @@ import ifcopenshell.api +def unassign_cost_item_quantity(file, cost_item=None, products=None) -> None: + """Removes quantities of a cost item that are calculated on products + + A cost item may have quantities that are parametrically calculated on + physical products. This lets you remove those quantities. This means + that any future changes in the physical product's dimensions will not + have any impact on the cost item. + + :param cost_item: The IfcCostItem to remove quantities from + :type cost_item: ifcopenshell.entity_instance + :param products: A list of IfcProducts that may have parametrically + connected quantities to the cost item + :type products: list[ifcopenshell.entity_instance] + :return: None + :rtype: None + + Example: + + .. code:: python + + schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) + item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) + + # Let's imagine a unit cost of 5.0 per unit volume + value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) + ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, + attributes={"AppliedValue": 5.0}) + + slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") + # Usually the quantity would be automatically calculated via a + # graphical authoring application but let's assign a manual quantity + # for now. + qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) + + # Now let's parametrically link the slab's quantity to the cost + # item. If the slab is edited in the future and 42.0 changes, then + # the updated value will also automatically be applied to the cost + # item. + ifcopenshell.api.run("cost.assign_cost_item_quantity", model, + cost_item=item, products=[slab], prop_name="NetVolume") + + # Let's change our mind and remove the parametric connection + ifcopenshell.api.run("cost.unassign_cost_item_quantity", model, + cost_item=item, products=[slab]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"cost_item": cost_item, "products": products or []} + return usecase.execute() + + class Usecase: - def __init__(self, file, cost_item=None, products=None): - """Removes quantities of a cost item that are calculated on products - - A cost item may have quantities that are parametrically calculated on - physical products. This lets you remove those quantities. This means - that any future changes in the physical product's dimensions will not - have any impact on the cost item. - - :param cost_item: The IfcCostItem to remove quantities from - :type cost_item: ifcopenshell.entity_instance - :param products: A list of IfcProducts that may have parametrically - connected quantities to the cost item - :type products: list[ifcopenshell.entity_instance] - :return: None - :rtype: None - - Example: - - .. code:: python - - schedule = ifcopenshell.api.run("cost.add_cost_schedule", model) - item = ifcopenshell.api.run("cost.add_cost_item", model, cost_schedule=schedule) - - # Let's imagine a unit cost of 5.0 per unit volume - value = ifcopenshell.api.run("cost.add_cost_value", model, parent=item) - ifcopenshell.api.run("cost.edit_cost_value", model, cost_value=value, - attributes={"AppliedValue": 5.0}) - - slab = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSlab") - # Usually the quantity would be automatically calculated via a - # graphical authoring application but let's assign a manual quantity - # for now. - qto = ifcopenshell.api.run("pset.add_qto", model, product=slab, name="Qto_SlabBaseQuantities") - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetVolume": 42.0}) - - # Now let's parametrically link the slab's quantity to the cost - # item. If the slab is edited in the future and 42.0 changes, then - # the updated value will also automatically be applied to the cost - # item. - ifcopenshell.api.run("cost.assign_cost_item_quantity", model, - cost_item=item, products=[slab], prop_name="NetVolume") - - # Let's change our mind and remove the parametric connection - ifcopenshell.api.run("cost.unassign_cost_item_quantity", model, - cost_item=item, products=[slab]) - """ - self.file = file - self.settings = {"cost_item": cost_item, "products": products or []} - def execute(self): self.quantities = set(self.settings["cost_item"].CostQuantities or []) for quantity in self.settings["cost_item"].CostQuantities or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py index e0caddbe3c..b1affe3a71 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/__init__.py @@ -15,3 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_information import add_information +from .add_reference import add_reference +from .assign_document import assign_document +from .edit_information import edit_information +from .edit_reference import edit_reference +from .remove_information import remove_information +from .remove_reference import remove_reference +from .unassign_document import unassign_document diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py index 68c9c53759..fc60134477 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_information.py @@ -19,69 +19,62 @@ import ifcopenshell -class Usecase: - def __init__(self, file, parent=None): - """Adds a new document information to the project +def add_information(file, parent=None) -> None: + """Adds a new document information to the project - An IFC document information is a document associated with the project. - It may be a drawing, specification, schedule, certificate, warranty - guarantee, manual, contract, and so on. They are often used for drawings - and facility management purposes. + An IFC document information is a document associated with the project. + It may be a drawing, specification, schedule, certificate, warranty + guarantee, manual, contract, and so on. They are often used for drawings + and facility management purposes. - A document may also be a subdocument of a larger document, this is - useful for superseding documents or tracking older versions. The parent - is considered the latest version and the children are older revisions. + A document may also be a subdocument of a larger document, this is + useful for superseding documents or tracking older versions. The parent + is considered the latest version and the children are older revisions. - :param parent: The parent document, if necessary. - :type parent: ifcopenshell.entity_instance, optional - :return: The newly created IfcDocumentInformation entity - :rtype: ifcopenshell.entity_instance + :param parent: The parent document, if necessary. + :type parent: ifcopenshell.entity_instance, optional + :return: The newly created IfcDocumentInformation entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - # A document typically has a unique drawing or document name (which - # follows a coding system depending on the project), as well as a - # title. This should match what is shown on the titleblock or title - # page of the document. At a minimum you'd also want to specify a - # URI location. The location may be on local, or on a CDE, or any - # other platform. - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - """ - self.file = file - self.settings = {"parent": parent} + document = ifcopenshell.api.run("document.add_information", model) + # A document typically has a unique drawing or document name (which + # follows a coding system depending on the project), as well as a + # title. This should match what is shown on the titleblock or title + # page of the document. At a minimum you'd also want to specify a + # URI location. The location may be on local, or on a CDE, or any + # other platform. + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + """ + settings = {"parent": parent} - def execute(self): - id_attribute = "DocumentId" if self.file.schema == "IFC2X3" else "Identification" - information = self.file.create_entity( - "IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"} + id_attribute = "DocumentId" if file.schema == "IFC2X3" else "Identification" + information = file.create_entity("IfcDocumentInformation", **{id_attribute: "X", "Name": "Unnamed"}) + parent = settings["parent"] + if not parent and file.by_type("IfcProject"): + parent = file.by_type("IfcProject")[0] + if parent.is_a("IfcProject") or parent.is_a("IfcContext"): + file.create_entity( + "IfcRelAssociatesDocument", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatingDocument=information, + RelatedObjects=[parent], ) - parent = self.settings["parent"] - if not parent and self.file.by_type("IfcProject"): - parent = self.file.by_type("IfcProject")[0] - if parent.is_a("IfcProject") or parent.is_a("IfcContext"): - self.file.create_entity( - "IfcRelAssociatesDocument", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatingDocument=information, - RelatedObjects=[parent], + elif parent.is_a("IfcDocumentInformation"): + if parent.IsPointer: + rel = parent.IsPointer[0] + documents = set(rel.RelatedDocuments) + documents.add(information) + rel.RelatedDocuments = list(documents) + else: + file.create_entity( + "IfcDocumentInformationRelationship", RelatingDocument=parent, RelatedDocuments=[information] ) - elif parent.is_a("IfcDocumentInformation"): - if parent.IsPointer: - rel = parent.IsPointer[0] - documents = set(rel.RelatedDocuments) - documents.add(information) - rel.RelatedDocuments = list(documents) - else: - self.file.create_entity( - "IfcDocumentInformationRelationship", - RelatingDocument=parent, - RelatedDocuments=[information] - ) - return information + return information diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py index 80cf91d8a1..3b96b6d666 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/add_reference.py @@ -19,62 +19,57 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, information: ifcopenshell.entity_instance): - """Creates a new reference to a document to assign to products +def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: + """Creates a new reference to a document to assign to products - A document may be associated with physical products, tasks, cost items, - and so on. For example, spaces, storeys, and buildings may have a list - of associated drawings so you can see which drawings (e.g. plans, - sections, details) are documenting that location. Alternatively, - equipment may have associated training manuals, operation and - maintenance manuals or detailed assembly drawings. Resources may be - training certification required, schedules may have gantt charts or bid - documents, and so on. + A document may be associated with physical products, tasks, cost items, + and so on. For example, spaces, storeys, and buildings may have a list + of associated drawings so you can see which drawings (e.g. plans, + sections, details) are documenting that location. Alternatively, + equipment may have associated training manuals, operation and + maintenance manuals or detailed assembly drawings. Resources may be + training certification required, schedules may have gantt charts or bid + documents, and so on. - In order to associate a document with an object, a reference to that - document needs to be created. It could be a reference to the entire - document, or a reference to a particular page or chapter. See - ifcopenshell.api.document.assign_document for more information. + In order to associate a document with an object, a reference to that + document needs to be created. It could be a reference to the entire + document, or a reference to a particular page or chapter. See + ifcopenshell.api.document.assign_document for more information. - :param information: The IfcDocumentInformation that the reference will - be created for - :type information: ifcopenshell.entity_instance - :return: The newly created IfcDocumentReference entity - :rtype: ifcopenshell.entity_instance + :param information: The IfcDocumentInformation that the reference will + be created for + :type information: ifcopenshell.entity_instance + :return: The newly created IfcDocumentReference entity + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) - # In this case, we don't specify any more information, and so the - # reference is for the entire document, as opposed to a single page or - # chapter or section. - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + # In this case, we don't specify any more information, and so the + # reference is for the entire document, as opposed to a single page or + # chapter or section. + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Alternatively, we can specify a single section, such as by a - # subheading code. - reference2 = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.edit_reference", model, - reference=reference2, attributes={"Identification": "2.1.15"}) - """ - self.file = file - self.settings = {"information": information} + # Alternatively, we can specify a single section, such as by a + # subheading code. + reference2 = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.edit_reference", model, + reference=reference2, attributes={"Identification": "2.1.15"}) + """ + settings = {"information": information} - def execute(self) -> ifcopenshell.entity_instance: - if self.file.schema == "IFC2X3": - reference = self.file.create_entity("IfcDocumentReference", ItemReference="X") - if self.settings["information"]: - references = list(self.settings["information"].DocumentReferences or []) - references.append(reference) - self.settings["information"].DocumentReferences = references - return reference - return self.file.create_entity( - "IfcDocumentReference", ReferencedDocument=self.settings["information"], Identification="X" - ) + if file.schema == "IFC2X3": + reference = file.create_entity("IfcDocumentReference", ItemReference="X") + if settings["information"]: + references = list(settings["information"].DocumentReferences or []) + references.append(reference) + settings["information"].DocumentReferences = references + return reference + return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X") diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py index f9b433213a..5818347a90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/assign_document.py @@ -22,93 +22,85 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - document: ifcopenshell.entity_instance, - ): - """Assigns a document to a list of products +def assign_document( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + document: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns a document to a list of products - An object may be assigned to zero, one, or multiple documents. Almost - any object or property may be assigned to a document, though typically - we'd only use it for spaces, types, physical products and schedules. - Adding a new assignment is typically done using a document reference and - an object. IFC technically allows association with a document - information and an object, but this is not encouraged because it is not - consistent with other external relationships (such as classification - systems or libraries). + An object may be assigned to zero, one, or multiple documents. Almost + any object or property may be assigned to a document, though typically + we'd only use it for spaces, types, physical products and schedules. + Adding a new assignment is typically done using a document reference and + an object. IFC technically allows association with a document + information and an object, but this is not encouraged because it is not + consistent with other external relationships (such as classification + systems or libraries). - :param product: The list of objects to associate the document to. This could be - almost any sensible object in IFC. - :type product: list[ifcopenshell.entity_instance] - :param document: The IfcDocumentReference to associate to, or - alternatively an IfcDocumentInformation, though this is not - recommended. - :type document: ifcopenshell.entity_instance - :return: The IfcRelAssociatesDocument relationship - or `None` if `products` was an empty list or all products were - already assigned to the `document`. - :rtype: ifcopenshell.entity_instance + :param product: The list of objects to associate the document to. This could be + almost any sensible object in IFC. + :type product: list[ifcopenshell.entity_instance] + :param document: The IfcDocumentReference to associate to, or + alternatively an IfcDocumentInformation, though this is not + recommended. + :type document: ifcopenshell.entity_instance + :return: The IfcRelAssociatesDocument relationship + or `None` if `products` was an empty list or all products were + already assigned to the `document`. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Let's imagine storey represents an IfcBuildingStorey for the ground floor - ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) - """ - self.file = file - self.settings = { - "products": products, - "document": document, - } + # Let's imagine storey represents an IfcBuildingStorey for the ground floor + ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) + """ + settings = { + "products": products, + "document": document, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - # NOTE: reuses code from `library.assign_reference` + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # NOTE: reuses code from `library.assign_reference` - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["document"]) - products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) - products = products - referenced_elements + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["document"]) + products: set[ifcopenshell.entity_instance] = set(settings["products"]) + products = products - referenced_elements - if not products: - return + if not products: + return - if self.file.schema == "IFC2X3": - rel = next( - ( - r - for r in self.file.by_type("IfcRelAssociatesDocument") - if r.RelatingDocument == self.settings["document"] - ), - None, - ) - else: - ifc_class = self.settings["document"].is_a() - if ifc_class == "IfcDocumentReference": - rel = next(iter(self.settings["document"].DocumentRefForObjects), None) - elif ifc_class == "IfcDocumentInformation": - rel = next(iter(self.settings["document"].DocumentInfoForObjects), None) + if file.schema == "IFC2X3": + rel = next( + (r for r in file.by_type("IfcRelAssociatesDocument") if r.RelatingDocument == settings["document"]), + None, + ) + else: + ifc_class = settings["document"].is_a() + if ifc_class == "IfcDocumentReference": + rel = next(iter(settings["document"].DocumentRefForObjects), None) + elif ifc_class == "IfcDocumentInformation": + rel = next(iter(settings["document"].DocumentInfoForObjects), None) - if not rel: - return self.file.create_entity( - "IfcRelAssociatesDocument", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatedObjects=list(products), - RelatingDocument=self.settings["document"], - ) + if not rel: + return file.create_entity( + "IfcRelAssociatesDocument", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatedObjects=list(products), + RelatingDocument=settings["document"], + ) - related_objects = set(rel.RelatedObjects) | products - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + related_objects = set(rel.RelatedObjects) | products + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py index 96c0120120..478c1c11da 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_information.py @@ -19,38 +19,34 @@ import ifcopenshell from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - information: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcDocumentInformation +def edit_information( + file: ifcopenshell.file, + information: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcDocumentInformation - For more information about the attributes and data types of an - IfcDocumentInformation, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDocumentInformation, consult the IFC documentation. - :param reference: The IfcDocumentInformation entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcDocumentInformation entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - """ - self.file = file - self.settings = {"information": information, "attributes": attributes or {}} + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + """ + settings = {"information": information, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - setattr(self.settings["information"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["information"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py index d88afdfc2f..fb705fbbc2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/edit_reference.py @@ -19,41 +19,37 @@ import ifcopenshell from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcDocumentReference +def edit_reference( + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcDocumentReference - For more information about the attributes and data types of an - IfcDocumentReference, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDocumentReference, consult the IFC documentation. - :param reference: The IfcDocumentReference entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcDocumentReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.edit_reference", model, - reference=reference, attributes={"Identification": "2.1.15"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.edit_reference", model, + reference=reference, attributes={"Identification": "2.1.15"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py index 56f57df283..86531252e9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_information.py @@ -22,45 +22,42 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, information=None): - """Removes a document information +def remove_information(file, information=None) -> None: + """Removes a document information - All references and associations are also removed. + All references and associations are also removed. - :param information: The IfcDocumentInformation to remove - :type information: ifcopenshell.entity_instance - :return: None - :rtype: None + :param information: The IfcDocumentInformation to remove + :type information: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add a document - document = ifcopenshell.api.run("document.add_information", model) - # ... and remove it! - ifcopenshell.api.run("document.remove_information", model, information=document) - """ - self.file = file - self.settings = {"information": information} + # Add a document + document = ifcopenshell.api.run("document.add_information", model) + # ... and remove it! + ifcopenshell.api.run("document.remove_information", model, information=document) + """ + settings = {"information": information} - def execute(self): - for reference in self.settings["information"].HasDocumentReferences or []: - ifcopenshell.api.run("document.remove_reference", self.file, reference=reference) + for reference in settings["information"].HasDocumentReferences or []: + ifcopenshell.api.run("document.remove_reference", file, reference=reference) - for rel in self.settings["information"].IsPointer or []: - for information in rel.RelatedDocuments: - ifcopenshell.api.run("document.remove_information", self.file, information=information) + for rel in settings["information"].IsPointer or []: + for information in rel.RelatedDocuments: + ifcopenshell.api.run("document.remove_information", file, information=information) - for rel in self.settings["information"].IsPointedTo or []: - if rel.RelatedDocuments == (self.settings["information"],): - # This relationship is non-rooted - self.file.remove(rel) + for rel in settings["information"].IsPointedTo or []: + if rel.RelatedDocuments == (settings["information"],): + # This relationship is non-rooted + file.remove(rel) - for rel in self.settings["information"].DocumentInfoForObjects or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["information"]) + for rel in settings["information"].DocumentInfoForObjects or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["information"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py index 61fd6810c1..5321b480f7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/remove_reference.py @@ -20,32 +20,29 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance): - """Remove a document reference +def remove_reference(file: ifcopenshell.file, reference: ifcopenshell.entity_instance) -> None: + """Remove a document reference - All associations with objects are removed. + All associations with objects are removed. - :param reference: The IfcDocumentReference to remove - :type reference: ifcopenshell.entity_instance - :return: None - :rtype: None + :param reference: The IfcDocumentReference to remove + :type reference: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) - ifcopenshell.api.run("document.remove_reference", model, reference=reference) - """ - self.file = file - self.settings = {"reference": reference} + document = ifcopenshell.api.run("document.add_information", model) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) + ifcopenshell.api.run("document.remove_reference", model, reference=reference) + """ + settings = {"reference": reference} - def execute(self) -> None: - for rel in self.settings["reference"].DocumentRefForObjects or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["reference"]) + for rel in settings["reference"].DocumentRefForObjects or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py index c4728d5511..44c0543501 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py +++ b/src/ifcopenshell-python/ifcopenshell/api/document/unassign_document.py @@ -21,69 +21,65 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - document: ifcopenshell.entity_instance, - ): - """Unassigns a document and an association to the list of products +def unassign_document( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + document: ifcopenshell.entity_instance, +) -> None: + """Unassigns a document and an association to the list of products - :param product: The list of objects that the document reference or information is - related to. - :type product: list[ifcopenshell.entity_instance] - :param document: The IfcDocumentReference (typically) or in rare cases - the IfcDocumentInformation that is associated with the product - :type document: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The list of objects that the document reference or information is + related to. + :type product: list[ifcopenshell.entity_instance] + :param document: The IfcDocumentReference (typically) or in rare cases + the IfcDocumentInformation that is associated with the product + :type document: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - document = ifcopenshell.api.run("document.add_information", model) - ifcopenshell.api.run("document.edit_information", model, - information=document, - attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", - "Location": "A-GA-6100 - Overall Plan.pdf"}) - reference = ifcopenshell.api.run("document.add_reference", model, information=document) + document = ifcopenshell.api.run("document.add_information", model) + ifcopenshell.api.run("document.edit_information", model, + information=document, + attributes={"Identification": "A-GA-6100", "Name": "Overall Plan", + "Location": "A-GA-6100 - Overall Plan.pdf"}) + reference = ifcopenshell.api.run("document.add_reference", model, information=document) - # Let's imagine storey represents an IfcBuildingStorey for the ground floor - ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) + # Let's imagine storey represents an IfcBuildingStorey for the ground floor + ifcopenshell.api.run("document.assign_document", model, products=[storey], document=reference) - # Now let's change our mind and remove the association - ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference) - """ - self.file = file - self.settings = { - "products": products, - "document": document, - } + # Now let's change our mind and remove the association + ifcopenshell.api.run("document.unassign_document", model, products=[storey], document=reference) + """ + settings = { + "products": products, + "document": document, + } - def execute(self): - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - # NOTE: reuses code from `library.un assign_reference` + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # NOTE: reuses code from `library.un assign_reference` - reference_rels: set[ifcopenshell.entity_instance] = set() - products = set(self.settings["products"]) - for product in products: - reference_rels.update(product.HasAssociations) + reference_rels: set[ifcopenshell.entity_instance] = set() + products = set(settings["products"]) + for product in products: + reference_rels.update(product.HasAssociations) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == self.settings["document"] - } + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"] + } - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py index e0caddbe3c..dd010e886e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_product import assign_product +from .edit_text_literal import edit_text_literal +from .unassign_product import unassign_product diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py index 051dd985b3..b35d0bd564 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/assign_product.py @@ -20,95 +20,92 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Associates a product and an object, typically for annotation +def assign_product(file, relating_product=None, related_object=None) -> None: + """Associates a product and an object, typically for annotation - Warning: this is an experimental API. + Warning: this is an experimental API. - When you want to draw attention to a feature or characteristic (such as - a dimension, material, or name) or of a product (e.g. wall, slab, - furniture, etc), an annotation object is created. This annotation is - then associated with the product so that it can reference attributes, - properties, and relationships. + When you want to draw attention to a feature or characteristic (such as + a dimension, material, or name) or of a product (e.g. wall, slab, + furniture, etc), an annotation object is created. This annotation is + then associated with the product so that it can reference attributes, + properties, and relationships. - For example, an annotation of a line will be associated with a grid - axis, such that when that grid axis moves, the annotation of that grid - axis (which is typically truncated to the extents of a drawing) will - also move. + For example, an annotation of a line will be associated with a grid + axis, such that when that grid axis moves, the annotation of that grid + axis (which is typically truncated to the extents of a drawing) will + also move. - Another example might be a label of a furniture product, which might - have some text of the name of the furniture to be shown on drawings or - in 3D. + Another example might be a label of a furniture product, which might + have some text of the name of the furniture to be shown on drawings or + in 3D. - :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance - :param related_object: The object (typically IfcAnnotation) that the - product is related to - :type related_object: ifcopenshell.entity_instance - :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct the object is related to + :type relating_product: ifcopenshell.entity_instance + :param related_object: The object (typically IfcAnnotation) that the + product is related to + :type related_object: ifcopenshell.entity_instance + :return: The created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") - ifcopenshell.api.run("drawing.assign_product", model, - relating_product=furniture, related_object=annotation) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") + ifcopenshell.api.run("drawing.assign_product", model, + relating_product=furniture, related_object=annotation) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis") + is_grid_axis = settings["relating_product"].is_a("IfcGridAxis") - if is_grid_axis: - if self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToProduct") and rel.Name == self.settings["relating_product"].AxisTag: - return - elif self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == self.settings["relating_product"]: + if is_grid_axis: + if settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag: return + elif settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]: + return - referenced_by = None + referenced_by = None - if is_grid_axis: - axis = self.settings["relating_product"] - grid = None - for attribute in ("PartOfW", "PartOfV", "PartOfU"): - if getattr(axis, attribute, None): - grid = getattr(axis, attribute)[0] - self.settings["relating_product"] = grid - for rel in grid.ReferencedBy: - if rel.Name == axis.AxisTag: - referenced_by = rel - break - elif self.settings["relating_product"].ReferencedBy: - referenced_by = self.settings["relating_product"].ReferencedBy[0] + if is_grid_axis: + axis = settings["relating_product"] + grid = None + for attribute in ("PartOfW", "PartOfV", "PartOfU"): + if getattr(axis, attribute, None): + grid = getattr(axis, attribute)[0] + settings["relating_product"] = grid + for rel in grid.ReferencedBy: + if rel.Name == axis.AxisTag: + referenced_by = rel + break + elif settings["relating_product"].ReferencedBy: + referenced_by = settings["relating_product"].ReferencedBy[0] - if referenced_by: - related_objects = list(referenced_by.RelatedObjects) - related_objects.append(self.settings["related_object"]) - referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by}) - else: - referenced_by = self.file.create_entity( - "IfcRelAssignsToProduct", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProduct": self.settings["relating_product"], - } - ) + if referenced_by: + related_objects = list(referenced_by.RelatedObjects) + related_objects.append(settings["related_object"]) + referenced_by.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by}) + else: + referenced_by = file.create_entity( + "IfcRelAssignsToProduct", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProduct": settings["relating_product"], + } + ) - if is_grid_axis: - referenced_by.Name = axis.AxisTag - return referenced_by + if is_grid_axis: + referenced_by.Name = axis.AxisTag + return referenced_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py index 00b8bc4f82..f1aadc25b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/edit_text_literal.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, text_literal=None, attributes=None): - """Edits the attributes of an IfcTextLiteral +def edit_text_literal(file, text_literal=None, attributes=None) -> None: + """Edits the attributes of an IfcTextLiteral - For more information about the attributes and data types of an - IfcTextLiteral, consult the IFC documentation. + For more information about the attributes and data types of an + IfcTextLiteral, consult the IFC documentation. - :param reference: The IfcTextLiteral entity you want to edit - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcTextLiteral entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - text = model.createIfcTextLiteral() - ifcopenshell.api.run("drawing.edit_text_literal", model, - text_literal=text, attributes={"Literal": "MY ANNOTATION"}) - """ - self.file = file - self.settings = {"text_literal": text_literal, "attributes": attributes or {}} + text = model.createIfcTextLiteral() + ifcopenshell.api.run("drawing.edit_text_literal", model, + text_literal=text, attributes={"Literal": "MY ANNOTATION"}) + """ + settings = {"text_literal": text_literal, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["text_literal"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["text_literal"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py index 91ee7ded3d..8254bffdce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/drawing/unassign_product.py @@ -21,54 +21,51 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Unassigns a product and an object (typically an annotation) +def unassign_product(file, relating_product=None, related_object=None) -> None: + """Unassigns a product and an object (typically an annotation) - Smart annotation objects can be associated with products so that they - can annotate attributes and properties. This function lets you remove - the association, so that you may change the assocation with another - object later or leave the annotation as a "dumb" annotation. + Smart annotation objects can be associated with products so that they + can annotate attributes and properties. This function lets you remove + the association, so that you may change the assocation with another + object later or leave the annotation as a "dumb" annotation. - :param relating_product: The IfcProduct the object is related to - :type relating_product: ifcopenshell.entity_instance - :param related_object: The object (typically IfcAnnotation) that the - product is related to - :type related_object: ifcopenshell.entity_instance - :return: The created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct the object is related to + :type relating_product: ifcopenshell.entity_instance + :param related_object: The object (typically IfcAnnotation) that the + product is related to + :type related_object: ifcopenshell.entity_instance + :return: The created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") - ifcopenshell.api.run("drawing.assign_product", model, - relating_product=furniture, related_object=annotation) + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + annotation = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcAnnotation") + ifcopenshell.api.run("drawing.assign_product", model, + relating_product=furniture, related_object=annotation) - # Let's change our mind and remove the relationship - ifcopenshell.api.run("drawing.unassign_product", model, - relating_product=furniture, related_object=annotation) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Let's change our mind and remove the relationship + ifcopenshell.api.run("drawing.unassign_product", model, + relating_product=furniture, related_object=annotation) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py index e0caddbe3c..1caaa312ba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/__init__.py @@ -15,3 +15,30 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_axis_representation import add_axis_representation +from .add_boolean import add_boolean +from .add_door_representation import add_door_representation +from .add_footprint_representation import add_footprint_representation +from .add_mesh_representation import add_mesh_representation +from .add_profile_representation import add_profile_representation +from .add_railing_representation import add_railing_representation + +try: + from .add_representation import add_representation +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}") +from .add_slab_representation import add_slab_representation +from .add_wall_representation import add_wall_representation +from .add_window_representation import add_window_representation +from .assign_representation import assign_representation +from .connect_element import connect_element +from .connect_path import connect_path +from .create_2pt_wall import create_2pt_wall +from .disconnect_element import disconnect_element +from .disconnect_path import disconnect_path +from .edit_object_placement import edit_object_placement +from .map_representation import map_representation +from .remove_boolean import remove_boolean +from .remove_representation import remove_representation +from .unassign_representation import unassign_representation diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py index 8a09531a26..d8180d287f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_axis_representation.py @@ -19,61 +19,64 @@ import ifcopenshell.util.unit +def add_axis_representation(file, context=None, axis=None) -> None: + """Adds a new axis representation + + Certain objects are typically "axis-based", such as walls, beams, + and columns. This means you can represent them abstractly by simply + drawing a single line either in 2D (such as for walls) or 3D (for beams + and columns). Humans can understand this axis-based representation as + being a simplification of a layered extrusion or a profile that is being + extruded along that axis and joined to other elements. + + Using an axis-based representation makes it easy for users and computers + to analyse connectivity and spatial relationships, as well as makes it + easy to parametrically edit these objects by simply stretching the start + or end of the axis. + + For now, only simple straight line axes are supported, represented by a + start and end coordinate. The order is important. For walls, the start + must be at the minimum local X ordinate, and the end at the maximum + local X ordinate. For beams and columns, the start is at the minimum + local Z ordinate, and the end of the maximum local Z ordinate. The first + coordinate is the "start" and the second coordinate is the "end". This + stat and end is then used to determine any parametric junctions with + other elements. + + Using an axis-representation is optional, but highly recommended for + "standard" representations of walls, beams, columns, and other + structural members. A rule of thumb is that if you can draw it as a line + on paper, you can probably represent it using an axis. + + :param context: The IfcGeometricRepresentationContext that the + representation is part of. This must be either a + Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D). + :type context: ifcopenshell.entity_instance + :param axis: The axis, as a list of two coordinates, the coordinates + being either a list of 2 or 3 float coordinates depending on whether + the axis is 2D or 3D. + :type axis: list[list[float]] + :return: The newly created IfcShapeRepresentation entity + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW") + axis = ifcopenshell.api.run("geometry.add_axis_representation", model, + context=context, axis=[(0.0, 0.0), (1.0, 0.0)]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": context, + "axis": axis or [], + } + return usecase.execute() + + class Usecase: - def __init__(self, file, context=None, axis=None): - """Adds a new axis representation - - Certain objects are typically "axis-based", such as walls, beams, - and columns. This means you can represent them abstractly by simply - drawing a single line either in 2D (such as for walls) or 3D (for beams - and columns). Humans can understand this axis-based representation as - being a simplification of a layered extrusion or a profile that is being - extruded along that axis and joined to other elements. - - Using an axis-based representation makes it easy for users and computers - to analyse connectivity and spatial relationships, as well as makes it - easy to parametrically edit these objects by simply stretching the start - or end of the axis. - - For now, only simple straight line axes are supported, represented by a - start and end coordinate. The order is important. For walls, the start - must be at the minimum local X ordinate, and the end at the maximum - local X ordinate. For beams and columns, the start is at the minimum - local Z ordinate, and the end of the maximum local Z ordinate. The first - coordinate is the "start" and the second coordinate is the "end". This - stat and end is then used to determine any parametric junctions with - other elements. - - Using an axis-representation is optional, but highly recommended for - "standard" representations of walls, beams, columns, and other - structural members. A rule of thumb is that if you can draw it as a line - on paper, you can probably represent it using an axis. - - :param context: The IfcGeometricRepresentationContext that the - representation is part of. This must be either a - Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D). - :type context: ifcopenshell.entity_instance - :param axis: The axis, as a list of two coordinates, the coordinates - being either a list of 2 or 3 float coordinates depending on whether - the axis is 2D or 3D. - :type axis: list[list[float]] - :return: The newly created IfcShapeRepresentation entity - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW") - axis = ifcopenshell.api.run("geometry.add_axis_representation", model, - context=context, axis=[(0.0, 0.0), (1.0, 0.0)]) - """ - self.file = file - self.settings = { - "context": context, - "axis": axis or [], - } - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) is_2d = len(self.settings["axis"][0]) == 2 @@ -82,9 +85,13 @@ class Usecase: curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points]) else: if is_2d: - curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False) + curve = self.file.createIfcIndexedPolyCurve( + self.file.createIfcCartesianPointList2D(points), None, False + ) else: - curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points), None, False) + curve = self.file.createIfcIndexedPolyCurve( + self.file.createIfcCartesianPointList3D(points), None, False + ) return self.file.createIfcShapeRepresentation( self.settings["context"], self.settings["context"].ContextIdentifier, diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py index 66a5bf3baf..3ec590a6f3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py @@ -20,24 +20,27 @@ import ifcopenshell.util.unit import numpy as np -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "representation": None, - "operator": "DIFFERENCE", - # IfcHalfSpaceSolid, Mesh - "type": "IfcHalfSpaceSolid", - # The XY plane is the clipping boundary and +Z is removed. - "matrix": None, # A matrix to define a clipping Ifchalfspacesolid. - "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type - "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type - "should_force_faceted_brep": False, - "should_force_triangulation": False, - } - for key, value in settings.items(): - self.settings[key] = value +def add_boolean(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "representation": None, + "operator": "DIFFERENCE", + # IfcHalfSpaceSolid, Mesh + "type": "IfcHalfSpaceSolid", + # The XY plane is the clipping boundary and +Z is removed. + "matrix": None, # A matrix to define a clipping Ifchalfspacesolid. + "blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type + "blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type + "should_force_faceted_brep": False, + "should_force_triangulation": False, + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) if self.settings["type"] == "IfcHalfSpaceSolid": diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py index 4da8d5b1be..da7678aac4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_door_representation.py @@ -63,11 +63,7 @@ def create_ifc_door_lining( points = [p.xz for p in points] door_lining = builder.polyline(points, closed=True) - door_lining = builder.extrude( - door_lining, - size.y, - **builder.extrude_kwargs("Y") - ) + door_lining = builder.extrude(door_lining, size.y, **builder.extrude_kwargs("Y")) builder.translate(door_lining, position) return door_lining @@ -79,75 +75,78 @@ def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0, return box -class Usecase: - def __init__(self, file, **settings): - """units in settings expected to be in ifc project units""" - self.file = file - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - "overall_height": self.convert_si_to_unit(2.0), - "overall_width": self.convert_si_to_unit(0.9), - # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL, - # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT, - # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING, - # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT, - # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT, - # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL, - # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT, - # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT - "operation_type": "SINGLE_SWING_LEFT", # door type - "lining_properties": { - "LiningDepth": self.convert_si_to_unit(0.050), - "LiningThickness": self.convert_si_to_unit(0.050), - # offset from the outer side of the wall (by Y-axis) - "LiningOffset": self.convert_si_to_unit(0.0), - # offset from the wall - "LiningToPanelOffsetX": self.convert_si_to_unit(0.025), - # offset from the X-axis (unlike windows) - "LiningToPanelOffsetY": self.convert_si_to_unit(0.025), - # transom - vertical distance between door and window panels - "TransomThickness": self.convert_si_to_unit(0.000), - # TransomOffset - distance from the bottom door opening - # to the beginning of the transom - # unlike windows TransomOffset which goes to the center of the transom - "TransomOffset": self.convert_si_to_unit(1.525), - "ShapeAspectStyle": None, # DEPRECATED - # Casing cover wall faces around the opening - # on the left, right and upper sides - # Casing should be either on both sides of the wall or no casing - # If `LiningOffset` is present then therefore casing is not possible on outer wall - # therefore there will be no casing on inner wall either - "CasingDepth": self.convert_si_to_unit(0.005), - "CasingThickness": self.convert_si_to_unit(0.075), # by Z-axis - # Threshold covers the bottom side of the opening - "ThresholdDepth": self.convert_si_to_unit(0.1), - "ThresholdThickness": self.convert_si_to_unit(0.025), # by Z-axis - # offset by Y-axis - "ThresholdOffset": self.convert_si_to_unit(0.000), - }, - "panel_properties": { - "PanelDepth": self.convert_si_to_unit(0.035), # by Y - "PanelWidth": 1.0, # as ratio to the clear door opening - "FrameDepth": self.convert_si_to_unit(0.035), # by Y - "FrameThickness": self.convert_si_to_unit(0.035), # by X - # LEFT, MIDDLE, RIGHT, NOTDEFINED - "PanelPosition": ..., # NEVER USED - # defines the basic ways to describe how door panels operate - # basically how it opens - "PanelOperation": None, # NEVER USED - "ShapeAspectStyle": None, # DEPRECATED - }, - } - ) - for key, value in settings.items(): - self.settings[key] = value +def add_door_representation(file, **usecase_settings) -> None: + """units in usecase_settings expected to be in ifc project units""" + usecase = Usecase() + usecase.file = file + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + "overall_height": usecase.convert_si_to_unit(2.0), + "overall_width": usecase.convert_si_to_unit(0.9), + # DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL, + # DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT, + # DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING, + # DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT, + # FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT, + # LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL, + # ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT, + # SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT + "operation_type": "SINGLE_SWING_LEFT", # door type + "lining_properties": { + "LiningDepth": usecase.convert_si_to_unit(0.050), + "LiningThickness": usecase.convert_si_to_unit(0.050), + # offset from the outer side of the wall (by Y-axis) + "LiningOffset": usecase.convert_si_to_unit(0.0), + # offset from the wall + "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025), + # offset from the X-axis (unlike windows) + "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025), + # transom - vertical distance between door and window panels + "TransomThickness": usecase.convert_si_to_unit(0.000), + # TransomOffset - distance from the bottom door opening + # to the beginning of the transom + # unlike windows TransomOffset which goes to the center of the transom + "TransomOffset": usecase.convert_si_to_unit(1.525), + "ShapeAspectStyle": None, # DEPRECATED + # Casing cover wall faces around the opening + # on the left, right and upper sides + # Casing should be either on both sides of the wall or no casing + # If `LiningOffset` is present then therefore casing is not possible on outer wall + # therefore there will be no casing on inner wall either + "CasingDepth": usecase.convert_si_to_unit(0.005), + "CasingThickness": usecase.convert_si_to_unit(0.075), # by Z-axis + # Threshold covers the bottom side of the opening + "ThresholdDepth": usecase.convert_si_to_unit(0.1), + "ThresholdThickness": usecase.convert_si_to_unit(0.025), # by Z-axis + # offset by Y-axis + "ThresholdOffset": usecase.convert_si_to_unit(0.000), + }, + "panel_properties": { + "PanelDepth": usecase.convert_si_to_unit(0.035), # by Y + "PanelWidth": 1.0, # as ratio to the clear door opening + "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y + "FrameThickness": usecase.convert_si_to_unit(0.035), # by X + # LEFT, MIDDLE, RIGHT, NOTDEFINED + "PanelPosition": ..., # NEVER USED + # defines the basic ways to describe how door panels operate + # basically how it opens + "PanelOperation": None, # NEVER USED + "ShapeAspectStyle": None, # DEPRECATED + }, + } + ) + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): builder = ShapeBuilder(self.file) overall_height = self.settings["overall_height"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py index afdf95155a..976b48e5ce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_footprint_representation.py @@ -19,20 +19,17 @@ import ifcopenshell.util.unit -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "curves": [], # A list of IFC curves to include in the curve set - } - for key, value in settings.items(): - self.settings[key] = value +def add_footprint_representation(file, **usecase_settings) -> None: + settings = { + "context": None, # IfcGeometricRepresentationContext + "curves": [], # A list of IFC curves to include in the curve set + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - return self.file.createIfcShapeRepresentation( - self.settings["context"], - self.settings["context"].ContextIdentifier, - "GeometricCurveSet", - [self.file.createIfcGeometricCurveSet(self.settings["curves"])], - ) + return file.createIfcShapeRepresentation( + settings["context"], + settings["context"].ContextIdentifier, + "GeometricCurveSet", + [file.createIfcGeometricCurveSet(settings["curves"])], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py index ac2167a70e..fbe42063d9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_mesh_representation.py @@ -19,25 +19,28 @@ import ifcopenshell.util.unit -class Usecase: - def __init__(self, file: ifcopenshell.file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...] - # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...] - "vertices": None, # A list of coordinates - # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...] - "edges": None, # A list of edges, represented by vertex index pairs - # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...] - "faces": None, # A list of polygons, represented by vertex indices - "coordinate_offset": None, # Optionally apply a vector offset to all coordinates - "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different - "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets - } - for key, value in settings.items(): - self.settings[key] = value +def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + # Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...] + # ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...] + "vertices": None, # A list of coordinates + # ... where itemN = [(0, 1), (1, 2), (v1, v2), ...] + "edges": None, # A list of edges, represented by vertex index pairs + # ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...] + "faces": None, # A list of polygons, represented by vertex indices + "coordinate_offset": None, # Optionally apply a vector offset to all coordinates + "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different + "force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["unit_scale"] is None: self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py index c38aaf87df..025f09f0fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_profile_representation.py @@ -21,22 +21,25 @@ import ifcopenshell.util.unit from ifcopenshell.util.data import Clipping -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "profile": None, - "depth": 1.0, - "cardinal_point": 5, - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - "placement_zx_axes": (None, None), - } - for key, value in settings.items(): - self.settings[key] = value +def add_profile_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "profile": None, + "depth": 1.0, + "cardinal_point": 5, + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + "placement_zx_axes": (None, None), + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py index b597633678..9de884c8c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_railing_representation.py @@ -31,39 +31,42 @@ def mm(x): return x / 1000 +def add_railing_representation(file, **usecase_settings) -> None: + """ + units in usecase_settings expected to be in ifc project units + + `railing_path` is a list of point coordinates for the railing path, + coordinates are expected to be at the top of the railing, not at the center + + `railing_path` is expected to be a list of Vector objects + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + "railing_type": "WALL_MOUNTED_HANDRAIL", + "railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]), + "use_manual_supports": False, + "support_spacing": usecase.convert_si_to_unit(mm(1000)), + "railing_diameter": usecase.convert_si_to_unit(mm(50)), + "clear_width": usecase.convert_si_to_unit(mm(40)), + "terminal_type": "180", + "height": usecase.convert_si_to_unit(mm(1000)), + "looped_path": False, + } + ) + + for key, value in usecase_settings.items(): + usecase.settings[key] = value + + if usecase.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL": + raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.') + return usecase.execute() + + class Usecase: - def __init__(self, file, **settings): - """ - units in settings expected to be in ifc project units - - `railing_path` is a list of point coordinates for the railing path, - coordinates are expected to be at the top of the railing, not at the center - - `railing_path` is expected to be a list of Vector objects - """ - self.file = file - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - "railing_type": "WALL_MOUNTED_HANDRAIL", - "railing_path": self.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]), - "use_manual_supports": False, - "support_spacing": self.convert_si_to_unit(mm(1000)), - "railing_diameter": self.convert_si_to_unit(mm(50)), - "clear_width": self.convert_si_to_unit(mm(40)), - "terminal_type": "180", - "height": self.convert_si_to_unit(mm(1000)), - "looped_path": False, - } - ) - - for key, value in settings.items(): - self.settings[key] = value - - if self.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL": - raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.') - def execute(self): arc_points = [] items_3d = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index 155bfb2bea..6c9a8da058 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -28,37 +28,40 @@ X_AXIS = Vector((1, 0, 0)) EPSILON = 1e-6 -class Usecase: - def __init__(self, file: ifcopenshell.file, **settings): - # TODO: This usecase currently depends on Blender's data model - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now - "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now - "coordinate_offset": None, # Optionally apply a vector offset to all coordinates - "total_items": 1, # How many representation items to create - "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different - "should_force_faceted_brep": False, # If we should force faceted breps for meshes - "should_force_triangulation": False, # If we should force triangulation for meshes - "should_generate_uvs": False, # If UV coordinates should also be generated - # Possible IFC representation classes: - # IfcExtrudedAreaSolid/IfcRectangleProfileDef - # IfcExtrudedAreaSolid/IfcCircleProfileDef - # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef - # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids - # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage - # IfcGeometricCurveSet/IfcTextLiteral - # IfcTextLiteral - "ifc_representation_class": None, # Whether to cast a mesh into a particular class - "profile_set_usage": None, # The material profile set if the extrusion requires it - "text_literal": None, # The text literal if the representation requires it - } - self.ifc_vertices = [] - for key, value in settings.items(): - self.settings[key] = value +def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance: + usecase = Usecase() + # TODO: This usecase currently depends on Blender's data model + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now + "geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now + "coordinate_offset": None, # Optionally apply a vector offset to all coordinates + "total_items": 1, # How many representation items to create + "unit_scale": None, # A scale factor to apply for all vectors in case the unit is different + "should_force_faceted_brep": False, # If we should force faceted breps for meshes + "should_force_triangulation": False, # If we should force triangulation for meshes + "should_generate_uvs": False, # If UV coordinates should also be generated + # Possible IFC representation classes: + # IfcExtrudedAreaSolid/IfcRectangleProfileDef + # IfcExtrudedAreaSolid/IfcCircleProfileDef + # IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef + # IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids + # IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage + # IfcGeometricCurveSet/IfcTextLiteral + # IfcTextLiteral + "ifc_representation_class": None, # Whether to cast a mesh into a particular class + "profile_set_usage": None, # The material profile set if the extrusion requires it + "text_literal": None, # The text literal if the representation requires it + } + usecase.ifc_vertices = [] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() - def execute(self) -> ifcopenshell.entity_instance: + +class Usecase: + def execute(self): self.is_manifold = None if ( isinstance(self.settings["geometry"], bpy.types.Mesh) @@ -374,10 +377,12 @@ class Usecase: return items def create_plane(self, polygon): - return self.file.createIfcPlane(Position=self.file.createIfcAxis2Placement3D( - Location=self.file.createIfcCartesianPoint(polygon.center), - Axis=self.file.createIfcDirection(polygon.normal), - )) + return self.file.createIfcPlane( + Position=self.file.createIfcAxis2Placement3D( + Location=self.file.createIfcCartesianPoint(polygon.center), + Axis=self.file.createIfcDirection(polygon.normal), + ) + ) def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]: items = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py index 2edd68d5c1..7514ade38f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_slab_representation.py @@ -20,20 +20,23 @@ import ifcopenshell.util.unit from math import sin, cos -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "depth": 0.2, - "x_angle": 0, # Radians - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - } - for key, value in settings.items(): - self.settings[key] = value +def add_slab_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "depth": 0.2, + "x_angle": 0, # Radians + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) return self.file.createIfcShapeRepresentation( diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py index 6aa5466f58..504a89078b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_wall_representation.py @@ -21,25 +21,28 @@ from math import sin, cos from ifcopenshell.util.data import Clipping -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "context": None, # IfcGeometricRepresentationContext - "length": 1.0, - "height": 3.0, - "offset": 0.0, - "thickness": 0.2, - # Sloped walls along the wall's X axis, provided in radians - "x_angle": 0, - # Planes are defined either by Clipping objects - # or by dictionaries of arguments for `Clipping.parse` - "clippings": [], # A list of planes that define clipping half space solids - "booleans": [], # Any existing IfcBooleanResults - } - for key, value in settings.items(): - self.settings[key] = value +def add_wall_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "context": None, # IfcGeometricRepresentationContext + "length": 1.0, + "height": 3.0, + "offset": 0.0, + "thickness": 0.2, + # Sloped walls along the wall's X axis, provided in radians + "x_angle": 0, + # Planes are defined either by Clipping objects + # or by dictionaries of arguments for `Clipping.parse` + "clippings": [], # A list of planes that define clipping half space solids + "booleans": [], # Any existing IfcBooleanResults + } + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py index 94b97636aa..b0a46e69ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_window_representation.py @@ -56,12 +56,7 @@ def create_ifc_window_frame_simple( th_left, th_up, th_right, th_bottom = thickness def get_extruded_profile(profile): - return builder.extrude( - profile, - size.y, - position=position, - **builder.extrude_kwargs("Y") - ) + return builder.extrude(profile, size.y, position=position, **builder.extrude_kwargs("Y")) # if all lining sides are present then we can just use two rectangles # as inner and outer curves of the profile @@ -207,12 +202,7 @@ def create_ifc_window( glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0) glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0]) - glass = builder.extrude( - glass_rect, - glass_thickness, - position=glass_position, - **builder.extrude_kwargs("Y") - ) + glass = builder.extrude(glass_rect, glass_thickness, position=glass_position, **builder.extrude_kwargs("Y")) output_items = [lining_items, frame_extruded_items, [glass]] builder.translate(chain(*output_items), position) @@ -220,73 +210,76 @@ def create_ifc_window( return output_items -class Usecase: - def __init__(self, file, **settings): - """units in settings expected to be in ifc project units""" - self.file = file - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm - # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm - self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)} - self.settings.update( - { - "context": None, # IfcGeometricRepresentationContext - # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, - # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, - # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL - "partition_type": "SINGLE_PANEL", - "overall_height": self.convert_si_to_unit(0.9), - "overall_width": self.convert_si_to_unit(0.6), - "lining_properties": { - "LiningDepth": self.convert_si_to_unit(0.050), - "LiningThickness": self.convert_si_to_unit(0.050), - "LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall - # offset from the wall - "LiningToPanelOffsetX": self.convert_si_to_unit(0.025), - # offset from the lining - # that way it allows you to define overall_depth constant between all panels - # and still have panels with different size: - # overall_depth = lining_depth + offset_y - # full offset from X axis = overall_depth - frame_depth - "LiningToPanelOffsetY": self.convert_si_to_unit(0.025), - # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, - # TriplePanelLeft, TriplePanelRight - # mullion - horizontal distance between panels - "MullionThickness": self.convert_si_to_unit(0.050), - # distance from the first lining to the mullion center - "FirstMullionOffset": self.convert_si_to_unit(0.3), - # applies to TriplePanelVertical - # distance from the first lining to the second mullion center - "SecondMullionOffset": self.convert_si_to_unit(0.45), - # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, - # TriplePanelLeft, TriplePanelRight - # works similar way to mullion - "TransomThickness": self.convert_si_to_unit(0.050), - "FirstTransomOffset": self.convert_si_to_unit(0.3), - # applies to TriplePanelHorizontal - "SecondTransomOffset": self.convert_si_to_unit(0.6), +def add_window_representation(file, **usecase_settings) -> None: + """units in usecase_settings expected to be in ifc project units""" + usecase = Usecase() + usecase.file = file + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm + # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm + usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)} + usecase.settings.update( + { + "context": None, # IfcGeometricRepresentationContext + # SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, + # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, + # TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL + "partition_type": "SINGLE_PANEL", + "overall_height": usecase.convert_si_to_unit(0.9), + "overall_width": usecase.convert_si_to_unit(0.6), + "lining_properties": { + "LiningDepth": usecase.convert_si_to_unit(0.050), + "LiningThickness": usecase.convert_si_to_unit(0.050), + "LiningOffset": usecase.convert_si_to_unit(0.050), # offset to the wall + # offset from the wall + "LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025), + # offset from the lining + # that way it allows you to define overall_depth constant between all panels + # and still have panels with different size: + # overall_depth = lining_depth + offset_y + # full offset from X axis = overall_depth - frame_depth + "LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025), + # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, + # TriplePanelLeft, TriplePanelRight + # mullion - horizontal distance between panels + "MullionThickness": usecase.convert_si_to_unit(0.050), + # distance from the first lining to the mullion center + "FirstMullionOffset": usecase.convert_si_to_unit(0.3), + # applies to TriplePanelVertical + # distance from the first lining to the second mullion center + "SecondMullionOffset": usecase.convert_si_to_unit(0.45), + # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, + # TriplePanelLeft, TriplePanelRight + # works similar way to mullion + "TransomThickness": usecase.convert_si_to_unit(0.050), + "FirstTransomOffset": usecase.convert_si_to_unit(0.3), + # applies to TriplePanelHorizontal + "SecondTransomOffset": usecase.convert_si_to_unit(0.6), + "ShapeAspectStyle": None, # DEPRECATED + }, + "panel_properties": [ + { + "FrameDepth": usecase.convert_si_to_unit(0.035), # by Y + "FrameThickness": usecase.convert_si_to_unit(0.035), # by X + # BOTTOM, LEFT, MIDDLE, RIGHT, TOP + "PanelPosition": ..., # NEVER USED + # defines the basic ways to describe how window panels operate + # how it's hanged, how it opens + "OperationType": None, # NEVER USED "ShapeAspectStyle": None, # DEPRECATED }, - "panel_properties": [ - { - "FrameDepth": self.convert_si_to_unit(0.035), # by Y - "FrameThickness": self.convert_si_to_unit(0.035), # by X - # BOTTOM, LEFT, MIDDLE, RIGHT, TOP - "PanelPosition": ..., # NEVER USED - # defines the basic ways to describe how window panels operate - # how it's hanged, how it opens - "OperationType": None, # NEVER USED - "ShapeAspectStyle": None, # DEPRECATED - }, - ], - } - ) + ], + } + ) - for key, value in settings.items(): - self.settings[key] = value - self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]] + return usecase.execute() + +class Usecase: def execute(self): builder = ShapeBuilder(self.file) overall_height = self.settings["overall_height"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py index 41d7f9422b..1df964d845 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/assign_representation.py @@ -20,13 +20,16 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"product": None, "representation": None} - for key, value in settings.items(): - self.settings[key] = value +def assign_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": None, "representation": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["product"].is_a("IfcProduct"): product_type = ifcopenshell.util.element.get_type(self.settings["product"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py index 9339752d4c..64df2e59d6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_element.py @@ -21,44 +21,41 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "description": None, - } - for key, value in settings.items(): - self.settings[key] = value +def connect_element(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "description": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] + incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - for rel in self.settings["relating_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: - rel.Description = self.settings["description"] - return rel + for rel in settings["relating_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]: + rel.Description = settings["description"] + return rel - return self.file.createIfcRelConnectsElements( - ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - Description=self.settings["description"], - RelatingElement=self.settings["relating_element"], - RelatedElement=self.settings["related_element"], - ) + return file.createIfcRelConnectsElements( + ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + Description=settings["description"], + RelatingElement=settings["relating_element"], + RelatedElement=settings["related_element"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py index 1a610a7d2c..7cc60c4ef1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/connect_path.py @@ -21,76 +21,73 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "relating_connection": "NOTDEFINED", - "related_connection": "NOTDEFINED", - "description": None, - } - for key, value in settings.items(): - self.settings[key] = value +def connect_path(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "relating_connection": "NOTDEFINED", + "related_connection": "NOTDEFINED", + "description": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedTo: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if rel.RelatedElement == self.settings["related_element"]: - incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == self.settings["relating_connection"] - ): - incompatible_connections.append(rel) + incompatible_connections = [] + for rel in settings["relating_element"].ConnectedTo: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if rel.RelatedElement == settings["related_element"]: + incompatible_connections.append(rel) + elif ( + rel.RelatingConnectionType in ["ATSTART", "ATEND"] + and rel.RelatingConnectionType == settings["relating_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["relating_element"].ConnectedFrom: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == self.settings["relating_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if ( + rel.RelatedConnectionType in ["ATSTART", "ATEND"] + and rel.RelatedConnectionType == settings["relating_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedFrom: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if ( - rel.RelatedConnectionType in ["ATSTART", "ATEND"] - and rel.RelatedConnectionType == self.settings["related_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedFrom: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if ( + rel.RelatedConnectionType in ["ATSTART", "ATEND"] + and rel.RelatedConnectionType == settings["related_connection"] + ): + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if not rel.is_a("IfcRelConnectsPathElements"): - continue - if rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) - elif ( - rel.RelatingConnectionType in ["ATSTART", "ATEND"] - and rel.RelatingConnectionType == self.settings["related_connection"] - ): - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if not rel.is_a("IfcRelConnectsPathElements"): + continue + if rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) + elif ( + rel.RelatingConnectionType in ["ATSTART", "ATEND"] + and rel.RelatingConnectionType == settings["related_connection"] + ): + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - return self.file.createIfcRelConnectsPathElements( - ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - Description=self.settings["description"], - RelatingElement=self.settings["relating_element"], - RelatedElement=self.settings["related_element"], - RelatingConnectionType=self.settings["relating_connection"], - RelatedConnectionType=self.settings["related_connection"], - RelatingPriorities=[], - RelatedPriorities=[], - ) + return file.createIfcRelConnectsPathElements( + ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + Description=settings["description"], + RelatingElement=settings["relating_element"], + RelatedElement=settings["related_element"], + RelatingConnectionType=settings["relating_connection"], + RelatedConnectionType=settings["related_connection"], + RelatingPriorities=[], + RelatedPriorities=[], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py index e228730974..cd508d4a3e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/create_2pt_wall.py @@ -21,20 +21,25 @@ import ifcopenshell.api import ifcopenshell.util.unit -class Usecase: - def __init__(self, file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True): - self.file = file - self.settings = { - "element": element, - "context": context, - "p1": p1, - "p2": p2, - "elevation": elevation, - "height": height, - "thickness": thickness, - "is_si": is_si - } +def create_2pt_wall( + file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True +) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "context": context, + "p1": p1, + "p2": p2, + "elevation": elevation, + "height": height, + "thickness": thickness, + "is_si": is_si, + } + return usecase.execute() + +class Usecase: def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) @@ -44,9 +49,9 @@ class Usecase: length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"])) if not self.settings["is_si"]: - length=self.convert_unit_to_si(length) - self.settings["height"]=self.convert_unit_to_si(self.settings["height"]) - self.settings["thickness"]=self.convert_unit_to_si(self.settings["thickness"]) + length = self.convert_unit_to_si(length) + self.settings["height"] = self.convert_unit_to_si(self.settings["height"]) + self.settings["thickness"] = self.convert_unit_to_si(self.settings["thickness"]) self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0]) self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1]) self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py index 680b3e85e7..1e1eaaa82b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_element.py @@ -20,38 +20,35 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - } - for key, value in settings.items(): - self.settings[key] = value +def disconnect_element(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - incompatible_connections = [] + incompatible_connections = [] - for rel in self.settings["relating_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["relating_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]: - incompatible_connections.append(rel) + for rel in settings["relating_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedTo: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedTo: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]: + incompatible_connections.append(rel) - for rel in self.settings["related_element"].ConnectedFrom: - if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["relating_element"]: - incompatible_connections.append(rel) + for rel in settings["related_element"].ConnectedFrom: + if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]: + incompatible_connections.append(rel) - if incompatible_connections: - for connection in set(incompatible_connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if incompatible_connections: + for connection in set(incompatible_connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py index 8e52c7e4ff..14bbaf9e7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/disconnect_path.py @@ -21,38 +21,35 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "relating_element": None, - "related_element": None, - "element": None, - "connection_type": None, - } - for key, value in settings.items(): - self.settings[key] = value +def disconnect_path(file, **usecase_settings) -> None: + settings = { + "relating_element": None, + "related_element": None, + "element": None, + "connection_type": None, + } + for key, value in usecase_settings.items(): + settings[key] = value - def execute(self): - if self.settings["connection_type"] and self.settings["element"]: - connections = [ - r - for r in self.settings["element"].ConnectedTo - if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == self.settings["connection_type"] - ] + [ - r - for r in self.settings["element"].ConnectedFrom - if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == self.settings["connection_type"] - ] - else: - connections = [ - r - for r in self.settings["relating_element"].ConnectedTo - if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == self.settings["related_element"] - ] + if settings["connection_type"] and settings["element"]: + connections = [ + r + for r in settings["element"].ConnectedTo + if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"] + ] + [ + r + for r in settings["element"].ConnectedFrom + if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"] + ] + else: + connections = [ + r + for r in settings["relating_element"].ConnectedTo + if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"] + ] - for connection in set(connections): - history = connection.OwnerHistory - self.file.remove(connection) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for connection in set(connections): + history = connection.OwnerHistory + file.remove(connection) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py index 2f442388b6..768468e03a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/edit_object_placement.py @@ -27,24 +27,26 @@ from typing import Optional, Union NPArrayOfFloats = npt.NDArray[np.float64] -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - product: ifcopenshell.entity_instance, - matrix: Optional[NPArrayOfFloats] = None, - is_si=True, - should_transform_children=False, - ): - self.file = file - self.settings = { - "product": product, - "matrix": matrix if matrix is not None else np.eye(4), - "is_si": is_si, - "should_transform_children": should_transform_children, - } +def edit_object_placement( + file: ifcopenshell.file, + product: ifcopenshell.entity_instance, + matrix: Optional[NPArrayOfFloats] = None, + is_si=True, + should_transform_children=False, +) -> ifcopenshell.entity_instance: + usecase = Usecase() + usecase.file = file + usecase.settings = { + "product": product, + "matrix": matrix if matrix is not None else np.eye(4), + "is_si": is_si, + "should_transform_children": should_transform_children, + } + return usecase.execute() - def execute(self) -> ifcopenshell.entity_instance: + +class Usecase: + def execute(self): if not hasattr(self.settings["product"], "ObjectPlacement"): return self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py index 1e4b4e2207..83e1e1e821 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/map_representation.py @@ -17,14 +17,17 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"representation": None} - self.ifc_vertices = [] - for key, value in settings.items(): - self.settings[key] = value +def map_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"representation": None} + usecase.ifc_vertices = [] + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): mapping_source = self.get_mapping_source() diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py index 85ca854233..5d81203a5d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py @@ -19,13 +19,16 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"item": None} - for key, value in settings.items(): - self.settings[key] = value +def remove_boolean(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"item": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): item = None for inverse in self.file.get_inverse(self.settings["item"]): diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py index d7893ea317..7aafb751e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py @@ -19,62 +19,57 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, representation: ifcopenshell.entity_instance): - """Remove a representation. +def remove_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None: + """Remove a representation. - Also purges representation items and their related elements - like IfcStyledItem, tessellated facesets colours and UV map. + Also purges representation items and their related elements + like IfcStyledItem, tessellated facesets colours and UV map. - :param representation: IfcRepresentation to remove. - Note that it's expected that IfcRepresentation won't be in use - before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect) - otherwise representation won't be removed. - :type representation: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"representation": representation} + :param representation: IfcRepresentation to remove. + Note that it's expected that IfcRepresentation won't be in use + before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect) + otherwise representation won't be removed. + :type representation: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"representation": representation} - def execute(self) -> None: - styled_items = set() - presentation_layer_assignments = set() - textures = set() - colours = set() - for subelement in self.file.traverse(self.settings["representation"]): - if subelement.is_a("IfcRepresentationItem"): - [styled_items.add(s) for s in subelement.StyledByItem or []] - # IFC2X3 is using LayerAssignments - for s in ( - subelement.LayerAssignment - if hasattr(subelement, "LayerAssignment") - else subelement.LayerAssignments - ): - presentation_layer_assignments.add(s) - # IfcTessellatedFaceSet inverses - [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []] - [colours.add(t) for t in getattr(subelement, "HasColours", []) or []] - elif subelement.is_a("IfcRepresentation"): - for layer in subelement.LayerAssignments: - presentation_layer_assignments.add(layer) + styled_items = set() + presentation_layer_assignments = set() + textures = set() + colours = set() + for subelement in file.traverse(settings["representation"]): + if subelement.is_a("IfcRepresentationItem"): + [styled_items.add(s) for s in subelement.StyledByItem or []] + # IFC2X3 is using LayerAssignments + for s in ( + subelement.LayerAssignment if hasattr(subelement, "LayerAssignment") else subelement.LayerAssignments + ): + presentation_layer_assignments.add(s) + # IfcTessellatedFaceSet inverses + [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []] + [colours.add(t) for t in getattr(subelement, "HasColours", []) or []] + elif subelement.is_a("IfcRepresentation"): + for layer in subelement.LayerAssignments: + presentation_layer_assignments.add(layer) - ifcopenshell.util.element.remove_deep2( - self.file, - self.settings["representation"], - also_consider=list(styled_items | presentation_layer_assignments | colours), - do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"), - ) + ifcopenshell.util.element.remove_deep2( + file, + settings["representation"], + also_consider=list(styled_items | presentation_layer_assignments | colours), + do_not_delete=file.by_type("IfcGeometricRepresentationContext"), + ) - for texture in textures: - ifcopenshell.util.element.remove_deep2(self.file, texture) - for colour in colours: - ifcopenshell.util.element.remove_deep2(self.file, colour) + for texture in textures: + ifcopenshell.util.element.remove_deep2(file, texture) + for colour in colours: + ifcopenshell.util.element.remove_deep2(file, colour) - to_delete = getattr(self.file, "to_delete", ()) - for element in styled_items: - if not element.Item or element.Item in to_delete: - self.file.remove(element) - for element in presentation_layer_assignments: - if all(item in to_delete for item in element.AssignedItems): - self.file.remove(element) + to_delete = getattr(file, "to_delete", ()) + for element in styled_items: + if not element.Item or element.Item in to_delete: + file.remove(element) + for element in presentation_layer_assignments: + if all(item in to_delete for item in element.AssignedItems): + file.remove(element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py index 389aad88d6..83b1570ac6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/unassign_representation.py @@ -20,13 +20,16 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = {"product": None, "representation": None} - for key, value in settings.items(): - self.settings[key] = value +def unassign_representation(file, **usecase_settings) -> None: + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": None, "representation": None} + for key, value in usecase_settings.items(): + usecase.settings[key] = value + return usecase.execute() + +class Usecase: def execute(self): if self.settings["product"].is_a("IfcProduct"): self.unassign_product_representation(self.settings["product"], self.settings["representation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py index e0caddbe3c..1aa858db17 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_georeferencing import add_georeferencing +from .edit_georeferencing import edit_georeferencing +from .remove_georeferencing import remove_georeferencing diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py index a957970a91..5da8819e47 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/add_georeferencing.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file): - """Add empty georeferencing entities to a model +def add_georeferencing(file) -> None: + """Add empty georeferencing entities to a model - By default, models are not georeferenced. Georeferencing requires two - entities: a definition of the projected coordinated reference system - (CRS) used, and the transformation parameters between any local coordinate - system and that projected CRS if any. + By default, models are not georeferenced. Georeferencing requires two + entities: a definition of the projected coordinated reference system + (CRS) used, and the transformation parameters between any local coordinate + system and that projected CRS if any. - This function will create the entities to store the projected CRS and - map conversion transformation, but will leave all the parameters blank. - It is this the users responsibility to specify the correct - georeferencing parameters. See - ifcopenshell.api.georeference.edit_georeferencing. + This function will create the entities to store the projected CRS and + map conversion transformation, but will leave all the parameters blank. + It is this the users responsibility to specify the correct + georeferencing parameters. See + ifcopenshell.api.georeference.edit_georeferencing. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("georeference.add_georeferencing", model) - """ - self.file = file + ifcopenshell.api.run("georeference.add_georeferencing", model) + """ - def execute(self): - source_crs = None - for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): - if context.ContextType == "Model": - source_crs = context - break - if not source_crs: - return - projected_crs = self.file.create_entity("IfcProjectedCRS", **{"Name": ""}) - self.file.create_entity( - "IfcMapConversion", - **{ - "SourceCRS": source_crs, - "TargetCRS": projected_crs, - "Eastings": 0, - "Northings": 0, - "OrthogonalHeight": 0, - } - ) + source_crs = None + for context in file.by_type("IfcGeometricRepresentationContext", include_subtypes=False): + if context.ContextType == "Model": + source_crs = context + break + if not source_crs: + return + projected_crs = file.create_entity("IfcProjectedCRS", **{"Name": ""}) + file.create_entity( + "IfcMapConversion", + **{ + "SourceCRS": source_crs, + "TargetCRS": projected_crs, + "Eastings": 0, + "Northings": 0, + "OrthogonalHeight": 0, + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py index 77554e1f7f..f4118d96e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/edit_georeferencing.py @@ -17,77 +17,80 @@ # along with IfcOpenShell. If not, see . +def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_north=None) -> None: + """Edits the attributes of a map conversion, projected CRS, and true north + + Setting the correct georeferencing parameters is a complex topic and + should ideally be done with three parties present: the lead architect, + surveyor, and a third-party digital engineer with expertise in IFC to + moderate. For more information, read the BlenderBIM Add-on documentation + for Georeferencing: + https://docs.blenderbim.org/users/georeferencing.html + + For more information about the attributes and data types of an + IfcMapConversion, consult the IFC documentation. + + For more information about the attributes and data types of an + IfcProjectedCRS, consult the IFC documentation. + + True north is defined as a unitised 2D vector pointing to true north. + Note that true north is not part of georeferencing, and is only + optionally provided as a reference value, typically for solar analysis. + + See ifcopenshell.util.geolocation for more utilities to convert to and + from local and map coordinates to check your results. + + :param map_conversion: The IfcMapConversion dictionary of attribute + names and values you want to edit. + :type map_conversion: dict, optional + :param projected_crs: The IfcProjectedCRS dictionary of attribute + names and values you want to edit. + :type projected_crs: dict, optional + :param true_north: A unitised 2D vector, where each ordinate is a float + :type true_north: list[float] + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("georeference.add_georeferencing", model) + # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone + # 56, typically used in Sydney, Australia) but with no local + # coordinates. This is only recommended for horizontal construction + # projects, not for vertical construction (such as buildings). + ifcopenshell.api.run("georeference.edit_georeferencing", model, + projected_crs={"Name": "EPSG:7856"}) + + # For buildings, it is almost always recommended to specify map + # conversion parameters to a false origin and orientation to project + # north. See the diagram in the BlenderBIM Add-on Georeferencing + # documentation for correct calculation of the X Axis Abcissa and + # Ordinate. + ifcopenshell.api.run("georeference.edit_georeferencing", model, + projected_crs={"Name": "EPSG:7856"}, + map_conversion={ + "Eastings": 335087.17, # The architect nominates a false origin + "Northings": 6251635.41, # The architect nominates a false origin + # Note: this is the angle difference between Project North + # and Grid North. Remember: True North should never be used! + "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north + "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north + "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor! + }) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "map_conversion": map_conversion or {}, + "projected_crs": projected_crs or {}, + "true_north": true_north or [], + } + return usecase.execute() + + class Usecase: - def __init__(self, file, map_conversion=None, projected_crs=None, true_north=None): - """Edits the attributes of a map conversion, projected CRS, and true north - - Setting the correct georeferencing parameters is a complex topic and - should ideally be done with three parties present: the lead architect, - surveyor, and a third-party digital engineer with expertise in IFC to - moderate. For more information, read the BlenderBIM Add-on documentation - for Georeferencing: - https://docs.blenderbim.org/users/georeferencing.html - - For more information about the attributes and data types of an - IfcMapConversion, consult the IFC documentation. - - For more information about the attributes and data types of an - IfcProjectedCRS, consult the IFC documentation. - - True north is defined as a unitised 2D vector pointing to true north. - Note that true north is not part of georeferencing, and is only - optionally provided as a reference value, typically for solar analysis. - - See ifcopenshell.util.geolocation for more utilities to convert to and - from local and map coordinates to check your results. - - :param map_conversion: The IfcMapConversion dictionary of attribute - names and values you want to edit. - :type map_conversion: dict, optional - :param projected_crs: The IfcProjectedCRS dictionary of attribute - names and values you want to edit. - :type projected_crs: dict, optional - :param true_north: A unitised 2D vector, where each ordinate is a float - :type true_north: list[float] - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("georeference.add_georeferencing", model) - # This is the simplest scenario, a defined CRS (GDA2020 / MGA Zone - # 56, typically used in Sydney, Australia) but with no local - # coordinates. This is only recommended for horizontal construction - # projects, not for vertical construction (such as buildings). - ifcopenshell.api.run("georeference.edit_georeferencing", model, - projected_crs={"Name": "EPSG:7856"}) - - # For buildings, it is almost always recommended to specify map - # conversion parameters to a false origin and orientation to project - # north. See the diagram in the BlenderBIM Add-on Georeferencing - # documentation for correct calculation of the X Axis Abcissa and - # Ordinate. - ifcopenshell.api.run("georeference.edit_georeferencing", model, - projected_crs={"Name": "EPSG:7856"}, - map_conversion={ - "Eastings": 335087.17, # The architect nominates a false origin - "Northings": 6251635.41, # The architect nominates a false origin - # Note: this is the angle difference between Project North - # and Grid North. Remember: True North should never be used! - "XAxisAbscissa": cos(radians(-30)), # The architect nominates a project north - "XAxisOrdinate": sin(radians(-30)), # The architect nominates a project north - "Scale": 0.99956, # Ask your surveyor for your site's average combined scale factor! - }) - """ - self.file = file - self.settings = { - "map_conversion": map_conversion or {}, - "projected_crs": projected_crs or {}, - "true_north": true_north or [], - } - def execute(self): map_conversion = self.file.by_type("IfcMapConversion")[0] projected_crs = self.file.by_type("IfcProjectedCRS")[0] diff --git a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py index 2ac32a0c6e..3d3941ed7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py +++ b/src/ifcopenshell-python/ifcopenshell/api/georeference/remove_georeferencing.py @@ -17,29 +17,26 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file): - """Remove georeferencing data +def remove_georeferencing(file) -> None: + """Remove georeferencing data - All georeferencing parameters such as projected CRS and map conversion - data will be lost. + All georeferencing parameters such as projected CRS and map conversion + data will be lost. - :return: None - :rtype: None + :return: None + :rtype: None - Example: + Example: - ifcopenshell.api.run("georeference.add_georeferencing", model) - # Let's change our mind - ifcopenshell.api.run("georeference.remove_georeferencing", model) - """ - self.file = file + ifcopenshell.api.run("georeference.add_georeferencing", model) + # Let's change our mind + ifcopenshell.api.run("georeference.remove_georeferencing", model) + """ - def execute(self): - map_conversion = self.file.by_type("IfcMapConversion")[0] - projected_crs = self.file.by_type("IfcProjectedCRS")[0] - if projected_crs.MapUnit and len(self.file.get_inverse(projected_crs.MapUnit)) == 1: - # TODO: go deeper for conversion units - self.file.remove(projected_crs.MapUnit) - self.file.remove(projected_crs) - self.file.remove(map_conversion) + map_conversion = file.by_type("IfcMapConversion")[0] + projected_crs = file.by_type("IfcProjectedCRS")[0] + if projected_crs.MapUnit and len(file.get_inverse(projected_crs.MapUnit)) == 1: + # TODO: go deeper for conversion units + file.remove(projected_crs.MapUnit) + file.remove(projected_crs) + file.remove(map_conversion) diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py index e0caddbe3c..c66e86a668 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/__init__.py @@ -15,3 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .create_axis_curve import create_axis_curve +from .create_grid_axis import create_grid_axis +from .remove_grid_axis import remove_grid_axis diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py index 21fead74e5..2f3520c662 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_axis_curve.py @@ -22,46 +22,49 @@ import ifcopenshell.util.placement from mathutils import Matrix # For now, we depend on Blender +def create_axis_curve(file, axis_curve=None, grid_axis=None) -> None: + """Adds curve geometry to a grid axis to represent the axis extents + + This currently depends on the Blender geometry kernel to function. + + An IFC grid will have a minimum of two axes (typically perpendicular). Each + axis will then have a line which represents the extents of the axis. + + :param axis_curve: The Blender object that contains a mesh data block with a + single edge. + :type axis_curve: bpy.types.Object + :param grid_axis: The IfcGridAxis element to add geometry to. + :type grid_axis: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) + + # Assume you have these Blender objects in your active Blender session + obj1 = bpy.data.objects.get("AxisA") + obj2 = bpy.data.objects.get("Axis1") + ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a) + ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "axis_curve": axis_curve, # A Blender object + "grid_axis": grid_axis, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, axis_curve=None, grid_axis=None): - """Adds curve geometry to a grid axis to represent the axis extents - - This currently depends on the Blender geometry kernel to function. - - An IFC grid will have a minimum of two axes (typically perpendicular). Each - axis will then have a line which represents the extents of the axis. - - :param axis_curve: The Blender object that contains a mesh data block with a - single edge. - :type axis_curve: bpy.types.Object - :param grid_axis: The IfcGridAxis element to add geometry to. - :type grid_axis: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) - - # Assume you have these Blender objects in your active Blender session - obj1 = bpy.data.objects.get("AxisA") - obj2 = bpy.data.objects.get("Axis1") - ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj1, grid_axis=axis_a) - ifcopenshell.api.run("grid.create_axis_curve", model, axis_curve=obj2, grid_axis=axis_1) - """ - self.file = file - self.settings = { - "axis_curve": axis_curve, # A Blender object - "grid_axis": grid_axis, - } - def execute(self): existing_curve = self.settings["grid_axis"].AxisCurve if existing_curve and len(self.file.get_inverse(existing_curve)) == 1: diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py index de667bb5bc..f089b43b98 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/create_grid_axis.py @@ -17,69 +17,66 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None): - """Adds a new grid axis to a grid +def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None) -> None: + """Adds a new grid axis to a grid - An IFC grid will typically have a minimum of two axes which will be - perpendicular to one another. Grids may be rectangular (typically - perpendicular lines), radial (where one set of axes is a circle and the - other is a line), or triangular (three sets of axes, each at a different - angle to one another). + An IFC grid will typically have a minimum of two axes which will be + perpendicular to one another. Grids may be rectangular (typically + perpendicular lines), radial (where one set of axes is a circle and the + other is a line), or triangular (three sets of axes, each at a different + angle to one another). - For a simple rectangular grid, the "UAxes" are a set of one or more - horizontal axes, which are typically labeled with the convention of A, - B, C, etc. The "VAxes" is another set of one or more vertical axes, - typically labeled with the convention of 1, 2, 3, etc. These axes are - horizontal or vertical relative to project north. + For a simple rectangular grid, the "UAxes" are a set of one or more + horizontal axes, which are typically labeled with the convention of A, + B, C, etc. The "VAxes" is another set of one or more vertical axes, + typically labeled with the convention of 1, 2, 3, etc. These axes are + horizontal or vertical relative to project north. - For a radial grid, the "UAxes" are straight lines, typically radiating - from a central point. The "VAxes" are circular perimeters, with the - center of these circles being the same central point. + For a radial grid, the "UAxes" are straight lines, typically radiating + from a central point. The "VAxes" are circular perimeters, with the + center of these circles being the same central point. - For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one - or more straight lines. + For a triangular grid, the UAxes, VAxes, and WAxes are all sets of one + or more straight lines. - :param axis_tag: The name of the axis, that would typically be labeled - on drawings or described on site during coordination, such as A, B, - C, 1, 2, 3, etc. Defaults to "A". - :type axis_tag: str, optional - :param same_sense: Determines whether the direction of the axis's line - is reversed. True means the direction the geometry is defined in - represents the direction of the axis. False means the direction is - reversed. Leave as True if unsure. Defaults to "True". - :type same_sense: bool, optional - :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on - which set of axes the new axis you are adding should belong to. - Defaults to "UAxes". - :type uvw_axes: str, optional - :param grid: The IfcGrid you are adding the axis to. - :type grid: ifcopenshell.entity_instance - :return: The newly created IfcGridAxis - :rtype: ifcopenshell.entity_instance + :param axis_tag: The name of the axis, that would typically be labeled + on drawings or described on site during coordination, such as A, B, + C, 1, 2, 3, etc. Defaults to "A". + :type axis_tag: str, optional + :param same_sense: Determines whether the direction of the axis's line + is reversed. True means the direction the geometry is defined in + represents the direction of the axis. False means the direction is + reversed. Leave as True if unsure. Defaults to "True". + :type same_sense: bool, optional + :param uvw_axes: Choose from "UAxes", "VAxes" or "WAxes" depending on + which set of axes the new axis you are adding should belong to. + Defaults to "UAxes". + :type uvw_axes: str, optional + :param grid: The IfcGrid you are adding the axis to. + :type grid: ifcopenshell.entity_instance + :return: The newly created IfcGridAxis + :rtype: ifcopenshell.entity_instance - Example: + Example: - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) - """ - self.file = file - self.settings = { - "axis_tag": axis_tag or "A", - "same_sense": same_sense or True, - "uvw_axes": uvw_axes or "UAxes", # Choose which axes - "grid": grid, - } + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) + """ + settings = { + "axis_tag": axis_tag or "A", + "same_sense": same_sense or True, + "uvw_axes": uvw_axes or "UAxes", # Choose which axes + "grid": grid, + } - def execute(self): - element = self.file.create_entity( - "IfcGridAxis", **{"AxisTag": self.settings["axis_tag"], "SameSense": self.settings["same_sense"]} - ) - axes = list(getattr(self.settings["grid"], self.settings["uvw_axes"]) or []) - axes.append(element) - setattr(self.settings["grid"], self.settings["uvw_axes"], axes) - return element + element = file.create_entity( + "IfcGridAxis", **{"AxisTag": settings["axis_tag"], "SameSense": settings["same_sense"]} + ) + axes = list(getattr(settings["grid"], settings["uvw_axes"]) or []) + axes.append(element) + setattr(settings["grid"], settings["uvw_axes"], axes) + return element diff --git a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py index b380778a67..51032ed52f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/grid/remove_grid_axis.py @@ -19,36 +19,33 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, axis=None): - """Removes a grid axis from a grid +def remove_grid_axis(file, axis=None) -> None: + """Removes a grid axis from a grid - :param axis: The IfcGridAxis you want to remove. - :type axis: ifcopenshell.entity_instance - :return: None - :rtype: None + :param axis: The IfcGridAxis you want to remove. + :type axis: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - # A pretty standard rectangular grid, with only two axes. - grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") - axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="A", uvw_axes="UAxes", grid=grid) - axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="1", uvw_axes="VAxes", grid=grid) + # A pretty standard rectangular grid, with only two axes. + grid = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcGrid") + axis_a = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="A", uvw_axes="UAxes", grid=grid) + axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="1", uvw_axes="VAxes", grid=grid) - # Let's create a third so we can remove it later - axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model, - axis_tag="2", uvw_axes="VAxes", grid=grid) + # Let's create a third so we can remove it later + axis_2 = ifcopenshell.api.run("grid.create_grid_axis", model, + axis_tag="2", uvw_axes="VAxes", grid=grid) - # Let's remove it! - ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2) - """ - self.file = file - self.settings = {"axis": axis} + # Let's remove it! + ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2) + """ + settings = {"axis": axis} - def execute(self): - if len(self.file.get_inverse(self.settings["axis"].AxisCurve)) == 1: - ifcopenshell.util.element.remove_deep(self.file, self.settings["axis"].AxisCurve) - self.file.remove(self.settings["axis"].AxisCurve) - self.file.remove(self.settings["axis"]) + if len(file.get_inverse(settings["axis"].AxisCurve)) == 1: + ifcopenshell.util.element.remove_deep(file, settings["axis"].AxisCurve) + file.remove(settings["axis"].AxisCurve) + file.remove(settings["axis"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py index e0caddbe3c..5b729b0dfc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_group import add_group +from .assign_group import assign_group +from .edit_group import edit_group +from .remove_group import remove_group +from .unassign_group import unassign_group +from .update_group_products import update_group_products diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py index 298ec42ba6..1d576f7173 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/add_group.py @@ -20,44 +20,41 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, Name="Unnamed", Description=None): - """Adds a new group +def add_group(file, Name="Unnamed", Description=None) -> None: + """Adds a new group - An IFC group is an arbitrary collection of products, which are typically - physical. It may be used when there is no other more specific group - which may be used. Other types of groups include distribution systems, - which group together products that are connected and circulate a medium - (such as fluid or electricity), or zones, which group together spaces, - or structural load groups, which group together loads for structural - analysis, or inventories, which are groups of assets. + An IFC group is an arbitrary collection of products, which are typically + physical. It may be used when there is no other more specific group + which may be used. Other types of groups include distribution systems, + which group together products that are connected and circulate a medium + (such as fluid or electricity), or zones, which group together spaces, + or structural load groups, which group together loads for structural + analysis, or inventories, which are groups of assets. - :param Name: The name of the group. Defaults to "Unnamed" - :type Name: str, optional - :param Description: The description of the purpose of the group. - :type Description: str, optional - :return: The newly created IfcGroup - :rtype: ifcopenshell.entity_instance + :param Name: The name of the group. Defaults to "Unnamed" + :type Name: str, optional + :param Description: The description of the purpose of the group. + :type Description: str, optional + :return: The newly created IfcGroup + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - """ - self.file = file - self.settings = { - "Name": Name or "Unnamed", - "Description": Description, + ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + """ + settings = { + "Name": Name or "Unnamed", + "Description": Description, + } + + return file.create_entity( + "IfcGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["Name"], + "Description": settings["Description"], } - - def execute(self): - return self.file.create_entity( - "IfcGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["Name"], - "Description": self.settings["Description"], - } - ) + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py index d312a95bd6..c5afd5b9b0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py @@ -21,56 +21,53 @@ import ifcopenshell.api from typing import Union -class Usecase: - def __init__( - self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance - ): - """Assigns products to a group +def assign_group( + file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns products to a group - If a product is already assigned to the group, it will not be assigned - twice. + If a product is already assigned to the group, it will not be assigned + twice. - :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: A list of IfcProduct elements to assign to the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to assign the products to + :type group: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - ifcopenshell.api.run("group.assign_group", model, - products=model.by_type("IfcFurniture"), group=group) - """ - self.file = file - self.settings = { - "products": products, - "group": group, - } + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + ifcopenshell.api.run("group.assign_group", model, + products=model.by_type("IfcFurniture"), group=group) + """ + settings = { + "products": products, + "group": group, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - if not self.settings["group"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": self.settings["products"], - "RelatingGroup": self.settings["group"], - } - ) - rel = self.settings["group"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - products = set(self.settings["products"]) - if products.issubset(related_objects): - return rel - rel.RelatedObjects = list(related_objects | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + if not settings["group"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": settings["products"], + "RelatingGroup": settings["group"], + } + ) + rel = settings["group"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + products = set(settings["products"]) + if products.issubset(related_objects): return rel + rel.RelatedObjects = list(related_objects | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py index 87fa9dcf12..1eb0c8d6f4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/edit_group.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, group=None, attributes=None): - """Edits the attributes of an IfcGroup +def edit_group(file, group=None, attributes=None) -> None: + """Edits the attributes of an IfcGroup - For more information about the attributes and data types of an - IfcGroup, consult the IFC documentation. + For more information about the attributes and data types of an + IfcGroup, consult the IFC documentation. - :param group: The IfcGroup entity you want to edit - :type group: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param group: The IfcGroup entity you want to edit + :type group: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - ifcopenshell.api.run("group.edit_group", model, - group=group, attributes={"Description": "All furniture and joinery included in the unit"}) - """ - self.file = file - self.settings = {"group": group, "attributes": attributes or {}} + group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + ifcopenshell.api.run("group.edit_group", model, + group=group, attributes={"Description": "All furniture and joinery included in the unit"}) + """ + settings = {"group": group, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["group"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["group"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py index c87b36e316..05e85f3fc0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/remove_group.py @@ -21,53 +21,50 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, group=None): - """Removes a group +def remove_group(file, group=None) -> None: + """Removes a group - All products assigned to the group will remain, but the relationship to - the group will be removed. + All products assigned to the group will remain, but the relationship to + the group will be removed. - :param group: The IfcGroup entity you want to remove - :type group: ifcopenshell.entity_instance - :return: None - :rtype: None + :param group: The IfcGroup entity you want to remove + :type group: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") - ifcopenshell.api.run("group.remove_group", model, group=group) - """ - self.file = file - self.settings = {"group": group} + group = ifcopenshell.api.run("group.add_group", model, Name="Unit 1A") + ifcopenshell.api.run("group.remove_group", model, group=group) + """ + settings = {"group": group} - def execute(self): - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["group"])]: - try: - inverse = self.file.by_id(inverse_id) - except: - continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["group"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToGroup"): - if inverse.RelatingGroup == self.settings["group"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["group"].OwnerHistory - self.file.remove(self.settings["group"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for inverse_id in [i.id() for i in file.get_inverse(settings["group"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["group"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToGroup"): + if inverse.RelatingGroup == settings["group"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["group"].OwnerHistory + file.remove(settings["group"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py index 9229281a69..c486cceab6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/unassign_group.py @@ -21,48 +21,47 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance): - """Unassigns products from a group +def unassign_group( + file: ifcopenshell.file, products: list[ifcopenshell.entity_instance], group: ifcopenshell.entity_instance +) -> None: + """Unassigns products from a group - If the product isn't assigned to the group, nothing will happen. + If the product isn't assigned to the group, nothing will happen. - :param products: A list of IfcProduct elements to unassign from the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to unassign from - :type group: ifcopenshell.entity_instance - :return: None - :rtype: None + :param products: A list of IfcProduct elements to unassign from the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to unassign from + :type group: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - furniture = model.by_type("IfcFurniture") - ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group) + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + furniture = model.by_type("IfcFurniture") + ifcopenshell.api.run("group.assign_group", model, products=furniture, group=group) - bad_furniture = furniture[0] - ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group) - """ - self.file = file - self.settings = { - "products": products, - "group": group, - } + bad_furniture = furniture[0] + ifcopenshell.api.run("group.unassign_group", model, products=[bad_furniture], group=group) + """ + settings = { + "products": products, + "group": group, + } - def execute(self) -> None: - if not self.settings["group"].IsGroupedBy: - return - rel = self.settings["group"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - products = set(self.settings["products"]) - related_objects -= products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if not settings["group"].IsGroupedBy: + return + rel = settings["group"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + products = set(settings["products"]) + related_objects -= products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py index 61b96c2ba6..f526666fa2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/update_group_products.py @@ -20,51 +20,48 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, group=None, products=None): - """Sets a group products to be an explicit list of products +def update_group_products(file, group=None, products=None) -> None: + """Sets a group products to be an explicit list of products - Any previous products assigned to that group will have their assignment - removed. + Any previous products assigned to that group will have their assignment + removed. - :param products: A list of IfcProduct elements to assign to the group - :type products: list[ifcopenshell.entity_instance] - :param group: The IfcGroup to assign the products to - :type group: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance + :param products: A list of IfcProduct elements to assign to the group + :type products: list[ifcopenshell.entity_instance] + :param group: The IfcGroup to assign the products to + :type group: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") - ifcopenshell.api.run("group.update_group_products", model, - products=model.by_type("IfcFurniture"), group=group) - """ - self.file = file - self.settings = { - "group": group, - "products": products, - } + group = ifcopenshell.api.run("group.add_group", model, Name="Furniture") + ifcopenshell.api.run("group.update_group_products", model, + products=model.by_type("IfcFurniture"), group=group) + """ + settings = { + "group": group, + "products": products, + } - def execute(self): - if not self.settings["group"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": self.settings["products"], - "RelatingGroup": self.settings["group"], - } - ) - else: - # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes - # where the cardinality is 0:? - vulevukusej - rel = self.settings["group"].IsGroupedBy[0] - existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")] + if not settings["group"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": settings["products"], + "RelatingGroup": settings["group"], + } + ) + else: + # assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes + # where the cardinality is 0:? - vulevukusej + rel = settings["group"].IsGroupedBy[0] + existing_sub_groups = [g for g in rel.RelatedObjects if g.is_a("IfcGroup")] - rel.RelatedObjects = self.settings["products"] - for g in existing_sub_groups: - rel.RelatedObjects.add(g) + rel.RelatedObjects = settings["products"] + for g in existing_sub_groups: + rel.RelatedObjects.add(g) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py index e0caddbe3c..03145bf34a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_layer import add_layer +from .assign_layer import assign_layer +from .edit_layer import edit_layer +from .remove_layer import remove_layer +from .unassign_layer import unassign_layer diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py index 8638a76b22..5379ff1b3a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/add_layer.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, Name=None): - """Adds a new layer +def add_layer(file, Name=None) -> None: + """Adds a new layer - An IFC layer is like a CAD layer. Portions of an object's geometry - (typically portions of its 2D linework) can be assigned to layers, which - can provide stylistic information such as line weights, colours, or - simply be used for filtering. + An IFC layer is like a CAD layer. Portions of an object's geometry + (typically portions of its 2D linework) can be assigned to layers, which + can provide stylistic information such as line weights, colours, or + simply be used for filtering. - Layers have historically been used to organise CAD data and included in - ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to - be compatible with older, 2D-oriented, layer-based workflows. + Layers have historically been used to organise CAD data and included in + ISO standards such as ISO 13567 or by the AIA. This alllows IFC data to + be compatible with older, 2D-oriented, layer-based workflows. - Some software that are still based on layers, such as Tekla or ArchiCAD - may also use this layer information for filtering. + Some software that are still based on layers, such as Tekla or ArchiCAD + may also use this layer information for filtering. - :param Name: The name of the layer. Defaults to "Unnamed". - :type Name: str, optional - :return: The newly created IfcPresentationLayerAssignment element - :rtype: ifcopenshell.entity_instance + :param Name: The name of the layer. Defaults to "Unnamed". + :type Name: str, optional + :return: The newly created IfcPresentationLayerAssignment element + :rtype: ifcopenshell.entity_instance - Example: + Example: - ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N") - """ - self.file = file - self.settings = {"Name": Name or "Unnamed"} + ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL-FULL-DIMS-N") + """ + settings = {"Name": Name or "Unnamed"} - def execute(self): - return self.file.create_entity("IfcPresentationLayerAssignment", Name=self.settings["Name"]) + return file.create_entity("IfcPresentationLayerAssignment", Name=settings["Name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py index 93a863d66f..70926625c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/assign_layer.py @@ -19,64 +19,61 @@ import ifcopenshell -class Usecase: - def __init__( - self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance - ): - """Assigns representation items to a layer +def assign_layer( + file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance +) -> None: + """Assigns representation items to a layer - In IFC, instead of objects being assigned to layers, representation - items are assigned to layers. Representation items are portions of the - object's representation. For example, this allows a single IFC Window - element to have portions of its 2D linework (e.g. the cross section of - its frame) assigned to one layer, and another portion (e.g. the glazing - panels) assigned to another layer. + In IFC, instead of objects being assigned to layers, representation + items are assigned to layers. Representation items are portions of the + object's representation. For example, this allows a single IFC Window + element to have portions of its 2D linework (e.g. the cross section of + its frame) assigned to one layer, and another portion (e.g. the glazing + panels) assigned to another layer. - :param items: The list of IfcRepresentationItems to assign to the layer. This - should be the items from the object's IfcShapeRepresentation. - :type items: list[ifcopenshell.entity_instance] - :param layer: The IfcPresentationLayerAssignment layer to assign the - item to. - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param items: The list of IfcRepresentationItems to assign to the layer. This + should be the items from the object's IfcShapeRepresentation. + :type items: list[ifcopenshell.entity_instance] + :param layer: The IfcPresentationLayerAssignment layer to assign the + item to. + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remember, all geometry needs to specify the context it is part of first. - # See ifcopenshell.api.context.add_context for details. - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model - ) + # Remember, all geometry needs to specify the context it is part of first. + # See ifcopenshell.api.context.add_context for details. + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model + ) - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Now let's create a layer that contains walls - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + # Now let's create a layer that contains walls + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - # And assign our wall representation item (in this example, there is - # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) - """ - self.file = file - self.settings = { - "items": items, - "layer": layer, - } + # And assign our wall representation item (in this example, there is + # only one item) to the layer. + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) + """ + settings = { + "items": items, + "layer": layer, + } - def execute(self) -> None: - # support AssignedItems == None since layer might just got created - layer = self.settings["layer"] - assigned_items = set(layer.AssignedItems or []) - items = set(self.settings["items"]) - if items.issubset(assigned_items): - return - layer.AssignedItems = list(assigned_items | items) + # support AssignedItems == None since layer might just got created + layer = settings["layer"] + assigned_items = set(layer.AssignedItems or []) + items = set(settings["items"]) + if items.issubset(assigned_items): + return + layer.AssignedItems = list(assigned_items | items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py index c2b1cbc99a..9d96156cfc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/edit_layer.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None, attributes=None): - """Edits the attributes of an IfcPresentationLayerAssignment +def edit_layer(file, layer=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationLayerAssignment - For more information about the attributes and data types of an - IfcPresentationLayerAssignment, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPresentationLayerAssignment, consult the IFC documentation. - :param layer: The IfcPresentationLayerAssignment entity you want to edit - :type layer: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param layer: The IfcPresentationLayerAssignment entity you want to edit + :type layer: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - ifcopenshell.api.run("layer.edit_layer", model, - layer=layer, attributes={"Description": "All walls, based on the AIA standard."}) - """ - self.file = file - self.settings = {"layer": layer, "attributes": attributes or {}} + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + ifcopenshell.api.run("layer.edit_layer", model, + layer=layer, attributes={"Description": "All walls, based on the AIA standard."}) + """ + settings = {"layer": layer, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["layer"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["layer"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py index 8e83e475ab..790b396174 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/remove_layer.py @@ -17,27 +17,24 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None): - """Removes a layer +def remove_layer(file, layer=None) -> None: + """Removes a layer - All representation items assigned to the layer will remain, but the - relationship to the layer will be removed. + All representation items assigned to the layer will remain, but the + relationship to the layer will be removed. - :param layer: The IfcPresentationLayerAssignment entity to remove - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param layer: The IfcPresentationLayerAssignment entity to remove + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - ifcopenshell.api.run("layer.remove_layer", model, layer=layer) - """ - self.file = file - self.settings = {"layer": layer} + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + ifcopenshell.api.run("layer.remove_layer", model, layer=layer) + """ + settings = {"layer": layer} - def execute(self): - self.file.remove(self.settings["layer"]) + file.remove(settings["layer"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py index f9d6a024a3..9418a28ad6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/layer/unassign_layer.py @@ -20,68 +20,65 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__( - self, file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance - ): - """Unassigns representation items from a layer +def unassign_layer( + file: ifcopenshell.file, items: list[ifcopenshell.entity_instance], layer: ifcopenshell.entity_instance +) -> None: + """Unassigns representation items from a layer - If the representation item isn't assigned to the layer, nothing will - happen. - If after unassignment layer won't have any assigned items it will be - removed to keep IFC valid. + If the representation item isn't assigned to the layer, nothing will + happen. + If after unassignment layer won't have any assigned items it will be + removed to keep IFC valid. - :param items: A list IfcRepresentationItem elements to unassign - :type items: list[ifcopenshell.entity_instance] - :param layer: The IfcPresentationLayerAssignment to unassign from - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param items: A list IfcRepresentationItem elements to unassign + :type items: list[ifcopenshell.entity_instance] + :param layer: The IfcPresentationLayerAssignment to unassign from + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remember, all geometry needs to specify the context it is part of first. - # See ifcopenshell.api.context.add_context for details. - model = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model - ) + # Remember, all geometry needs to specify the context it is part of first. + # See ifcopenshell.api.context.add_context for details. + model = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model + ) - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Now let's create a layer that contains walls - layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") + # Now let's create a layer that contains walls + layer = ifcopenshell.api.run("layer.add_layer", model, Name="AI-WALL") - # And assign our wall representation item (in this example, there is - # only one item) to the layer. - ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) + # And assign our wall representation item (in this example, there is + # only one item) to the layer. + ifcopenshell.api.run("layer.assign_layer", model, items=[representation.Items[0]], layer=layer) - # Let's undo it! - ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer) - """ - self.file = file - self.settings = { - "items": items, - "layer": layer, - } + # Let's undo it! + ifcopenshell.api.run("layer.unassign_layer", model, items=[representation.Items[0]], layer=layer) + """ + settings = { + "items": items, + "layer": layer, + } - def execute(self): - layer = self.settings["layer"] - assigned_items = set(layer.AssignedItems) or set() - items = set(self.settings["items"]) - if not items.issubset(assigned_items): - return - assigned_items = list(assigned_items - items) + layer = settings["layer"] + assigned_items = set(layer.AssignedItems) or set() + items = set(settings["items"]) + if not items.issubset(assigned_items): + return + assigned_items = list(assigned_items - items) - # keep IFC valid in case if there are no items left - if assigned_items: - layer.AssignedItems = assigned_items - else: - self.file.remove(layer) + # keep IFC valid in case if there are no items left + if assigned_items: + layer.AssignedItems = assigned_items + else: + file.remove(layer) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py index e0caddbe3c..dbb74de3d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/__init__.py @@ -15,3 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py index f20494ac5f..16cd00b914 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_library.py @@ -21,48 +21,45 @@ import ifcopenshell.util.schema import ifcopenshell.util.date -class Usecase: - def __init__(self, file, name=None): - """Adds a new library to the project +def add_library(file, name=None) -> None: + """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. + 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. + 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'). + 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. + 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 - :type name: str - :return: The newly created IfcLibraryInformation - :rtype: ifcopenshell.entity_instance + :param name: The name of the library + :type name: str + :return: The newly created IfcLibraryInformation + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("library.add_library", model, name="Brickschema") - """ - self.file = file - self.settings = {"name": name} + ifcopenshell.api.run("library.add_library", model, name="Brickschema") + """ + settings = {"name": name} - def execute(self): - return self.file.create_entity("IfcLibraryInformation", Name=self.settings["name"]) + return file.create_entity("IfcLibraryInformation", Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py index 84f6605cf0..626d413b3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/add_reference.py @@ -19,48 +19,45 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, library: ifcopenshell.entity_instance): - """Adds a new reference to a library +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. + 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. + 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 - :type library: ifcopenshell.entity_instance - :return: The newly created IfcLibraryReference element - :rtype: ifcopenshell.entity_instance + :param library: The IfcLibraryInformation element to add a reference to + :type library: ifcopenshell.entity_instance + :return: The newly created IfcLibraryReference element + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - """ - self.file = file - self.settings = { - "library": library, - } + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + """ + settings = { + "library": library, + } - def execute(self) -> ifcopenshell.entity_instance: - if self.file.schema == "IFC2X3": - reference = self.file.createIfcLibraryReference() - references = list(self.settings["library"].LibraryReference or []) - references.append(reference) - self.settings["library"].LibraryReference = references - return reference - return self.file.createIfcLibraryReference(ReferencedLibrary=self.settings["library"]) + if file.schema == "IFC2X3": + reference = file.createIfcLibraryReference() + references = list(settings["library"].LibraryReference or []) + references.append(reference) + settings["library"].LibraryReference = references + return reference + return file.createIfcLibraryReference(ReferencedLibrary=settings["library"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py index 8a0880ccf0..6cbd208865 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/assign_reference.py @@ -22,82 +22,75 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance - ): - """Associates a list products with a library reference +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. + 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 - :type products: list[ifcopenshell.entity_instance] - :param reference: The IfcLibraryReference you want the product to be - associated with. - :type reference: ifcopenshell.entity_instance - :return: The IfcRelAssociatesLibrary relationship entity - or `None` if `products` was an empty list or all products were - already assigned to the `reference`. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of IfcProducts you want to associate with the reference + :type products: list[ifcopenshell.entity_instance] + :param reference: The IfcLibraryReference you want the product to be + associated with. + :type reference: ifcopenshell.entity_instance + :return: The IfcRelAssociatesLibrary relationship entity + or `None` if `products` was an empty list or all products were + already assigned to the `reference`. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("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.run("root.create_entity", model, - ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") + # Let's assume we have an AHU in our model. + ahu = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") - # And now assign the IFC model's AHU with its Brickschema counterpart - ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) - """ - self.file = file - self.settings = { - "products": products, - "reference": reference, - } + # And now assign the IFC model's AHU with its Brickschema counterpart + ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) + """ + settings = { + "products": products, + "reference": reference, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? - referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"]) - products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) - products = products - referenced_elements + referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"]) + products: set[ifcopenshell.entity_instance] = set(settings["products"]) + products = products - referenced_elements - if not products: - return + if not products: + return - if self.file.schema == "IFC2X3": - rel = next( - ( - r - for r in self.file.by_type("IfcRelAssociatesLibrary") - if r.RelatingLibrary == self.settings["reference"] - ), - None, - ) - else: - rel = next(iter(self.settings["reference"].LibraryRefForObjects), None) + if file.schema == "IFC2X3": + rel = next( + (r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]), + None, + ) + else: + rel = next(iter(settings["reference"].LibraryRefForObjects), None) - if not rel: - return self.file.create_entity( - "IfcRelAssociatesLibrary", - GlobalId=ifcopenshell.guid.new(), - OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file), - RelatedObjects=list(products), - RelatingLibrary=self.settings["reference"], - ) + if not rel: + return file.create_entity( + "IfcRelAssociatesLibrary", + GlobalId=ifcopenshell.guid.new(), + OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file), + RelatedObjects=list(products), + RelatingLibrary=settings["reference"], + ) - related_objects = set(rel.RelatedObjects) | products - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + related_objects = set(rel.RelatedObjects) | products + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py index aca508cc38..5a53869a3f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_library.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, library=None, attributes=None): - """Edits the attributes of an IfcLibraryInformation +def edit_library(file, library=None, attributes=None) -> None: + """Edits the attributes of an IfcLibraryInformation - For more information about the attributes and data types of an - IfcLibraryInformation, consult the IFC documentation. + 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 - :type library: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param library: The IfcLibraryInformation entity you want to edit + :type library: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - ifcopenshell.api.run("library.edit_library", model, library=library, - attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."}) - """ + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + ifcopenshell.api.run("library.edit_library", model, library=library, + attributes={"Description": "A Brickschema TTL including only mechanical distribution systems."}) + """ - self.file = file - self.settings = {"library": library, "attributes": attributes or {}} + settings = {"library": library, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["library"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["library"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py index 35a1be4709..1d2487820a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/edit_reference.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, reference=None, attributes=None): - """Edits the attributes of an IfcLibraryReference +def edit_reference(file, reference=None, attributes=None) -> None: + """Edits the attributes of an IfcLibraryReference - For more information about the attributes and data types of an - IfcLibraryReference, consult the IFC documentation. + 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 - :type reference: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param reference: The IfcLibraryReference entity you want to edit + :type reference: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) - """ - self.file = file - self.settings = {"reference": reference, "attributes": attributes or {}} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("library.edit_reference", model, + reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + """ + settings = {"reference": reference, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["reference"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["reference"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py index e921016c4d..ba5244537c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_library.py @@ -20,35 +20,32 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, library=None): - """Removes a library +def remove_library(file, library=None) -> 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. + 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 - :type library: ifcopenshell.entity_instance - :return: None - :rtype: None + :param library: The IfcLibraryInformation entity you want to remove + :type library: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - ifcopenshell.api.run("library.remove_library", model, library=library) - """ - self.file = file - self.settings = {"library": library} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + ifcopenshell.api.run("library.remove_library", model, library=library) + """ + settings = {"library": library} - def execute(self): - for reference in set(self.settings["library"].HasLibraryReferences or []): - self.file.remove(reference) - self.file.remove(self.settings["library"]) - for rel in self.file.by_type("IfcRelAssociatesLibrary"): - if not rel.RelatingLibrary: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for reference in set(settings["library"].HasLibraryReferences or []): + file.remove(reference) + file.remove(settings["library"]) + for rel in file.by_type("IfcRelAssociatesLibrary"): + if not rel.RelatingLibrary: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py index d34973f6b2..0b5ad42d1a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/remove_reference.py @@ -20,34 +20,31 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, reference: ifcopenshell.entity_instance): - """Removes a library reference +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. + Any products which have relationships to this reference will not be + removed. - :param reference: The IfcLibraryReference entity you want to remove - :type reference: ifcopenshell.entity_instance - :return: None - :rtype: None + :param reference: The IfcLibraryReference entity you want to remove + :type reference: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - # Let's change our mind and remove it. - ifcopenshell.api.run("library.remove_reference", model, reference=reference) - """ - self.file = file - self.settings = {"reference": reference} + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + # Let's change our mind and remove it. + ifcopenshell.api.run("library.remove_reference", model, reference=reference) + """ + settings = {"reference": reference} - def execute(self) -> None: - for rel in self.settings["reference"].LibraryRefForObjects: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - self.file.remove(self.settings["reference"]) + for rel in settings["reference"].LibraryRefForObjects: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + file.remove(settings["reference"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py index 420b7fa0d4..c2f2837ad0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py @@ -21,70 +21,66 @@ import ifcopenshell.util.element import ifcopenshell.api -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - reference: ifcopenshell.entity_instance, - products: list[ifcopenshell.entity_instance], - ): - """Unassigns a product of products from a reference +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. + If the product isn't assigned to the reference, nothing will happen. - :param reference: The IfcLibraryReference to unassign from - :type reference: ifcopenshell.entity_instance - :param products: A list of IfcProduct elements to unassign from the reference - :type products: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param reference: The IfcLibraryReference to unassign from + :type reference: ifcopenshell.entity_instance + :param products: A list of IfcProduct elements to unassign from the reference + :type products: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") + library = ifcopenshell.api.run("library.add_library", model, name="Brickschema") - # Let's create a reference to a single AHU in our Brickschema dataset - reference = ifcopenshell.api.run("library.add_reference", model, library=library) - ifcopenshell.api.run("library.edit_reference", model, - reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"}) + # Let's create a reference to a single AHU in our Brickschema dataset + reference = ifcopenshell.api.run("library.add_reference", model, library=library) + ifcopenshell.api.run("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.run("root.create_entity", model, - ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") + # Let's assume we have an AHU in our model. + ahu = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER") - # And now assign the IFC model's AHU with its Brickschema counterpart - ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) + # And now assign the IFC model's AHU with its Brickschema counterpart + ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) - # Let's change our mind and unassign it. - ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu]) - """ + # Let's change our mind and unassign it. + ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu]) + """ - self.file = file - self.settings = {"reference": reference, "products": products} + settings = {"reference": reference, "products": products} - def execute(self): - # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + # 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(self.settings["products"]) - for product in products: - reference_rels.update(product.HasAssociations) + reference_rels: set[ifcopenshell.entity_instance] = set() + products = set(settings["products"]) + for product in products: + reference_rels.update(product.HasAssociations) - reference_rels = { - rel - for rel in reference_rels - if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"] - } + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"] + } - for rel in reference_rels: - related_objects = set(rel.RelatedObjects) - products - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py index e0caddbe3c..2831915f76 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/__init__.py @@ -15,3 +15,28 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_constituent import add_constituent +from .add_layer import add_layer +from .add_list_item import add_list_item +from .add_material import add_material +from .add_material_set import add_material_set +from .add_profile import add_profile +from .assign_material import assign_material +from .assign_profile import assign_profile +from .copy_material import copy_material +from .edit_assigned_material import edit_assigned_material +from .edit_constituent import edit_constituent +from .edit_layer import edit_layer +from .edit_layer_usage import edit_layer_usage +from .edit_material import edit_material +from .edit_profile import edit_profile +from .edit_profile_usage import edit_profile_usage +from .remove_constituent import remove_constituent +from .remove_layer import remove_layer +from .remove_list_item import remove_list_item +from .remove_material import remove_material +from .remove_material_set import remove_material_set +from .remove_profile import remove_profile +from .reorder_set_item import reorder_set_item +from .unassign_material import unassign_material diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py index 278eb50872..b1f3f76073 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_constituent.py @@ -17,75 +17,72 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent_set=None, material=None): - """Adds a new constituent to a constituent set +def add_constituent(file, constituent_set=None, material=None) -> None: + """Adds a new constituent to a constituent set - A constituent describes how a portion of an object is made out of a - material whereas other portions of the object is made out of other - materials. For example, a window might be made out of an aluminium frame - and a glass panel. The aluminium used for the frame is one constituent - of the material, and glass would be another constituent. Another example - might be concrete, where one constituent might be cement, and another - constituent might be binder. In the case of the window, the constituent - is represented explicitly by the geometry of the window frame and the - geometry of the window panel. In the case of a concrete slab, the - constituents might be represented in terms of percentages. + A constituent describes how a portion of an object is made out of a + material whereas other portions of the object is made out of other + materials. For example, a window might be made out of an aluminium frame + and a glass panel. The aluminium used for the frame is one constituent + of the material, and glass would be another constituent. Another example + might be concrete, where one constituent might be cement, and another + constituent might be binder. In the case of the window, the constituent + is represented explicitly by the geometry of the window frame and the + geometry of the window panel. In the case of a concrete slab, the + constituents might be represented in terms of percentages. - Constituents are not available in IFC2X3. + Constituents are not available in IFC2X3. - :param constituent_set: The IfcMaterialConstituentSet that the - constituent is part of. The constituent set represents a group of - constituents. See ifcopenshell.api.material.add_material_set for - information on how to add a constituent set. - :type constituent_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the constituent is made out of. - :type material: ifcopenshell.entity_instance - :return: The newly created IfcMaterialConstituent - :rtype: ifcopenshell.entity_instance + :param constituent_set: The IfcMaterialConstituentSet that the + constituent is part of. The constituent set represents a group of + constituents. See ifcopenshell.api.material.add_material_set for + information on how to add a constituent set. + :type constituent_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the constituent is made out of. + :type material: ifcopenshell.entity_instance + :return: The newly created IfcMaterialConstituent + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a window type that has an aluminium frame - # and a glass glazing panel. Notice we are assigning to the type - # only, as all occurrences of that type will automatically inherit - # the material. - window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") + # Let's imagine we have a window type that has an aluminium frame + # and a glass glazing panel. Notice we are assigning to the type + # only, as all occurrences of that type will automatically inherit + # the material. + window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") - # First, let's create a constituent set. This will later be assigned - # to our window element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # First, let's create a constituent set. This will later be assigned + # to our window element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two constituents in our set. - ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium) - ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Now let's use those materials as two constituents in our set. + ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium) + ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Great! Let's assign our material set to our window type. - # We're technically not done here, we might want to add geometry to - # our window too, but to keep this example simple, geometry is - # optional and it is enough to say that this window is made out of - # aluminium and glass. - ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) - """ - self.file = file - self.settings = {"constituent_set": constituent_set, "material": material} + # Great! Let's assign our material set to our window type. + # We're technically not done here, we might want to add geometry to + # our window too, but to keep this example simple, geometry is + # optional and it is enough to say that this window is made out of + # aluminium and glass. + ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) + """ + settings = {"constituent_set": constituent_set, "material": material} - def execute(self): - constituents = list(self.settings["constituent_set"].MaterialConstituents or []) - constituent = self.file.create_entity("IfcMaterialConstituent", **{"Material": self.settings["material"]}) - constituents.append(constituent) - self.settings["constituent_set"].MaterialConstituents = constituents - return constituent + constituents = list(settings["constituent_set"].MaterialConstituents or []) + constituent = file.create_entity("IfcMaterialConstituent", **{"Material": settings["material"]}) + constituents.append(constituent) + settings["constituent_set"].MaterialConstituents = constituents + return constituent diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py index aa572f07bd..68885715ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_layer.py @@ -17,75 +17,70 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer_set=None, material=None): - """Adds a new layer to a layer set +def add_layer(file, layer_set=None, material=None) -> None: + """Adds a new layer to a layer set - A layer represents a portion of material within a layered build up, - defined by a thickness. Typical layered construction includes walls and - slabs, where a wall might include a layer of finish, a layer of - structure, a layer of insulation, and so on. It is recommended to define - layered construction this way where it is unnecessary to define the - exact geometry of how the wall or slab will be built, and it will - instead be determined on site by a trade. + A layer represents a portion of material within a layered build up, + defined by a thickness. Typical layered construction includes walls and + slabs, where a wall might include a layer of finish, a layer of + structure, a layer of insulation, and so on. It is recommended to define + layered construction this way where it is unnecessary to define the + exact geometry of how the wall or slab will be built, and it will + instead be determined on site by a trade. - Layers are defined in a particular order and thickness, so that it is - clear which layer comes next. + Layers are defined in a particular order and thickness, so that it is + clear which layer comes next. - :param layer_set: The IfcMaterialLayerSet that the layer is part of. The - layer set represents a group of layers. See - ifcopenshell.api.material.add_material_set for more information on - how to add a layer set. - :type layer_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the layer is made out of. - :type material: ifcopenshell.entity_instance - :return: The newly created IfcMaterialLayer - :rtype: ifcopenshell.entity_instance + :param layer_set: The IfcMaterialLayerSet that the layer is part of. The + layer set represents a group of layers. See + ifcopenshell.api.material.add_material_set for more information on + how to add a layer set. + :type layer_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the layer is made out of. + :type material: ifcopenshell.entity_instance + :return: The newly created IfcMaterialLayer + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a wall type that has two layers of - # gypsum with steel studs inside. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # Let's imagine we have a wall type that has two layers of + # gypsum with steel studs inside. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - """ - self.file = file - self.settings = {"layer_set": layer_set, "material": material} + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + """ + settings = {"layer_set": layer_set, "material": material} - def execute(self): - layers = list(self.settings["layer_set"].MaterialLayers or []) - layer = self.file.create_entity( - "IfcMaterialLayer", **{"Material": self.settings["material"], "LayerThickness": 1.0} - ) - layers.append(layer) - self.settings["layer_set"].MaterialLayers = layers - return layer + layers = list(settings["layer_set"].MaterialLayers or []) + layer = file.create_entity("IfcMaterialLayer", **{"Material": settings["material"], "LayerThickness": 1.0}) + layers.append(layer) + settings["layer_set"].MaterialLayers = layers + return layer diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py index 9a12ed044b..7eaa8159ce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_list_item.py @@ -19,70 +19,67 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material_list=None, material=None): - """Adds a new material in a list of materials +def add_list_item(file, material_list=None, material=None) -> None: + """Adds a new material in a list of materials - In IFC2X3, if you wanted an object to have multiple materials (i.e. a - composite material) you would assign the object to a material list, - which would contain a list of materials. For example, a window might - have a list of 2 materials, one being aluminium for the frame, and - another being glass for the panel. + In IFC2X3, if you wanted an object to have multiple materials (i.e. a + composite material) you would assign the object to a material list, + which would contain a list of materials. For example, a window might + have a list of 2 materials, one being aluminium for the frame, and + another being glass for the panel. - In IFC4 and above, this is deprecated and should not be used. Instead, - you should use constituent sets instead, which achieve the same thing - but are more powerful as they allow you to define the properties of the - constituents too. + In IFC4 and above, this is deprecated and should not be used. Instead, + you should use constituent sets instead, which achieve the same thing + but are more powerful as they allow you to define the properties of the + constituents too. - However if you're stuck on IFC2X3, you have my condolences as well as - this function. + However if you're stuck on IFC2X3, you have my condolences as well as + this function. - :param material_list: The IfcMaterialList the material should be added - to. - :type material_list: ifcopenshell.entity_instance - :param material: The IfcMaterial to add to the list - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material_list: The IfcMaterialList the material should be added + to. + :type material_list: ifcopenshell.entity_instance + :param material: The IfcMaterial to add to the list + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a window type that has an aluminium frame - # and a glass glazing panel. Notice we are assigning to the type - # only, as all occurrences of that type will automatically inherit - # the material. - window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") + # Let's imagine we have a window type that has an aluminium frame + # and a glass glazing panel. Notice we are assigning to the type + # only, as all occurrences of that type will automatically inherit + # the material. + window_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWindowType") - # First, let's create a list. This will later be assigned to our - # window element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialList") + # First, let's create a list. This will later be assigned to our + # window element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialList") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Great! Let's assign our material set to our window type. - # We're technically not done here, we might want to add geometry to - # our window too, but to keep this example simple, geometry is - # optional and it is enough to say that this window is made out of - # aluminium and glass. - ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) - """ - self.file = file - self.settings = {"material_list": material_list, "material": material} + # Great! Let's assign our material set to our window type. + # We're technically not done here, we might want to add geometry to + # our window too, but to keep this example simple, geometry is + # optional and it is enough to say that this window is made out of + # aluminium and glass. + ifcopenshell.api.run("material.assign_material", model, products=[window_type], material=material_set) + """ + settings = {"material_list": material_list, "material": material} - def execute(self): - materials = list(self.settings["material_list"].Materials or []) - materials.append(self.settings["material"]) - self.settings["material_list"].Materials = materials + materials = list(settings["material_list"].Materials or []) + materials.append(settings["material"]) + settings["material_list"].Materials = materials diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py index bac5a3ac0a..534d9e911c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material.py @@ -17,64 +17,61 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, category=None): - """Adds a new material +def add_material(file, name=None, category=None) -> None: + """Adds a new material - A material in IFC represents a physical material, such as timber, steel, - concrete, aluminium, etc. It may also contain physical properties used - for structural or lighting simulation. Note that unlike the computer - graphics industry, a material by itself does not define any colour or - lighting information. Colours in IFC are known as "styles", and an IFC - material may or may not have any style information associated with it. - See ifcopenshell.api.style for more information. + A material in IFC represents a physical material, such as timber, steel, + concrete, aluminium, etc. It may also contain physical properties used + for structural or lighting simulation. Note that unlike the computer + graphics industry, a material by itself does not define any colour or + lighting information. Colours in IFC are known as "styles", and an IFC + material may or may not have any style information associated with it. + See ifcopenshell.api.style for more information. - A material is typically given a code name which is used by architects in - elevations and details when tagging finishes. Materials are also useful - to structural engineers in specifying the exact types of concrete and - steel to be used in structural simulations. + A material is typically given a code name which is used by architects in + elevations and details when tagging finishes. Materials are also useful + to structural engineers in specifying the exact types of concrete and + steel to be used in structural simulations. - In addition, materials can belong to a category. Specifying this - category is critical to allow model recipients to make simple queries - like "show me all concrete / steel" elements in the model. Without - standardised category naming of all materials, this type of query - becomes a bespoke and inefficient task. A list of categories are: - 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood', - 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to - specify their own category instead if none of these categories are - appropriate. + In addition, materials can belong to a category. Specifying this + category is critical to allow model recipients to make simple queries + like "show me all concrete / steel" elements in the model. Without + standardised category naming of all materials, this type of query + becomes a bespoke and inefficient task. A list of categories are: + 'concrete', 'steel', 'aluminium', 'block', 'brick', 'stone', 'wood', + 'glass', 'gypsum', 'plastic', and 'earth'. The user is allowed to + specify their own category instead if none of these categories are + appropriate. - Note that categories are not available in IFC2X3. This shortcoming is - one of the big reasons projects should upgrade to IFC4. + Note that categories are not available in IFC2X3. This shortcoming is + one of the big reasons projects should upgrade to IFC4. - :param name: The name of the material, typically tagged in a finishes - drawing or schedule. - :type name: str - :param category: The category of the material. - :type category: str, optional - :return: The newly created IfcMaterial - :rtype: ifcopenshell.entity_instance + :param name: The name of the material, typically tagged in a finishes + drawing or schedule. + :type name: str + :param category: The category of the material. + :type category: str, optional + :return: The newly created IfcMaterial + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create two materials with their respective categories - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create two materials with their respective categories + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Let's imagine an urban concrete bench which is purely made out of concrete - concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + # Let's imagine an urban concrete bench which is purely made out of concrete + concrete_bench = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - # Assign the concrete material to that bench. Note that no colour - # "Style" has been specified. - ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete) - """ - self.file = file - self.settings = {"name": name or "Unnamed", "category": category} + # Assign the concrete material to that bench. Note that no colour + # "Style" has been specified. + ifcopenshell.api.run("material.assign_material", model, products=[concrete_bench], material=concrete) + """ + settings = {"name": name or "Unnamed", "category": category} - def execute(self): - material = self.file.create_entity("IfcMaterial", **{"Name": self.settings["name"] or "Unnamed"}) - if self.settings["category"]: - material.Category = self.settings["category"] - return material + material = file.create_entity("IfcMaterial", **{"Name": settings["name"] or "Unnamed"}) + if settings["category"]: + material.Category = settings["category"] + return material diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py index 277aa258f2..99cfafad41 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py @@ -17,97 +17,94 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name="Unnamed", set_type="IfcMaterialConstituentSet"): - """Adds a new material set +def add_material_set(file, name="Unnamed", set_type="IfcMaterialConstituentSet") -> None: + """Adds a new material set - IFC allows you to state that objects are made out of multiple materials. - These are known generically as material sets, but may also be called - layered materials, composite materials, or other names in software. + IFC allows you to state that objects are made out of multiple materials. + These are known generically as material sets, but may also be called + layered materials, composite materials, or other names in software. - There are three types of material sets: + There are three types of material sets: - - A layer set, used for layered construction such as walls, where the - element is parametrically made out of extruded layers, each layer - having a thickness defined. Even though this is known as a layer - "set" it is still recommended to use it for all standared layered - construction as it describes the intent of the element to be layered - construction and thus can be used for parametric editing. - - A profile set, used for profiled construction such as beams or - columns, where the element is parametrically made out of one or more - extruded profiles, where each profile may be parametric from a - standard section (e.g. standardised steel profile) or an arbitrary - shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note - that even though this is called a profile "set", it should still be - used even if there is only a single profile. This is not available in - IFC2X3. - - A constituent set, used for arbitrary composite construction where - the object is made out of multiple materials. The constituents may be - explicitly defined via a shape, such as a window where the frame - geometry is made from one material and the panel geometry is made - from another material. Alternatively, the constituents may be - represented in terms of percentages, such as in mixtures like - concrete where there might be a percentage constituent of cement and - another percentage constituent of binder. This is not available in - IFC2X3. + - A layer set, used for layered construction such as walls, where the + element is parametrically made out of extruded layers, each layer + having a thickness defined. Even though this is known as a layer + "set" it is still recommended to use it for all standared layered + construction as it describes the intent of the element to be layered + construction and thus can be used for parametric editing. + - A profile set, used for profiled construction such as beams or + columns, where the element is parametrically made out of one or more + extruded profiles, where each profile may be parametric from a + standard section (e.g. standardised steel profile) or an arbitrary + shape (e.g. cold rolled sections, or skirtings, moldings, etc). Note + that even though this is called a profile "set", it should still be + used even if there is only a single profile. This is not available in + IFC2X3. + - A constituent set, used for arbitrary composite construction where + the object is made out of multiple materials. The constituents may be + explicitly defined via a shape, such as a window where the frame + geometry is made from one material and the panel geometry is made + from another material. Alternatively, the constituents may be + represented in terms of percentages, such as in mixtures like + concrete where there might be a percentage constituent of cement and + another percentage constituent of binder. This is not available in + IFC2X3. - There is also a fourth material set known as a material list, which is a - legacy type of set used by IFC2X3. It should not be used on IFC4 and - above, and constituent sets should be used instead. + There is also a fourth material set known as a material list, which is a + legacy type of set used by IFC2X3. It should not be used on IFC4 and + above, and constituent sets should be used instead. - :param name: The name of the material set, which may be purely - descriptive or annotated in drawings. Defaults to "Unnamed". - :type name: str, optional - :param set_type: What type of set you want to create, chosen from - IfcMaterialLayerSet, IfcMaterialProfileSet, - IfcMaterialConstituentSet, or IfcMaterialList. Defaults to - IfcMaterialConstituentSet. - :type set_type: str, optional - :return: The newly created material set element - :rtype: ifcopenshell.entity_instance + :param name: The name of the material set, which may be purely + descriptive or annotated in drawings. Defaults to "Unnamed". + :type name: str, optional + :param set_type: What type of set you want to create, chosen from + IfcMaterialLayerSet, IfcMaterialProfileSet, + IfcMaterialConstituentSet, or IfcMaterialList. Defaults to + IfcMaterialConstituentSet. + :type set_type: str, optional + :return: The newly created material set element + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a wall type that has two layers of - # gypsum with steel studs inside. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # Let's imagine we have a wall type that has two layers of + # gypsum with steel studs inside. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - """ - self.file = file - self.settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"} + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + """ + settings = {"name": name or "Unnamed", "set_type": set_type or "IfcMaterialConstituentSet"} - def execute(self): - if self.settings["set_type"] == "IfcMaterialLayerSet": - return self.file.create_entity("IfcMaterialLayerSet", LayerSetName=self.settings["name"] or "Unnamed") - elif self.settings["set_type"] == "IfcMaterialList": - return self.file.create_entity("IfcMaterialList") - return self.file.create_entity(self.settings["set_type"], Name=self.settings["name"] or "Unnamed") + if settings["set_type"] == "IfcMaterialLayerSet": + return file.create_entity("IfcMaterialLayerSet", LayerSetName=settings["name"] or "Unnamed") + elif settings["set_type"] == "IfcMaterialList": + return file.create_entity("IfcMaterialList") + return file.create_entity(settings["set_type"], Name=settings["name"] or "Unnamed") diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py index ff2cf3bed5..3a23dd44a9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_profile.py @@ -19,86 +19,82 @@ import ifcopenshell from typing import Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - profile_set: ifcopenshell.entity_instance, - material: Optional[ifcopenshell.entity_instance] = None, - profile: Optional[ifcopenshell.entity_instance] = None, - ): - """Add a new profile item to a profile set +def add_profile( + file: ifcopenshell.file, + profile_set: ifcopenshell.entity_instance, + material: Optional[ifcopenshell.entity_instance] = None, + profile: Optional[ifcopenshell.entity_instance] = None, +) -> ifcopenshell.entity_instance: + """Add a new profile item to a profile set - A profile item in a profile set represents an extruded 2D profile curve - that is extruded along the axis of the element. Most commonly there will - only be a single profile item in a profile set. For example, a beam will - have a material profile set containing a single profile item, which may - have a steel material and a I-beam shaped profile curve. + A profile item in a profile set represents an extruded 2D profile curve + that is extruded along the axis of the element. Most commonly there will + only be a single profile item in a profile set. For example, a beam will + have a material profile set containing a single profile item, which may + have a steel material and a I-beam shaped profile curve. - Note that the "profile item" represents a single extrusion in the - profile set, whereas the "profile curve" represents a 2D curve used by a - "profile item". + Note that the "profile item" represents a single extrusion in the + profile set, whereas the "profile curve" represents a 2D curve used by a + "profile item". - In some cases, a profiled element (i.e. beam, column) may be a composite - beam or column and include multiple extrusions. This is rare. The order - of the profiles does not matter. + In some cases, a profiled element (i.e. beam, column) may be a composite + beam or column and include multiple extrusions. This is rare. The order + of the profiles does not matter. - :param profile_set: The IfcMaterialProfileSet that the profile is part of. The - profile set represents a group of profile items. See - ifcopenshell.api.material.add_material_set for more information on - how to add a profile set. - :type profile_set: ifcopenshell.entity_instance - :param material: The IfcMaterial that the profile item is made out of. - :type material: ifcopenshell.entity_instance, optional - :param profile: The IfcProfileDef that represents the 2D cross section - of the the profile item. - :type profile: ifcopenshell.entity_instance, optional - :return: The newly created IfcMaterialProfile - :rtype: ifcopenshell.entity_instance + :param profile_set: The IfcMaterialProfileSet that the profile is part of. The + profile set represents a group of profile items. See + ifcopenshell.api.material.add_material_set for more information on + how to add a profile set. + :type profile_set: ifcopenshell.entity_instance + :param material: The IfcMaterial that the profile item is made out of. + :type material: ifcopenshell.entity_instance, optional + :param profile: The IfcProfileDef that represents the 2D cross section + of the the profile item. + :type profile: ifcopenshell.entity_instance, optional + :return: The newly created IfcMaterialProfile + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - """ - self.file = file - self.settings = {"profile_set": profile_set, "material": material, "profile": profile} + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + """ + settings = {"profile_set": profile_set, "material": material, "profile": profile} - def execute(self) -> ifcopenshell.entity_instance: - profiles = list(self.settings["profile_set"].MaterialProfiles or []) - profile = self.file.create_entity("IfcMaterialProfile") - if self.settings["material"]: - profile.Material = self.settings["material"] - if self.settings["profile"]: - profile.Profile = self.settings["profile"] - profiles.append(profile) - self.settings["profile_set"].MaterialProfiles = profiles - return profile + profiles = list(settings["profile_set"].MaterialProfiles or []) + profile = file.create_entity("IfcMaterialProfile") + if settings["material"]: + profile.Material = settings["material"] + if settings["profile"]: + profile.Profile = settings["profile"] + profiles.append(profile) + settings["profile_set"].MaterialProfiles = profiles + return profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py index a65a644866..9098ca72fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py @@ -23,133 +23,135 @@ import ifcopenshell.util.representation from typing import Optional, Union +def assign_material( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + type: str = "IfcMaterial", + material: Optional[ifcopenshell.entity_instance] = None, +) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]: + """Assigns a material to the list of products + + Will unassign previously assigned material. + + When a material is assigned to a product, it means that the product is + made out of that material. In its simplest form, a single material may + be assigned to a product, meaning that the entire product is made out of + that one material. Alternatively, a material set may be assigned to a + product, meaning that the product is made out of a set of materials. + There are three types of sets, including layered construction, profiled + materials, and arbitrary material constituents. See + ifcopenshell.api.material.add_material_set for details. + + Materials are typically assigned to the element types rather than + individual occurrences of elements. Individual occurrences would then + inherit the material from the type. + + If the type has a material set, then the geometry of the occurrences + must comply with the material set. For example, if the type has a + constituent set, then it is expected that all occurrences also inherit + the geometry of the type, which is made out of those constituents. + Alternatively, if the type has a layer set, then all occurrences must + have geometry that has a thickness equal to the sum of all layers. If a + type has a profile set, then all occurrences must has the same profile + extruded along its axis. + + For layers and profiles assigned to types, the occurrences must be + assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage. + This allows individual occurrences to override the layered or profiled + construction offset from a reference line. + + :param products: The list of IfcProducts to assign the material or material set + to. + :type products: list[ifcopenshell.entity_instance] + :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", + "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", + "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or + "IfcMaterialList". Note that "Set Usages" may only be assigned to + occurrences, not types. Defaults to "IfcMaterial". + :type type: str + :param material: The IfcMaterial or material set you are assigning here. + If type is Usage then no need to provide `material`, it will be deduced + from the element type automatically. + :type material: ifcopenshell.entity_instance, optional + :return: IfcRelAssociatesMaterial entity + or a list of IfcRelAssociatesMaterial entities + (possible if `type` is Usage + and `products` require different Usages) + or `None` if `products` was empty list. + :rtype: Union[ + ifcopenshell.entity_instance, + list[ifcopenshell.entity_instance], None] + + Example: + + .. code:: python + + # Let's start with a simple concrete material + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Let's imagine a concrete bench made out of a single concrete + # material. Let's assign it to the type. + bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + ifcopenshell.api.run("material.assign_material", model, + products=[bench_type], type="IfcMaterial", material=concrete) + + # Let's imagine there are a two occurrences of this bench. It's not + # necessary to assign any material to these benches as they + # automatically inherit the material from the type. + bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type) + ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type) + + # If we have a concrete wall, we should use a layer set. Again, + # let's start with a wall type, not occurrences. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + + # Even though there is only one layer in our layer set, we still use + # a layer set because it makes it clear that this is a layered + # construction. Let's say it's a 200mm thick concrete layer. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="CON200", set_type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) + + # Our wall type now has the layer set assigned to it + ifcopenshell.api.run("material.assign_material", model, + products=[wall_type], type="IfcMaterialLayerSet", material=material_set) + + # Let's imagine an occurrence of this wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + + # Our wall occurrence needs to have a "set usage" which describes + # how the layers relate to a reference line (typically a 2D line + # representing the extents of the wall). Usages are special since + # they automatically detect the inherited material set from the + # type. You'd write similar code for a profile set. + ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterialLayerSetUsage") + + # To be complete, let's create the wall's axis and body + # representation. Notice how the axis guides the walls "reference + # line" which determines where layers are extruded from, and the + # body has a thickness of 200mm, same as our total layer set + # thickness. + axis = ifcopenshell.api.run("geometry.add_axis_representation", model, + context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)]) + body = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body_context, length=5000, height=3000, thickness=200) + ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis) + ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"products": products, "type": type, "material": material} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - type: str = "IfcMaterial", - material: Optional[ifcopenshell.entity_instance] = None, - ): - """Assigns a material to the list of products - - Will unassign previously assigned material. - - When a material is assigned to a product, it means that the product is - made out of that material. In its simplest form, a single material may - be assigned to a product, meaning that the entire product is made out of - that one material. Alternatively, a material set may be assigned to a - product, meaning that the product is made out of a set of materials. - There are three types of sets, including layered construction, profiled - materials, and arbitrary material constituents. See - ifcopenshell.api.material.add_material_set for details. - - Materials are typically assigned to the element types rather than - individual occurrences of elements. Individual occurrences would then - inherit the material from the type. - - If the type has a material set, then the geometry of the occurrences - must comply with the material set. For example, if the type has a - constituent set, then it is expected that all occurrences also inherit - the geometry of the type, which is made out of those constituents. - Alternatively, if the type has a layer set, then all occurrences must - have geometry that has a thickness equal to the sum of all layers. If a - type has a profile set, then all occurrences must has the same profile - extruded along its axis. - - For layers and profiles assigned to types, the occurrences must be - assigned an IfcMaterialLayerSetUsage or an IfcMaterialProfileSetUsage. - This allows individual occurrences to override the layered or profiled - construction offset from a reference line. - - :param products: The list of IfcProducts to assign the material or material set - to. - :type products: list[ifcopenshell.entity_instance] - :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", - "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", - "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or - "IfcMaterialList". Note that "Set Usages" may only be assigned to - occurrences, not types. Defaults to "IfcMaterial". - :type type: str - :param material: The IfcMaterial or material set you are assigning here. - If type is Usage then no need to provide `material`, it will be deduced - from the element type automatically. - :type material: ifcopenshell.entity_instance, optional - :return: IfcRelAssociatesMaterial entity - or a list of IfcRelAssociatesMaterial entities - (possible if `type` is Usage - and `products` require different Usages) - or `None` if `products` was empty list. - :rtype: Union[ - ifcopenshell.entity_instance, - list[ifcopenshell.entity_instance], None] - - Example: - - .. code:: python - - # Let's start with a simple concrete material - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Let's imagine a concrete bench made out of a single concrete - # material. Let's assign it to the type. - bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - ifcopenshell.api.run("material.assign_material", model, - products=[bench_type], type="IfcMaterial", material=concrete) - - # Let's imagine there are a two occurrences of this bench. It's not - # necessary to assign any material to these benches as they - # automatically inherit the material from the type. - bench1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - bench2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - ifcopenshell.api.run("type.assign_type", model, related_objects=[bench1], relating_type=bench_type) - ifcopenshell.api.run("type.assign_type", model, related_objects=[bench2], relating_type=bench_type) - - # If we have a concrete wall, we should use a layer set. Again, - # let's start with a wall type, not occurrences. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - - # Even though there is only one layer in our layer set, we still use - # a layer set because it makes it clear that this is a layered - # construction. Let's say it's a 200mm thick concrete layer. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="CON200", set_type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) - - # Our wall type now has the layer set assigned to it - ifcopenshell.api.run("material.assign_material", model, - products=[wall_type], type="IfcMaterialLayerSet", material=material_set) - - # Let's imagine an occurrence of this wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - - # Our wall occurrence needs to have a "set usage" which describes - # how the layers relate to a reference line (typically a 2D line - # representing the extents of the wall). Usages are special since - # they automatically detect the inherited material set from the - # type. You'd write similar code for a profile set. - ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterialLayerSetUsage") - - # To be complete, let's create the wall's axis and body - # representation. Notice how the axis guides the walls "reference - # line" which determines where layers are extruded from, and the - # body has a thickness of 200mm, same as our total layer set - # thickness. - axis = ifcopenshell.api.run("geometry.add_axis_representation", model, - context=axis_context, axis=[(0.0, 0.0), (5000.0, 0.0)]) - body = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body_context, length=5000, height=3000, thickness=200) - ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=axis) - ifcopenshell.api.run("geometry.assign_representation", model, product=wall, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = {"products": products, "type": type, "material": material} - - def execute(self) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance], None]: + def execute(self): self.products: set[ifcopenshell.entity_instance] = set(self.settings["products"]) if not self.products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py index 4d1678a0ae..15c7e4d779 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_profile.py @@ -19,78 +19,81 @@ import ifcopenshell.util.representation +def assign_profile(file, material_profile=None, profile=None) -> None: + """Changes the profile curve of a material profile item in a profile set + + In addition to changing the profile curve, it will also change the + profile curve used in any body representation extrusions. + + :param material_profile: The IfcMaterialProfile to change the profile + curve of. See ifcopenshell.api.material.add_profile to see how to + create profiles. + :type material_profile: ifcopenshell.entity_instance + :param profile: The IfcProfileDef to set the profile item's curve to. + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") + + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) + + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + + # Let's create an occurrence of this beam. + beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") + ifcopenshell.api.run("material.assign_material", model, + products=[beam], type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geometry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) + + # Now let's change the profile to a HEA200 standard profile instead. + # This will automatically change the body representation that we + # just added as well to a HEA200 profile. + hea200 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", + OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, + ) + ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"material_profile": material_profile, "profile": profile} + return usecase.execute() + + class Usecase: - def __init__(self, file, material_profile=None, profile=None): - """Changes the profile curve of a material profile item in a profile set - - In addition to changing the profile curve, it will also change the - profile curve used in any body representation extrusions. - - :param material_profile: The IfcMaterialProfile to change the profile - curve of. See ifcopenshell.api.material.add_profile to see how to - create profiles. - :type material_profile: ifcopenshell.entity_instance - :param profile: The IfcProfileDef to set the profile item's curve to. - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") - - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) - - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - - # Let's create an occurrence of this beam. - beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") - ifcopenshell.api.run("material.assign_material", model, - products=[beam], type="IfcMaterialProfileSetUsage") - - # Let's give a 1000mm long beam body representation. - body = ifcopenshell.api.run("geometry.add_profile_representation", - context=body_context, profile=hea100, depth=1000) - ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) - - # Now let's change the profile to a HEA200 standard profile instead. - # This will automatically change the body representation that we - # just added as well to a HEA200 profile. - hea200 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", - OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, - ) - ifcopenshell.api.run("material.assign_profile", model, material_profile=profile_item, profile=hea200) - """ - self.file = file - self.settings = {"material_profile": material_profile, "profile": profile} - def execute(self): # TODO: handle composite profiles old_profile = self.settings["material_profile"].Profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py index 8dac43b8e0..c862e9cb4b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/copy_material.py @@ -20,45 +20,42 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Copies a material +def copy_material(file, material=None) -> None: + """Copies a material - All material psets and styles are copied. The copied material is not - associated to any elements. + All material psets and styles are copied. The copied material is not + associated to any elements. - :param material: The IfcMaterial to copy - :type material: ifcopenshell.entity_instance - :return: The new copy of the material - :rtype: ifcopenshell.entity_instance + :param material: The IfcMaterial to copy + :type material: ifcopenshell.entity_instance + :return: The new copy of the material + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - # Let's duplicate the concrete material - concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete) - """ - self.file = file - self.settings = {"material": material} + # Let's duplicate the concrete material + concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete) + """ + settings = {"material": material} - def execute(self): - if self.settings["material"].is_a("IfcMaterial"): - new = ifcopenshell.util.element.copy(self.file, self.settings["material"]) - for inverse in self.file.get_inverse(self.settings["material"]): - if inverse.is_a("IfcMaterialProperties"): - # Properties must not be shared between objects for convenience of authoring - inverse = ifcopenshell.util.element.copy(self.file, inverse) - properties = [] - for pset in inverse.Properties: - properties.append(ifcopenshell.util.element.copy_deep(self.file, pset)) - inverse.Properties = properties - inverse.Material = new - elif inverse.is_a("IfcMaterialDefinitionRepresentation"): - inverse = ifcopenshell.util.element.copy_deep( - self.file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"] - ) - inverse.RepresentedMaterial = new - return new + if settings["material"].is_a("IfcMaterial"): + new = ifcopenshell.util.element.copy(file, settings["material"]) + for inverse in file.get_inverse(settings["material"]): + if inverse.is_a("IfcMaterialProperties"): + # Properties must not be shared between objects for convenience of authoring + inverse = ifcopenshell.util.element.copy(file, inverse) + properties = [] + for pset in inverse.Properties: + properties.append(ifcopenshell.util.element.copy_deep(file, pset)) + inverse.Properties = properties + inverse.Material = new + elif inverse.is_a("IfcMaterialDefinitionRepresentation"): + inverse = ifcopenshell.util.element.copy_deep( + file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"] + ) + inverse.RepresentedMaterial = new + return new diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py index 3e3a03dbd8..3102d5a645 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_assigned_material.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, element=None, attributes=None): - """Edits the attributes of an IfcMaterial +def edit_assigned_material(file, element=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterial - For more information about the attributes and data types of an - IfcMaterial, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterial, consult the IFC documentation. - :param element: The IfcMaterial entity you want to edit - :type element: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param element: The IfcMaterial entity you want to edit + :type element: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - ifcopenshell.api.run("material.edit_assigned_material", model, - element=concrete, attributes={"Description": "40MPA concrete with broom finish"}) - """ - self.file = file - self.settings = {"element": element, "attributes": attributes or {}} + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + ifcopenshell.api.run("material.edit_assigned_material", model, + element=concrete, attributes={"Description": "40MPA concrete with broom finish"}) + """ + settings = {"element": element, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["element"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["element"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py index ef036527a3..998bef98bd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_constituent.py @@ -17,52 +17,49 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent=None, attributes=None, material=None): - """Edits the attributes of an IfcMaterialConstituent +def edit_constituent(file, constituent=None, attributes=None, material=None) -> None: + """Edits the attributes of an IfcMaterialConstituent - For more information about the attributes and data types of an - IfcMaterialConstituent, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialConstituent, consult the IFC documentation. - :param constituent: The IfcMaterialConstituent entity you want to edit - :type constituent: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param material: The IfcMaterial entity you want to change the constituent to - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param constituent: The IfcMaterialConstituent entity you want to edit + :type constituent: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param material: The IfcMaterial entity you want to change the constituent to + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's add two materials - aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + # Let's add two materials + aluminium1 = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + aluminium2 = ifcopenshell.api.run("material.add_material", model, name="AL02", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - # Set up two constituents, one for the frame and the other for the glazing. - framing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium1) - glazing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Set up two constituents, one for the frame and the other for the glazing. + framing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium1) + glazing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Let's make sure this constituent refers to the framing of the - # window and uses the second aluminium material instead. - ifcopenshell.api.run("material.edit_constituent", model, - constituent=framing, attributes={"Name": "Framing"}, material=aluminium2) + # Let's make sure this constituent refers to the framing of the + # window and uses the second aluminium material instead. + ifcopenshell.api.run("material.edit_constituent", model, + constituent=framing, attributes={"Name": "Framing"}, material=aluminium2) - ifcopenshell.api.run("material.edit_constituent", model, - constituent=constituent, attributes={"Name": "Glazing"}) - """ - self.file = file - self.settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} + ifcopenshell.api.run("material.edit_constituent", model, + constituent=constituent, attributes={"Name": "Glazing"}) + """ + settings = {"constituent": constituent, "attributes": attributes or {}, "material": material} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["constituent"], name, value) - self.settings["constituent"].Material = self.settings["material"] + for name, value in settings["attributes"].items(): + setattr(settings["constituent"], name, value) + settings["constituent"].Material = settings["material"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py index 3e31194452..78ce15132f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer.py @@ -17,51 +17,48 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None, attributes=None, material=None): - """Edits the attributes of an IfcMaterialLayer +def edit_layer(file, layer=None, attributes=None, material=None) -> None: + """Edits the attributes of an IfcMaterialLayer - For more information about the attributes and data types of an - IfcMaterialLayer, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialLayer, consult the IFC documentation. - :param layer: The IfcMaterialLayer entity you want to edit - :type layer: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param material: The IfcMaterial entity you want the layer to be made - from. - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param layer: The IfcMaterialLayer entity you want to edit + :type layer: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param material: The IfcMaterial entity you want the layer to be made + from. + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create two materials typically used for steel stud partition - # walls with gypsum lining. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Let's create two materials typically used for steel stud partition + # walls with gypsum lining. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create a material layer set to contain our layers. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # Create a material layer set to contain our layers. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) - """ - self.file = file - self.settings = {"layer": layer, "attributes": attributes or {}, "material": material} + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 92}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 13}) + """ + settings = {"layer": layer, "attributes": attributes or {}, "material": material} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["layer"], name, value) - if self.settings["material"]: - self.settings["layer"].Material = self.settings["material"] + for name, value in settings["attributes"].items(): + setattr(settings["layer"], name, value) + if settings["material"]: + settings["layer"].Material = settings["material"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py index a30fa39f21..9204728004 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_layer_usage.py @@ -17,66 +17,63 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, usage=None, attributes=None): - """Edits the attributes of an IfcMaterialLayerSetUsage +def edit_layer_usage(file, usage=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterialLayerSetUsage - This is typically used to change the offset from the reference line to - the layers. + This is typically used to change the offset from the reference line to + the layers. - For more information about the attributes and data types of an - IfcMaterialLayerSetUsage, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialLayerSetUsage, consult the IFC documentation. - :param usage: The IfcMaterialLayerSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param usage: The IfcMaterialLayerSetUsage entity you want to edit + :type usage: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's start with a simple concrete material - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + # Let's start with a simple concrete material + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - # If we have a concrete wall, we should use a layer set. Again, - # let's start with a wall type, not occurrences. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + # If we have a concrete wall, we should use a layer set. Again, + # let's start with a wall type, not occurrences. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - # Even though there is only one layer in our layer set, we still use - # a layer set because it makes it clear that this is a layered - # construction. Let's say it's a 200mm thick concrete layer. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="CON200", set_type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) + # Even though there is only one layer in our layer set, we still use + # a layer set because it makes it clear that this is a layered + # construction. Let's say it's a 200mm thick concrete layer. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="CON200", set_type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": 200}) - # Our wall type now has the layer set assigned to it - ifcopenshell.api.run("material.assign_material", model, - products=[wall_type], type="IfcMaterialLayerSet", material=material_set) + # Our wall type now has the layer set assigned to it + ifcopenshell.api.run("material.assign_material", model, + products=[wall_type], type="IfcMaterialLayerSet", material=material_set) - # Let's imagine an occurrence of this wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + # Let's imagine an occurrence of this wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - # Our wall occurrence needs to have a "set usage" which describes - # how the layers relate to a reference line (typically a 2D line - # representing the extents of the wall). Usages are special since - # they automatically detect the inherited material set from the - # type. You'd write similar code for a profile set. - rel = ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterialLayerSetUsage") + # Our wall occurrence needs to have a "set usage" which describes + # how the layers relate to a reference line (typically a 2D line + # representing the extents of the wall). Usages are special since + # they automatically detect the inherited material set from the + # type. You'd write similar code for a profile set. + rel = ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterialLayerSetUsage") - # Let's change the offset from the reference line to be 200mm - # instead of the default of 0mm. - ifcopenshell.api.run("material.edit_layer_usage", model, - usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200}) - """ - self.file = file - self.settings = {"usage": usage, "attributes": attributes or {}} + # Let's change the offset from the reference line to be 200mm + # instead of the default of 0mm. + ifcopenshell.api.run("material.edit_layer_usage", model, + usage=rel.RelatingMaterial, attributes={"OffsetFromReferenceLine": 200}) + """ + settings = {"usage": usage, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["usage"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["usage"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py index c712af15ac..87b3f2c7fb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_material.py @@ -17,13 +17,10 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, material=None, attributes=None): - """Edits the attributes of an IfcMaterial""" - - self.file = file - self.settings = {"material": material, "attributes": attributes or {}} +def edit_material(file, material=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterial""" - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["material"], name, value) + settings = {"material": material, "attributes": attributes or {}} + + for name, value in settings["attributes"].items(): + setattr(settings["material"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py index 6fc781f9a6..aa1310dbca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile.py @@ -17,72 +17,69 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, profile=None, attributes=None, profile_def=None, material=None): - """Edits the attributes of an IfcMaterialProfile +def edit_profile(file, profile=None, attributes=None, profile_def=None, material=None) -> None: + """Edits the attributes of an IfcMaterialProfile - For more information about the attributes and data types of an - IfcMaterialProfile, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMaterialProfile, consult the IFC documentation. - :param profile: The IfcMaterialProfile entity you want to edit - :type profile: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :param profile_def: The IfcProfileDef entity the profile curve should be - extruded from. - :type profile_def: ifcopenshell.entity_instance, optional - :param material: The IfcMaterial entity you want to change the profile - to be made from. - :type material: ifcopenshell.entity_instance, optional - :return: None - :rtype: None + :param profile: The IfcMaterialProfile entity you want to edit + :type profile: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :param profile_def: The IfcProfileDef entity the profile curve should be + extruded from. + :type profile_def: ifcopenshell.entity_instance, optional + :param material: The IfcMaterial entity you want to change the profile + to be made from. + :type material: ifcopenshell.entity_instance, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a material set to store our profiles. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # Let's create a material set to store our profiles. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a couple steel materials. - steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a couple steel materials. + steel1 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + steel2 = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create some I-shaped profiles. Notice how we name our profiles based - # on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - hea200 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", - OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, - ) + # Create some I-shaped profiles. Notice how we name our profiles based + # on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + hea200 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA200", ProfileType="AREA", + OverallWidth=200, OverallDepth=190, WebThickness=6.5, FlangeThickness=10, FilletRadius=18, + ) - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel1, profile=hea100) + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel1, profile=hea100) - # Edit our profile item to use a HEA200 profile instead made out of - # another type of steel. - ifcopenshell.api.run("material.edit_profile", model, - profile=profile_item, profile_def=hea200, material=steel2) - """ - self.file = file - self.settings = { - "profile": profile, - "attributes": attributes or {}, - "profile_def": profile_def, - "material": material, - } + # Edit our profile item to use a HEA200 profile instead made out of + # another type of steel. + ifcopenshell.api.run("material.edit_profile", model, + profile=profile_item, profile_def=hea200, material=steel2) + """ + settings = { + "profile": profile, + "attributes": attributes or {}, + "profile_def": profile_def, + "material": material, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["profile"], name, value) - if self.settings["material"]: - self.settings["profile"].Material = self.settings["material"] - if self.settings["profile_def"]: - self.settings["profile"].Profile = self.settings["profile_def"] + for name, value in settings["attributes"].items(): + setattr(settings["profile"], name, value) + if settings["material"]: + settings["profile"].Material = settings["material"] + if settings["profile_def"]: + settings["profile"].Profile = settings["profile_def"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py index afd9007c7b..8ad5192570 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/edit_profile_usage.py @@ -20,79 +20,82 @@ import ifcopenshell.geom import ifcopenshell.util.representation +def edit_profile_usage(file, usage=None, attributes=None) -> None: + """Edits the attributes of an IfcMaterialProfileSetUsage + + This is typically used to change the cardinal point of the profile. + The cardinal point represents whether the profile is extruded along the + center of the axis line, at a corner, at a shear center, at the bottom, + top, etc. + + For more information about the attributes and data types of an + IfcMaterialProfileSetUsage, consult the IFC documentation. + + :param usage: The IfcMaterialProfileSetUsage entity you want to edit + :type usage: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a steel I-beam. Notice we are assigning to + # the type only, as all occurrences of that type will automatically + # inherit the material. + beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") + + # First, let's create a material set. This will later be assigned + # to our beam type element. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") + + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = usecase.file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) + + # Define that steel material and cross section as a single profile + # item. If this were a composite beam, we might add multiple profile + # items instead, but this is rarely the case in most construction. + profile_item = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) + + # Great! Let's assign our material set to our beam type. + ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) + + # Let's create an occurrence of this beam. + beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") + rel = ifcopenshell.api.run("material.assign_material", model, + products=[beam], type="IfcMaterialProfileSetUsage") + + # Let's give a 1000mm long beam body representation. + body = ifcopenshell.api.run("geometry.add_profile_representation", + context=body_context, profile=hea100, depth=1000) + ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) + ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) + + # Let's change the cardinal point to be the top center of the axis + # line. This is represented by the number "8". Consult the IFC + # documentation for all the numbers you can use. + ifcopenshell.api.run("material.edit_profile_usage", model, + usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8}) + """ + usecase = Usecase() + + usecase.file = file + usecase.settings = {"usage": usage, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, usage=None, attributes=None): - """Edits the attributes of an IfcMaterialProfileSetUsage - - This is typically used to change the cardinal point of the profile. - The cardinal point represents whether the profile is extruded along the - center of the axis line, at a corner, at a shear center, at the bottom, - top, etc. - - For more information about the attributes and data types of an - IfcMaterialProfileSetUsage, consult the IFC documentation. - - :param usage: The IfcMaterialProfileSetUsage entity you want to edit - :type usage: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a steel I-beam. Notice we are assigning to - # the type only, as all occurrences of that type will automatically - # inherit the material. - beam_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeamType", name="B1") - - # First, let's create a material set. This will later be assigned - # to our beam type element. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") - - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) - - # Define that steel material and cross section as a single profile - # item. If this were a composite beam, we might add multiple profile - # items instead, but this is rarely the case in most construction. - profile_item = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) - - # Great! Let's assign our material set to our beam type. - ifcopenshell.api.run("material.assign_material", model, products=[beam_type], material=material_set) - - # Let's create an occurrence of this beam. - beam = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBeam", name="B1.01") - rel = ifcopenshell.api.run("material.assign_material", model, - products=[beam], type="IfcMaterialProfileSetUsage") - - # Let's give a 1000mm long beam body representation. - body = ifcopenshell.api.run("geometry.add_profile_representation", - context=body_context, profile=hea100, depth=1000) - ifcopenshell.api.run("geometry.assign_representation", model, product=beam, representation=body) - ifcopenshell.api.run("geometry.edit_object_placement", model, product=beam) - - # Let's change the cardinal point to be the top center of the axis - # line. This is represented by the number "8". Consult the IFC - # documentation for all the numbers you can use. - ifcopenshell.api.run("material.edit_profile_usage", model, - usage=rel.RelatingMaterial, attributes={"CardinalPoint": 8}) - """ - - self.file = file - self.settings = {"usage": usage, "attributes": attributes or {}} - def execute(self): self.cardinal_point = self.settings["attributes"].get("CardinalPoint") if self.cardinal_point and self.cardinal_point != self.settings["usage"].CardinalPoint: diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py index 2fb919d10d..02256e0695 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_constituent.py @@ -17,42 +17,39 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, constituent=None): - """Removes a constituent from a constituent set +def remove_constituent(file, constituent=None) -> None: + """Removes a constituent from a constituent set - Note that it is invalid to have zero items in a set, so you should leave - at least one constituent to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one constituent to ensure a valid IFC dataset. - :param constituent: The IfcMaterialConstituent entity you want to remove - :type constituent: ifcopenshell.entity_instance - :return: None - :rtype: None + :param constituent: The IfcMaterialConstituent entity you want to remove + :type constituent: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set for windows made out of aluminium and glass. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # Create a material set for windows made out of aluminium and glass. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two constituents in our set. - framing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=aluminium) - glazing = ifcopenshell.api.run("material.add_constituent", model, - constituent_set=material_set, material=glass) + # Now let's use those materials as two constituents in our set. + framing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=aluminium) + glazing = ifcopenshell.api.run("material.add_constituent", model, + constituent_set=material_set, material=glass) - # Let's remove the glass constituent. Note that we should not remove - # the framing, at this would mean there are no constituents which is - # invalid. - ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing) - """ - self.file = file - self.settings = {"constituent": constituent} + # Let's remove the glass constituent. Note that we should not remove + # the framing, at this would mean there are no constituents which is + # invalid. + ifcopenshell.api.run("material.remove_constituent", model, constituent=glazing) + """ + settings = {"constituent": constituent} - def execute(self): - self.file.remove(self.settings["constituent"]) + file.remove(settings["constituent"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py index f068641533..fda5cf2151 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_layer.py @@ -17,45 +17,42 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, layer=None): - """Removes a layer from a layer set +def remove_layer(file, layer=None) -> None: + """Removes a layer from a layer set - Note that it is invalid to have zero items in a set, so you should leave - at least one layer to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one layer to ensure a valid IFC dataset. - :param layer: The IfcMaterialLayer entity you want to remove - :type layer: ifcopenshell.entity_instance - :return: None - :rtype: None + :param layer: The IfcMaterialLayer entity you want to remove + :type layer: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set for steel stud partition walls. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialConstituentSet") + # Create a material set for steel stud partition walls. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialConstituentSet") - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13}) - layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92}) - layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13}) + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer1 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer1, attributes={"LayerThickness": 13}) + layer2 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer2, attributes={"LayerThickness": 92}) + layer3 = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer3, attributes={"LayerThickness": 13}) - # Let's remove the last layer, such that the wall might be clad only - # one one side such as to line a services riser. - ifcopenshell.api.run("material.remove_layer", model, layer=layer3) - """ - self.file = file - self.settings = {"layer": layer} + # Let's remove the last layer, such that the wall might be clad only + # one one side such as to line a services riser. + ifcopenshell.api.run("material.remove_layer", model, layer=layer3) + """ + settings = {"layer": layer} - def execute(self): - self.file.remove(self.settings["layer"]) + file.remove(settings["layer"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py index 276d9e64d2..a41b6ec7a8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_list_item.py @@ -19,44 +19,41 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material_list=None, material_index=0): - """Removes an item in an material list +def remove_list_item(file, material_list=None, material_index=0) -> None: + """Removes an item in an material list - Note that it is invalid to have zero items in a list, so you should leave - at least one item to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a list, so you should leave + at least one item to ensure a valid IFC dataset. - :param material_list: The IfcMaterialList entity you want to remove an - item from. - :type material_list: ifcopenshell.entity_instance - :param material_index: The index of the material you want to remove from - the list. Starts counting at 0. Defaults to 0. - :type material_index: int, optional - :return: None - :rtype: None + :param material_list: The IfcMaterialList entity you want to remove an + item from. + :type material_list: ifcopenshell.entity_instance + :param material_index: The index of the material you want to remove from + the list. Starts counting at 0. Defaults to 0. + :type material_index: int, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material list for aluminium windows. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialMaterialList") + # Create a material list for aluminium windows. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialMaterialList") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Let's remove the glass - ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1) - """ - self.file = file - self.settings = {"material_list": material_list, "material_index": material_index} + # Let's remove the glass + ifcopenshell.api.run("material.remove_list_item", model, material_list=material_set, material_index=1) + """ + settings = {"material_list": material_list, "material_index": material_index} - def execute(self): - materials = list(self.settings["material_list"].Materials) - materials.pop(self.settings["material_index"]) - self.settings["material_list"].Materials = materials + materials = list(settings["material_list"].Materials) + materials.pop(settings["material_index"]) + settings["material_list"].Materials = materials diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py index ffdf9693d8..a59fee6caf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py @@ -20,57 +20,54 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Removes a material +def remove_material(file, material=None) -> None: + """Removes a material - If the material is used in a material set, the corresponding layer, - profile, or constituent is also removed. Note that this may result in a - material set with zero items in it, which is invalid, so the user must - take care of this situation themselves. + If the material is used in a material set, the corresponding layer, + profile, or constituent is also removed. Note that this may result in a + material set with zero items in it, which is invalid, so the user must + take care of this situation themselves. - :param material: The IfcMaterial entity you want to remove - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterial entity you want to remove + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + # Create a material + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - # ... and remove it - ifcopenshell.api.run("material.remove_material", model, material=aluminium) - """ - self.file = file - self.settings = {"material": material} + # ... and remove it + ifcopenshell.api.run("material.remove_material", model, material=aluminium) + """ + settings = {"material": material} - def execute(self): - inverse_elements = self.file.get_inverse(self.settings["material"]) - self.file.remove(self.settings["material"]) - # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set - # This can lead to invalid material sets, but we assume the user will deal with it - for inverse in inverse_elements: - if inverse.is_a("IfcMaterialConstituent"): - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialLayer"): - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialProfile"): - self.file.remove(inverse) - elif inverse.is_a("IfcRelAssociatesMaterial"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcMaterialProperties"): - for prop in inverse.Properties or []: - self.file.remove(prop) - self.file.remove(inverse) - elif inverse.is_a("IfcMaterialDefinitionRepresentation"): - for representation in inverse.Representations: - for item in representation.Items: - self.file.remove(item) - self.file.remove(representation) - self.file.remove(inverse) + inverse_elements = file.get_inverse(settings["material"]) + file.remove(settings["material"]) + # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set + # This can lead to invalid material sets, but we assume the user will deal with it + for inverse in inverse_elements: + if inverse.is_a("IfcMaterialConstituent"): + file.remove(inverse) + elif inverse.is_a("IfcMaterialLayer"): + file.remove(inverse) + elif inverse.is_a("IfcMaterialProfile"): + file.remove(inverse) + elif inverse.is_a("IfcRelAssociatesMaterial"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcMaterialProperties"): + for prop in inverse.Properties or []: + file.remove(prop) + file.remove(inverse) + elif inverse.is_a("IfcMaterialDefinitionRepresentation"): + for representation in inverse.Representations: + for item in representation.Items: + file.remove(item) + file.remove(representation) + file.remove(inverse) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py index 79093789aa..5ede76c1c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material_set.py @@ -20,65 +20,62 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, material=None): - """Removes a material set +def remove_material_set(file, material=None) -> None: + """Removes a material set - All set items, such as layers, profiles, or constituents will also be - removed. However, the materials and profile curves used by the layers, - profiles and constituents will not be removed. + All set items, such as layers, profiles, or constituents will also be + removed. However, the materials and profile curves used by the layers, + profiles and constituents will not be removed. - :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, - IfcMaterialProfileSet entity you want to remove. - :type material: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet, + IfcMaterialProfileSet entity you want to remove. + :type material: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a material set - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + # Create a material set + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - # Create some materials - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create some materials + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Add some layers - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + # Add some layers + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - # Completely delete the set and all layers. The gypsum and steel - # material still exist, though. - ifcopenshell.api.run("material.remove_material_set", model, material=material_set) - """ + # Completely delete the set and all layers. The gypsum and steel + # material still exist, though. + ifcopenshell.api.run("material.remove_material_set", model, material=material_set) + """ - self.file = file - self.settings = {"material": material} + settings = {"material": material} - def execute(self): - inverse_elements = self.file.get_inverse(self.settings["material"]) - if self.settings["material"].is_a("IfcMaterialLayerSet"): - set_items = self.settings["material"].MaterialLayers or [] - elif self.settings["material"].is_a("IfcMaterialProfileSet"): - set_items = self.settings["material"].MaterialProfiles or [] - elif self.settings["material"].is_a("IfcMaterialConstituentSet"): - set_items = self.settings["material"].MaterialConstituents or [] - elif self.settings["material"].is_a("IfcMaterialList"): - set_items = [] - for set_item in set_items: - self.file.remove(set_item) - self.file.remove(self.settings["material"]) - for inverse in inverse_elements: - if inverse.is_a("IfcRelAssociatesMaterial"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcMaterialProperties"): - for prop in inverse.Properties or []: - self.file.remove(prop) - self.file.remove(inverse) + inverse_elements = file.get_inverse(settings["material"]) + if settings["material"].is_a("IfcMaterialLayerSet"): + set_items = settings["material"].MaterialLayers or [] + elif settings["material"].is_a("IfcMaterialProfileSet"): + set_items = settings["material"].MaterialProfiles or [] + elif settings["material"].is_a("IfcMaterialConstituentSet"): + set_items = settings["material"].MaterialConstituents or [] + elif settings["material"].is_a("IfcMaterialList"): + set_items = [] + for set_item in set_items: + file.remove(set_item) + file.remove(settings["material"]) + for inverse in inverse_elements: + if inverse.is_a("IfcRelAssociatesMaterial"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcMaterialProperties"): + for prop in inverse.Properties or []: + file.remove(prop) + file.remove(inverse) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py index 9c930866ed..858b6f1289 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_profile.py @@ -21,58 +21,55 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, profile=None): - """Removes a profile item from a profile set +def remove_profile(file, profile=None) -> None: + """Removes a profile item from a profile set - Note that it is invalid to have zero items in a set, so you should leave - at least one profile to ensure a valid IFC dataset. + Note that it is invalid to have zero items in a set, so you should leave + at least one profile to ensure a valid IFC dataset. - :param profile: The IfcMaterialProfile entity you want to remove - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None + :param profile: The IfcMaterialProfile entity you want to remove + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # First, let's create a material set. - material_set = ifcopenshell.api.run("material.add_profile_set", model, - name="B1", set_type="IfcMaterialProfileSet") + # First, let's create a material set. + material_set = ifcopenshell.api.run("material.add_profile_set", model, + name="B1", set_type="IfcMaterialProfileSet") - # Create a steel material. - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + # Create a steel material. + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - # Create an I-beam profile curve. Notice how we name our profiles - # based on standardised steel profile names. - hea100 = self.file.create_entity( - "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", - OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, - ) + # Create an I-beam profile curve. Notice how we name our profiles + # based on standardised steel profile names. + hea100 = file.create_entity( + "IfcIShapeProfileDef", ProfileName="HEA100", ProfileType="AREA", + OverallWidth=100, OverallDepth=96, WebThickness=5, FlangeThickness=8, FilletRadius=12, + ) - # Define that steel material and cross section as a single profile item. - ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=hea100) + # Define that steel material and cross section as a single profile item. + ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=hea100) - # Imagine a welded square along the length of the profile. - welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, - profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)]) - weld_profile = ifcopenshell.api.run("material.add_profile", model, - profile_set=material_set, material=steel, profile=welded_square) + # Imagine a welded square along the length of the profile. + welded_square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, + profile=[(.0025, .0025), (.0325, .0025), (.0325, -.0025), (.0025, -.0025), (.0025, .0025)]) + weld_profile = ifcopenshell.api.run("material.add_profile", model, + profile_set=material_set, material=steel, profile=welded_square) - # Let's remove our welded square. - ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile) - """ + # Let's remove our welded square. + ifcopenshell.api.run("material.remove_profile", model, profile=weld_profile) + """ - self.file = file - self.settings = {"profile": profile} + settings = {"profile": profile} - def execute(self): - subelements = set() - for attribute in self.settings["profile"]: - if isinstance(attribute, ifcopenshell.entity_instance): - subelements.add(attribute) - self.file.remove(self.settings["profile"]) - for subelement in subelements: - ifcopenshell.util.element.remove_deep2(self.file, subelement) + subelements = set() + for attribute in settings["profile"]: + if isinstance(attribute, ifcopenshell.entity_instance): + subelements.add(attribute) + file.remove(settings["profile"]) + for subelement in subelements: + ifcopenshell.util.element.remove_deep2(file, subelement) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py index 442fec8b5e..481050ff63 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/reorder_set_item.py @@ -17,56 +17,53 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, material_set=None, old_index=0, new_index=0): - """Reorders an item in a material set +def reorder_set_item(file, material_set=None, old_index=0, new_index=0) -> None: + """Reorders an item in a material set - In some material sets, the order have meaning, like in a layer set. In - other cases, it is purely for human convenience. + In some material sets, the order have meaning, like in a layer set. In + other cases, it is purely for human convenience. - :param material_set: The IfcMaterialSet which you want to reorder an - item in. - :type material_set: ifcopenshell.entity_instance - :param old_index: The index of the item you want to move. This starts - counting from 0. - :type old_index: int - :param new_index: The index of the new position the item will move to. - This starts counting from 0. - :type new_index: int - :return: None - :rtype: None + :param material_set: The IfcMaterialSet which you want to reorder an + item in. + :type material_set: ifcopenshell.entity_instance + :param old_index: The index of the item you want to move. This starts + counting from 0. + :type old_index: int + :param new_index: The index of the new position the item will move to. + This starts counting from 0. + :type new_index: int + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="Window", set_type="IfcMaterialList") + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="Window", set_type="IfcMaterialList") - aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") - glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") + aluminium = ifcopenshell.api.run("material.add_material", model, name="AL01", category="aluminium") + glass = ifcopenshell.api.run("material.add_material", model, name="GLZ01", category="glass") - # Now let's use those materials as two items in our list. - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) - ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) + # Now let's use those materials as two items in our list. + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=aluminium) + ifcopenshell.api.run("material.add_list_item", model, material_list=material_set, material=glass) - # Switch the order around, this has no meaning for a list, so this - # is just for fun. - ifcopenshell.api.run("material.reorder_set_item", model, - material_set=material_set, old_index=0, new_index=1) - """ - self.file = file - self.settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index} + # Switch the order around, this has no meaning for a list, so this + # is just for fun. + ifcopenshell.api.run("material.reorder_set_item", model, + material_set=material_set, old_index=0, new_index=1) + """ + settings = {"material_set": material_set, "old_index": old_index, "new_index": new_index} - def execute(self): - if self.settings["material_set"].is_a("IfcMaterialConstituentSet"): - set_name = "MaterialConstituents" - elif self.settings["material_set"].is_a("IfcMaterialLayerSet"): - set_name = "MaterialLayers" - elif self.settings["material_set"].is_a("IfcMaterialProfileSet"): - set_name = "MaterialProfiles" - elif self.settings["material_set"].is_a("IfcMaterialList"): - set_name = "Materials" - items = list(getattr(self.settings["material_set"], set_name) or []) - items.insert(self.settings["new_index"], items.pop(self.settings["old_index"])) - setattr(self.settings["material_set"], set_name, items) + if settings["material_set"].is_a("IfcMaterialConstituentSet"): + set_name = "MaterialConstituents" + elif settings["material_set"].is_a("IfcMaterialLayerSet"): + set_name = "MaterialLayers" + elif settings["material_set"].is_a("IfcMaterialProfileSet"): + set_name = "MaterialProfiles" + elif settings["material_set"].is_a("IfcMaterialList"): + set_name = "Materials" + items = list(getattr(settings["material_set"], set_name) or []) + items.insert(settings["new_index"], items.pop(settings["old_index"])) + setattr(settings["material_set"], set_name, items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py index 5f88963c36..93a7f11aea 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/unassign_material.py @@ -21,41 +21,44 @@ import ifcopenshell.api import ifcopenshell.util.element +def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Removes any material relationship with the list of products + + A product can only have one material assigned to it, which is why it is + not necessary to specify the material to unassign. The material is not + removed, only the relationship is removed. + + If the product does not have a material, nothing happens. + + :param products: The list IfcProducts that may or may not have a material + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None + + Example: + + .. code:: python + + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Let's imagine a concrete bench made out of concrete. + bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") + ifcopenshell.api.run("material.assign_material", model, + products=[bench_type], type="IfcMaterial", material=concrete) + + # Let's change our mind and remove the concrete assignment. The + # concrete material still exists, but the bench is no longer made + # out of concrete now. + ifcopenshell.api.run("material.unassign_material", model, products=[bench_type]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"products": products} + return usecase.execute() + + class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Removes any material relationship with the list of products - - A product can only have one material assigned to it, which is why it is - not necessary to specify the material to unassign. The material is not - removed, only the relationship is removed. - - If the product does not have a material, nothing happens. - - :param products: The list IfcProducts that may or may not have a material - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None - - Example: - - .. code:: python - - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Let's imagine a concrete bench made out of concrete. - bench_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurnitureType") - ifcopenshell.api.run("material.assign_material", model, - products=[bench_type], type="IfcMaterial", material=concrete) - - # Let's change our mind and remove the concrete assignment. The - # concrete material still exists, but the bench is no longer made - # out of concrete now. - ifcopenshell.api.run("material.unassign_material", model, products=[bench_type]) - """ - self.file = file - self.settings = {"products": products} - - def execute(self) -> None: + def execute(self): self.products = set(self.settings["products"]) if not self.products: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py index e0caddbe3c..242e12eb11 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_object import assign_object +from .change_nest import change_nest +from .reorder_nesting import reorder_nesting +from .unassign_object import unassign_object diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py index 1c9ab8c9b0..7bf76e5c08 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/assign_object.py @@ -22,156 +22,152 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_objects: list[ifcopenshell.entity_instance], - relating_object: ifcopenshell.entity_instance, - ): - """Assigns objects as nested children to a parent host +def assign_object( + file: ifcopenshell.file, + related_objects: list[ifcopenshell.entity_instance], + relating_object: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns objects as nested children to a parent host - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. - Another type of "decomposition" relationship is known as "nesting". - Nesting is used when an child object is physically attached to a parent - host object, through a physical predetermined connection point. The - child object must be specifically designed to attach to a other objects - at specific positions with a particular form factor. Examples include - faucets which must always be attached through a predrilled hole in a - basin. Alternatively, it could be a modular attachment with a - correlating male and female joint that must join at a particular point. - Because there is a strict connection point, when the parent moves, all - nested children must move with the parent. Another example might be a - predrilled hole in a door panel where hardware must fit through. + Another type of "decomposition" relationship is known as "nesting". + Nesting is used when an child object is physically attached to a parent + host object, through a physical predetermined connection point. The + child object must be specifically designed to attach to a other objects + at specific positions with a particular form factor. Examples include + faucets which must always be attached through a predrilled hole in a + basin. Alternatively, it could be a modular attachment with a + correlating male and female joint that must join at a particular point. + Because there is a strict connection point, when the parent moves, all + nested children must move with the parent. Another example might be a + predrilled hole in a door panel where hardware must fit through. - Nesting relationships are not very commonly used in most design and - construction models. Its main usecase is in modular construction, kit of - parts, or fabrication models. + Nesting relationships are not very commonly used in most design and + construction models. Its main usecase is in modular construction, kit of + parts, or fabrication models. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an nesting relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an nesting relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - IFC placements follow a convention where the placement is relative to - its parent in the spatial hierarchy. If your product has a placement, - its placement will be recalculated to follow this convention. + IFC placements follow a convention where the placement is relative to + its parent in the spatial hierarchy. If your product has a placement, + its placement will be recalculated to follow this convention. - For physical connections which are part of a distribution system, such - as a plug connecting into a GPO, or a duct connecting to an AHU, or two - pipe segments connecting with a bend, tee, or wye fitting, you should - not nest the two objects directly. Instead, you should nest a connection - port, which determines the type of compatible distribution flow that can - be connected to it. To do this, do not use this function, but instead - use the more specific functions in the ifcopenshell.api.system module. + For physical connections which are part of a distribution system, such + as a plug connecting into a GPO, or a duct connecting to an AHU, or two + pipe segments connecting with a bend, tee, or wye fitting, you should + not nest the two objects directly. Instead, you should nest a connection + port, which determines the type of compatible distribution flow that can + be connected to it. To do this, do not use this function, but instead + use the more specific functions in the ifcopenshell.api.system module. - Note that nesting relationships may also be used by non-physical - elements, such as cost items or tasks. In this context, nesting means - that there is an implied order to the child cost items or tasks (i.e. - task 1 should be shown before task 2). It is not necessary to use this - function for nesting non-physical elements. Instead, it is recommended - to instead just use the relevant API functions, like - ifcopenshell.api.cost.add_cost_item or - ifcopenshell.api.sequence.add_task. + Note that nesting relationships may also be used by non-physical + elements, such as cost items or tasks. In this context, nesting means + that there is an implied order to the child cost items or tasks (i.e. + task 1 should be shown before task 2). It is not necessary to use this + function for nesting non-physical elements. Instead, it is recommended + to instead just use the relevant API functions, like + ifcopenshell.api.cost.add_cost_item or + ifcopenshell.api.sequence.add_task. - :param related_objects: The list of children of the nesting relationship, - typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance] - :param relating_object: The host parent of the nesting relationship, - typically an IfcElement. - :type relating_object: ifcopenshell.entity_instance - :return: The IfcRelNests relationship instance - or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param related_objects: The list of children of the nesting relationship, + typically IfcElements. + :type related_objects: list[ifcopenshell.entity_instance] + :param relating_object: The host parent of the nesting relationship, + typically an IfcElement. + :type relating_object: ifcopenshell.entity_instance + :return: The IfcRelNests relationship instance + or `None` if `related_objects` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Faucets are designed to attach onto a sink through a predrilled hole. - sink = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcSanitaryTerminal", predefined_type="SINK") - faucet = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcValve", predefined_type="FAUCET") - ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink) - """ - self.file = file - self.settings = {"related_objects": related_objects, "relating_object": relating_object} + # Faucets are designed to attach onto a sink through a predrilled hole. + sink = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcSanitaryTerminal", predefined_type="SINK") + faucet = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcValve", predefined_type="FAUCET") + ifcopenshell.api.run("nest.assign_object", model, related_objects=[faucet], relating_object=sink) + """ + settings = {"related_objects": related_objects, "relating_object": relating_object} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["related_objects"]: - return + if not settings["related_objects"]: + return - ifc2x3 = self.file.schema == "IFC2X3" + ifc2x3 = file.schema == "IFC2X3" - related_objects = set(self.settings["related_objects"]) - relating_object = self.settings["relating_object"] + related_objects = set(settings["related_objects"]) + relating_object = settings["relating_object"] + if ifc2x3: + is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None) + else: + is_nested_by = next((i for i in relating_object.IsNestedBy), None) + + previous_nests_rels: set[ifcopenshell.entity_instance] = set() + objects_without_nests: list[ifcopenshell.entity_instance] = [] + objects_with_nests: list[ifcopenshell.entity_instance] = [] + + # check if there is anything to change + for object in related_objects: if ifc2x3: - is_nested_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelNests")), None) + object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None) else: - is_nested_by = next((i for i in relating_object.IsNestedBy), None) + object_rel = next(iter(object.Nests), None) - previous_nests_rels: set[ifcopenshell.entity_instance] = set() - objects_without_nests: list[ifcopenshell.entity_instance] = [] - objects_with_nests: list[ifcopenshell.entity_instance] = [] + if object_rel is None: + objects_without_nests.append(object) + continue - # check if there is anything to change - for object in related_objects: - if ifc2x3: - object_rel = next((i for i in object.Decomposes if i.is_a("IfcRelNests")), None) - else: - object_rel = next(iter(object.Nests), None) + # either is_nested_by is None or product is part of different rel + if object_rel != is_nested_by: + previous_nests_rels.add(object_rel) + objects_with_nests.append(object) - if object_rel is None: - objects_without_nests.append(object) - continue - - # either is_nested_by is None or product is part of different rel - if object_rel != is_nested_by: - previous_nests_rels.add(object_rel) - objects_with_nests.append(object) - - # products with already assigned nestings will be skipped - - objects_to_change = objects_without_nests + objects_with_nests - # nothing to change - if not objects_to_change: - return is_nested_by - - # NOTE: An object can both be nested and assigned to a container or an aggregate. - - # unassign elements from previous nests - for nests in previous_nests_rels: - cur_related_objects = set(nests.RelatedObjects) - related_objects - if cur_related_objects: - nests.RelatedObjects = list(cur_related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests}) - else: - history = nests.OwnerHistory - self.file.remove(nests) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new nesting - if is_nested_by: - is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_nested_by}) - else: - is_nested_by = self.file.create_entity( - "IfcRelNests", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": list(related_objects), - "RelatingObject": relating_object, - } - ) - - # NOTE: Creating a nesting relationship doesn't localize the object's placement, - # unlike assigning it to an aggregate or a container. + # products with already assigned nestings will be skipped + objects_to_change = objects_without_nests + objects_with_nests + # nothing to change + if not objects_to_change: return is_nested_by + + # NOTE: An object can both be nested and assigned to a container or an aggregate. + + # unassign elements from previous nests + for nests in previous_nests_rels: + cur_related_objects = set(nests.RelatedObjects) - related_objects + if cur_related_objects: + nests.RelatedObjects = list(cur_related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests}) + else: + history = nests.OwnerHistory + file.remove(nests) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new nesting + if is_nested_by: + is_nested_by.RelatedObjects = list(set(is_nested_by.RelatedObjects) | related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": is_nested_by}) + else: + is_nested_by = file.create_entity( + "IfcRelNests", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": list(related_objects), + "RelatingObject": relating_object, + } + ) + + # NOTE: Creating a nesting relationship doesn't localize the object's placement, + # unlike assigning it to an aggregate or a container. + + return is_nested_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py index 6d83a9e4e8..22ba7d0fc3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/change_nest.py @@ -21,29 +21,26 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, item=None, new_parent=None): - """Assigns a cost item to a new parent cost item""" - self.file = file - self.settings = {"item": item, "new_parent": new_parent} +def change_nest(file, item=None, new_parent=None) -> None: + """Assigns a cost item to a new parent cost item""" + settings = {"item": item, "new_parent": new_parent} - def execute(self): - if not self.settings["item"].Nests: - return - nests = self.settings["item"].Nests[0] - related_objects = list(nests.RelatedObjects) - related_objects.remove(self.settings["item"]) - if related_objects: - nests.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": nests}) - else: - history = nests.OwnerHistory - self.file.remove(nests) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[self.settings["item"]], - relating_object=self.settings["new_parent"], - ) + if not settings["item"].Nests: + return + nests = settings["item"].Nests[0] + related_objects = list(nests.RelatedObjects) + related_objects.remove(settings["item"]) + if related_objects: + nests.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": nests}) + else: + history = nests.OwnerHistory + file.remove(nests) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[settings["item"]], + relating_object=settings["new_parent"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py index cdc23c07a5..63b591ee28 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/reorder_nesting.py @@ -17,20 +17,17 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, item=None, old_index=0, new_index=0): - """Reorders an item in a nesting set""" - self.file = file - self.settings = {"item": item, "old_index":old_index, "new_index": new_index} +def reorder_nesting(file, item=None, old_index=0, new_index=0) -> None: + """Reorders an item in a nesting set""" + settings = {"item": item, "old_index": old_index, "new_index": new_index} - def execute(self): - if not self.settings["item"].Nests: - return - nesting_set = self.settings["item"].Nests[0] - if not self.settings["old_index"]: - old_index = nesting_set.RelatedObjects.index(self.settings["item"]) - else: - old_index = self.settings["old_index"] - items = list(getattr(nesting_set, "RelatedObjects") or []) - items.insert(self.settings["new_index"], items.pop(old_index)) - setattr(nesting_set, "RelatedObjects", items) + if not settings["item"].Nests: + return + nesting_set = settings["item"].Nests[0] + if not settings["old_index"]: + old_index = nesting_set.RelatedObjects.index(settings["item"]) + else: + old_index = settings["old_index"] + items = list(getattr(nesting_set, "RelatedObjects") or []) + items.insert(settings["new_index"], items.pop(old_index)) + setattr(nesting_set, "RelatedObjects", items) diff --git a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py index b9f48b4b5b..42e35777ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/nest/unassign_object.py @@ -21,57 +21,54 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]): - """Unassigns related_objects from their nests. +def unassign_object(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None: + """Unassigns related_objects from their nests. - An object (the whole within a decomposition) is Nested by zero or one more smaller objects. - This function will remove this nesting relationship. + An object (the whole within a decomposition) is Nested by zero or one more smaller objects. + This function will remove this nesting relationship. - If the object is not part of a nesting relationship, nothing will happen. + If the object is not part of a nesting relationship, nothing will happen. - :param related_objects: The list of children of the nesting relationship, - typically IfcElements. - :type related_objects: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param related_objects: The list of children of the nesting relationship, + typically IfcElements. + :type related_objects: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks") - subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") - subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") - ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task) - ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task) - # nothing is returned - rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1]) - # nothing is returned, relationship is removed - ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2]) - """ - self.file = file - self.settings = {"related_objects": related_objects} + task = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTasks") + subtask1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") + subtask2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcTask") + ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask1], relating_object=task) + ifcopenshell.api.run("nest.assign_object", model, related_objects=[subtask2], relating_object=task) + # nothing is returned + rel = ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask1]) + # nothing is returned, relationship is removed + ifcopenshell.api.run("nest.unassign_object", model, related_objects=[subtask2]) + """ + settings = {"related_objects": related_objects} - def execute(self) -> None: - related_objects = set(self.settings["related_objects"]) - ifc2x3 = self.file.schema == "IFC2X3" - if ifc2x3: - rels = set( - rel - for object in related_objects - if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None)) - ) + related_objects = set(settings["related_objects"]) + ifc2x3 = file.schema == "IFC2X3" + if ifc2x3: + rels = set( + rel + for object in related_objects + if (rel := next((rel for rel in object.Decomposes if rel.is_a("IfcRelNests")), None)) + ) + else: + rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None))) + + for rel in rels: + related_objects = set(rel.RelatedObjects) - related_objects + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - rels = set(rel for object in related_objects if (rel := next((rel for rel in object.Nests), None))) - - for rel in rels: - related_objects = set(rel.RelatedObjects) - related_objects - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py index e0caddbe3c..755fa4fc5f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/__init__.py @@ -15,3 +15,27 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_actor import add_actor +from .add_address import add_address +from .add_application import add_application +from .add_organisation import add_organisation +from .add_person import add_person +from .add_person_and_organisation import add_person_and_organisation +from .add_role import add_role +from .assign_actor import assign_actor +from .create_owner_history import create_owner_history +from .edit_actor import edit_actor +from .edit_address import edit_address +from .edit_organisation import edit_organisation +from .edit_person import edit_person +from .edit_role import edit_role +from .remove_actor import remove_actor +from .remove_address import remove_address +from .remove_application import remove_application +from .remove_organisation import remove_organisation +from .remove_person import remove_person +from .remove_person_and_organisation import remove_person_and_organisation +from .remove_role import remove_role +from .unassign_actor import unassign_actor +from .update_owner_history import update_owner_history diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py index c3ff65c3d1..124561f136 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_actor.py @@ -21,49 +21,46 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, actor=None, ifc_class="IfcActor"): - """Adds a new actor +def add_actor(file, actor=None, ifc_class="IfcActor") -> None: + """Adds a new actor - An actor is a person or an organisation who has a responsibility or role - to play in a project. Actor roles include design consultants, - architects, engineers, cost planners, suppliers, manufacturers, - warrantors, owners, subcontractors, etc. + An actor is a person or an organisation who has a responsibility or role + to play in a project. Actor roles include design consultants, + architects, engineers, cost planners, suppliers, manufacturers, + warrantors, owners, subcontractors, etc. - Actors may either be project actors, who are responsible for the - delivery of the project, or occupants, who are responsible for the - consumption of the project. + Actors may either be project actors, who are responsible for the + delivery of the project, or occupants, who are responsible for the + consumption of the project. - Identifying and managing actors is critical for asset management, and - identifying liability for legal submissions. + Identifying and managing actors is critical for asset management, and + identifying liability for legal submissions. - :param actor: Most commonly, an IfcOrganization (in compliance with GDPR - requirements for non personally identifiable information), or an - IfcPerson if it is a sole individual, or an IfcPersonAndOrganization - if a specific person is liable within an organisation and must be - legally nominated. - :type actor: ifcopenshell.entity_instance - :param ifc_class: Either "IfcActor" or "IfcOccupant". - :type ifc_class: str, optional - :return: The newly created IfcActor or IfcOccupant - :rtype: ifcopenshell.entity_instance + :param actor: Most commonly, an IfcOrganization (in compliance with GDPR + requirements for non personally identifiable information), or an + IfcPerson if it is a sole individual, or an IfcPersonAndOrganization + if a specific person is liable within an organisation and must be + legally nominated. + :type actor: ifcopenshell.entity_instance + :param ifc_class: Either "IfcActor" or "IfcOccupant". + :type ifc_class: str, optional + :return: The newly created IfcActor or IfcOccupant + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - """ - self.file = file - self.settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"} + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + """ + settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"} - def execute(self): - actor = ifcopenshell.api.run("root.create_entity", self.file, ifc_class=self.settings["ifc_class"]) - actor.TheActor = self.settings["actor"] - return actor + actor = ifcopenshell.api.run("root.create_entity", file, ifc_class=settings["ifc_class"]) + actor.TheActor = settings["actor"] + return actor diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py index b184408fa9..a214c86d81 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_address.py @@ -17,58 +17,53 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, assigned_object=None, ifc_class="IfcPostalAddress"): - """Add a new telecom or postal address to an organisation or person +def add_address(file, assigned_object=None, ifc_class="IfcPostalAddress") -> None: + """Add a new telecom or postal address to an organisation or person - A person or organisation may have associated contact details such as - phone numbers, mailing addresses, websites, email addresses, and instant - messaging handles. This information is critical in recording the contact - information of manufacturers and suppliers for facility management, or - liable actors. + A person or organisation may have associated contact details such as + phone numbers, mailing addresses, websites, email addresses, and instant + messaging handles. This information is critical in recording the contact + information of manufacturers and suppliers for facility management, or + liable actors. - There are two types of addresses, postal addresses for physical snail - mail, and telecom addresses for telephone or internet contact numbers - and addresses. + There are two types of addresses, postal addresses for physical snail + mail, and telecom addresses for telephone or internet contact numbers + and addresses. - :param assigned_object: The IfcOrganization or IfcPerson the contact - address belongs to. - :type assigned_object: ifcopenshell.entity_instance - :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults - to IfcPostalAddress. - :type ifc_class: str, optional - :return: The new IfcPostalAddress or IfcTelecomAddress - :rtype: ifcopenshell.entity_instance + :param assigned_object: The IfcOrganization or IfcPerson the contact + address belongs to. + :type assigned_object: ifcopenshell.entity_instance + :param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults + to IfcPostalAddress. + :type ifc_class: str, optional + :return: The new IfcPostalAddress or IfcTelecomAddress + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model) + organisation = ifcopenshell.api.run("owner.add_organisation", model) - # A snail mail address - postal = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") - ifcopenshell.api.run("owner.edit_address", model, address=postal, - attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], - "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) + # A snail mail address + postal = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") + ifcopenshell.api.run("owner.edit_address", model, address=postal, + attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], + "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) - # A phone or internet address - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["bobthebuilder@example.com"], - "WWWHomePageURL": "https://thinkmoult.com"}) - """ - self.file = file - self.settings = {"assigned_object": assigned_object, "ifc_class": ifc_class} + # A phone or internet address + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["bobthebuilder@example.com"], + "WWWHomePageURL": "https://thinkmoult.com"}) + """ + settings = {"assigned_object": assigned_object, "ifc_class": ifc_class} - def execute(self): - address = self.file.create_entity(self.settings["ifc_class"], "OFFICE") - addresses = ( - list(self.settings["assigned_object"].Addresses) if self.settings["assigned_object"].Addresses else [] - ) - addresses.append(address) - self.settings["assigned_object"].Addresses = addresses - return address + address = file.create_entity(settings["ifc_class"], "OFFICE") + addresses = list(settings["assigned_object"].Addresses) if settings["assigned_object"].Addresses else [] + addresses.append(address) + settings["assigned_object"].Addresses = addresses + return address diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py index 92861a3417..07a59db0d7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_application.py @@ -19,50 +19,52 @@ import ifcopenshell.api +def add_application( + file, + application_developer=None, + version=None, + application_full_name="IfcOpenShell", + application_identifier="IfcOpenShell", +) -> None: + """Adds a new application + + IFC data may be associated with an authoring application to identify + which application was responsible for editing or authoring the data. An + application is defined by the developing organisation, as well as a full + name and identifier. This is akin to how web browsers have an + identification string. + + :param application_developer: The IfcOrganization responsible for + creating the application. Defaults to generating an IfcOpenShell + organisation if none is provided. + :type application_developer: ifcopenshell.entity_instance, optional + :param version: The version of the application. Defaults to the + ifcopenshell.version data if not specified. + :type version: str, optional + :param application_full_name: The name of the application + :type application_full_name: str, optional + :param application_identifier: An identification string for the + application intended for computers to read. + :type application_identifier: str, optional + + Example: + + .. code:: python + + application = ifcopenshell.api.run("owner.add_application", model) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "application_developer": application_developer, + "version": version or ifcopenshell.version, + "application_full_name": application_full_name, + "application_identifier": application_identifier, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - application_developer=None, - version=None, - application_full_name="IfcOpenShell", - application_identifier="IfcOpenShell", - ): - """Adds a new application - - IFC data may be associated with an authoring application to identify - which application was responsible for editing or authoring the data. An - application is defined by the developing organisation, as well as a full - name and identifier. This is akin to how web browsers have an - identification string. - - :param application_developer: The IfcOrganization responsible for - creating the application. Defaults to generating an IfcOpenShell - organisation if none is provided. - :type application_developer: ifcopenshell.entity_instance, optional - :param version: The version of the application. Defaults to the - ifcopenshell.version data if not specified. - :type version: str, optional - :param application_full_name: The name of the application - :type application_full_name: str, optional - :param application_identifier: An identification string for the - application intended for computers to read. - :type application_identifier: str, optional - - Example: - - .. code:: python - - application = ifcopenshell.api.run("owner.add_application", model) - """ - self.file = file - self.settings = { - "application_developer": application_developer, - "version": version or ifcopenshell.version, - "application_full_name": application_full_name, - "application_identifier": application_identifier, - } - def execute(self): if not self.settings["application_developer"]: self.settings["application_developer"] = self.create_application_organisation() diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py index 2127acd570..354d66f76a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_organisation.py @@ -18,38 +18,37 @@ import ifcopenshell -class Usecase: - def __init__(self, file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science"): - """Adds a new organisation +def add_organisation( + file: ifcopenshell.file, identification: str = "APTR", name: str = "Aperture Science" +) -> ifcopenshell.entity_instance: + """Adds a new organisation - Organisations are the main way to identify manufacturers, suppliers, and - other actors who do not have a single representative or must not have - any personally identifiable information. + Organisations are the main way to identify manufacturers, suppliers, and + other actors who do not have a single representative or must not have + any personally identifiable information. - :param identification: The short code identifying the organisation. - Sometimes used in drawing naming schemes. Otherise used as a - canonicalised way of computers to identify the organisation. Like - their stock name. - :type identification: str, optional - :param name: The legal name of the organisation - :type name: str, optional - :return: The newly created IfcOrganization - :rtype: ifcopenshell.entity_instance + :param identification: The short code identifying the organisation. + Sometimes used in drawing naming schemes. Otherise used as a + canonicalised way of computers to identify the organisation. Like + their stock name. + :type identification: str, optional + :param name: The legal name of the organisation + :type name: str, optional + :return: The newly created IfcOrganization + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - """ - self.file = file - self.settings = {"identification": identification, "name": name} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + """ + settings = {"identification": identification, "name": name} - def execute(self) -> ifcopenshell.entity_instance: - data = {"Name": self.settings["name"]} - if self.file.schema == "IFC2X3": - data["Id"] = self.settings["identification"] - else: - data["Identification"] = self.settings["identification"] - return self.file.create_entity("IfcOrganization", **data) + data = {"Name": settings["name"]} + if file.schema == "IFC2X3": + data["Id"] = settings["identification"] + else: + data["Identification"] = settings["identification"] + return file.create_entity("IfcOrganization", **data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py index a607d8af29..5d571920d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person.py @@ -18,47 +18,43 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - identification: str = "HSeldon", - family_name: str = "Seldon", - given_name: str = "Hari", - ): - """Adds a new person +def add_person( + file: ifcopenshell.entity_instance, + identification: str = "HSeldon", + family_name: str = "Seldon", + given_name: str = "Hari", +) -> None: + """Adds a new person - Persons are used to identify a legal or liable representative of an - organisation or point of contact. + Persons are used to identify a legal or liable representative of an + organisation or point of contact. - :param identification: The computer readable unique identification of - the person. For example, their username in a CDE or alias. - :type identification: str, optional - :param family_name: The family name - :type family_name: str, optional - :param given_name: The given name - :type given_name: str, optional - :return: The newly created IfcPerson - :rtype: ifcopenshell.entity_instance + :param identification: The computer readable unique identification of + the person. For example, their username in a CDE or alias. + :type identification: str, optional + :param family_name: The family name + :type family_name: str, optional + :param given_name: The given name + :type given_name: str, optional + :return: The newly created IfcPerson + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - """ - self.file = file - self.settings = { - "identification": identification, - "family_name": family_name, - "given_name": given_name, - } + ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + """ + settings = { + "identification": identification, + "family_name": family_name, + "given_name": given_name, + } - def execute(self) ->ifcopenshell.entity_instance: - data = {"FamilyName": self.settings["family_name"], "GivenName": self.settings["given_name"]} - if self.file.schema == "IFC2X3": - data["Id"] = self.settings["identification"] - else: - data["Identification"] = self.settings["identification"] - return self.file.create_entity("IfcPerson", **data) + data = {"FamilyName": settings["family_name"], "GivenName": settings["given_name"]} + if file.schema == "IFC2X3": + data["Id"] = settings["identification"] + else: + data["Identification"] = settings["identification"] + return file.create_entity("IfcPerson", **data) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py index a5987b4b76..7f07c1991c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_person_and_organisation.py @@ -18,40 +18,36 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - person: ifcopenshell.entity_instance, - organisation: ifcopenshell.entity_instance, - ): - """Adds a paired person and organisation +def add_person_and_organisation( + file: ifcopenshell.entity_instance, + person: ifcopenshell.entity_instance, + organisation: ifcopenshell.entity_instance, +) -> ifcopenshell.entity_instance: + """Adds a paired person and organisation - A person and an organisation may be paired to create a representative - belonging to a company. + A person and an organisation may be paired to create a representative + belonging to a company. - :param person: The IfcPerson being the representative of the - organisation. - :type person: ifcopenshell.entity_instance - :param organisation: The IfcOrganization itself. - :type organisation: ifcopenshell.entity_instance - :return: The newly created IfcPersonAndOrganization - :rtype: ifcopenshell.entity_instance + :param person: The IfcPerson being the representative of the + organisation. + :type person: ifcopenshell.entity_instance + :param organisation: The IfcOrganization it + :type organisation: ifcopenshell.entity_instance + :return: The newly created IfcPersonAndOrganization + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") + person = ifcopenshell.api.run("owner.add_person", model, + identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) - """ - self.file = file - self.settings = {"person": person, "organisation": organisation} + ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) + """ + settings = {"person": person, "organisation": organisation} - def execute(self) -> ifcopenshell.entity_instance: - return self.file.createIfcPersonAndOrganization(self.settings["person"], self.settings["organisation"]) + return file.createIfcPersonAndOrganization(settings["person"], settings["organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py index 3b1369877c..415ed4efde 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/add_role.py @@ -17,47 +17,44 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, assigned_object=None, role="ARCHITECT"): - """Adds and assigns a new role +def add_role(file, assigned_object=None, role="ARCHITECT") -> None: + """Adds and assigns a new role - People and organisations must play one or more roles on a project. Roles - include architects, engineers, subcontractors, clients, manufacturers, - etc. Typically these roles and their corresponding responsibilities will - be outlined in contractual documents. + People and organisations must play one or more roles on a project. Roles + include architects, engineers, subcontractors, clients, manufacturers, + etc. Typically these roles and their corresponding responsibilities will + be outlined in contractual documents. - This function will both add and assign the role to the person or - organisation. + This function will both add and assign the role to the person or + organisation. - :param assigned_object: The IfcPerson or IfcOrganization the role should - be assigned to. - :type assigned_object: ifcopenshell.entity_instance - :param role: The type of role, taken from the IFC documentation for - IfcActorRole, or a custom name. - :type role: str, optional - :return: The newly created IfcActorRole - :rtype: ifcopenshell.entity_instance + :param assigned_object: The IfcPerson or IfcOrganization the role should + be assigned to. + :type assigned_object: ifcopenshell.entity_instance + :param role: The type of role, taken from the IFC documentation for + IfcActorRole, or a custom name. + :type role: str, optional + :return: The newly created IfcActorRole + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - """ - self.file = file - self.settings = {"assigned_object": assigned_object, "role": role} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + """ + settings = {"assigned_object": assigned_object, "role": role} - def execute(self): - element = self.file.createIfcActorRole("ARCHITECT") - if self.settings["role"]: - try: - element.Role = self.settings["role"] - except: - element.Role = "USERDEFINED" - element.UserDefinedRole = self.settings["role"] - roles = list(self.settings["assigned_object"].Roles) if self.settings["assigned_object"].Roles else [] - roles.append(element) - self.settings["assigned_object"].Roles = roles - return element + element = file.createIfcActorRole("ARCHITECT") + if settings["role"]: + try: + element.Role = settings["role"] + except: + element.Role = "USERDEFINED" + element.UserDefinedRole = settings["role"] + roles = list(settings["assigned_object"].Roles) if settings["assigned_object"].Roles else [] + roles.append(element) + settings["assigned_object"].Roles = roles + return element diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py index 1085adeb34..361c32bf2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/assign_actor.py @@ -20,88 +20,85 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_actor=None, related_object=None): - """Assigns an actor to an object +def assign_actor(file, relating_actor=None, related_object=None) -> None: + """Assigns an actor to an object - An actor may be assigned to objects which implies that the actor is - responsible for. This is most commonly used in facility management for - indicating the manufacturers, suppliers, and warrantors for product - types. + An actor may be assigned to objects which implies that the actor is + responsible for. This is most commonly used in facility management for + indicating the manufacturers, suppliers, and warrantors for product + types. - Here are a list of objects you may assign an actor to: + Here are a list of objects you may assign an actor to: - * IfcControl: Indicates project directives issued by the actor. - * IfcGroup: Indicates groups for which the actor is responsible. - * IfcProduct: Indicates products for which the actor is responsible. - * IfcProcess: Indicates processes for which the actor is responsible. - * IfcResource: Indicates resources for which the actor is responsible to - allocate, manage, or delegate. This is not the actor actually using - the resource or performing the work. For that type of actor, see - ifcopenshell.api.resource.assign_resource. + * IfcControl: Indicates project directives issued by the actor. + * IfcGroup: Indicates groups for which the actor is responsible. + * IfcProduct: Indicates products for which the actor is responsible. + * IfcProcess: Indicates processes for which the actor is responsible. + * IfcResource: Indicates resources for which the actor is responsible to + allocate, manage, or delegate. This is not the actor actually using + the resource or performing the work. For that type of actor, see + ifcopenshell.api.resource.assign_resource. - :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance - :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToActor relationship. - :rtype: ifcopenshell.entity_instance + :param relating_actor: The IfcActor who is responsible for the object. + :type relating_actor: ifcopenshell.entity_instance + :param related_object: The object the actor is responsible for. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToActor relationship. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We need to procure and install 2 of this particular pump type in our facility. - pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") + # We need to procure and install 2 of this particular pump type in our facility. + pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") - # Define who the manufacturer is - manufacturer = ifcopenshell.api.run("owner.add_organisation", model, - identification="PWP", name="Pumps With Power") - ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") + # Define who the manufacturer is + manufacturer = ifcopenshell.api.run("owner.add_organisation", model, + identification="PWP", name="Pumps With Power") + ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") - # To help our facility manager, it's nice to provide contact details - # of the manufacturer so they know how to call when the pump breaks. - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["contact@example.com"], - "WWWHomePageURL": "https://example.com"}) + # To help our facility manager, it's nice to provide contact details + # of the manufacturer so they know how to call when the pump breaks. + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["contact@example.com"], + "WWWHomePageURL": "https://example.com"}) - # Make the manufacturer responsible for that pump type. - ifcopenshell.api.run("owner.assign_actor", model, - relating_actor=manufacturer, related_object=pump_type) - """ - self.file = file - self.settings = { - "relating_actor": relating_actor, - "related_object": related_object, - } + # Make the manufacturer responsible for that pump type. + ifcopenshell.api.run("owner.assign_actor", model, + relating_actor=manufacturer, related_object=pump_type) + """ + settings = { + "relating_actor": relating_actor, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for rel in self.settings["related_object"].HasAssignments: - if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == self.settings["relating_actor"]: - return + if settings["related_object"].HasAssignments: + for rel in settings["related_object"].HasAssignments: + if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]: + return - rel = None + rel = None - if self.settings["relating_actor"].IsActingUpon: - rel = self.settings["relating_actor"].IsActingUpon[0] + if settings["relating_actor"].IsActingUpon: + rel = settings["relating_actor"].IsActingUpon[0] - if rel: - related_objects = list(rel.RelatedObjects) - related_objects.append(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - rel = self.file.create_entity( - "IfcRelAssignsToActor", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["related_object"]], - "RelatingActor": self.settings["relating_actor"], - } - ) - return rel + if rel: + related_objects = list(rel.RelatedObjects) + related_objects.append(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + rel = file.create_entity( + "IfcRelAssignsToActor", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingActor": settings["relating_actor"], + } + ) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py index 94183e0c6b..2b4729897f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/create_owner_history.py @@ -22,100 +22,97 @@ import ifcopenshell.api.owner.settings from typing import Union -class Usecase: - def __init__(self, file: ifcopenshell.entity_instance): - """Creates a new owner history indicating an element was added +def create_owner_history(file: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: + """Creates a new owner history indicating an element was added - Any object in IFC with a unique ID and name (such as physical products, - tasks, calendars, etc) may have an owner associated with it. An owner is - a liable person and/or organisation which a bit of metadata indicating - whether they have created the object, edited the object, when the change - was made, and which application they used. + Any object in IFC with a unique ID and name (such as physical products, + tasks, calendars, etc) may have an owner associated with it. An owner is + a liable person and/or organisation which a bit of metadata indicating + whether they have created the object, edited the object, when the change + was made, and which application they used. - IFC does not offer a comprehensive specification for version control and - change tracking, as this is completely out of scope. However this - similar ability allows IFC to satisfy legal requirements where object - ownership, responsibilities, and permissions must be specified. - Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is - not recommended to store this ownership data in IFC4 unless a legal - requirement is in place. + IFC does not offer a comprehensive specification for version control and + change tracking, as this is completely out of scope. However this + similar ability allows IFC to satisfy legal requirements where object + ownership, responsibilities, and permissions must be specified. + Recording the owner is mandatory in IFC2X3 but optional in IFC4. It is + not recommended to store this ownership data in IFC4 unless a legal + requirement is in place. - Because owner tracking is mandatory in IFC2X3, be aware that some - configuration may be required to work correctly. Read on. + Because owner tracking is mandatory in IFC2X3, be aware that some + configuration may be required to work correctly. Read on. - To track the owner, at a minimum we have to know the application that - the element was authored from, as well as the user (person and - organisation) that made the change. The IfcOpenShell API is a low level - software library and will not know what application the API is being - called from, and nor does it have the responsibility to manage the - "active user" making edits, which may be as simple as hardcoding it to - "Bob" or even be as complex as integration with a CDE's authentication - system. As a result, the developer responsible to integrate with - IfcOpenShell is expected to overload the - ifcopenshell.api.owner.settings.get_user and - ifcopenshell.api.owner.settings.get_application functions. + To track the owner, at a minimum we have to know the application that + the element was authored from, as well as the user (person and + organisation) that made the change. The IfcOpenShell API is a low level + software library and will not know what application the API is being + called from, and nor does it have the responsibility to manage the + "active user" making edits, which may be as simple as hardcoding it to + "Bob" or even be as complex as integration with a CDE's authentication + system. As a result, the developer responsible to integrate with + IfcOpenShell is expected to overload the + ifcopenshell.api.owner.settings.get_user and + ifcopenshell.api.owner.settings.get_application functions. - It is not necessary to call this function directly if you are already - using other API calls. It is a low level function only available if you - are writing your own advanced scripts and want to take advantage of the - easier ownership tracking. + It is not necessary to call this function directly if you are already + using other API calls. It is a low level function only available if you + are writing your own advanced scripts and want to take advantage of the + easier ownership tracking. - :return: The newly created IfcOwnerHistory element or `None` if it's - not IFC2X3 and user or application is not found in the current project. - :rtype: Union[ifcopenshell.entity_instance, None] + :return: The newly created IfcOwnerHistory element or `None` if it's + not IFC2X3 and user or application is not found in the current project. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we're writing a small script, not large enough to be - # its own fully branded application. In this case, let's use the - # default application which is prepopulated with "IfcOpenShell" as - # the name and version. - application = ifcopenshell.api.run("owner.add_application", model) + # Let's imagine we're writing a small script, not large enough to be + # its own fully branded application. In this case, let's use the + # default application which is prepopulated with "IfcOpenShell" as + # the name and version. + application = ifcopenshell.api.run("owner.add_application", model) - # Let's imagine we run this as an automated QA process in an - # architectural firm. However, the results must be signed off by the - # registered architect who is liable for the project. - person = ifcopenshell.api.run("owner.add_person", model, - identification="LPARTEE", family_name="Partee", given_name="Leeable") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - user = ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) + # Let's imagine we run this as an automated QA process in an + # architectural firm. However, the results must be signed off by the + # registered architect who is liable for the project. + person = ifcopenshell.api.run("owner.add_person", model, + identification="LPARTEE", family_name="Partee", given_name="Leeable") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + user = ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) - # Let's configure our owner settings to hardcode always returning - # the application and user. In theory, you could build complex user - # access control lookup functions here, but this is simple enough. - ifcopenshell.api.owner.settings.get_user = lambda x: user - ifcopenshell.api.owner.settings.get_application = lambda x: application + # Let's configure our owner settings to hardcode always returning + # the application and user. In theory, you could build complex user + # access control lookup functions here, but this is simple enough. + ifcopenshell.api.owner.settings.get_user = lambda x: user + ifcopenshell.api.owner.settings.get_application = lambda x: application - # We've finished our ownership setup. Now let's start our script and - # create a space. Notice we don't actually call - # create_owner_history at all. This is already automatically handled - # by the API when necessary. Under the hood, the API is actually - # running this code on the IfcSpace element: - # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - """ - self.file = file - self.settings = {} + # We've finished our ownership setup. Now let's start our script and + # create a space. Notice we don't actually call + # create_owner_history at all. This is already automatically handled + # by the API when necessary. Under the hood, the API is actually + # running this code on the IfcSpace element: + # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + """ + settings = {} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - user = ifcopenshell.api.owner.settings.get_user(self.file) - if self.file.schema != "IFC2X3" and not user: - return - application = ifcopenshell.api.owner.settings.get_application(self.file) - if self.file.schema != "IFC2X3" and not application: - return - return self.file.create_entity( - "IfcOwnerHistory", - OwningUser=user, - OwningApplication=application, - State="READWRITE", - ChangeAction="ADDED", - LastModifiedDate=int(time.time()), - LastModifyingUser=user, - LastModifyingApplication=application, - CreationDate=int(time.time()), - ) + user = ifcopenshell.api.owner.settings.get_user(file) + if file.schema != "IFC2X3" and not user: + return + application = ifcopenshell.api.owner.settings.get_application(file) + if file.schema != "IFC2X3" and not application: + return + return file.create_entity( + "IfcOwnerHistory", + OwningUser=user, + OwningApplication=application, + State="READWRITE", + ChangeAction="ADDED", + LastModifiedDate=int(time.time()), + LastModifyingUser=user, + LastModifyingApplication=application, + CreationDate=int(time.time()), + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py index eb6491b359..e1125ab212 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_actor.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, actor=None, attributes=None): - """Edits the attributes of an IfcActor +def edit_actor(file, actor=None, attributes=None) -> None: + """Edits the attributes of an IfcActor - For more information about the attributes and data types of an - IfcActor, consult the IFC documentation. + For more information about the attributes and data types of an + IfcActor, consult the IFC documentation. - :param actor: The IfcActor entity you want to edit - :type actor: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param actor: The IfcActor entity you want to edit + :type actor: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # Edit the description of the attribute. - ifcopenshell.api.run("actor.edit_actor", model, - actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."}) - """ - self.file = file - self.settings = {"actor": actor, "attributes": attributes or {}} + # Edit the description of the attribute. + ifcopenshell.api.run("actor.edit_actor", model, + actor=actor, attributes={"Description": "Responsible for buildings A, B, and C."}) + """ + settings = {"actor": actor, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["actor"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["actor"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py index 0f48af25d5..ba74f0ede6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_address.py @@ -17,42 +17,39 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, address=None, attributes=None): - """Edits the attributes of an IfcAddress +def edit_address(file, address=None, attributes=None) -> None: + """Edits the attributes of an IfcAddress - For more information about the attributes and data types of an - IfcAddress, consult the IFC documentation. + For more information about the attributes and data types of an + IfcAddress, consult the IFC documentation. - :param address: The IfcAddress entity you want to edit - :type address: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param address: The IfcAddress entity you want to edit + :type address: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A snail mail address - postal = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") - ifcopenshell.api.run("owner.edit_address", model, address=postal, - attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], - "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) + # A snail mail address + postal = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") + ifcopenshell.api.run("owner.edit_address", model, address=postal, + attributes={"Purpose": "OFFICE", "AddressLines": ["42 Wallaby Way"], + "Town": "Sydney", "Region": "NSW", "PostalCode": "2000"}) - # A phone or internet address - telecom = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcTelecomAddress") - ifcopenshell.api.run("owner.edit_address", model, address=telecom, - attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], - "ElectronicMailAddresses": ["bobthebuilder@example.com"], - "WWWHomePageURL": "https://thinkmoult.com"}) - """ - self.file = file - self.settings = {"address": address, "attributes": attributes or {}} + # A phone or internet address + telecom = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcTelecomAddress") + ifcopenshell.api.run("owner.edit_address", model, address=telecom, + attributes={"Purpose": "OFFICE", "TelephoneNumbers": ["+61432466949"], + "ElectronicMailAddresses": ["bobthebuilder@example.com"], + "WWWHomePageURL": "https://thinkmoult.com"}) + """ + settings = {"address": address, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["address"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["address"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py index 19c8d1e431..012e9152ba 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_organisation.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, organisation=None, attributes=None): - """Edits the attributes of an IfcOrganization +def edit_organisation(file, organisation=None, attributes=None) -> None: + """Edits the attributes of an IfcOrganization - For more information about the attributes and data types of an - IfcOrganization, consult the IFC documentation. + For more information about the attributes and data types of an + IfcOrganization, consult the IFC documentation. - :param organisation: The IfcOrganization entity you want to edit - :type organisation: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param organisation: The IfcOrganization entity you want to edit + :type organisation: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects With Ballpens") - ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation, - attributes={"name": "Architects Without Ballpens"}) - """ - self.file = file - self.settings = {"organisation": organisation, "attributes": attributes or {}} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects With Ballpens") + ifcopenshell.api.run("owner.edit_organisation", model, organisation=organisation, + attributes={"name": "Architects Without Ballpens"}) + """ + settings = {"organisation": organisation, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["organisation"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["organisation"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py index 19eedc23db..a8fdb56168 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_person.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, person=None, attributes=None): - """Edits the attributes of an IfcPerson +def edit_person(file, person=None, attributes=None) -> None: + """Edits the attributes of an IfcPerson - For more information about the attributes and data types of an - IfcPerson, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPerson, consult the IFC documentation. - :param person: The IfcPerson entity you want to edit - :type person: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param person: The IfcPerson entity you want to edit + :type person: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - ifcopenshell.api.run("owner.edit_person", model, person=person, - attributes={"MiddleNames": ["The"], "FamilyName": "Builder"}) - """ - self.file = file - self.settings = {"person": person, "attributes": attributes or {}} + person = ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + ifcopenshell.api.run("owner.edit_person", model, person=person, + attributes={"MiddleNames": ["The"], "FamilyName": "Builder"}) + """ + settings = {"person": person, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["person"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["person"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py index 160f6f6d91..6934af27e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/edit_role.py @@ -17,36 +17,33 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, role=None, attributes=None): - """Edits the attributes of an IfcActorRole +def edit_role(file, role=None, attributes=None) -> None: + """Edits the attributes of an IfcActorRole - For more information about the attributes and data types of an - IfcActorRole, consult the IFC documentation. + For more information about the attributes and data types of an + IfcActorRole, consult the IFC documentation. - :param role: The IfcActorRole entity you want to edit - :type role: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param role: The IfcActorRole entity you want to edit + :type role: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + person = ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - # By default, the role is an architect - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person) + # By default, the role is an architect + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=person) - # But Bob is not an architect - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"}) - """ - self.file = file - self.settings = {"role": role, "attributes": attributes or {}} + # But Bob is not an architect + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "CONSTRUCTIONMANAGER"}) + """ + settings = {"role": role, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["role"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["role"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py index ac99299a6f..49feb54179 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_actor.py @@ -20,36 +20,33 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, actor=None): - """Removes an actor +def remove_actor(file, actor=None) -> None: + """Removes an actor - :param actor: The IfcActor to remove. - :type actor: ifcopenshell.entity_instance - :return: None - :rtype: None + :param actor: The IfcActor to remove. + :type actor: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Setup an organisation with a single role - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) - ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) + # Setup an organisation with a single role + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation) + ifcopenshell.api.run("owner.edit_role", model, role=role, attributes={"Role": "ARCHITECT"}) - # Assign that organisation to a newly created actor - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Assign that organisation to a newly created actor + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # Actually we need ballpens on this project - ifcopenshell.api.run("owner.remove_actor", model, actor=actor) - """ - self.file = file - self.settings = {"actor": actor} + # Actually we need ballpens on this project + ifcopenshell.api.run("owner.remove_actor", model, actor=actor) + """ + settings = {"actor": actor} - def execute(self): - history = self.settings["actor"].OwnerHistory - self.file.remove(self.settings["actor"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["actor"].OwnerHistory + file.remove(settings["actor"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py index 728ffb45f5..5fba84583b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_address.py @@ -17,35 +17,32 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, address=None): - """Removes an address +def remove_address(file, address=None) -> None: + """Removes an address - Naturally, any organisations or people using that address will have the - relationship removed. + Naturally, any organisations or people using that address will have the + relationship removed. - :param address: The IfcAddress to remove. - :type address: ifcopenshell.entity_instance - :return: None - :rtype: None + :param address: The IfcAddress to remove. + :type address: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model) - address = ifcopenshell.api.run("owner.add_address", model, - assigned_object=organisation, ifc_class="IfcPostalAddress") + organisation = ifcopenshell.api.run("owner.add_organisation", model) + address = ifcopenshell.api.run("owner.add_address", model, + assigned_object=organisation, ifc_class="IfcPostalAddress") - # Change our mind and delete it - ifcopenshell.api.run("owner.remove_address", model, address=address) - """ - self.file = file - self.settings = {"address": address} + # Change our mind and delete it + ifcopenshell.api.run("owner.remove_address", model, address=address) + """ + settings = {"address": address} - def execute(self): - for inverse in self.file.get_inverse(self.settings["address"]): - if inverse.is_a() in ("IfcOrganization", "IfcPerson"): - if inverse.Addresses == (self.settings["address"],): - inverse.Addresses = None - self.file.remove(self.settings["address"]) + for inverse in file.get_inverse(settings["address"]): + if inverse.is_a() in ("IfcOrganization", "IfcPerson"): + if inverse.Addresses == (settings["address"],): + inverse.Addresses = None + file.remove(settings["address"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py index 7e21c07943..16973cfcc4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_application.py @@ -17,27 +17,24 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, application=None): - """Removes an application +def remove_application(file, application=None) -> None: + """Removes an application - Warning: removing an application may invalidate ownership histories. - Check whether or not the application is used anywhere prior to removal. + Warning: removing an application may invalidate ownership histories. + Check whether or not the application is used anywhere prior to removal. - :param address: The IfcApplication to remove. - :type address: ifcopenshell.entity_instance - :return: None - :rtype: None + :param address: The IfcApplication to remove. + :type address: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - application = ifcopenshell.api.run("owner.add_application", model) - ifcopenshell.api.run("owner.remove_address", model, application=application) - """ - self.file = file - self.settings = {"application": application} + application = ifcopenshell.api.run("owner.add_application", model) + ifcopenshell.api.run("owner.remove_address", model, application=application) + """ + settings = {"application": application} - def execute(self): - self.file.remove(self.settings["application"]) + file.remove(settings["application"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py index e9e2c77e9e..42111e017e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_organisation.py @@ -19,53 +19,50 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, organisation=None): - """Remove an organisation +def remove_organisation(file, organisation=None) -> None: + """Remove an organisation - All roles and addresses assigned to the organisation will also be - removed. + All roles and addresses assigned to the organisation will also be + removed. - :param organisation: The IfcOrganization to remove - :type organisation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param organisation: The IfcOrganization to remove + :type organisation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation) - """ - self.file = file - self.settings = {"organisation": organisation} + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + ifcopenshell.api.run("owner.remove_organisation", model, organisation=organisation) + """ + settings = {"organisation": organisation} - def execute(self): - for role in self.settings["organisation"].Roles or []: - if len(self.file.get_inverse(role)) == 1: - ifcopenshell.api.run("owner.remove_role", self.file, role=role) - for address in self.settings["organisation"].Addresses or []: - if len(self.file.get_inverse(address)) == 1: - ifcopenshell.api.run("owner.remove_address", self.file, address=address) - for inverse in self.file.get_inverse(self.settings["organisation"]): - if inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatingOrganization == self.settings["organisation"]: - self.file.remove(inverse) - elif inverse.RelatedOrganizations == (self.settings["organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["organisation"],): - inverse.Editors = None - elif inverse.is_a("IfcPersonAndOrganization"): - ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse) - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatedResourceObjects == (self.settings["organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcApplication"): - ifcopenshell.api.run("owner.remove_application", self.file, application=inverse) + for role in settings["organisation"].Roles or []: + if len(file.get_inverse(role)) == 1: + ifcopenshell.api.run("owner.remove_role", file, role=role) + for address in settings["organisation"].Addresses or []: + if len(file.get_inverse(address)) == 1: + ifcopenshell.api.run("owner.remove_address", file, address=address) + for inverse in file.get_inverse(settings["organisation"]): + if inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatingOrganization == settings["organisation"]: + file.remove(inverse) + elif inverse.RelatedOrganizations == (settings["organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["organisation"],): + inverse.Editors = None + elif inverse.is_a("IfcPersonAndOrganization"): + ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse) + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatedResourceObjects == (settings["organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcApplication"): + ifcopenshell.api.run("owner.remove_application", file, application=inverse) - self.file.remove(self.settings["organisation"]) + file.remove(settings["organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py index 8e1ba7a972..aac583b22e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person.py @@ -19,51 +19,48 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, person=None): - """Remove an person +def remove_person(file, person=None) -> None: + """Remove an person - All roles and addresses assigned to the person will also be - removed. + All roles and addresses assigned to the person will also be + removed. - :param person: The IfcPerson to remove - :type person: ifcopenshell.entity_instance - :return: None - :rtype: None + :param person: The IfcPerson to remove + :type person: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("owner.add_person", model, - identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") - ifcopenshell.api.run("owner.remove_person", model, person=person) - """ - self.file = file - self.settings = {"person": person} + ifcopenshell.api.run("owner.add_person", model, + identification="bobthebuilder", family_name="Thebuilder", given_name="Bob") + ifcopenshell.api.run("owner.remove_person", model, person=person) + """ + settings = {"person": person} - def execute(self): - for role in self.settings["person"].Roles or []: - if len(self.file.get_inverse(role)) == 1: - ifcopenshell.api.run("owner.remove_role", self.file, role=role) - for address in self.settings["person"].Addresses or []: - if len(self.file.get_inverse(address)) == 1: - ifcopenshell.api.run("owner.remove_address", self.file, address=address) - for inverse in self.file.get_inverse(self.settings["person"]): - if inverse.is_a("IfcWorkControl"): - if inverse.Creators == (self.settings["person"],): - inverse.Creators = None - elif inverse.is_a("IfcInventory"): - if inverse.ResponsiblePersons == (self.settings["person"],): - inverse.ResponsiblePersons = None - elif inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["person"],): - inverse.Editors = None - elif inverse.is_a("IfcPersonAndOrganization"): - ifcopenshell.api.run("owner.remove_person_and_organisation", self.file, person_and_organisation=inverse) - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship"): - if inverse.RelatedResourceObjects == (self.settings["person"],): - self.file.remove(inverse) - self.file.remove(self.settings["person"]) + for role in settings["person"].Roles or []: + if len(file.get_inverse(role)) == 1: + ifcopenshell.api.run("owner.remove_role", file, role=role) + for address in settings["person"].Addresses or []: + if len(file.get_inverse(address)) == 1: + ifcopenshell.api.run("owner.remove_address", file, address=address) + for inverse in file.get_inverse(settings["person"]): + if inverse.is_a("IfcWorkControl"): + if inverse.Creators == (settings["person"],): + inverse.Creators = None + elif inverse.is_a("IfcInventory"): + if inverse.ResponsiblePersons == (settings["person"],): + inverse.ResponsiblePersons = None + elif inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["person"],): + inverse.Editors = None + elif inverse.is_a("IfcPersonAndOrganization"): + ifcopenshell.api.run("owner.remove_person_and_organisation", file, person_and_organisation=inverse) + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship"): + if inverse.RelatedResourceObjects == (settings["person"],): + file.remove(inverse) + file.remove(settings["person"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py index fc85722e68..8473e04b87 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_person_and_organisation.py @@ -19,45 +19,42 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, person_and_organisation=None): - """Removes a person and organisation +def remove_person_and_organisation(file, person_and_organisation=None) -> None: + """Removes a person and organisation - Note that the underlying person and organisation is not removed, only - the "person and organisation" group. + Note that the underlying person and organisation is not removed, only + the "person and organisation" group. - :param person_and_organisation: The IfcPersonAndOrganization to remove. - :type person_and_organisation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param person_and_organisation: The IfcPersonAndOrganization to remove. + :type person_and_organisation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - person = ifcopenshell.api.run("owner.add_person", model, - identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") + person = ifcopenshell.api.run("owner.add_person", model, + identification="lecorbycorbycorb", family_name="Curbosiar", given_name="Le") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") - user = ifcopenshell.api.run("owner.add_person_and_organisation", model, - person=person, organisation=organisation) + user = ifcopenshell.api.run("owner.add_person_and_organisation", model, + person=person, organisation=organisation) - ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user) - """ - self.file = file - self.settings = {"person_and_organisation": person_and_organisation} + ifcopenshell.api.run("owner.remove_person_and_organisation", model, person_and_organisation=user) + """ + settings = {"person_and_organisation": person_and_organisation} - def execute(self): - for inverse in self.file.get_inverse(self.settings["person_and_organisation"]): - if inverse.is_a("IfcDocumentInformation"): - if inverse.Editors == (self.settings["person_and_organisation"],): - inverse.Editors = None - elif inverse.is_a("IfcActor"): - ifcopenshell.api.run("root.remove_product", self.file, product=inverse) - elif inverse.is_a("IfcResourceLevelRelationship"): - if inverse.RelatedResourceObjects == (self.settings["person_and_organisation"],): - self.file.remove(inverse) - elif inverse.is_a("IfcOwnerHistory"): - self.file.remove(inverse) - self.file.remove(self.settings["person_and_organisation"]) + for inverse in file.get_inverse(settings["person_and_organisation"]): + if inverse.is_a("IfcDocumentInformation"): + if inverse.Editors == (settings["person_and_organisation"],): + inverse.Editors = None + elif inverse.is_a("IfcActor"): + ifcopenshell.api.run("root.remove_product", file, product=inverse) + elif inverse.is_a("IfcResourceLevelRelationship"): + if inverse.RelatedResourceObjects == (settings["person_and_organisation"],): + file.remove(inverse) + elif inverse.is_a("IfcOwnerHistory"): + file.remove(inverse) + file.remove(settings["person_and_organisation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py index 5de9915c33..08bf39881c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/remove_role.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, role=None): - """Removes a role +def remove_role(file, role=None) -> None: + """Removes a role - People and organisations using the role will be untouched. This may - leave some of them without roles. + People and organisations using the role will be untouched. This may + leave some of them without roles. - :param role: The IfcActorRole to remove. - :type role: ifcopenshell.entity_instance - :return: None - :rtype: None + :param role: The IfcActorRole to remove. + :type role: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="AWB", name="Architects Without Ballpens") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="AWB", name="Architects Without Ballpens") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="ARCHITECT") - # After running this, the organisation will have no role again - ifcopenshell.api.run("owner.remove_role", model, role=role) - """ - self.file = file - self.settings = {"role": role} + # After running this, the organisation will have no role again + ifcopenshell.api.run("owner.remove_role", model, role=role) + """ + settings = {"role": role} - def execute(self): - for inverse in self.file.get_inverse(self.settings["role"]): - if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"): - if inverse.Roles == (self.settings["role"],): - inverse.Roles = None - elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): - if inverse.RelatedResourceObjects == (self.settings["organisation"],): - self.file.remove(inverse) - self.file.remove(self.settings["role"]) + for inverse in file.get_inverse(settings["role"]): + if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"): + if inverse.Roles == (settings["role"],): + inverse.Roles = None + elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"): + if inverse.RelatedResourceObjects == (settings["organisation"],): + file.remove(inverse) + file.remove(settings["role"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py index 711732bcdc..c57d0172f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/unassign_actor.py @@ -21,58 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_actor=None, related_object=None): - """Unassigns an actor to an object +def unassign_actor(file, relating_actor=None, related_object=None) -> None: + """Unassigns an actor to an object - This means that the actor is no longer responsible for the object. + This means that the actor is no longer responsible for the object. - :param relating_actor: The IfcActor who is responsible for the object. - :type relating_actor: ifcopenshell.entity_instance - :param related_object: The object the actor is responsible for. - :type related_object: ifcopenshell.entity_instance - :return: The updated IfcRelAssignsToActor relationship or none if there - is no more valid relationship. - :rtype: None, ifcopenshell.entity_instance + :param relating_actor: The IfcActor who is responsible for the object. + :type relating_actor: ifcopenshell.entity_instance + :param related_object: The object the actor is responsible for. + :type related_object: ifcopenshell.entity_instance + :return: The updated IfcRelAssignsToActor relationship or none if there + is no more valid relationship. + :rtype: None, ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # We need to procure and install 2 of this particular pump type in our facility. - pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") + # We need to procure and install 2 of this particular pump type in our facility. + pump_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcPumpType") - # Define who the manufacturer is - manufacturer = ifcopenshell.api.run("owner.add_organisation", model, - identification="PWP", name="Pumps With Power") - ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") + # Define who the manufacturer is + manufacturer = ifcopenshell.api.run("owner.add_organisation", model, + identification="PWP", name="Pumps With Power") + ifcopenshell.api.run("owner.add_role", model, assigned_object=manufacturer, role="MANUFACTURER") - # Make the manufacturer responsible for that pump type. - ifcopenshell.api.run("owner.assign_actor", model, - relating_actor=manufacturer, related_object=pump_type) + # Make the manufacturer responsible for that pump type. + ifcopenshell.api.run("owner.assign_actor", model, + relating_actor=manufacturer, related_object=pump_type) - # Undo the assignment - ifcopenshell.api.run("owner.unassign_actor", model, - relating_actor=manufacturer, related_object=pump_type) - """ - self.file = file - self.settings = { - "relating_actor": relating_actor, - "related_object": related_object, - } + # Undo the assignment + ifcopenshell.api.run("owner.unassign_actor", model, + relating_actor=manufacturer, related_object=pump_type) + """ + settings = { + "relating_actor": relating_actor, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != self.settings["relating_actor"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != settings["relating_actor"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py index 797d1b8b57..95a356ef2f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py +++ b/src/ifcopenshell-python/ifcopenshell/api/owner/update_owner_history.py @@ -24,71 +24,70 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__(self, file: ifcopenshell.file, element: ifcopenshell.entity_instance): - """Updates the owner that is assigned to an object +def update_owner_history( + file: ifcopenshell.file, element: ifcopenshell.entity_instance +) -> Union[ifcopenshell.entity_instance, None]: + """Updates the owner that is assigned to an object - This ensures that the owner is tracked to have modified the object last, - including the time when the change occured. See - ifcopenshell.api.owner.create_owner_history for details. + This ensures that the owner is tracked to have modified the object last, + including the time when the change occured. See + ifcopenshell.api.owner.create_owner_history for details. - :param element: The IfcRoot element to update the ownership details on - when a change is made. - :type element: ifcopenshell.entity_instance - :return: The updated IfcOwnerHistory element. - :rtype: ifcopenshell.entity_instance + :param element: The IfcRoot element to update the ownership details on + when a change is made. + :type element: ifcopenshell.entity_instance + :return: The updated IfcOwnerHistory element. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # See ifcopenshell.api.owner.create_owner_history for setup - # [ ... example setup code ... ] + # See ifcopenshell.api.owner.create_owner_history for setup + # [ ... example setup code ... ] - # We've finished our ownership setup. Now let's start our script and - # create a space. Notice we don't actually call - # create_owner_history at all. This is already automatically handled - # by the API when necessary. Under the hood, the API is actually - # running this code on the IfcSpace element: - # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + # We've finished our ownership setup. Now let's start our script and + # create a space. Notice we don't actually call + # create_owner_history at all. This is already automatically handled + # by the API when necessary. Under the hood, the API is actually + # running this code on the IfcSpace element: + # element.OwnerHistory = ifcopenshell.api.run("owner.create_owner_history", model) + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - # Any edits we make will have ownership tracking automatically - # applied. There is no need to run any owner.update_owner_history - # API calls either. - ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"}) - """ - self.file = file - self.settings = {"element": element} + # Any edits we make will have ownership tracking automatically + # applied. There is no need to run any owner.update_owner_history + # API calls either. + ifcopenshell.api.run("attribute.edit_attributes", model, product=space, attributes={"Name": "Lobby"}) + """ + settings = {"element": element} - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - element = self.settings["element"] - if not element.is_a("IfcRoot"): - return - user = ifcopenshell.api.owner.settings.get_user(self.file) - if not user: - return - application = ifcopenshell.api.owner.settings.get_application(self.file) - if not application: - return + element = settings["element"] + if not element.is_a("IfcRoot"): + return + user = ifcopenshell.api.owner.settings.get_user(file) + if not user: + return + application = ifcopenshell.api.owner.settings.get_application(file) + if not application: + return - # 1 IfcRoot IfcOwnerHistory - owner_history = element[1] - if not owner_history: - owner_history = ifcopenshell.api.run("owner.create_owner_history", self.file) - element[1] = owner_history - return owner_history - - if self.file.get_total_inverses(owner_history) > 1: - owner_history = ifcopenshell.util.element.copy(self.file, owner_history) - element[1] = owner_history - - # 3 IfcOwnerHistory ChangeAction - owner_history[3] = "MODIFIED" - # 4 IfcOwnerHistory LastModifiedDate - owner_history[4] = int(time.time()) - # 5 IfcOwnerHistory LastModifyingUser - owner_history[5] = user - # 6 IfcOwnerHistory LastModifyingApplication - owner_history[6] = application + # 1 IfcRoot IfcOwnerHistory + owner_history = element[1] + if not owner_history: + owner_history = ifcopenshell.api.run("owner.create_owner_history", file) + element[1] = owner_history return owner_history + + if file.get_total_inverses(owner_history) > 1: + owner_history = ifcopenshell.util.element.copy(file, owner_history) + element[1] = owner_history + + # 3 IfcOwnerHistory ChangeAction + owner_history[3] = "MODIFIED" + # 4 IfcOwnerHistory LastModifiedDate + owner_history[4] = int(time.time()) + # 5 IfcOwnerHistory LastModifyingUser + owner_history[5] = user + # 6 IfcOwnerHistory LastModifyingApplication + owner_history[6] = application + return owner_history diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py index e0caddbe3c..7decc6750b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_arbitrary_profile import add_arbitrary_profile +from .add_arbitrary_profile_with_voids import add_arbitrary_profile_with_voids +from .add_parameterized_profile import add_parameterized_profile +from .edit_profile import edit_profile +from .remove_profile import remove_profile diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py index 8cced9d934..9acc800b89 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile.py @@ -19,39 +19,42 @@ import ifcopenshell.util.unit +def add_arbitrary_profile(file, profile=None, name=None) -> None: + """Adds a new arbitrary polyline-based profile + + The profile is represented as a polyline defined by a list of + coordinates. Only straight segments are allowed. Coordinates must be + provided in SI meters. + + To represent a closed curve, the first and last coordinate must be + identical. + + :param profile: A list of coordinates + :type profile: list[list[float]] + :param name: If the profile is semantically significant (i.e. to be + managed and reused by the user) then it must be named. Otherwise, + this may be left as none. + :type name: str, optional + :return: The newly created IfcArbitraryClosedProfileDef + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A 10mm by 100mm rectangle, such that might be used as a wooden + # skirting board or kick plate. + square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, + profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)], + name="SK01 Profile") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"profile": profile, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, profile=None, name=None): - """Adds a new arbitrary polyline-based profile - - The profile is represented as a polyline defined by a list of - coordinates. Only straight segments are allowed. Coordinates must be - provided in SI meters. - - To represent a closed curve, the first and last coordinate must be - identical. - - :param profile: A list of coordinates - :type profile: list[list[float]] - :param name: If the profile is semantically significant (i.e. to be - managed and reused by the user) then it must be named. Otherwise, - this may be left as none. - :type name: str, optional - :return: The newly created IfcArbitraryClosedProfileDef - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A 10mm by 100mm rectangle, such that might be used as a wooden - # skirting board or kick plate. - square = ifcopenshell.api.run("profile.add_arbitrary_profile", model, - profile=[(0., 0.), (.01, 0.), (.01, .1), (0., .1), (0., 0.)], - name="SK01 Profile") - """ - self.file = file - self.settings = {"profile": profile, "name": name} - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) points = [self.convert_si_to_unit(p) for p in self.settings["profile"]] diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py index 7c1af83294..e1a6fe9cdb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_arbitrary_profile_with_voids.py @@ -19,46 +19,49 @@ import ifcopenshell.util.unit +def add_arbitrary_profile_with_voids(file, outer_profile=None, inner_profiles=None, name=None) -> None: + """Adds a new arbitrary polyline-based profile with voids + + The outer profile is represented as a polyline defined by a list of + coordinates. Only straight segments are allowed. Coordinates must be + provided in SI meters. + + To represent a closed curve, the first and last coordinate must be + identical. + + The inner profiles are represented as a list of polylines. + Every polyline in defined by a list of coordinates. + Only straight segments are allowed. Coordinates must be + provided in SI meters. + + :param outer_profile: A list of coordinates + :type profile: list[float] + :param inner_profiles: A list of polylines + :type profile: list[list[float]] + :param name: If the profile is semantically significant (i.e. to be + managed and reused by the user) then it must be named. Otherwise, + this may be left as none. + :type name: str, optional + :return: The newly created IfcArbitraryProfileDefWithVoids + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A 400mm by 400mm square with a 200mm by 200mm hole in it. + square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model, + outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)], + inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]], + name="SK01 Hole Profile") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, outer_profile=None, inner_profiles=None, name=None): - """Adds a new arbitrary polyline-based profile with voids - - The outer profile is represented as a polyline defined by a list of - coordinates. Only straight segments are allowed. Coordinates must be - provided in SI meters. - - To represent a closed curve, the first and last coordinate must be - identical. - - The inner profiles are represented as a list of polylines. - Every polyline in defined by a list of coordinates. - Only straight segments are allowed. Coordinates must be - provided in SI meters. - - :param outer_profile: A list of coordinates - :type profile: list[float] - :param inner_profiles: A list of polylines - :type profile: list[list[float]] - :param name: If the profile is semantically significant (i.e. to be - managed and reused by the user) then it must be named. Otherwise, - this may be left as none. - :type name: str, optional - :return: The newly created IfcArbitraryProfileDefWithVoids - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A 400mm by 400mm square with a 200mm by 200mm hole in it. - square_with_hole = ifcopenshell.api.run("profile.add_arbitrary_profile_with_voids", model, - outer_profile=[(0., 0.), (.4, 0.), (.4, .4), (0., .4), (0., 0.)], - inner_profiles=[[(0.1, 0.1), (0.3, 0.1), (0.3, 0.3), (0.1, 0.3), (0.1, 0.1)]], - name="SK01 Hole Profile") - """ - self.file = file - self.settings = {"outer_profile": outer_profile, "inner_profiles": inner_profiles, "name": name} - def execute(self): self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) outer_points = [self.convert_si_to_unit(p) for p in self.settings["outer_profile"]] @@ -69,7 +72,9 @@ class Usecase: outer_curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in outer_points]) inner_curves = [] for inner_point in inner_points: - inner_curves.append(self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point])) + inner_curves.append( + self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in inner_point]) + ) else: outer_curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(outer_points)) inner_curves = [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py index 0201095feb..c0a6348656 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/add_parameterized_profile.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, ifc_class=None): - """Adds a new parameterised profile +def add_parameterized_profile(file, ifc_class=None) -> None: + """Adds a new parameterised profile - IFC offers parameterised profiles for common standardised hot roll - steel sections and common concrete forms. A full list is available on - the IFC documentation as subclasses of IfcParameterizedProfileDef. + IFC offers parameterised profiles for common standardised hot roll + steel sections and common concrete forms. A full list is available on + the IFC documentation as subclasses of IfcParameterizedProfileDef. - Currently, this API has no benefit over directly calling - ifcopenshell.file.create_entity. + Currently, this API has no benefit over directly calling + ifcopenshell.file.create_entity. - :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd - like to create. - :type ifc_class: str - :return: The newly created element depending on the specified ifc_class. - :rtype: ifcopenshell.entity_instance + :param ifc_class: The subclass of IfcParameterizedProfileDef that you'd + like to create. + :type ifc_class: str + :return: The newly created element depending on the specified ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle.Radius = 1. - """ - self.file = file - self.settings = {"ifc_class": ifc_class} + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle.Radius = 1. + """ + settings = {"ifc_class": ifc_class} - def execute(self): - return self.file.create_entity(self.settings["ifc_class"]) + return file.create_entity(settings["ifc_class"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py index 759a5d4c7d..4d525a5d32 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/edit_profile.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, profile=None, attributes=None): - """Edits the attributes of an IfcProfileDef +def edit_profile(file, profile=None, attributes=None) -> None: + """Edits the attributes of an IfcProfileDef - For more information about the attributes and data types of an - IfcProfileDef, consult the IFC documentation. + For more information about the attributes and data types of an + IfcProfileDef, consult the IFC documentation. - :param profile: The IfcProfileDef entity you want to edit - :type profile: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param profile: The IfcProfileDef entity you want to edit + :type profile: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle = 1. + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle = 1. - ifcopenshell.api.run("profile.edit_profile", model, - profile=circle, attributes={"ProfileName": "1000mm Dia"}) - """ - self.file = file - self.settings = {"profile": profile, "attributes": attributes or {}} + ifcopenshell.api.run("profile.edit_profile", model, + profile=circle, attributes={"ProfileName": "1000mm Dia"}) + """ + settings = {"profile": profile, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["profile"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["profile"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py index 47d3391e00..58f3eebedb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py +++ b/src/ifcopenshell-python/ifcopenshell/api/profile/remove_profile.py @@ -20,32 +20,29 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, profile=None): - """Removes a profile +def remove_profile(file, profile=None) -> None: + """Removes a profile - :param profile: The IfcProfileDef to remove. - :type profile: ifcopenshell.entity_instance - :return: None - :rtype: None + :param profile: The IfcProfileDef to remove. + :type profile: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, - ifc_class="IfcCircleProfileDef") - circle = 1. - ifcopenshell.api.run("profile.remove_profile", model, profile=circle) - """ - self.file = file - self.settings = {"profile": profile} + circle = ifcopenshell.api.run("profile.add_parameterized_profile", model, + ifc_class="IfcCircleProfileDef") + circle = 1. + ifcopenshell.api.run("profile.remove_profile", model, profile=circle) + """ + settings = {"profile": profile} - def execute(self): - subelements = set() - for attribute in self.settings["profile"]: - if isinstance(attribute, ifcopenshell.entity_instance): - subelements.add(attribute) - self.file.remove(self.settings["profile"]) - for subelement in subelements: - ifcopenshell.util.element.remove_deep2(self.file, subelement) + subelements = set() + for attribute in settings["profile"]: + if isinstance(attribute, ifcopenshell.entity_instance): + subelements.add(attribute) + file.remove(settings["profile"]) + for subelement in subelements: + ifcopenshell.util.element.remove_deep2(file, subelement) diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py index e0caddbe3c..e9c21fbddd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .append_asset import append_asset +from .assign_declaration import assign_declaration +from .create_file import create_file +from .unassign_declaration import unassign_declaration diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index 3e88c7c82a..fc101347ee 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -21,100 +21,103 @@ import ifcopenshell.api import ifcopenshell.api.owner.settings +def append_asset(file, library=None, element=None, reuse_identities=None) -> None: + """Appends an asset from a library into the active project + + A BIM library asset may be a type product (e.g. wall type), product + (e.g. pump), material, profile, or cost schedule. + + This copies the asset from the specified library file into the active + project. It handles all details like ensuring that product materials, + styles, properties, quantities, and so on are preserved. + + If an asset contains geometry, the geometric contexts are also + intelligentely transplanted such that existing equivalent contexts are + reused. + + Do not mix units. + + :param library: The file object containing the asset. + :type library: ifcopenshell.file + :param element: An element in the library file of the asset. It may be + an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or + IfcProfileDef. + :type element: ifcopenshell.entity_instance + :param reuse_identities: Optional dictionary of mapped entities' identities to the + already created elements. It will be used to avoid creating + duplicated inverse elements during multiple `project.append_asset` calls. If you want + to add just 1 asset or if added assets won't have any shared elements, then it can be left empty. + :type reuse_identities: dict[int, ifcopenshell.entity_instance] + :return: The appended element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + + # Assign units for our example library + unit = ifcopenshell.api.run("unit.add_si_unit", library, + unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") + ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) + + # Let's create a single asset of a 200mm thick concrete wall + wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") + concrete = ifcopenshell.api.run("material.add_material", usecase.file, name="CON", category="concrete") + rel = ifcopenshell.api.run("material.assign_material", library, + products=[wall_type], type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", library, + layer_set=rel.RelatingMaterial, material=concrete) + layer.Name = "Structure" + layer.LayerThickness = 200 + + # Mark our wall type as a reusable asset in our library. + ifcopenshell.api.run("project.assign_declaration", library, + definitions=[wall_type], relating_context=context) + + # Let's imagine we're starting a new project + model = ifcopenshell.api.run("project.create_file") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") + + # Now we can easily append our wall type from our libary + wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type) + + Example of adding multiple assets and avoiding duplicated inverses: + + .. code:: python + + # since occurrences of IfcWindow of the same type + # might have shared inverses (e.g. IfcStyledItem) + # we provide a dictionary that will be populated with newly created items + # and reused to avoid duplicated elements + reuse_identities = dict() + + for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"): + ifcopenshell.api.run( + "project.append_asset", + model, library=library, + element=wall_type + reuse_identities=reuse_identities + ) + + """ + usecase = Usecase() + usecase.file: ifcopenshell.file = file + usecase.settings = { + "library": library, + "element": element, + "reuse_identities": {} if reuse_identities is None else reuse_identities, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, library=None, element=None, reuse_identities=None): - """Appends an asset from a library into the active project - - A BIM library asset may be a type product (e.g. wall type), product - (e.g. pump), material, profile, or cost schedule. - - This copies the asset from the specified library file into the active - project. It handles all details like ensuring that product materials, - styles, properties, quantities, and so on are preserved. - - If an asset contains geometry, the geometric contexts are also - intelligentely transplanted such that existing equivalent contexts are - reused. - - Do not mix units. - - :param library: The file object containing the asset. - :type library: ifcopenshell.file - :param element: An element in the library file of the asset. It may be - an IfcTypeProduct, IfcProduct, IfcMaterial, IfcCostSchedule, or - IfcProfileDef. - :type element: ifcopenshell.entity_instance - :param reuse_identities: Optional dictionary of mapped entities' identities to the - already created elements. It will be used to avoid creating - duplicated inverse elements during multiple `project.append_asset` calls. If you want - to add just 1 asset or if added assets won't have any shared elements, then it can be left empty. - :type reuse_identities: dict[int, ifcopenshell.entity_instance] - :return: The appended element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - - # Assign units for our example library - unit = ifcopenshell.api.run("unit.add_si_unit", library, - unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") - ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) - - # Let's create a single asset of a 200mm thick concrete wall - wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") - concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete") - rel = ifcopenshell.api.run("material.assign_material", library, - products=[wall_type], type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", library, - layer_set=rel.RelatingMaterial, material=concrete) - layer.Name = "Structure" - layer.LayerThickness = 200 - - # Mark our wall type as a reusable asset in our library. - ifcopenshell.api.run("project.assign_declaration", library, - definitions=[wall_type], relating_context=context) - - # Let's imagine we're starting a new project - model = ifcopenshell.api.run("project.create_file") - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") - - # Now we can easily append our wall type from our libary - wall_type = ifcopenshell.api.run("project.append_asset", model, library=library, element=wall_type) - - Example of adding multiple assets and avoiding duplicated inverses: - - .. code:: python - - # since occurrences of IfcWindow of the same type - # might have shared inverses (e.g. IfcStyledItem) - # we provide a dictionary that will be populated with newly created items - # and reused to avoid duplicated elements - reuse_identities = dict() - - for element in ifcopenshell.util.selector.filter_elements(model, "IfcWindow"): - ifcopenshell.api.run( - "project.append_asset", - model, library=library, - element=wall_type - reuse_identities=reuse_identities - ) - - """ - self.file: ifcopenshell.file = file - self.settings = { - "library": library, - "element": element, - "reuse_identities": {} if reuse_identities is None else reuse_identities, - } - def execute(self): # mapping of old element ids to new elements self.added_elements: dict[int, ifcopenshell.entity_instance] = {} diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py index e6e919b77b..776a26b8f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -22,129 +22,125 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.entity_instance, - definitions: list[ifcopenshell.entity_instance], - relating_context: ifcopenshell.entity_instance, - ): - """Declares the list of elements to the project +def assign_declaration( + file: ifcopenshell.entity_instance, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Declares the list of elements to the project - All data in a model must be directly or indirectly related to the - project. Most data is indirectly related, existing instead within the - spatial decomposition tree. Other data, such as types, may be declared - at the top level. + All data in a model must be directly or indirectly related to the + project. Most data is indirectly related, existing instead within the + spatial decomposition tree. Other data, such as types, may be declared + at the top level. - Most of the time, the API handles declaration automatically for you. - There is one scenario where you might want to explicitly declare objects - to the project, and that's when you want to organise objects into - project libraries for future use (such as an assets library). Assigning - a declaration lets you say that an object belongs to a library. + Most of the time, the API handles declaration automatically for you. + There is one scenario where you might want to explicitly declare objects + to the project, and that's when you want to organise objects into + project libraries for future use (such as an assets library). Assigning + a declaration lets you say that an object belongs to a library. - :param definitions: The list of objects you want to declare. Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance] - :param relating_context: The IfcProject, or more commonly the - IfcProjectLibrary that you want the object to be part of. - :type relating_context: ifcopenshell.entity_instance - :return: The new IfcRelDeclares relationship or None if all definitions - were already declared / do not support declaration. - :rtype: Union[ifcopenshell.entity_instance, None] + :param definitions: The list of objects you want to declare. Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance] + :param relating_context: The IfcProject, or more commonly the + IfcProjectLibrary that you want the object to be part of. + :type relating_context: ifcopenshell.entity_instance + :return: The new IfcRelDeclares relationship or None if all definitions + were already declared / do not support declaration. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") - # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + # It's necessary to say our library is part of our project. + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - # Assign units for our example library - unit = ifcopenshell.api.run("unit.add_si_unit", library, - unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") - ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) + # Assign units for our example library + unit = ifcopenshell.api.run("unit.add_si_unit", library, + unit_type="LENGTHUNIT", name="METRE", prefix="MILLI") + ifcopenshell.api.run("unit.assign_unit", library, units=[unit]) - # Let's create a single asset of a 200mm thick concrete wall - wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") - concrete = ifcopenshell.api.run("material.add_material", self.file, name="CON", category="concrete") - rel = ifcopenshell.api.run("material.assign_material", library, - products=[wall_type], type="IfcMaterialLayerSet") - layer = ifcopenshell.api.run("material.add_layer", library, - layer_set=rel.RelatingMaterial, material=concrete) - layer.Name = "Structure" - layer.LayerThickness = 200 + # Let's create a single asset of a 200mm thick concrete wall + wall_type = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcWallType", name="WAL01") + concrete = ifcopenshell.api.run("material.add_material", file, name="CON", category="concrete") + rel = ifcopenshell.api.run("material.assign_material", library, + products=[wall_type], type="IfcMaterialLayerSet") + layer = ifcopenshell.api.run("material.add_layer", library, + layer_set=rel.RelatingMaterial, material=concrete) + layer.Name = "Structure" + layer.LayerThickness = 200 - # Mark our wall type as a reusable asset in our library. - ifcopenshell.api.run("project.assign_declaration", library, - definitions=[wall_type], relating_context=context) + # Mark our wall type as a reusable asset in our library. + ifcopenshell.api.run("project.assign_declaration", library, + definitions=[wall_type], relating_context=context) - # All done, just for fun let's save our asset library to disk for later use. - library.write("/path/to/my-library.ifc") - """ - self.file = file - self.settings = { - "definitions": definitions, - "relating_context": relating_context, - } + # All done, just for fun let's save our asset library to disk for later use. + library.write("/path/to/my-library.ifc") + """ + settings = { + "definitions": definitions, + "relating_context": relating_context, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - relating_context = self.settings["relating_context"] - all_declares = relating_context.Declares - definitions = set(self.settings["definitions"]) + relating_context = settings["relating_context"] + all_declares = relating_context.Declares + definitions = set(settings["definitions"]) - previous_declares_rels: set[ifcopenshell.entity_instance] = set() - objects_without_contexts: list[ifcopenshell.entity_instance] = [] - objects_with_contexts: list[ifcopenshell.entity_instance] = [] + previous_declares_rels: set[ifcopenshell.entity_instance] = set() + objects_without_contexts: list[ifcopenshell.entity_instance] = [] + objects_with_contexts: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for definition in definitions: - has_context = getattr(definition, "HasContext", None) - if has_context is None: - continue + # check if there is anything to change + for definition in definitions: + has_context = getattr(definition, "HasContext", None) + if has_context is None: + continue - object_rel = next(iter(has_context), None) - if object_rel is None: - objects_without_contexts.append(definition) - continue + object_rel = next(iter(has_context), None) + if object_rel is None: + objects_without_contexts.append(definition) + continue - # either rel doesn't exist or product is part of different rel - if object_rel not in all_declares: - previous_declares_rels.add(object_rel) - objects_with_contexts.append(definition) + # either rel doesn't exist or product is part of different rel + if object_rel not in all_declares: + previous_declares_rels.add(object_rel) + objects_with_contexts.append(definition) - objects_to_change = objects_without_contexts + objects_with_contexts - # nothing to change - if not objects_to_change: - return None + objects_to_change = objects_without_contexts + objects_with_contexts + # nothing to change + if not objects_to_change: + return None - for has_context in previous_declares_rels: - related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts - if related_definitions: - has_context.RelatedDefinitions = related_definitions - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_context}) - else: - history = has_context.OwnerHistory - self.file.remove(has_context) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - declares = next(iter(all_declares), None) - if declares: - declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change)) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": declares}) + for has_context in previous_declares_rels: + related_definitions = set(has_context.RelatedDefinitions) - objects_with_contexts + if related_definitions: + has_context.RelatedDefinitions = related_definitions + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": has_context}) else: - declares = self.file.create_entity( - "IfcRelDeclares", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedDefinitions": list(objects_to_change), - "RelatingContext": relating_context, - } - ) - return declares + history = has_context.OwnerHistory + file.remove(has_context) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + declares = next(iter(all_declares), None) + if declares: + declares.RelatedDefinitions = list(set(declares.RelatedDefinitions) | set(objects_to_change)) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": declares}) + else: + declares = file.create_entity( + "IfcRelDeclares", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedDefinitions": list(objects_to_change), + "RelatingContext": relating_context, + } + ) + return declares diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py index 1b3e644cbd..6db9e87c01 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/create_file.py @@ -20,52 +20,46 @@ import datetime import ifcopenshell -class Usecase: - def __init__(self, version: str = "IFC4"): - """Create a blank IFC model file object +def create_file(version: str = "IFC4") -> ifcopenshell.file: + """Create a blank IFC model file object - Create a new IFC file object based on the nominated schema version. The - schema version you choose determines what type of IFC data you can store - in this model. The file is blank and contains no entities. + Create a new IFC file object based on the nominated schema version. The + schema version you choose determines what type of IFC data you can store + in this model. The file is blank and contains no entities. - It also sets up header data for STEP file serialisation, such as the - current timestamp, IfcOpenShell as the preprocessor, and defaults to a - DesignTransferView MVD. + It also sets up header data for STEP file serialisation, such as the + current timestamp, IfcOpenShell as the preprocessor, and defaults to a + DesignTransferView MVD. - :param version: The schema version of the IFC file. Choose from - "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom - schema, you may specify that schema identifier here too. - :type version: str, optional - :return: The created IFC file object. - :rtype: ifcopenshell.file + :param version: The schema version of the IFC file. Choose from + "IFC2X3", "IFC4", or "IFC4X3". If you have loaded in a custom + schema, you may specify that schema identifier here too. + :type version: str, optional + :return: The created IFC file object. + :rtype: ifcopenshell.file - Example: + Example: - .. code:: python + .. code:: python - # Start a new model. - model = ifcopenshell.api.run("project.create_file") + # Start a new model. + model = ifcopenshell.api.run("project.create_file") - # It's currently a blank model, so typically the first thing we do - # is create a project in it. - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") + # It's currently a blank model, so typically the first thing we do + # is create a project in it. + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject", name="Test") - # ... and off we go! - """ - self.settings = {"version": version} + # ... and off we go! + """ + settings = {"version": version} - def execute(self) -> ifcopenshell.file: - self.file = ifcopenshell.file(schema=self.settings["version"]) - self.file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe - self.file.wrapped_data.header.file_name.time_stamp = ( - datetime.datetime.utcnow() - .replace(tzinfo=datetime.timezone.utc) - .astimezone() - .replace(microsecond=0) - .isoformat() - ) - self.file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version) - self.file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version) - self.file.wrapped_data.header.file_name.authorization = "Nobody" - self.file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",) - return self.file + file = ifcopenshell.file(schema=settings["version"]) + file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe + file.wrapped_data.header.file_name.time_stamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat() + ) + file.wrapped_data.header.file_name.preprocessor_version = "IfcOpenShell {}".format(ifcopenshell.version) + file.wrapped_data.header.file_name.originating_system = "IfcOpenShell {}".format(ifcopenshell.version) + file.wrapped_data.header.file_name.authorization = "Nobody" + file.wrapped_data.header.file_description.description = ("ViewDefinition[DesignTransferView]",) + return file diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py index 8f8e726570..38a0ae4e25 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -21,59 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - definitions: list[ifcopenshell.entity_instance], - relating_context: ifcopenshell.entity_instance, - ): - """Unassigns a list of objects from a project or project library +def unassign_declaration( + file: ifcopenshell.file, + definitions: list[ifcopenshell.entity_instance], + relating_context: ifcopenshell.entity_instance, +) -> None: + """Unassigns a list of objects from a project or project library - Typically used to remove an asset from a project library. + Typically used to remove an asset from a project library. - :param definitions: The list of objects you want to undeclare. - Typically a list of assets. - :type definitions: list[ifcopenshell.entity_instance] - :param relating_context: The IfcProject, or more commonly the - IfcProjectLibrary that you want the object to no longer be part of. - :type relating_context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param definitions: The list of objects you want to undeclare. + Typically a list of assets. + :type definitions: list[ifcopenshell.entity_instance] + :param relating_context: The IfcProject, or more commonly the + IfcProjectLibrary that you want the object to no longer be part of. + :type relating_context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Programmatically generate a library. You could do this visually too. - library = ifcopenshell.api.run("project.create_file") - root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") - context = ifcopenshell.api.run("root.create_entity", library, - ifc_class="IfcProjectLibrary", name="Demo Library") + # Programmatically generate a library. You could do this visually too. + library = ifcopenshell.api.run("project.create_file") + root = ifcopenshell.api.run("root.create_entity", library, ifc_class="IfcProject", name="Demo Library") + context = ifcopenshell.api.run("root.create_entity", library, + ifc_class="IfcProjectLibrary", name="Demo Library") - # It's necessary to say our library is part of our project. - ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) + # It's necessary to say our library is part of our project. + ifcopenshell.api.run("project.assign_declaration", library, definitions=[context], relating_context=root) - # Remove the library from our project - ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root) - """ - self.file = file - self.settings = { - "definitions": definitions, - "relating_context": relating_context, - } + # Remove the library from our project + ifcopenshell.api.run("project.unassign_declaration", library, definitions=[context], relating_context=root) + """ + settings = { + "definitions": definitions, + "relating_context": relating_context, + } - def execute(self): - definitions = set(self.settings["definitions"]) - rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))} + definitions = set(settings["definitions"]) + rels = {rel for obj in definitions if (rel := next(iter(obj.HasContext), None))} - for rel in rels: - related_definitions = set(rel.RelatedDefinitions) - definitions - if related_definitions: - rel.RelatedDefinitions = list(related_definitions) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_definitions = set(rel.RelatedDefinitions) - definitions + if related_definitions: + rel.RelatedDefinitions = list(related_definitions) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py index e0caddbe3c..c3e01e30df 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/__init__.py @@ -15,3 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_pset import add_pset +from .add_qto import add_qto +from .edit_pset import edit_pset +from .edit_qto import edit_qto +from .remove_pset import remove_pset diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py index 1fd1a0c61a..6cb72e435c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py @@ -19,133 +19,127 @@ import ifcopenshell -class Usecase: - def __init__(self, file, product=None, name=None): - """Adds a new property set to a product +def add_pset(file, product=None, name=None) -> None: + """Adds a new property set to a product - Products, such as physical objects or types in IFC may have properties - associated with them. These properties are typically simple key value - metadata with data types. For example, a wall type may have a property - called FireRating with a text value of "2HR". Properties are grouped - into property sets, so that related properties are grouped together. + Products, such as physical objects or types in IFC may have properties + associated with them. These properties are typically simple key value + metadata with data types. For example, a wall type may have a property + called FireRating with a text value of "2HR". Properties are grouped + into property sets, so that related properties are grouped together. - If a property is assigned to a type, the property is inherited by all - occurrences of that type. For example, a wall type with a FireRating - property of "2HR" automatically implies that all walls of that wall type - also have a FireRating of "2HR". It is not necessary to explictly define - the property again for each occurrence. This also means that properties - are typically defined on types. If the same property is defined at an - occurrence, this overrides the property defined on the type. + If a property is assigned to a type, the property is inherited by all + occurrences of that type. For example, a wall type with a FireRating + property of "2HR" automatically implies that all walls of that wall type + also have a FireRating of "2HR". It is not necessary to explictly define + the property again for each occurrence. This also means that properties + are typically defined on types. If the same property is defined at an + occurrence, this overrides the property defined on the type. - buildingSMART has come up with a long list of standardised properties - for the most common properties required internationally. This solves the - age-old question of "where do I store my FireRating data for walls"? The - answer, in this case, is in the "FireRating" property with an "IfcLabel" - data type grouped in the "Pset_WallCommon" property set. It is - recommended to view the list of standardised buildingSMART properties - and see if any suit your needs first. If none are appropriate, then you - are free to create your own custom properties. + buildingSMART has come up with a long list of standardised properties + for the most common properties required internationally. This solves the + age-old question of "where do I store my FireRating data for walls"? The + answer, in this case, is in the "FireRating" property with an "IfcLabel" + data type grouped in the "Pset_WallCommon" property set. It is + recommended to view the list of standardised buildingSMART properties + and see if any suit your needs first. If none are appropriate, then you + are free to create your own custom properties. - This function adds a blank named property set. One you have a property - set you may add properties using ifcopenshell.api.pset.edit_pset. + This function adds a blank named property set. One you have a property + set you may add properties using ifcopenshell.api.pset.edit_pset. - See also ifcopenshell.api.pset.add_qto if you want to add quantification - data, rather than arbitrary metadata. + See also ifcopenshell.api.pset.add_qto if you want to add quantification + data, rather than arbitrary metadata. - :param product: The IfcObject that you want to assign a property set to. - :type product: ifcopenshell.entity_instance - :param name: The name of the property set. Property sets that are - standardised by buildingSMART typically have a prefix of "Pset_", - like "Pset_WallCommon". If you create your own, you must not use - that prefix. It is recommended to use your own prefix tailored to - your project, company, or local government requirement. - :type name: str - :return: The newly created IfcPropertySet - :rtype: ifcopenshell.entity_instance + :param product: The IfcObject that you want to assign a property set to. + :type product: ifcopenshell.entity_instance + :param name: The name of the property set. Property sets that are + standardised by buildingSMART typically have a prefix of "Pset_", + like "Pset_WallCommon". If you create your own, you must not use + that prefix. It is recommended to use your own prefix tailored to + your project, company, or local government requirement. + :type name: str + :return: The newly created IfcPropertySet + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a new wall type. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + # Let's imagine we have a new wall type. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - # Note that this only creates and assigns an empty property set. We - # still need to add properties into the property set. Having blank - # property sets are invalid. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + # Note that this only creates and assigns an empty property set. We + # still need to add properties into the property set. Having blank + # property sets are invalid. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - # Add a fire rating property standardised by buildingSMART. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"}) - """ - self.file = file - self.settings = {"product": product, "name": name} + # Add a fire rating property standardised by buildingSMART. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"FireRating": "2HR"}) + """ + settings = {"product": product, "name": name} - def execute(self): - if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"): - for rel in self.settings["product"].IsDefinedBy or []: - if ( - rel.is_a("IfcRelDefinesByProperties") - and rel.RelatingPropertyDefinition.Name == self.settings["name"] - ): - return rel.RelatingPropertyDefinition + if settings["product"].is_a("IfcObject") or settings["product"].is_a("IfcContext"): + for rel in settings["product"].IsDefinedBy or []: + if rel.is_a("IfcRelDefinesByProperties") and rel.RelatingPropertyDefinition.Name == settings["name"]: + return rel.RelatingPropertyDefinition - pset = self.file.create_entity( - "IfcPropertySet", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["name"], - } - ) - self.file.create_entity( - "IfcRelDefinesByProperties", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], - "RelatingPropertyDefinition": pset, - } - ) - return pset - elif self.settings["product"].is_a("IfcTypeObject"): - for definition in self.settings["product"].HasPropertySets or []: - if definition.Name == self.settings["name"]: - return definition + pset = file.create_entity( + "IfcPropertySet", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["name"], + } + ) + file.create_entity( + "IfcRelDefinesByProperties", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["product"]], + "RelatingPropertyDefinition": pset, + } + ) + return pset + elif settings["product"].is_a("IfcTypeObject"): + for definition in settings["product"].HasPropertySets or []: + if definition.Name == settings["name"]: + return definition - pset = self.file.create_entity( - "IfcPropertySet", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": self.settings["name"], - } - ) - has_property_sets = list(self.settings["product"].HasPropertySets or []) - has_property_sets.append(pset) - self.settings["product"].HasPropertySets = has_property_sets - return pset - elif self.settings["product"].is_a("IfcMaterialDefinition"): - for definition in self.settings["product"].HasProperties or []: - if definition.Name == self.settings["name"]: - return definition + pset = file.create_entity( + "IfcPropertySet", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": settings["name"], + } + ) + has_property_sets = list(settings["product"].HasPropertySets or []) + has_property_sets.append(pset) + settings["product"].HasPropertySets = has_property_sets + return pset + elif settings["product"].is_a("IfcMaterialDefinition"): + for definition in settings["product"].HasProperties or []: + if definition.Name == settings["name"]: + return definition - return self.file.create_entity( - "IfcMaterialProperties", - **{ - "Name": self.settings["name"], - "Material": self.settings["product"], - } - ) - elif self.settings["product"].is_a("IfcProfileDef"): - for definition in self.settings["product"].HasProperties or []: - if definition.Name == self.settings["name"]: - return definition + return file.create_entity( + "IfcMaterialProperties", + **{ + "Name": settings["name"], + "Material": settings["product"], + } + ) + elif settings["product"].is_a("IfcProfileDef"): + for definition in settings["product"].HasProperties or []: + if definition.Name == settings["name"]: + return definition - return self.file.create_entity( - "IfcProfileProperties", - **{ - "Name": self.settings["name"], - "ProfileDefinition": self.settings["product"], - } - ) + return file.create_entity( + "IfcProfileProperties", + **{ + "Name": settings["name"], + "ProfileDefinition": settings["product"], + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py index a3c7b54299..0215ab31ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_qto.py @@ -20,65 +20,68 @@ import ifcopenshell import ifcopenshell.api +def add_qto(file, product=None, name=None) -> None: + """Adds a new quantity set to a product + + Products, such as physical objects or types in IFC may have quantities + associated with them. These quantities are typically simple key value + metadata with data types. For example, a wall type may have a quantity + called NetSideArea with a area value of "4.2". Quantities are grouped + into quantity sets, so that related quantities are grouped together. + + Quantities are similar to, but different from properties in that they + may store a method of measurement or formula. Quantities may also have + parametric relationships to other calculated values, such as cost + schedules, resource utilisation, or construction task durations. + + buildingSMART has come up with a long list of standardised quantities + for the most common quantities required internationally. This solves the + age-old question of "what's the standard way of storing quantity + take-off data"? It is recommended to view the list of standardised + buildingSMART quantities and see if any suit your needs first. If none + are appropriate, then you are free to create your own custom quantities. + + This function adds a blank named quantity set. One you have a quantity + set you may add quantities using ifcopenshell.api.pset.edit_qto. + + See also ifcopenshell.api.pset.add_qto if you want to arbitrary + metadata, rather than quantification data. + + :param product: The IfcObject that you want to assign a quantity set to. + :type product: ifcopenshell.entity_instance + :param name: The name of the quantity set. Quantity sets that are + standardised by buildingSMART typically have a prefix of "Qto_", + like "Qto_WallBaseQuantities". If you create your own, you must not + use that prefix. It is recommended to use your own prefix tailored + to your project, company, or local government requirement. + :type name: str + :return: The newly created IfcElementQuantity + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Let's imagine we have a new wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Note that this only creates and assigns an empty quantity set. We + # still need to add quantities into the property set. Having blank + # quantity sets are invalid. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities") + + # Add a side area property standardised by buildingSMART. This + # allows quantity take-off to occur, even though no geometry has + # even been modelled! + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": product, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, product=None, name=None): - """Adds a new quantity set to a product - - Products, such as physical objects or types in IFC may have quantities - associated with them. These quantities are typically simple key value - metadata with data types. For example, a wall type may have a quantity - called NetSideArea with a area value of "4.2". Quantities are grouped - into quantity sets, so that related quantities are grouped together. - - Quantities are similar to, but different from properties in that they - may store a method of measurement or formula. Quantities may also have - parametric relationships to other calculated values, such as cost - schedules, resource utilisation, or construction task durations. - - buildingSMART has come up with a long list of standardised quantities - for the most common quantities required internationally. This solves the - age-old question of "what's the standard way of storing quantity - take-off data"? It is recommended to view the list of standardised - buildingSMART quantities and see if any suit your needs first. If none - are appropriate, then you are free to create your own custom quantities. - - This function adds a blank named quantity set. One you have a quantity - set you may add quantities using ifcopenshell.api.pset.edit_qto. - - See also ifcopenshell.api.pset.add_qto if you want to arbitrary - metadata, rather than quantification data. - - :param product: The IfcObject that you want to assign a quantity set to. - :type product: ifcopenshell.entity_instance - :param name: The name of the quantity set. Quantity sets that are - standardised by buildingSMART typically have a prefix of "Qto_", - like "Qto_WallBaseQuantities". If you create your own, you must not - use that prefix. It is recommended to use your own prefix tailored - to your project, company, or local government requirement. - :type name: str - :return: The newly created IfcElementQuantity - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Let's imagine we have a new wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Note that this only creates and assigns an empty quantity set. We - # still need to add quantities into the property set. Having blank - # quantity sets are invalid. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall_type, name="Qto_WallBaseQuantities") - - # Add a side area property standardised by buildingSMART. This - # allows quantity take-off to occur, even though no geometry has - # even been modelled! - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"NetSideArea": 4.2}) - """ - self.file = file - self.settings = {"product": product, "name": name} - def execute(self): if self.settings["product"].is_a("IfcObject") or self.settings["product"].is_a("IfcContext"): for rel in self.settings["product"].IsDefinedBy or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py index 0eb711b803..c4955e1605 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_pset.py @@ -20,141 +20,144 @@ import ifcopenshell import ifcopenshell.util.pset +def edit_pset(file, pset=None, name=None, properties=None, pset_template=None, should_purge=False) -> None: + """Edits a property set and its properties + + At its simplest usage, this may be used to edit the name of a property + set. It may also be used to add, edit, or remove properties, either + arbitrarily or using a property set template. + + A list of properties are provided as a dictionary, where the keys are + property names, and values are property values. Keys that don't already + exist are interpreted as properties to be added. Keys that already exist + are interpreted as properties to be edited. A "None" value may specify a + property to be deleted. + + Properties must have a data type. There are lots of data types in IFCs, + not just simple unitless data types like integers, booleans, text, but + also distinguishing between types of text, like labels versus + descriptive text. There are also lots of unit-based data types like + areas, volumes, lengths, power, density, flow rates, pressure, etc. + + To ensure the appropriate data type is used for properties, a property + set template may be used. These can be seen as "property + specifications". A default selection is provided by buildingSMART, so + that all buildingSMART defined standard properties have exactly the same + data types and exactly the right property names without fear of invalid + data or typos. The built-in buildingSMART templates are always loaded. + However, you may also specify your own templates. If you try to add a + non-standard property that does not exist in either your own template or + in the built-in buildingSMART template, then you have the responsibility + to ensure that data types are always consistent and correct. + + :param pset: The IfcPropertySet to edit. + :type pset: ifcopenshell.entity_instance + :param name: A new name for the property set. If no name is specified, + the property set name is not changed. + :type name: str, optional + :param properties: A dictionary of properties. The keys must be a string + of the name of the property. The data type of the value will be + determined by the property set template. If no property set + template is found, the data types of the Python values will + influence the IFC data type of the property. String values will + become IfcLabel, float values will become IfcReal, booleans will + become IfcBoolean, and integers will become IfcInteger. If more + control is desired, you may explicitly specify IFC data objects + directly. Note that provided `properties` might be mutated in the process. + :type properties: dict + :param pset_template: If a property set template is provided, this will + be used to determine data types. If no user-defined template is + provided, the built-in buildingSMART templates will be loaded. + :type pset_template: ifcopenshell.entity_instance + :param should_purge: If left as False, properties set to None will be + left as None but not removed. If set to true, properties set to None + will actually be removed. + :type should_purge: bool, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a new wall type. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + + # This is a standard buildingSMART property set. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + + # In this scenario, we don't specify any pset_template because it is + # part of the built-in buildingSMART templates, and so the + # FireRating will automatically be an IfcLabel, and the thermal + # transmittance value will automatically be an + # IfcThermalTransmittanceMeasure. Neither of these properties exist + # yet, so they will be created. + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3}) + + # We can edit existing properties. In this case, "FireRating" is + # edited from "2HR" to "1HR". Combustible is new, and will be added. + # The existing "ThermalTransmittance" property will be left + # unchanged. + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"FireRating": "1HR", "Combustible": False}) + + # Setting to None will change the value but not delete the property. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None}) + + # If you actually want to delete the property, enable purging. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, + properties={"Combustible": None}, should_purge=True) + + # What if we wanted to manage our own properties? Let's create our + # own "Company Standard" property set templates. Notice how we + # prefix our property set with "Foo_", if our company name was "Foo" + # this would make sense. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar") + + # Let's imagine we want all model authors to specify two properties, + # one being a length measurement and another being a boolean. + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure") + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, + pset_template=template, name="DemoB", primary_measure_type="IfcBoolean") + + # Now we can use our property set template to add our properties, + # and the data types will always match our template. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar") + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template) + + # Here's a third scenario where we want to add arbitrary properties + # that are not standardised by anything, not even our own custom + # templates. + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset") + ifcopenshell.api.run("pset.edit_pset", model, + pset=pset, properties={ + # Basic Python data types are mapped to a sensible default + "SomeLabel": "Foo", + "SomeNumber": 12.3, + # But we can always specify exactly what we're after too + "ExplicitLength": model.createIfcLengthMeasure(42.3) + }) + + # Editing existing properties will retain their current data types + # if possible. So this will still be a length measure. + ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "pset": pset, + "name": name, + "properties": properties or {}, + "pset_template": pset_template, + "should_purge": should_purge, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, pset=None, name=None, properties=None, pset_template=None, should_purge=False): - """Edits a property set and its properties - - At its simplest usage, this may be used to edit the name of a property - set. It may also be used to add, edit, or remove properties, either - arbitrarily or using a property set template. - - A list of properties are provided as a dictionary, where the keys are - property names, and values are property values. Keys that don't already - exist are interpreted as properties to be added. Keys that already exist - are interpreted as properties to be edited. A "None" value may specify a - property to be deleted. - - Properties must have a data type. There are lots of data types in IFCs, - not just simple unitless data types like integers, booleans, text, but - also distinguishing between types of text, like labels versus - descriptive text. There are also lots of unit-based data types like - areas, volumes, lengths, power, density, flow rates, pressure, etc. - - To ensure the appropriate data type is used for properties, a property - set template may be used. These can be seen as "property - specifications". A default selection is provided by buildingSMART, so - that all buildingSMART defined standard properties have exactly the same - data types and exactly the right property names without fear of invalid - data or typos. The built-in buildingSMART templates are always loaded. - However, you may also specify your own templates. If you try to add a - non-standard property that does not exist in either your own template or - in the built-in buildingSMART template, then you have the responsibility - to ensure that data types are always consistent and correct. - - :param pset: The IfcPropertySet to edit. - :type pset: ifcopenshell.entity_instance - :param name: A new name for the property set. If no name is specified, - the property set name is not changed. - :type name: str, optional - :param properties: A dictionary of properties. The keys must be a string - of the name of the property. The data type of the value will be - determined by the property set template. If no property set - template is found, the data types of the Python values will - influence the IFC data type of the property. String values will - become IfcLabel, float values will become IfcReal, booleans will - become IfcBoolean, and integers will become IfcInteger. If more - control is desired, you may explicitly specify IFC data objects - directly. Note that provided `properties` might be mutated in the process. - :type properties: dict - :param pset_template: If a property set template is provided, this will - be used to determine data types. If no user-defined template is - provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance - :param should_purge: If left as False, properties set to None will be - left as None but not removed. If set to true, properties set to None - will actually be removed. - :type should_purge: bool, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a new wall type. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - - # This is a standard buildingSMART property set. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - - # In this scenario, we don't specify any pset_template because it is - # part of the built-in buildingSMART templates, and so the - # FireRating will automatically be an IfcLabel, and the thermal - # transmittance value will automatically be an - # IfcThermalTransmittanceMeasure. Neither of these properties exist - # yet, so they will be created. - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"FireRating": "2HR", "ThermalTransmittance": 42.3}) - - # We can edit existing properties. In this case, "FireRating" is - # edited from "2HR" to "1HR". Combustible is new, and will be added. - # The existing "ThermalTransmittance" property will be left - # unchanged. - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"FireRating": "1HR", "Combustible": False}) - - # Setting to None will change the value but not delete the property. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"Combustible": None}) - - # If you actually want to delete the property, enable purging. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, - properties={"Combustible": None}, should_purge=True) - - # What if we wanted to manage our own properties? Let's create our - # own "Company Standard" property set templates. Notice how we - # prefix our property set with "Foo_", if our company name was "Foo" - # this would make sense. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Foo_bar") - - # Let's imagine we want all model authors to specify two properties, - # one being a length measurement and another being a boolean. - prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, - pset_template=template, name="DemoA", primary_measure_type="IfcLengthMeasure") - prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, - pset_template=template, name="DemoB", primary_measure_type="IfcBoolean") - - # Now we can use our property set template to add our properties, - # and the data types will always match our template. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Foo_Bar") - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={"DemoA": 42.3, "DemoB": True}, pset_template=template) - - # Here's a third scenario where we want to add arbitrary properties - # that are not standardised by anything, not even our own custom - # templates. - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Custom_Pset") - ifcopenshell.api.run("pset.edit_pset", model, - pset=pset, properties={ - # Basic Python data types are mapped to a sensible default - "SomeLabel": "Foo", - "SomeNumber": 12.3, - # But we can always specify exactly what we're after too - "ExplicitLength": model.createIfcLengthMeasure(42.3) - }) - - # Editing existing properties will retain their current data types - # if possible. So this will still be a length measure. - ifcopenshell.api.run("pset.edit_pset", model, pset=pset, properties={"ExplicitLength": 12.3}) - """ - self.file = file - self.settings = { - "pset": pset, - "name": name, - "properties": properties or {}, - "pset_template": pset_template, - "should_purge": should_purge, - } - def execute(self): self.update_pset_name() self.load_pset_template() diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py index cd5a3bca05..ba5b7c93e0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/edit_qto.py @@ -20,97 +20,100 @@ import ifcopenshell import ifcopenshell.util.pset +def edit_qto(file, qto=None, name=None, properties=None, pset_template=None) -> None: + """Edits a quantity set and its quantities + + At its simplest usage, this may be used to edit the name of a quantity + set. It may also be used to add, edit, or remove quantities. + + See ifcopenshell.api.pset.edit_pset for documentation on how this is + intended to be used. + + One major difference is that quantities set to None are always purged. + It is not allowed to have None quantities in IFC. + + :param qto: The IfcElementQuantity to edit. + :type qto: ifcopenshell.entity_instance + :param name: A new name for the quantity set. If no name is specified, + the quantity set name is not changed. + :type name: str, optional + :param properties: A dictionary of properties. The keys must be a string + of the name of the quantity. The data type of the value will be + determined by the quantity set template. If no quantity set + template is found, the data types of the Python values will + influence the IFC data type of the quantity. String values will + become IfcLabel, float values will become IfcReal, booleans will + become IfcBoolean, and integers will become IfcInteger. If more + control is desired, you may explicitly specify IFC data objects + directly. + :type properties: dict + :param pset_template: If a quantity set template is provided, this will + be used to determine data types. If no user-defined template is + provided, the built-in buildingSMART templates will be loaded. + :type pset_template: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we have a new wall type. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # This is a standard buildingSMART property set. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities") + + # In this scenario, we don't specify any pset_template because it is + # part of the built-in buildingSMART templates, and so the Length + # will automatically be an IfcLengthMeasure, and the NetVolume will + # automatically be an IfcVolumeMeasure. Neither of these properties + # exist yet, so they will be created. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2}) + + # Setting to None will delete the quantity. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None}) + + # What if we wanted to manage our own properties? Let's create our + # own "Company Standard" property set templates. Notice how we + # prefix our property set with "Foo_", if our company name was "Foo" + # this would make sense. In this example, we say that our template + # only applies to walls and is for quantities. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, + name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall") + + # Let's imagine we want all model authors to specify a length + # measurement for the portion of a wall that is overhanging. + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure") + + # Now we can use our property set template to add our properties, + # and the data types will always match our template. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall") + ifcopenshell.api.run("pset.edit_qto", model, + qto=qto, properties={"OverhangLength": 42.3}, pset_template=template) + + # Here's a third scenario where we want to add arbitrary quantities + # that are not standardised by anything, not even our own custom + # templates. + qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto") + ifcopenshell.api.run("pset.edit_qto", model, + qto=qto, properties={ + "SomeLength": model.createIfcLengthMeasure(42.3), + "SomeArea": model.createIfcAreaMeasure(21.0) + }) + + # Editing existing quantities will retain their current data types + # if possible. So this will still be a length measure. + ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template} + return usecase.execute() + + class Usecase: - def __init__(self, file, qto=None, name=None, properties=None, pset_template=None): - """Edits a quantity set and its quantities - - At its simplest usage, this may be used to edit the name of a quantity - set. It may also be used to add, edit, or remove quantities. - - See ifcopenshell.api.pset.edit_pset for documentation on how this is - intended to be used. - - One major difference is that quantities set to None are always purged. - It is not allowed to have None quantities in IFC. - - :param qto: The IfcElementQuantity to edit. - :type qto: ifcopenshell.entity_instance - :param name: A new name for the quantity set. If no name is specified, - the quantity set name is not changed. - :type name: str, optional - :param properties: A dictionary of properties. The keys must be a string - of the name of the quantity. The data type of the value will be - determined by the quantity set template. If no quantity set - template is found, the data types of the Python values will - influence the IFC data type of the quantity. String values will - become IfcLabel, float values will become IfcReal, booleans will - become IfcBoolean, and integers will become IfcInteger. If more - control is desired, you may explicitly specify IFC data objects - directly. - :type properties: dict - :param pset_template: If a quantity set template is provided, this will - be used to determine data types. If no user-defined template is - provided, the built-in buildingSMART templates will be loaded. - :type pset_template: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we have a new wall type. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # This is a standard buildingSMART property set. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Qto_WallBaseQuantities") - - # In this scenario, we don't specify any pset_template because it is - # part of the built-in buildingSMART templates, and so the Length - # will automatically be an IfcLengthMeasure, and the NetVolume will - # automatically be an IfcVolumeMeasure. Neither of these properties - # exist yet, so they will be created. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": 12, "NetVolume": 7.2}) - - # Setting to None will delete the quantity. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"Length": None}) - - # What if we wanted to manage our own properties? Let's create our - # own "Company Standard" property set templates. Notice how we - # prefix our property set with "Foo_", if our company name was "Foo" - # this would make sense. In this example, we say that our template - # only applies to walls and is for quantities. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, - name="Foo_Wall", template_type="QTO_OCCURRENCEDRIVEN", applicable_entity="IfcWall") - - # Let's imagine we want all model authors to specify a length - # measurement for the portion of a wall that is overhanging. - prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="OverhangLength", template_type="Q_LENGTH", primary_measure_type="IfcLengthMeasure") - - # Now we can use our property set template to add our properties, - # and the data types will always match our template. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Foo_Wall") - ifcopenshell.api.run("pset.edit_qto", model, - qto=qto, properties={"OverhangLength": 42.3}, pset_template=template) - - # Here's a third scenario where we want to add arbitrary quantities - # that are not standardised by anything, not even our own custom - # templates. - qto = ifcopenshell.api.run("pset.add_qto", model, product=wall, name="Custom_Qto") - ifcopenshell.api.run("pset.edit_qto", model, - qto=qto, properties={ - "SomeLength": model.createIfcLengthMeasure(42.3), - "SomeArea": model.createIfcAreaMeasure(21.0) - }) - - # Editing existing quantities will retain their current data types - # if possible. So this will still be a length measure. - ifcopenshell.api.run("pset.edit_qto", model, qto=qto, properties={"SomeLength": 12.3}) - """ - self.file = file - self.settings = {"qto": qto, "name": name, "properties": properties or {}, "pset_template": pset_template} - def execute(self): self.qto_idx = 5 if self.settings["qto"].is_a("IfcPhysicalComplexQuantity"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py index da77accbb6..50ef427bb4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/remove_pset.py @@ -20,68 +20,65 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, product=None, pset=None): - """Removes a property set from a product +def remove_pset(file, product=None, pset=None) -> None: + """Removes a property set from a product - All properties that are part of this property set are also removed. + All properties that are part of this property set are also removed. - :param product: The IfcObject to remove the property set from. - :type product: ifcopenshell.entity_instance - :param pset: The IfcPropertySet or IfcElementQuantity to remove. - :type pset: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The IfcObject to remove the property set from. + :type product: ifcopenshell.entity_instance + :param pset: The IfcPropertySet or IfcElementQuantity to remove. + :type pset: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we have a new wall type with a property set. - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") + # Let's imagine we have a new wall type with a property set. + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + pset = ifcopenshell.api.run("pset.add_pset", model, product=wall_type, name="Pset_WallCommon") - # Remove it! - ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset) - """ - self.file = file - self.settings = {"product": product, "pset": pset} + # Remove it! + ifcopenshell.api.run("pset.remove_pset", model, product=wall_type, pset=pset) + """ + settings = {"product": product, "pset": pset} - def execute(self): - to_purge = [] - should_remove_pset = True - for inverse in self.file.get_inverse(self.settings["pset"]): - if inverse.is_a("IfcRelDefinesByProperties"): - if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1: - to_purge.append(inverse) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["product"]) - inverse.RelatedObjects = related_objects - should_remove_pset = False - if should_remove_pset: - properties = [] # Predefined psets have no properties - if self.settings["pset"].is_a("IfcPropertySet"): - properties = self.settings["pset"].HasProperties or [] - elif self.settings["pset"].is_a("IfcQuantitySet"): - properties = self.settings["pset"].Quantities or [] - elif self.settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"): - properties = self.settings["pset"].Properties or [] - for prop in properties: - if self.file.get_total_inverses(prop) != 1: - continue - if prop.is_a("IfcPropertyEnumeratedValue"): - enumeration = prop.EnumerationReference - if enumeration and self.file.get_total_inverses(enumeration) == 1: - self.file.remove(enumeration) - self.file.remove(prop) - # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory - history = getattr(self.settings["pset"], "OwnerHistory", None) - self.file.remove(self.settings["pset"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - for element in to_purge: - history = getattr(element, "OwnerHistory", None) - self.file.remove(element) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + to_purge = [] + should_remove_pset = True + for inverse in file.get_inverse(settings["pset"]): + if inverse.is_a("IfcRelDefinesByProperties"): + if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1: + to_purge.append(inverse) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["product"]) + inverse.RelatedObjects = related_objects + should_remove_pset = False + if should_remove_pset: + properties = [] # Predefined psets have no properties + if settings["pset"].is_a("IfcPropertySet"): + properties = settings["pset"].HasProperties or [] + elif settings["pset"].is_a("IfcQuantitySet"): + properties = settings["pset"].Quantities or [] + elif settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"): + properties = settings["pset"].Properties or [] + for prop in properties: + if file.get_total_inverses(prop) != 1: + continue + if prop.is_a("IfcPropertyEnumeratedValue"): + enumeration = prop.EnumerationReference + if enumeration and file.get_total_inverses(enumeration) == 1: + file.remove(enumeration) + file.remove(prop) + # IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory + history = getattr(settings["pset"], "OwnerHistory", None) + file.remove(settings["pset"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + for element in to_purge: + history = getattr(element, "OwnerHistory", None) + file.remove(element) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py index e0caddbe3c..1c5963479c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/__init__.py @@ -15,3 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_prop_template import add_prop_template +from .add_pset_template import add_pset_template +from .edit_prop_template import edit_prop_template +from .edit_pset_template import edit_pset_template +from .remove_prop_template import remove_prop_template +from .remove_pset_template import remove_pset_template diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py index 5a9dc42355..109c830c94 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_prop_template.py @@ -19,95 +19,91 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file, - pset_template=None, - name="NewProperty", - description=None, - template_type="P_SINGLEVALUE", - primary_measure_type="IfcLabel", - ): - """Adds new property templates to a property set template +def add_prop_template( + file, + pset_template=None, + name="NewProperty", + description=None, + template_type="P_SINGLEVALUE", + primary_measure_type="IfcLabel", +) -> None: + """Adds new property templates to a property set template - Assuming you first have a property set template, this allows you to add - templates for properties within that property set. A property template - lets you specify the name, description, and data type of a property. - When the template is provided to a model author, this gives them clear - instructions about the intention of the property and exactly which data - type to use. + Assuming you first have a property set template, this allows you to add + templates for properties within that property set. A property template + lets you specify the name, description, and data type of a property. + When the template is provided to a model author, this gives them clear + instructions about the intention of the property and exactly which data + type to use. - Types of properties and quantities include: + Types of properties and quantities include: - * P_SINGLEVALUE - a single value, the most common type of property. - * P_ENUMERATEDVALUE - the property value may one or more values chosen - from a preset list of values. - * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value. - * P_LISTVALUE - the property has a list of values. - * P_TABLEVALUE - the property has a table of values. - * P_REFERENCEVALUE - the property is a parametric reference to another - value. This is only for advanced users. - * Q_LENGTH - the quantity is a length. - * Q_AREA - the quantity is an area. - * Q_VOLUME - the quantity is a volume. - * Q_COUNT - the quantity is counting a item. - * Q_WEIGHT - the quantity is a weight. - * Q_TIME - the quantity is a time duration. + * P_SINGLEVALUE - a single value, the most common type of property. + * P_ENUMERATEDVALUE - the property value may one or more values chosen + from a preset list of values. + * P_BOUNDEDVALUE - the property has a minimum, maximum, and set value. + * P_LISTVALUE - the property has a list of values. + * P_TABLEVALUE - the property has a table of values. + * P_REFERENCEVALUE - the property is a parametric reference to another + value. This is only for advanced users. + * Q_LENGTH - the quantity is a length. + * Q_AREA - the quantity is an area. + * Q_VOLUME - the quantity is a volume. + * Q_COUNT - the quantity is counting a item. + * Q_WEIGHT - the quantity is a weight. + * Q_TIME - the quantity is a time duration. - :param pset_template: The property set template to add the property - template to. - :type pset_template: ifcopenshell.entity_instance - :param name: The name of the property - :type name: str,optional - :param description: A few words describing what the property stores. - :type description: str,optional - :param primary_measure_type: The data type of the property. Consult the - IFC documentation for the full list of data types. - :param primary_measure_type: str,optional - :return: The newly created IfcSimplePropertyTemplate. - :rtype: ifcopenshell.entity_instance + :param pset_template: The property set template to add the property + template to. + :type pset_template: ifcopenshell.entity_instance + :param name: The name of the property + :type name: str,optional + :param description: A few words describing what the property stores. + :type description: str,optional + :param primary_measure_type: The data type of the property. Consult the + IFC documentation for the full list of data types. + :param primary_measure_type: str,optional + :return: The newly created IfcSimplePropertyTemplate. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple template that may be applied to all types - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a simple template that may be applied to all types + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's one example property - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="HighVoltage", description="Whether there is a risk of high voltage.", - primary_measure_type="IfcBoolean") + # Here's one example property + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") - # Here's another - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="ChemicalType", description="The class of chemical spillage.", - primary_measure_type="IfcLabel") - """ - self.file = file - self.settings = { - "pset_template": pset_template, - "name": name, - "description": description, - "template_type": template_type, - "primary_measure_type": primary_measure_type, + # Here's another + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="ChemicalType", description="The class of chemical spillage.", + primary_measure_type="IfcLabel") + """ + settings = { + "pset_template": pset_template, + "name": name, + "description": description, + "template_type": template_type, + "primary_measure_type": primary_measure_type, + } + + prop_template = file.create_entity( + "IfcSimplePropertyTemplate", + **{ + "GlobalId": ifcopenshell.guid.new(), + "Name": settings["name"], + "Description": settings["description"], + "PrimaryMeasureType": settings["primary_measure_type"], + "TemplateType": settings["template_type"], + "AccessState": "READWRITE", + "Enumerators": None, } - - def execute(self): - prop_template = self.file.create_entity( - "IfcSimplePropertyTemplate", - **{ - "GlobalId": ifcopenshell.guid.new(), - "Name": self.settings["name"], - "Description": self.settings["description"], - "PrimaryMeasureType": self.settings["primary_measure_type"], - "TemplateType": self.settings["template_type"], - "AccessState": "READWRITE", - "Enumerators": None, - } - ) - has_property_templates = list(self.settings["pset_template"].HasPropertyTemplates or []) - has_property_templates.append(prop_template) - self.settings["pset_template"].HasPropertyTemplates = has_property_templates - return prop_template + ) + has_property_templates = list(settings["pset_template"].HasPropertyTemplates or []) + has_property_templates.append(prop_template) + settings["pset_template"].HasPropertyTemplates = has_property_templates + return prop_template diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py index 242f7b7510..9a22b6a97a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/add_pset_template.py @@ -19,95 +19,91 @@ import ifcopenshell -class Usecase: - def __init__( - self, - file, - name="New_Pset", - template_type="PSET_TYPEDRIVENOVERRIDE", - applicable_entity="IfcObject,IfcTypeObject", - ): - """Adds a new property set template +def add_pset_template( + file, + name="New_Pset", + template_type="PSET_TYPEDRIVENOVERRIDE", + applicable_entity="IfcObject,IfcTypeObject", +) -> None: + """Adds a new property set template - This creates a new template for property sets. A template defines what - the name of the property set should be, what properties it can have, - what entities (e.g. wall) the property set can be assigned to, whether - it should be assigned at a type or occurrence level, the data types of - the properties, and descriptions of the properties. This template can - then be used as a project, company, or local government standard. + This creates a new template for property sets. A template defines what + the name of the property set should be, what properties it can have, + what entities (e.g. wall) the property set can be assigned to, whether + it should be assigned at a type or occurrence level, the data types of + the properties, and descriptions of the properties. This template can + then be used as a project, company, or local government standard. - buildingSMART itself ships a catalogue of property sets using these - templates, ensuring that internationally common properties (e.g. fire - rating of a wall) are all implemented exactly the same way across all - vendors and projects. Naturally, not everything can be standardised - internationally, so this allows you to create your own templates. + buildingSMART itself ships a catalogue of property sets using these + templates, ensuring that internationally common properties (e.g. fire + rating of a wall) are all implemented exactly the same way across all + vendors and projects. Naturally, not everything can be standardised + internationally, so this allows you to create your own templates. - You may either create a property template to store properties, or a - quantity template to store quantities. For convenience, we will always - call them "property templates" as they are conceptually very similar. + You may either create a property template to store properties, or a + quantity template to store quantities. For convenience, we will always + call them "property templates" as they are conceptually very similar. - This function only creates a template for the property set, not the - properties themselves within the property set. At this level, you are - allowed to define the name of the property set, whether it is type or - occurrence based, and which entities it applies to. + This function only creates a template for the property set, not the + properties themselves within the property set. At this level, you are + allowed to define the name of the property set, whether it is type or + occurrence based, and which entities it applies to. - See the documentation for IfcPropertySetTemplate for instructions on - the types of template type and list of applicable entities. + See the documentation for IfcPropertySetTemplate for instructions on + the types of template type and list of applicable entities. - The types of property set templates are: + The types of property set templates are: - * PSET_TYPEDRIVENONLY - assigned only to types - * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both, - the occurrence overrides the type. - * PSET_OCCURRENCEDRIVEN - assigned to occurrences only. - * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is - only recommended for advanced users. - * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities. - * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for - quantities. If both, the occurrence overrides the type. - * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for - quantities. + * PSET_TYPEDRIVENONLY - assigned only to types + * PSET_TYPEDRIVENOVERRIDE - assigned to types or occurrences. If both, + the occurrence overrides the type. + * PSET_OCCURRENCEDRIVEN - assigned to occurrences only. + * PSET_PERFORMANCEDRIVEN - assigned as a timeseries data range. This is + only recommended for advanced users. + * QTO_TYPEDRIVENONLY - assigned only to types, but for quantities. + * QTO_TYPEDRIVENOVERRIDE - assigned to types or occurrences, but for + quantities. If both, the occurrence overrides the type. + * QTO_OCCURRENCEDRIVEN - assigned to occurrences only, but for + quantities. - By default, this creates a template that can be applied to types, but - overridden by occurrences, and is applicable to everything. + By default, this creates a template that can be applied to types, but + overridden by occurrences, and is applicable to everything. - :param name: The name of the property set - :type name: str,optional - :param template_type: Choose from one of PSET_TYPEDRIVENONLY, - PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN, - PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE, - QTO_OCCURRENCEDRIVEN, NOTDEFINED - :type template_type: str,optional - :param applicable_entity: The entity that this template is allowed to be - applied to. For example, IfcWall means that the property set may be - assigned to walls only. IfcTypeObject, the default, means that the - property set may be assigned to any type. - :type applicable_entity: str,optional - :return: The newly created IfcPropertySetTemplate - :rtype: ifcopenshell.entity_instance + :param name: The name of the property set + :type name: str,optional + :param template_type: Choose from one of PSET_TYPEDRIVENONLY, + PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN, + PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE, + QTO_OCCURRENCEDRIVEN, NOTDEFINED + :type template_type: str,optional + :param applicable_entity: The entity that this template is allowed to be + applied to. For example, IfcWall means that the property set may be + assigned to walls only. IfcTypeObject, the default, means that the + property set may be assigned to any type. + :type applicable_entity: str,optional + :return: The newly created IfcPropertySetTemplate + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple template that may be applied to all types - ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a simple template that may be applied to all types + ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Note that we aren't finished yet. Our property set template - # doesn't have any properties in it. Let's add a minimum of one - # property. - ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, - name="HighVoltage", description="Whether there is a risk of high voltage.", - primary_measure_type="IfcBoolean") - """ - self.file = file - self.settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity} + # Note that we aren't finished yet. Our property set template + # doesn't have any properties in it. Let's add a minimum of one + # property. + ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template, + name="HighVoltage", description="Whether there is a risk of high voltage.", + primary_measure_type="IfcBoolean") + """ + settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity} - def execute(self): - return self.file.create_entity( - "IfcPropertySetTemplate", - GlobalId=ifcopenshell.guid.new(), - Name=self.settings["name"], - TemplateType=self.settings["template_type"], - ApplicableEntity=self.settings["applicable_entity"], - ) + return file.create_entity( + "IfcPropertySetTemplate", + GlobalId=ifcopenshell.guid.new(), + Name=settings["name"], + TemplateType=settings["template_type"], + ApplicableEntity=settings["applicable_entity"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py index 6b2633992f..dcc6c9b3ae 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_prop_template.py @@ -17,36 +17,33 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, prop_template=None, attributes=None): - """Edits the attributes of an IfcSimplePropertyTemplate +def edit_prop_template(file, prop_template=None, attributes=None) -> None: + """Edits the attributes of an IfcSimplePropertyTemplate - For more information about the attributes and data types of an - IfcSimplePropertyTemplate, consult the IFC documentation. + For more information about the attributes and data types of an + IfcSimplePropertyTemplate, consult the IFC documentation. - :param prop_template: The IfcSimplePropertyTemplate entity you want to edit - :type prop_template: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param prop_template: The IfcSimplePropertyTemplate entity you want to edit + :type prop_template: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's a property with just default values. - prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + # Here's a property with just default values. + prop = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - # Let's edit it to give the actual values we need. - ifcopenshell.api.run("pset_template.edit_prop_template", model, - prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) - """ - self.file = file - self.settings = {"prop_template": prop_template, "attributes": attributes or {}} + # Let's edit it to give the actual values we need. + ifcopenshell.api.run("pset_template.edit_prop_template", model, + prop_template=prop, attributes={"Name": "DemoA", "PrimaryMeasureType": "IfcLengthMeasure"}) + """ + settings = {"prop_template": prop_template, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["prop_template"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["prop_template"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py index 303618f509..8a0581efdc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/edit_pset_template.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, pset_template=None, attributes=None): - """Edits the attributes of an IfcPropertySetTemplate +def edit_pset_template(file, pset_template=None, attributes=None) -> None: + """Edits the attributes of an IfcPropertySetTemplate - For more information about the attributes and data types of an - IfcPropertySetTemplate, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPropertySetTemplate, consult the IFC documentation. - :param pset_template: The IfcPropertySetTemplate entity you want to edit - :type pset_template: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param pset_template: The IfcPropertySetTemplate entity you want to edit + :type pset_template: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Whoops! We named it with a buildingSMART reserved "Pset_" prefix! - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors") + # Whoops! We named it with a buildingSMART reserved "Pset_" prefix! + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="Pset_RiskFactors") - # Let's fix it to prefix with our company code instead. - ifcopenshell.api.run("pset_template.edit_pset_template", model, - pset_template=template, attributes={"Name": "ABC_RiskFactors"}) - """ - self.file = file - self.settings = {"pset_template": pset_template, "attributes": attributes or {}} + # Let's fix it to prefix with our company code instead. + ifcopenshell.api.run("pset_template.edit_pset_template", model, + pset_template=template, attributes={"Name": "ABC_RiskFactors"}) + """ + settings = {"pset_template": pset_template, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["pset_template"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["pset_template"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py index 6479e6ffc2..7a247ac383 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_prop_template.py @@ -19,41 +19,38 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, prop_template=None): - """Removes a property template +def remove_prop_template(file, prop_template=None) -> None: + """Removes a property template - Note that a property set template should always have at least one - property template to be valid, so take care when removing property - templates. + Note that a property set template should always have at least one + property template to be valid, so take care when removing property + templates. - :param prop_template: The IfcSimplePropertyTemplate to remove. - :type prop_template: ifcopenshell.entity_instance - :return: None - :rtype: None + :param prop_template: The IfcSimplePropertyTemplate to remove. + :type prop_template: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Here's two propertes with just default values. - prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + # Here's two propertes with just default values. + prop1 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) + prop2 = ifcopenshell.api.run("pset_template.add_prop_template", model, pset_template=template) - # Let's remove the second one. - ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2) - """ - self.file = file - self.settings = {"prop_template": prop_template} + # Let's remove the second one. + ifcopenshell.api.run("pset_template.remove_prop_template", model, prop_template=prop2) + """ + settings = {"prop_template": prop_template} - def execute(self): - for inverse in self.file.get_inverse(self.settings["prop_template"]): - if len(inverse.HasPropertyTemplates) == 1: - inverse.HasPropertyTemplates = [] - else: - has_property_templates = list(inverse.HasPropertyTemplates) - has_property_templates.remove(self.settings["prop_template"]) - inverse.HasPropertyTemplates = has_property_templates - ifcopenshell.util.element.remove_deep(self.file, self.settings["prop_template"]) + for inverse in file.get_inverse(settings["prop_template"]): + if len(inverse.HasPropertyTemplates) == 1: + inverse.HasPropertyTemplates = [] + else: + has_property_templates = list(inverse.HasPropertyTemplates) + has_property_templates.remove(settings["prop_template"]) + inverse.HasPropertyTemplates = has_property_templates + ifcopenshell.util.element.remove_deep(file, settings["prop_template"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py index 4cb2c2695a..c567a55033 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset_template/remove_pset_template.py @@ -19,30 +19,27 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, pset_template=None): - """Removes a property set template +def remove_pset_template(file, pset_template=None) -> None: + """Removes a property set template - All property templates within the property set template are also removed - along with it. + All property templates within the property set template are also removed + along with it. - :param pset_template: The IfcPropertySetTemplate to remove. - :type pset_template: ifcopenshell.entity_instance - :return: None - :rtype: None + :param pset_template: The IfcPropertySetTemplate to remove. + :type pset_template: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a template. - template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") + # Create a template. + template = ifcopenshell.api.run("pset_template.add_pset_template", model, name="ABC_RiskFactors") - # Let's remove the template. - ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template) - """ - self.file = file - self.settings = {"pset_template": pset_template} + # Let's remove the template. + ifcopenshell.api.run("pset_template.remove_pset_template", model, pset_template=template) + """ + settings = {"pset_template": pset_template} - def execute(self): - ifcopenshell.util.element.remove_deep(self.file, self.settings["pset_template"]) + ifcopenshell.util.element.remove_deep(file, settings["pset_template"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py index e0caddbe3c..8fcff6a7fc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_resource import add_resource +from .add_resource_quantity import add_resource_quantity +from .add_resource_time import add_resource_time +from .assign_resource import assign_resource +from .calculate_resource_usage import calculate_resource_usage +from .calculate_resource_work import calculate_resource_work +from .edit_resource import edit_resource +from .edit_resource_quantity import edit_resource_quantity +from .edit_resource_time import edit_resource_time +from .remove_resource import remove_resource +from .remove_resource_quantity import remove_resource_quantity +from .unassign_resource import unassign_resource diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py index 03a67ab648..e2a1dab308 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py @@ -19,93 +19,89 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, +def add_resource( + file, + parent_resource=None, + ifc_class="IfcCrewResource", + name=None, + predefined_type="NOTDEFINED", +) -> None: + """Add a new construction resource + + Construction resources may be managed and connected to cost schedules + and construction schedules. This allows calculations to be done on + resource utilisation, cost optimisation (e.g. labour rates), and + optioneering on build strategies. + + There are typically two types of resources. Crew resources are resources + where you manage your own crew and you have full control over the + equipment, labour, products, and materials used by your crew. + Alternatively, there are subcontractor resources, where you simply + delegate all the details to a subcontractor and it is not decomposed + into further levels of detail. + + This means when adding resources, you'd first either add a crew or + subcontract resource. If it is a crew resource, you'd then add child + resources to that crew, such as equipment (cranes, excavators, hoists, + etc), material (wood, concrete, etc), and labour (rigging crews, + formworkers, etc). + + :param parent_resource: If this is a child resource (typically to a crew + resource), then nominate the parent IfcConstructionResource here. + :type parent_resource: ifcopenshell.entity_instance + :param ifc_class: The class of resource chosen from + IfcConstructionEquipmentResource, IfcConstructionMaterialResource, + IfcConstructionProductResource, IfcCrewResource, IfcLaborResource, + or IfcSubContractResource. + :type ifc_class: str,optional + :param name: The name of the resource + :type name: str,optional + :param predefined_type: Consult the IFC documentation for the valid + predefined types for each type of resource class. + :type predefined_type: str,optional + :return: The newly created resource depending on the nominated IFC + class. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource") + """ + settings = { + "parent_resource": parent_resource, + "ifc_class": ifc_class, + "name": name, + "predefined_type": predefined_type, + } + + resource = ifcopenshell.api.run( + "root.create_entity", file, - parent_resource=None, - ifc_class="IfcCrewResource", - name=None, - predefined_type="NOTDEFINED", - ): - """Add a new construction resource - - Construction resources may be managed and connected to cost schedules - and construction schedules. This allows calculations to be done on - resource utilisation, cost optimisation (e.g. labour rates), and - optioneering on build strategies. - - There are typically two types of resources. Crew resources are resources - where you manage your own crew and you have full control over the - equipment, labour, products, and materials used by your crew. - Alternatively, there are subcontractor resources, where you simply - delegate all the details to a subcontractor and it is not decomposed - into further levels of detail. - - This means when adding resources, you'd first either add a crew or - subcontract resource. If it is a crew resource, you'd then add child - resources to that crew, such as equipment (cranes, excavators, hoists, - etc), material (wood, concrete, etc), and labour (rigging crews, - formworkers, etc). - - :param parent_resource: If this is a child resource (typically to a crew - resource), then nominate the parent IfcConstructionResource here. - :type parent_resource: ifcopenshell.entity_instance - :param ifc_class: The class of resource chosen from - IfcConstructionEquipmentResource, IfcConstructionMaterialResource, - IfcConstructionProductResource, IfcCrewResource, IfcLaborResource, - or IfcSubContractResource. - :type ifc_class: str,optional - :param name: The name of the resource - :type name: str,optional - :param predefined_type: Consult the IFC documentation for the valid - predefined types for each type of resource class. - :type predefined_type: str,optional - :return: The newly created resource depending on the nominated IFC - class. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - ifcopenshell.api.run("resource.add_resource", model, parent_resource=crew, ifc_class="IfcLaborResource") - """ - self.file = file - self.settings = { - "parent_resource": parent_resource, - "ifc_class": ifc_class, - "name": name, - "predefined_type": predefined_type, - } - - def execute(self): - resource = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class=self.settings["ifc_class"], - predefined_type=self.settings["predefined_type"], - name=self.settings["name"] or "Unnamed", + ifc_class=settings["ifc_class"], + predefined_type=settings["predefined_type"], + name=settings["name"] or "Unnamed", + ) + # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ? + # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550 + if settings["parent_resource"]: + ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[resource], + relating_object=settings["parent_resource"], ) - # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ? - # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550 - if self.settings["parent_resource"]: - ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[resource], - relating_object=self.settings["parent_resource"], - ) - else: - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[resource], - relating_context=context, - ) - return resource + else: + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[resource], + relating_context=context, + ) + return resource diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py index 4e5ef0c0a0..6600a06ae2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_quantity.py @@ -19,58 +19,55 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None, ifc_class="IfcQuantityCount"): - """Adds a quantity to a resource +def add_resource_quantity(file, resource=None, ifc_class="IfcQuantityCount") -> None: + """Adds a quantity to a resource - The quantity of a resource represents the "unit quantity" of that - resource. For example, labour might be hired on a daily basis (8 hours). - There are different types of quantities (e.g. volume, count, or time). - Which quantity is used depends on the type of resource. Material - resources may be quantified in terms of length, area, volume, or weight. - Equipment and labour resources are quantified in terms of time. Products - resources are quantified in terms of counts. + The quantity of a resource represents the "unit quantity" of that + resource. For example, labour might be hired on a daily basis (8 hours). + There are different types of quantities (e.g. volume, count, or time). + Which quantity is used depends on the type of resource. Material + resources may be quantified in terms of length, area, volume, or weight. + Equipment and labour resources are quantified in terms of time. Products + resources are quantified in terms of counts. - This base quantity is then used in other calculations. + This base quantity is then used in other calculations. - :param resource: The IfcConstructionResource to add a quantity to. - :type resource: ifcopenshell.entity_instance - :param ifc_class: The type of quantity to add, chosen from - IfcQuantityArea (for material), IfcQuantityCount (for products), - IfcQuantityLength (for material), IfcQuantityTime (for equipment or - labour), IfcQuantityVolume (for material), and IfcQuantityWeight - (for material). - :type ifc_class: str,optional - :return: The newly created quantity depending on the IFC class - :rtype: ifcopenshell.entity_instance + :param resource: The IfcConstructionResource to add a quantity to. + :type resource: ifcopenshell.entity_instance + :param ifc_class: The type of quantity to add, chosen from + IfcQuantityArea (for material), IfcQuantityCount (for products), + IfcQuantityLength (for material), IfcQuantityTime (for equipment or + labour), IfcQuantityVolume (for material), and IfcQuantityWeight + (for material). + :type ifc_class: str,optional + :return: The newly created quantity depending on the IFC class + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) - """ - self.file = file - self.settings = {"resource": resource, "ifc_class": ifc_class} + # Store the time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) + """ + settings = {"resource": resource, "ifc_class": ifc_class} - def execute(self): - quantity = self.file.create_entity(self.settings["ifc_class"], Name="Unnamed") - quantity[3] = 0.0 - old_quantity = self.settings["resource"].BaseQuantity - self.settings["resource"].BaseQuantity = quantity - if old_quantity: - ifcopenshell.util.element.remove_deep(self.file, old_quantity) - return quantity + quantity = file.create_entity(settings["ifc_class"], Name="Unnamed") + quantity[3] = 0.0 + old_quantity = settings["resource"].BaseQuantity + settings["resource"].BaseQuantity = quantity + if old_quantity: + ifcopenshell.util.element.remove_deep(file, old_quantity) + return quantity diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py index 8627e319a7..3066441330 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py @@ -19,50 +19,47 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, resource=None): - """Adds the time that a resource is used for +def add_resource_time(file, resource=None) -> None: + """Adds the time that a resource is used for - For labour and equipment resources, the total duration that the resource - is used for may be stored. This may either be input manually or - calculated parametrically. This is known as the resource time, and may - be used to calculate other parameters like resource utilisation. + For labour and equipment resources, the total duration that the resource + is used for may be stored. This may either be input manually or + calculated parametrically. This is known as the resource time, and may + be used to calculate other parameters like resource utilisation. - :param resource: The IfcConstructionResource to record time for. - :type resource: ifcopenshell.entity_instance - :return: The newly created IfcResourceTime - :rtype: ifcopenshell.entity_instance + :param resource: The IfcConstructionResource to record time for. + :type resource: ifcopenshell.entity_instance + :return: The newly created IfcResourceTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) - # Let's imagine we've used the resource for 2 days. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "PT16H"}) - """ - self.file = file - self.settings = { - "resource": resource, - } + # Let's imagine we've used the resource for 2 days. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "PT16H"}) + """ + settings = { + "resource": resource, + } - def execute(self): - resource_time = self.file.create_entity("IfcResourceTime") - self.settings["resource"].Usage = resource_time - return resource_time + resource_time = file.create_entity("IfcResourceTime") + settings["resource"].Usage = resource_time + return resource_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py index f71ec00260..44d856ef0f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/assign_resource.py @@ -20,101 +20,93 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_resource=None, related_object=None): - """Assigns a resource to an object +def assign_resource(file, relating_resource=None, related_object=None) -> None: + """Assigns a resource to an object - Two types of objects are typically assigned to resources: products and - actors. + Two types of objects are typically assigned to resources: products and + actors. - If a product is assigned to a resource, that means that the product - represents the resource on site. This may be represented via material - handling zones on a construction site, or equipment like cranes and - their physical locations. + If a product is assigned to a resource, that means that the product + represents the resource on site. This may be represented via material + handling zones on a construction site, or equipment like cranes and + their physical locations. - If an actor is assigned to a resource, that means that the actor (person - or organisation) is the actor consuming the resource (e.g. if the - resource is material or equipment) or the actor performing the work - (e.g. if the resource is a labour resource). + If an actor is assigned to a resource, that means that the actor (person + or organisation) is the actor consuming the resource (e.g. if the + resource is material or equipment) or the actor performing the work + (e.g. if the resource is a labour resource). - :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance - :param related_object: The IfcProduct or IfcActor to assign to the - object. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance + :param relating_resource: The IfcResource to assign the object to. + :type relating_resource: ifcopenshell.entity_instance + :param related_object: The IfcProduct or IfcActor to assign to the + object. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToResource + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some a tower crane to our crew. - crane = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") + # Add some a tower crane to our crew. + crane = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") - # Our tower crane will be placed via this physical product. - product = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") + # Our tower crane will be placed via this physical product. + product = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") - # Let's place our crane at some X, Y coordinates. - matrix = numpy.eye(4) - matrix[0][3], matrix[1][3] = 3.0, 4.0 - ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix) + # Let's place our crane at some X, Y coordinates. + matrix = numpy.eye(4) + matrix[0][3], matrix[1][3] = 3.0, 4.0 + ifcopenshell.api.run("geometry.edit_object_placement", model, product=crane, matrix=matrix) - # Let's assign our crane to the resource. The crane now represents - # the resource. - ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product) + # Let's assign our crane to the resource. The crane now represents + # the resource. + ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=product) - # Setup an organisation actor who will operate the crane - organisation = ifcopenshell.api.run("owner.add_organisation", model, - identification="UCO", name="Unionised Crane Operators Pty Ltd") - role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW") - actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) + # Setup an organisation actor who will operate the crane + organisation = ifcopenshell.api.run("owner.add_organisation", model, + identification="UCO", name="Unionised Crane Operators Pty Ltd") + role = ifcopenshell.api.run("owner.add_role", model, assigned_object=organisation, role="CREW") + actor = ifcopenshell.api.run("owner.add_actor", model, actor=organisation) - # This means that UCO is now our crane operator. - ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor) - """ - self.file = file - self.settings = { - "relating_resource": relating_resource, - "related_object": related_object, - } + # This means that UCO is now our crane operator. + ifcopenshell.api.run("resource.assign_resource", model, relating_resource=crane, related_object=actor) + """ + settings = { + "relating_resource": relating_resource, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfclRelAssignsToResource") - and assignment.RelatingResource - == self.settings["relating_resource"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if ( + assignment.is_a("IfclRelAssignsToResource") + and assignment.RelatingResource == settings["relating_resource"] + ): + return - resource_of = None - if self.settings["relating_resource"].ResourceOf: - resource_of = self.settings["relating_resource"].ResourceOf[0] + resource_of = None + if settings["relating_resource"].ResourceOf: + resource_of = settings["relating_resource"].ResourceOf[0] - if resource_of: - related_objects = list(resource_of.RelatedObjects) - related_objects.append(self.settings["related_object"]) - resource_of.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": resource_of} - ) - else: - resource_of = self.file.create_entity( - "IfcRelAssignsToResource", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingResource": self.settings["relating_resource"], - } - ) - return resource_of + if resource_of: + related_objects = list(resource_of.RelatedObjects) + related_objects.append(settings["related_object"]) + resource_of.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": resource_of}) + else: + resource_of = file.create_entity( + "IfcRelAssignsToResource", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingResource": settings["relating_resource"], + } + ) + return resource_of diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py index 08602c9f0f..fc63b2d9d4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_usage.py @@ -23,42 +23,29 @@ import ifcopenshell.util.element import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, resource=None): - """Calculates the number of resources required to perform scheduled work on a task. - """ - self.file = file - self.settings = {"resource": resource} +def calculate_resource_usage(file, resource=None) -> None: + """Calculates the number of resources required to perform scheduled work on a task.""" + settings = {"resource": resource} - def execute(self): - if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleUsage"): - return - if ( - not self.settings["resource"].Usage - or not self.settings["resource"].Usage.ScheduleWork - ): - return + if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleUsage"): + return + if not settings["resource"].Usage or not settings["resource"].Usage.ScheduleWork: + return - task = ifcopenshell.util.resource.get_task_assignments( - self.settings["resource"] - ) - if not task or not task.TaskTime: - return + task = ifcopenshell.util.resource.get_task_assignments(settings["resource"]) + if not task or not task.TaskTime: + return - if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME": - hours_per_day = 8 - else: - hours_per_day = 24 + if not task.TaskTime.DurationType or task.TaskTime.DurationType == "WORKTIME": + hours_per_day = 8 + else: + hours_per_day = 24 - task_duration = ifcopenshell.util.date.ifc2datetime( - task.TaskTime.ScheduleDuration - ) - seconds = task_duration.days * hours_per_day * 60 * 60 - seconds += task_duration.seconds + task_duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration) + seconds = task_duration.days * hours_per_day * 60 * 60 + seconds += task_duration.seconds - person_hours = ifcopenshell.util.date.ifc2datetime( - self.settings["resource"].Usage.ScheduleWork - ) + person_hours = ifcopenshell.util.date.ifc2datetime(settings["resource"].Usage.ScheduleWork) - required_resources = person_hours.total_seconds() / seconds - self.settings["resource"].Usage.ScheduleUsage = float(required_resources) + required_resources = person_hours.total_seconds() / seconds + settings["resource"].Usage.ScheduleUsage = float(required_resources) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py index 746f0d88c0..dd5621d386 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/calculate_resource_work.py @@ -23,52 +23,49 @@ import ifcopenshell.util.element import ifcopenshell.util.resource -class Usecase: - def __init__(self, file, resource=None): - """Calculates the work that a resource is used for +def calculate_resource_work(file, resource=None) -> None: + """Calculates the work that a resource is used for - This is an unofficial parametric calculation that may be done on a - resource based on careful analysis of the relationships between the - costing, scheduling, and resource domains in IFC. + This is an unofficial parametric calculation that may be done on a + resource based on careful analysis of the relationships between the + costing, scheduling, and resource domains in IFC. - A resource may store a productivity rate in a property set called - EPset_Productivity. This stores three properties: + A resource may store a productivity rate in a property set called + EPset_Productivity. This stores three properties: - * BaseQuantityConsumed - a duration that the resource is consumed for. - * BaseQuantityProducedName - what quantity the resource can produce, - such as area or volume. - * BaseQuantityProducedValue - what value of that quantity the resource - can produce during that duration. + * BaseQuantityConsumed - a duration that the resource is consumed for. + * BaseQuantityProducedName - what quantity the resource can produce, + such as area or volume. + * BaseQuantityProducedValue - what value of that quantity the resource + can produce during that duration. - For example, a labour or equipment resource might produce 100m3 of - NetVolume every day (i.e. 8 hours are consumed). + For example, a labour or equipment resource might produce 100m3 of + NetVolume every day (i.e. 8 hours are consumed). - Then, if a resource is assigned to a construction task, and that - construction task is assigned to concrete slabs totalling 200m3, we can - calculate that the resource consumes 16 hours of work. + Then, if a resource is assigned to a construction task, and that + construction task is assigned to concrete slabs totalling 200m3, we can + calculate that the resource consumes 16 hours of work. - This calculated work is stored against the resource as scheduled work - under the resource time data. + This calculated work is stored against the resource as scheduled work + under the resource time data. - :param resource: The IfcConstructionResource that you want to calculate - the work performed. - :type resource: ifcopenshell.entity_instance - :return None: - :rtype: None: - """ - self.file = file - self.settings = {"resource": resource} + :param resource: The IfcConstructionResource that you want to calculate + the work performed. + :type resource: ifcopenshell.entity_instance + :return None: + :rtype: None: + """ + settings = {"resource": resource} - def execute(self): - if ifcopenshell.util.constraint.is_attribute_locked(self.settings["resource"], "Usage.ScheduleWork"): - return - amount_worked = ifcopenshell.util.resource.get_resource_required_work(self.settings["resource"]) - if not amount_worked: - return - if not self.settings["resource"].Usage: - ifcopenshell.api.run( - "resource.add_resource_time", - self.file, - resource=self.settings["resource"], - ) - self.settings["resource"].Usage.ScheduleWork = amount_worked + if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleWork"): + return + amount_worked = ifcopenshell.util.resource.get_resource_required_work(settings["resource"]) + if not amount_worked: + return + if not settings["resource"].Usage: + ifcopenshell.api.run( + "resource.add_resource_time", + file, + resource=settings["resource"], + ) + settings["resource"].Usage.ScheduleWork = amount_worked diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py index c28f4c0661..2ab8ac669a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, resource=None, attributes=None): - """Edits the attributes of an IfcResource +def edit_resource(file, resource=None, attributes=None) -> None: + """Edits the attributes of an IfcResource - For more information about the attributes and data types of an - IfcResource, consult the IFC documentation. + For more information about the attributes and data types of an + IfcResource, consult the IFC documentation. - :param resource: The IfcResource entity you want to edit - :type resource: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param resource: The IfcResource entity you want to edit + :type resource: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Change the name of the resource to "Zone A Crew" - ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"resource": resource, "attributes": attributes or {}} + # Change the name of the resource to "Zone A Crew" + ifcopenshell.api.run("resource.edit_resource", model, resource=resource, attributes={"Name": "Foo"}) + """ + settings = {"resource": resource, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["resource"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["resource"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py index 0785caa02e..b4d016c7ad 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_quantity.py @@ -17,45 +17,42 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, physical_quantity=None, attributes=None): - """Edits the attributes of an IFC quantity +def edit_resource_quantity(file, physical_quantity=None, attributes=None) -> None: + """Edits the attributes of an IFC quantity - For more information about the attributes and data types of an - IfC quantity, consult the IFC documentation. + For more information about the attributes and data types of an + IfC quantity, consult the IFC documentation. - :param physical_quantity: The IfC quantity entity you want to edit - :type physical_quantity: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param physical_quantity: The IfC quantity entity you want to edit + :type physical_quantity: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Store the time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=time, attributes={"TimeValue": 8.0}) - """ - self.file = file - self.settings = { - "physical_quantity": physical_quantity, - "attributes": attributes or {}, - } + # Store the time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=time, attributes={"TimeValue": 8.0}) + """ + settings = { + "physical_quantity": physical_quantity, + "attributes": attributes or {}, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["physical_quantity"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["physical_quantity"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py index c9db827a89..9ec41a60c5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py @@ -20,47 +20,50 @@ import datetime import ifcopenshell +def edit_resource_time(file, resource_time=None, attributes=None) -> None: + """Edits the attributes of an IfcResourceTime + + For more information about the attributes and data types of an + IfcResourceTime, consult the IFC documentation. + + :param resource_time: The IfcResourceTime entity you want to edit + :type resource_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") + + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") + + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=time, attributes={"TimeValue": 8.0}) + + # Let's imagine we've used the resource for 2 days. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "P16H"}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"resource_time": resource_time, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, resource_time=None, attributes=None): - """Edits the attributes of an IfcResourceTime - - For more information about the attributes and data types of an - IfcResourceTime, consult the IFC documentation. - - :param resource_time: The IfcResourceTime entity you want to edit - :type resource_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") - - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") - - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=time, attributes={"TimeValue": 8.0}) - - # Let's imagine we've used the resource for 2 days. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "P16H"}) - """ - self.file = file - self.settings = {"resource_time": resource_time, "attributes": attributes or {}} - def execute(self): self.resource = self.get_resource() @@ -70,43 +73,25 @@ class Usecase: and "ScheduleFinish" in self.settings["attributes"].keys() ): del self.settings["attributes"]["ScheduleFinish"] - if ( - self.settings["attributes"].get("ActualWork", None) - and "ActualFinish" in self.settings["attributes"].keys() - ): + if self.settings["attributes"].get("ActualWork", None) and "ActualFinish" in self.settings["attributes"].keys(): del self.settings["attributes"]["ActualFinish"] for name, value in self.settings["attributes"].items(): - metrics = ifcopenshell.util.constraint.get_metric_constraints( - self.resource, "Usage." + name - ) + metrics = ifcopenshell.util.constraint.get_metric_constraints(self.resource, "Usage." + name) if metrics and ifcopenshell.util.constraint.is_hard_constraint(metrics[0]): continue if value: if "Start" in name or "Finish" in name or name == "StatusTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif ( - name == "ScheduleWork" - or name == "ActualWork" - or name == "RemainingTime" - ): + elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") setattr(self.settings["resource_time"], name, value) - if ( - name == "ScheduleUsage" - and ifcopenshell.util.constraint.get_metric_constraints( - self.resource, "Usage.ScheduleWork" - ) + if name == "ScheduleUsage" and ifcopenshell.util.constraint.get_metric_constraints( + self.resource, "Usage.ScheduleWork" ): task = ifcopenshell.util.resource.get_task_assignments(self.resource) if task: - ifcopenshell.api.run( - "sequence.calculate_task_duration", self.file, task=task - ) + ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=task) def get_resource(self): - return [ - e - for e in self.file.get_inverse(self.settings["resource_time"]) - if e.is_a("IfcResource") - ][0] + return [e for e in self.file.get_inverse(self.settings["resource_time"]) if e.is_a("IfcResource")][0] diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py index db8153a3e0..cfbdd25fd9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource.py @@ -21,71 +21,68 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None): - """Removes a resource and all relationships +def remove_resource(file, resource=None) -> None: + """Removes a resource and all relationships - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Fire our crew - ifcopenshell.api.run("resource.remove_resource", model, resource=crew) - """ - self.file = file - self.settings = {"resource": resource} + # Fire our crew + ifcopenshell.api.run("resource.remove_resource", model, resource=crew) + """ + settings = {"resource": resource} - def execute(self): - # TODO: review deep purge - for inverse in self.file.get_inverse(self.settings["resource"]): - if inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["resource"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run( - "resource.remove_resource", - self.file, - resource=related_object, - ) - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToControl"): - if len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["resource"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToResource"): - if inverse.RelatingResource == self.settings["resource"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run( - "resource.unassign_resource", - self.file, - related_object=related_object, - resource=self.settings["resource"], - ) - elif inverse.RelatedObjects == tuple(self.settings["resource"]): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - if self.settings["resource"].Usage: - self.file.remove(self.settings["resource"].Usage) - if self.settings["resource"].BaseQuantity: - ifcopenshell.api.run( - "resource.remove_resource_quantity", - self.file, - resource=self.settings["resource"], - ) - history = self.settings["resource"].OwnerHistory - self.file.remove(self.settings["resource"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: review deep purge + for inverse in file.get_inverse(settings["resource"]): + if inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["resource"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run( + "resource.remove_resource", + file, + resource=related_object, + ) + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToControl"): + if len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["resource"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToResource"): + if inverse.RelatingResource == settings["resource"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run( + "resource.unassign_resource", + file, + related_object=related_object, + resource=settings["resource"], + ) + elif inverse.RelatedObjects == tuple(settings["resource"]): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + if settings["resource"].Usage: + file.remove(settings["resource"].Usage) + if settings["resource"].BaseQuantity: + ifcopenshell.api.run( + "resource.remove_resource_quantity", + file, + resource=settings["resource"], + ) + history = settings["resource"].OwnerHistory + file.remove(settings["resource"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py index afafa9a193..221d94c5a6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/remove_resource_quantity.py @@ -19,34 +19,31 @@ import ifcopenshell.util.element -class Usecase: - def __init__(self, file, resource=None): - """Removes the base quantity of a resource +def remove_resource_quantity(file, resource=None) -> None: + """Removes the base quantity of a resource - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") - # Labour resource is quantified in terms of time. - ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") + # Labour resource is quantified in terms of time. + ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") - # Let's say we only want to store the resource but no quantities, - # let's clean up our mess and remove the quantity. - ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour) - """ - self.file = file - self.settings = {"resource": resource} + # Let's say we only want to store the resource but no quantities, + # let's clean up our mess and remove the quantity. + ifcopenshell.api.run("resource.remove_resource_quantity", model, resource=labour) + """ + settings = {"resource": resource} - def execute(self): - old_quantity = self.settings["resource"].BaseQuantity - self.settings["resource"].BaseQuantity = None - if old_quantity: - ifcopenshell.util.element.remove_deep(self.file, old_quantity) + old_quantity = settings["resource"].BaseQuantity + settings["resource"].BaseQuantity = None + if old_quantity: + ifcopenshell.util.element.remove_deep(file, old_quantity) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py index ceed0dbf2a..7b1a59f519 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/unassign_resource.py @@ -21,65 +21,57 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_resource=None, related_object=None): - """Removes the relationship between a resource and object +def unassign_resource(file, relating_resource=None, related_object=None) -> None: + """Removes the relationship between a resource and object - :param relating_resource: The IfcResource to assign the object to. - :type relating_resource: ifcopenshell.entity_instance - :param related_object: The IfcProduct or IfcActor to assign to the - object. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToResource - :rtype: ifcopenshell.entity_instance + :param relating_resource: The IfcResource to assign the object to. + :type relating_resource: ifcopenshell.entity_instance + :param related_object: The IfcProduct or IfcActor to assign to the + object. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToResource + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - # Add some a tower crane to our crew. - crane = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") + # Add some a tower crane to our crew. + crane = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcConstructionEquipmentResource", name="Tower Crane 01") - # Our tower crane will be placed via this physical product. - product = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") + # Our tower crane will be placed via this physical product. + product = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcBuildingElementProxy", predefined_type="CRANE") - # Let's assign our crane to the resource. The crane now represents - # the resource. - ifcopenshell.api.run("resource.assign_resource", model, - relating_resource=crane, related_object=product) + # Let's assign our crane to the resource. The crane now represents + # the resource. + ifcopenshell.api.run("resource.assign_resource", model, + relating_resource=crane, related_object=product) - # Undo it. - ifcopenshell.api.run("resource.unassign_resource", model, - relating_resource=crane, related_object=product) - """ - self.file = file - self.settings = { - "relating_resource": relating_resource, - "related_object": related_object, - } + # Undo it. + ifcopenshell.api.run("resource.unassign_resource", model, + relating_resource=crane, related_object=product) + """ + settings = { + "relating_resource": relating_resource, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if ( - not rel.is_a("IfcRelAssignsToResource") - or rel.RelatingResource != self.settings["relating_resource"] - ): - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": rel} - ) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != settings["relating_resource"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py index e0caddbe3c..309f87cfff 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .copy_class import copy_class +from .create_entity import create_entity +from .reassign_class import reassign_class +from .remove_product import remove_product diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index 389930d409..976010c3c3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -21,52 +21,55 @@ import ifcopenshell.util.system import ifcopenshell.util.element +def copy_class(file, product=None) -> None: + """Copies a product + + The following relationships are also duplicated: + + * The copy will have the same object placement coordinates as the + original. + * The copy will have duplicated property sets, properties, and quantities + * The copy will have all nested distribution ports copied too + * The copy will be part of the same aggregate + * The copy will be contained in the same spatial structure + * The copy, if it is an occurrence, will have the same type + * Voids are duplicated too + * The copy will have the same material as the original. Parametric + material set usages will be copied. + * The copy will be part of the same groups as the original. + + Be warned that: + + * Representations are _not_ copied. Copying representations is an + expensive operation so for now the user is responsible for handling + representations. + * Filled voids are not copied, as there is no guarantee that the filling + will also be copied. + * Path connectivity is not copied, as there is no guarantee that the + connections are still valid. + + :param product: The IfcProduct to copy. + :type param: ifcopenshell.entity_instance + :return: The copied product + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # And now we have two + wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"product": product} + return usecase.execute() + + class Usecase: - def __init__(self, file, product=None): - """Copies a product - - The following relationships are also duplicated: - - * The copy will have the same object placement coordinates as the - original. - * The copy will have duplicated property sets, properties, and quantities - * The copy will have all nested distribution ports copied too - * The copy will be part of the same aggregate - * The copy will be contained in the same spatial structure - * The copy, if it is an occurrence, will have the same type - * Voids are duplicated too - * The copy will have the same material as the original. Parametric - material set usages will be copied. - * The copy will be part of the same groups as the original. - - Be warned that: - - * Representations are _not_ copied. Copying representations is an - expensive operation so for now the user is responsible for handling - representations. - * Filled voids are not copied, as there is no guarantee that the filling - will also be copied. - * Path connectivity is not copied, as there is no guarantee that the - connections are still valid. - - :param product: The IfcProduct to copy. - :type param: ifcopenshell.entity_instance - :return: The copied product - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # And now we have two - wall_copy = ifcopenshell.api.run("root.copy_class", model, product=wall) - """ - self.file = file - self.settings = {"product": product} - def execute(self): result = ifcopenshell.util.element.copy(self.file, self.settings["product"]) self.copy_direct_attributes(result) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py index 7619eec067..5bb64d411a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/create_entity.py @@ -21,63 +21,65 @@ import ifcopenshell.api from typing import Optional +def create_entity( + file: ifcopenshell.file, + ifc_class: str = "IfcBuildingElementProxy", + predefined_type: Optional[str] = None, + name: Optional[str] = None, +) -> ifcopenshell.entity_instance: + """Create a new rooted product + + This is a critical function used to create almost any rooted product or + product type. If you want to create walls, spaces, buildings, wall + types, and so on, use this function. + + Just specify the class you want to create, as well as the predefined + type and name. It will handle the storage of the predefined type and + check whether the predefined type is built-in or custom. It will also + generate a valid GlobalId and store ownership history. It will also + handle some edge cases for default validity where users might forget to + populate some mandatory attributes. For example, doors must define an + operation type but many people forget. + + :param ifc_class: Any rooted IFC class. + :type ifc_class: str,optional + :param predefined_type: Any built-in or user-defined predefined type that + is applicable to that IFC class. For user-defined predefined types + just enter in any value and the API will handle it automatically. + :type predefined_type: str,optional + :param name: The name of the new element. + :type name: str,optional + :return: The newly created element based on the specified IFC class. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a project. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + + # We have a building. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + + # We have a wall. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # We have a wall type. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "ifc_class": ifc_class, + "predefined_type": predefined_type, + "name": name, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - ifc_class: str = "IfcBuildingElementProxy", - predefined_type: Optional[str] = None, - name: Optional[str] = None, - ): - """Create a new rooted product - - This is a critical function used to create almost any rooted product or - product type. If you want to create walls, spaces, buildings, wall - types, and so on, use this function. - - Just specify the class you want to create, as well as the predefined - type and name. It will handle the storage of the predefined type and - check whether the predefined type is built-in or custom. It will also - generate a valid GlobalId and store ownership history. It will also - handle some edge cases for default validity where users might forget to - populate some mandatory attributes. For example, doors must define an - operation type but many people forget. - - :param ifc_class: Any rooted IFC class. - :type ifc_class: str,optional - :param predefined_type: Any built-in or user-defined predefined type that - is applicable to that IFC class. For user-defined predefined types - just enter in any value and the API will handle it automatically. - :type predefined_type: str,optional - :param name: The name of the new element. - :type name: str,optional - :return: The newly created element based on the specified IFC class. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a project. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - - # We have a building. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - - # We have a wall. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # We have a wall type. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType") - """ - self.file = file - self.settings = { - "ifc_class": ifc_class, - "predefined_type": predefined_type, - "name": name, - } - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): element = self.file.create_entity( self.settings["ifc_class"], **{ diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py index 1035a6b17c..c124786a83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -22,61 +22,63 @@ import ifcopenshell.util.schema import ifcopenshell.util.element +def reassign_class( + file, + product=None, + ifc_class="IfcBuildingElementProxy", + predefined_type=None, +) -> None: + """Changes the class of a product + + If you ever created a wall then realised it's meant to be something + else, this function lets you change the IFC class whilst retaining all + other geometry and relationships. + + This is especially useful when dealing with poorly classified data from + proprietary software with limited IFC capabilities. + + If you are reassigning a type, the occurrence classes are also + reassigned to maintain validity. + + Vice versa, if you are reassigning an occurrence, the type is also + reassigned in IFC4 and up. In IFC2X3, this may not occur if the type + cannot be unambiguously derived, so you are required to manually check + this. + + :param product: The IfcProduct that you want to change the class of. + :type product: ifcopenshell.entity_instance + :param ifc_class: The new IFC class you want to change it to. + :type ifc_class: str,optional + :param predefined_type: In case you want to change the predefined type + too. User defined types are also allowed, just type what you want. + :type predefined_type: str,optional + :return: The newly modified product. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # We have a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Oh, did I say wall? I meant slab. + slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab") + + # Warning: this will crash since wall doesn't exist any more. + print(wall) # Kaboom. + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "product": product, + "ifc_class": ifc_class, + "predefined_type": predefined_type, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - product=None, - ifc_class="IfcBuildingElementProxy", - predefined_type=None, - ): - """Changes the class of a product - - If you ever created a wall then realised it's meant to be something - else, this function lets you change the IFC class whilst retaining all - other geometry and relationships. - - This is especially useful when dealing with poorly classified data from - proprietary software with limited IFC capabilities. - - If you are reassigning a type, the occurrence classes are also - reassigned to maintain validity. - - Vice versa, if you are reassigning an occurrence, the type is also - reassigned in IFC4 and up. In IFC2X3, this may not occur if the type - cannot be unambiguously derived, so you are required to manually check - this. - - :param product: The IfcProduct that you want to change the class of. - :type product: ifcopenshell.entity_instance - :param ifc_class: The new IFC class you want to change it to. - :type ifc_class: str,optional - :param predefined_type: In case you want to change the predefined type - too. User defined types are also allowed, just type what you want. - :type predefined_type: str,optional - :return: The newly modified product. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # We have a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Oh, did I say wall? I meant slab. - slab = ifcopenshell.api.run("root.reassign_class", model, product=wall, ifc_class="IfcSlab") - - # Warning: this will crash since wall doesn't exist any more. - print(wall) # Kaboom. - """ - self.file = file - self.settings = { - "product": product, - "ifc_class": ifc_class, - "predefined_type": predefined_type, - } - def execute(self): element = self.reassign_class(self.settings["product"], self.settings["ifc_class"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py index cecb5544f3..6a1c48b404 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/remove_product.py @@ -20,212 +20,207 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, product: ifcopenshell.entity_instance): - """Removes a product +def remove_product(file: ifcopenshell.file, product: ifcopenshell.entity_instance) -> None: + """Removes a product - This is effectively a smart delete function that not only removes a - product, but also all of its relationships. It is always recommended to - use this function to prevent orphaned data in your IFC model. + This is effectively a smart delete function that not only removes a + product, but also all of its relationships. It is always recommended to + use this function to prevent orphaned data in your IFC model. - This is intended to be used for removing: + This is intended to be used for removing: - - IfcAnnotation - - IfcElement - - IfcElementType - - IfcSpatialElement - - IfcSpatialElementType + - IfcAnnotation + - IfcElement + - IfcElementType + - IfcSpatialElement + - IfcSpatialElementType - For example, geometric representations are removed. Placement - coordinates are also removed. Properties are removed. Material, type, - containment, aggregation, and nesting relationships are removed (but - naturally, the materials, types, containers, etc themselves remain). + For example, geometric representations are removed. Placement + coordinates are also removed. Properties are removed. Material, type, + containment, aggregation, and nesting relationships are removed (but + naturally, the materials, types, containers, etc themselves remain). - :param product: The element to remove. - :type product: ifcopenshell.entity_instance - :return: None - :rtype: None + :param product: The element to remove. + :type product: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # We have a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # We have a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # No we don't. - ifcopenshell.api.run("root.remove_product", model, product=wall) - """ - self.file = file - self.settings = {"product": product} + # No we don't. + ifcopenshell.api.run("root.remove_product", model, product=wall) + """ + settings = {"product": product} - def execute(self) -> None: - representations = [] - if self.settings["product"].is_a("IfcProduct"): - if self.settings["product"].Representation: - representations = self.settings["product"].Representation.Representations or [] - else: - representations = [] + representations = [] + if settings["product"].is_a("IfcProduct"): + if settings["product"].Representation: + representations = settings["product"].Representation.Representations or [] + else: + representations = [] - # remove object placements - object_placement = self.settings["product"].ObjectPlacement - if object_placement: - if self.file.get_total_inverses(object_placement) == 1: - self.settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work - ifcopenshell.util.element.remove_deep2(self.file, object_placement) + # remove object placements + object_placement = settings["product"].ObjectPlacement + if object_placement: + if file.get_total_inverses(object_placement) == 1: + settings["product"].ObjectPlacement = None # remove the inverse for remove_deep2 to work + ifcopenshell.util.element.remove_deep2(file, object_placement) - elif self.settings["product"].is_a("IfcTypeProduct"): - representations = [rm.MappedRepresentation for rm in self.settings["product"].RepresentationMaps or []] + elif settings["product"].is_a("IfcTypeProduct"): + representations = [rm.MappedRepresentation for rm in settings["product"].RepresentationMaps or []] - # remove psets - psets = self.settings["product"].HasPropertySets or [] - for pset in psets: - if self.file.get_total_inverses(pset) != 1: - continue - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["product"], - pset=pset, - ) - - for representation in representations: - ifcopenshell.api.run( - "geometry.unassign_representation", - self.file, - **{"product": self.settings["product"], "representation": representation} - ) - ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation}) - for opening in getattr(self.settings["product"], "HasOpenings", []) or []: - ifcopenshell.api.run("void.remove_opening", self.file, opening=opening.RelatedOpeningElement) - - if self.settings["product"].is_a("IfcGrid"): - for axis in ( - self.settings["product"].UAxes + self.settings["product"].VAxes + (self.settings["product"].WAxes or ()) - ): - ifcopenshell.api.run("grid.remove_grid_axis", self.file, axis=axis) - - def element_exists(element_id): - try: - self.file.by_id(element_id) - return True - except RuntimeError: - return False - - # TODO: remove object placement and other relationships - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["product"])]: - try: - inverse = self.file.by_id(inverse_id) - except: + # remove psets + psets = settings["product"].HasPropertySets or [] + for pset in psets: + if file.get_total_inverses(pset) != 1: continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["product"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssociatesMaterial"): - ifcopenshell.api.run("material.unassign_material", self.file, products=[self.settings["product"]]) - elif inverse.is_a("IfcRelDefinesByType"): - if inverse.RelatingType == self.settings["product"]: - ifcopenshell.api.run("type.unassign_type", self.file, related_objects=inverse.RelatedObjects) - else: - ifcopenshell.api.run("type.unassign_type", self.file, related_objects=[self.settings["product"]]) - elif inverse.is_a("IfcRelSpaceBoundary"): - ifcopenshell.api.run("boundary.remove_boundary", self.file, boundary=inverse) - elif inverse.is_a("IfcRelFillsElement"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelVoidsElement"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelServicesBuildings"): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["product"]: - inverse_id = inverse.id() - for subelement in inverse.RelatedObjects: - if subelement.is_a("IfcDistributionPort"): - ifcopenshell.api.run("root.remove_product", self.file, product=subelement) - # IfcRelNests could have been already deleted after removing one of the products - if element_exists(inverse_id): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.RelatedObjects == (self.settings["product"],): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["product"], + pset=pset, + ) + + for representation in representations: + ifcopenshell.api.run( + "geometry.unassign_representation", + file, + **{"product": settings["product"], "representation": representation} + ) + ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation}) + for opening in getattr(settings["product"], "HasOpenings", []) or []: + ifcopenshell.api.run("void.remove_opening", file, opening=opening.RelatedOpeningElement) + + if settings["product"].is_a("IfcGrid"): + for axis in settings["product"].UAxes + settings["product"].VAxes + (settings["product"].WAxes or ()): + ifcopenshell.api.run("grid.remove_grid_axis", file, axis=axis) + + def element_exists(element_id): + try: + file.by_id(element_id) + return True + except RuntimeError: + return False + + # TODO: remove object placement and other relationships + for inverse_id in [i.id() for i in file.get_inverse(settings["product"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["product"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssociatesMaterial"): + ifcopenshell.api.run("material.unassign_material", file, products=[settings["product"]]) + elif inverse.is_a("IfcRelDefinesByType"): + if inverse.RelatingType == settings["product"]: + ifcopenshell.api.run("type.unassign_type", file, related_objects=inverse.RelatedObjects) + else: + ifcopenshell.api.run("type.unassign_type", file, related_objects=[settings["product"]]) + elif inverse.is_a("IfcRelSpaceBoundary"): + ifcopenshell.api.run("boundary.remove_boundary", file, boundary=inverse) + elif inverse.is_a("IfcRelFillsElement"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelVoidsElement"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelServicesBuildings"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["product"]: + inverse_id = inverse.id() + for subelement in inverse.RelatedObjects: + if subelement.is_a("IfcDistributionPort"): + ifcopenshell.api.run("root.remove_product", file, product=subelement) + # IfcRelNests could have been already deleted after removing one of the products + if element_exists(inverse_id): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAggregates"): - if inverse.RelatingObject == self.settings["product"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelContainedInSpatialStructure"): - if inverse.RelatingStructure == self.settings["product"] or len(inverse.RelatedElements) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelConnectsElements"): - if inverse.is_a("IfcRelConnectsWithRealizingElements"): - if self.settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any( - el for el in inverse.RealizingElements if el != self.settings["product"] - ): - continue + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.RelatedObjects == (settings["product"],): history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelConnectsPorts"): - if self.settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort): - # if it's not RelatingPort/RelatedPort then it's optional RealizingElement - # so we keep the relationship + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAggregates"): + if inverse.RelatingObject == settings["product"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelContainedInSpatialStructure"): + if inverse.RelatingStructure == settings["product"] or len(inverse.RelatedElements) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelConnectsElements"): + if inverse.is_a("IfcRelConnectsWithRealizingElements"): + if settings["product"] not in (inverse.RelatingElement, inverse.RelatedElement) and any( + el for el in inverse.RealizingElements if el != settings["product"] + ): continue + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelConnectsPorts"): + if settings["product"] not in (inverse.RelatingPort, inverse.RelatedPort): + # if it's not RelatingPort/RelatedPort then it's optional RealizingElement + # so we keep the relationship + continue + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToGroup"): + if len(inverse.RelatedObjects) == 1: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToGroup"): - if len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToProduct"): - if inverse.RelatingProduct == self.settings["product"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelFlowControlElements"): - if inverse.RelatingFlowElement == self.settings["product"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.RelatedControlElements == (self.settings["product"],): - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["product"].OwnerHistory - self.file.remove(self.settings["product"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToProduct"): + if inverse.RelatingProduct == settings["product"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelFlowControlElements"): + if inverse.RelatingFlowElement == settings["product"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.RelatedControlElements == (settings["product"],): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["product"].OwnerHistory + file.remove(settings["product"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py index e0caddbe3c..90cb5f4922 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/__init__.py @@ -15,3 +15,47 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_task import add_task +from .add_task_time import add_task_time +from .add_time_period import add_time_period +from .add_work_calendar import add_work_calendar +from .add_work_plan import add_work_plan +from .add_work_schedule import add_work_schedule +from .add_work_time import add_work_time +from .assign_lag_time import assign_lag_time +from .assign_process import assign_process +from .assign_product import assign_product +from .assign_recurrence_pattern import assign_recurrence_pattern +from .assign_sequence import assign_sequence +from .assign_workplan import assign_workplan +from .calculate_task_duration import calculate_task_duration +from .cascade_schedule import cascade_schedule +from .create_baseline import create_baseline +from .duplicate_task import duplicate_task +from .edit_lag_time import edit_lag_time +from .edit_recurrence_pattern import edit_recurrence_pattern +from .edit_sequence import edit_sequence +from .edit_task import edit_task +from .edit_task_time import edit_task_time +from .edit_work_calendar import edit_work_calendar +from .edit_work_plan import edit_work_plan +from .edit_work_schedule import edit_work_schedule +from .edit_work_time import edit_work_time +from .get_related_products import get_related_products + +try: + from .recalculate_schedule import recalculate_schedule +except ModuleNotFoundError as e: + print(f"Note: API not available due to missing dependencies: sequence.recalculate_schedule - {e}") +from .remove_task import remove_task +from .remove_time_period import remove_time_period +from .remove_work_calendar import remove_work_calendar +from .remove_work_plan import remove_work_plan +from .remove_work_schedule import remove_work_schedule +from .remove_work_time import remove_work_time +from .unassign_lag_time import unassign_lag_time +from .unassign_process import unassign_process +from .unassign_product import unassign_product +from .unassign_recurrence_pattern import unassign_recurrence_pattern +from .unassign_sequence import unassign_sequence diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py index 60ab48c6df..7d1a91423f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py @@ -20,169 +20,159 @@ import ifcopenshell.api import ifcopenshell -class Usecase: - def __init__( - self, +def add_task( + file, + work_schedule=None, + parent_task=None, + name=None, + description=None, + identification=None, + predefined_type="NOTDEFINED", +) -> None: + """Adds a new task + + Tasks are typically used for two purposes: construction scheduling and + facility management. + + In construction scheduling, a task represents a job to be done in a work + schedule. Tasks are organised in a hierarchical manner known as a work + breakdown structure (WBS) and have lots of sequential relationships + (e.g. this task must finish before the next task can start) and date + information (e.g. durations, start dates). This is often represented as + a gantt chart and used to analyse critical paths to try and reduce + project time to stay on-time and within budget. + + In facility management, a task represents a maintenance task to maintain + a piece of equipment. Tasks are broken down into a punch list, or simply + a bulleted or ordered sequence of tasks to be performed (e.g. turn off + equipment, check power connection, etc) in order to maintain the + equipment. Tasks will also typically have recurring scheduled dates in + line with the maintenance schedule. These maintenance tasks and + procedures are typically published as part of an operations and + maintenance manual. + + All tasks must be grouped in a work schedule, either directly as a root + or top-level task, or indirectly as a child or subtask of a parent task. + In construction scheduling, tasks may be nested many times to create the + work breakdown structure, and the "leaf" tasks (i.e. tasks with no more + subtasks) are considered to be the activities with dates, whereas all + parent tasks are part of the breakdown structure used for categorisation + purposes. In facility management, top-level tasks represent the overall + maintenance job to be performed, and child tasks represent an ordered + list of things to do for that maintenance. These form a 2-level + hierarchy. No further child tasks are recommended. + + :param work_schedule: The work schedule to group the task in, if the + task is to be a top-level or root task. This is mutually exclusive + with the parent_task parameter. + :type work_schedule: ifcopenshell.entity_instance + :param parent_task: The parent task, if the task is to be a subtask or + child task. This is mutually exclusive with the work_schedule + parameter. + :type parent_task: ifcopenshell.entity_instance + :param name: The name of the task. + :type name: str,optional + :param description: The description of the task. + :type description: str,optional + :param identification: The identification code of the task. + :type identification: str,optional + :param predefined_type: The predefined type of the task. Common ones + include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the + IFC documentation for IfcTaskTypeEnum for more information. + :type predefined_type: str + :return: The newly created IfcTask + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + + # Let's start creating our work breakdown structure. + ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Early Works", identification="C1") + ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Substructure", identification="C2") + superstructure = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Superstructure", identification="C3") + + # Notice how the leaf task is the actual activity + ifcopenshell.api.run("sequence.add_task", model, + parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") + + # Let's imagine we are digitising an operations and maintenance + # manual for the mechanical discipline. + maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance") + + # Imagine we have to clean the condenser coils for a chiller every + # month. Like the schedule above, to keep things simple we won't + # show scheduling times and calendars. This root task represents the + # overall maintenance task. + cleaning = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=maintenance, name="Condenser coil cleaning") + + # These subtasks represent the punch list of maintenance tasks. + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1", + description="Prior to work, wear safety shoes, gloves, and goggles.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2", + description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Switch OFF the chiller unit.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Open the isolator switch.") + ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", + description="Setup the water pressure by tapping to a water supply and connecting to a ...") + """ + settings = { + "work_schedule": work_schedule, + "parent_task": parent_task, + "name": name, + "description": description, + "identification": identification, + "predefined_type": predefined_type, + } + + task = ifcopenshell.api.run( + "root.create_entity", file, - work_schedule=None, - parent_task=None, - name=None, - description=None, - identification=None, - predefined_type="NOTDEFINED", - ): - """Adds a new task - - Tasks are typically used for two purposes: construction scheduling and - facility management. - - In construction scheduling, a task represents a job to be done in a work - schedule. Tasks are organised in a hierarchical manner known as a work - breakdown structure (WBS) and have lots of sequential relationships - (e.g. this task must finish before the next task can start) and date - information (e.g. durations, start dates). This is often represented as - a gantt chart and used to analyse critical paths to try and reduce - project time to stay on-time and within budget. - - In facility management, a task represents a maintenance task to maintain - a piece of equipment. Tasks are broken down into a punch list, or simply - a bulleted or ordered sequence of tasks to be performed (e.g. turn off - equipment, check power connection, etc) in order to maintain the - equipment. Tasks will also typically have recurring scheduled dates in - line with the maintenance schedule. These maintenance tasks and - procedures are typically published as part of an operations and - maintenance manual. - - All tasks must be grouped in a work schedule, either directly as a root - or top-level task, or indirectly as a child or subtask of a parent task. - In construction scheduling, tasks may be nested many times to create the - work breakdown structure, and the "leaf" tasks (i.e. tasks with no more - subtasks) are considered to be the activities with dates, whereas all - parent tasks are part of the breakdown structure used for categorisation - purposes. In facility management, top-level tasks represent the overall - maintenance job to be performed, and child tasks represent an ordered - list of things to do for that maintenance. These form a 2-level - hierarchy. No further child tasks are recommended. - - :param work_schedule: The work schedule to group the task in, if the - task is to be a top-level or root task. This is mutually exclusive - with the parent_task parameter. - :type work_schedule: ifcopenshell.entity_instance - :param parent_task: The parent task, if the task is to be a subtask or - child task. This is mutually exclusive with the work_schedule - parameter. - :type parent_task: ifcopenshell.entity_instance - :param name: The name of the task. - :type name: str,optional - :param description: The description of the task. - :type description: str,optional - :param identification: The identification code of the task. - :type identification: str,optional - :param predefined_type: The predefined type of the task. Common ones - include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the - IFC documentation for IfcTaskTypeEnum for more information. - :type predefined_type: str - :return: The newly created IfcTask - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - - # Let's start creating our work breakdown structure. - ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Early Works", identification="C1") - ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Substructure", identification="C2") - superstructure = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Superstructure", identification="C3") - - # Notice how the leaf task is the actual activity - ifcopenshell.api.run("sequence.add_task", model, - parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") - - # Let's imagine we are digitising an operations and maintenance - # manual for the mechanical discipline. - maintenance = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Mechanical Maintenance") - - # Imagine we have to clean the condenser coils for a chiller every - # month. Like the schedule above, to keep things simple we won't - # show scheduling times and calendars. This root task represents the - # overall maintenance task. - cleaning = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=maintenance, name="Condenser coil cleaning") - - # These subtasks represent the punch list of maintenance tasks. - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="1", - description="Prior to work, wear safety shoes, gloves, and goggles.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="2", - description="Prepare jet pump, screwdriver, hose clamp, and control panel door key.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Switch OFF the chiller unit.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Open the isolator switch.") - ifcopenshell.api.run("sequence.add_task", model, parent_task=cleaning, identification="3", - description="Setup the water pressure by tapping to a water supply and connecting to a ...") - """ - self.file = file - self.settings = { - "work_schedule": work_schedule, - "parent_task": parent_task, - "name": name, - "description": description, - "identification": identification, - "predefined_type": predefined_type, - } - - def execute(self): - task = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcTask", - name=self.settings["name"], - predefined_type=self.settings["predefined_type"], + ifc_class="IfcTask", + name=settings["name"], + predefined_type=settings["predefined_type"], + ) + if settings["description"]: + task.Description = settings["description"] + if settings["identification"]: + task.Identification = settings["identification"] + task.IsMilestone = False + if settings["work_schedule"]: + file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [task], + "RelatingControl": settings["work_schedule"], + } ) - if self.settings["description"]: - task.Description = self.settings["description"] - if self.settings["identification"]: - task.Identification = self.settings["identification"] - task.IsMilestone = False - if self.settings["work_schedule"]: - self.file.create_entity( - "IfcRelAssignsToControl", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [task], - "RelatingControl": self.settings["work_schedule"], - } - ) - elif self.settings["parent_task"]: - rel = ifcopenshell.api.run( - "nest.assign_object", - self.file, - related_objects=[task], - relating_object=self.settings["parent_task"], - ) - if self.settings["parent_task"].Identification: - task.Identification = ( - self.settings["parent_task"].Identification - + "." - + str(len(rel.RelatedObjects)) - ) - return task + elif settings["parent_task"]: + rel = ifcopenshell.api.run( + "nest.assign_object", + file, + related_objects=[task], + relating_object=settings["parent_task"], + ) + if settings["parent_task"].Identification: + task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects)) + return task diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py index 7c4381c361..bbd51c2e68 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py @@ -17,55 +17,52 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, task=None, is_recurring=False): - """Adds a task time to a task +def add_task_time(file, task=None, is_recurring=False) -> None: + """Adds a task time to a task - Some tasks, such as activities within a work breakdown structure or - overall maintenance tasks will have time related information. This - includes start dates, durations, end dates, and possible recurring times - (especially for maintenance tasks). + Some tasks, such as activities within a work breakdown structure or + overall maintenance tasks will have time related information. This + includes start dates, durations, end dates, and possible recurring times + (especially for maintenance tasks). - :param task: The task to add time data to. - :type task: ifcopenshell.entity_instance - :param is_recurring: Whether or not the time should recur. - :type is_recurring: bool - :return: The newly created IfcTaskTime. - :rtype: ifcopenshell.entity_instance + :param task: The task to add time data to. + :type task: ifcopenshell.entity_instance + :param is_recurring: Whether or not the time should recur. + :type is_recurring: bool + :return: The newly created IfcTaskTime. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Create a portion of a work breakdown structure. - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - superstructure = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Superstructure", identification="C3") - task = ifcopenshell.api.run("sequence.add_task", model, - parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") + # Create a portion of a work breakdown structure. + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + superstructure = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Superstructure", identification="C3") + task = ifcopenshell.api.run("sequence.add_task", model, + parent_task=superstructure, name="Ground Floor FRP", identification="C3.1") - # Add time data. Note that time data is blank by default. - time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) + # Add time data. Note that time data is blank by default. + time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) - # Let's say our task starts on the first of January when everybody - # is still drunk from the new years celebration, and lasts for 2 - # days. Note we don't need to specify the end date, as that is - # derived from the start plus the duration. In this simple example, - # no calendar has been specified, so we are working 24/7. Yikes! - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - """ - self.file = file - self.settings = {"task": task, "is_recurring": is_recurring} + # Let's say our task starts on the first of January when everybody + # is still drunk from the new years celebration, and lasts for 2 + # days. Note we don't need to specify the end date, as that is + # derived from the start plus the duration. In this simple example, + # no calendar has been specified, so we are working 24/7. Yikes! + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + """ + settings = {"task": task, "is_recurring": is_recurring} - def execute(self): - if self.settings["is_recurring"]: - task_time = self.file.create_entity("IfcTaskTimeRecurring") - else: - task_time = self.file.create_entity("IfcTaskTime") - self.settings["task"].TaskTime = task_time - return task_time + if settings["is_recurring"]: + task_time = file.create_entity("IfcTaskTimeRecurring") + else: + task_time = file.create_entity("IfcTaskTime") + settings["task"].TaskTime = task_time + return task_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py index 35a022a9ea..479524a4b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py @@ -23,79 +23,72 @@ from datetime import datetime from datetime import timedelta -class Usecase: - def __init__(self, file, recurrence_pattern=None, start_time=None, end_time=None): - """Adds a time period to a recurrence pattern +def add_time_period(file, recurrence_pattern=None, start_time=None, end_time=None) -> None: + """Adds a time period to a recurrence pattern - A recurring time may be an all-day event, or only during certain time - periods of the day. For example, you might say that every 1st of January - recurring is a public holiday, which is an all-day event. Alternatively, - you might say that you work every (i.e. recurringly) Monday to Friday, - from 9am to 5pm. The 9am to 5pm is the time period. + A recurring time may be an all-day event, or only during certain time + periods of the day. For example, you might say that every 1st of January + recurring is a public holiday, which is an all-day event. Alternatively, + you might say that you work every (i.e. recurringly) Monday to Friday, + from 9am to 5pm. The 9am to 5pm is the time period. - There may also be multiple recurrence patterns, such as from 9am to - 12pm, and then another from 1pm to 5pm (to indicate an hour break for - lunch). + There may also be multiple recurrence patterns, such as from 9am to + 12pm, and then another from 1pm to 5pm (to indicate an hour break for + lunch). - :param recurrence_pattern: The IfcRecurrencePattern to add the time - period to. See ifcopenshell.api.sequence.assign_recurrence_pattern. - :type recurrence_pattern: ifcopenshell.entity_instance - :param start_time: The start time of the time period, in a format - compatible with IfcTime, such as an ISO format time string or a - datetime.time object. - :type start_time: str,datetime.time - :param end_time: The end time of the time period, in a format - compatible with IfcTime, such as an ISO format time string or a - datetime.time object. - :type end_time: str,datetime.time - :return: The newly created IfcTimePeriod - :rtype: ifcopenshell.entity_instance + :param recurrence_pattern: The IfcRecurrencePattern to add the time + period to. See ifcopenshell.api.sequence.assign_recurrence_pattern. + :type recurrence_pattern: ifcopenshell.entity_instance + :param start_time: The start time of the time period, in a format + compatible with IfcTime, such as an ISO format time string or a + datetime.time object. + :type start_time: str,datetime.time + :param end_time: The end time of the time period, in a format + compatible with IfcTime, such as an ISO format time string or a + datetime.time object. + :type end_time: str,datetime.time + :return: The newly created IfcTimePeriod + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # The morning work session, lunch, then the afternoon work session. - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="12:00") - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="13:00", end_time="17:00") - """ - self.file = file - self.settings = { - "recurrence_pattern": recurrence_pattern, - "start_time": start_time, - "end_time": end_time, - } + # The morning work session, lunch, then the afternoon work session. + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="12:00") + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="13:00", end_time="17:00") + """ + settings = { + "recurrence_pattern": recurrence_pattern, + "start_time": start_time, + "end_time": end_time, + } - def execute(self): - time_period = self.file.create_entity("IfcTimePeriod") - time_period.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcTime" - ) - time_period.EndTime = ifcopenshell.util.date.datetime2ifc( - self.settings["end_time"], "IfcTime" - ) - time_periods = list(self.settings["recurrence_pattern"].TimePeriods or []) - time_periods.append(time_period) - self.settings["recurrence_pattern"].TimePeriods = time_periods + time_period = file.create_entity("IfcTimePeriod") + time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime") + time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime") + time_periods = list(settings["recurrence_pattern"].TimePeriods or []) + time_periods.append(time_period) + settings["recurrence_pattern"].TimePeriods = time_periods - ifcopenshell.util.sequence.is_working_day.cache_clear() - ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() + ifcopenshell.util.sequence.is_working_day.cache_clear() + ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() - return time_period + return time_period diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index 373d628be6..250a1b2fe0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -19,80 +19,77 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, name="Unnamed", predefined_type="NOTDEFINED"): - """Add a work calendar +def add_work_calendar(file, name="Unnamed", predefined_type="NOTDEFINED") -> None: + """Add a work calendar - A work calendar defines when work is allowed to occur and when the - holidays are. This is a fundamental concept in construction planning. - Every task in a work schedule will have an associated calendar. Some - task and resources work 24/7, whereas others work Monday to Friday, or - 5.5 day weeks, etc. This is important, as tasks durations may only occur - during working times in a work calendar. + A work calendar defines when work is allowed to occur and when the + holidays are. This is a fundamental concept in construction planning. + Every task in a work schedule will have an associated calendar. Some + task and resources work 24/7, whereas others work Monday to Friday, or + 5.5 day weeks, etc. This is important, as tasks durations may only occur + during working times in a work calendar. - Work calendars can also be used to associate with events, such as - indicating that during certain days and times of the year, motion - sensors should turn on the lights, and other smart building controls. + Work calendars can also be used to associate with events, such as + indicating that during certain days and times of the year, motion + sensors should turn on the lights, and other smart building controls. - :param name: The name of the calendar. Typically something like - "5 Day Working Week" or "24/7". - :type name: str, optional - :param predefined_type: The type of calendar, typically used to more - specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or - THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage. - :return: The newly created IfcWorkCalendar - :rtype: ifcopenshell.entity_instance + :param name: The name of the calendar. Typically something like + "5 Day Working Week" or "24/7". + :type name: str, optional + :param predefined_type: The type of calendar, typically used to more + specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or + THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage. + :return: The newly created IfcWorkCalendar + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the construction tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Add a root task to represent the construction tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="17:00") + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday), 9am to 5pm + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="17:00") - # We associate the calendar with the construction root task. All - # subtasks underneath the construction work task will also inherit - # this calendar by default (though you can override them). - ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task) - """ - self.file = file - self.settings = {"name": name, "predefined_type": predefined_type} + # We associate the calendar with the construction root task. All + # subtasks underneath the construction work task will also inherit + # this calendar by default (though you can override them). + ifcopenshell.api.run("control.assign_control", model, relating_control=calendar, related_object=task) + """ + settings = {"name": name, "predefined_type": predefined_type} - def execute(self): - work_calendar = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkCalendar", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_calendar], - relating_context=context, - ) - return work_calendar + work_calendar = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcWorkCalendar", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_calendar], + relating_context=context, + ) + return work_calendar diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py index f6fba71315..858d944d66 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py @@ -21,70 +21,63 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__(self, file, name=None, predefined_type="NOTDEFINED", start_time=None): - """Add a new work plan +def add_work_plan(file, name=None, predefined_type="NOTDEFINED", start_time=None) -> None: + """Add a new work plan - A work plan is a group of work schedules. Since work schedules may have - different purposes, such as for maintenance or construction scheduling, - baseline comparison, or phasing, work plans can be used to group related - work schedules. At a minimum, it is recommended to use work plans to - indicate whether the work schedules are for facility management or for - construction scheduling. + A work plan is a group of work schedules. Since work schedules may have + different purposes, such as for maintenance or construction scheduling, + baseline comparison, or phasing, work plans can be used to group related + work schedules. At a minimum, it is recommended to use work plans to + indicate whether the work schedules are for facility management or for + construction scheduling. - :param name: The name of the work plan. Recommended to be "Maintenance" - or "Construction" for the two main purposes. - :type name: str, optional - :param predefined_type: The type of work plan, used for baselining. - Leave as "NOTDEFINED" if unsure. - :type predefined_type: str - :param start_time: The earliest start time when the schedules grouped - within the work plan are relevant. - :type start_time: str,datetime.time - :return: The newly created IfcWorkPlan - :rtype: ifcopenshell.entity_instance + :param name: The name of the work plan. Recommended to be "Maintenance" + or "Construction" for the two main purposes. + :type name: str, optional + :param predefined_type: The type of work plan, used for baselining. + Leave as "NOTDEFINED" if unsure. + :type predefined_type: str + :param start_time: The earliest start time when the schedules grouped + within the work plan are relevant. + :type start_time: str,datetime.time + :return: The newly created IfcWorkPlan + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # This is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) - """ - self.file = file - self.settings = { - "name": name, - "predefined_type": predefined_type, - "start_time": start_time or datetime.now(), - } + # This is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) + """ + settings = { + "name": name, + "predefined_type": predefined_type, + "start_time": start_time or datetime.now(), + } - def execute(self): - work_plan = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkPlan", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], - ) - work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc( - datetime.now(), "IfcDateTime" - ) - user = ifcopenshell.api.owner.settings.get_user(self.file) - if user: - work_plan.Creators = [user.ThePerson] - work_plan.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcDateTime" - ) + work_plan = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcWorkPlan", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + user = ifcopenshell.api.owner.settings.get_user(file) + if user: + work_plan.Creators = [user.ThePerson] + work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime") - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_plan], - relating_context=context, - ) - return work_plan + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_plan], + relating_context=context, + ) + return work_plan diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index 47e96a8fa3..21f508999c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -21,104 +21,96 @@ import ifcopenshell.util.date from datetime import datetime -class Usecase: - def __init__( - self, +def add_work_schedule( + file, + name="Unnamed", + predefined_type="NOTDEFINED", + object_type=None, + start_time=None, + work_plan=None, +) -> None: + """Add a new work schedule + + A work schedule is a group of tasks, where the tasks are typically + either for maintenance or for construction scheduling. + + :param name: The name of the work schedule. + :type name: str + :param predefined_type: The type of schedule, chosen from ACTUAL, + BASELINE, and PLANNED. Typically you would start with PLANNED, then + convert to a BASELINE when changes are made with separate schedules, + then have a parallel ACTUAL schedule. + :type predefined_type: str + :param start_time: The earlier start time when the schedule is relevant. + May be represented with an ISO standard string. + :type start_time: str,datetime.time,optional + :param work_plan: The IfcWorkPlan the schedule will be part of. If not + provided, the schedule will not be grouped in a work plan and would + exist as a top level schedule in the project. This is not + recommended. + :type work_plan: ifcopenshell.entity_instance,optional + :return: The newly created IfcWorkSchedule + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) + + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") + """ + settings = { + "name": name, + "predefined_type": predefined_type, + "object_type": object_type, + "start_time": start_time or datetime.now(), + "work_plan": work_plan, + } + + work_schedule = ifcopenshell.api.run( + "root.create_entity", file, - name="Unnamed", - predefined_type="NOTDEFINED", - object_type=None, - start_time=None, - work_plan=None, - ): - """Add a new work schedule - - A work schedule is a group of tasks, where the tasks are typically - either for maintenance or for construction scheduling. - - :param name: The name of the work schedule. - :type name: str - :param predefined_type: The type of schedule, chosen from ACTUAL, - BASELINE, and PLANNED. Typically you would start with PLANNED, then - convert to a BASELINE when changes are made with separate schedules, - then have a parallel ACTUAL schedule. - :type predefined_type: str - :param start_time: The earlier start time when the schedule is relevant. - May be represented with an ISO standard string. - :type start_time: str,datetime.time,optional - :param work_plan: The IfcWorkPlan the schedule will be part of. If not - provided, the schedule will not be grouped in a work plan and would - exist as a top level schedule in the project. This is not - recommended. - :type work_plan: ifcopenshell.entity_instance,optional - :return: The newly created IfcWorkSchedule - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) - - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") - """ - self.file = file - self.settings = { - "name": name, - "predefined_type": predefined_type, - "object_type": object_type, - "start_time": start_time or datetime.now(), - "work_plan": work_plan, - } - - def execute(self): - work_schedule = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcWorkSchedule", - predefined_type=self.settings["predefined_type"], - name=self.settings["name"], + ifc_class="IfcWorkSchedule", + predefined_type=settings["predefined_type"], + name=settings["name"], + ) + work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime") + user = ifcopenshell.api.owner.settings.get_user(file) + if user: + work_schedule.Creators = [user.ThePerson] + work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime") + if settings["object_type"]: + work_schedule.ObjectType = settings["object_type"] + if settings["work_plan"]: + ifcopenshell.api.run( + "aggregate.assign_object", + file, + **{ + "products": [work_schedule], + "relating_object": settings["work_plan"], + } ) - work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc( - datetime.now(), "IfcDateTime" + else: + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + context = file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", + file, + definitions=[work_schedule], + relating_context=context, ) - user = ifcopenshell.api.owner.settings.get_user(self.file) - if user: - work_schedule.Creators = [user.ThePerson] - work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc( - self.settings["start_time"], "IfcDateTime" - ) - if self.settings["object_type"]: - work_schedule.ObjectType = self.settings["object_type"] - if self.settings["work_plan"]: - ifcopenshell.api.run( - "aggregate.assign_object", - self.file, - **{ - "products": [work_schedule], - "relating_object": self.settings["work_plan"], - } - ) - else: - # TODO: this is an ambiguity by buildingSMART - # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 - context = self.file.by_type("IfcContext")[0] - ifcopenshell.api.run( - "project.assign_declaration", - self.file, - definitions=[work_schedule], - relating_context=context, - ) - return work_schedule + return work_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py index 0d75914949..86d4666ad9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py @@ -17,69 +17,66 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_calendar=None, time_type="WorkingTimes"): - """Add either working times or holiday times to a calendar +def add_work_time(file, work_calendar=None, time_type="WorkingTimes") -> None: + """Add either working times or holiday times to a calendar - A calendar defines when work occurs by defining working times and - holiday times. First, the working times are defined, then the holidays - may override the working times. For this reason, holidays are also known - as exception times. For example, you might define the working times as - every Monday to Friday, then define a few holidays in the year, such as - the 1st of January. If the 1st of January is on a weekday, it will - override the work time. + A calendar defines when work occurs by defining working times and + holiday times. First, the working times are defined, then the holidays + may override the working times. For this reason, holidays are also known + as exception times. For example, you might define the working times as + every Monday to Friday, then define a few holidays in the year, such as + the 1st of January. If the 1st of January is on a weekday, it will + override the work time. - :param work_calendar: The IfcWorkCalendar to add the work or holiday - time definition to. - :type work_calendar: ifcopenshell.entity_instance - :param time_type: Either WorkingTimes or ExceptionTimes, depending on - what you want to define. - :type time_type: str - :return: The newly created IfcWorkTime - :rtype: ifcopenshell.entity_instance + :param work_calendar: The IfcWorkCalendar to add the work or holiday + time definition to. + :type work_calendar: ifcopenshell.entity_instance + :param time_type: Either WorkingTimes or ExceptionTimes, depending on + what you want to define. + :type time_type: str + :return: The newly created IfcWorkTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # Let's set some holidays - holidays = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="ExceptionTimes") + # Let's set some holidays + holidays = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="ExceptionTimes") - # We create a yearly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH") + # We create a yearly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="YEARLY_BY_DAY_OF_MONTH") - # The holiday is every 1st of January - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]}) - """ - self.file = file - self.settings = {"work_calendar": work_calendar, "time_type": time_type} + # The holiday is every 1st of January + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]}) + """ + settings = {"work_calendar": work_calendar, "time_type": time_type} - def execute(self): - work_time = self.file.create_entity("IfcWorkTime") - if self.settings["time_type"] == "WorkingTimes": - working_times = list(self.settings["work_calendar"].WorkingTimes or []) - working_times.append(work_time) - self.settings["work_calendar"].WorkingTimes = working_times - elif self.settings["time_type"] == "ExceptionTimes": - exception_times = list(self.settings["work_calendar"].ExceptionTimes or []) - exception_times.append(work_time) - self.settings["work_calendar"].ExceptionTimes = exception_times - return work_time + work_time = file.create_entity("IfcWorkTime") + if settings["time_type"] == "WorkingTimes": + working_times = list(settings["work_calendar"].WorkingTimes or []) + working_times.append(work_time) + settings["work_calendar"].WorkingTimes = working_times + elif settings["time_type"] == "ExceptionTimes": + exception_times = list(settings["work_calendar"].ExceptionTimes or []) + exception_times.append(work_time) + settings["work_calendar"].ExceptionTimes = exception_times + return work_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py index 87f2882055..0ba89d0346 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_lag_time.py @@ -19,88 +19,78 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, rel_sequence=None, lag_value=None, duration_type="WORKTIME"): - """Assign a lag time to a sequence relationship between tasks +def assign_lag_time(file, rel_sequence=None, lag_value=None, duration_type="WORKTIME") -> None: + """Assign a lag time to a sequence relationship between tasks - A task sequence (e.g. finish to start) may optionally have a lag time - defined. This is a fundamental concept in construction scheduling. The - lag is defined as a duration, and the duration is typically either - calendar based (i.e. follows the working times and holidays of the - calendar) or elapsed time based (i.e. 24/7). + A task sequence (e.g. finish to start) may optionally have a lag time + defined. This is a fundamental concept in construction scheduling. The + lag is defined as a duration, and the duration is typically either + calendar based (i.e. follows the working times and holidays of the + calendar) or elapsed time based (i.e. 24/7). - A sequence may only have a single lag time defined. Negative lag times - are allowed. + A sequence may only have a single lag time defined. Negative lag times + are allowed. - :param rel_sequence: The IfcRelSequence to assign the lag time to. - :type rel_sequence: ifcopenshell.entity_instance - :param lag_value: An ISO standardised duration string. - :type lag_value: str - :param duration_type: Choose from WORKTIME for the associated - calendar-based lag times (this is the most common scenario and is - recommended as a default), or ELAPSEDTIME to not follow the - calendar. You may also choose NOTDEFINED but the behaviour of this - is unclear. - :type duration_type: str - :return: The newly created IfcLagTime - :rtype: ifcopenshell.entity_instance + :param rel_sequence: The IfcRelSequence to assign the lag time to. + :type rel_sequence: ifcopenshell.entity_instance + :param lag_value: An ISO standardised duration string. + :type lag_value: str + :param duration_type: Choose from WORKTIME for the associated + calendar-based lag times (this is the most common scenario and is + recommended as a default), or ELAPSEDTIME to not follow the + calendar. You may also choose NOTDEFINED but the behaviour of this + is unclear. + :type duration_type: str + :return: The newly created IfcLagTime + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's say the formwork must finish before the reinforcement - # can start. This is a typical finish to start relationship (FS). - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) + # Now let's say the formwork must finish before the reinforcement + # can start. This is a typical finish to start relationship (FS). + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) - # Now typically there would be no lag time between formwork and - # reinforcement, but let's pretend that we had to allow 1 day gap - # for whatever reason. - ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") - """ - self.file = file - self.settings = { - "rel_sequence": rel_sequence, - "lag_value": lag_value, - "duration_type": duration_type, - } + # Now typically there would be no lag time between formwork and + # reinforcement, but let's pretend that we had to allow 1 day gap + # for whatever reason. + ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") + """ + settings = { + "rel_sequence": rel_sequence, + "lag_value": lag_value, + "duration_type": duration_type, + } - def execute(self): - lag_value = self.file.createIfcDuration( - ifcopenshell.util.date.datetime2ifc(self.settings["lag_value"], "IfcDuration") - ) - lag_time = self.file.create_entity( - "IfcLagTime", DurationType=self.settings["duration_type"], LagValue=lag_value - ) - if self.settings["rel_sequence"].is_a("IfcRelSequence"): - if ( - self.settings["rel_sequence"].TimeLag - and len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1 - ): - self.file.remove(self.settings["rel_sequence"].TimeLag) - self.settings["rel_sequence"].TimeLag = lag_time + lag_value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(settings["lag_value"], "IfcDuration")) + lag_time = file.create_entity("IfcLagTime", DurationType=settings["duration_type"], LagValue=lag_value) + if settings["rel_sequence"].is_a("IfcRelSequence"): + if settings["rel_sequence"].TimeLag and len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1: + file.remove(settings["rel_sequence"].TimeLag) + settings["rel_sequence"].TimeLag = lag_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py index 5aa2210d42..12f8cfe78c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_process.py @@ -20,109 +20,99 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_process=None, related_object=None): - """Assigns an object to be related to a process, typically a construction task +def assign_process(file, relating_process=None, related_object=None) -> None: + """Assigns an object to be related to a process, typically a construction task - Processes work using the ICOM (Input, Controls, Outputs, Mechanisms) - paradigm in IFC. This process model is commonly used in modeling - manufacturing functions. + Processes work using the ICOM (Input, Controls, Outputs, Mechanisms) + paradigm in IFC. This process model is commonly used in modeling + manufacturing functions. - For example, processes (such as tasks) consume Inputs and transform them - into Outputs. The process may only occur within the limits of Controls - (e.g. cost items) and may require Mechanisms (ISO9000 calls them - Mechanisms, whereas IFC calls them resources, such as raw materials, - labour, or equipment). + For example, processes (such as tasks) consume Inputs and transform them + into Outputs. The process may only occur within the limits of Controls + (e.g. cost items) and may require Mechanisms (ISO9000 calls them + Mechanisms, whereas IFC calls them resources, such as raw materials, + labour, or equipment). - +----------+ - | Controls | - +----------+ - | - V - +--------+ +---------+ +---------+ - | Inputs | --> | Process | --> | Outputs | - +--------+ +---------+ +---------+ - ^ - | - +-----------+ - | Resources | - +-----------+ + +----------+ + | Controls | + +----------+ + | + V + +--------+ +---------+ +---------+ + | Inputs | --> | Process | --> | Outputs | + +--------+ +---------+ +---------+ + ^ + | + +-----------+ + | Resources | + +-----------+ - There are three main scenarios where an object may be related to a - task: defining inputs, controls, and resources of a process. + There are three main scenarios where an object may be related to a + task: defining inputs, controls, and resources of a process. - For inputs, a product (i.e. wall) may be defined as an input to a task, - such as when the task is to demolish the wall (i.e. the wall is an - input, and there is no output). + For inputs, a product (i.e. wall) may be defined as an input to a task, + such as when the task is to demolish the wall (i.e. the wall is an + input, and there is no output). - For controls, a cost item may be defined as a control to a task. + For controls, a cost item may be defined as a control to a task. - For resources, any construction resource may be assigned to a task. + For resources, any construction resource may be assigned to a task. - :param relating_process: The IfcProcess (typically IfcTask) that the - input, control, or resource is related to. - :type relating_process: ifcopenshell.entity_instance - :param related_object: The IfcProduct (for input), IfcCostItem (for - control) or IfcConstructionResource (for resource). - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToProcess relationship - :rtype: ifcopenshell.entity_instance + :param relating_process: The IfcProcess (typically IfcTask) that the + input, control, or resource is related to. + :type relating_process: ifcopenshell.entity_instance + :param related_object: The IfcProduct (for input), IfcCostItem (for + control) or IfcConstructionResource (for resource). + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToProcess relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's demolish that wall! - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_object": related_object, - } + # Let's demolish that wall! + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) + """ + settings = { + "relating_process": relating_process, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToProcess") - and assignment.RelatingProcess == self.settings["relating_process"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]: + return - operates_on = None - if self.settings["relating_process"].OperatesOn: - operates_on = self.settings["relating_process"].OperatesOn[0] + operates_on = None + if settings["relating_process"].OperatesOn: + operates_on = settings["relating_process"].OperatesOn[0] - if operates_on: - related_objects = list(operates_on.RelatedObjects) - related_objects.append(self.settings["related_object"]) - operates_on.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": operates_on} - ) - else: - operates_on = self.file.create_entity( - "IfcRelAssignsToProcess", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProcess": self.settings["relating_process"], - } - ) - return operates_on + if operates_on: + related_objects = list(operates_on.RelatedObjects) + related_objects.append(settings["related_object"]) + operates_on.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": operates_on}) + else: + operates_on = file.create_entity( + "IfcRelAssignsToProcess", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProcess": settings["relating_process"], + } + ) + return operates_on diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py index 3431a71f19..bd9b90d2da 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_product.py @@ -20,85 +20,75 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Assigns a product to be produced as a result of a process +def assign_product(file, relating_product=None, related_object=None) -> None: + """Assigns a product to be produced as a result of a process - A construction task may result in products (e.g. a wall) being - constructed. These task "Outputs" are defined in IFC through product - relationships. + A construction task may result in products (e.g. a wall) being + constructed. These task "Outputs" are defined in IFC through product + relationships. - Not all tasks have Outputs. For example, maintenance tasks will - typically not have any outputs. + Not all tasks have Outputs. For example, maintenance tasks will + typically not have any outputs. - See ifcopenshell.api.sequence.assign_process for Inputs and other types - of process relationships that can be described in manufacturing - process modeling. + See ifcopenshell.api.sequence.assign_process for Inputs and other types + of process relationships that can be described in manufacturing + process modeling. - :param relating_product: The IfcProduct that was constructed as a result - of the task. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcProcess (typically IfcTask) of the - construction task. - :type related_object: ifcopenshell.entity_instance - :return: The newly created IfcRelAssignsToProduct relationship - :rtype: ifcopenshell.entity_instance + :param relating_product: The IfcProduct that was constructed as a result + of the task. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcProcess (typically IfcTask) of the + construction task. + :type related_object: ifcopenshell.entity_instance + :return: The newly created IfcRelAssignsToProduct relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", model, relating_product=wall, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - if self.settings["related_object"].HasAssignments: - for assignment in self.settings["related_object"].HasAssignments: - if ( - assignment.is_a("IfcRelAssignsToProduct") - and assignment.RelatingProduct == self.settings["relating_product"] - ): - return + if settings["related_object"].HasAssignments: + for assignment in settings["related_object"].HasAssignments: + if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]: + return - referenced_by = None - if self.settings["relating_product"].ReferencedBy: - referenced_by = self.settings["relating_product"].ReferencedBy[0] + referenced_by = None + if settings["relating_product"].ReferencedBy: + referenced_by = settings["relating_product"].ReferencedBy[0] - if referenced_by: - related_objects = list(referenced_by.RelatedObjects) - related_objects.append(self.settings["related_object"]) - referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": referenced_by} - ) - else: - referenced_by = self.file.create_entity( - "IfcRelAssignsToProduct", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatedObjects": [self.settings["related_object"]], - "RelatingProduct": self.settings["relating_product"], - } - ) - return referenced_by + if referenced_by: + related_objects = list(referenced_by.RelatedObjects) + related_objects.append(settings["related_object"]) + referenced_by.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": referenced_by}) + else: + referenced_by = file.create_entity( + "IfcRelAssignsToProduct", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["related_object"]], + "RelatingProduct": settings["relating_product"], + } + ) + return referenced_by diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py index a3243f1057..177d90fb8d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py @@ -17,112 +17,101 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, parent=None, recurrence_type="WEEKLY"): - """Define a time to recur at a particular interval +def assign_recurrence_pattern(file, parent=None, recurrence_type="WEEKLY") -> None: + """Define a time to recur at a particular interval - There are two scenarios where you might want to define a recurring time - pattern. + There are two scenarios where you might want to define a recurring time + pattern. - You might want a task to be scheduled at a recurring interval, - this is common for maintenance tasks which need to be performed monthly, - every 6 months, every year, etc. + You might want a task to be scheduled at a recurring interval, + this is common for maintenance tasks which need to be performed monthly, + every 6 months, every year, etc. - Alternatively, you might be defining a work calendar, which defines - working days or holidays. The working days might be every week from - monday to friday ("every" week means it recurs every week), or the - holidays might be the same every year. + Alternatively, you might be defining a work calendar, which defines + working days or holidays. The working days might be every week from + monday to friday ("every" week means it recurs every week), or the + holidays might be the same every year. - The types of recurrence are: + The types of recurrence are: - - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences. - e.g. Every day, every 2 days, every day up to 5 times, etc - - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X - (Occurrences) occurrences. e.g. Every Monday, every weekday, every - other saturday, etc - - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth - (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of - the Month. - - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent) - of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g. - Every second Tuesday of the Month. - - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND - (MonthComponent) month of every Yth (Interval) Year up to Z - (Occurrences) occurrences. e.g. every 25th of December. - - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of - every JFMAMJJASOND (MonthComponent) month of every Yth (Interval) - Year up to Z (Occurrences) occurrences. e.g. every third Wednesday - of January. + - DAILY: every Nth (interval) day for up to X (Occurrences) occurrences. + e.g. Every day, every 2 days, every day up to 5 times, etc + - WEEKLY: every Nth (interval) MTWTFSS (WeekdayComponent) for up to X + (Occurrences) occurrences. e.g. Every Monday, every weekday, every + other saturday, etc + - MONTHLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every Xth + (Interval) Month up to Y (Occurrences) occurrences. e.g. Every 15th of + the Month. + - MONTHLY_BY_POSITION: Every Nth (Position) MTWTFSS (WeekdayComponent) + of every Xth (Interval) Month up to Y (Occurrences) occurrences. e.g. + Every second Tuesday of the Month. + - YEARLY_BY_DAY_OF_MONTH: every Nth (DayComponent) of every JFMAMJJASOND + (MonthComponent) month of every Yth (Interval) Year up to Z + (Occurrences) occurrences. e.g. every 25th of December. + - YEARLY_BY_POSITION: every Nth (Position) MTWTFSS (WeekdayComponent) of + every JFMAMJJASOND (MonthComponent) month of every Yth (Interval) + Year up to Z (Occurrences) occurrences. e.g. every third Wednesday + of January. - These recurrence patterns are fairly standard in all calendar and - scheduling applications. + These recurrence patterns are fairly standard in all calendar and + scheduling applications. - :param parent: Either an IfcTaskTimeRecurring if you are defining a - recurring schedule for a task, or IfcWorkTime if you are defining a - recurring pattern for a workdays or holidays in a calendar. - :type parent: ifcopenshell.entity_instance - :param recurrence_type: One of the types of recurrences. - :type recurrence_type: str - :return: The newly created IfcRecurrencePattern - :rtype: ifcopenshell.entity_instance + :param parent: Either an IfcTaskTimeRecurring if you are defining a + recurring schedule for a task, or IfcWorkTime if you are defining a + recurring pattern for a workdays or holidays in a calendar. + :type parent: ifcopenshell.entity_instance + :param recurrence_type: One of the types of recurrences. + :type recurrence_type: str + :return: The newly created IfcRecurrencePattern + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # Let's imagine we are creating a maintenance schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance") + # Let's imagine we are creating a maintenance schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Equipment Maintenance") - # Now let's imagine we have a task to maintain the chillers - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Chiller maintenance") + # Now let's imagine we have a task to maintain the chillers + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Chiller maintenance") - # Because it is a maintenance task, we must schedule a recurring time - time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True) + # Because it is a maintenance task, we must schedule a recurring time + time = ifcopenshell.api.run("sequence.add_task_time", model, task=task, is_recurring=True) - # We create a monthly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH") + # We create a monthly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="MONTHLY_BY_DAY_OF_MONTH") - # Specifically, the maintenance task must occur every 6 months - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6}) - """ - self.file = file - self.settings = {"parent": parent, "recurrence_type": recurrence_type} + # Specifically, the maintenance task must occur every 6 months + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"DayComponent": [1], "Interval": 6}) + """ + settings = {"parent": parent, "recurrence_type": recurrence_type} - def execute(self): - recurrence = self.file.createIfcRecurrencePattern( - self.settings["recurrence_type"] - ) + recurrence = file.createIfcRecurrencePattern(settings["recurrence_type"]) - if self.settings["parent"].is_a("IfcWorkTime"): - if ( - self.settings["parent"].RecurrencePattern - and len( - self.file.get_inverse(self.settings["parent"].RecurrencePattern) - ) - == 1 - ): - self.file.remove(self.settings["parent"].RecurrencePattern) - self.settings["parent"].RecurrencePattern = recurrence - elif self.settings["parent"].is_a("IfcTaskTimeRecurring"): - if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1: - self.file.remove(self.settings["parent"].Recurrence) - self.settings["parent"].Recurrence = recurrence - return recurrence + if settings["parent"].is_a("IfcWorkTime"): + if settings["parent"].RecurrencePattern and len(file.get_inverse(settings["parent"].RecurrencePattern)) == 1: + file.remove(settings["parent"].RecurrencePattern) + settings["parent"].RecurrencePattern = recurrence + elif settings["parent"].is_a("IfcTaskTimeRecurring"): + if len(file.get_inverse(settings["parent"].Recurrence)) == 1: + file.remove(settings["parent"].Recurrence) + settings["parent"].Recurrence = recurrence + return recurrence diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py index e1c1100760..ed5103758c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_sequence.py @@ -20,119 +20,111 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__( - self, - file, - relating_process=None, - related_process=None, - sequence_type="FINISH_START", - ): - """Assign a sequential relationship between tasks +def assign_sequence( + file, + relating_process=None, + related_process=None, + sequence_type="FINISH_START", +) -> None: + """Assign a sequential relationship between tasks - Tasks in construction sequencing typically have sequence relationships - between them, indicating that one task must happen after another. This - is used to automatically compute new start and end dates and cascade - changes when dates are changed. This is also used to calculate critical - paths and floats. + Tasks in construction sequencing typically have sequence relationships + between them, indicating that one task must happen after another. This + is used to automatically compute new start and end dates and cascade + changes when dates are changed. This is also used to calculate critical + paths and floats. - There are four types of sequence relationships, known as finish to - start, finish to finish, start to start, and start to finish, sometimes - abbreviated as a (FS, FF, SS, and SF). The most common is the finish to - start relationship, indicating that the previous task must finish before - the next task can start. + There are four types of sequence relationships, known as finish to + start, finish to finish, start to start, and start to finish, sometimes + abbreviated as a (FS, FF, SS, and SF). The most common is the finish to + start relationship, indicating that the previous task must finish before + the next task can start. - You must not create cyclical task sequences. This makes the computer - unhappy. + You must not create cyclical task sequences. This makes the computer + unhappy. - Note that "previous" or "next" does not necessarily mean the task - chronologically happens before or after. They simply indicate the order - of the sequence relationship. For this reason, they are often called - predecessor and successor tasks in the planning profession. + Note that "previous" or "next" does not necessarily mean the task + chronologically happens before or after. They simply indicate the order + of the sequence relationship. For this reason, they are often called + predecessor and successor tasks in the planning profession. - :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance - :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance - :param sequence_type: Choose from FINISH_START, FINISH_FINISH, - START_START, or START_FINISH. - :return: The newly created IfcRelSequence - :rtype: ifcopenshell.entity_instance + :param relating_process: The previous / predecessor task. + :type relating_process: ifcopenshell.entity_instance + :param related_process: The next / successor task. + :type related_process: ifcopenshell.entity_instance + :param sequence_type: Choose from FINISH_START, FINISH_FINISH, + START_START, or START_FINISH. + :return: The newly created IfcRelSequence + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now the pour itself. It'll only take 1 day. - pour = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.3") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"}) + # Now the pour it It'll only take 1 day. + pour = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.3") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=pour) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P1D"}) - # Now let's say the formwork must finish before the reinforcement - # can start, and the reinforcement must finish before the pour can - # start. This is a typical finish to start relationship (FS). - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=reinforcement, related_process=pour) + # Now let's say the formwork must finish before the reinforcement + # can start, and the reinforcement must finish before the pour can + # start. This is a typical finish to start relationship (FS). + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=reinforcement, related_process=pour) - # Notice how we set all the scheduled start dates arbitrarily at - # 2000-01-01. This is because we can ask IfcOpenShell to - # automatically cascade the dates, starting from any task. This will - # update the reinforcement date to be 2000-01-03 and the pour date - # to be 2000-01-05. - ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_process": related_process, - "sequence_type": sequence_type, + # Notice how we set all the scheduled start dates arbitrarily at + # 2000-01-01. This is because we can ask IfcOpenShell to + # automatically cascade the dates, starting from any task. This will + # update the reinforcement date to be 2000-01-03 and the pour date + # to be 2000-01-05. + ifcopenshell.api.run("sequence.cascade_schedule", model, task=formwork) + """ + settings = { + "relating_process": relating_process, + "related_process": related_process, + "sequence_type": sequence_type, + } + + for rel in settings["related_process"].IsSuccessorFrom or []: + if rel.RelatingProcess == settings["relating_process"]: + return rel + rel = file.create_entity( + "IfcRelSequence", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatingProcess": settings["relating_process"], + "RelatedProcess": settings["related_process"], + "SequenceType": settings["sequence_type"], } - - def execute(self): - for rel in self.settings["related_process"].IsSuccessorFrom or []: - if rel.RelatingProcess == self.settings["relating_process"]: - return rel - rel = self.file.create_entity( - "IfcRelSequence", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), - "RelatingProcess": self.settings["relating_process"], - "RelatedProcess": self.settings["related_process"], - "SequenceType": self.settings["sequence_type"], - } - ) - ifcopenshell.api.run( - "sequence.cascade_schedule", self.file, task=self.settings["relating_process"] - ) - return rel + ) + ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["relating_process"]) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index a1eaed71be..634f2af494 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -20,52 +20,49 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, work_schedule=None, work_plan=None): - """Assigns a work schedule to a work plan +def assign_workplan(file, work_schedule=None, work_plan=None) -> None: + """Assigns a work schedule to a work plan - Typically, work schedules would be assigned to a work plan at creation. - However you may also delay this and do it manually afterwards. + Typically, work schedules would be assigned to a work plan at creation. + However you may also delay this and do it manually afterwards. - :param work_schedule: The IfcWorkSchedule that will be assigned to the - work plan. - :type work_schedule: ifcopenshell.entity_instance - :param work_plan: The IfcWorkPlan for the schedule to be assigned to. - :type work_plan: ifcopenshell.entity_instance - :return: The IfcRelAggregates relationship - :rtype: ifcopenshell.entity_instance + :param work_schedule: The IfcWorkSchedule that will be assigned to the + work plan. + :type work_schedule: ifcopenshell.entity_instance + :param work_plan: The IfcWorkPlan for the schedule to be assigned to. + :type work_plan: ifcopenshell.entity_instance + :return: The IfcRelAggregates relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Alternatively, if you create a schedule without a work plan ... - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Alternatively, if you create a schedule without a work plan ... + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # ... you can assign the work plan afterwards. - ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan) - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "work_plan": work_plan} + # ... you can assign the work plan afterwards. + ifcopenshell.api.run("sequence.assign_workplan", work_schedule=schedule, work_plan=work_plan) + """ + settings = {"work_schedule": work_schedule, "work_plan": work_plan} - def execute(self): - # TODO: this is an ambiguity by buildingSMART - # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_schedule"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - rel_aggregates = ifcopenshell.api.run( - "aggregate.assign_object", - self.file, - **{ - "products": [self.settings["work_schedule"]], - "relating_object": self.settings["work_plan"], - } - ) - return rel_aggregates + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_schedule"]], + relating_context=file.by_type("IfcContext")[0], + ) + rel_aggregates = ifcopenshell.api.run( + "aggregate.assign_object", + file, + **{ + "products": [settings["work_schedule"]], + "relating_object": settings["work_plan"], + } + ) + return rel_aggregates diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py index 6698ab85a2..22c9ec7dda 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/calculate_task_duration.py @@ -22,68 +22,71 @@ import ifcopenshell.util.date import ifcopenshell.util.element +def calculate_task_duration(file, task=None) -> None: + """Calculates the task duration based on resource usage + + If a task has labour or equipment resources assigned to it, its duration + may be parametrically derived from the scheduled work of the resource. + For example, a labour resource with scheduled work of 10 working days + and a resource utilisation of 200% (i.e. two labour teams) will imply + that the task duration is 5 working days. + + If this data is not available, such as if the task has no resources, + then nothing happens. + + :param task: The IfcTask to calculate the duration for. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Add our own crew + crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") + + # Add some labour to our crew. + labour = ifcopenshell.api.run("resource.add_resource", model, + parent_resource=crew, ifc_class="IfcLaborResource") + + # Labour resource is quantified in terms of time. + quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, + resource=labour, ifc_class="IfcQuantityTime") + + # Store the unit time used in hours + ifcopenshell.api.run("resource.edit_resource_quantity", model, + physical_quantity=quantity, attributes={"TimeValue": 8.0}) + + # Let's imagine we've used the resource for 10 days with a + # utilisation of 200%. + time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) + ifcopenshell.api.run("resource.edit_resource_time", model, + resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2}) + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Foundations", identification="A") + + # Assign our resource to the task. + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour) + + # Now we can calculate the task duration based on the resource. This + # will set task.TaskTime.ScheduleDuration to be P5D. + ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Calculates the task duration based on resource usage - - If a task has labour or equipment resources assigned to it, its duration - may be parametrically derived from the scheduled work of the resource. - For example, a labour resource with scheduled work of 10 working days - and a resource utilisation of 200% (i.e. two labour teams) will imply - that the task duration is 5 working days. - - If this data is not available, such as if the task has no resources, - then nothing happens. - - :param task: The IfcTask to calculate the duration for. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Add our own crew - crew = ifcopenshell.api.run("resource.add_resource", model, ifc_class="IfcCrewResource") - - # Add some labour to our crew. - labour = ifcopenshell.api.run("resource.add_resource", model, - parent_resource=crew, ifc_class="IfcLaborResource") - - # Labour resource is quantified in terms of time. - quantity = ifcopenshell.api.run("resource.add_resource_quantity", model, - resource=labour, ifc_class="IfcQuantityTime") - - # Store the unit time used in hours - ifcopenshell.api.run("resource.edit_resource_quantity", model, - physical_quantity=quantity, attributes={"TimeValue": 8.0}) - - # Let's imagine we've used the resource for 10 days with a - # utilisation of 200%. - time = ifcopenshell.api.run("resource.add_resource_time", model, resource=labour) - ifcopenshell.api.run("resource.edit_resource_time", model, - resource_time=time, attributes={"ScheduleWork": "PT80H", "ScheduleUsage": 2}) - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Foundations", identification="A") - - # Assign our resource to the task. - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=labour) - - # Now we can calculate the task duration based on the resource. This - # will set task.TaskTime.ScheduleDuration to be P5D. - ifcopenshell.api.run("sequence.calculate_task_duration", model, task=task) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.seconds_per_workday = self.calculate_seconds_per_workday() duration = self.calculate_max_resource_usage_duration() @@ -93,9 +96,7 @@ class Usecase: def calculate_seconds_per_workday(self): def get_work_schedule(task): for rel in task.HasAssignments or []: - if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a( - "IfcWorkSchedule" - ): + if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"): return rel.RelatingControl for rel in task.Nests or []: return get_work_schedule(rel.RelatingObject) @@ -111,9 +112,7 @@ class Usecase: or "WorkDayDuration" not in psets["Pset_WorkControlCommon"] ): return default_seconds_per_workday - work_day_duration = ifcopenshell.util.date.ifc2datetime( - psets["Pset_WorkControlCommon"]["WorkDayDuration"] - ) + work_day_duration = ifcopenshell.util.date.ifc2datetime(psets["Pset_WorkControlCommon"]["WorkDayDuration"]) return work_day_duration.seconds def calculate_max_resource_usage_duration(self): @@ -133,23 +132,15 @@ class Usecase: if not resource.Usage or not resource.Usage.ScheduleWork: return schedule_usage = resource.Usage.ScheduleUsage or 1 - schedule_duration = ifcopenshell.util.date.ifc2datetime( - resource.Usage.ScheduleWork - ) + schedule_duration = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork) if is_hourly_work(resource.Usage.ScheduleWork): - schedule_seconds = ( - schedule_duration.days * 24 * 60 * 60 - ) + schedule_duration.seconds + schedule_seconds = (schedule_duration.days * 24 * 60 * 60) + schedule_duration.seconds else: partial_days = schedule_duration.seconds / (24 * 60 * 60) - schedule_seconds = ( - schedule_duration.days + partial_days - ) * self.seconds_per_workday + schedule_seconds = (schedule_duration.days + partial_days) * self.seconds_per_workday return math.ceil((schedule_seconds / self.seconds_per_workday) / schedule_usage) def set_task_duration(self, duration): if not self.settings["task"].TaskTime: - ifcopenshell.api.run( - "sequence.add_task_time", self.file, task=self.settings["task"] - ) + ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.settings["task"]) self.settings["task"].TaskTime.ScheduleDuration = f"P{duration}D" diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py index 2a720b9fa1..0a2aea7190 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py @@ -21,90 +21,93 @@ import ifcopenshell.util.date import ifcopenshell.util.sequence +def cascade_schedule(file, task=None) -> None: + """Cascades start and end dates of tasks based on durations + + Given a start task with a start date and duration, the end date, and the + start and end of all successor tasks with durations may be automatically + computed. + + Using this automatic computation is recommended is an alternative to + manually specifying dates. It is useful for doing edits and cascading + changes. + + Dates can only cascade from predecessor to successors, not backwards. + Cyclical relationships are invalid and will result in a recursion error + being raised. + + Note that there may be differences between how different planning + software calculate start and end dates. Some may consider Monday 5pm to + be equivalent to be Tuesday 8am, for instance. + + :param task: The start task to begin cascading from. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Define a convenience function to add a task chained to a predecessor + def add_task(model, name, predecessor, work_schedule): + # Add a construction task + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION") + + # Give it a time + task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) + + # Arbitrarily set the task's scheduled time duration to be 1 week + ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time, + attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"}) + + # If a predecessor exists, create a finish to start relationship + if predecessor: + ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=predecessor, related_process=task) + + return task + + # Open an existing IFC4 model you have of a building + model = ifcopenshell.open("/path/to/existing/model.ifc") + + # Create a new construction schedule + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction") + + # Let's imagine a starting task for site establishment. + task = add_task(model, "Site establishment", None, schedule) + start_task = task + + # Get all our storeys sorted by elevation ascending. + storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s)) + + # For each storey ... + for storey in storeys: + + # Add a construction task to construct that storey, using our convenience function + task = add_task(model, f"Construct {storey.Name}", task, schedule) + + # Assign all the products in that storey to the task as construction outputs. + for product in get_decomposition(storey): + ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task) + + # Ask the computer to calculate all the dates for us from the start task. + # For example, if the first task started on the 1st of January and took a + # week, the next task will start on the 8th of January. This saves us + # manually doing date calculations. + ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task) + + # Calculate the critical path and floats. + ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Cascades start and end dates of tasks based on durations - - Given a start task with a start date and duration, the end date, and the - start and end of all successor tasks with durations may be automatically - computed. - - Using this automatic computation is recommended is an alternative to - manually specifying dates. It is useful for doing edits and cascading - changes. - - Dates can only cascade from predecessor to successors, not backwards. - Cyclical relationships are invalid and will result in a recursion error - being raised. - - Note that there may be differences between how different planning - software calculate start and end dates. Some may consider Monday 5pm to - be equivalent to be Tuesday 8am, for instance. - - :param task: The start task to begin cascading from. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Define a convenience function to add a task chained to a predecessor - def add_task(model, name, predecessor, work_schedule): - # Add a construction task - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=work_schedule, name=name, predefined_type="CONSTRUCTION") - - # Give it a time - task_time = ifcopenshell.api.run("sequence.add_task_time", model, task=task) - - # Arbitrarily set the task's scheduled time duration to be 1 week - ifcopenshell.api.run("sequence.edit_task_time", model, task_time=task_time, - attributes={"ScheduleStart": datetime.date(2000, 1, 1), "ScheduleDuration": "P1W"}) - - # If a predecessor exists, create a finish to start relationship - if predecessor: - ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=predecessor, related_process=task) - - return task - - # Open an existing IFC4 model you have of a building - model = ifcopenshell.open("/path/to/existing/model.ifc") - - # Create a new construction schedule - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction") - - # Let's imagine a starting task for site establishment. - task = add_task(model, "Site establishment", None, schedule) - start_task = task - - # Get all our storeys sorted by elevation ascending. - storeys = sorted(model.by_type("IfcBuildingStorey"), key=lambda s: get_storey_elevation(s)) - - # For each storey ... - for storey in storeys: - - # Add a construction task to construct that storey, using our convenience function - task = add_task(model, f"Construct {storey.Name}", task, schedule) - - # Assign all the products in that storey to the task as construction outputs. - for product in get_decomposition(storey): - ifcopenshell.api.run("sequence.assign_product", model, relating_product=product, related_object=task) - - # Ask the computer to calculate all the dates for us from the start task. - # For example, if the first task started on the 1st of January and took a - # week, the next task will start on the 8th of January. This saves us - # manually doing date calculations. - ifcopenshell.api.run("sequence.cascade_schedule", model, task=start_task) - - # Calculate the critical path and floats. - ifcopenshell.api.run("sequence.recalculate_schedule", model, work_schedule=schedule) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.calendar_cache = {} self.cascade_task(self.settings["task"], is_first_task=True) @@ -135,14 +138,10 @@ class Usecase: finishes = [] starts = [] - for rel in ifcopenshell.util.sequence.get_sequence_assignment( - task, "predecessor" - ): + for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor"): predecessor = rel.RelatingProcess predecessor_duration = ( - ifcopenshell.util.date.ifc2datetime( - predecessor.TaskTime.ScheduleDuration - ) + ifcopenshell.util.date.ifc2datetime(predecessor.TaskTime.ScheduleDuration) if predecessor.TaskTime and predecessor.TaskTime.ScheduleDuration else datetime.timedelta() ) @@ -154,14 +153,16 @@ class Usecase: duration_type = "WORKTIME" if rel.TimeLag: # updated to handle IfcRatioMeasure as a TimeLag value - days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days += ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType if days: starts.append( datetime.datetime.combine( - self.offset_date( - finish, days, duration_type, self.get_calendar(task) - ), + self.offset_date(finish, days, duration_type, self.get_calendar(task)), datetime.time(9), ) ) @@ -183,18 +184,14 @@ class Usecase: if not start: continue if rel.TimeLag: - days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days = ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType - starts.append( - self.offset_date( - start, days, duration_type, self.get_calendar(task) - ) - ) - starts.append( - self.offset_date( - start, days, duration_type, self.get_calendar(predecessor) - ) - ) + starts.append(self.offset_date(start, days, duration_type, self.get_calendar(task))) + starts.append(self.offset_date(start, days, duration_type, self.get_calendar(predecessor))) else: starts.append(start) elif rel.SequenceType == "FINISH_FINISH": @@ -202,18 +199,14 @@ class Usecase: if not finish: continue if rel.TimeLag: - days = self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days = ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType - finishes.append( - self.offset_date( - finish, days, duration_type, self.get_calendar(task) - ) - ) - finishes.append( - self.offset_date( - finish, days, duration_type, self.get_calendar(predecessor) - ) - ) + finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(task))) + finishes.append(self.offset_date(finish, days, duration_type, self.get_calendar(predecessor))) else: finishes.append(finish) elif rel.SequenceType == "START_FINISH": @@ -223,14 +216,16 @@ class Usecase: days = -1 duration_type = "WORKTIME" if rel.TimeLag: - days += self.get_lag_time_days(rel.TimeLag) if rel.TimeLag.LagValue.is_a("IfcDuration") else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + days += ( + self.get_lag_time_days(rel.TimeLag) + if rel.TimeLag.LagValue.is_a("IfcDuration") + else predecessor_duration.days * rel.TimeLag.LagValue.wrappedValue + ) duration_type = rel.TimeLag.DurationType if days or rel.TimeLag: finishes.append( datetime.datetime.combine( - self.offset_date( - start, days, duration_type, self.get_calendar(task) - ), + self.offset_date(start, days, duration_type, self.get_calendar(task)), datetime.time(17), ) ) @@ -263,9 +258,7 @@ class Usecase: if task.TaskTime.ScheduleStart == start_ifc and not is_first_task: return task.TaskTime.ScheduleStart = start_ifc - task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc( - potential_finish, "IfcDateTime" - ) + task.TaskTime.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(potential_finish, "IfcDateTime") else: finish_ifc = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime") if task.TaskTime.ScheduleFinish == finish_ifc and not is_first_task: @@ -328,15 +321,11 @@ class Usecase: def get_calendar(self, task): if task.id() not in self.calendar_cache: - self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar( - task - ) + self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(task) return self.calendar_cache[task.id()] def offset_date(self, date, days, duration_type, calendar): - return ifcopenshell.util.sequence.offset_date( - date, datetime.timedelta(days=days), duration_type, calendar - ) + return ifcopenshell.util.sequence.offset_date(date, datetime.timedelta(days=days), duration_type, calendar) def get_task_time_attribute(self, task, attribute): if task.TaskTime: diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py index c0ebe4f72f..a06fe8d722 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/create_baseline.py @@ -21,38 +21,41 @@ import ifcopenshell.util.system import ifcopenshell.util.element +def create_baseline(file, work_schedule=None, name=None) -> None: + """Creates a baseline for your Work Schedule + + Using a IfcWorkSchdule having PredefinedType=PLANNED, + We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE + and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline. + + The following relationships are also baselined: + + * Same Tasks & attributes + * Same Task Relationships + * Same Construction Resources + * Same Resource Relationships + + :param work_schedule: The planned work_schedule to baseline + :type work_schedule: ifcopenshell.entity_instance + :return: The baseline work_schedule + :rtype: ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a Work Schedule + planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01") + + # And now we have a baseline for our Work Schedule + baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1") + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"work_schedule": work_schedule, "name": name} + return usecase.execute() + + class Usecase: - def __init__(self, file, work_schedule=None, name=None): - """Creates a baseline for your Work Schedule - - Using a IfcWorkSchdule having PredefinedType=PLANNED, - We can create a baseline for our work schedule. This IfcWorkSchedule will have PredefinedType=BASELINE - and the IfcWorkSchedule.CreationDate indicating the date of the baseline creation, and IfcWorkSchedule.Name indicating the name of the baseline. - - The following relationships are also baselined: - - * Same Tasks & attributes - * Same Task Relationships - * Same Construction Resources - * Same Resource Relationships - - :param work_schedule: The planned work_schedule to baseline - :type work_schedule: ifcopenshell.entity_instance - :return: The baseline work_schedule - :rtype: ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a Work Schedule - planned_work_schedule = WorkSchedule(name="Design new feature",predefinedType="PLANNED", deadline="2023-03-01") - - # And now we have a baseline for our Work Schedule - baseline_work_schedule = ifcopenshell.api.run("sequence.create_baseline",file, work_schedule= planned_work_schedule, name="Baseline 1") - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "name": name} - def execute(self): result = self.create_baseline_work_schedule(self.settings["work_schedule"]) return result @@ -92,17 +95,13 @@ class Usecase: related_objects = list(referenced_by.RelatedObjects) related_objects.append(related_object) referenced_by.RelatedObjects = related_objects - ifcopenshell.api.run( - "owner.update_owner_history", self.file, **{"element": referenced_by} - ) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": referenced_by}) else: referenced_by = self.file.create_entity( "IfcRelDefinesByObject", **{ "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run( - "owner.create_owner_history", self.file - ), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "RelatedObjects": [related_object], "RelatingObject": relating_object, } diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py index 91d5ed1b99..14016794cd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py @@ -22,33 +22,36 @@ import ifcopenshell.util.element import ifcopenshell.util.sequence +def duplicate_task(file, task=None) -> None: + """Duplicates a task in the project + + The following relationships are also duplicated: + + * The copy will have the same attributes and property sets as the original task + * The copy will be assigned to the parent task or work schedule + * The copy will have duplicated nested tasks + + :param task: The task to be duplicated + :type task: ifcopenshell.entity_instance + :return: The duplicated task or the list of duplicated tasks if the latter has children + :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance + + Example: + .. code:: python + + # We have a task + original_task = Task(name="Design new feature", deadline="2023-03-01") + + # And now we have two + duplicated_task = project.duplicate_task(original_task) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task": task} + return usecase.execute() + + class Usecase: - def __init__(self, file, task=None): - """Duplicates a task in the project - - The following relationships are also duplicated: - - * The copy will have the same attributes and property sets as the original task - * The copy will be assigned to the parent task or work schedule - * The copy will have duplicated nested tasks - - :param task: The task to be duplicated - :type task: ifcopenshell.entity_instance - :return: The duplicated task or the list of duplicated tasks if the latter has children - :rtype: ifcopenshell.entity_instance or list of ifcopenshell.entity_instance - - Example: - .. code:: python - - # We have a task - original_task = Task(name="Design new feature", deadline="2023-03-01") - - # And now we have two - duplicated_task = project.duplicate_task(original_task) - """ - self.file = file - self.settings = {"task": task} - def execute(self): self.tracker = {"current": [], "duplicate": []} self.duplicate_task(self.settings["task"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py index f5779b058c..4b77daf9e8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_lag_time.py @@ -20,79 +20,68 @@ import ifcopenshell.api import ifcopenshell.util.date -class Usecase: - def __init__(self, file, lag_time=None, attributes=None): - """Edits the attributes of an IfcLagTime +def edit_lag_time(file, lag_time=None, attributes=None) -> None: + """Edits the attributes of an IfcLagTime - For more information about the attributes and data types of an - IfcLagTime, consult the IFC documentation. + For more information about the attributes and data types of an + IfcLagTime, consult the IFC documentation. - :param lag_time: The IfcLagTime entity you want to edit - :type lag_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param lag_time: The IfcLagTime entity you want to edit + :type lag_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're doing a typically formwork, reinforcement, - # pour sequence. Let's start with the formwork. It'll take us 2 - # days. - formwork = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Formwork", identification="C.1") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Let's imagine we're doing a typically formwork, reinforcement, + # pour sequence. Let's start with the formwork. It'll take us 2 + # days. + formwork = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Formwork", identification="C.1") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's do the reinforcement. It'll take us another 2 days. - reinforcement = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Reinforcement", identification="C.2") - time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + # Now let's do the reinforcement. It'll take us another 2 days. + reinforcement = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Reinforcement", identification="C.2") + time = ifcopenshell.api.run("sequence.add_task_time", model, task=reinforcement) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - # Now let's say the formwork must finish before the reinforcement - # can start. This is a typical finish to start relationship (FS). - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=formwork, related_process=reinforcement) + # Now let's say the formwork must finish before the reinforcement + # can start. This is a typical finish to start relationship (FS). + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=formwork, related_process=reinforcement) - # Now typically there would be no lag time between formwork and - # reinforcement, but let's pretend that we had to allow 1 day gap - # for whatever reason. - lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") + # Now typically there would be no lag time between formwork and + # reinforcement, but let's pretend that we had to allow 1 day gap + # for whatever reason. + lag = ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1D") - # Or, let's make it 2 days instead. - ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"}) - """ - self.file = file - self.settings = {"lag_time": lag_time, "attributes": attributes or {}} + # Or, let's make it 2 days instead. + ifcopenshell.api.run("sequence.edit_lag_time", model, lag_time=lag, attributes={"LagValue": "P2D"}) + """ + settings = {"lag_time": lag_time, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "LagValue" and value is not None: - if isinstance(value, float): - value = self.file.createIfcRatioMeasure(value) - else: - value = self.file.createIfcDuration( - ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - ) - setattr(self.settings["lag_time"], name, value) - for rel in [ - r - for r in self.file.get_inverse(self.settings["lag_time"]) - if r.is_a("IfcRelSequence") - ]: - ifcopenshell.api.run( - "sequence.cascade_schedule", self.file, task=rel.RelatedProcess - ) + for name, value in settings["attributes"].items(): + if name == "LagValue" and value is not None: + if isinstance(value, float): + value = file.createIfcRatioMeasure(value) + else: + value = file.createIfcDuration(ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")) + setattr(settings["lag_time"], name, value) + for rel in [r for r in file.get_inverse(settings["lag_time"]) if r.is_a("IfcRelSequence")]: + ifcopenshell.api.run("sequence.cascade_schedule", file, task=rel.RelatedProcess) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py index 21292233ad..2863102b3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -20,48 +20,45 @@ import ifcopenshell import ifcopenshell.util.sequence -class Usecase: - def __init__(self, file, recurrence_pattern=None, attributes=None): - """Edits the attributes of an IfcRecurrencePattern +def edit_recurrence_pattern(file, recurrence_pattern=None, attributes=None) -> None: + """Edits the attributes of an IfcRecurrencePattern - For more information about the attributes and data types of an - IfcRecurrencePattern, consult the IFC documentation. + For more information about the attributes and data types of an + IfcRecurrencePattern, consult the IFC documentation. - :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit - :type recurrence_pattern: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param recurrence_pattern: The IfcRecurrencePattern entity you want to edit + :type recurrence_pattern: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - """ - self.file = file - self.settings = { - "recurrence_pattern": recurrence_pattern, - "attributes": attributes or {}, - } + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + """ + settings = { + "recurrence_pattern": recurrence_pattern, + "attributes": attributes or {}, + } - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["recurrence_pattern"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["recurrence_pattern"], name, value) - ifcopenshell.util.sequence.is_working_day.cache_clear() - ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() + ifcopenshell.util.sequence.is_working_day.cache_clear() + ifcopenshell.util.sequence.is_calendar_applicable.cache_clear() diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py index c563cb6990..bcbc521ef3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_sequence.py @@ -20,55 +20,52 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, rel_sequence=None, attributes=None): - """Edits the attributes of an IfcRelSequence +def edit_sequence(file, rel_sequence=None, attributes=None) -> None: + """Edits the attributes of an IfcRelSequence - For more information about the attributes and data types of an - IfcRelSequence, consult the IFC documentation. + For more information about the attributes and data types of an + IfcRelSequence, consult the IFC documentation. - :param rel_sequence: The IfcRelSequence entity you want to edit - :type rel_sequence: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param rel_sequence: The IfcRelSequence entity you want to edit + :type rel_sequence: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=zone1, related_process=zone2) - # What if they both started at the same time? - ifcopenshell.api.run("sequence.edit_sequence", model, - rel_sequence=sequence, attributes={"SequenceType": "START_START"}) - """ - self.file = file - self.settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}} + # What if they both started at the same time? + ifcopenshell.api.run("sequence.edit_sequence", model, + rel_sequence=sequence, attributes={"SequenceType": "START_START"}) + """ + settings = {"rel_sequence": rel_sequence, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["rel_sequence"], name, value) - if "SequenceType" in self.settings["attributes"].keys(): - ifcopenshell.api.run( - "sequence.cascade_schedule", - self.file, - task=self.settings["rel_sequence"].RelatedProcess, - ) + for name, value in settings["attributes"].items(): + setattr(settings["rel_sequence"], name, value) + if "SequenceType" in settings["attributes"].keys(): + ifcopenshell.api.run( + "sequence.cascade_schedule", + file, + task=settings["rel_sequence"].RelatedProcess, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py index cbdaa18de0..d151926a69 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task.py @@ -17,39 +17,36 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, task=None, attributes=None): - """Edits the attributes of an IfcTask +def edit_task(file, task=None, attributes=None) -> None: + """Edits the attributes of an IfcTask - For more information about the attributes and data types of an - IfcTask, consult the IFC documentation. + For more information about the attributes and data types of an + IfcTask, consult the IFC documentation. - :param task: The IfcTask entity you want to edit - :type task: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param task: The IfcTask entity you want to edit + :type task: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the design milestones, and major - # project phases. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") + # Add a root task to represent the design milestones, and major + # project phases. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") - # Change the identification - ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"}) - """ - self.file = file - self.settings = {"task": task, "attributes": attributes or {}} + # Change the identification + ifcopenshell.api.run("sequence.edit_task", model, task=task, attributes={"Identification": "M"}) + """ + settings = {"task": task, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["task"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["task"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py index 10bec7909c..f81b2b1f90 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py @@ -23,46 +23,48 @@ import ifcopenshell.util.sequence from typing import Any, Optional +def edit_task_time( + file: ifcopenshell.file, + task_time: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcTaskTime + + For more information about the attributes and data types of an + IfcTaskTime, consult the IFC documentation. + + :param task_time: The IfcTaskTime entity you want to edit + :type task_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + + # Create a task to do formwork + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Formwork", identification="A") + + # Let's say it takes 2 days and starts on the 1st of January, 2000 + time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) + ifcopenshell.api.run("sequence.edit_task_time", model, + task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"task_time": task_time, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - task_time: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcTaskTime - - For more information about the attributes and data types of an - IfcTaskTime, consult the IFC documentation. - - :param task_time: The IfcTaskTime entity you want to edit - :type task_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - - # Create a task to do formwork - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Formwork", identification="A") - - # Let's say it takes 2 days and starts on the 1st of January, 2000 - time = ifcopenshell.api.run("sequence.add_task_time", model, task=formwork) - ifcopenshell.api.run("sequence.edit_task_time", model, - task_time=time, attributes={"ScheduleStart": "2000-01-01", "ScheduleDuration": "P2D"}) - """ - self.file = file - self.settings = {"task_time": task_time, "attributes": attributes or {}} - - def execute(self) -> None: + def execute(self): self.task = self.get_task() self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task) @@ -73,17 +75,13 @@ class Usecase: ): del self.settings["attributes"]["ScheduleFinish"] - duration_type = self.settings["attributes"].get( - "DurationType", self.settings["task_time"].DurationType - ) + duration_type = self.settings["attributes"].get("DurationType", self.settings["task_time"].DurationType) finish = self.settings["attributes"].get("ScheduleFinish", None) if finish: if isinstance(finish, str): finish = datetime.datetime.fromisoformat(finish) self.settings["attributes"]["ScheduleFinish"] = datetime.datetime.combine( - ifcopenshell.util.sequence.get_soonest_working_day( - finish, duration_type, self.calendar - ), + ifcopenshell.util.sequence.get_soonest_working_day(finish, duration_type, self.calendar), datetime.time(17), ) start = self.settings["attributes"].get("ScheduleStart", None) @@ -91,9 +89,7 @@ class Usecase: if isinstance(start, str): start = datetime.datetime.fromisoformat(start) self.settings["attributes"]["ScheduleStart"] = datetime.datetime.combine( - ifcopenshell.util.sequence.get_soonest_working_day( - start, duration_type, self.calendar - ), + ifcopenshell.util.sequence.get_soonest_working_day(start, duration_type, self.calendar), datetime.time(9), ) @@ -101,11 +97,7 @@ class Usecase: if value is not None: if "Start" in name or "Finish" in name or name == "StatusTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif ( - name == "ScheduleDuration" - or name == "ActualDuration" - or name == "RemainingTime" - ): + elif name == "ScheduleDuration" or name == "ActualDuration" or name == "RemainingTime": value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") setattr(self.settings["task_time"], name, value) @@ -115,15 +107,9 @@ class Usecase: and self.settings["task_time"].ScheduleStart ): self.calculate_finish() - elif ( - self.settings["attributes"].get("ScheduleStart", None) - and self.settings["task_time"].ScheduleDuration - ): + elif self.settings["attributes"].get("ScheduleStart", None) and self.settings["task_time"].ScheduleDuration: self.calculate_finish() - elif ( - self.settings["attributes"].get("ScheduleFinish", None) - and self.settings["task_time"].ScheduleStart - ): + elif self.settings["attributes"].get("ScheduleFinish", None) and self.settings["task_time"].ScheduleStart: self.calculate_duration() if self.settings["task_time"].ScheduleDuration and ( @@ -137,57 +123,36 @@ class Usecase: def calculate_finish(self): finish = ifcopenshell.util.sequence.get_start_or_finish_date( - ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleStart - ), - ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleDuration - ), + ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart), + ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleDuration), self.settings["task_time"].DurationType, self.calendar, date_type="FINISH", ) - self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc( - finish, "IfcDateTime" - ) + self.settings["task_time"].ScheduleFinish = ifcopenshell.util.date.datetime2ifc(finish, "IfcDateTime") def calculate_duration(self): - start = ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleStart - ) - finish = ifcopenshell.util.date.ifc2datetime( - self.settings["task_time"].ScheduleFinish - ) + start = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleStart) + finish = ifcopenshell.util.date.ifc2datetime(self.settings["task_time"].ScheduleFinish) current_date = datetime.date(start.year, start.month, start.day) finish_date = datetime.date(finish.year, finish.month, finish.day) duration = datetime.timedelta(days=1) while current_date < finish_date: - if ( - self.settings["task_time"].DurationType == "ELAPSEDTIME" - or not self.calendar - ): + if self.settings["task_time"].DurationType == "ELAPSEDTIME" or not self.calendar: duration += datetime.timedelta(days=1) elif ifcopenshell.util.sequence.is_working_day(current_date, self.calendar): duration += datetime.timedelta(days=1) current_date += datetime.timedelta(days=1) - self.settings[ - "task_time" - ].ScheduleDuration = ifcopenshell.util.date.datetime2ifc( - duration, "IfcDuration" - ) + self.settings["task_time"].ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration, "IfcDuration") def get_task(self) -> ifcopenshell.entity_instance: - return next( - e - for e in self.file.get_inverse(self.settings["task_time"]) - if e.is_a("IfcTask") - ) + return next(e for e in self.file.get_inverse(self.settings["task_time"]) if e.is_a("IfcTask")) def handle_resource_calculation(self): resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False) for resource in resources: if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleWork"): ifcopenshell.api.run("resource.calculate_resource_usage", self.file, resource=resource) - #TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated. + # TODO: If the duration changes, this implies the productivity rate must change to accomModate the new Schedule Work to be calculated. # elif ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"): # ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=resource) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py index 12ce1e15d1..4efb35da84 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_calendar=None, attributes=None): - """Edits the attributes of an IfcWorkCalendar +def edit_work_calendar(file, work_calendar=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkCalendar - For more information about the attributes and data types of an - IfcWorkCalendar, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkCalendar, consult the IFC documentation. - :param work_calendar: The IfcWorkCalendar entity you want to edit - :type work_calendar: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_calendar: The IfcWorkCalendar entity you want to edit + :type work_calendar: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_calendar", model, - work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"}) - """ - self.file = file - self.settings = {"work_calendar": work_calendar, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_calendar", model, + work_calendar=calendar, attributes={"Description": "Monday to Friday 8 hour days"}) + """ + settings = {"work_calendar": work_calendar, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["work_calendar"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["work_calendar"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py index 669ef0193c..e2bbcae33f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py @@ -19,39 +19,36 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, work_plan=None, attributes=None): - """Edits the attributes of an IfcWorkPlan +def edit_work_plan(file, work_plan=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkPlan - For more information about the attributes and data types of an - IfcWorkPlan, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkPlan, consult the IFC documentation. - :param work_plan: The IfcWorkPlan entity you want to edit - :type work_plan: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_plan: The IfcWorkPlan entity you want to edit + :type work_plan: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_plan", model, - work_plan=work_plan, attributes={"Description": "Construction of phase 1"}) - """ - self.file = file - self.settings = {"work_plan": work_plan, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_plan", model, + work_plan=work_plan, attributes={"Description": "Construction of phase 1"}) + """ + settings = {"work_plan": work_plan, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if value: - if "Date" in name or "Time" in name: - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif name == "Duration" or name == "TotalFloat": - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["work_plan"], name, value) + for name, value in settings["attributes"].items(): + if value: + if "Date" in name or "Time" in name: + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") + elif name == "Duration" or name == "TotalFloat": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") + setattr(settings["work_plan"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py index cd7ca163b2..49e6b053ac 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py @@ -19,43 +19,40 @@ import ifcopenshell.util.date -class Usecase: - def __init__(self, file, work_schedule=None, attributes=None): - """Edits the attributes of an IfcWorkSchedule +def edit_work_schedule(file, work_schedule=None, attributes=None) -> None: + """Edits the attributes of an IfcWorkSchedule - For more information about the attributes and data types of an - IfcWorkSchedule, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkSchedule, consult the IFC documentation. - :param work_schedule: The IfcWorkSchedule entity you want to edit - :type work_schedule: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_schedule: The IfcWorkSchedule entity you want to edit + :type work_schedule: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) - # Let's give it a description - ifcopenshell.api.run("sequence.edit_work_schedule", model, - work_schedule=work_schedule, attributes={"Description": "3 crane design option"}) - """ - self.file = file - self.settings = {"work_schedule": work_schedule, "attributes": attributes or {}} + # Let's give it a description + ifcopenshell.api.run("sequence.edit_work_schedule", model, + work_schedule=work_schedule, attributes={"Description": "3 crane design option"}) + """ + settings = {"work_schedule": work_schedule, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if value: - if "Date" in name or "Time" in name: - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") - elif name == "Duration" or name == "TotalFloat": - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") - setattr(self.settings["work_schedule"], name, value) + for name, value in settings["attributes"].items(): + if value: + if "Date" in name or "Time" in name: + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") + elif name == "Duration" or name == "TotalFloat": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") + setattr(settings["work_schedule"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py index d62c3a5357..ac0a05dad0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -20,54 +20,50 @@ import ifcopenshell.util.date from typing import Any, Optional -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - work_time: ifcopenshell.entity_instance, - attributes: Optional[dict[str, Any]] = None, - ): - """Edits the attributes of an IfcWorkTime +def edit_work_time( + file: ifcopenshell.file, + work_time: ifcopenshell.entity_instance, + attributes: Optional[dict[str, Any]] = None, +) -> None: + """Edits the attributes of an IfcWorkTime - For more information about the attributes and data types of an - IfcWorkTime, consult the IFC documentation. + For more information about the attributes and data types of an + IfcWorkTime, consult the IFC documentation. - :param work_time: The IfcWorkTime entity you want to edit - :type work_time: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param work_time: The IfcWorkTime entity you want to edit + :type work_time: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # If we don't specify any recurring time periods in our work time, - # we need to specify a start and end date of the work time. It - # starts at 0:00 on the start date and 24:00 at the end date. - ifcopenshell.api.run("sequence.edit_work_time", model, - work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"}) - """ - self.file = file - self.settings = {"work_time": work_time, "attributes": attributes or {}} + # If we don't specify any recurring time periods in our work time, + # we need to specify a start and end date of the work time. It + # starts at 0:00 on the start date and 24:00 at the end date. + ifcopenshell.api.run("sequence.edit_work_time", model, + work_time=work_time, attributes={"StartDate": "2000-01-01", "FinishDate": "2000-01-02"}) + """ + settings = {"work_time": work_time, "attributes": attributes or {}} - def execute(self) -> None: - for name, value in self.settings["attributes"].items(): - if name in ("Start", "StartDate"): - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") - # 4 IfcWorktime Start - self.settings["work_time"][4] = value - elif name in ("Finish", "FinishDate"): - value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") - # 5 IfcWorktime Finish - self.settings["work_time"][5] = value - else: - setattr(self.settings["work_time"], name, value) + for name, value in settings["attributes"].items(): + if name in ("Start", "StartDate"): + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") + # 4 IfcWorktime Start + settings["work_time"][4] = value + elif name in ("Finish", "FinishDate"): + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") + # 5 IfcWorktime Finish + settings["work_time"][5] = value + else: + setattr(settings["work_time"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py index eb8af300d1..df402c31ed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/get_related_products.py @@ -19,61 +19,58 @@ import ifcopenshell -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Gets the related products being output by a task +def get_related_products(file, relating_product=None, related_object=None) -> None: + """Gets the related products being output by a task - This API function will be removed in the future and migrated to a - utility module. + This API function will be removed in the future and migrated to a + utility module. - :param relating_product: One of the products already output by the task. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcTask that you want to get all the related - products for. - :type related_object: ifcopenshell.entity_instance - :return: A set of IfcProducts output by the IfcTask. - :rtype: set[ifcopenshell.entity_instance] + :param relating_product: One of the products already output by the task. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcTask that you want to get all the related + products for. + :type related_object: ifcopenshell.entity_instance + :return: A set of IfcProducts output by the IfcTask. + :rtype: set[ifcopenshell.entity_instance] - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) - # This will give us a set with that wall in it. - products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # This will give us a set with that wall in it. + products = ifcopenshell.api.run("sequence.get_related_products", model, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - products = set() - related_object = None - if self.settings["related_object"]: - related_object = self.settings["related_object"] - elif self.settings["relating_product"]: - for reference in self.settings["relating_product"].ReferencedBy: - if reference.is_a("IfcRelAssignsToProduct"): - related_object = reference.RelatedObjects[0] - if related_object: - assignments = self.settings["related_object"].HasAssignments - for assignment in assignments: - if assignment.is_a("IfcRelAssignsToProduct"): - products.add(assignment.RelatingProduct.id()) - return products + products = set() + related_object = None + if settings["related_object"]: + related_object = settings["related_object"] + elif settings["relating_product"]: + for reference in settings["relating_product"].ReferencedBy: + if reference.is_a("IfcRelAssignsToProduct"): + related_object = reference.RelatedObjects[0] + if related_object: + assignments = settings["related_object"].HasAssignments + for assignment in assignments: + if assignment.is_a("IfcRelAssignsToProduct"): + products.add(assignment.RelatingProduct.id()) + return products diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py index da07337f7b..d54bf01579 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/recalculate_schedule.py @@ -23,35 +23,38 @@ import ifcopenshell.util.date import ifcopenshell.util.sequence +def recalculate_schedule(file, work_schedule=None) -> None: + """Calculate the critical path and floats for a work schedule + + This implements critical path analysis, using the forward pass and + backward pass method. When run, any tasks that have no float will be + marked as critical, and both the total and free floats will be + populated for all task times. + + Cyclical relationships are detected and will result in a recursion + error. + + :param work_schedule: The IfcWorkSchedule to perform the calculation on. + :type work_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # See the example for ifcopenshell.api.sequence.cascade_schedule for + # details of how to set up a basic set of tasks and calculate the + # critical path. Typically cascade_schedule is run prior to ensure + # that dates are correct. + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"work_schedule": work_schedule} + return usecase.execute() + + class Usecase: - def __init__(self, file, work_schedule=None): - """Calculate the critical path and floats for a work schedule - - This implements critical path analysis, using the forward pass and - backward pass method. When run, any tasks that have no float will be - marked as critical, and both the total and free floats will be - populated for all task times. - - Cyclical relationships are detected and will result in a recursion - error. - - :param work_schedule: The IfcWorkSchedule to perform the calculation on. - :type work_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # See the example for ifcopenshell.api.sequence.cascade_schedule for - # details of how to set up a basic set of tasks and calculate the - # critical path. Typically cascade_schedule is run prior to ensure - # that dates are correct. - """ - self.file = file - self.settings = {"work_schedule": work_schedule} - def execute(self): # The method implemented is the same as shown here: # https://www.youtube.com/watch?v=qTErIV6OqLg @@ -84,9 +87,7 @@ class Usecase: break # We have an infinite loop due to a cyclic graph if is_cyclic: - raise RecursionError( - "Task graph is cyclic and so critical path method cannot be performed." - ) + raise RecursionError("Task graph is cyclic and so critical path method cannot be performed.") return self.pending_nodes = set(self.g.nodes) @@ -112,9 +113,7 @@ class Usecase: self.g = nx.DiGraph() self.edges = [] self.g.add_node("start", duration=0, duration_type="ELAPSEDTIME", calendar=None) - self.g.add_node( - "finish", duration=0, duration_type="ELAPSEDTIME", calendar=None - ) + self.g.add_node("finish", duration=0, duration_type="ELAPSEDTIME", calendar=None) for rel in self.settings["work_schedule"].Controls: for related_object in rel.RelatedObjects: if not related_object.is_a("IfcTask"): @@ -129,9 +128,7 @@ class Usecase: return if task.TaskTime and task.TaskTime.ScheduleDuration: - duration = ifcopenshell.util.date.ifc2datetime( - task.TaskTime.ScheduleDuration - ).days + duration = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleDuration).days duration_type = task.TaskTime.DurationType else: duration = 0 @@ -150,11 +147,11 @@ class Usecase: rel.RelatingProcess.id(), task.id(), { - "lag_time": 0 - if not rel.TimeLag - else ifcopenshell.util.date.ifc2datetime( - rel.TimeLag.LagValue.wrappedValue - ).days, + "lag_time": ( + 0 + if not rel.TimeLag + else ifcopenshell.util.date.ifc2datetime(rel.TimeLag.LagValue.wrappedValue).days + ), "type": self.sequence_type_map[rel.SequenceType], }, ) @@ -162,16 +159,20 @@ class Usecase: ] ) - predecessor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor")] - successor_types = [rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor")] + predecessor_types = [ + rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "predecessor") + ] + successor_types = [ + rel.SequenceType for rel in ifcopenshell.util.sequence.get_sequence_assignment(task, "successor") + ] if not predecessor_types: self.edges.append(("start", task.id(), {"lag_time": 0, "type": "FS"})) if task.TaskTime and task.TaskTime.ScheduleStart: - self.start_dates.append( - ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) - ) - self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart) # we assume this task is constrained to start on this date + self.start_dates.append(ifcopenshell.util.date.ifc2datetime(task.TaskTime.ScheduleStart)) + self.g.nodes[task.id()]["early_start"] = ifcopenshell.util.date.ifc2datetime( + task.TaskTime.ScheduleStart + ) # we assume this task is constrained to start on this date if not successor_types: self.edges.append((task.id(), "finish", {"lag_time": 0, "type": "FF"})) @@ -188,25 +189,13 @@ class Usecase: self.file, task_time=task.TaskTime, attributes={ - "FreeFloat": ifcopenshell.util.date.datetime2ifc( - data["free_float"], "IfcDuration" - ), - "TotalFloat": ifcopenshell.util.date.datetime2ifc( - data["total_float"], "IfcDuration" - ), + "FreeFloat": ifcopenshell.util.date.datetime2ifc(data["free_float"], "IfcDuration"), + "TotalFloat": ifcopenshell.util.date.datetime2ifc(data["total_float"], "IfcDuration"), "IsCritical": data["total_float"].days == 0, - "EarlyStart": ifcopenshell.util.date.datetime2ifc( - data["early_start"], "IfcDateTime" - ), - "EarlyFinish": ifcopenshell.util.date.datetime2ifc( - data["early_finish"], "IfcDateTime" - ), - "LateStart": ifcopenshell.util.date.datetime2ifc( - data["late_start"], "IfcDateTime" - ), - "LateFinish": ifcopenshell.util.date.datetime2ifc( - data["late_finish"], "IfcDateTime" - ), + "EarlyStart": ifcopenshell.util.date.datetime2ifc(data["early_start"], "IfcDateTime"), + "EarlyFinish": ifcopenshell.util.date.datetime2ifc(data["early_finish"], "IfcDateTime"), + "LateStart": ifcopenshell.util.date.datetime2ifc(data["late_start"], "IfcDateTime"), + "LateFinish": ifcopenshell.util.date.datetime2ifc(data["late_finish"], "IfcDateTime"), }, ) @@ -246,11 +235,7 @@ class Usecase: if edge["lag_time"]: days += edge["lag_time"] if days: - starts.append( - datetime.datetime.combine( - self.offset_date(finish, days, data), datetime.time(9) - ) - ) + starts.append(datetime.datetime.combine(self.offset_date(finish, days, data), datetime.time(9))) starts.append( datetime.datetime.combine( self.offset_date(finish, days, predecessor_data), @@ -265,9 +250,7 @@ class Usecase: return if edge["lag_time"]: starts.append(self.offset_date(start, edge["lag_time"], data)) - starts.append( - self.offset_date(start, edge["lag_time"], predecessor_data) - ) + starts.append(self.offset_date(start, edge["lag_time"], predecessor_data)) else: starts.append(start) elif edge["type"] == "FF": @@ -275,12 +258,8 @@ class Usecase: if finish is None: return if edge["lag_time"]: - finishes.append( - self.offset_date(finish, edge["lag_time"], data) - ) - finishes.append( - self.offset_date(finish, edge["lag_time"], predecessor_data) - ) + finishes.append(self.offset_date(finish, edge["lag_time"], data)) + finishes.append(self.offset_date(finish, edge["lag_time"], predecessor_data)) else: finishes.append(finish) elif edge["type"] == "SF": @@ -292,9 +271,7 @@ class Usecase: days += edge["lag_time"] if days or edge["lag_time"]: finishes.append( - datetime.datetime.combine( - self.offset_date(start, days, data), datetime.time(17) - ) + datetime.datetime.combine(self.offset_date(start, days, data), datetime.time(17)) ) finishes.append( datetime.datetime.combine( @@ -317,9 +294,7 @@ class Usecase: if potential_finish > data["early_finish"]: data["early_finish"] = potential_finish else: - data[ - "early_start" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + data["early_start"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["early_finish"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -375,9 +350,7 @@ class Usecase: days += edge["lag_time"] if days or edge["lag_time"]: finishes.append( - datetime.datetime.combine( - self.offset_date(start, -days, data), datetime.time(17) - ) + datetime.datetime.combine(self.offset_date(start, -days, data), datetime.time(17)) ) finishes.append( datetime.datetime.combine( @@ -402,9 +375,7 @@ class Usecase: return if edge["lag_time"]: starts.append(self.offset_date(start, -edge["lag_time"], data)) - starts.append( - self.offset_date(start, -edge["lag_time"], successor_data) - ) + starts.append(self.offset_date(start, -edge["lag_time"], successor_data)) else: starts.append(start) free_floats.append( @@ -421,12 +392,8 @@ class Usecase: if finish is None: return if edge["lag_time"]: - finishes.append( - self.offset_date(finish, -edge["lag_time"], data) - ) - finishes.append( - self.offset_date(finish, -edge["lag_time"], successor_data) - ) + finishes.append(self.offset_date(finish, -edge["lag_time"], data)) + finishes.append(self.offset_date(finish, -edge["lag_time"], successor_data)) else: finishes.append(finish) free_floats.append( @@ -447,9 +414,7 @@ class Usecase: days += edge["lag_time"] if days: starts.append( - datetime.datetime.combine( - self.offset_date(finish, -days, data), datetime.time(9) - ) + datetime.datetime.combine(self.offset_date(finish, -days, data), datetime.time(9)) ) starts.append( datetime.datetime.combine( @@ -471,13 +436,8 @@ class Usecase: if starts and finishes: data["late_start"] = min(starts) data["late_finish"] = min(finishes) - if ( - self.offset_date(data["late_start"], data["duration"], data) - < data["late_finish"] - ): - data[ - "late_finish" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + if self.offset_date(data["late_start"], data["duration"], data) < data["late_finish"]: + data["late_finish"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["late_start"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -485,9 +445,7 @@ class Usecase: date_type="FINISH", ) else: - data[ - "late_start" - ] = ifcopenshell.util.sequence.get_start_or_finish_date( + data["late_start"] = ifcopenshell.util.sequence.get_start_or_finish_date( data["late_finish"], datetime.timedelta(days=data["duration"]), data["duration_type"], @@ -528,9 +486,7 @@ class Usecase: data["total_float"] = data["late_finish"] - data["early_finish"] # If the float is within the span of a single day, it may show as a 8 hours if data["total_float"].seconds == 60 * 60 * 8: - data["total_float"] = datetime.timedelta( - days=data["total_float"].days + 1 - ) + data["total_float"] = datetime.timedelta(days=data["total_float"].days + 1) data["free_float"] = min(free_floats) if free_floats else None # If the float is within the span of a single day, it may show as a 8 hours diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py index 6b7fac4f75..d49da8324e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_task.py @@ -21,124 +21,121 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, task=None): - """Removes a task +def remove_task(file, task=None) -> None: + """Removes a task - All subtasks are also removed recursively. Any relationships such as - sequences or controls are also removed. + All subtasks are also removed recursively. Any relationships such as + sequences or controls are also removed. - :param task: The IfcTask to remove. - :type task: ifcopenshell.entity_instance - :return: None - :rtype: None + :param task: The IfcTask to remove. + :type task: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Add a root task to represent the design milestones, and major - # project phases. - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Milestones", identification="A") - design = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Design", identification="B") - ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Add a root task to represent the design milestones, and major + # project phases. + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Milestones", identification="A") + design = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Design", identification="B") + ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Ah, let's delete the design section, who needs it anyway we'll - # just fix it on site. - ifcopenshell.api.run("sequence.remove_task", model, task=design) - """ - self.file = file - self.settings = {"task": task} + # Ah, let's delete the design section, who needs it anyway we'll + # just fix it on site. + ifcopenshell.api.run("sequence.remove_task", model, task=design) + """ + settings = {"task": task} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["task"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["task"].TaskTime: - self.file.remove(self.settings["task"].TaskTime) - for inverse in self.file.get_inverse(self.settings["task"]): - if inverse.is_a("IfcRelSequence"): + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["task"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["task"].TaskTime: + file.remove(settings["task"].TaskTime) + for inverse in file.get_inverse(settings["task"]): + if inverse.is_a("IfcRelSequence"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelNests"): + if inverse.RelatingObject == settings["task"]: + for related_object in inverse.RelatedObjects: + ifcopenshell.api.run("sequence.remove_task", file, task=related_object) + elif not inverse.RelatedObjects: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelNests"): - if inverse.RelatingObject == self.settings["task"]: - for related_object in inverse.RelatedObjects: - ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object) - elif not inverse.RelatedObjects: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif self.settings["task"] in inverse.RelatedObjects: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - if not related_objects: - self.file.remove(inverse) - else: - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToControl"): - if inverse.RelatingControl == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + elif settings["task"] in inverse.RelatedObjects: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + if not related_objects: + file.remove(inverse) else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["task"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToProcess"): - if inverse.RelatingProcess == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif inverse.is_a("IfcRelAssignsToProduct"): - if inverse.RelatingProduct == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToObject"): - if inverse.RelatingObject == self.settings["task"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["task"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToProcess"): + elif inverse.is_a("IfcRelAssignsToControl"): + if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1: history = inverse.OwnerHistory - self.file.remove(inverse) + file.remove(inverse) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["task"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToProcess"): + if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif inverse.is_a("IfcRelAssignsToProduct"): + if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToObject"): + if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["task"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToProcess"): + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - history = self.settings["task"].OwnerHistory - self.file.remove(self.settings["task"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["task"].OwnerHistory + file.remove(settings["task"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py index 32effdf4ac..672606c421 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py @@ -19,45 +19,42 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, time_period=None): - """Removes a time period +def remove_time_period(file, time_period=None) -> None: + """Removes a time period - :param time_period: The IfcTimePeriod to remove. - :type time_period: ifcopenshell.entity_instance - :return: None - :rtype: None + :param time_period: The IfcTimePeriod to remove. + :type time_period: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) - ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, - recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) + # State that we work from weekdays 1 to 5 (i.e. Monday to Friday) + ifcopenshell.api.run("sequence.edit_recurrence_pattern", model, + recurrence_pattern=pattern, attributes={"WeekdayComponent": [1, 2, 3, 4, 5]}) - # The morning work session, lunch, then the afternoon work session. - morning = ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="09:00", end_time="12:00") - afternoon = ifcopenshell.api.run("sequence.add_time_period", model, - recurrence_pattern=pattern, start_time="13:00", end_time="17:00") + # The morning work session, lunch, then the afternoon work session. + morning = ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="09:00", end_time="12:00") + afternoon = ifcopenshell.api.run("sequence.add_time_period", model, + recurrence_pattern=pattern, start_time="13:00", end_time="17:00") - # Let's take the afternoon off! - ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon) - """ - self.file = file - self.settings = {"time_period": time_period} + # Let's take the afternoon off! + ifcopenshell.api.run("sequence.remove_time_period", model, time_period=afternoon) + """ + settings = {"time_period": time_period} - def execute(self): - self.file.remove(self.settings["time_period"]) + file.remove(settings["time_period"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index e233bef26d..22a362c42d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -20,49 +20,46 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_calendar=None): - """Removes a work calendar +def remove_work_calendar(file, work_calendar=None) -> None: + """Removes a work calendar - All relationships are also removed, such as if a task is set to use that - calendar. + All relationships are also removed, such as if a task is set to use that + calendar. - :param work_calendar: The IfcWorkCalendar to remove - :type work_calendar: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_calendar: The IfcWorkCalendar to remove + :type work_calendar: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model, name="5 Day Week") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar) - """ - self.file = file - self.settings = {"work_calendar": work_calendar} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_calendar", model, work_calendar=calendar) + """ + settings = {"work_calendar": work_calendar} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_calendar"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["work_calendar"].Controls: - for rel in self.settings["work_calendar"].Controls: - for related_object in rel.RelatedObjects: - ifcopenshell.api.run( - "control.unassign_control", - self.file, - relating_control=self.settings["work_calendar"], - related_object=related_object, - ) - history = self.settings["work_calendar"].OwnerHistory - self.file.remove(self.settings["work_calendar"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_calendar"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["work_calendar"].Controls: + for rel in settings["work_calendar"].Controls: + for related_object in rel.RelatedObjects: + ifcopenshell.api.run( + "control.unassign_control", + file, + relating_control=settings["work_calendar"], + related_object=related_object, + ) + history = settings["work_calendar"].OwnerHistory + file.remove(settings["work_calendar"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py index bbd631829d..28675fe6a5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py @@ -20,40 +20,37 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_plan=None): - """Removes a work plan +def remove_work_plan(file, work_plan=None) -> None: + """Removes a work plan - Note that schedules that are grouped under the work plan are not - removed. + Note that schedules that are grouped under the work plan are not + removed. - :param work_plan: The IfcWorkPlan to remove. - :type work_plan: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_plan: The IfcWorkPlan to remove. + :type work_plan: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan) - """ - self.file = file - self.settings = {"work_plan": work_plan} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_plan", model, work_plan=work_plan) + """ + settings = {"work_plan": work_plan} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_plan"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - history = self.settings["work_plan"].OwnerHistory - self.file.remove(self.settings["work_plan"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_plan"]], + relating_context=file.by_type("IfcContext")[0], + ) + history = settings["work_plan"].OwnerHistory + file.remove(settings["work_plan"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py index 66b69c8804..ec06a9146b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py @@ -21,69 +21,66 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, work_schedule=None): - """Removes a work schedule +def remove_work_schedule(file, work_schedule=None) -> None: + """Removes a work schedule - All tasks in the work schedule are also removed recursively. + All tasks in the work schedule are also removed recursively. - :param work_schedule: The IfcWorkSchedule to remove. - :type work_schedule: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_schedule: The IfcWorkSchedule to remove. + :type work_schedule: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # This will hold all our construction schedules - work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") + # This will hold all our construction schedules + work_plan = ifcopenshell.api.run("sequence.add_work_plan", model, name="Construction") - # Let's imagine this is one of our schedules in our work plan. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, - name="Construction Schedule A", work_plan=work_plan) + # Let's imagine this is one of our schedules in our work plan. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, + name="Construction Schedule A", work_plan=work_plan) - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule) - """ - self.file = file - self.settings = {"work_schedule": work_schedule} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_schedule", model, work_schedule=schedule) + """ + settings = {"work_schedule": work_schedule} - def execute(self): - # TODO: do a deep purge - ifcopenshell.api.run( - "project.unassign_declaration", - self.file, - definitions=[self.settings["work_schedule"]], - relating_context=self.file.by_type("IfcContext")[0], - ) - if self.settings["work_schedule"].Declares: - for rel in self.settings["work_schedule"].Declares: - for work_schedule in rel.RelatedObjects: - ifcopenshell.api.run( - "sequence.remove_work_schedule", - self.file, - work_schedule=work_schedule, - ) - for inverse in self.file.get_inverse(self.settings["work_schedule"]): - if inverse.is_a("IfcRelDefinesByObject"): - if inverse.RelatingObject == self.settings["work_schedule"] or len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - else: - related_objects = list(inverse.RelatedObjects) - related_objects.remove(self.settings["work_schedule"]) - inverse.RelatedObjects = related_objects - elif inverse.is_a("IfcRelAssignsToControl"): - [ - ifcopenshell.api.run("sequence.remove_task", self.file, task=related_object) - for related_object in inverse.RelatedObjects - if related_object.is_a("IfcTask") - ] + # TODO: do a deep purge + ifcopenshell.api.run( + "project.unassign_declaration", + file, + definitions=[settings["work_schedule"]], + relating_context=file.by_type("IfcContext")[0], + ) + if settings["work_schedule"].Declares: + for rel in settings["work_schedule"].Declares: + for work_schedule in rel.RelatedObjects: + ifcopenshell.api.run( + "sequence.remove_work_schedule", + file, + work_schedule=work_schedule, + ) + for inverse in file.get_inverse(settings["work_schedule"]): + if inverse.is_a("IfcRelDefinesByObject"): + if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + else: + related_objects = list(inverse.RelatedObjects) + related_objects.remove(settings["work_schedule"]) + inverse.RelatedObjects = related_objects + elif inverse.is_a("IfcRelAssignsToControl"): + [ + ifcopenshell.api.run("sequence.remove_task", file, task=related_object) + for related_object in inverse.RelatedObjects + if related_object.is_a("IfcTask") + ] - history = self.settings["work_schedule"].OwnerHistory - self.file.remove(self.settings["work_schedule"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = settings["work_schedule"].OwnerHistory + file.remove(settings["work_schedule"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py index 3898e3655a..ab4587ce6a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, work_time=None): - """Removes a work time +def remove_work_time(file, work_time=None) -> None: + """Removes a work time - :param work_time: The IfcWorkTime to remove. - :type work_time: ifcopenshell.entity_instance - :return: None - :rtype: None + :param work_time: The IfcWorkTime to remove. + :type work_time: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # And remove it immediately - ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time) - """ - self.file = file - self.settings = {"work_time": work_time} + # And remove it immediately + ifcopenshell.api.run("sequence.remove_work_time", model, work_time=work_time) + """ + settings = {"work_time": work_time} - def execute(self): - self.file.remove(self.settings["work_time"]) + file.remove(settings["work_time"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py index cca278da44..cac8f95372 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_lag_time.py @@ -19,57 +19,54 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, rel_sequence=None): - """Removes any lag time in a sequence +def unassign_lag_time(file, rel_sequence=None) -> None: + """Removes any lag time in a sequence - The schedule is cascaded afterwards. + The schedule is cascaded afterwards. - :param rel_sequence: The sequence to remove the lag time from. - :type rel_sequence: ifcopenshell.entity_instance - :return: None - :rtype: None + :param rel_sequence: The sequence to remove the lag time from. + :type rel_sequence: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - sequence = ifcopenshell.api.run("sequence.assign_sequence", model, - relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + sequence = ifcopenshell.api.run("sequence.assign_sequence", model, + relating_process=zone1, related_process=zone2) - # What if you had to wait 1 week before you could start zone 2? - ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W") + # What if you had to wait 1 week before you could start zone 2? + ifcopenshell.api.run("sequence.assign_lag_time", model, rel_sequence=sequence, lag_value="P1W") - # What if you didn't? - ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence) - """ - self.file = file - self.settings = { - "rel_sequence": rel_sequence, - } + # What if you didn't? + ifcopenshell.api.run("sequence.unassign_lag_time", model, rel_sequence=sequence) + """ + settings = { + "rel_sequence": rel_sequence, + } - def execute(self): - if len(self.file.get_inverse(self.settings["rel_sequence"].TimeLag)) == 1: - self.file.remove(self.settings["rel_sequence"].TimeLag) - else: - self.settings["rel_sequence"].TimeLag = None - ifcopenshell.api.run( - "sequence.cascade_schedule", - self.file, - task=self.settings["rel_sequence"].RelatedProcess, - ) + if len(file.get_inverse(settings["rel_sequence"].TimeLag)) == 1: + file.remove(settings["rel_sequence"].TimeLag) + else: + settings["rel_sequence"].TimeLag = None + ifcopenshell.api.run( + "sequence.cascade_schedule", + file, + task=settings["rel_sequence"].RelatedProcess, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py index dfc12068d3..f7e141afc1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_process.py @@ -21,59 +21,56 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_process=None, related_object=None): - """Unassigns a process and object relationship +def unassign_process(file, relating_process=None, related_object=None) -> None: + """Unassigns a process and object relationship - See ifcopenshell.api.sequence.assign_process for details. + See ifcopenshell.api.sequence.assign_process for details. - :param relating_process: The IfcTask in the relationship. - :type relating_process: ifcopenshell.entity_instance - :param related_object: The related object. - :type related_object: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_process: The IfcTask in the relationship. + :type relating_process: ifcopenshell.entity_instance + :param related_object: The related object. + :type related_object: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Demolish existing", identification="A", predefined_type="DEMOLITION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's demolish that wall! - ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) + # Let's demolish that wall! + ifcopenshell.api.run("sequence.assign_process", model, relating_process=task, related_object=wall) - # Change our mind. - ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_object": related_object, - } + # Change our mind. + ifcopenshell.api.run("sequence.unassign_process", model, relating_process=task, related_object=wall) + """ + settings = { + "relating_process": relating_process, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != self.settings["relating_process"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py index 31d9edb0e1..23f9281c95 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_product.py @@ -21,59 +21,56 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_product=None, related_object=None): - """Unassigns a product and object relationship +def unassign_product(file, relating_product=None, related_object=None) -> None: + """Unassigns a product and object relationship - See ifcopenshell.api.sequence.assign_product for details. + See ifcopenshell.api.sequence.assign_product for details. - :param relating_product: The IfcProduct in the relationship. - :type relating_product: ifcopenshell.entity_instance - :param related_object: The IfcTask in the relationship. - :type related_object: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_product: The IfcProduct in the relationship. + :type relating_product: ifcopenshell.entity_instance + :param related_object: The IfcTask in the relationship. + :type related_object: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's create a construction task. Note that the predefined type is - # important to distinguish types of tasks. - task = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") + # Let's create a construction task. Note that the predefined type is + # important to distinguish types of tasks. + task = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Build wall", identification="A", predefined_type="CONSTRUCTION") - # Let's say we have a wall somewhere. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Let's say we have a wall somewhere. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's construct that wall! - ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) + # Let's construct that wall! + ifcopenshell.api.run("sequence.assign_product", relating_product=wall, related_object=task) - # Change our mind. - ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task) - """ - self.file = file - self.settings = { - "relating_product": relating_product, - "related_object": related_object, - } + # Change our mind. + ifcopenshell.api.run("sequence.unassign_product", relating_product=wall, related_object=task) + """ + settings = { + "relating_product": relating_product, + "related_object": related_object, + } - def execute(self): - for rel in self.settings["related_object"].HasAssignments or []: - if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != self.settings["relating_product"]: - continue - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["related_object"]) - rel.RelatedObjects = related_objects - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - return rel + for rel in settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]: + continue + if len(rel.RelatedObjects) == 1: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_objects = list(rel.RelatedObjects) + related_objects.remove(settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py index fc74c69a95..46c99207f5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py @@ -17,40 +17,37 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, recurrence_pattern=None): - """Unassigns a recurrence pattern +def unassign_recurrence_pattern(file, recurrence_pattern=None) -> None: + """Unassigns a recurrence pattern - Note that a recurring task time must have a recurrence pattern, so if - you remove it, be sure to clean up after yourself. + Note that a recurring task time must have a recurrence pattern, so if + you remove it, be sure to clean up after your - :param recurrence_pattern: The IfcRecurrencePattern to remove. - :type recurrence_pattern: ifcopenshell.entity_instance - :return: None - :rtype: None + :param recurrence_pattern: The IfcRecurrencePattern to remove. + :type recurrence_pattern: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's create a new calendar. - calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) + # Let's create a new calendar. + calendar = ifcopenshell.api.run("sequence.add_work_calendar", model) - # Let's start defining the times that we work during the week. - work_time = ifcopenshell.api.run("sequence.add_work_time", model, - work_calendar=calendar, time_type="WorkingTimes") + # Let's start defining the times that we work during the week. + work_time = ifcopenshell.api.run("sequence.add_work_time", model, + work_calendar=calendar, time_type="WorkingTimes") - # We create a weekly recurrence - pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, - parent=work_time, recurrence_type="WEEKLY") + # We create a weekly recurrence + pattern = ifcopenshell.api.run("sequence.assign_recurrence_pattern", model, + parent=work_time, recurrence_type="WEEKLY") - # Change our mind, let's just maintain it whenever we feel like it. - ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern) - """ - self.file = file - self.settings = {"recurrence_pattern": recurrence_pattern} + # Change our mind, let's just maintain it whenever we feel like it. + ifcopenshell.api.run("sequence.unassign_recurrence_pattern", recurrence_pattern=pattern) + """ + settings = {"recurrence_pattern": recurrence_pattern} - def execute(self): - for time_period in self.settings["recurrence_pattern"].TimePeriods or []: - self.file.remove(time_period) - self.file.remove(self.settings["recurrence_pattern"]) + for time_period in settings["recurrence_pattern"].TimePeriods or []: + file.remove(time_period) + file.remove(settings["recurrence_pattern"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py index c7286909bd..f10b11f893 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_sequence.py @@ -21,53 +21,50 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_process=None, related_process=None): - """Removes a sequence relationship between tasks +def unassign_sequence(file, relating_process=None, related_process=None) -> None: + """Removes a sequence relationship between tasks - :param relating_process: The previous / predecessor task. - :type relating_process: ifcopenshell.entity_instance - :param related_process: The next / successor task. - :type related_process: ifcopenshell.entity_instance - :return: None - :rtype: None + :param relating_process: The previous / predecessor task. + :type relating_process: ifcopenshell.entity_instance + :param related_process: The next / successor task. + :type related_process: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Let's imagine we are creating a construction schedule. All tasks - # need to be part of a work schedule. - schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") + # Let's imagine we are creating a construction schedule. All tasks + # need to be part of a work schedule. + schedule = ifcopenshell.api.run("sequence.add_work_schedule", model, name="Construction Schedule A") - # Let's imagine a root construction task - construction = ifcopenshell.api.run("sequence.add_task", model, - work_schedule=schedule, name="Construction", identification="C") + # Let's imagine a root construction task + construction = ifcopenshell.api.run("sequence.add_task", model, + work_schedule=schedule, name="Construction", identification="C") - # Let's imagine we're building 2 zones, one after another. - zone1 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 1", identification="C.1") - zone2 = ifcopenshell.api.run("sequence.add_task", model, - parent_task=construction, name="Zone 2", identification="C.2") + # Let's imagine we're building 2 zones, one after another. + zone1 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 1", identification="C.1") + zone2 = ifcopenshell.api.run("sequence.add_task", model, + parent_task=construction, name="Zone 2", identification="C.2") - # Zone 1 finishes, then zone 2 starts. - ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2) + # Zone 1 finishes, then zone 2 starts. + ifcopenshell.api.run("sequence.assign_sequence", model, relating_process=zone1, related_process=zone2) - # Let's make them unrelated - ifcopenshell.api.run("sequence.unassign_sequence", model, - relating_process=zone1, related_process=zone2) - """ - self.file = file - self.settings = { - "relating_process": relating_process, - "related_process": related_process, - } + # Let's make them unrelated + ifcopenshell.api.run("sequence.unassign_sequence", model, + relating_process=zone1, related_process=zone2) + """ + settings = { + "relating_process": relating_process, + "related_process": related_process, + } - def execute(self): - for rel in self.settings["related_process"].IsSuccessorFrom or []: - if rel.RelatingProcess == self.settings["relating_process"]: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=self.settings["related_process"]) + for rel in settings["related_process"].IsSuccessorFrom or []: + if rel.RelatingProcess == settings["relating_process"]: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run("sequence.cascade_schedule", file, task=settings["related_process"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py index e0caddbe3c..22891f5c83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_container import assign_container +from .dereference_structure import dereference_structure +from .reference_structure import reference_structure +from .unassign_container import unassign_container diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py index 9edf7ccba8..298fe3dfdb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/assign_container.py @@ -23,163 +23,159 @@ import ifcopenshell.util.placement from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Assigns products to be contained hierarchically in a space +def assign_container( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns products to be contained hierarchically in a space - All physical IFC model elements must be part of a hierarchical tree - called the "spatial decomposition", where large things are made up of - smaller things. This tree always begins at an "IfcProject" and is then - broken down using "decomposition" relationships, of which aggregation is - the first relationship you will use. See - ifcopenshell.api.aggregate.assign_object for more details about - aggregation. + All physical IFC model elements must be part of a hierarchical tree + called the "spatial decomposition", where large things are made up of + smaller things. This tree always begins at an "IfcProject" and is then + broken down using "decomposition" relationships, of which aggregation is + the first relationship you will use. See + ifcopenshell.api.aggregate.assign_object for more details about + aggregation. - The IfcProject will be "decomposed" into spatial structure elements. - These are virtual spaces like stes, buildings, storeys, and spaces (i.e. - rooms). You can't physically touch these spaces, but you can touch the - products contained within these spaces. + The IfcProject will be "decomposed" into spatial structure elements. + These are virtual spaces like stes, buildings, storeys, and spaces (i.e. + rooms). You can't physically touch these spaces, but you can touch the + products contained within these spaces. - To state that a product is contained in a space, you will use a - "containment" relationship. Containment is a very common relationship - used to create the hierarchical spatial decomposition tree. For example, - you might say that "This wall is on the third building storey", or "this - table is in the living room space". + To state that a product is contained in a space, you will use a + "containment" relationship. Containment is a very common relationship + used to create the hierarchical spatial decomposition tree. For example, + you might say that "This wall is on the third building storey", or "this + table is in the living room space". - The distinguishing factor between aggregation and containment is that - aggregation occurs between objects of the same type (e.g. a large space - is made up of smaller spaces), whereas containment is between two - different types: explicitly saying that a physical product exists within - a virtual space. + The distinguishing factor between aggregation and containment is that + aggregation occurs between objects of the same type (e.g. a large space + is made up of smaller spaces), whereas containment is between two + different types: explicitly saying that a physical product exists within + a virtual space. - Containment is critical in construction management, to know which - objects are in which spaces, as often you would divide your construction - schedule into storey by storey, or zone by zone. Containment is also - critical in facility management, as it indicates through which space - equipment may be accessed for maintenance purposes. + Containment is critical in construction management, to know which + objects are in which spaces, as often you would divide your construction + schedule into storey by storey, or zone by zone. Containment is also + critical in facility management, as it indicates through which space + equipment may be accessed for maintenance purposes. - As a product may only have a single location in the "spatial - decomposition" tree, assigning an aggregate relationship will remove any - previous aggregation, containment, or nesting relationships it may have. + As a product may only have a single location in the "spatial + decomposition" tree, assigning an aggregate relationship will remove any + previous aggregation, containment, or nesting relationships it may have. - :param products: A list of physical IfcElements existing in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :return: The IfcRelContainedInSpatialStructure relationship instance - or `None` if `products` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: A list of physical IfcElements existing in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: The IfcRelContainedInSpatialStructure relationship instance + or `None` if `products` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + space = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSpace") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a wall and furniture - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # Create a wall and furniture + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # The wall is in the storey, and the furniture is in the space - ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) - ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space) - """ - self.file = file - self.settings = { - "products": products, - "relating_structure": relating_structure, - } + # The wall is in the storey, and the furniture is in the space + ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) + ifcopenshell.api.run("spatial.assign_container", model, products=[furniture], relating_structure=space) + """ + settings = { + "products": products, + "relating_structure": relating_structure, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - if not self.settings["products"]: - return + if not settings["products"]: + return - products = set(self.settings["products"]) - relating_structure = self.settings["relating_structure"] - structure_rel = next(iter(relating_structure.ContainsElements), None) + products = set(settings["products"]) + relating_structure = settings["relating_structure"] + structure_rel = next(iter(relating_structure.ContainsElements), None) - previous_containers_rels: set[ifcopenshell.entity_instance] = set() - products_without_containers: list[ifcopenshell.entity_instance] = [] - products_with_containers: list[ifcopenshell.entity_instance] = [] + previous_containers_rels: set[ifcopenshell.entity_instance] = set() + products_without_containers: list[ifcopenshell.entity_instance] = [] + products_with_containers: list[ifcopenshell.entity_instance] = [] - # check if there is anything to change - for product in products: - product_rel = next(iter(product.ContainedInStructure), None) + # check if there is anything to change + for product in products: + product_rel = next(iter(product.ContainedInStructure), None) - if product_rel is None: - products_without_containers.append(product) - continue + if product_rel is None: + products_without_containers.append(product) + continue - # either structure_rel is None or product is part of different rel - if product_rel != structure_rel: - previous_containers_rels.add(product_rel) - products_with_containers.append(product) + # either structure_rel is None or product is part of different rel + if product_rel != structure_rel: + previous_containers_rels.add(product_rel) + products_with_containers.append(product) - # products with already assigned containers will be skipped + # products with already assigned containers will be skipped - products_to_change = products_without_containers + products_with_containers - # nothing to change - if not products_to_change: - return structure_rel + products_to_change = products_without_containers + products_with_containers + # nothing to change + if not products_to_change: + return structure_rel - # can be either only aggregated or only contained at the same time - ifcopenshell.api.run("aggregate.unassign_object", self.file, products=products_without_containers) + # can be either only aggregated or only contained at the same time + ifcopenshell.api.run("aggregate.unassign_object", file, products=products_without_containers) - # unassign elements from previous containers - for rel in previous_containers_rels: - related_elements = set(rel.RelatedElements) - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - - # assign elements to a new container - if structure_rel: - structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": structure_rel}) + # unassign elements from previous containers + for rel in previous_containers_rels: + related_elements = set(rel.RelatedElements) - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - structure_rel = self.file.create_entity( - "IfcRelContainedInSpatialStructure", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedElements": list(products), - "RelatingStructure": self.settings["relating_structure"], - } + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + + # assign elements to a new container + if structure_rel: + structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": structure_rel}) + else: + structure_rel = file.create_entity( + "IfcRelContainedInSpatialStructure", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedElements": list(products), + "RelatingStructure": settings["relating_structure"], + } + ) + + # localize placement relative to a new container for affected products + for product in products_to_change: + placement = getattr(product, "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=product, + matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), + is_si=False, ) - # localize placement relative to a new container for affected products - for product in products_to_change: - placement = getattr(product, "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=product, - matrix=ifcopenshell.util.placement.get_local_placement(product.ObjectPlacement), - is_si=False, - ) - - return structure_rel + return structure_rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py index 6902018b46..50eb72305b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/dereference_structure.py @@ -21,70 +21,66 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Dereferences a list of products and space +def dereference_structure( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> None: + """Dereferences a list of products and space - :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :return: None - :rtype: None + :param products: The list of physical IfcElements that exists in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a column, this column spans 3 storeys - column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The column is contained in the lowermost storey - ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) - # And referenced in the others - ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2) - ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3) + # And referenced in the others + ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey2) + ifcopenshell.api.run("spatial.reference_structure", model, products=[column], relating_structure=storey3) - # Actually, it only goes up to storey 2. - ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3) - """ - self.file = file - self.settings = {"products": products, "relating_structure": relating_structure} + # Actually, it only goes up to storey 2. + ifcopenshell.api.run("spatial.dereference_structure", model, products=[column], relating_structure=storey3) + """ + settings = {"products": products, "relating_structure": relating_structure} - def execute(self) -> None: - products = set(self.settings["products"]) - for rel in self.settings["relating_structure"].ReferencesElements: - related_elements = set(rel.RelatedElements) - if not related_elements.intersection(products): - continue - related_elements = related_elements - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + products = set(settings["products"]) + for rel in settings["relating_structure"].ReferencesElements: + related_elements = set(rel.RelatedElements) + if not related_elements.intersection(products): + continue + related_elements = related_elements - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py index 32ef580b96..48b5c6bc1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/reference_structure.py @@ -22,103 +22,99 @@ import ifcopenshell.util.element from typing import Union -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - relating_structure: ifcopenshell.entity_instance, - ): - """Denote that a list products is related to a list of spatial structures +def reference_structure( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + relating_structure: ifcopenshell.entity_instance, +) -> Union[ifcopenshell.entity_instance, None]: + """Denote that a list products is related to a list of spatial structures - This is similar to ifcopenshell.api.spatial.assign_container, except - that containment can only occur between a product and a single spatial - structure element. This is fine if a wall is on level 1, but not - appropriate if you have a multistorey column on multiple levels, or a - door with a to and from space, or a stair going from one floor to - another floor. This is where spatial referencing is used. + This is similar to ifcopenshell.api.spatial.assign_container, except + that containment can only occur between a product and a single spatial + structure element. This is fine if a wall is on level 1, but not + appropriate if you have a multistorey column on multiple levels, or a + door with a to and from space, or a stair going from one floor to + another floor. This is where spatial referencing is used. - Typically, the product will be contained in the lowermost, constructed - first, or primarily accessible space. For a multistorey column or stair, - the column or stair will therefore be contained in the lowermost storey. - Then, any other storeys will be referenced. + Typically, the product will be contained in the lowermost, constructed + first, or primarily accessible space. For a multistorey column or stair, + the column or stair will therefore be contained in the lowermost storey. + Then, any other storeys will be referenced. - Referencing is non-hierarchical, so a door may be referenced in multiple - spaces simultaneously. + Referencing is non-hierarchical, so a door may be referenced in multiple + spaces simultaneously. - :param products: The list of physical IfcElements that exists in the space. - :type products: list[ifcopenshell.entity_instance] - :param relating_structure: The IfcSpatialStructureElement element, such - as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element - exists in. - :type relating_structure: ifcopenshell.entity_instance - :return: The IfcRelReferencedInSpatialStructure relationship instance - or `None` if `products` was an empty list. - :rtype: Union[ifcopenshell.entity_instance, None] + :param products: The list of physical IfcElements that exists in the space. + :type products: list[ifcopenshell.entity_instance] + :param relating_structure: The IfcSpatialStructureElement element, such + as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element + exists in. + :type relating_structure: ifcopenshell.entity_instance + :return: The IfcRelReferencedInSpatialStructure relationship instance + or `None` if `products` was an empty list. + :rtype: Union[ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey1 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey2 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + storey3 = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + ifcopenshell.api.run("aggregate.assign_object", model, products=[space], relating_object=storey) - # Create a column, this column spans 3 storeys - column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a column, this column spans 3 storeys + column = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The column is contained in the lowermost storey - ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) + # The column is contained in the lowermost storey + ifcopenshell.api.run("spatial.assign_container", model, products=[column], relating_structure=storey1) - # And referenced in the others - ifcopenshell.api.run( - "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3] - ) - """ - self.file = file - self.settings = { - "products": products, - "relating_structure": relating_structure, - } + # And referenced in the others + ifcopenshell.api.run( + "spatial.reference_structure", model, products=[column], relating_structure=[storey2, storey3] + ) + """ + settings = { + "products": products, + "relating_structure": relating_structure, + } - def execute(self) -> Union[ifcopenshell.entity_instance, None]: - structure = self.settings["relating_structure"] - products = set(self.settings["products"]) + structure = settings["relating_structure"] + products = set(settings["products"]) - if not products: - return + if not products: + return - referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure) - products_to_assign = products - referenced - rel = next(iter(structure.ReferencesElements), None) - - if not products_to_assign: - return rel - - if rel is None: - rel = self.file.create_entity( - "IfcRelReferencedInSpatialStructure", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedElements": list(products_to_assign), - "RelatingStructure": structure, - } - ) - else: - related_elements = set(rel.RelatedElements) | products_to_assign - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + referenced = ifcopenshell.util.element.get_structure_referenced_elements(structure) + products_to_assign = products - referenced + rel = next(iter(structure.ReferencesElements), None) + if not products_to_assign: return rel + + if rel is None: + rel = file.create_entity( + "IfcRelReferencedInSpatialStructure", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedElements": list(products_to_assign), + "RelatingStructure": structure, + } + ) + else: + related_elements = set(rel.RelatedElements) | products_to_assign + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py index d1418d3be5..b6afbc13c9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py +++ b/src/ifcopenshell-python/ifcopenshell/api/spatial/unassign_container.py @@ -21,56 +21,53 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]): - """Unassigns a container from products. +def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.entity_instance]) -> None: + """Unassigns a container from products. - :param product: A list of IfcProducts to remove the containment from. - :type product: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param product: A list of IfcProducts to remove the containment from. + :type product: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") - building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") - storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") + project = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + site = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcSite") + building = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuilding") + storey = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcBuildingStorey") - # The project contains a site (note that project aggregation is a special case in IFC) - ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) + # The project contains a site (note that project aggregation is a special case in IFC) + ifcopenshell.api.run("aggregate.assign_object", model, products=[site], relating_object=project) - # The site has a building, the building has a storey, and the storey has a space - ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) - ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) + # The site has a building, the building has a storey, and the storey has a space + ifcopenshell.api.run("aggregate.assign_object", model, products=[building], relating_object=site) + ifcopenshell.api.run("aggregate.assign_object", model, products=[storey], relating_object=building) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # The wall is in the storey - ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) + # The wall is in the storey + ifcopenshell.api.run("spatial.assign_container", model, products=[wall], relating_structure=storey) - # Not anymore! - ifcopenshell.api.run("spatial.unassign_container", model, products=[wall]) - """ - self.file = file - self.settings = { - "products": products, - } + # Not anymore! + ifcopenshell.api.run("spatial.unassign_container", model, products=[wall]) + """ + settings = { + "products": products, + } - def execute(self) -> None: - products = set(self.settings["products"]) - rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None))) + products = set(settings["products"]) + rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None))) - for rel in rels: - related_elements = set(rel.RelatedElements) - products - if related_elements: - rel.RelatedElements = list(related_elements) - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + related_elements = set(rel.RelatedElements) - products + if related_elements: + rel.RelatedElements = list(related_elements) + ifcopenshell.api.run("owner.update_owner_history", file, element=rel) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py index e0caddbe3c..3bdaf8c004 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/__init__.py @@ -15,3 +15,25 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_structural_activity import add_structural_activity +from .add_structural_analysis_model import add_structural_analysis_model +from .add_structural_boundary_condition import add_structural_boundary_condition +from .add_structural_load import add_structural_load +from .add_structural_load_case import add_structural_load_case +from .add_structural_load_group import add_structural_load_group +from .add_structural_member_connection import add_structural_member_connection +from .assign_structural_analysis_model import assign_structural_analysis_model +from .edit_structural_analysis_model import edit_structural_analysis_model +from .edit_structural_boundary_condition import edit_structural_boundary_condition +from .edit_structural_connection_cs import edit_structural_connection_cs +from .edit_structural_item_axis import edit_structural_item_axis +from .edit_structural_load import edit_structural_load +from .edit_structural_load_case import edit_structural_load_case +from .remove_structural_analysis_model import remove_structural_analysis_model +from .remove_structural_boundary_condition import remove_structural_boundary_condition +from .remove_structural_connection_condition import remove_structural_connection_condition +from .remove_structural_load import remove_structural_load +from .remove_structural_load_case import remove_structural_load_case +from .remove_structural_load_group import remove_structural_load_group +from .unassign_structural_analysis_model import unassign_structural_analysis_model diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py index faf1daf366..4be210fcf1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_activity.py @@ -19,65 +19,61 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, +def add_structural_activity( + file, + ifc_class="IfcStructuralPlanarAction", + predefined_type="CONST", + global_or_local="GLOBAL_COORDS", + applied_load=None, + structural_member=None, +) -> None: + """Adds a new structural activity + + A structural activity is either a structural action or a reaction. It + may be applied to a point, a curve, or a planar surface, and may be a + constant load, linear, etc. + + The activity must be defined using an applied load, and associated with + a structural member. + + :param ifc_class: Choose from any subtype of IfcStructuralActivity. + :type ifc_class: str + :param predefined_type: View the IFC documentation for what valid + predefined types may be chosen. + :type predefined_type: str + :param global_or_local: The location coordinates of the load is always + defined locally relative to the structural member the activity is + assigned to. However, the directions of the applied load may either + be specified globally or locally depending on how this argument is + set. Choose from GLOBAL_COORDS or LOCAL_COORDS. + :type global_or_local: str + :param applied_load: The IfcStructuralLoad that is applied in this + activity. + :type applied_load: ifcopenshell.entity_instance + :param structural_member: The IfcStructuralMember that the load is + applied to. + :type structural_member: ifcopenshell.entity_instance + :return: The newly created entity based on the ifc_class + :rtype: ifcopenshell.entity_instance + """ + settings = { + "ifc_class": ifc_class, + "predefined_type": predefined_type, + "global_or_local": global_or_local, + "applied_load": applied_load, + "structural_member": structural_member, + } + + activity = ifcopenshell.api.run( + "root.create_entity", file, - ifc_class="IfcStructuralPlanarAction", - predefined_type="CONST", - global_or_local="GLOBAL_COORDS", - applied_load=None, - structural_member=None, - ): - """Adds a new structural activity + ifc_class=settings["ifc_class"], + predefined_type=settings["predefined_type"], + ) + activity.AppliedLoad = settings["applied_load"] + activity.GlobalOrLocal = settings["global_or_local"] - A structural activity is either a structural action or a reaction. It - may be applied to a point, a curve, or a planar surface, and may be a - constant load, linear, etc. - - The activity must be defined using an applied load, and associated with - a structural member. - - :param ifc_class: Choose from any subtype of IfcStructuralActivity. - :type ifc_class: str - :param predefined_type: View the IFC documentation for what valid - predefined types may be chosen. - :type predefined_type: str - :param global_or_local: The location coordinates of the load is always - defined locally relative to the structural member the activity is - assigned to. However, the directions of the applied load may either - be specified globally or locally depending on how this argument is - set. Choose from GLOBAL_COORDS or LOCAL_COORDS. - :type global_or_local: str - :param applied_load: The IfcStructuralLoad that is applied in this - activity. - :type applied_load: ifcopenshell.entity_instance - :param structural_member: The IfcStructuralMember that the load is - applied to. - :type structural_member: ifcopenshell.entity_instance - :return: The newly created entity based on the ifc_class - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "ifc_class": ifc_class, - "predefined_type": predefined_type, - "global_or_local": global_or_local, - "applied_load": applied_load, - "structural_member": structural_member, - } - - def execute(self): - activity = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class=self.settings["ifc_class"], - predefined_type=self.settings["predefined_type"], - ) - activity.AppliedLoad = self.settings["applied_load"] - activity.GlobalOrLocal = self.settings["global_or_local"] - - rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralActivity") - rel.RelatingElement = self.settings["structural_member"] - rel.RelatedStructuralActivity = activity - return activity + rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralActivity") + rel.RelatingElement = settings["structural_member"] + rel.RelatedStructuralActivity = activity + return activity diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py index 39181f9a4c..837fd29cce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_analysis_model.py @@ -20,30 +20,27 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file): - """Add a new structural analysis model +def add_structural_analysis_model(file) -> None: + """Add a new structural analysis model - A structural analysis model is a group of all the loads, reactions, - structural members, and structural connections required to describe a - structural analysis model. + A structural analysis model is a group of all the loads, reactions, + structural members, and structural connections required to describe a + structural analysis model. - A 3D analytical model is assumed. + A 3D analytical model is assumed. - :return: The newly created IfcStructuralAnalysisModel - :rtype: ifcopenshell.entity_instance + :return: The newly created IfcStructuralAnalysisModel + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a fresh blank structural analysis - analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model) - """ - self.file = file - self.settings = {} + # Create a fresh blank structural analysis + analysis = ifcopenshell.api.run("structural.add_structural_analysis_model", model) + """ + settings = {} - def execute(self): - return ifcopenshell.api.run( - "root.create_entity", self.file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D" - ) + return ifcopenshell.api.run( + "root.create_entity", file, ifc_class="IfcStructuralAnalysisModel", predefined_type="LOADING_3D" + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py index 1cd9dfef9f..5aef16efed 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_boundary_condition.py @@ -17,55 +17,50 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition"): - """Adds a new structural boundary condition to a structural connection +def add_structural_boundary_condition(file, name=None, connection=None, ifc_class="IfcBoundaryNodeCondition") -> None: + """Adds a new structural boundary condition to a structural connection - The type of boundary condition depends on the connection. Point - connections will have a node condition, curve connections will have an - edge condition, and surface connections will have a face condition. + The type of boundary condition depends on the connection. Point + connections will have a node condition, curve connections will have an + edge condition, and surface connections will have a face condition. - :param name: The name of the boundary condition. - :type name: str,optional - :param connection: The IfcStructuralConnection to apply the boundary - condition to. This will determine the type of condition that is - created. If no connection is supplied, an orphan boundary condition - will be created using the ifc_class that you specify. - :type connection: ifcopenshell.entity_instance,optional - :param ifc_class: The class of IfcBoundaryCondition to create, only - relevant if you do not specify a connection and want to create an - orphaned boundary condition. - :type ifc_class: str,optional - :return: The newly created IfcBoundaryCondition - :rtype: ifcopenshell.entity_instance + :param name: The name of the boundary condition. + :type name: str,optional + :param connection: The IfcStructuralConnection to apply the boundary + condition to. This will determine the type of condition that is + created. If no connection is supplied, an orphan boundary condition + will be created using the ifc_class that you specify. + :type connection: ifcopenshell.entity_instance,optional + :param ifc_class: The class of IfcBoundaryCondition to create, only + relevant if you do not specify a connection and want to create an + orphaned boundary condition. + :type ifc_class: str,optional + :return: The newly created IfcBoundaryCondition + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection) - """ - self.file = file - self.settings = {"name": name, "connection": connection, "ifc_class": ifc_class} + ifcopenshell.api.run("structural.add_structural_boundary_condition", model, connection=connection) + """ + settings = {"name": name, "connection": connection, "ifc_class": ifc_class} - def execute(self): - if self.settings["connection"]: - # assign boundary condition to a connection - if self.settings["connection"].is_a("IfcRelConnectsStructuralMember"): - related_connection = self.settings["connection"].RelatedStructuralConnection - else: - related_connection = self.settings["connection"] - - if related_connection.is_a("IfcStructuralPointConnection"): - boundary_class = "IfcBoundaryNodeCondition" - elif related_connection.is_a("IfcStructuralCurveConnection"): - boundary_class = "IfcBoundaryEdgeCondition" - elif related_connection.is_a("IfcStructuralSurfaceConnection"): - boundary_class = "IfcBoundaryFaceCondition" - - self.settings["connection"].AppliedCondition = self.file.create_entity( - boundary_class, Name=self.settings["name"] - ) + if settings["connection"]: + # assign boundary condition to a connection + if settings["connection"].is_a("IfcRelConnectsStructuralMember"): + related_connection = settings["connection"].RelatedStructuralConnection else: - # add an orphan boundary condition - return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"]) + related_connection = settings["connection"] + + if related_connection.is_a("IfcStructuralPointConnection"): + boundary_class = "IfcBoundaryNodeCondition" + elif related_connection.is_a("IfcStructuralCurveConnection"): + boundary_class = "IfcBoundaryEdgeCondition" + elif related_connection.is_a("IfcStructuralSurfaceConnection"): + boundary_class = "IfcBoundaryFaceCondition" + + settings["connection"].AppliedCondition = file.create_entity(boundary_class, Name=settings["name"]) + else: + # add an orphan boundary condition + return file.create_entity(settings["ifc_class"], Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py index 6d51d7dc22..3cb06cd513 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load.py @@ -19,35 +19,32 @@ import ifcopenshell.api -class Usecase: - def __init__(self, file, name=None, ifc_class="IfcStructuralLoadLinearForce"): - """Adds a new structural load +def add_structural_load(file, name=None, ifc_class="IfcStructuralLoadLinearForce") -> None: + """Adds a new structural load - Structural loads may be actions or reactions. A simple load might be a - static and be linear, planar, or a single point. Alternatively, loads - may be defined as a configuration of multiple loads. + Structural loads may be actions or reactions. A simple load might be a + static and be linear, planar, or a single point. Alternatively, loads + may be defined as a configuration of multiple loads. - :param name: The name of the load - :type name: str,optional - :param ifc_class: The subtype of IfcStructuralLoad to create. Consult - the IFC documentation to see all the types of loads. - :type ifc_class: str - :return: The newly created load entity, depending on the ifc_class - specified. - :rtype: ifcopenshell.entity_instance + :param name: The name of the load + :type name: str,optional + :param ifc_class: The subtype of IfcStructuralLoad to create. Consult + the IFC documentation to see all the types of loads. + :type ifc_class: str + :return: The newly created load entity, depending on the ifc_class + specified. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a simple linear load - ifcopenshell.api.run("structural.add_structural_load", model) - """ - self.file = file - self.settings = { - "name": name, - "ifc_class": ifc_class, - } + # Create a simple linear load + ifcopenshell.api.run("structural.add_structural_load", model) + """ + settings = { + "name": name, + "ifc_class": ifc_class, + } - def execute(self): - return self.file.create_entity(self.settings["ifc_class"], Name=self.settings["name"]) + return file.create_entity(settings["ifc_class"], Name=settings["name"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py index afc4e676db..e3d2c4f6c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_case.py @@ -19,39 +19,34 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED" - ): - """Adds a new load case, which is a collection of related load groups +def add_structural_load_case(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None: + """Adds a new load case, which is a collection of related load groups - :param name: The name of the load case - :type name: str - :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, - or VARIABLE_Q, taken from the Eurocode standard. - :type action_type: str - :param action_source: The source of the load case, such as DEAD_LOAD_G, - LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult - IfcActionSourceTypeEnum in the IFC documentation. - :type action_source: str - :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "name": name, - "action_type": action_type, - "action_source": action_source, - } + :param name: The name of the load case + :type name: str + :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, + or VARIABLE_Q, taken from the Eurocode standard. + :type action_type: str + :param action_source: The source of the load case, such as DEAD_LOAD_G, + LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult + IfcActionSourceTypeEnum in the IFC documentation. + :type action_source: str + :return: The new IfcStructuralLoadCase + :rtype: ifcopenshell.entity_instance + """ + settings = { + "name": name, + "action_type": action_type, + "action_source": action_source, + } - def execute(self): - load_case = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcStructuralLoadCase", - predefined_type="LOAD_CASE", - name=self.settings["name"], - ) - load_case.ActionType = self.settings["action_type"] - load_case.ActionSource = self.settings["action_source"] - return load_case + load_case = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcStructuralLoadCase", + predefined_type="LOAD_CASE", + name=settings["name"], + ) + load_case.ActionType = settings["action_type"] + load_case.ActionSource = settings["action_source"] + return load_case diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py index 497977fe6e..3f450df5c8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_load_group.py @@ -19,39 +19,34 @@ import ifcopenshell.api -class Usecase: - def __init__( - self, file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED" - ): - """Adds a new load group, which is a collection of related loads +def add_structural_load_group(file, name="Unnamed", action_type="NOTDEFINED", action_source="NOTDEFINED") -> None: + """Adds a new load group, which is a collection of related loads - :param name: The name of the load group - :type name: str - :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, - or VARIABLE_Q, taken from the Eurocode standard. - :type action_type: str - :param action_source: The source of the load case, such as DEAD_LOAD_G, - LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult - IfcActionSourceTypeEnum in the IFC documentation. - :type action_source: str - :return: The new IfcStructuralLoadCase - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "name": name, - "action_type": action_type, - "action_source": action_source, - } + :param name: The name of the load group + :type name: str + :param action_type: Choose from EXTRAORDINARY_A, PERMANENT_G, + or VARIABLE_Q, taken from the Eurocode standard. + :type action_type: str + :param action_source: The source of the load case, such as DEAD_LOAD_G, + LIVE_LOAD_Q, TRANSPORT, ICE, etc. For the full list consult + IfcActionSourceTypeEnum in the IFC documentation. + :type action_source: str + :return: The new IfcStructuralLoadCase + :rtype: ifcopenshell.entity_instance + """ + settings = { + "name": name, + "action_type": action_type, + "action_source": action_source, + } - def execute(self): - load_group = ifcopenshell.api.run( - "root.create_entity", - self.file, - ifc_class="IfcStructuralLoadGroup", - predefined_type="LOAD_GROUP", - name=self.settings["name"], - ) - load_group.ActionType = self.settings["action_type"] - load_group.ActionSource = self.settings["action_source"] - return load_group + load_group = ifcopenshell.api.run( + "root.create_entity", + file, + ifc_class="IfcStructuralLoadGroup", + predefined_type="LOAD_GROUP", + name=settings["name"], + ) + load_group.ActionType = settings["action_type"] + load_group.ActionSource = settings["action_source"] + return load_group diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py index eda5fc96c2..792c03b66a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/add_structural_member_connection.py @@ -20,30 +20,27 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_structural_member=None, related_structural_connection=None): - """Relates a structural member and a structural connection +def add_structural_member_connection(file, relating_structural_member=None, related_structural_connection=None) -> None: + """Relates a structural member and a structural connection - :param relating_structural_member: The IfcStructuralMember to have a - connection added to it. - :type relating_structural_member: ifcopenshell.entity_instance - :param related_structural_connection: The IfcStructuralConnection to add - to the IfcStructuralMember. - :type related_structural_connection: ifcopenshell.entity_instance - :return: The IfcRelConnectsStructuralMember relationship - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "relating_structural_member": relating_structural_member, - "related_structural_connection": related_structural_connection, - } + :param relating_structural_member: The IfcStructuralMember to have a + connection added to it. + :type relating_structural_member: ifcopenshell.entity_instance + :param related_structural_connection: The IfcStructuralConnection to add + to the IfcStructuralMember. + :type related_structural_connection: ifcopenshell.entity_instance + :return: The IfcRelConnectsStructuralMember relationship + :rtype: ifcopenshell.entity_instance + """ + settings = { + "relating_structural_member": relating_structural_member, + "related_structural_connection": related_structural_connection, + } - def execute(self): - for connection in self.settings["related_structural_connection"].ConnectsStructuralMembers or []: - if connection.RelatingStructuralMember == self.settings["relating_structural_member"]: - return - rel = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcRelConnectsStructuralMember") - rel.RelatingStructuralMember = self.settings["relating_structural_member"] - rel.RelatedStructuralConnection = self.settings["related_structural_connection"] - return rel + for connection in settings["related_structural_connection"].ConnectsStructuralMembers or []: + if connection.RelatingStructuralMember == settings["relating_structural_member"]: + return + rel = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcRelConnectsStructuralMember") + rel.RelatingStructuralMember = settings["relating_structural_member"] + rel.RelatedStructuralConnection = settings["related_structural_connection"] + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py index 61f771c982..19c31e34ff 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/assign_structural_analysis_model.py @@ -20,37 +20,34 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, product=None, structural_analysis_model=None): - """Assigns a load or structural member to an analysis model +def assign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None: + """Assigns a load or structural member to an analysis model - :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance - :param structural_analysis_model: The IfcStructuralAnalysisModel that - the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - :rtype: ifcopenshell.entity_instance - """ - self.file = file - self.settings = { - "product": product, - "structural_analysis_model": structural_analysis_model, - } + :param product: The structural element that is part of the analysis. + :type product: ifcopenshell.entity_instance + :param structural_analysis_model: The IfcStructuralAnalysisModel that + the structural element is related to. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + :rtype: ifcopenshell.entity_instance + """ + settings = { + "product": product, + "structural_analysis_model": structural_analysis_model, + } - def execute(self): - if not self.settings["structural_analysis_model"].IsGroupedBy: - return self.file.create_entity( - "IfcRelAssignsToGroup", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], - "RelatingGroup": self.settings["structural_analysis_model"], - } - ) - rel = self.settings["structural_analysis_model"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - related_objects.add(self.settings["product"]) - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + if not settings["structural_analysis_model"].IsGroupedBy: + return file.create_entity( + "IfcRelAssignsToGroup", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedObjects": [settings["product"]], + "RelatingGroup": settings["structural_analysis_model"], + } + ) + rel = settings["structural_analysis_model"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + related_objects.add(settings["product"]) + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py index 39c46f6fe7..7c41c59478 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_analysis_model.py @@ -17,24 +17,21 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_analysis_model=None, attributes=None): - """Edits the attributes of an IfcStructuralAnalysisModel +def edit_structural_analysis_model(file, structural_analysis_model=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralAnalysisModel - For more information about the attributes and data types of an - IfcStructuralAnalysisModel, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralAnalysisModel, consult the IFC documentation. - :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit - :type structural_analysis_model: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}} + :param structural_analysis_model: The IfcStructuralAnalysisModel entity you want to edit + :type structural_analysis_model: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"structural_analysis_model": structural_analysis_model, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["structural_analysis_model"], name, value) - return self.settings["structural_analysis_model"] + for name, value in settings["attributes"].items(): + setattr(settings["structural_analysis_model"], name, value) + return settings["structural_analysis_model"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py index e6814c5242..2674a4869e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_boundary_condition.py @@ -17,29 +17,26 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, condition=None, attributes=None): - """Edits the attributes of an IfcBoundaryCondition +def edit_structural_boundary_condition(file, condition=None, attributes=None) -> None: + """Edits the attributes of an IfcBoundaryCondition - For more information about the attributes and data types of an - IfcBoundaryCondition, consult the IFC documentation. + For more information about the attributes and data types of an + IfcBoundaryCondition, consult the IFC documentation. - :param condition: The IfcBoundaryCondition entity you want to edit - :type condition: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"condition": condition, "attributes": attributes or {}} + :param condition: The IfcBoundaryCondition entity you want to edit + :type condition: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"condition": condition, "attributes": attributes or {}} - def execute(self): - for name, data in self.settings["attributes"].items(): - if data["type"] == "string" or data["type"] == "null": - value = data["value"] - elif data["type"] == "IfcBoolean": - value = self.file.createIfcBoolean(data["value"]) - else: - value = self.file.create_entity(data["type"], data["value"]) - setattr(self.settings["condition"], name, value) + for name, data in settings["attributes"].items(): + if data["type"] == "string" or data["type"] == "null": + value = data["value"] + elif data["type"] == "IfcBoolean": + value = file.createIfcBoolean(data["value"]) + else: + value = file.create_entity(data["type"], data["value"]) + setattr(settings["condition"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py index a66bbb989e..89faa62ecd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_connection_cs.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_item=None, axis=None, ref_direction=None): - """Edits the coordinate system of a structural connection +def edit_structural_connection_cs(file, structural_item=None, axis=None, ref_direction=None) -> None: + """Edits the coordinate system of a structural connection - :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance - :param axis: The unit Z axis vector defined as a list of 3 floats. - Defaults to [0., 0., 1.]. - :type axis: list[float] - :param ref_direction: The unit X axis vector defined as a list of 3 - floats. Defaults to [1., 0., 0.]. - :type ref_direction: list[float] - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "structural_item": structural_item, - "axis": axis or [0.0, 0.0, 1.0], - "ref_direction": ref_direction or [1.0, 0.0, 0.0], - } + :param structural_item: The IfcStructuralItem you want to modify. + :type structural_item: ifcopenshell.entity_instance + :param axis: The unit Z axis vector defined as a list of 3 floats. + Defaults to [0., 0., 1.]. + :type axis: list[float] + :param ref_direction: The unit X axis vector defined as a list of 3 + floats. Defaults to [1., 0., 0.]. + :type ref_direction: list[float] + :return: None + :rtype: None + """ + settings = { + "structural_item": structural_item, + "axis": axis or [0.0, 0.0, 1.0], + "ref_direction": ref_direction or [1.0, 0.0, 0.0], + } - def execute(self): - if self.settings["structural_item"].ConditionCoordinateSystem is None: - point = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)) - ccs = self.file.createIfcAxis2Placement3D(point, None, None) - self.settings["structural_item"].ConditionCoordinateSystem = ccs + if settings["structural_item"].ConditionCoordinateSystem is None: + point = file.createIfcCartesianPoint((0.0, 0.0, 0.0)) + ccs = file.createIfcAxis2Placement3D(point, None, None) + settings["structural_item"].ConditionCoordinateSystem = ccs - ccs = self.settings["structural_item"].ConditionCoordinateSystem - if ccs.Axis and len(self.file.get_inverse(ccs.Axis)) == 1: - self.file.remove(ccs.Axis) - ccs.Axis = self.file.createIfcDirection(self.settings["axis"]) - if ccs.RefDirection and len(self.file.get_inverse(ccs.RefDirection)) == 1: - self.file.remove(ccs.RefDirection) - ccs.RefDirection = self.file.createIfcDirection(self.settings["ref_direction"]) + ccs = settings["structural_item"].ConditionCoordinateSystem + if ccs.Axis and len(file.get_inverse(ccs.Axis)) == 1: + file.remove(ccs.Axis) + ccs.Axis = file.createIfcDirection(settings["axis"]) + if ccs.RefDirection and len(file.get_inverse(ccs.RefDirection)) == 1: + file.remove(ccs.RefDirection) + ccs.RefDirection = file.createIfcDirection(settings["ref_direction"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py index ec4b163aca..dbb2541371 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_item_axis.py @@ -17,22 +17,19 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_item=None, axis=None): - """Edits the coordinate system of a structural connection +def edit_structural_item_axis(file, structural_item=None, axis=None) -> None: + """Edits the coordinate system of a structural connection - :param structural_item: The IfcStructuralItem you want to modify. - :type structural_item: ifcopenshell.entity_instance - :param axis: The unit Z axis vector defined as a list of 3 floats. - Defaults to [0., 0., 1.]. - :type axis: list[float] - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]} + :param structural_item: The IfcStructuralItem you want to modify. + :type structural_item: ifcopenshell.entity_instance + :param axis: The unit Z axis vector defined as a list of 3 floats. + Defaults to [0., 0., 1.]. + :type axis: list[float] + :return: None + :rtype: None + """ + settings = {"structural_item": structural_item, "axis": axis or [0.0, 0.0, 1.0]} - def execute(self): - if len(self.file.get_inverse(self.settings["structural_item"].Axis)) == 1: - self.file.remove(self.settings["structural_item"].Axis) - self.settings["structural_item"].Axis = self.file.createIfcDirection(self.settings["axis"]) + if len(file.get_inverse(settings["structural_item"].Axis)) == 1: + file.remove(settings["structural_item"].Axis) + settings["structural_item"].Axis = file.createIfcDirection(settings["axis"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py index 3adba0ade9..2c577deb83 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_load=None, attributes=None): - """Edits the attributes of an IfcStructuralLoad +def edit_structural_load(file, structural_load=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralLoad - For more information about the attributes and data types of an - IfcStructuralLoad, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralLoad, consult the IFC documentation. - :param structural_load: The IfcStructuralLoad entity you want to edit - :type structural_load: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_load": structural_load, "attributes": attributes or {}} + :param structural_load: The IfcStructuralLoad entity you want to edit + :type structural_load: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"structural_load": structural_load, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["structural_load"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["structural_load"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py index cffce454bf..4c84573795 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/edit_structural_load_case.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, load_case=None, attributes=None): - """Edits the attributes of an IfcStructuralLoadCase +def edit_structural_load_case(file, load_case=None, attributes=None) -> None: + """Edits the attributes of an IfcStructuralLoadCase - For more information about the attributes and data types of an - IfcStructuralLoadCase, consult the IFC documentation. + For more information about the attributes and data types of an + IfcStructuralLoadCase, consult the IFC documentation. - :param load_case: The IfcStructuralLoadCase entity you want to edit - :type load_case: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_case": load_case, "attributes": attributes or {}} + :param load_case: The IfcStructuralLoadCase entity you want to edit + :type load_case: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"load_case": load_case, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["load_case"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["load_case"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py index 4ebff6cc3d..b238562b18 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_analysis_model.py @@ -20,28 +20,25 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, structural_analysis_model=None): - """Removes an analysis model +def remove_structural_analysis_model(file, structural_analysis_model=None) -> None: + """Removes an analysis model - Note that the contents of an analysis model are currently preserved. + Note that the contents of an analysis model are currently preserved. - :param structural_analysis_model: The IfcStructuralAnalysisModel to - remove. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_analysis_model": structural_analysis_model} + :param structural_analysis_model: The IfcStructuralAnalysisModel to + remove. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"structural_analysis_model": structural_analysis_model} - def execute(self): - for rel in self.settings["structural_analysis_model"].IsGroupedBy or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["structural_analysis_model"].OwnerHistory - self.file.remove(self.settings["structural_analysis_model"]) + for rel in settings["structural_analysis_model"].IsGroupedBy or []: + history = rel.OwnerHistory + file.remove(rel) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["structural_analysis_model"].OwnerHistory + file.remove(settings["structural_analysis_model"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py index 02cfb79e3c..7aa4f6bd74 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_boundary_condition.py @@ -17,31 +17,28 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, connection=None, boundary_condition=None): - """Removes a condition from a connection, or an orphased boundary condition +def remove_structural_boundary_condition(file, connection=None, boundary_condition=None) -> None: + """Removes a condition from a connection, or an orphased boundary condition - :param connection: The IfcStructuralConnection to remove the condition - from. If omitted, it is assumed to be an orphaned condition. - :type connection: ifcopenshell.entity_instance,optional - :param boundary_condition: The IfcBoundaryCondition to remove. - :type boundary_condition: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"connection": connection, "boundary_condition": boundary_condition} + :param connection: The IfcStructuralConnection to remove the condition + from. If omitted, it is assumed to be an orphaned condition. + :type connection: ifcopenshell.entity_instance,optional + :param boundary_condition: The IfcBoundaryCondition to remove. + :type boundary_condition: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"connection": connection, "boundary_condition": boundary_condition} - def execute(self): - if self.settings["connection"]: - # remove boundary condition from a connection - if not self.settings["connection"].AppliedCondition: - return - if len(self.file.get_inverse(self.settings["connection"].AppliedCondition)) == 1: - self.file.remove(self.settings["connection"].AppliedCondition) - self.settings["connection"].AppliedCondition = None - else: - # remove the boundary condition - for conn in self.file.get_inverse(self.settings["boundary_condition"]): - conn.AppliedCondition = None - self.file.remove(self.settings["boundary_condition"]) + if settings["connection"]: + # remove boundary condition from a connection + if not settings["connection"].AppliedCondition: + return + if len(file.get_inverse(settings["connection"].AppliedCondition)) == 1: + file.remove(settings["connection"].AppliedCondition) + settings["connection"].AppliedCondition = None + else: + # remove the boundary condition + for conn in file.get_inverse(settings["boundary_condition"]): + conn.AppliedCondition = None + file.remove(settings["boundary_condition"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py index 28ce9fc4c9..21ed51f712 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_connection_condition.py @@ -21,28 +21,25 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relation=None): - """Removes a relationship between a connection and a condition +def remove_structural_connection_condition(file, relation=None) -> None: + """Removes a relationship between a connection and a condition - The condition and the member itself is preserved. + The condition and the member itself is preserved. - :param relation: The IfcRelConnectsStructuralMember to remove. - :type relation: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"relation": relation} + :param relation: The IfcRelConnectsStructuralMember to remove. + :type relation: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"relation": relation} - def execute(self): - if self.settings["relation"].AppliedCondition: - ifcopenshell.api.run( - "structural.remove_structural_boundary_condition", - self.file, - connection=self.settings["relation"].RelatedStructuralConnection - ) - history = self.settings["relation"].OwnerHistory - self.file.remove(self.settings["relation"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if settings["relation"].AppliedCondition: + ifcopenshell.api.run( + "structural.remove_structural_boundary_condition", + file, + connection=settings["relation"].RelatedStructuralConnection, + ) + history = settings["relation"].OwnerHistory + file.remove(settings["relation"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py index 55b83a7f1b..afe97029ab 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load.py @@ -17,17 +17,14 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, structural_load=None): - """Removes a structural load +def remove_structural_load(file, structural_load=None) -> None: + """Removes a structural load - :param structural_load: The IfcStructuralLoad to remove. - :type structural_load: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"structural_load": structural_load} + :param structural_load: The IfcStructuralLoad to remove. + :type structural_load: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"structural_load": structural_load} - def execute(self): - self.file.remove(self.settings["structural_load"]) + file.remove(settings["structural_load"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py index e331309239..de317ed354 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_case.py @@ -21,25 +21,22 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, load_case=None): - """Removes a structural load case +def remove_structural_load_case(file, load_case=None) -> None: + """Removes a structural load case - :param load_case: The IfcStructuralLoadCase to remove. - :type load_case: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_case": load_case} + :param load_case: The IfcStructuralLoadCase to remove. + :type load_case: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"load_case": load_case} - def execute(self): - # TODO: do a deep purge - for rel in self.settings["load_case"].IsGroupedBy or []: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["load_case"].OwnerHistory - self.file.remove(self.settings["load_case"]) - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for rel in settings["load_case"].IsGroupedBy or []: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["load_case"].OwnerHistory + file.remove(settings["load_case"]) + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py index 93500aba1b..541dd87811 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/remove_structural_load_group.py @@ -21,27 +21,24 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, load_group=None): - """Removes a structural load group +def remove_structural_load_group(file, load_group=None) -> None: + """Removes a structural load group - :param load_group: The IfcStructuralLoadGroup to remove. - :type load_group: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = {"load_group": load_group} + :param load_group: The IfcStructuralLoadGroup to remove. + :type load_group: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = {"load_group": load_group} - def execute(self): - # TODO: do a deep purge - for inverse in self.file.get_inverse(self.settings["load_group"]): - if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["load_group"].OwnerHistory - self.file.remove(self.settings["load_group"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + # TODO: do a deep purge + for inverse in file.get_inverse(settings["load_group"]): + if inverse.is_a("IfcRelAssignsToGroup") and len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["load_group"].OwnerHistory + file.remove(settings["load_group"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py index 5a86a6a9f3..b4dedc2832 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py +++ b/src/ifcopenshell-python/ifcopenshell/api/structural/unassign_structural_analysis_model.py @@ -21,35 +21,32 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, product=None, structural_analysis_model=None): - """Removes a relationship between a structural element and the analysis model +def unassign_structural_analysis_model(file, product=None, structural_analysis_model=None) -> None: + """Removes a relationship between a structural element and the analysis model - :param product: The structural element that is part of the analysis. - :type product: ifcopenshell.entity_instance - :param structural_analysis_model: The IfcStructuralAnalysisModel that - the structural element is related to. - :type structural_analysis_model: ifcopenshell.entity_instance - :return: None - :rtype: None - """ - self.file = file - self.settings = { - "product": product, - "structural_analysis_model": structural_analysis_model, - } + :param product: The structural element that is part of the analysis. + :type product: ifcopenshell.entity_instance + :param structural_analysis_model: The IfcStructuralAnalysisModel that + the structural element is related to. + :type structural_analysis_model: ifcopenshell.entity_instance + :return: None + :rtype: None + """ + settings = { + "product": product, + "structural_analysis_model": structural_analysis_model, + } - def execute(self): - if not self.settings["structural_analysis_model"].IsGroupedBy: - return - rel = self.settings["structural_analysis_model"].IsGroupedBy[0] - related_objects = set(rel.RelatedObjects) or set() - related_objects.remove(self.settings["product"]) - if len(related_objects): - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if not settings["structural_analysis_model"].IsGroupedBy: + return + rel = settings["structural_analysis_model"].IsGroupedBy[0] + related_objects = set(rel.RelatedObjects) or set() + related_objects.remove(settings["product"]) + if len(related_objects): + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) + else: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py index e0caddbe3c..df9fd47518 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_style import add_style +from .add_surface_style import add_surface_style +from .add_surface_textures import add_surface_textures +from .assign_material_style import assign_material_style +from .assign_representation_styles import assign_representation_styles +from .edit_presentation_style import edit_presentation_style +from .edit_surface_style import edit_surface_style +from .remove_style import remove_style +from .remove_styled_representation import remove_styled_representation +from .remove_surface_style import remove_surface_style +from .unassign_material_style import unassign_material_style +from .unassign_representation_styles import unassign_representation_styles diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py index 650043039f..599feaef3d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_style.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, name=None, ifc_class="IfcSurfaceStyle"): - """Add a new presentation style +def add_style(file, name=None, ifc_class="IfcSurfaceStyle") -> None: + """Add a new presentation style - A presentation style is a container of visual settings (called - presentation items) that affect the appearance of objects. There are - four types of style: + A presentation style is a container of visual settings (called + presentation items) that affect the appearance of objects. There are + four types of style: - - Surface styles, which give 3D objects (which have surfaces / faces) - their colours and textures. This is the most common type of style. - - Curve styles, which give 2D and 3D curves, lines, polylines, their - stroke thickness and colour. - - Fill area styles, which gives 2D polygons and flat 3D planes their - colours, hatch patterns, tiled patterns, and pattern scales. - - Text styles, which gives text their font family, weight, variant, - size, indentation, alignment, decoration, spacing, and transformation. + - Surface styles, which give 3D objects (which have surfaces / faces) + their colours and textures. This is the most common type of style. + - Curve styles, which give 2D and 3D curves, lines, polylines, their + stroke thickness and colour. + - Fill area styles, which gives 2D polygons and flat 3D planes their + colours, hatch patterns, tiled patterns, and pattern scales. + - Text styles, which gives text their font family, weight, variant, + size, indentation, alignment, decoration, spacing, and transformation. - Once you have created a presentation style object, you can further - define the properties of your style using other API functions by adding - presentation items, such as ifcopenshell.api.style.add_surface_style. + Once you have created a presentation style object, you can further + define the properties of your style using other API functions by adding + presentation items, such as ifcopenshell.api.style.add_surface_style. - :param name: The name of the style. Used to easily identify it using a - style library. - :type name: str,optional - :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle, - IfcFillAreaStyle, or IfcTextStyle. - :type ifc_class: str - :return: The newly created style element, based on the provided - ifc_class. - :rtype: ifcopenshell.entity_instance + :param name: The name of the style. Used to easily identify it using a + style library. + :type name: str,optional + :param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle, + IfcFillAreaStyle, or IfcTextStyle. + :type ifc_class: str + :return: The newly created style element, based on the provided + ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - """ - self.file = file - self.settings = {"name": name, "ifc_class": ifc_class} + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + """ + settings = {"name": name, "ifc_class": ifc_class} - def execute(self): - if self.settings["ifc_class"] == "IfcSurfaceStyle": - # Name is filled out because Revit treats this incorrectly as the material name - return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH") + if settings["ifc_class"] == "IfcSurfaceStyle": + # Name is filled out because Revit treats this incorrectly as the material name + return file.createIfcSurfaceStyle(settings["name"], "BOTH") diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py index 32064811f9..c8f9c415d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_style.py @@ -20,117 +20,112 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None): - """Adds a new presentation item to a surface style +def add_surface_style(file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None) -> None: + """Adds a new presentation item to a surface style - A surface style can have multiple different types of presentation items - assigned to it: + A surface style can have multiple different types of presentation items + assigned to it: - - Shading, this is the simplest item, which defines a single basic - colour and transparency that can be used to display the object on a - screen. It is an indicative colour of what the object would be in real - life. It is commonly incorrectly abused to colour code systems for MEP - equipment or object types for structural steel. If you just want to - give something a colour, this is what you need. - - Rendering, this is an advanced extension of shading, which includes - the definition of a shader for a rendering engine. You may select the - reflectance / lighting model such as PHYSICAL, for PBR style - rendering, or FLAT, for flat shading, or PHONG for older biased - rendering workflows. Based on the chosen lighting model, you may then - specify the appropriate colour maps, such as diffuse colours, - specularity, emissive component, etc. These lighting models are fully - compatible with glTF and X3D. This should be used if your model is - prepared to be rendered by a rendering engine which is compatible with - glTF / X3D shader descriptions. If you are doing archviz or 3D - rendering, this is what you need. - - Textures, this is a special type of Rendering presentation item that - uses image textures instead of single colours. Textures may be either - mapped using a bounding box stretch mapping, or with UV coordinates - for mesh-like geometry. - - Lighting, this is used to define photometrically accurate colour - parameters used in lighting simulation. If you are a simulationist, - this is what you need. - - Reflectance, this is a special type of Lighting presentation item - which includes some lesser used photometric properties, typically - required for advanced materials like glazing. - - External, this is for any other surface style defined using an - external URI. This is relevant if you are using a third-party non-glTF - compatible shader definition such as for Cycles, Renderman, V-Ray, - etc, or a complex lighting simulation definition, such as for - Radiance. + - Shading, this is the simplest item, which defines a single basic + colour and transparency that can be used to display the object on a + screen. It is an indicative colour of what the object would be in real + life. It is commonly incorrectly abused to colour code systems for MEP + equipment or object types for structural steel. If you just want to + give something a colour, this is what you need. + - Rendering, this is an advanced extension of shading, which includes + the definition of a shader for a rendering engine. You may select the + reflectance / lighting model such as PHYSICAL, for PBR style + rendering, or FLAT, for flat shading, or PHONG for older biased + rendering workflows. Based on the chosen lighting model, you may then + specify the appropriate colour maps, such as diffuse colours, + specularity, emissive component, etc. These lighting models are fully + compatible with glTF and X3D. This should be used if your model is + prepared to be rendered by a rendering engine which is compatible with + glTF / X3D shader descriptions. If you are doing archviz or 3D + rendering, this is what you need. + - Textures, this is a special type of Rendering presentation item that + uses image textures instead of single colours. Textures may be either + mapped using a bounding box stretch mapping, or with UV coordinates + for mesh-like geometry. + - Lighting, this is used to define photometrically accurate colour + parameters used in lighting simulation. If you are a simulationist, + this is what you need. + - Reflectance, this is a special type of Lighting presentation item + which includes some lesser used photometric properties, typically + required for advanced materials like glazing. + - External, this is for any other surface style defined using an + external URI. This is relevant if you are using a third-party non-glTF + compatible shader definition such as for Cycles, Renderman, V-Ray, + etc, or a complex lighting simulation definition, such as for + Radiance. - Shading is sufficient for the majority of basic models. + Shading is sufficient for the majority of basic models. - The attributes you specify will depend on the type of presentation item - you are adding. An example is shown below, but for full details please - refer to the IFC documentation. + The attributes you specify will depend on the type of presentation item + you are adding. An example is shown below, but for full details please + refer to the IFC documentation. - :param style: The IfcSurfaceStyle you want to add to presentation item - to. See ifcopenshell.api.style.add_style. - :type style: ifcopenshell.entity_instance - :param ifc_class: Choose from IfcSurfaceStyleShading, - IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, - IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or - IfcExternallyDefinedSurfaceStyle. - :type ifc_class: str - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: The newly created presentation item based on the provided - ifc_class. - :rtype: ifcopenshell.entity_instance + :param style: The IfcSurfaceStyle you want to add to presentation item + to. See ifcopenshell.api.style.add_style. + :type style: ifcopenshell.entity_instance + :param ifc_class: Choose from IfcSurfaceStyleShading, + IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, + IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or + IfcExternallyDefinedSurfaceStyle. + :type ifc_class: str + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: The newly created presentation item based on the provided + ifc_class. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Create a simple shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + # Create a simple shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) - # Alternatively, create a rendering style. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleRendering", attributes={ - # A surface colour and transparency is still supplied for - # viewport display only. This will supersede the shading - # presentation item. - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent + # Alternatively, create a rendering style. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleRendering", attributes={ + # A surface colour and transparency is still supplied for + # viewport display only. This will supersede the shading + # presentation item. + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent - # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting - # model. In IFC4X3, you may choose PHYSICAL directly. - "ReflectanceMethod": "NOTDEFINED", + # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting + # model. In IFC4X3, you may choose PHYSICAL directly. + "ReflectanceMethod": "NOTDEFINED", - # For PBR shading, you may specify these parameters: - "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, - "SpecularColour": 0.1, # Metallic factor - "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor - }) - """ - self.file = file - self.settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}} + # For PBR shading, you may specify these parameters: + "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, + "SpecularColour": 0.1, # Metallic factor + "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor + }) + """ + settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}} - def execute(self): - style_item = self.file.create_entity(self.settings["ifc_class"]) - ifcopenshell.api.run( - "style.edit_surface_style", self.file, style=style_item, attributes=self.settings["attributes"] - ) - styles = list(self.settings["style"].Styles or []) + style_item = file.create_entity(settings["ifc_class"]) + ifcopenshell.api.run("style.edit_surface_style", file, style=style_item, attributes=settings["attributes"]) + styles = list(settings["style"].Styles or []) - select_class = self.settings["ifc_class"] - if select_class == "IfcSurfaceStyleRendering": - select_class = "IfcSurfaceStyleShading" - duplicate_items = [s for s in styles if s.is_a(select_class)] - for duplicate_item in duplicate_items: - ifcopenshell.api.run("style.remove_surface_style", self.file, style=duplicate_item) + select_class = settings["ifc_class"] + if select_class == "IfcSurfaceStyleRendering": + select_class = "IfcSurfaceStyleShading" + duplicate_items = [s for s in styles if s.is_a(select_class)] + for duplicate_item in duplicate_items: + ifcopenshell.api.run("style.remove_surface_style", file, style=duplicate_item) - styles = list(self.settings["style"].Styles or []) - styles.append(style_item) - self.settings["style"].Styles = styles - return style_item + styles = list(settings["style"].Styles or []) + styles.append(style_item) + settings["style"].Styles = styles + return style_item diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py index 88aabfe801..9b6fbda053 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/add_surface_textures.py @@ -20,39 +20,42 @@ import ifcopenshell import ifcopenshell.api +def add_surface_textures(file, material=None, uv_maps=None, textures=None) -> None: + """Add surface texture based on a Blender material definition or texture data. + + :param material: The Blender material definition with a node tree that + is compatible with glTF. See one of the valid combinations here: + https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html + :type material: bpy.types.Material + :param uv_maps: A list of IfcIndexedTextureMap for any + IfcTessellatedFaceSets that the representation has, obtained from + the HasTextures attribute. + :type uv_maps: list[ifcopenshell.entity_instance] + :param textures: A list of dictionaries containing: + + 1. Attributes to create IfcImageTexture. + 2. One additional parameter `uv_mode` to map IfcImageTexture to correct + IfcTextureCoordinate type. + + Possible `uv_mode` values: + + * `UV` - use IfcTextureCoordinate from `uv_maps` parameter; + * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV + based on geometry); + * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV + based on camera position) + :type textures: list[dict] + :return: A list of IfcImageTexture + :rtype: list[ifcopenshell.entity_instance] + """ + usecase = Usecase() + # TODO: This usecase currently depends on Blender's data model + usecase.file = file + usecase.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []} + return usecase.execute() + + class Usecase: - def __init__(self, file, material=None, uv_maps=None, textures=None): - """Add surface texture based on a Blender material definition or texture data. - - :param material: The Blender material definition with a node tree that - is compatible with glTF. See one of the valid combinations here: - https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html - :type material: bpy.types.Material - :param uv_maps: A list of IfcIndexedTextureMap for any - IfcTessellatedFaceSets that the representation has, obtained from - the HasTextures attribute. - :type uv_maps: list[ifcopenshell.entity_instance] - :param textures: A list of dictionaries containing: - - 1. Attributes to create IfcImageTexture. - 2. One additional parameter `uv_mode` to map IfcImageTexture to correct - IfcTextureCoordinate type. - - Possible `uv_mode` values: - - * `UV` - use IfcTextureCoordinate from `uv_maps` parameter; - * `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV - based on geometry); - * `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV - based on camera position) - :type textures: list[dict] - :return: A list of IfcImageTexture - :rtype: list[ifcopenshell.entity_instance] - """ - # TODO: This usecase currently depends on Blender's data model - self.file = file - self.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []} - def execute(self): if self.file.schema == "IFC2X3": # TODO: research how compatible IFC2X3 and IFC4 textures are diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py index 06d3a6339a..630a842bf6 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_material_style.py @@ -21,91 +21,96 @@ import ifcopenshell.api import ifcopenshell.util.element +def assign_material_style( + file, material=None, style=None, context=None, should_use_presentation_style_assignment=False +) -> None: + """Assigns a style to a material + + A style may either be assigned directly to an object's representation, + or to a material which is then associated with the object. If both + exist, then the style assigned directly to the object's representation + takes precedence. It is recommended to use materials and assign styles + to materials. This API function provides that capability. + + :param material: The IfcMaterial which you want to assign the style to. + :type material: ifcopenshell.entity_instance + :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that + you want to assign to the material. This will then be applied to all + objects that have that material. + :type style: ifcopenshell.entity_instance + :param context: The IfcGeometricRepresentationSubContext at which this + style should be used. Typically this is the Model BODY context. + :type context: ifcopenshell.entity_instance + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: None + :rtype: None + + Example: + + .. code:: python + + # A model context is needed to store 3D geometry + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # Specifically, we want to store body geometry + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Let's prepare a concrete material. Note that our concrete material + # does not have any colours (styles) at this point. + concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") + + # Assign our concrete material to our wall + ifcopenshell.api.run("material.assign_material", model, + products=[wall], type="IfcMaterial", material=concrete) + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a simple grey shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) + + # Now any element (like our wall) with a concrete material will have + # a grey colour applied. + ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "material": material, + "style": style, + "context": context, + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, material=None, style=None, context=None, should_use_presentation_style_assignment=False): - """Assigns a style to a material - - A style may either be assigned directly to an object's representation, - or to a material which is then associated with the object. If both - exist, then the style assigned directly to the object's representation - takes precedence. It is recommended to use materials and assign styles - to materials. This API function provides that capability. - - :param material: The IfcMaterial which you want to assign the style to. - :type material: ifcopenshell.entity_instance - :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that - you want to assign to the material. This will then be applied to all - objects that have that material. - :type style: ifcopenshell.entity_instance - :param context: The IfcGeometricRepresentationSubContext at which this - style should be used. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: None - :rtype: None - - Example: - - .. code:: python - - # A model context is needed to store 3D geometry - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # Specifically, we want to store body geometry - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - - # Let's prepare a concrete material. Note that our concrete material - # does not have any colours (styles) at this point. - concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete") - - # Assign our concrete material to our wall - ifcopenshell.api.run("material.assign_material", model, - products=[wall], type="IfcMaterial", material=concrete) - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a simple grey shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) - - # Now any element (like our wall) with a concrete material will have - # a grey colour applied. - ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body) - """ - self.file = file - self.settings = { - "material": material, - "style": style, - "context": context, - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): self.style = self.settings["style"] if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py index c6236c8a4a..e2f5daf766 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/assign_representation_styles.py @@ -17,98 +17,100 @@ # along with IfcOpenShell. If not, see . +def assign_representation_styles( + file, + shape_representation=None, + styles=None, + replace_previous_same_type_style=True, + should_use_presentation_style_assignment=False, +) -> None: + """Assigns a style directly to an object representation + + A style may either be assigned directly to an object's representation, + or to a material which is then associated with the object. If both + exist, then the style assigned directly to the object's representation + takes precedence. It is recommended to use materials and assign styles + to materials. However, sometimes you may want to assign colours directly + to the object representation as an override. This API function provides + that capability. + + If you want to assign styles to a material instead (recommended), then + please see ifcopenshell.api.style.assign_material_style. + + :param shape_representation: The IfcShapeRepresentation of the object + that you want to assign styles to. This implicitly defines the + context at which the styles should be used. + :type shape_representation: ifcopenshell.entity_instance + :param styles: A list of presentation styles, typically IfcSurfaceStyle. + The number of items in the list should correlate with the number of + items in the shape_representation's Items attribute. If you have + more items than styles, the last style is used. + :type styles: list[ifcopenshell.entity_instance] + :param replace_previous_same_type_style: Remove previously assigned styles + of the same type as currently assign style`. Defaults to `True`. + :type replace_previous_same_type_style: bool + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: List of created IfcStyledItems + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A model context is needed to store 3D geometry + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + + # Specifically, we want to store body geometry + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Let's create a new wall. The wall does not have any geometry yet. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a simple grey shading colour and transparency. + ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) + + # Now specifically our wall only will be coloured grey. + ifcopenshell.api.run("style.assign_representation_styles", model, + shape_representation=representation, styles=[style]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "shape_representation": shape_representation, + "styles": styles or [], + "replace_previous_same_type_style": replace_previous_same_type_style, + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file, - shape_representation=None, - styles=None, - replace_previous_same_type_style=True, - should_use_presentation_style_assignment=False, - ): - """Assigns a style directly to an object representation - - A style may either be assigned directly to an object's representation, - or to a material which is then associated with the object. If both - exist, then the style assigned directly to the object's representation - takes precedence. It is recommended to use materials and assign styles - to materials. However, sometimes you may want to assign colours directly - to the object representation as an override. This API function provides - that capability. - - If you want to assign styles to a material instead (recommended), then - please see ifcopenshell.api.style.assign_material_style. - - :param shape_representation: The IfcShapeRepresentation of the object - that you want to assign styles to. This implicitly defines the - context at which the styles should be used. - :type shape_representation: ifcopenshell.entity_instance - :param styles: A list of presentation styles, typically IfcSurfaceStyle. - The number of items in the list should correlate with the number of - items in the shape_representation's Items attribute. If you have - more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance] - :param replace_previous_same_type_style: Remove previously assigned styles - of the same type as currently assign style`. Defaults to `True`. - :type replace_previous_same_type_style: bool - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: List of created IfcStyledItems - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A model context is needed to store 3D geometry - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - - # Specifically, we want to store body geometry - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Let's create a new wall. The wall does not have any geometry yet. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a simple grey shading colour and transparency. - ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) - - # Now specifically our wall only will be coloured grey. - ifcopenshell.api.run("style.assign_representation_styles", model, - shape_representation=representation, styles=[style]) - """ - self.file = file - self.settings = { - "shape_representation": shape_representation, - "styles": styles or [], - "replace_previous_same_type_style": replace_previous_same_type_style, - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): if not self.settings["styles"]: return [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py index 877d0f89c2..268acfdc2e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_presentation_style.py @@ -17,33 +17,30 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, style=None, attributes=None): - """Edits the attributes of an IfcPresentationStyle +def edit_presentation_style(file, style=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationStyle - For more information about the attributes and data types of an - IfcPresentationStyle, consult the IFC documentation. + For more information about the attributes and data types of an + IfcPresentationStyle, consult the IFC documentation. - :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param style: The IfcPresentationStyle entity you want to edit + :type style: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Change the name of the style to "Foo" - ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"}) - """ - self.file = file - self.settings = {"style": style, "attributes": attributes or {}} + # Change the name of the style to "Foo" + ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"}) + """ + settings = {"style": style, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["style"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["style"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py index 20c1002fdf..b8c7a30be8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/edit_surface_style.py @@ -17,61 +17,64 @@ # along with IfcOpenShell. If not, see . +def edit_surface_style(file, style=None, attributes=None) -> None: + """Edits the attributes of an IfcPresentationItem + + For more information about the attributes and data types of an + IfcPresentationItem, consult the IFC documentation. + + The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading, + IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, + IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or + IfcExternallyDefinedSurfaceStyle. + + To represent a colour, a nested dictionary should be used. See the + example below. + + :param style: The IfcPresentationStyle entity you want to edit + :type style: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Create a blank rendering style. + rendering = ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleRendering") + + # Edit the attributes of the rendering style. + ifcopenshell.api.run("style.edit_surface_style", model, + style=rendering, attributes={ + # A surface colour and transparency is still supplied for + # viewport display only. This will supersede the shading + # presentation item. + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + + # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting + # model. In IFC4X3, you may choose PHYSICAL directly. + "ReflectanceMethod": "NOTDEFINED", + + # For PBR shading, you may specify these parameters: + "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, + "SpecularColour": 0.1, # Metallic factor + "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor + }) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"style": style, "attributes": attributes or {}} + return usecase.execute() + + class Usecase: - def __init__(self, file, style=None, attributes=None): - """Edits the attributes of an IfcPresentationItem - - For more information about the attributes and data types of an - IfcPresentationItem, consult the IFC documentation. - - The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading, - IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures, - IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or - IfcExternallyDefinedSurfaceStyle. - - To represent a colour, a nested dictionary should be used. See the - example below. - - :param style: The IfcPresentationStyle entity you want to edit - :type style: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Create a blank rendering style. - rendering = ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleRendering") - - # Edit the attributes of the rendering style. - ifcopenshell.api.run("style.edit_surface_style", model, - style=rendering, attributes={ - # A surface colour and transparency is still supplied for - # viewport display only. This will supersede the shading - # presentation item. - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - - # NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting - # model. In IFC4X3, you may choose PHYSICAL directly. - "ReflectanceMethod": "NOTDEFINED", - - # For PBR shading, you may specify these parameters: - "DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 }, - "SpecularColour": 0.1, # Metallic factor - "SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor - }) - """ - self.file = file - self.settings = {"style": style, "attributes": attributes or {}} - def execute(self): attributes = {} for attribute in self.settings["style"].wrapped_data.declaration().as_entity().all_attributes(): diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py index 40692982bb..453f511f2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_style.py @@ -19,30 +19,33 @@ import ifcopenshell.util.element +def remove_style(file, style=None) -> None: + """Removes a presentation style + + All of the presentation items of the style will also be removed. + + :param style: The IfcPresentationStyle to remove. + :type style: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) + + # Not anymore! + ifcopenshell.api.run("style.remove_style", model, style=style) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"style": style} + return usecase.execute() + + class Usecase: - def __init__(self, file, style=None): - """Removes a presentation style - - All of the presentation items of the style will also be removed. - - :param style: The IfcPresentationStyle to remove. - :type style: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) - - # Not anymore! - ifcopenshell.api.run("style.remove_style", model, style=style) - """ - self.file = file - self.settings = {"style": style} - def execute(self): self.purge_styled_items(self.settings["style"]) for style in self.settings["style"].Styles or []: diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py index ab061e182c..62ab7e4973 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_styled_representation.py @@ -17,38 +17,35 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, representation=None): - """Removes a styled representation +def remove_styled_representation(file, representation=None) -> None: + """Removes a styled representation - Styled representations are typically associated with materials. This - removes the representation but not the underlying styles. + Styled representations are typically associated with materials. This + removes the representation but not the underlying styles. - :param representation: The IfcStyledRepresentation to remove. - :type representation: ifcopenshell.entity_instance - :return: None - :rtype: None + :param representation: The IfcStyledRepresentation to remove. + :type representation: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Remove a styled representation - ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation) - """ - self.file = file - self.settings = {"representation": representation} + # Remove a styled representation + ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation) + """ + settings = {"representation": representation} - def execute(self): - for inverse in self.file.get_inverse(self.settings["representation"]): - if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1: - self.file.remove(inverse) + for inverse in file.get_inverse(settings["representation"]): + if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1: + file.remove(inverse) - for item in self.settings["representation"].Items: - if item.is_a("IfcStyledItem") and self.file.get_total_inverses(item) == 1: - for style in item.Styles: - if style.is_a("IfcPresentationStyleAssignment"): - self.file.remove(style) - self.file.remove(item) + for item in settings["representation"].Items: + if item.is_a("IfcStyledItem") and file.get_total_inverses(item) == 1: + for style in item.Styles: + if style.is_a("IfcPresentationStyleAssignment"): + file.remove(style) + file.remove(item) - self.file.remove(self.settings["representation"]) + file.remove(settings["representation"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py index ce214dbf21..9621d5b51a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/remove_surface_style.py @@ -20,50 +20,47 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, style=None): - """Removes a presentation item from a presentation style +def remove_surface_style(file, style=None) -> None: + """Removes a presentation item from a presentation style - :param style: The IfcPresentationItem to remove. - :type style: ifcopenshell.entity_instance - :return: None - :rtype: None + :param style: The IfcPresentationItem to remove. + :type style: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a new surface style - style = ifcopenshell.api.run("style.add_style", model) + # Create a new surface style + style = ifcopenshell.api.run("style.add_style", model) - # Create a simple shading colour and transparency. - shading = ifcopenshell.api.run("style.add_surface_style", model, - style=style, ifc_class="IfcSurfaceStyleShading", attributes={ - "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, - "Transparency": 0., # 0 is opaque, 1 is transparent - }) + # Create a simple shading colour and transparency. + shading = ifcopenshell.api.run("style.add_surface_style", model, + style=style, ifc_class="IfcSurfaceStyleShading", attributes={ + "SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 }, + "Transparency": 0., # 0 is opaque, 1 is transparent + }) - # Remove the shading item - ifcopenshell.api.run("style.remove_surface_style", model, style=shading) - """ - self.file = file - self.settings = {"style": style} + # Remove the shading item + ifcopenshell.api.run("style.remove_surface_style", model, style=shading) + """ + settings = {"style": style} - def execute(self): - to_delete = set() - if self.settings["style"].is_a("IfcSurfaceStyleWithTextures"): - for texture in self.settings["style"].Textures or []: - if texture.IsMappedBy: - for coordinate in texture.IsMappedBy: - to_delete.add(coordinate) - else: - to_delete.add(texture) + to_delete = set() + if settings["style"].is_a("IfcSurfaceStyleWithTextures"): + for texture in settings["style"].Textures or []: + if texture.IsMappedBy: + for coordinate in texture.IsMappedBy: + to_delete.add(coordinate) + else: + to_delete.add(texture) - for attribute in self.settings["style"]: - if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id(): - to_delete.add(attribute) + for attribute in settings["style"]: + if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id(): + to_delete.add(attribute) - self.file.remove(self.settings["style"]) + file.remove(settings["style"]) - for element in to_delete: - ifcopenshell.util.element.remove_deep2(self.file, element) + for element in to_delete: + ifcopenshell.util.element.remove_deep2(file, element) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py index f1e2e7e85b..59b37a1935 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_material_style.py @@ -20,78 +20,75 @@ import ifcopenshell -class Usecase: - def __init__(self, file, material=None, style=None, context=None): - """Unassigns a style to a material +def unassign_material_style(file, material=None, style=None, context=None) -> None: + """Unassigns a style to a material - This does the inverse of assign_material_style. + This does the inverse of assign_material_style. - :param material: The IfcMaterial which you want to unassign the style from. - :type material: ifcopenshell.entity_instance - :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that - you want to unassign from material. This will then be applied to all - objects that have that material. - :type style: ifcopenshell.entity_instance - :param context: The IfcGeometricRepresentationSubContext at which this - style should be unassigned. Typically this is the Model BODY context. - :type context: ifcopenshell.entity_instance - :return: None - :rtype: None + :param material: The IfcMaterial which you want to unassign the style from. + :type material: ifcopenshell.entity_instance + :param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that + you want to unassign from material. This will then be applied to all + objects that have that material. + :type style: ifcopenshell.entity_instance + :param context: The IfcGeometricRepresentationSubContext at which this + style should be unassigned. Typically this is the Model BODY context. + :type context: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body) - """ - self.file = file - self.settings = { - "material": material, - "style": style, - "context": context, - } + ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body) + """ + settings = { + "material": material, + "style": style, + "context": context, + } - def execute(self): - for definition in self.settings["material"].HasRepresentation: - for representation in definition.Representations: - if not representation.is_a("IfcStyledRepresentation"): - continue - if representation.ContextOfItems != self.settings["context"]: - continue - for item in representation.Items: - if not item.is_a("IfcStyledItem"): - continue - styles = [s for s in item.Styles if s != self.settings["style"]] - if not styles: - self.file.remove(item) - elif len(styles) != len(item.Styles): - item.Styles = styles - if not representation.Items: - self.file.remove(representation) - if not definition.Representations: - self.file.remove(definition) - - # handle material constituents and shape aspects - material_constituents_names = [] - for inverse in self.file.get_inverse(self.settings["material"]): - if inverse.is_a("IfcMaterialConstituent") and inverse.Name: - material_constituents_names.append(inverse.Name) - if not material_constituents_names: - return - - elements = ifcopenshell.util.element.get_elements_by_material(self.file, self.settings["material"]) - shape_aspects = [] - for element in elements: - shape_aspects += ifcopenshell.util.element.get_shape_aspects(element) - - for shape_aspect in shape_aspects: - if shape_aspect.Name not in material_constituents_names: + for definition in settings["material"].HasRepresentation: + for representation in definition.Representations: + if not representation.is_a("IfcStyledRepresentation"): continue + if representation.ContextOfItems != settings["context"]: + continue + for item in representation.Items: + if not item.is_a("IfcStyledItem"): + continue + styles = [s for s in item.Styles if s != settings["style"]] + if not styles: + file.remove(item) + elif len(styles) != len(item.Styles): + item.Styles = styles + if not representation.Items: + file.remove(representation) + if not definition.Representations: + file.remove(definition) - for rep in shape_aspect.ShapeRepresentations: - ifcopenshell.api.run( - "style.unassign_representation_styles", - self.file, - shape_representation=rep, - styles=[self.settings["style"]], - ) + # handle material constituents and shape aspects + material_constituents_names = [] + for inverse in file.get_inverse(settings["material"]): + if inverse.is_a("IfcMaterialConstituent") and inverse.Name: + material_constituents_names.append(inverse.Name) + if not material_constituents_names: + return + + elements = ifcopenshell.util.element.get_elements_by_material(file, settings["material"]) + shape_aspects = [] + for element in elements: + shape_aspects += ifcopenshell.util.element.get_shape_aspects(element) + + for shape_aspect in shape_aspects: + if shape_aspect.Name not in material_constituents_names: + continue + + for rep in shape_aspect.ShapeRepresentations: + ifcopenshell.api.run( + "style.unassign_representation_styles", + file, + shape_representation=rep, + styles=[settings["style"]], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py index 83f60fe3d4..14f52e9c6e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py +++ b/src/ifcopenshell-python/ifcopenshell/api/style/unassign_representation_styles.py @@ -17,43 +17,48 @@ # along with IfcOpenShell. If not, see . +def unassign_representation_styles( + file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False +) -> None: + """Unassigns styles directly assigned to an object representation + + This does the inverse of assign_representation_styles. + + :param shape_representation: The IfcShapeRepresentation of the object + that you want to unassign styles from. + :type shape_representation: ifcopenshell.entity_instance + :param styles: A list of presentation styles, typically IfcSurfaceStyle. + The number of items in the list should correlate with the number of + items in the shape_representation's Items attribute. If you have + more items than styles, the last style is used. + :type styles: list[ifcopenshell.entity_instance] + :param should_use_presentation_style_assignment: This is a technical + detail to accomodate a bug in Revit. This should always be left as + the default of False, unless you are finding that colours aren't + showing up in Revit. In that case, set it to True, but keep in mind + that this is no longer a valid IFC. Blame Autodesk. + :type should_use_presentation_style_assignment: bool + :return: None + :rtype: None + + Example: + + .. code:: python + + ifcopenshell.api.run("style.unassign_representation_styles", model, + shape_representation=representation, styles=[style]) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "shape_representation": shape_representation, + "styles": styles or [], + "should_use_presentation_style_assignment": should_use_presentation_style_assignment, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False): - """Unassigns styles directly assigned to an object representation - - This does the inverse of assign_representation_styles. - - :param shape_representation: The IfcShapeRepresentation of the object - that you want to unassign styles from. - :type shape_representation: ifcopenshell.entity_instance - :param styles: A list of presentation styles, typically IfcSurfaceStyle. - The number of items in the list should correlate with the number of - items in the shape_representation's Items attribute. If you have - more items than styles, the last style is used. - :type styles: list[ifcopenshell.entity_instance] - :param should_use_presentation_style_assignment: This is a technical - detail to accomodate a bug in Revit. This should always be left as - the default of False, unless you are finding that colours aren't - showing up in Revit. In that case, set it to True, but keep in mind - that this is no longer a valid IFC. Blame Autodesk. - :type should_use_presentation_style_assignment: bool - :return: None - :rtype: None - - Example: - - .. code:: python - - ifcopenshell.api.run("style.unassign_representation_styles", model, - shape_representation=representation, styles=[style]) - """ - self.file = file - self.settings = { - "shape_representation": shape_representation, - "styles": styles or [], - "should_use_presentation_style_assignment": should_use_presentation_style_assignment, - } - def execute(self): if not self.settings["styles"]: return [] diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py index e0caddbe3c..14213ca168 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/__init__.py @@ -15,3 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_port import add_port +from .add_system import add_system +from .assign_flow_control import assign_flow_control +from .assign_port import assign_port +from .assign_system import assign_system +from .connect_port import connect_port +from .disconnect_port import disconnect_port +from .edit_system import edit_system +from .remove_system import remove_system +from .unassign_flow_control import unassign_flow_control +from .unassign_port import unassign_port +from .unassign_system import unassign_system diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py index f792ecc2fa..a3664cffbb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_port.py @@ -20,44 +20,41 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, element=None): - """Adds a new distribution port to an element +def add_port(file, element=None) -> None: + """Adds a new distribution port to an element - A distribution port represents a connection point on an element, where - a distribution element may be connected to another distribution element. - For example, a duct segment will typically have two ports, one at either - end, because you can attach another segment or fitting to either end of - the duct segment. + A distribution port represents a connection point on an element, where + a distribution element may be connected to another distribution element. + For example, a duct segment will typically have two ports, one at either + end, because you can attach another segment or fitting to either end of + the duct segment. - This will both add a distribution port and automatically assign it to a - distribution element. + This will both add a distribution port and automatically assign it to a + distribution element. - :param element: The IfcDistributionElement you want to add a - distribution port to. - :type element: ifcopenshell.entity_instance - :return: The newly created IfcDistributionPort - :rtype: ifcopenshell.entity_instance + :param element: The IfcDistributionElement you want to add a + distribution port to. + :type element: ifcopenshell.entity_instance + :return: The newly created IfcDistributionPort + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - """ - self.file = file - self.settings = { - "element": element, - } + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + """ + settings = { + "element": element, + } - def execute(self): - port = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDistributionPort") - if self.settings["element"]: - ifcopenshell.api.run("system.assign_port", self.file, element=self.settings["element"], port=port) - return port + port = ifcopenshell.api.run("root.create_entity", file, ifc_class="IfcDistributionPort") + if settings["element"]: + ifcopenshell.api.run("system.assign_port", file, element=settings["element"], port=port) + return port diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py index 26c8cfe8fc..75027ee55a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/add_system.py @@ -20,45 +20,42 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem"): - """Add a new distribution system +def add_system(file: ifcopenshell.file, ifc_class: str = "IfcDistributionSystem") -> ifcopenshell.entity_instance: + """Add a new distribution system - A distribution system is a group of distribution elements, like ducts, - pipes, pumps, filters, fans, and so on that distribute a medium (air, - liquid, or electricity) throughout a facility. Systems may be - hierarchical, with larger systems composed of smaller subsystems. + A distribution system is a group of distribution elements, like ducts, + pipes, pumps, filters, fans, and so on that distribute a medium (air, + liquid, or electricity) throughout a facility. Systems may be + hierarchical, with larger systems composed of smaller subsystems. - :param ifc_class: The type of system, chosen from IfcDistributionSystem - for mechanical, electrical, communications, plumbing, fire, or - security systems. Alternatively you may choose IfcBuildingSystem for - specialised building facade systems or similar. For IFC2X3, choose - IfcSystem. - :type ifc_class: str - :return: The newly created IfcSystem. - :rtype: ifcopenshell.entity_instance + :param ifc_class: The type of system, chosen from IfcDistributionSystem + for mechanical, electrical, communications, plumbing, fire, or + security systems. Alternatively you may choose IfcBuildingSystem for + specialised building facade systems or similar. For IFC2X3, choose + IfcSystem. + :type ifc_class: str + :return: The newly created IfcSystem. + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) - """ - self.file = file - self.settings = {"ifc_class": ifc_class} + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + """ + settings = {"ifc_class": ifc_class} - def execute(self) -> ifcopenshell.entity_instance: - ifc_class = self.settings["ifc_class"] - # workaround for failing default argument in ifc2x3 - if self.file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem": - ifc_class = "IfcSystem" + ifc_class = settings["ifc_class"] + # workaround for failing default argument in ifc2x3 + if file.schema == "IFC2X3" and ifc_class == "IfcDistributionSystem": + ifc_class = "IfcSystem" - return self.file.create_entity( - ifc_class, - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "Name": "Unnamed", - } - ) + return file.create_entity( + ifc_class, + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "Name": "Unnamed", + } + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py index aae80ab6eb..2254a43dec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_flow_control.py @@ -20,66 +20,63 @@ import ifcopenshell import ifcopenshell.api -class Usecase: - def __init__(self, file, relating_flow_element=None, related_flow_control=None): - """Assigns to the flow element control element that either sense or control - some aspect of the flow element. +def assign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None: + """Assigns to the flow element control element that either sense or control + some aspect of the flow element. - Note that control can be assigned only to the one flow element. + Note that control can be assigned only to the one flow element. - :param related_flow_control: IfcDistributionControlElement - which may be used to impart control on the flow element - :type related_flow_control: ifcopenshell.entity_instance - :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed - :type relating_flow_element: ifcopenshell.entity_instance - :return: Matching or newly created IfcRelFlowControlElements. If control - is already assigned to some other element method will return None. - :rtype: ifcopenshell.entity_instance, None + :param related_flow_control: IfcDistributionControlElement + which may be used to impart control on the flow element + :type related_flow_control: ifcopenshell.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled / sensed + :type relating_flow_element: ifcopenshell.entity_instance + :return: Matching or newly created IfcRelFlowControlElements. If control + is already assigned to some other element method will return None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - flow_element = model.createIfcFlowSegment() - flow_control = model.createIfcController() - relation = ifcopenshell.api.run( - "system.assign_flow_control", model, - related_flow_control=flow_control, relating_flow_element=flow_element - ) - """ - self.file = file - self.settings = { - "relating_flow_element": relating_flow_element, - "related_flow_control": related_flow_control, - } + flow_element = model.createIfcFlowSegment() + flow_control = model.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", model, + related_flow_control=flow_control, relating_flow_element=flow_element + ) + """ + settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } - def execute(self): - if self.settings["related_flow_control"].AssignedToFlowElement: - # only 1 control per 1 flow element is possible - assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] - if assignment.RelatingFlowElement == self.settings["relating_flow_element"]: - return assignment - # return None if this control is already assigned to another flow element - return + if settings["related_flow_control"].AssignedToFlowElement: + # only 1 control per 1 flow element is possible + assignment = settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement == settings["relating_flow_element"]: + return assignment + # return None if this control is already assigned to another flow element + return - if self.settings["relating_flow_element"].HasControlElements: - assignment = self.settings["relating_flow_element"].HasControlElements[0] - if self.settings["related_flow_control"] in assignment.RelatedControlElements: - return assignment - - related_flow_controls = set(assignment.RelatedControlElements) - related_flow_controls.add(self.settings["related_flow_control"]) - assignment.RelatedControlElements = list(related_flow_controls) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) + if settings["relating_flow_element"].HasControlElements: + assignment = settings["relating_flow_element"].HasControlElements[0] + if settings["related_flow_control"] in assignment.RelatedControlElements: return assignment - assignment = self.file.create_entity( - "IfcRelFlowControlElements", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedControlElements": [self.settings["related_flow_control"]], - "RelatingFlowElement": self.settings["relating_flow_element"], - }, - ) + related_flow_controls = set(assignment.RelatedControlElements) + related_flow_controls.add(settings["related_flow_control"]) + assignment.RelatedControlElements = list(related_flow_controls) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment}) return assignment + + assignment = file.create_entity( + "IfcRelFlowControlElements", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatedControlElements": [settings["related_flow_control"]], + "RelatingFlowElement": settings["relating_flow_element"], + }, + ) + return assignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py index 728a935395..c424f8eb4b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_port.py @@ -21,46 +21,49 @@ import ifcopenshell.api import ifcopenshell.util.placement +def assign_port(file, element=None, port=None) -> None: + """Assigns a port to an element + + If you have an orphaned port, you may assign it to a distribution + element using this function. Ports should typically not be orphaned, but + it may be useful when patching up models. + + :param element: The IfcDistributionElement to assign the port to. + :type element: ifcopenshell.entity_instance + :param port: The IfcDistributionPort you want to assign. + :type port: ifcopenshell.entity_instance + :return: The IfcRelNests relationship, or the + IfcRelConnectsPortToElement for IFC2X3. + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + + # Reassign it back + ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "port": port, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, element=None, port=None): - """Assigns a port to an element - - If you have an orphaned port, you may assign it to a distribution - element using this function. Ports should typically not be orphaned, but - it may be useful when patching up models. - - :param element: The IfcDistributionElement to assign the port to. - :type element: ifcopenshell.entity_instance - :param port: The IfcDistributionPort you want to assign. - :type port: ifcopenshell.entity_instance - :return: The IfcRelNests relationship, or the - IfcRelConnectsPortToElement for IFC2X3. - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - - # Unassign one port for some weird reason. - ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) - - # Reassign it back - ifcopenshell.api.run("system.assign_port", model, element=duct, port=port1) - """ - self.file = file - self.settings = { - "element": element, - "port": port, - } - def execute(self): if self.file.schema == "IFC2X3": return self.execute_ifc2x3() diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py index 20f5a8519f..e0b19c5165 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/assign_system.py @@ -21,51 +21,47 @@ import ifcopenshell.api import ifcopenshell.util.system -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - system: ifcopenshell.entity_instance, - ): - """Assigns distribution elements to a system +def assign_system( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + system: ifcopenshell.entity_instance, +) -> None: + """Assigns distribution elements to a system - Note that it is not necessary to assign distribution ports to a system. + Note that it is not necessary to assign distribution ports to a system. - :param products: The list of IfcDistributionElements to assign to the system. - :type products: list[ifcopenshell.entity_instance] - :param system: The IfcSystem you want to assign the element to. - :type system: ifcopenshell.entity_instance - :return: The IfcRelAssignsToGroup relationship - or `None` if `products` was empty list. - :rtype: [ifcopenshell.entity_instance, None] + :param products: The list of IfcDistributionElements to assign to the system. + :type products: list[ifcopenshell.entity_instance] + :param system: The IfcSystem you want to assign the element to. + :type system: ifcopenshell.entity_instance + :return: The IfcRelAssignsToGroup relationship + or `None` if `products` was empty list. + :rtype: [ifcopenshell.entity_instance, None] - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # This duct is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - """ - self.file = file - self.settings = { - "products": products, - "system": system, - } + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + """ + settings = { + "products": products, + "system": system, + } - def execute(self): - system = self.settings["system"] - products = self.settings["products"] + system = settings["system"] + products = settings["products"] - if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products): - raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}") + if not all(ifcopenshell.util.system.is_assignable(failed_product := product, system) for product in products): + raise TypeError(f"You cannot assign an {failed_product.is_a()} to an {system.is_a()}") - rel = ifcopenshell.api.run("group.assign_group", self.file, products=products, group=system) - return rel + rel = ifcopenshell.api.run("group.assign_group", file, products=products, group=system) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py index 7d23dde1a7..f5773c5a21 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/connect_port.py @@ -21,84 +21,87 @@ import ifcopenshell.api import ifcopenshell.util.element +def connect_port(file, port1=None, port2=None, direction="NOTDEFINED", element=None) -> None: + """Connects two ports together + + A distribution element (e.g. a duct) may be connected to another + distribution element (e.g. a fitting) by connecting a port at one of the + duct to a port at the same end of the fitting. + + Ports may only have one connection, so you cannot have multiple things + connected to the same port. Nor can you have incompatible port + connections, such as an electrical port connected to an airflow port. + + Port connectivity may be explicit or implicit. Explicit connections are + where the port connectivity is described for every single distribution + element in detail. For example, a duct segment would have port + connections to a duct fitting, which would have port connections to + another duct segment, all the way from a fan to an air terminal exactly + as constructed on site. Implicit connections only consider the key + distribution control elements (e.g. the fan and the terminal) and ignore + all of the details of the duct segments and fittings in between. + Generally, explicit connectivity is preferred for later detailed design, + and implicit connectivity is preferred for early phase design. + + :param port1: The port of the first distribution element to connect. + :type port1: ifcopenshell.entity_instance + :param port2: The port of the second distribution element to connect. + :type port2: ifcopenshell.entity_instance + :param direction: The directionality of distribution flow through the + port connection. NOTDEFINED means that the direction has not yet + been determined. This is useful during preliminary system design. + SOURCE means that the flow is from the first element to the second + element. SINK means that the flow is from the second element to the + first element. SOURCEANDSINK means that flow is bi-directional + between the first and second element. SOURCEANDSINK is a relatively + rare scenario. + :type direction: str + :param element: Optionally set an element through which the port + connectivity is made, such as a segment or fitting. This is only to + be used for implicit port connectivity where the segments and + fittings are less important. + :type element: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) + + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") + + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) + + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) + + # Connect the duct and fitting together. At this point, we have not + # yet determined the direction of the flow, so we leave direction as + # NOTDEFINED. + ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "port1": port1, + "port2": port2, + "direction": direction, + "element": element, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, port1=None, port2=None, direction="NOTDEFINED", element=None): - """Connects two ports together - - A distribution element (e.g. a duct) may be connected to another - distribution element (e.g. a fitting) by connecting a port at one of the - duct to a port at the same end of the fitting. - - Ports may only have one connection, so you cannot have multiple things - connected to the same port. Nor can you have incompatible port - connections, such as an electrical port connected to an airflow port. - - Port connectivity may be explicit or implicit. Explicit connections are - where the port connectivity is described for every single distribution - element in detail. For example, a duct segment would have port - connections to a duct fitting, which would have port connections to - another duct segment, all the way from a fan to an air terminal exactly - as constructed on site. Implicit connections only consider the key - distribution control elements (e.g. the fan and the terminal) and ignore - all of the details of the duct segments and fittings in between. - Generally, explicit connectivity is preferred for later detailed design, - and implicit connectivity is preferred for early phase design. - - :param port1: The port of the first distribution element to connect. - :type port1: ifcopenshell.entity_instance - :param port2: The port of the second distribution element to connect. - :type port2: ifcopenshell.entity_instance - :param direction: The directionality of distribution flow through the - port connection. NOTDEFINED means that the direction has not yet - been determined. This is useful during preliminary system design. - SOURCE means that the flow is from the first element to the second - element. SINK means that the flow is from the second element to the - first element. SOURCEANDSINK means that flow is bi-directional - between the first and second element. SOURCEANDSINK is a relatively - rare scenario. - :type direction: str - :param element: Optionally set an element through which the port - connectivity is made, such as a segment or fitting. This is only to - be used for implicit port connectivity where the segments and - fittings are less important. - :type element: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) - - # Create a duct and a 90 degree bend fitting - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - fitting = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctFitting", predefined_type="BEND") - - # The duct and fitting is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) - - # Create 2 ports, one for either end of both the duct and fitting. - duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) - fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) - - # Connect the duct and fitting together. At this point, we have not - # yet determined the direction of the flow, so we leave direction as - # NOTDEFINED. - ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) - """ - self.file = file - self.settings = { - "port1": port1, - "port2": port2, - "direction": direction, - "element": element, - } - def execute(self): # Note: there are a number of ambiguities with port connectivity. We # assume system topology is represented by a directed graph. In other diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py index 071074e9e7..15d5890493 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/disconnect_port.py @@ -21,63 +21,60 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, port=None): - """Disconnects a port from any other port +def disconnect_port(file, port=None) -> None: + """Disconnects a port from any other port - A port may only be connected to one other port, so the other port is not - needed to be specified. + A port may only be connected to one other port, so the other port is not + needed to be specified. - :param port: The IfcDistributionPort to disconnect. - :type port: ifcopenshell.entity_instance - :return: None - :rtype: None + :param port: The IfcDistributionPort to disconnect. + :type port: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct and a 90 degree bend fitting - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - fitting = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctFitting", predefined_type="BEND") + # Create a duct and a 90 degree bend fitting + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + fitting = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctFitting", predefined_type="BEND") - # The duct and fitting is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) + # The duct and fitting is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + ifcopenshell.api.run("system.assign_system", model, products=[fitting], system=system) - # Create 2 ports, one for either end of both the duct and fitting. - duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) - fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) + # Create 2 ports, one for either end of both the duct and fitting. + duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting) + fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting) - # Connect the duct and fitting together. At this point, we have not - # yet determined the direction of the flow, so we leave direction as - # NOTDEFINED. - ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) + # Connect the duct and fitting together. At this point, we have not + # yet determined the direction of the flow, so we leave direction as + # NOTDEFINED. + ifcopenshell.api.run("system.connect_port", model, port1=duct_port2, port2=fitting_port1) - # Disconnect the port. note we could've equally disconnected - # fitting_port1 instead of duct_port2 - ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2) - """ - self.file = file - self.settings = { - "port": port, - } + # Disconnect the port. note we could've equally disconnected + # fitting_port1 instead of duct_port2 + ifcopenshell.api.run("system.disconnect_port", model, port=duct_port2) + """ + settings = { + "port": port, + } - def execute(self): - rels = self.settings["port"].ConnectedTo or () - rels += self.settings["port"].ConnectedFrom or () + rels = settings["port"].ConnectedTo or () + rels += settings["port"].ConnectedFrom or () - for rel in rels: - rel.RelatingPort.FlowDirection = None - rel.RelatedPort.FlowDirection = None - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for rel in rels: + rel.RelatingPort.FlowDirection = None + rel.RelatedPort.FlowDirection = None + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py index 315c04ccd5..83fd250ddd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/edit_system.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, system=None, attributes=None): - """Edits the attributes of an IfcSystem +def edit_system(file, system=None, attributes=None) -> None: + """Edits the attributes of an IfcSystem - For more information about the attributes and data types of an - IfcSystem, consult the IFC documentation. + For more information about the attributes and data types of an + IfcSystem, consult the IFC documentation. - :param system: The IfcSystem entity you want to edit - :type system: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param system: The IfcSystem entity you want to edit + :type system: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Change the name of the system to "HW" for Hot Water - ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"}) - """ + # Change the name of the system to "HW" for Hot Water + ifcopenshell.api.run("system.edit_system", model, system=system, attributes={"Name": "HW"}) + """ - self.file = file - self.settings = {"system": system, "attributes": attributes or {}} + settings = {"system": system, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["system"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["system"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py index f331a5f3e3..a81a06127f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/remove_system.py @@ -21,55 +21,52 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, system=None): - """Removes a distribution system +def remove_system(file, system=None) -> None: + """Removes a distribution system - All the distribution elements within the system are retained. + All the distribution elements within the system are retained. - :param system: The IfcSystem to remove. - :type system: ifcopenshell.entity_instance - :return: None - :rtype: None + :param system: The IfcSystem to remove. + :type system: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Delete it. - ifcopenshell.api.run("system.remove_system", model, system=system) - """ - self.file = file - self.settings = {"system": system} + # Delete it. + ifcopenshell.api.run("system.remove_system", model, system=system) + """ + settings = {"system": system} - def execute(self): - for inverse_id in [i.id() for i in self.file.get_inverse(self.settings["system"])]: - try: - inverse = self.file.by_id(inverse_id) - except: - continue - if inverse.is_a("IfcRelDefinesByProperties"): - ifcopenshell.api.run( - "pset.remove_pset", - self.file, - product=self.settings["system"], - pset=inverse.RelatingPropertyDefinition, - ) - elif inverse.is_a("IfcRelAssignsToGroup"): - if inverse.RelatingGroup == self.settings["system"]: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - elif len(inverse.RelatedObjects) == 1: - history = inverse.OwnerHistory - self.file.remove(inverse) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - history = self.settings["system"].OwnerHistory - self.file.remove(self.settings["system"]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + for inverse_id in [i.id() for i in file.get_inverse(settings["system"])]: + try: + inverse = file.by_id(inverse_id) + except: + continue + if inverse.is_a("IfcRelDefinesByProperties"): + ifcopenshell.api.run( + "pset.remove_pset", + file, + product=settings["system"], + pset=inverse.RelatingPropertyDefinition, + ) + elif inverse.is_a("IfcRelAssignsToGroup"): + if inverse.RelatingGroup == settings["system"]: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + elif len(inverse.RelatedObjects) == 1: + history = inverse.OwnerHistory + file.remove(inverse) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + history = settings["system"].OwnerHistory + file.remove(settings["system"]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py index 04eda27f83..feba961f1b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_flow_control.py @@ -21,57 +21,54 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file, relating_flow_element=None, related_flow_control=None): - """Unassigns flow control element from the flow element. +def unassign_flow_control(file, relating_flow_element=None, related_flow_control=None) -> None: + """Unassigns flow control element from the flow element. - :param related_flow_control: IfcDistributionControlElement controling the - flow element - :type related_flow_control: ifcopenshell.entity_instance - :param relating_flow_element: The IfcDistributionFlowElement that is being controlled - :type relating_flow_element: ifcopenshell.entity_instance - :return: If the control still is related to other objects, the - IfcRelFlowControlElements is returned, otherwise None. - :rtype: ifcopenshell.entity_instance, None + :param related_flow_control: IfcDistributionControlElement controling the + flow element + :type related_flow_control: ifcopenshell.entity_instance + :param relating_flow_element: The IfcDistributionFlowElement that is being controlled + :type relating_flow_element: ifcopenshell.entity_instance + :return: If the control still is related to other objects, the + IfcRelFlowControlElements is returned, otherwise None. + :rtype: ifcopenshell.entity_instance, None - Example: + Example: - .. code:: python + .. code:: python - # assign control to the flow element - flow_element = self.file.createIfcFlowSegment() - flow_control = self.file.createIfcController() - relation = ifcopenshell.api.run( - "system.assign_flow_control", self.file, - relating_control=flow_control, related_object=flow_element - ) + # assign control to the flow element + flow_element = file.createIfcFlowSegment() + flow_control = file.createIfcController() + relation = ifcopenshell.api.run( + "system.assign_flow_control", file, + relating_control=flow_control, related_object=flow_element + ) - # und unassign it - ifcopenshell.api.run("system.unassign_flow_control", self.file, - relating_control=flow_control, related_object=flow_element - ) - """ + # und unassign it + ifcopenshell.api.run("system.unassign_flow_control", file, + relating_control=flow_control, related_object=flow_element + ) + """ - self.file = file - self.settings = { - "relating_flow_element": relating_flow_element, - "related_flow_control": related_flow_control, - } + settings = { + "relating_flow_element": relating_flow_element, + "related_flow_control": related_flow_control, + } - def execute(self): - if not self.settings["related_flow_control"].AssignedToFlowElement: - return - assignment = self.settings["related_flow_control"].AssignedToFlowElement[0] - if assignment.RelatingFlowElement != self.settings["relating_flow_element"]: - return - if len(assignment.RelatedControlElements) == 1: - history = assignment.OwnerHistory - self.file.remove(assignment) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - return - related_flow_controls = list(assignment.RelatedControlElements) - related_flow_controls.remove(self.settings["related_flow_control"]) - assignment.RelatedControlElements = related_flow_controls - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": assignment}) - return assignment + if not settings["related_flow_control"].AssignedToFlowElement: + return + assignment = settings["related_flow_control"].AssignedToFlowElement[0] + if assignment.RelatingFlowElement != settings["relating_flow_element"]: + return + if len(assignment.RelatedControlElements) == 1: + history = assignment.OwnerHistory + file.remove(assignment) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + return + related_flow_controls = list(assignment.RelatedControlElements) + related_flow_controls.remove(settings["related_flow_control"]) + assignment.RelatedControlElements = related_flow_controls + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": assignment}) + return assignment diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py index e9d82722aa..678c086140 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_port.py @@ -20,42 +20,45 @@ import ifcopenshell import ifcopenshell.api +def unassign_port(file, element=None, port=None) -> None: + """Unassigns a port to an element + + Ports are typically always assigned to a distribution element, but in + some edge cases you may want to unassign the port to create an orphaned + port for cleaning or patchin purposes. + + :param element: The IfcDistributionElement to unassign the port from. + :type element: ifcopenshell.entity_instance + :param port: The IfcDistributionPort you want to unassign. + :type port: ifcopenshell.entity_instance + :return: None + :rtype: None + + Example: + + .. code:: python + + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + + # Create 2 ports, one for either end. + port1 = ifcopenshell.api.run("system.add_port", model, element=duct) + port2 = ifcopenshell.api.run("system.add_port", model, element=duct) + + # Unassign one port for some weird reason. + ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "element": element, + "port": port, + } + return usecase.execute() + + class Usecase: - def __init__(self, file, element=None, port=None): - """Unassigns a port to an element - - Ports are typically always assigned to a distribution element, but in - some edge cases you may want to unassign the port to create an orphaned - port for cleaning or patchin purposes. - - :param element: The IfcDistributionElement to unassign the port from. - :type element: ifcopenshell.entity_instance - :param port: The IfcDistributionPort you want to unassign. - :type port: ifcopenshell.entity_instance - :return: None - :rtype: None - - Example: - - .. code:: python - - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - - # Create 2 ports, one for either end. - port1 = ifcopenshell.api.run("system.add_port", model, element=duct) - port2 = ifcopenshell.api.run("system.add_port", model, element=duct) - - # Unassign one port for some weird reason. - ifcopenshell.api.run("system.unassign_port", model, element=duct, port=port1) - """ - self.file = file - self.settings = { - "element": element, - "port": port, - } - def execute(self): if self.file.schema == "IFC2X3": return self.execute_ifc2x3() diff --git a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py index fbc3dd854b..7bb82aebb7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py +++ b/src/ifcopenshell-python/ifcopenshell/api/system/unassign_system.py @@ -21,46 +21,40 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - products: list[ifcopenshell.entity_instance], - system: ifcopenshell.entity_instance, - ): - """Unassigns list of products from a system +def unassign_system( + file: ifcopenshell.file, + products: list[ifcopenshell.entity_instance], + system: ifcopenshell.entity_instance, +) -> None: + """Unassigns list of products from a system - :param products: The list of IfcDistributionElements to unassign from the system. - :type products: list[ifcopenshell.entity_instance] - :param system: The IfcSystem you want to unassign the element from. - :type system: ifcopenshell.entity_instance - :return: None - :rtype: None + :param products: The list of IfcDistributionElements to unassign from the system. + :type products: list[ifcopenshell.entity_instance] + :param system: The IfcSystem you want to unassign the element from. + :type system: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A completely empty distribution system - system = ifcopenshell.api.run("system.add_system", model) + # A completely empty distribution system + system = ifcopenshell.api.run("system.add_system", model) - # Create a duct - duct = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") + # Create a duct + duct = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcDuctSegment", predefined_type="RIGIDSEGMENT") - # This duct is part of the system - ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) + # This duct is part of the system + ifcopenshell.api.run("system.assign_system", model, products=[duct], system=system) - # Not anymore! - ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system) - """ - self.file = file - self.settings = { - "products": products, - "system": system, - } + # Not anymore! + ifcopenshell.api.run("system.unassign_system", model, products=[duct], system=system) + """ + settings = { + "products": products, + "system": system, + } - def execute(self): - ifcopenshell.api.run( - "group.unassign_group", self.file, products=self.settings["products"], group=self.settings["system"] - ) + ifcopenshell.api.run("group.unassign_group", file, products=settings["products"], group=settings["system"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py index e0caddbe3c..dddd90a49f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .assign_type import assign_type +from .get_related_objects import get_related_objects +from .map_type_representations import map_type_representations +from .unassign_type import unassign_type diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py index b8d7cf357a..9d30d6083a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/assign_type.py @@ -22,166 +22,168 @@ import ifcopenshell.util.element from typing import Union, Iterable +def assign_type( + file: ifcopenshell.file, + related_objects: list[ifcopenshell.entity_instance], + relating_type: ifcopenshell.entity_instance, + should_map_representations=True, +) -> Union[ifcopenshell.entity_instance, None]: + """Assigns a type to occurrences of an object + + IFC supports the concept of occurrences and types. An occurrence is an + actual physical product in the real world: like a wall, a chair, a door, + a column, a pump, and so on. + + Most occurrences have a corresponding type. A type describes either a + common shape and set of properties of a particular model of equipment, + or a construction typology. An occurrence may only have zero or one + type. + + For example, architects would typically have a door schedule for + individual occurrences of doors and a door types schedule for a handful + of door types, described by the door hardware, frame, and panel. Other + examples might be window types or wall types. Structural engineers would + have a list of column types, beam types, slab types, etc, such as a 400 + diameter column, a 500 diameter column, and so on. Services consultant + might nominate a particular type of sprinkler which have many + occurrences, or light fixture types, and so on. + + Types are critical as they communicate to the procurement team what + types of equipment and products need to be procured. The individual + occurrences of that type tell them how many to procure. Types are also + critical in construction as they indicate succinctly how to manufacture + or construct something. For example, a wall type is enough information + for a builder to understand the build up and construction of a wall. + Types are used to help break down cost plans, or isolate portions of an + assembly process for construction scheduling. Types are also used in + facility maintenance, as occurrences sharing the same type can be + repaired in the same way or by replacing the same parts. + + An occurrence of a type inherits all the properties and materials of the + type. For example, a 2HR fire rated wall type implies that all + wall occurrences of that wall type will also be 2HR fire rated. + + A type may or may not have a geometric representation. If a type does + not have any representation, then the occurrences are free to have any + representation of their own. However, if a type has a representation, + all occurrences must have the same representation. For example, if a + light fixture downlight type has a representation of a cylinder, then + all occurrences must have exactly the same cylinder as its + representation. If you change the cylinder's shape of the type, then all + occurrence representations will also change. + + If a type does not have any geometric representation, they may have a + parametric material representation. This may be either a parametric + layered material or parametric cross-sectional profile material. If this + is the case, the occurrence must be constructed out of the parametric + material. For example, if a wall type uses a list of parametric layers + indicating a thickness of 13mm plasterboard and 90mm stud, then the + thickness of every wall occurrence representation must be 103mm. The + length of each wall, however, may vary. Similarly, if a beam type has a + parametric profile material of an I-beam, then all beam occurrences must + also be this I-beam shape, though the length may vary. + + It is highly recommended for every occurrence to have a type. There are + some exceptions to the rule, such as in heritage architecture or + as-built or dilapidation models, where existing conditions are + ambiguous, unknown or are so bespoke as to have no logical type. + + :param related_objects: The IfcElement occurrences. + :type related_objects: list[ifcopenshell.entity_instance] + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :param should_map_representations: If a type has a representation map, + IFC requires all occurrences to map those representations. Some IFC + vendors might disobey this, or you might want to handle it + yourusecase. In this scenario, you may set this to False. + This also enabled adding material usages mapping. + :type should_map_representations: bool + :return: The IfcRelDefinesByType relationship + or `None` if `related_objects` was empty list. + :rtype: Union[ifcopenshell.entity_instance, None] + + Example: + + .. code:: python + + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") + + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + + # Assign the furniture to the furniture type. If the furniture_type + # had a representation, the furniture occurrence will also now have + # the exact same representation. This is highly efficient as you + # don't need to define the representation for every occurrence. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + + # Let's imagine a parametric material layer set + wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") + + # First, let's create a material set. This will later be assigned + # to our wall type element. + material_set = ifcopenshell.api.run("material.add_material_set", model, + name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") + + # Let's create a few materials, it's important to also give them + # categories. This makes it easy for model recipients to do things + # like "show me everything made out of aluminium / concrete / steel + # / glass / etc". The IFC specification states a list of categories + # you can use. + gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") + steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") + + # Now let's use those materials as three layers in our set, such + # that the steel studs are sandwiched by the gypsum. Let's imagine + # we're setting the layer thickness in millimeters. + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092}) + layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) + ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) + + # Great! Let's assign our material set to our wall type. + ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) + + # Now, let's create a wall. + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # The wall is a WAL01 wall type. + ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) + + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.118) + + # Assign our new body geometry back to our wall + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = { + "related_objects": related_objects, + "relating_type": relating_type, + "should_map_representations": should_map_representations, + } + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_objects: list[ifcopenshell.entity_instance], - relating_type: ifcopenshell.entity_instance, - should_map_representations=True, - ): - """Assigns a type to occurrences of an object - - IFC supports the concept of occurrences and types. An occurrence is an - actual physical product in the real world: like a wall, a chair, a door, - a column, a pump, and so on. - - Most occurrences have a corresponding type. A type describes either a - common shape and set of properties of a particular model of equipment, - or a construction typology. An occurrence may only have zero or one - type. - - For example, architects would typically have a door schedule for - individual occurrences of doors and a door types schedule for a handful - of door types, described by the door hardware, frame, and panel. Other - examples might be window types or wall types. Structural engineers would - have a list of column types, beam types, slab types, etc, such as a 400 - diameter column, a 500 diameter column, and so on. Services consultant - might nominate a particular type of sprinkler which have many - occurrences, or light fixture types, and so on. - - Types are critical as they communicate to the procurement team what - types of equipment and products need to be procured. The individual - occurrences of that type tell them how many to procure. Types are also - critical in construction as they indicate succinctly how to manufacture - or construct something. For example, a wall type is enough information - for a builder to understand the build up and construction of a wall. - Types are used to help break down cost plans, or isolate portions of an - assembly process for construction scheduling. Types are also used in - facility maintenance, as occurrences sharing the same type can be - repaired in the same way or by replacing the same parts. - - An occurrence of a type inherits all the properties and materials of the - type. For example, a 2HR fire rated wall type implies that all - wall occurrences of that wall type will also be 2HR fire rated. - - A type may or may not have a geometric representation. If a type does - not have any representation, then the occurrences are free to have any - representation of their own. However, if a type has a representation, - all occurrences must have the same representation. For example, if a - light fixture downlight type has a representation of a cylinder, then - all occurrences must have exactly the same cylinder as its - representation. If you change the cylinder's shape of the type, then all - occurrence representations will also change. - - If a type does not have any geometric representation, they may have a - parametric material representation. This may be either a parametric - layered material or parametric cross-sectional profile material. If this - is the case, the occurrence must be constructed out of the parametric - material. For example, if a wall type uses a list of parametric layers - indicating a thickness of 13mm plasterboard and 90mm stud, then the - thickness of every wall occurrence representation must be 103mm. The - length of each wall, however, may vary. Similarly, if a beam type has a - parametric profile material of an I-beam, then all beam occurrences must - also be this I-beam shape, though the length may vary. - - It is highly recommended for every occurrence to have a type. There are - some exceptions to the rule, such as in heritage architecture or - as-built or dilapidation models, where existing conditions are - ambiguous, unknown or are so bespoke as to have no logical type. - - :param related_objects: The IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance] - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :param should_map_representations: If a type has a representation map, - IFC requires all occurrences to map those representations. Some IFC - vendors might disobey this, or you might want to handle it - yourself. In this scenario, you may set this to False. - This also enabled adding material usages mapping. - :type should_map_representations: bool - :return: The IfcRelDefinesByType relationship - or `None` if `related_objects` was empty list. - :rtype: Union[ifcopenshell.entity_instance, None] - - Example: - - .. code:: python - - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") - - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - - # Assign the furniture to the furniture type. If the furniture_type - # had a representation, the furniture occurrence will also now have - # the exact same representation. This is highly efficient as you - # don't need to define the representation for every occurrence. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - - # Let's imagine a parametric material layer set - wall_type = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWallType", name="WAL01") - - # First, let's create a material set. This will later be assigned - # to our wall type element. - material_set = ifcopenshell.api.run("material.add_material_set", model, - name="GYP-ST-GYP", set_type="IfcMaterialLayerSet") - - # Let's create a few materials, it's important to also give them - # categories. This makes it easy for model recipients to do things - # like "show me everything made out of aluminium / concrete / steel - # / glass / etc". The IFC specification states a list of categories - # you can use. - gypsum = ifcopenshell.api.run("material.add_material", model, name="PB01", category="gypsum") - steel = ifcopenshell.api.run("material.add_material", model, name="ST01", category="steel") - - # Now let's use those materials as three layers in our set, such - # that the steel studs are sandwiched by the gypsum. Let's imagine - # we're setting the layer thickness in millimeters. - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=steel) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .092}) - layer = ifcopenshell.api.run("material.add_layer", model, layer_set=material_set, material=gypsum) - ifcopenshell.api.run("material.edit_layer", model, layer=layer, attributes={"LayerThickness": .013}) - - # Great! Let's assign our material set to our wall type. - ifcopenshell.api.run("material.assign_material", model, products=[wall_type], material=material_set) - - # Now, let's create a wall. - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - - # The wall is a WAL01 wall type. - ifcopenshell.api.run("type.assign_type", model, related_objects=[wall], relating_type=wall_type) - - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - - # Notice how our thickness of 0.118 must equal .013 + .092 + .013 from our type - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.118) - - # Assign our new body geometry back to our wall - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) - - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - """ - self.file = file - self.settings = { - "related_objects": related_objects, - "relating_type": relating_type, - "should_map_representations": should_map_representations, - } - - def execute(self) -> Union[ifcopenshell.entity_instance, None]: + def execute(self): if not self.settings["related_objects"]: return diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py index 0a05de4118..3610f27af5 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/get_related_objects.py @@ -19,43 +19,40 @@ import ifcopenshell -class Usecase: - def __init__(self, file, related_object=None, relating_type=None): - """Gets all the related occurrences of a type +def get_related_objects(file, related_object=None, relating_type=None) -> None: + """Gets all the related occurrences of a type - Do not use this function. It will be removed. Use - ifcopenshell.util.element.get_type or - ifcopenshell.util.element.get_types instead. + Do not use this function. It will be removed. Use + ifcopenshell.util.element.get_type or + ifcopenshell.util.element.get_types instead. - :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :return: A list of occurrences of the type. - :rtype: list[ifcopenshell.entity_instance] - """ - self.file = file - self.settings = { - "related_object": related_object, - "relating_type": relating_type, - } + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :return: A list of occurrences of the type. + :rtype: list[ifcopenshell.entity_instance] + """ + settings = { + "related_object": related_object, + "relating_type": relating_type, + } - def execute(self): - if self.settings["related_object"]: - if self.file.schema == "IFC2X3": - is_defined_by = self.settings["related_object"].IsDefinedBy - for rel in is_defined_by: - if rel.is_a("IfcRelDefinesByType"): - return set([int(o.id()) for o in rel.RelatedObjects]) - else: - is_typed_by = self.settings["related_object"].IsTypedBy - if is_typed_by: - return set([int(o.id()) for o in is_typed_by[0].RelatedObjects]) - elif self.settings["relating_type"]: - if self.file.schema == "IFC2X3": - types = self.settings["relating_type"].ObjectTypeOf - else: - types = self.settings["relating_type"].Types - if types: - return set([int(o.id()) for o in types[0].RelatedObjects]) - return set() + if settings["related_object"]: + if file.schema == "IFC2X3": + is_defined_by = settings["related_object"].IsDefinedBy + for rel in is_defined_by: + if rel.is_a("IfcRelDefinesByType"): + return set([int(o.id()) for o in rel.RelatedObjects]) + else: + is_typed_by = settings["related_object"].IsTypedBy + if is_typed_by: + return set([int(o.id()) for o in is_typed_by[0].RelatedObjects]) + elif settings["relating_type"]: + if file.schema == "IFC2X3": + types = settings["relating_type"].ObjectTypeOf + else: + types = settings["relating_type"].Types + if types: + return set([int(o.id()) for o in types[0].RelatedObjects]) + return set() diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py index 064fb148bb..59c1655c6f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/map_type_representations.py @@ -21,99 +21,93 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__( - self, - file: ifcopenshell.file, - related_object: ifcopenshell.entity_instance, - relating_type: ifcopenshell.entity_instance, - ): - """Ensures that all occurrences has the same representation as the type +def map_type_representations( + file: ifcopenshell.file, + related_object: ifcopenshell.entity_instance, + relating_type: ifcopenshell.entity_instance, +) -> None: + """Ensures that all occurrences has the same representation as the type - If a type has a representation, all occurrences must have the same - representation. If the type's representation changes, this function may - be used to ensure consistency of the occurrence's representations. + If a type has a representation, all occurrences must have the same + representation. If the type's representation changes, this function may + be used to ensure consistency of the occurrence's representations. - :param related_object: The IfcElement occurrence. - :type related_object: ifcopenshell.entity_instance - :param relating_type: The IfcElementType type. - :type relating_type: ifcopenshell.entity_instance - :return: None - :rtype: None + :param related_object: The IfcElement occurrence. + :type related_object: ifcopenshell.entity_instance + :param relating_type: The IfcElementType type. + :type relating_type: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # Place our furniture at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture) + # Place our furniture at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=furniture) - # Assign the furniture to the furniture type. Right now, the - # furniture type has no representation, so the furniture may also - # have no representation, or any arbitrary representation that may - # vary from occurrence to occurrence. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + # Assign the furniture to the furniture type. Right now, the + # furniture type has no representation, so the furniture may also + # have no representation, or any arbitrary representation that may + # vary from occurrence to occurrence. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our furniture type. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our furniture type. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Let's create a mesh representation of an arbitrary 2m cube. - representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body, - vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0), - (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]], - faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]]) + # Let's create a mesh representation of an arbitrary 2m cube. + representation = ifcopenshell.api.run("geometry.add_sverchok_representation", model, context=body, + vertices=[[(-1.0, -1.0, 0.0), (-1.0, -1.0, 2.0), (-1.0, 1.0, 0.0), (-1.0, 1.0, 2.0), + (1.0, -1.0, 0.0), (1.0, -1.0, 2.0), (1.0, 1.0, 0.0), (1.0, 1.0, 2.0)]], + faces=[[[0, 1, 3, 2], [2, 3, 7, 6], [6, 7, 5, 4], [4, 5, 1, 0], [2, 6, 4, 0], [7, 3, 1, 5]]]) - # Assign our new body geometry back to our furniture type. In this - # case, since we use the API, all occurrences automatically get the - # representation mapped, so there is nothing more we need to do. - ifcopenshell.api.run("geometry.assign_representation", model, - product=furniture_type, representation=representation) + # Assign our new body geometry back to our furniture type. In this + # case, since we use the API, all occurrences automatically get the + # representation mapped, so there is nothing more we need to do. + ifcopenshell.api.run("geometry.assign_representation", model, + product=furniture_type, representation=representation) - # However, if you were doing some sort of manual IFC patching, like - # assigning furniture_type.RepresentationMaps directly, then you - # might make this call: - # ifcopenshell.api.run("type.map_type_representations", model, - # related_object=furniture, relating_type=furniture_type) - """ - self.file = file - self.settings = { - "related_object": related_object, - "relating_type": relating_type, - } + # However, if you were doing some sort of manual IFC patching, like + # assigning furniture_type.RepresentationMaps directly, then you + # might make this call: + # ifcopenshell.api.run("type.map_type_representations", model, + # related_object=furniture, relating_type=furniture_type) + """ + settings = { + "related_object": related_object, + "relating_type": relating_type, + } - def execute(self) -> None: - if not self.settings["relating_type"].RepresentationMaps: - return - representations = [] - if self.settings["related_object"].Representation: - representations = self.settings["related_object"].Representation.Representations - for representation in representations: - ifcopenshell.api.run( - "geometry.unassign_representation", - self.file, - product=self.settings["related_object"], - representation=representation, - ) - ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation}) - for representation_map in self.settings["relating_type"].RepresentationMaps: - representation = representation_map.MappedRepresentation - mapped_representation = ifcopenshell.api.run( - "geometry.map_representation", self.file, representation=representation - ) - ifcopenshell.api.run( - "geometry.assign_representation", - self.file, - product=self.settings["related_object"], - representation=mapped_representation, - ) + if not settings["relating_type"].RepresentationMaps: + return + representations = [] + if settings["related_object"].Representation: + representations = settings["related_object"].Representation.Representations + for representation in representations: + ifcopenshell.api.run( + "geometry.unassign_representation", + file, + product=settings["related_object"], + representation=representation, + ) + ifcopenshell.api.run("geometry.remove_representation", file, **{"representation": representation}) + for representation_map in settings["relating_type"].RepresentationMaps: + representation = representation_map.MappedRepresentation + mapped_representation = ifcopenshell.api.run("geometry.map_representation", file, representation=representation) + ifcopenshell.api.run( + "geometry.assign_representation", + file, + product=settings["related_object"], + representation=mapped_representation, + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py index a629100477..cbc41a7785 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py +++ b/src/ifcopenshell-python/ifcopenshell/api/type/unassign_type.py @@ -21,58 +21,55 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]): - """Unassigns a type from occurrences +def unassign_type(file: ifcopenshell.file, related_objects: list[ifcopenshell.entity_instance]) -> None: + """Unassigns a type from occurrences - Note that unassigning a type doesn't automatically remove mapped representations - and material usages associated with the previously assigned type. + Note that unassigning a type doesn't automatically remove mapped representations + and material usages associated with the previously assigned type. - :param related_objects: List of IfcElement occurrences. - :type related_objects: list[ifcopenshell.entity_instance] - :return: None - :rtype: None + :param related_objects: List of IfcElement occurrences. + :type related_objects: list[ifcopenshell.entity_instance] + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # A furniture type. This would correlate to a particular model in a - # manufacturer's catalogue. Like an Ikea sofa :) - furniture_type = ifcopenshell.api.run("root.create_entity", model, - ifc_class="IfcFurnitureType", name="FUN01") + # A furniture type. This would correlate to a particular model in a + # manufacturer's catalogue. Like an Ikea sofa :) + furniture_type = ifcopenshell.api.run("root.create_entity", model, + ifc_class="IfcFurnitureType", name="FUN01") - # An individual occurrence of a that sofa. - furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") + # An individual occurrence of a that sofa. + furniture = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcFurniture") - # Assign the furniture to the furniture type. - ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) + # Assign the furniture to the furniture type. + ifcopenshell.api.run("type.assign_type", model, related_objects=[furniture], relating_type=furniture_type) - # Change our mind. Maybe it's a different type? - ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture]) - """ - self.file = file - self.settings = {"related_objects": related_objects} + # Change our mind. Maybe it's a different type? + ifcopenshell.api.run("type.unassign_type", model, related_objects=[furniture]) + """ + settings = {"related_objects": related_objects} - def execute(self) -> None: - related_objects = set(self.settings["related_objects"]) + related_objects = set(settings["related_objects"]) - if self.file.schema == "IFC2X3": - rels = set( - rel - for object in related_objects - if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None)) - ) + if file.schema == "IFC2X3": + rels = set( + rel + for object in related_objects + if (rel := next((rel for rel in object.IsDefinedBy if rel.is_a("IfcRelDefinesByType")), None)) + ) + else: + rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None))) + + for rel in rels: + related_objects = set(rel.RelatedObjects) - related_objects + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", file, **{"element": rel}) else: - rels = set(rel for object in related_objects if (rel := next((rel for rel in object.IsTypedBy), None))) - - for rel in rels: - related_objects = set(rel.RelatedObjects) - related_objects - if related_objects: - rel.RelatedObjects = list(related_objects) - ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) - else: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py index e0caddbe3c..3813724dd7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/__init__.py @@ -15,3 +15,14 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_context_dependent_unit import add_context_dependent_unit +from .add_conversion_based_unit import add_conversion_based_unit +from .add_monetary_unit import add_monetary_unit +from .add_si_unit import add_si_unit +from .assign_unit import assign_unit +from .edit_derived_unit import edit_derived_unit +from .edit_monetary_unit import edit_monetary_unit +from .edit_named_unit import edit_named_unit +from .remove_unit import remove_unit +from .unassign_unit import unassign_unit diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py index a0d705a94b..5de43a505a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_context_dependent_unit.py @@ -17,48 +17,45 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None): - """Add a new arbitrary unit that can only be interpreted in a project specific context +def add_context_dependent_unit(file, unit_type="USERDEFINED", name="THINGAMAJIG", dimensions=None) -> None: + """Add a new arbitrary unit that can only be interpreted in a project specific context - Occasionally the construction industry uses arbitrary units to quantify - objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings - or equipment. + Occasionally the construction industry uses arbitrary units to quantify + objects, like "pairs" of door hardware, "palettes" or "boxes" of fixings + or equipment. - :param unit_type: Typically should be left as USERDEFINED, unless for - some bizarre reason you are redefining something you could use a - sensible normal unit for. In that case, firstly stop whatever you're - doing and have a hard think about your life, and then if life really - is going that badly for you, check out the IFC docs for IfcUnitEnum. - :type unit_type: str - :param name: Give your unit a name. X what? X bananas? - :type name: str - :param dimensions: Units typically measure one of 7 fundamental physical - dimensions: length, mass, time, electric current, temperature, - substance amount, or luminous intensity. These are represented as a - list of 7 integers, representing the exponents of each one of these - dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0), - where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per - second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is - recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0). - :type dimensions: list[int] - :return: The new IfcContextDependentUnit - :rtype: ifcopenshell.entity_instance + :param unit_type: Typically should be left as USERDEFINED, unless for + some bizarre reason you are redefining something you could use a + sensible normal unit for. In that case, firstly stop whatever you're + doing and have a hard think about your life, and then if life really + is going that badly for you, check out the IFC docs for IfcUnitEnum. + :type unit_type: str + :param name: Give your unit a name. X what? X bananas? + :type name: str + :param dimensions: Units typically measure one of 7 fundamental physical + dimensions: length, mass, time, electric current, temperature, + substance amount, or luminous intensity. These are represented as a + list of 7 integers, representing the exponents of each one of these + dimensions. For example, a length unit is (1, 0, 0, 0, 0, 0, 0), + where as an area unit is (2, 0, 0, 0, 0, 0, 0). A unit of meters per + second is (1, 0, -1, 0, 0, 0, 0). For context dependent units, it is + recommended to leave this as the default of (0, 0, 0, 0, 0, 0, 0). + :type dimensions: list[int] + :return: The new IfcContextDependentUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Boxes of things - ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") - """ - self.file = file - self.settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)} + # Boxes of things + ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") + """ + settings = {"unit_type": unit_type, "name": name, "dimensions": dimensions or (0, 0, 0, 0, 0, 0, 0)} - def execute(self): - return self.file.create_entity( - "IfcContextDependentUnit", - Dimensions=self.file.createIfcDimensionalExponents(*self.settings["dimensions"]), - UnitType=self.settings["unit_type"], - Name=self.settings["name"], - ) + return file.create_entity( + "IfcContextDependentUnit", + Dimensions=file.createIfcDimensionalExponents(*settings["dimensions"]), + UnitType=settings["unit_type"], + Name=settings["name"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py index 20b96d3298..b87011b2e1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_conversion_based_unit.py @@ -21,67 +21,66 @@ import ifcopenshell.util.unit from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None): - """Add a conversion based unit +def add_conversion_based_unit( + file: ifcopenshell.file, name: str = "foot", conversion_offset: Optional[float] = None +) -> ifcopenshell.entity_instance: + """Add a conversion based unit - If you're in one of those countries who don't use SI units, you're - probably simply using SI units converted into another unit. If you want - to use _those_ units, you can create a conversion based unit with this - function. You can choose from one of: inch, foot, yard, mile, square - inch, square foot, square yard, acre, square mile, cubic inch, cubic - foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint - US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf, - kip, psi, ksi, minute, hour, day, btu, and fahrenheit. + If you're in one of those countries who don't use SI units, you're + probably simply using SI units converted into another unit. If you want + to use _those_ units, you can create a conversion based unit with this + function. You can choose from one of: inch, foot, yard, mile, square + inch, square foot, square yard, acre, square mile, cubic inch, cubic + foot, cubic yard, litre, fluid ounce UK, fluid ounce US, pint UK, pint + US, gallon UK, gallon US, degree, ounce, pound, ton UK, ton US, lbf, + kip, psi, ksi, minute, hour, day, btu, and fahrenheit. - :param name: A converted name chosen from the list above. - :type name: str - :param conversion_offset: If you want to offset the conversion further - by a set number, you may specify it here. For example, fahrenheit is - 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note - that this is just an example and you don't actually need to specify - that for fahrenheit as it's built into this API function. For - advanced users only. - :type conversion_offset: float, optional - :return: The new IfcConversionBasedUnit or - IfcConversionBasedUnitWithOffset - :rtype: ifcopenshell.entity_instance + :param name: A converted name chosen from the list above. + :type name: str + :param conversion_offset: If you want to offset the conversion further + by a set number, you may specify it here. For example, fahrenheit is + 1.8 * kelvin - 459.67. The -459.67 is the conversion offset. Note + that this is just an example and you don't actually need to specify + that for fahrenheit as it's built into this API function. For + advanced users only. + :type conversion_offset: float, optional + :return: The new IfcConversionBasedUnit or + IfcConversionBasedUnitWithOffset + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Some common imperial measurements - length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch") - area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot") + # Some common imperial measurements + length = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="inch") + area = ifcopenshell.api.run("unit.add_conversion_based_unit", model, name="square foot") - # Make it our default units, if we are doing an imperial building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - """ - self.file = file - self.settings = {"name": name, "conversion_offset": conversion_offset} + # Make it our default units, if we are doing an imperial building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + """ + settings = {"name": name, "conversion_offset": conversion_offset} - def execute(self) -> ifcopenshell.entity_instance: - unit_type = ifcopenshell.util.unit.imperial_types.get(self.settings["name"], "USERDEFINED") - dimensions = ifcopenshell.util.unit.named_dimensions[unit_type] - exponents = self.file.createIfcDimensionalExponents(*dimensions) - si_name = ifcopenshell.util.unit.si_type_names[unit_type] - si_unit = self.file.createIfcSIUnit(UnitType=unit_type, Name=si_name) + unit_type = ifcopenshell.util.unit.imperial_types.get(settings["name"], "USERDEFINED") + dimensions = ifcopenshell.util.unit.named_dimensions[unit_type] + exponents = file.createIfcDimensionalExponents(*dimensions) + si_name = ifcopenshell.util.unit.si_type_names[unit_type] + si_unit = file.createIfcSIUnit(UnitType=unit_type, Name=si_name) - conversion_real = ifcopenshell.util.unit.si_conversions.get(self.settings["name"], 1) - value_component = self.file.create_entity("IfcReal", **{"wrappedValue": conversion_real}) - conversion_factor = self.file.createIfcMeasureWithUnit(value_component, si_unit) + conversion_real = ifcopenshell.util.unit.si_conversions.get(settings["name"], 1) + value_component = file.create_entity("IfcReal", **{"wrappedValue": conversion_real}) + conversion_factor = file.createIfcMeasureWithUnit(value_component, si_unit) - conversion_offset = self.settings["conversion_offset"] - if not conversion_offset: - conversion_offset = ifcopenshell.util.unit.si_offsets.get(self.settings["name"], 0) + conversion_offset = settings["conversion_offset"] + if not conversion_offset: + conversion_offset = ifcopenshell.util.unit.si_offsets.get(settings["name"], 0) - if conversion_offset: - return self.file.createIfcConversionBasedUnitWithOffset( - exponents, - unit_type, - self.settings["name"], - conversion_factor, - conversion_offset, - ) - return self.file.createIfcConversionBasedUnit(exponents, unit_type, self.settings["name"], conversion_factor) + if conversion_offset: + return file.createIfcConversionBasedUnitWithOffset( + exponents, + unit_type, + settings["name"], + conversion_factor, + conversion_offset, + ) + return file.createIfcConversionBasedUnit(exponents, unit_type, settings["name"], conversion_factor) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py index f15b18ab91..7e345b9a7c 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_monetary_unit.py @@ -17,32 +17,29 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, currency="DOLLARYDOO"): - """Add a new currency +def add_monetary_unit(file, currency="DOLLARYDOO") -> None: + """Add a new currency - Currency units are useful in cost plans to know in what currency the - costs are calculated in. The currencies should follow ISO 4217, like - USD, GBP, AUD, MYR, etc. + Currency units are useful in cost plans to know in what currency the + costs are calculated in. The currencies should follow ISO 4217, like + USD, GBP, AUD, MYR, etc. - :param currency: The currency code - :type currency: str - :return: The newly created IfcMonetaryUnit - :rtype: ifcopenshell.entity_instance + :param currency: The currency code + :type currency: str + :return: The newly created IfcMonetaryUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # If you do all your cost plans in Zimbabwean dollars then nobody - # knows how accurate the numbers are. - zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") + # If you do all your cost plans in Zimbabwean dollars then nobody + # knows how accurate the numbers are. + zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") - # Make it our default currency - ifcopenshell.api.run("unit.assign_unit", model, units=[zwl]) - """ - self.file = file - self.settings = {"currency": currency} + # Make it our default currency + ifcopenshell.api.run("unit.assign_unit", model, units=[zwl]) + """ + settings = {"currency": currency} - def execute(self): - return self.file.create_entity("IfcMonetaryUnit", self.settings["currency"]) + return file.create_entity("IfcMonetaryUnit", settings["currency"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py index 7eb8019632..67ce025dd8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/add_si_unit.py @@ -20,48 +20,45 @@ import ifcopenshell.util.unit from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None): - """Add a new SI unit +def add_si_unit( + file: ifcopenshell.file, unit_type: str = "LENGTHUNIT", prefix: Optional[str] = None +) -> ifcopenshell.entity_instance: + """Add a new SI unit - The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT, - AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT, - ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT, - ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT, - FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT, - LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT, - MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT, - RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT, - TIMEUNIT, VOLUMEUNIT. + The supported types are ABSORBEDDOSEUNIT, AMOUNTOFSUBSTANCEUNIT, + AREAUNIT, DOSEEQUIVALENTUNIT, ELECTRICCAPACITANCEUNIT, + ELECTRICCHARGEUNIT, ELECTRICCONDUCTANCEUNIT, ELECTRICCURRENTUNIT, + ELECTRICRESISTANCEUNIT, ELECTRICVOLTAGEUNIT, ENERGYUNIT, FORCEUNIT, + FREQUENCYUNIT, ILLUMINANCEUNIT, INDUCTANCEUNIT, LENGTHUNIT, + LUMINOUSFLUXUNIT, LUMINOUSINTENSITYUNIT, MAGNETICFLUXDENSITYUNIT, + MAGNETICFLUXUNIT, MASSUNIT, PLANEANGLEUNIT, POWERUNIT, PRESSUREUNIT, + RADIOACTIVITYUNIT, SOLIDANGLEUNIT, THERMODYNAMICTEMPERATUREUNIT, + TIMEUNIT, VOLUMEUNIT. - Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO, - KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA. + Prefixes supported are ATTO, CENTI, DECA, DECI, EXA, FEMTO, GIGA, HECTO, + KILO, MEGA, MICRO, MILLI, NANO, PETA, PICO, TERA. - :param unit_type: A type of unit chosen from the list above. For - example, choosing LENGTHUNIT will give you a metre. - :type unit_type: str - :param prefix: A prefix chosen from the list above, or None for no - prefix. - :type prefix: str,optional - :return: The newly created IfcSIUnit - :rtype: ifcopenshell.entity_instance + :param unit_type: A type of unit chosen from the list above. For + example, choosing LENGTHUNIT will give you a metre. + :type unit_type: str + :param prefix: A prefix chosen from the list above, or None for no + prefix. + :type prefix: str,optional + :return: The newly created IfcSIUnit + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - """ - self.file = file - self.settings = {"unit_type": unit_type, "prefix": prefix} + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + """ + settings = {"unit_type": unit_type, "prefix": prefix} - def execute(self) -> ifcopenshell.entity_instance: - name = ifcopenshell.util.unit.si_type_names.get(self.settings["unit_type"], None) - return self.file.create_entity( - "IfcSIUnit", UnitType=self.settings["unit_type"], Name=name, Prefix=self.settings["prefix"] - ) + name = ifcopenshell.util.unit.si_type_names.get(settings["unit_type"], None) + return file.create_entity("IfcSIUnit", UnitType=settings["unit_type"], Name=name, Prefix=settings["prefix"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index 67305295bd..1e6e1a9cdf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -21,61 +21,63 @@ import ifcopenshell.util.unit from typing import Optional +def assign_unit( + file: ifcopenshell.file, + units: Optional[list[ifcopenshell.entity_instance]] = None, + length: Optional[dict] = None, + area: Optional[dict] = None, + volume: Optional[dict] = None, +) -> ifcopenshell.entity_instance: + """Assign default project units + + Whenever a unitised quantity is specified, such as a length, area, + voltage, pressure, etc, these project units are used by default. + + It is also possible to override units for specific properties. For + example, generally you might want square metres for area measurements, + but you might want square millimeters for the measurements of the cross + sectional area of cables in cable trays. However, this function only + deals with the default project units. + + :param units: A list of units to assign as project defaults. See + ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit, + and unit.add_monetary_unit for information on how to create units. + :type units: list[ifcopenshell.entity_instance],optional + :return: The IfcUnitAssignment element + :rtype: ifcopenshell.entity_instance + + Example: + + .. code:: python + + # You need a project before you can assign units. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + + # Alternatively, you may specify without any arguments to + # automatically create millimeters, square meters, and cubic meters + # as a convenience for testing purposes. Sorry imperial folks, we + # prioritise metric here. + ifcopenshell.api.run("unit.assign_unit", model) + """ + usecase = Usecase() + usecase.file = file + usecase.settings = {"units": units} + # This is a convenience function, likely to be deprecated in the future. + usecase.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"} + usecase.settings["area"] = area or {"is_metric": True, "raw": "METERS"} + usecase.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"} + return usecase.execute() + + class Usecase: - def __init__( - self, - file: ifcopenshell.file, - units: Optional[list[ifcopenshell.entity_instance]] = None, - length: Optional[dict] = None, - area: Optional[dict] = None, - volume: Optional[dict] = None, - ): - """Assign default project units - - Whenever a unitised quantity is specified, such as a length, area, - voltage, pressure, etc, these project units are used by default. - - It is also possible to override units for specific properties. For - example, generally you might want square metres for area measurements, - but you might want square millimeters for the measurements of the cross - sectional area of cables in cable trays. However, this function only - deals with the default project units. - - :param units: A list of units to assign as project defaults. See - ifcopenshell.api.unit.add_si_unit, unit.add_conversion_based_unit, - and unit.add_monetary_unit for information on how to create units. - :type units: list[ifcopenshell.entity_instance],optional - :return: The IfcUnitAssignment element - :rtype: ifcopenshell.entity_instance - - Example: - - .. code:: python - - # You need a project before you can assign units. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - - # Alternatively, you may specify without any arguments to - # automatically create millimeters, square meters, and cubic meters - # as a convenience for testing purposes. Sorry imperial folks, we - # prioritise metric here. - ifcopenshell.api.run("unit.assign_unit", model) - """ - self.file = file - self.settings = {"units": units} - # This is a convenience function, likely to be deprecated in the future. - self.settings["length"] = length or {"is_metric": True, "raw": "MILLIMETERS"} - self.settings["area"] = area or {"is_metric": True, "raw": "METERS"} - self.settings["volume"] = volume or {"is_metric": True, "raw": "METERS"} - - def execute(self) -> ifcopenshell.entity_instance: + def execute(self): # We're going to refactor this to split unit creation and assignment if self.settings["units"]: units = self.settings["units"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py index 636430159c..ed56b80461 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_derived_unit.py @@ -17,23 +17,20 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcDerivedUnit +def edit_derived_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcDerivedUnit - For more information about the attributes and data types of an - IfcDerivedUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcDerivedUnit, consult the IFC documentation. - :param unit: The IfcDerivedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + :param unit: The IfcDerivedUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py index aee4b89305..b4f14f328a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_monetary_unit.py @@ -17,34 +17,31 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcMonetaryUnit +def edit_monetary_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcMonetaryUnit - For more information about the attributes and data types of an - IfcMonetaryUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcMonetaryUnit, consult the IFC documentation. - :param unit: The IfcMonetaryUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param unit: The IfcMonetaryUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # If you do all your cost plans in Zimbabwean dollars then nobody - # knows how accurate the numbers are. - zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") + # If you do all your cost plans in Zimbabwean dollars then nobody + # knows how accurate the numbers are. + zwl = ifcopenshell.api.run("unit.add_monetary_unit", model, currency="ZWL") - # Ah who are we kidding - ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"}) - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + # Ah who are we kidding + ifcopenshell.api.run("unit.edit_monetary_unit", model, unit=zwl, attributes={"Currency": "USD"}) + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py index da4ff5290f..be0384aabb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/edit_named_unit.py @@ -17,44 +17,41 @@ # along with IfcOpenShell. If not, see . -class Usecase: - def __init__(self, file, unit=None, attributes=None): - """Edits the attributes of an IfcNamedUnit +def edit_named_unit(file, unit=None, attributes=None) -> None: + """Edits the attributes of an IfcNamedUnit - Named units include SI units, conversion based units (imperial units), - and context dependent units. + Named units include SI units, conversion based units (imperial units), + and context dependent units. - For more information about the attributes and data types of an - IfcNamedUnit, consult the IFC documentation. + For more information about the attributes and data types of an + IfcNamedUnit, consult the IFC documentation. - :param unit: The IfcNamedUnit entity you want to edit - :type unit: ifcopenshell.entity_instance - :param attributes: a dictionary of attribute names and values. - :type attributes: dict, optional - :return: None - :rtype: None + :param unit: The IfcNamedUnit entity you want to edit + :type unit: ifcopenshell.entity_instance + :param attributes: a dictionary of attribute names and values. + :type attributes: dict, optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Boxes of things - unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") + # Boxes of things + unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="BOXES") - # Uh, crates? Boxes? Whatever. - ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"}) - """ - self.file = file - self.settings = {"unit": unit, "attributes": attributes or {}} + # Uh, crates? Boxes? Whatever. + ifcopenshell.api.run("unit.edit_named_unit", model, unit=unit, attibutes={"Name": "CRATES"}) + """ + settings = {"unit": unit, "attributes": attributes or {}} - def execute(self): - for name, value in self.settings["attributes"].items(): - if name == "Dimensions": - dimensions = self.settings["unit"].Dimensions - if len(self.file.get_inverse(dimensions)) > 1: - self.settings["unit"].Dimensions = self.file.createIfcDimensionalExponents(*value) - else: - for i, exponent in enumerate(value): - dimensions[i] = exponent - continue - setattr(self.settings["unit"], name, value) + for name, value in settings["attributes"].items(): + if name == "Dimensions": + dimensions = settings["unit"].Dimensions + if len(file.get_inverse(dimensions)) > 1: + settings["unit"].Dimensions = file.createIfcDimensionalExponents(*value) + else: + for i, exponent in enumerate(value): + dimensions[i] = exponent + continue + setattr(settings["unit"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py index ba9aff862b..ae2cd192cb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/remove_unit.py @@ -20,38 +20,35 @@ import ifcopenshell.util.unit import ifcopenshell.util.element -class Usecase: - def __init__(self, file, unit=None): - """Remove a unit +def remove_unit(file, unit=None) -> None: + """Remove a unit - Be very careful when a unit is removed, as it may mean that previously - defined quantities in the model completely lose their meaning. + Be very careful when a unit is removed, as it may mean that previously + defined quantities in the model completely lose their meaning. - :param unit: The unit element to remove - :type unit: ifcopenshell.entity_instance - :return: None - :rtype: None + :param unit: The unit element to remove + :type unit: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # What? - unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS") + # What? + unit = ifcopenshell.api.run("unit.add_context_dependent_unit", model, name="HANDFULS") - # Yeah maybe not. - ifcopenshell.api.run("unit.remove_unit", model, unit=unit) - """ - self.file = file - self.settings = {"unit": unit} + # Yeah maybe not. + ifcopenshell.api.run("unit.remove_unit", model, unit=unit) + """ + settings = {"unit": unit} - def execute(self): - unit_assignment = ifcopenshell.util.unit.get_unit_assignment(self.file) - if unit_assignment and self.settings["unit"] in unit_assignment.Units: - units = list(unit_assignment.Units) - units.remove(self.settings["unit"]) - if units: - unit_assignment.Units = units - else: - self.file.remove(unit_assignment) - ifcopenshell.util.element.remove_deep(self.file, self.settings["unit"]) + unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file) + if unit_assignment and settings["unit"] in unit_assignment.Units: + units = list(unit_assignment.Units) + units.remove(settings["unit"]) + if units: + unit_assignment.Units = units + else: + file.remove(unit_assignment) + ifcopenshell.util.element.remove_deep(file, settings["unit"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py index 2da27bd08c..0c0b41c2f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/unassign_unit.py @@ -19,43 +19,40 @@ import ifcopenshell from typing import Optional -class Usecase: - def __init__(self, file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None): - """Unassigns units as default units for the project +def unassign_unit(file: ifcopenshell.file, units: Optional[list[ifcopenshell.entity_instance]] = None) -> None: + """Unassigns units as default units for the project - :param units: A list of units to assign as project defaults. - :type units: list[ifcopenshell.entity_instance],optional - :return: None - :rtype: None + :param units: A list of units to assign as project defaults. + :type units: list[ifcopenshell.entity_instance],optional + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # You need a project before you can assign units. - ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") + # You need a project before you can assign units. + ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcProject") - # Millimeters and square meters - length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") - area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") + # Millimeters and square meters + length = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="LENGTHUNIT", prefix="MILLI") + area = ifcopenshell.api.run("unit.add_si_unit", model, unit_type="AREAUNIT") - # Make it our default units, if we are doing a metric building - ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) + # Make it our default units, if we are doing a metric building + ifcopenshell.api.run("unit.assign_unit", model, units=[length, area]) - # Actually, we don't need areas. - ifcopenshell.api.run("unit.unassign_unit", model, units=[area]) - """ - self.file = file - self.settings = {"units": units} + # Actually, we don't need areas. + ifcopenshell.api.run("unit.unassign_unit", model, units=[area]) + """ + settings = {"units": units} - def execute(self): - unit_assignment = self.file.by_type("IfcUnitAssignment") - if not unit_assignment: - return - unit_assignment = unit_assignment[0] - units = set(unit_assignment.Units or []) - units = units - set(self.settings["units"]) - if units: - unit_assignment.Units = list(units) - return unit_assignment - self.file.remove(unit_assignment) + unit_assignment = file.by_type("IfcUnitAssignment") + if not unit_assignment: + return + unit_assignment = unit_assignment[0] + units = set(unit_assignment.Units or []) + units = units - set(settings["units"]) + if units: + unit_assignment.Units = list(units) + return unit_assignment + file.remove(unit_assignment) diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py index e0caddbe3c..51e0db158b 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/__init__.py @@ -15,3 +15,8 @@ # # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . + +from .add_filling import add_filling +from .add_opening import add_opening +from .remove_filling import remove_filling +from .remove_opening import remove_opening diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py index a2867450f6..547178fff8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py @@ -20,103 +20,100 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, opening=None, element=None): - """Fill an opening with an element +def add_filling(file, opening=None, element=None) -> None: + """Fill an opening with an element - Physical elements may have openings in them. For example, a wall might - have an opening for a door. That opening is then filled by the door. - This indicates that when the door moves, the opening will move with it. - Or if the door is removed, then the opening may remain and need to be - filled. + Physical elements may have openings in them. For example, a wall might + have an opening for a door. That opening is then filled by the door. + This indicates that when the door moves, the opening will move with it. + Or if the door is removed, then the opening may remain and need to be + filled. - :param opening: The IfcOpeningElement to fill with the element. - :type opening: ifcopenshell.entity_instance - :param element: The IfcElement to be inserted into the opening. - :type element: ifcopenshell.entity_instance - :return: The new IfcRelFillsElement relationship - :rtype: ifcopenshell.entity_instance + :param opening: The IfcOpeningElement to fill with the element. + :type opening: ifcopenshell.entity_instance + :param element: The IfcElement to be inserted into the opening. + :type element: ifcopenshell.entity_instance + :return: The new IfcRelFillsElement relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall and opening. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Let's create an opening representation of a 950mm x 2100mm door. - # Notice how the thickness is greater than the wall thickness, this - # helps resolve floating point resolution errors in 3D. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.4) - ifcopenshell.api.run("geometry.assign_representation", model, - product=opening, representation=representation) + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, to create a nice overlap for the opening boolean. - matrix = np.identity(4) - matrix[:,3] = [1, -.1, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) - # The opening will now void the wall. - ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) - # Create a door - door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") - # Let's create a door representation of a 950mm x 2100mm door. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.05) - ifcopenshell.api.run("geometry.assign_representation", model, - product=door, representation=representation) + # Let's create a door representation of a 950mm x 2100mm door. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.05) + ifcopenshell.api.run("geometry.assign_representation", model, + product=door, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, which lines up with our opening. - matrix = np.identity(4) - matrix[:,3] = [1, .05, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, which lines up with our opening. + matrix = np.identity(4) + matrix[:,3] = [1, .05, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix) - # The door will now fill the opening. - ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) - """ - self.file = file - self.settings = {"opening": opening, "element": element} + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + """ + settings = {"opening": opening, "element": element} - def execute(self): - fills_voids = self.settings["element"].FillsVoids + fills_voids = settings["element"].FillsVoids - if fills_voids: - if fills_voids[0].RelatingOpeningElement == self.settings["opening"]: - return - history = fills_voids[0].OwnerHistory - self.file.remove(fills_voids[0]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if fills_voids: + if fills_voids[0].RelatingOpeningElement == settings["opening"]: + return + history = fills_voids[0].OwnerHistory + file.remove(fills_voids[0]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - self.file.create_entity( - "IfcRelFillsElement", - GlobalId=ifcopenshell.guid.new(), - RelatingOpeningElement=self.settings["opening"], - RelatedBuildingElement=self.settings["element"], - ) + file.create_entity( + "IfcRelFillsElement", + GlobalId=ifcopenshell.guid.new(), + RelatingOpeningElement=settings["opening"], + RelatedBuildingElement=settings["element"], + ) diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py index 142eaedd87..d873ac2cd3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py @@ -22,118 +22,115 @@ import ifcopenshell.util.element import ifcopenshell.util.placement -class Usecase: - def __init__( - self, file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance - ): - """Create an opening in an element +def add_opening( + file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance +) -> ifcopenshell.entity_instance: + """Create an opening in an element - It is often necessary to cut out openings in elements like walls and - slabs to make space to insert doors, windows, and other services that go - through these penetrations. + It is often necessary to cut out openings in elements like walls and + slabs to make space to insert doors, windows, and other services that go + through these penetrations. - Whereas it is possible to simply draw the wall as a rectangle with a - hole in it for the opening, often these openings have specific meanings. - For example, an opening might be filled with a window, and so when the - window moves, the opening should move with it. Alternatively, the - opening itself might have fire or acoustic requirements, such that any - service or equipment passing through that space must also comply with - those requirements. For these types of semantic openings, you should - have a distinct opening element which voids your regular element. For - example, your wall will still be a rectangular prism with no hole in it, - and a separate opening element will have a box representing the extents - of the opening for a window. The opening element will automatically - perform a geometric boolean operation to cut out the wall's geometry. + Whereas it is possible to simply draw the wall as a rectangle with a + hole in it for the opening, often these openings have specific meanings. + For example, an opening might be filled with a window, and so when the + window moves, the opening should move with it. Alternatively, the + opening itself might have fire or acoustic requirements, such that any + service or equipment passing through that space must also comply with + those requirements. For these types of semantic openings, you should + have a distinct opening element which voids your regular element. For + example, your wall will still be a rectangular prism with no hole in it, + and a separate opening element will have a box representing the extents + of the opening for a window. The opening element will automatically + perform a geometric boolean operation to cut out the wall's geometry. - Whenever you have an opening in you project, you should determine - whether or not the opening is semantic (i.e. should be represented by a - distinct opening object) or non-semantic (i.e. should simply be - booleaned or be part of the shape of the object). + Whenever you have an opening in you project, you should determine + whether or not the opening is semantic (i.e. should be represented by a + distinct opening object) or non-semantic (i.e. should simply be + booleaned or be part of the shape of the object). - :param opening: The IfcOpeningElement to cut out the element. - :type opening: ifcopenshell.entity_instance - :param element: The IfcElement to insert the opening into. - :type element: ifcopenshell.entity_instance - :return: The new IfcRelVoidsElement relationship - :rtype: ifcopenshell.entity_instance + :param opening: The IfcOpeningElement to cut out the element. + :type opening: ifcopenshell.entity_instance + :param element: The IfcElement to insert the opening into. + :type element: ifcopenshell.entity_instance + :return: The new IfcRelVoidsElement relationship + :rtype: ifcopenshell.entity_instance - Example: + Example: - .. code:: python + .. code:: python - # A bit of preparation, let's create some geometric contexts since - # we want to create some geometry for our wall and opening. - model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") - body = ifcopenshell.api.run("context.add_context", model, - context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Let's use the "3D Body" representation we created earlier to add a - # new wall-like body geometry, 5 meters long, 3 meters high, and - # 200mm thick - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=5, height=3, thickness=0.2) - ifcopenshell.api.run("geometry.assign_representation", model, - product=wall, representation=representation) + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) - # Place our wall at the origin - ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Let's create an opening representation of a 950mm x 2100mm door. - # Notice how the thickness is greater than the wall thickness, this - # helps resolve floating point resolution errors in 3D. - representation = ifcopenshell.api.run("geometry.add_wall_representation", model, - context=body, length=.95, height=2.1, thickness=0.4) - ifcopenshell.api.run("geometry.assign_representation", model, - product=opening, representation=representation) + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) - # Let's shift our door 1 meter along the wall and 100mm along the - # wall, to create a nice overlap for the opening boolean. - matrix = np.identity(4) - matrix[:,3] = [1, -.1, 0, 0] - ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) - # The opening will now void the wall. - ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) - """ - self.file = file - self.settings = {"opening": opening, "element": element} + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + """ + settings = {"opening": opening, "element": element} - def execute(self) -> ifcopenshell.entity_instance: - voids_elements = self.settings["opening"].VoidsElements + voids_elements = settings["opening"].VoidsElements - if voids_elements: - if voids_elements[0].RelatingBuildingElement == self.settings["element"]: - return voids_elements[0] - history = voids_elements[0].OwnerHistory - self.file.remove(voids_elements[0]) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) + if voids_elements: + if voids_elements[0].RelatingBuildingElement == settings["element"]: + return voids_elements[0] + history = voids_elements[0].OwnerHistory + file.remove(voids_elements[0]) + if history: + ifcopenshell.util.element.remove_deep2(file, history) - rel = self.file.create_entity( - "IfcRelVoidsElement", - **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatingBuildingElement": self.settings["element"], - "RelatedOpeningElement": self.settings["opening"], - } + rel = file.create_entity( + "IfcRelVoidsElement", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", file), + "RelatingBuildingElement": settings["element"], + "RelatedOpeningElement": settings["opening"], + } + ) + + placement = getattr(settings["opening"], "ObjectPlacement", None) + if placement and placement.is_a("IfcLocalPlacement"): + ifcopenshell.api.run( + "geometry.edit_object_placement", + file, + product=settings["opening"], + matrix=ifcopenshell.util.placement.get_local_placement(settings["opening"].ObjectPlacement), + is_si=False, ) - placement = getattr(self.settings["opening"], "ObjectPlacement", None) - if placement and placement.is_a("IfcLocalPlacement"): - ifcopenshell.api.run( - "geometry.edit_object_placement", - self.file, - product=self.settings["opening"], - matrix=ifcopenshell.util.placement.get_local_placement(self.settings["opening"].ObjectPlacement), - is_si=False, - ) - - return rel + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py index 6d5ab79752..b4c3188672 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py @@ -20,47 +20,44 @@ import ifcopenshell import ifcopenshell.util.element -class Usecase: - def __init__(self, file, element=None): - """Remove a filling relationship +def remove_filling(file, element=None) -> None: + """Remove a filling relationship - If an element is filling an opening, this removes the relationship such - that the opening and element both still exist, but the element no longer - fills the opening. + If an element is filling an opening, this removes the relationship such + that the opening and element both still exist, but the element no longer + fills the opening. - :param element: The element filling an opening. - :type element: ifcopenshell.entity_instance - :return: None - :rtype: None + :param element: The element filling an opening. + :type element: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create a wall - wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") - # Create an opening, such as for a service penetration with fire and - # acoustic requirements. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Create a door - door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") - # The door will now fill the opening. - ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) - # Not anymore! - ifcopenshell.api.run("void.remove_filling", model, element=door) - """ - self.file = file - self.settings = {"element": element} + # Not anymore! + ifcopenshell.api.run("void.remove_filling", model, element=door) + """ + settings = {"element": element} - def execute(self): - for rel in self.file.by_type("IfcRelFillsElement"): - if rel.RelatedBuildingElement == self.settings["element"]: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - break + for rel in file.by_type("IfcRelFillsElement"): + if rel.RelatedBuildingElement == settings["element"]: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + break diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py index 5ffba93e25..58b7782333 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py @@ -20,44 +20,41 @@ import ifcopenshell.api import ifcopenshell.util.element -class Usecase: - def __init__(self, file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance): - """Remove an opening +def remove_opening(file: ifcopenshell.entity_instance, opening: ifcopenshell.entity_instance) -> None: + """Remove an opening - Fillings are retained as orphans. Voided elements remain. Openings - cannot exist by themselves, so not only is the opening relationship - removed, the opening is also removed. + Fillings are retained as orphans. Voided elements remain. Openings + cannot exist by themselves, so not only is the opening relationship + removed, the opening is also removed. - :param opening: The IfcOpeningElement to remove. - :type opening: ifcopenshell.entity_instance - :return: None - :rtype: None + :param opening: The IfcOpeningElement to remove. + :type opening: ifcopenshell.entity_instance + :return: None + :rtype: None - Example: + Example: - .. code:: python + .. code:: python - # Create an oprhaned opening. Note that an orphaned opening is - # invalid, as an opening can only exist when voiding another - # element. - opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + # Create an oprhaned opening. Note that an orphaned opening is + # invalid, as an opening can only exist when voiding another + # element. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") - # Remove it. This brings us back to a valid model. - ifcopenshell.api.run("void.remove_opening", model, opening=opening) - """ - self.file = file - self.settings = {"opening": opening} + # Remove it. This brings us back to a valid model. + ifcopenshell.api.run("void.remove_opening", model, opening=opening) + """ + settings = {"opening": opening} - def execute(self) -> None: - for rel in self.settings["opening"].VoidsElements: + for rel in settings["opening"].VoidsElements: + history = rel.OwnerHistory + file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(file, history) + if settings["opening"].is_a("IfcOpeningElement"): + for rel in settings["opening"].HasFillings: history = rel.OwnerHistory - self.file.remove(rel) + file.remove(rel) if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - if self.settings["opening"].is_a("IfcOpeningElement"): - for rel in self.settings["opening"].HasFillings: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - ifcopenshell.api.run("root.remove_product", self.file, product=self.settings["opening"]) + ifcopenshell.util.element.remove_deep2(file, history) + ifcopenshell.api.run("root.remove_product", file, product=settings["opening"])