First Commit
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Manage cost schedules, cost items, cost estimation and parametric quantity
|
||||
take-off
|
||||
|
||||
IFC supports storing cost schedules and detailed cost breakdown structures,
|
||||
including formulas, subtotals, and parametric links to model element
|
||||
quantities.
|
||||
"""
|
||||
|
||||
from .. import wrap_usecases
|
||||
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 .copy_cost_schedule import copy_cost_schedule
|
||||
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
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_cost_item",
|
||||
"add_cost_item_quantity",
|
||||
"add_cost_schedule",
|
||||
"add_cost_value",
|
||||
"assign_cost_item_quantity",
|
||||
"assign_cost_value",
|
||||
"calculate_cost_item_resource_value",
|
||||
"copy_cost_item",
|
||||
"copy_cost_item_values",
|
||||
"copy_cost_schedule",
|
||||
"edit_cost_item",
|
||||
"edit_cost_item_quantity",
|
||||
"edit_cost_schedule",
|
||||
"edit_cost_value",
|
||||
"edit_cost_value_formula",
|
||||
"remove_cost_item",
|
||||
"remove_cost_item_quantity",
|
||||
"remove_cost_schedule",
|
||||
"remove_cost_value",
|
||||
"unassign_cost_item_quantity",
|
||||
]
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,66 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import ifcopenshell.api.control
|
||||
import ifcopenshell.api.nest
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.guid
|
||||
|
||||
|
||||
def add_cost_item(
|
||||
file: ifcopenshell.file,
|
||||
cost_schedule: Optional[ifcopenshell.entity_instance] = None,
|
||||
cost_item: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""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.
|
||||
|
||||
Either `cost_schedule` or `cost_item` must be provided.
|
||||
|
||||
: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.
|
||||
: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.
|
||||
:return: The newly created IfcCostItem
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# The very first cost item must be in a cost schedule
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
|
||||
# You may add cost items as top level item in the schedule
|
||||
item1 = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# Alternatively you may add them as subitems
|
||||
item2 = ifcopenshell.api.cost.add_cost_item(model, cost_item=item1)
|
||||
"""
|
||||
cost_item_ = ifcopenshell.api.root.create_entity(file, ifc_class="IfcCostItem")
|
||||
|
||||
if cost_schedule:
|
||||
ifcopenshell.api.control.assign_control(file, cost_schedule, [cost_item_])
|
||||
elif cost_item:
|
||||
ifcopenshell.api.nest.assign_object(file, related_objects=[cost_item_], relating_object=cost_item)
|
||||
return cost_item_
|
||||
@@ -0,0 +1,90 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
def add_cost_item_quantity(
|
||||
file: ifcopenshell.file,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
ifc_class: ifcopenshell.util.unit.QUANTITY_CLASS = "IfcQuantityCount",
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
:param ifc_class: The type of quantity to add
|
||||
:return: The newly created quantity entity, chosen from the ifc_class
|
||||
parameter
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
chair = ifcopenshell.api.root.create_entity(model, ifc_class="IfcFurniture")
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.control.assign_control(model,
|
||||
relating_control=item, related_objects=[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.cost.add_cost_item_quantity(model,
|
||||
cost_item=item, ifc_class="IfcQuantityCount")
|
||||
"""
|
||||
quantity = file.create_entity(ifc_class, Name="Unnamed")
|
||||
# 3 IfcPhysicalSimpleQuantity Value
|
||||
# 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 ifc_class == "IfcQuantityCount":
|
||||
count = 0
|
||||
for rel in cost_item.Controls:
|
||||
count += len(rel.RelatedObjects)
|
||||
quantity[3] = count
|
||||
else:
|
||||
quantity[3] = 0.0
|
||||
quantities = list(cost_item.CostQuantities or [])
|
||||
quantities.append(quantity)
|
||||
cost_item.CostQuantities = quantities
|
||||
return quantity
|
||||
@@ -0,0 +1,63 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.sequence
|
||||
|
||||
|
||||
def add_cost_schedule(
|
||||
file: ifcopenshell.file, name: Optional[str] = None, predefined_type: str = "NOTDEFINED"
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""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.
|
||||
|
||||
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.
|
||||
|
||||
:param name: The name of the cost schedule.
|
||||
:param predefined_type: The predefined type of the cost schedule, chosen
|
||||
from a valid type in the IFC documentation for
|
||||
IfcCostScheduleTypeEnum
|
||||
:return: The newly created IfcCostSchedule entity
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
# Now that we have a cost schedule, we may add cost items to it
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
"""
|
||||
cost_schedule = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class="IfcCostSchedule",
|
||||
predefined_type=predefined_type,
|
||||
name=name,
|
||||
)
|
||||
cost_schedule.UpdateDate = ifcopenshell.api.sequence.add_date_time(file, datetime.now())
|
||||
return cost_schedule
|
||||
@@ -0,0 +1,105 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new value or subvalue to a cost item
|
||||
|
||||
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 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).
|
||||
|
||||
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.
|
||||
|
||||
: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.
|
||||
:return: The newly created IfcCostValue
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# We always need a schedule first prior to adding any cost items
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
|
||||
# Option 1: This cost item will have a full cost of 42.0
|
||||
item1 = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item1)
|
||||
ifcopenshell.api.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.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item2)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5.0})
|
||||
quantity = ifcopenshell.api.cost.add_cost_item_quantity(model,
|
||||
cost_item=item2, ifc_class="IfcQuantityVolume")
|
||||
ifcopenshell.api.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.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item1)
|
||||
subvalue1 = ifcopenshell.api.cost.add_cost_value(model, parent=value)
|
||||
subvalue2 = ifcopenshell.api.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.cost.edit_cost_value(model, cost_value=value, attributes={"Category": "*"})
|
||||
ifcopenshell.api.cost.edit_cost_value(model,
|
||||
cost_value=subvalue1, attributes={"AppliedValue": 2.0})
|
||||
ifcopenshell.api.cost.edit_cost_value(model,
|
||||
cost_value=subvalue2, attributes={"AppliedValue": 3.0})
|
||||
"""
|
||||
value = file.create_entity("IfcCostValue")
|
||||
if parent.is_a("IfcCostItem"):
|
||||
values = list(parent.CostValues or [])
|
||||
values.append(value)
|
||||
parent.CostValues = values
|
||||
elif parent.is_a("IfcConstructionResource"):
|
||||
values = list(parent.BaseCosts or [])
|
||||
values.append(value)
|
||||
parent.BaseCosts = values
|
||||
elif parent.is_a("IfcCostValue"):
|
||||
values = list(parent.Components or [])
|
||||
values.append(value)
|
||||
parent.Components = values
|
||||
return value
|
||||
@@ -0,0 +1,160 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell.api.control
|
||||
import ifcopenshell.api.cost
|
||||
|
||||
|
||||
def assign_cost_item_quantity(
|
||||
file: ifcopenshell.file,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
products: list[ifcopenshell.entity_instance],
|
||||
prop_name: str = "",
|
||||
) -> 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.
|
||||
|
||||
If cost item has just 1 quantity and it's IfcQuantityCount, API will
|
||||
assume that quantity is used for counting controlled objects
|
||||
and it will recalculate the quantity value at the end of the API call
|
||||
as long as the RelatedObjects are not IfcConstructionResource which do not
|
||||
count towards the cost item (they only provide value).
|
||||
|
||||
:param cost_item: The IfcCostItem to assign parametric quantities to
|
||||
:param products: The IfcObjects to assign parametric quantities to
|
||||
: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.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# Let's imagine a unit cost of 5.0 per unit volume
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5.0})
|
||||
|
||||
slab = ifcopenshell.api.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.pset.add_qto(model, product=slab, name="Qto_SlabBaseQuantities")
|
||||
ifcopenshell.api.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.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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
if self.settings["prop_name"]:
|
||||
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
|
||||
for product in self.settings["products"]:
|
||||
if product.is_a("IfcSpatialElement"):
|
||||
continue
|
||||
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()
|
||||
):
|
||||
continue
|
||||
self.add_quantity_from_related_object(product)
|
||||
if self.settings["prop_name"]:
|
||||
self.settings["cost_item"].CostQuantities = list(self.quantities)
|
||||
else:
|
||||
self.update_cost_item_count()
|
||||
|
||||
def assign_cost_control(
|
||||
self, related_object: ifcopenshell.entity_instance, cost_item: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
return ifcopenshell.api.control.assign_control(
|
||||
self.file,
|
||||
related_objects=[related_object],
|
||||
relating_control=cost_item,
|
||||
)
|
||||
|
||||
def add_quantity_from_related_object(self, element: ifcopenshell.entity_instance) -> None:
|
||||
for relationship in element.IsDefinedBy:
|
||||
if relationship.is_a("IfcRelDefinesByProperties"):
|
||||
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
|
||||
|
||||
def add_quantity_from_qto(self, qto: ifcopenshell.entity_instance) -> None:
|
||||
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():
|
||||
self.quantities.add(prop)
|
||||
|
||||
def update_cost_item_count(self):
|
||||
# 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 not self.settings["cost_item"].CostQuantities:
|
||||
ifcopenshell.api.cost.add_cost_item_quantity(
|
||||
self.file,
|
||||
cost_item=self.settings["cost_item"],
|
||||
ifc_class="IfcQuantityCount",
|
||||
)
|
||||
if len(self.settings["cost_item"].CostQuantities) == 1:
|
||||
quantity = self.settings["cost_item"].CostQuantities[0]
|
||||
if quantity.is_a("IfcQuantityCount"):
|
||||
count = 0
|
||||
for rel in self.settings["cost_item"].Controls:
|
||||
for obj in rel.RelatedObjects:
|
||||
# Only increment if not a resource
|
||||
if not obj.is_a("IfcConstructionResource"):
|
||||
count += 1
|
||||
quantity[3] = count
|
||||
@@ -0,0 +1,71 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api.cost
|
||||
|
||||
|
||||
def assign_cost_value(
|
||||
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, cost_rate: ifcopenshell.entity_instance
|
||||
) -> 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.
|
||||
|
||||
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
|
||||
:param cost_rate: The IfcCostItem that you want to copy the values from
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Let's create a schedule of rates with a single rate in it of 5.0
|
||||
rate_tables = ifcopenshell.api.cost.add_cost_schedule(model,
|
||||
predefined_type="SCHEDULEOFRATES")
|
||||
rate = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=rate)
|
||||
ifcopenshell.api.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.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.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.cost.assign_cost_value(model, cost_item=item, cost_rate=rate)
|
||||
"""
|
||||
if cost_item.CostValues:
|
||||
[
|
||||
ifcopenshell.api.cost.remove_cost_value(
|
||||
file,
|
||||
parent=cost_item,
|
||||
cost_value=cost_value,
|
||||
)
|
||||
for cost_value in cost_item.CostValues
|
||||
]
|
||||
# This is an assumption, and not part of the official IFC documentation
|
||||
cost_item.CostValues = cost_rate.CostValues
|
||||
@@ -0,0 +1,112 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.resource
|
||||
|
||||
|
||||
def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> 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.
|
||||
|
||||
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.
|
||||
|
||||
:param cost_item: The IfcCostItem to calculate
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# First, we need a cost schedule and item
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# Let's imagine we have our own formworking crew
|
||||
crew = ifcopenshell.api.resource.add_resource(model, ifc_class="IfcCrewResource")
|
||||
|
||||
# ... and they need concrete
|
||||
concrete = ifcopenshell.api.resource.add_resource(model,
|
||||
ifc_class="IfcConstructionMaterialResource", parent_resource=crew)
|
||||
ifcopenshell.api.control.assign_control(model,
|
||||
relating_control=item, related_objects=[concrete])
|
||||
# ... which has a unit price of 42.0 per m3
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=concrete)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 42.0})
|
||||
# ... and a volume of 200m3
|
||||
quantity = ifcopenshell.api.resource.add_resource_quantity(model,
|
||||
resource=concrete, ifc_class="IfcQuantityVolume")
|
||||
ifcopenshell.api.resource.edit_resource_quantity(model,
|
||||
physical_quantity=quantity, "attributes": {"VolumeValue": 200.0})
|
||||
|
||||
# Let's say they also need some equipment
|
||||
equipment = ifcopenshell.api.resource.add_resource(model,
|
||||
ifc_class="IfcConstructionEquipmentResource", parent_resource=crew)
|
||||
ifcopenshell.api.control.assign_control(model,
|
||||
relating_control=item, related_objects=[equipment])
|
||||
# ... with a fixed price of 50,000
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=concrete)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 42.0})
|
||||
|
||||
# (42 * 200) + 50000 = 58400 is our calculated cost
|
||||
ifcopenshell.api.cost.calculate_cost_item_resource_value(model, cost_item=item)
|
||||
"""
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
ifcopenshell.api.cost.remove_cost_value(file, parent=cost_item, cost_value=cost_value)
|
||||
|
||||
resources = []
|
||||
for rel in 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 cost is None:
|
||||
# Concept to standardise - Not defined in schema, but this makes manual scheduling of resources 10x faster and less duplicate data.
|
||||
parent_cost = ifcopenshell.util.resource.get_parent_cost(resource)
|
||||
if parent_cost:
|
||||
cost, unit = parent_cost
|
||||
quantity = ifcopenshell.util.resource.get_quantity(resource)
|
||||
if cost is None:
|
||||
continue
|
||||
if unit and "day" in unit:
|
||||
quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
|
||||
formula = "{}*{}".format(cost, quantity)
|
||||
cost_value = ifcopenshell.api.cost.add_cost_value(file, parent=cost_item)
|
||||
cost_value.Name = resource.Name
|
||||
ifcopenshell.api.cost.edit_cost_value_formula(file, cost_value=cost_value, formula=formula)
|
||||
@@ -0,0 +1,115 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Union
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.nest
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def copy_cost_item(
|
||||
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
|
||||
"""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
|
||||
:return: The duplicated cost item or the list of duplicated cost items if the latter has children
|
||||
|
||||
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
|
||||
return usecase.execute(cost_item)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
new_cost_items: list[ifcopenshell.entity_instance]
|
||||
|
||||
def execute(
|
||||
self, cost_item: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
|
||||
self.new_cost_items = []
|
||||
self.duplicate_cost_item(cost_item)
|
||||
return self.new_cost_items[0] if len(self.new_cost_items) == 1 else self.new_cost_items
|
||||
|
||||
def duplicate_cost_item(self, cost_item: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
new_cost_item = ifcopenshell.util.element.copy_deep(self.file, cost_item)
|
||||
self.new_cost_items.append(new_cost_item)
|
||||
self.copy_indirect_attributes(cost_item, new_cost_item)
|
||||
return new_cost_item
|
||||
|
||||
def copy_indirect_attributes(
|
||||
self, from_element: ifcopenshell.entity_instance, to_element: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
for inverse in self.file.get_inverse(from_element):
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
inverse.RelatedObjects = [to_element]
|
||||
pset = ifcopenshell.util.element.copy_deep(self.file, inverse.RelatingPropertyDefinition)
|
||||
inverse.RelatingPropertyDefinition = pset
|
||||
elif inverse.is_a("IfcRelNests") and inverse.RelatingObject == from_element:
|
||||
nested_cost_items = [e for e in inverse.RelatedObjects]
|
||||
if nested_cost_items:
|
||||
new_cost_items = []
|
||||
for cost_item in nested_cost_items:
|
||||
new_cost_item = self.duplicate_cost_item(cost_item)
|
||||
new_cost_items.append(new_cost_item)
|
||||
inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
inverse.RelatingObject = to_element
|
||||
inverse.RelatedObjects = new_cost_items
|
||||
ifcopenshell.api.nest.unassign_object(self.file, related_objects=new_cost_items)
|
||||
ifcopenshell.api.nest.assign_object(
|
||||
self.file, related_objects=new_cost_items, relating_object=to_element
|
||||
)
|
||||
# elif inverse.is_a("IfcRelAssignsToProduct"):
|
||||
# continue
|
||||
# to_element.IsDefinedBy = inverse.RelatingOrder
|
||||
# elif inverse.is_a("IfcRelAssignsToProcess"):
|
||||
# to_element.IsDefinedBy = inverse.RelatingProcess
|
||||
# elif inverse.is_a("IfcRelAssignsToResource"):
|
||||
# continue
|
||||
# elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
# continue
|
||||
# elif inverse.is_a("IfcRelDefinesByObject"):
|
||||
# continue
|
||||
else:
|
||||
for i, value in enumerate(inverse):
|
||||
if value == from_element:
|
||||
new_inverse = ifcopenshell.util.element.copy(self.file, inverse)
|
||||
new_inverse[i] = to_element
|
||||
elif isinstance(value, (tuple, list)) and from_element in value:
|
||||
new_value = list(value)
|
||||
new_value.append(to_element)
|
||||
inverse[i] = new_value
|
||||
@@ -0,0 +1,58 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def copy_cost_item_values(
|
||||
file: ifcopenshell.file, source: ifcopenshell.entity_instance, destination: ifcopenshell.entity_instance
|
||||
) -> 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.
|
||||
|
||||
:param source: The IfcCostItem to copy cost values from
|
||||
:param destination: The IfcCostItem to copy cost values from
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Assume we have a schedule with multiple items in it
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item1 = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
item2 = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# One of the items has a value
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5000.0})
|
||||
|
||||
# Let's copy the value from one item to another
|
||||
ifcopenshell.api.cost.copy_cost_item_values(model, source=item1, destination=item2)
|
||||
"""
|
||||
for cost_value in destination.CostValues or []:
|
||||
ifcopenshell.api.cost.remove_cost_value(file, source, cost_value=cost_value)
|
||||
copied_cost_values = []
|
||||
for cost_value in source.CostValues or []:
|
||||
copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value))
|
||||
destination.CostValues = copied_cost_values
|
||||
@@ -0,0 +1,49 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api.control
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def copy_cost_schedule(
|
||||
file: ifcopenshell.file, cost_schedule: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Copy a cost schedule.
|
||||
|
||||
:param cost_schedule: IfcCostSchedule to copy.
|
||||
:return: The duplicated IfcCostSchedule entity
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
new_schedule = ifcopenshell.api.cost.copy_cost_schedule(schedule)
|
||||
"""
|
||||
# Shared code logic with copy_work_schedule.
|
||||
new_schedule = ifcopenshell.util.element.copy(file, cost_schedule)
|
||||
|
||||
for rel in cost_schedule.Controls:
|
||||
for cost_item in rel.RelatedObjects:
|
||||
duplicated_cost_item = ifcopenshell.api.cost.copy_cost_item(file, cost_item)
|
||||
if isinstance(duplicated_cost_item, list):
|
||||
# All other nested items are not connected to the cost schedule explicitly.
|
||||
duplicated_cost_item = duplicated_cost_item[0]
|
||||
ifcopenshell.api.control.assign_control(file, new_schedule, [duplicated_cost_item])
|
||||
return new_schedule
|
||||
@@ -0,0 +1,44 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_cost_item(
|
||||
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcCostItem
|
||||
|
||||
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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.cost.edit_cost_item(model, cost_item=item, attributes={"Name": "Foo"})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(cost_item, name, value)
|
||||
@@ -0,0 +1,52 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_cost_item_quantity(
|
||||
file: ifcopenshell.file, physical_quantity: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcPhysicalQuantity
|
||||
|
||||
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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.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.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5.0})
|
||||
quantity = ifcopenshell.api.cost.add_cost_item_quantity(model,
|
||||
cost_item=item, ifc_class="IfcQuantityVolume")
|
||||
ifcopenshell.api.cost.edit_cost_item_quantity(model,
|
||||
physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(physical_quantity, name, value)
|
||||
@@ -0,0 +1,44 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_cost_schedule(
|
||||
file: ifcopenshell.file, cost_schedule: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcCostSchedule
|
||||
|
||||
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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
ifcopenshell.api.cost.edit_cost_schedule(model,
|
||||
cost_schedule=schedule, attributes={"Name": "Foo"})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(cost_schedule, name, value)
|
||||
@@ -0,0 +1,64 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
|
||||
|
||||
def edit_cost_value(
|
||||
file: ifcopenshell.file, cost_value: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcCostValue
|
||||
|
||||
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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# This cost item will have a total cost of 42
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 42.0})
|
||||
"""
|
||||
for name, value in 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 = 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:
|
||||
ifcopenshell.util.element.remove_deep2(file, old_unit_basis)
|
||||
setattr(cost_value, name, value)
|
||||
@@ -0,0 +1,80 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.cost
|
||||
|
||||
|
||||
def edit_cost_value_formula(file: ifcopenshell.file, cost_value: ifcopenshell.entity_instance, formula: str) -> 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
|
||||
:param formula: The formula following the language of ifcopenshell.util.cost
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
try:
|
||||
data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"])
|
||||
except:
|
||||
return
|
||||
self.edit_cost_value(data)
|
||||
|
||||
def edit_cost_value(self, data, parent=None):
|
||||
ifc = data.get("ifc", None)
|
||||
if not ifc:
|
||||
ifc = ifcopenshell.api.cost.add_cost_value(self.file, parent=parent)
|
||||
if "AppliedValue" in data:
|
||||
if data["AppliedValue"]:
|
||||
ifc.AppliedValue = self.file.createIfcMonetaryMeasure(data["AppliedValue"])
|
||||
else:
|
||||
ifc.AppliedValue = None
|
||||
ifc.Category = data["Category"] if "Category" in data else None
|
||||
ifc.ArithmeticOperator = data["ArithmeticOperator"] if "ArithmeticOperator" in data else None
|
||||
if "Components" in data:
|
||||
for component in data["Components"]:
|
||||
self.edit_cost_value(component, ifc)
|
||||
@@ -0,0 +1,63 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> 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.
|
||||
|
||||
:param cost_item: The IfcCostItem entity you want to remove
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.cost.remove_cost_item(model, cost_item=item)
|
||||
"""
|
||||
# TODO: do a deep purge
|
||||
for inverse in file.get_inverse(cost_item):
|
||||
if inverse.is_a("IfcRelNests"):
|
||||
if inverse.RelatingObject == cost_item:
|
||||
for related_object in inverse.RelatedObjects:
|
||||
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
|
||||
elif inverse.RelatedObjects == (cost_item,):
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
if len(inverse.RelatedObjects) >= 2 or inverse.RelatingControl == cost_item:
|
||||
continue
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
history = cost_item.OwnerHistory
|
||||
file.remove(cost_item)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
@@ -0,0 +1,51 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def remove_cost_item_quantity(
|
||||
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, physical_quantity: ifcopenshell.entity_instance
|
||||
) -> 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.
|
||||
|
||||
:param cost_item: The IfcCostItem that the quantity is assigned to
|
||||
:param physical_quantity: The IfcPhysicalQuantity to remove
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
quantity = ifcopenshell.api.cost.add_cost_item_quantity(model,
|
||||
cost_item=item, ifc_class="IfcQuantityVolume")
|
||||
# Let's change our mind and delete it
|
||||
ifcopenshell.api.cost.remove_cost_item(model,
|
||||
cost_item=item, physical_quantity=quantity)
|
||||
"""
|
||||
if file.get_total_inverses(physical_quantity) == 1:
|
||||
file.remove(physical_quantity)
|
||||
return
|
||||
quantities = list(cost_item.CostQuantities or [])
|
||||
quantities.remove(physical_quantity)
|
||||
cost_item.CostQuantities = quantities
|
||||
@@ -0,0 +1,52 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.cost
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_cost_schedule(file: ifcopenshell.file, cost_schedule: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a cost schedule
|
||||
|
||||
All associated relationships with the cost schedule are also removed,
|
||||
including all cost items.
|
||||
|
||||
:param cost_schedule: The IfcCostSchedule entity you want to remove
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.cost.remove_cost_schedule(model, cost_schedule=schedule)
|
||||
"""
|
||||
# TODO: do a deep purge
|
||||
for inverse in file.get_inverse(cost_schedule):
|
||||
if inverse.is_a("IfcRelAssignsToControl"):
|
||||
[
|
||||
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
|
||||
for related_object in inverse.RelatedObjects
|
||||
if related_object.is_a("IfcCostItem")
|
||||
]
|
||||
history = cost_schedule.OwnerHistory
|
||||
file.remove(cost_schedule)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
@@ -0,0 +1,62 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def remove_cost_value(
|
||||
file: ifcopenshell.file, parent: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance
|
||||
) -> 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)
|
||||
|
||||
:param parent: The IfcCostItem, IfcConstructionResource, or IfcCostValue
|
||||
that the IfcCostValue is assigned to.
|
||||
:param cost_value: The IfcCostValue that you want to remove
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.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.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5.0})
|
||||
|
||||
ifcopenshell.api.cost.remove_cost_value(model, parent=item, cost_value=value)
|
||||
"""
|
||||
if file.get_total_inverses(cost_value) == 1:
|
||||
file.remove(cost_value)
|
||||
# TODO deep purge
|
||||
elif parent.is_a("IfcCostItem"):
|
||||
values = list(parent.CostValues)
|
||||
values.remove(cost_value)
|
||||
parent.CostValues = values if values else None
|
||||
elif parent.is_a("IfcConstructionResource"):
|
||||
values = list(parent.BaseCosts)
|
||||
values.remove(cost_value)
|
||||
parent.BaseCosts = values if values else None
|
||||
elif parent.is_a("IfcCostValue"):
|
||||
components = list(parent.Components)
|
||||
components.remove(cost_value)
|
||||
parent.Components = components if components else None
|
||||
@@ -0,0 +1,106 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.api.control
|
||||
|
||||
|
||||
def unassign_cost_item_quantity(
|
||||
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]
|
||||
) -> 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
|
||||
:param products: A list of IfcProducts that may have parametrically
|
||||
connected quantities to the cost item
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
schedule = ifcopenshell.api.cost.add_cost_schedule(model)
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
|
||||
# Let's imagine a unit cost of 5.0 per unit volume
|
||||
value = ifcopenshell.api.cost.add_cost_value(model, parent=item)
|
||||
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
|
||||
attributes={"AppliedValue": 5.0})
|
||||
|
||||
slab = ifcopenshell.api.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.pset.add_qto(model, product=slab, name="Qto_SlabBaseQuantities")
|
||||
ifcopenshell.api.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.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.cost.unassign_cost_item_quantity(model,
|
||||
cost_item=item, products=[slab])
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(cost_item, products or [])
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(self, cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]) -> None:
|
||||
quantities = set(cost_item.CostQuantities or [])
|
||||
for quantity in cost_item.CostQuantities or []:
|
||||
for inverse in self.file.get_inverse(quantity):
|
||||
if not inverse.is_a("IfcElementQuantity"):
|
||||
continue
|
||||
for rel in inverse.DefinesOccurrence or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
if related_object in products:
|
||||
quantities.remove(quantity)
|
||||
cost_item.CostQuantities = list(quantities)
|
||||
for product in products:
|
||||
ifcopenshell.api.control.unassign_control(
|
||||
self.file,
|
||||
related_objects=[product],
|
||||
relating_control=cost_item,
|
||||
)
|
||||
self.update_cost_item_count(cost_item)
|
||||
|
||||
def update_cost_item_count(self, cost_item: ifcopenshell.entity_instance) -> None:
|
||||
# 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 len(cost_item.CostQuantities) == 1:
|
||||
quantity = cost_item.CostQuantities[0]
|
||||
if quantity.is_a("IfcQuantityCount"):
|
||||
count = 0
|
||||
for rel in cost_item.Controls:
|
||||
count += len(rel.RelatedObjects)
|
||||
if count:
|
||||
quantity[3] = count
|
||||
else:
|
||||
self.file.remove(quantity)
|
||||
Reference in New Issue
Block a user