Skip to content

Commit 7deed02

Browse files
Merge pull request #868 from BIM2SIM/development
update joss with current dev branch
2 parents aa9ffa2 + 2072d22 commit 7deed02

File tree

237 files changed

+1259586
-2086
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

237 files changed

+1259586
-2086
lines changed

.gitlab-ci.yml

Lines changed: 51 additions & 617 deletions
Large diffs are not rendered by default.

.gitmodules

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
[submodule "test/resources"]
22
path = test/resources
33
url = https://github.com/BIM2SIM/bim2sim-test-resources.git
4-
branch = main
4+
branch = main
5+
[submodule "butterfly"]
6+
path = butterfly
7+
url = https://github.com/BIM2SIM/butterfly.git
8+
branch = master

Dockerfile

Lines changed: 0 additions & 27 deletions
This file was deleted.

EnergyPlus.Dockerfile

Lines changed: 0 additions & 39 deletions
This file was deleted.

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# <img src='docs/source/img/static/b2s_logo.png' width='200'> bim2sim
1+
# <img src='https://raw.githubusercontent.com/BIM2SIM/bim2sim/development/docs/source/img/static/b2s_logo.png' width='200'> bim2sim
22
[![pylint](https://bim2sim.github.io/bim2sim/development/pylint/pylint.svg)](https://bim2sim.github.io/bim2sim/development/pylint/pylint.html)
33
[![documentation](https://bim2sim.github.io/bim2sim/development/docs/doc.svg)](https://bim2sim.github.io/bim2sim/development/docs/index.html)
44
[![coverage (W/O Plugins)](https://bim2sim.github.io/bim2sim/development/coverage/badge.svg)](https://bim2sim.github.io/bim2sim/development/coverage)
@@ -12,7 +12,7 @@ bim2sim is a library to create simulation models for different purposes based on
1212
* Life Cycle Assessment (LCA)
1313

1414
The focus of the currently released tool is on BPS and HVAC but we already provide basic methods for CFD and LCA as well. The base structure is shown below:
15-
![Toolchain](docs/source/img/static/bim2sim_framework_overview.png)
15+
![Toolchain](https://raw.githubusercontent.com/BIM2SIM/bim2sim/development/docs/source/img/static/bim2sim_framework_overview.png)
1616

1717
## Installation and Usage
1818
bim2sim requires Python >= 3.10 and < 3.12. You can find detailed documentation and description how to install and to use in our [documentation](https://bim2sim.github.io/bim2sim//development/docs/index.html). We recommend reading at least:
@@ -36,6 +36,7 @@ bim2sim is developed and released by the following three partners under the [LGP
3636
* [Institute for Energy Efficient Buildings and Indoor Climate (RWTH Aachen University)](https://www.ebc.eonerc.rwth-aachen.de/cms/~dmzz/E-ON-ERC-EBC/)
3737
* [E3D - Institute of Energy Efficiency and Sustainable Building (RWTH Aachen University)](https://www.e3d.rwth-aachen.de/cms/~iyld/E3D/?lidx=1)
3838
* [ROM Technik GmbH](https://www.rom-technik.de/home/)
39+
3940
![all_partners](https://user-images.githubusercontent.com/27726960/211298128-09799889-774a-49a5-a7c4-9c9163613990.png)
4041

4142

@@ -46,4 +47,4 @@ bim2sim is still under heavy development and you can find a lot of features that
4647
* [ ] implementation of curtain walls for BPS part
4748
* [ ] support of AHU and ventilation simulations with Modelica
4849
* [ ] automated integration of weather files based of location of building
49-
* [ ] ... have a look at the [https://github.com/BIM2SIM/bim2sim/issues](issues)
50+
* [ ] ... have a look at the [issues](https://github.com/BIM2SIM/bim2sim/issues)

bim2sim/elements/base_elements.py

Lines changed: 121 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
logger = logging.getLogger(__name__)
2323
quality_logger = logging.getLogger('bim2sim.QualityReport')
24-
settings_products = ifcopenshell.geom.main.settings()
24+
settings_products = ifcopenshell.geom.settings()
2525
settings_products.set(settings_products.USE_PYTHON_OPENCASCADE, True)
26+
settings_products.set(settings_products.USE_WORLD_COORDS, True)
27+
settings_products.set(settings_products.PRECISION, 1e-6)
2628

2729

2830
class ElementError(Exception):
@@ -55,6 +57,7 @@ def __init__(self, guid=None, **kwargs):
5557
self.guid = guid or self.get_id(self.guid_prefix)
5658
# self.related_decisions: List[Decision] = []
5759
self.attributes = attribute.AttributeManager(bind=self)
60+
self.element_type = self.__class__.__name__
5861

5962
# set attributes based on kwargs
6063
for kw, arg in kwargs.items():
@@ -160,9 +163,9 @@ class IFCBased(Element):
160163
'-Something' start with minus to exclude
161164
162165
For example:
163-
{'IfcSlab': ['*', '-SomethingSpecialWeDontWant', 'BASESLAB']}
164-
{'IfcRoof': ['FLAT_ROOF', 'SHED_ROOF',...],
165-
'IfcSlab': ['ROOF']}"""
166+
>>> {'IfcSlab': ['*', '-SomethingSpecialWeDontWant', 'BASESLAB']}
167+
>>> {'IfcRoof': ['FLAT_ROOF', 'SHED_ROOF',...],
168+
>>> 'IfcSlab': ['ROOF']}"""
166169

167170
ifc_types: Dict[str, List[str]] = None
168171
pattern_ifc_type = []
@@ -333,29 +336,47 @@ def filter_properties(self, patterns):
333336
return matches
334337

335338
@classmethod
336-
def filter_for_text_fragments(
337-
cls, ifc_element, ifc_units: dict, optional_locations: list = None):
338-
"""Filter for text fragments in the ifc_element to identify the ifc_element."""
339+
def filter_for_text_fragments(cls, ifc_element, ifc_units: dict,
340+
optional_locations: list = None):
341+
"""Find text fragments that match the class patterns in an IFC element.
342+
343+
Args:
344+
ifc_element: The IFC element to check.
345+
ifc_units: Dictionary containing IFC unit information.
346+
optional_locations: Additional locations to check patterns beyond
347+
name. Defaults to None.
348+
349+
Returns:
350+
list: List of matched fragments, empty list if no matches found.
351+
"""
339352
results = []
340-
hits = [p.search(ifc_element.Name) for p in cls.pattern_ifc_type]
341-
# hits.extend([p.search(ifc_element.Description or '') for p in cls.pattern_ifc_type])
342-
hits = [x for x in hits if x is not None]
343-
if any(hits):
344-
quality_logger.info("Identified %s through text fracments in name. Criteria: %s", cls.ifc_type, hits)
345-
results.append(hits[0][0])
346-
# return hits[0][0]
353+
354+
# Check name matches
355+
name_hits = [p.search(ifc_element.Name) for p in cls.pattern_ifc_type]
356+
name_hits = [hit for hit in name_hits if hit is not None]
357+
if name_hits:
358+
quality_logger.info(
359+
f"Identified {cls.ifc_type} through text fragments in name. "
360+
f"Criteria: {name_hits}")
361+
results.append(name_hits[0][0])
362+
363+
# Check optional locations
347364
if optional_locations:
348365
for loc in optional_locations:
349-
hits = [p.search(ifc2python.get_property_set_by_name(
350-
loc, ifc_element, ifc_units) or '')
351-
for p in cls.pattern_ifc_type
352-
if ifc2python.get_property_set_by_name(
353-
loc, ifc_element, ifc_units)]
354-
hits = [x for x in hits if x is not None]
355-
if any(hits):
356-
quality_logger.info("Identified %s through text fracments in %s. Criteria: %s", cls.ifc_type, loc, hits)
357-
results.append(hits[0][0])
358-
return results if results else ''
366+
prop_value = ifc2python.get_property_set_by_name(
367+
loc, ifc_element, ifc_units)
368+
if not prop_value:
369+
continue
370+
371+
loc_hits = [p.search(prop_value) for p in cls.pattern_ifc_type]
372+
loc_hits = [hit for hit in loc_hits if hit is not None]
373+
if loc_hits:
374+
quality_logger.info(
375+
f"Identified {cls.ifc_type} through text fragments "
376+
f"in {loc}. Criteria: {loc_hits}")
377+
results.append(loc_hits[0][0])
378+
379+
return results
359380

360381
def get_exact_property(self, propertyset_name: str, property_name: str):
361382
"""Returns value of property specified by propertyset name and property name
@@ -526,6 +547,17 @@ def calc_cost_group(self) -> Optional[int]:
526547
"""Calculate the cost group according to DIN276"""
527548
return None
528549

550+
def calc_product_shape(self):
551+
"""Calculate the product shape based on IfcProduct representation."""
552+
if hasattr(self.ifc, 'Representation'):
553+
try:
554+
shape = ifcopenshell.geom.create_shape(
555+
settings_products, self.ifc).geometry
556+
return shape
557+
except:
558+
logger.warning(f"No calculation of product shape possible "
559+
f"for {self.ifc}.")
560+
529561
def calc_volume_from_ifc_shape(self):
530562
# todo use more efficient iterator to calc all shapes at once
531563
# with multiple cores:
@@ -710,9 +742,9 @@ class Factory:
710742
https://refactoring.guru/design-patterns/factory-method/python/example
711743
712744
Example:
713-
factory = Factory([Pipe, Boiler], dummy)
714-
ele = factory(some_ifc_element)
715-
"""
745+
>>> factory = Factory([Pipe, Boiler], dummy)
746+
>>> ele = factory(some_ifc_element)
747+
"""
716748

717749
def __init__(
718750
self,
@@ -882,14 +914,68 @@ def __init__(self, element):
882914
if self.is_picklable(value):
883915
setattr(self, attr_name, value)
884916
else:
885-
logger.info(
886-
f"Attribute {attr_name} will not be serialized, as it's "
887-
f"not pickleable")
888-
if hasattr(element, "space_boundaries"):
889-
self.space_boundaries = [bound.guid for bound in
890-
element.space_boundaries]
891-
if hasattr(element, "storeys"):
892-
self.storeys = [storey.guid for storey in element.storeys]
917+
try:
918+
logger.info(
919+
f"Attribute {attr_name} will not be serialized, as it's "
920+
f"not pickleable, trying to add alternative "
921+
f"information instead.")
922+
if isinstance(value, (list, tuple)):
923+
temp_list = []
924+
for val in value:
925+
if hasattr(val, 'guid'):
926+
temp_list.append(val.guid)
927+
setattr(self, attr_name, temp_list)
928+
logger.info(f"Successfully linked a list of guids.")
929+
elif isinstance(value, str):
930+
setattr(self, attr_name, value)
931+
logger.info(f"Successfully linked value as string.")
932+
elif hasattr(value, 'guid'):
933+
setattr(self, attr_name, value.guid)
934+
logger.info(f"Successfully linked a single guid.")
935+
elif hasattr(value, 'Coord'):
936+
setattr(self, attr_name, value.Coord())
937+
logger.info(f"Successfully linked a coordinate tuple.")
938+
elif value is None:
939+
setattr(self, attr_name, None)
940+
logger.info(f"Successfully set attribute value to "
941+
f"None.")
942+
else:
943+
logger.info("Linking alternative pickleable attributes "
944+
"failed.")
945+
except AttributeError:
946+
logger.info(f"Linking attribute failed.")
947+
for attr_name, attr_val in vars(element).items():
948+
if hasattr(self, attr_name) or attr_name == 'attributes':
949+
continue
950+
else:
951+
logger.info(f"Try to add attribute data for attribute "
952+
f"'{attr_name}' that is not in AttributeManager.")
953+
value = attr_val
954+
if isinstance(value, (list, tuple)):
955+
temp_list = []
956+
for val in value:
957+
if hasattr(val, 'guid'):
958+
temp_list.append(val.guid)
959+
setattr(self, attr_name, temp_list)
960+
logger.info(
961+
f"Successfully linked a list of guids.")
962+
elif isinstance(value, str):
963+
setattr(self, attr_name, value)
964+
logger.info(
965+
f"Successfully added {attr_name} as string.")
966+
elif hasattr(value, 'guid'):
967+
setattr(self, attr_name, value.guid)
968+
logger.info(f"Successfully linked a single guid.")
969+
elif hasattr(value, 'Coord'):
970+
setattr(self, attr_name, value.Coord())
971+
logger.info(f"Successfully linked a coordinate tuple.")
972+
elif value is None:
973+
setattr(self, attr_name, None)
974+
logger.info(f"Successfully set attribute value to "
975+
f"None.")
976+
else:
977+
logger.info("Linking alternative pickleable attributes "
978+
"failed.")
893979
if issubclass(element.__class__, AggregationMixin):
894980
self.elements = [ele.guid for ele in element.elements]
895981

0 commit comments

Comments
 (0)