diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2bd338ee7..ec2d62b411 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,9 @@ jobs: - name: Build ifcopenshell run: | + echo $Python3_ROOT_DIR + echo ${{ env.pythonLocation }} + mkdir build && cd build cmake \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ @@ -83,7 +86,9 @@ jobs: -DCMAKE_SYSTEM_PREFIX_PATH=/usr \ -DOCC_INCLUDE_DIR=/usr/include/opencascade \ -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ - -DPython3_ROOT_DIR=$Python3_ROOT_DIR \ + -DPYTHON_EXECUTABLE:FILEPATH=${{ env.pythonLocation }}/bin/python \ + -DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \ + -DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \ -DCOLLADA_SUPPORT=Off \ "-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \ -DGLTF_SUPPORT=On \ diff --git a/src/ifcopenshell-python/ifcopenshell/util/unit.py b/src/ifcopenshell-python/ifcopenshell/util/unit.py index 33ee3d9af3..cc20b88ac2 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/unit.py +++ b/src/ifcopenshell-python/ifcopenshell/util/unit.py @@ -16,9 +16,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . -import ifcopenshell -from math import pi from fractions import Fraction +from math import pi +from typing import Tuple, Iterable, Any + +import ifcopenshell +import ifcopenshell.api prefixes = { "EXA": 1e18, @@ -624,67 +627,75 @@ def format_length( return f"{rounded_val:.{decimal_places}f}" -def get_base_type_name( - content_type: ifcopenshell.ifcopenshell_wrapper.named_type | ifcopenshell.ifcopenshell_wrapper.type_declaration) -> ifcopenshell.ifcopenshell_wrapper.type_declaration | None: +def is_attr_type( + content_type: ifcopenshell.ifcopenshell_wrapper.named_type | ifcopenshell.ifcopenshell_wrapper.type_declaration, + ifc_unit_type_name: str) -> ifcopenshell.ifcopenshell_wrapper.type_declaration | None: cur_decl = content_type while hasattr(cur_decl, "declared_type") is True: cur_decl = cur_decl.declared_type() if hasattr(cur_decl, "name") is False: continue - if cur_decl.name() == "IfcLengthMeasure": + if cur_decl.name() == ifc_unit_type_name: return cur_decl if isinstance(cur_decl, ifcopenshell.ifcopenshell_wrapper.aggregation_type): res = cur_decl.type_of_element() cur_decl = res.declared_type() - if hasattr(cur_decl, "name") and cur_decl.name() == "IfcLengthMeasure": + if hasattr(cur_decl, "name") and cur_decl.name() == ifc_unit_type_name: return cur_decl while hasattr(cur_decl, "declared_type") is True: cur_decl = cur_decl.declared_type() if hasattr(cur_decl, "name") is False: continue - if cur_decl.name() == "IfcLengthMeasure": + if cur_decl.name() == ifc_unit_type_name: return cur_decl return None +def iter_element_and_attributes_per_type(ifc_file: ifcopenshell.file, attr_type_name: str) -> Iterable[ + Tuple[ifcopenshell.entity_instance, ifcopenshell.ifcopenshell_wrapper.attribute, Any, str]]: + schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema) + + for element in ifc_file: + entity = schema.declaration_by_name(element.is_a()) + attrs = entity.all_attributes() + for i, (attr, val, is_derived) in enumerate(zip(attrs, list(element), entity.derived())): + if is_derived: + continue + + # Get all methods and attributes of the element + attr_type = attr.type_of_attribute() + base_type = is_attr_type(attr_type, attr_type_name) + if base_type is None: + continue + + if val is None: + continue + + yield element, attr, val + + def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str) -> ifcopenshell.file: """Converts all units in an IFC file to the specified target units. Returns a new file.""" prefix = "MILLI" if target_units == "MILLIMETERS" else None - file_patched = ifcopenshell.api.run("project.create_file", version=ifc_file.schema) # Copy all elements from the original file to the patched file - for el in ifc_file: - file_patched.add(el) + file_patched = ifcopenshell.file.from_string(ifc_file.wrapped_data.to_string()) unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file_patched) old_length = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == "LENGTHUNIT"][0] new_length = ifcopenshell.api.run("unit.add_si_unit", file_patched, unit_type="LENGTHUNIT", prefix=prefix) - schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema) # Traverse all elements and their nested attributes in the file and convert them - for element in file_patched: - entity = schema.declaration_by_name(element.is_a()) - attrs = entity.all_attributes() - for i, (attr, val, is_derived) in enumerate(zip(attrs, list(element), entity.derived())): - if is_derived: - continue - # Get all methods and attributes of the element - attr_type = attr.type_of_attribute() - base_type = get_base_type_name(attr_type) - if base_type is None: - continue - if val is None: - continue - if isinstance(val, tuple): - new_el = [ifcopenshell.util.unit.convert_unit(v, old_length, new_length) for v in val] - setattr(element, attr.name(), tuple(new_el)) - else: - new_el = ifcopenshell.util.unit.convert_unit(val, old_length, new_length) - # set the new value - setattr(element, attr.name(), new_el) + for element, attr, val in iter_element_and_attributes_per_type(file_patched, "IfcLengthMeasure"): + if isinstance(val, tuple): + new_value = [ifcopenshell.util.unit.convert_unit(v, old_length, new_length) for v in val] + setattr(element, attr.name(), tuple(new_value)) + else: + new_value = ifcopenshell.util.unit.convert_unit(val, old_length, new_length) + setattr(element, attr.name(), new_value) file_patched.remove(old_length) unit_assignment.Units = tuple([new_length, *unit_assignment.Units]) diff --git a/src/ifcopenshell-python/test/fixtures/units/beam-standard-case.ifc b/src/ifcopenshell-python/test/fixtures/units/beam-standard-case.ifc new file mode 100644 index 0000000000..0f506b45a4 --- /dev/null +++ b/src/ifcopenshell-python/test/fixtures/units/beam-standard-case.ifc @@ -0,0 +1,426 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION((''),'2;1'); +FILE_NAME('','2019-03-20T15:56:44',(''),(''),'BuildingSmart IfcKit by Constructivity','IfcDoc 12.0.0.0',''); +FILE_SCHEMA(('IFC4x1')); +ENDSEC; + +DATA; + +#1= IFCBEAMTYPE('0juf4qyggSstrxA20Qwnsj',$,'IPE220','Beam type',$,$,$,$,$,.BEAM.); + +#2= IFCRELDECLARES('3P3zL0KYv4C9D9h3OX$dey',$,$,$,#5,(#1,#6)); + +#3= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Q49sj',$,$,$,(#1),#7); +#7= IFCMATERIALPROFILESET($,$,(#8),$); +#8= IFCMATERIALPROFILE('IPE220',$,#9,#10,$,$); +#9= IFCMATERIAL('S275J2',$,'Steel'); +#10= IFCISHAPEPROFILEDEF(.AREA.,'IPE220',$,110.,220.,5.9,9.2,12.,$,$); + +#4= IFCRELDEFINESBYTYPE('2aq$Crcs_xJvd69lbm2bMM',$,'beam typing',$,(#11,#12,#13,#14,#15,#16,#17,#18,#19),#1); + +#5= IFCPROJECT('32DJhIf6esIeAOIlD4Xw2m',#20,'Test model for beam cardinal points',$,$,$,$,(#21),#22); +#20= IFCOWNERHISTORY(#24,#25,$,.NOTDEFINED.,$,$,$,1320688800); +#21= IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.0E-05,#26,$); +#22= IFCUNITASSIGNMENT((#63,#64,#65,#66,#67,#68,#69,#70)); +#24= IFCPERSONANDORGANIZATION(#71,#72,$); +#25= IFCAPPLICATION(#72,'Unknown','SDS/2 Version 6.300 on NT','Unknown'); +#26= IFCAXIS2PLACEMENT3D(#73,#74,#75); +#27= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#76)); +#28= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#78)); +#29= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#79)); +#30= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#81)); +#31= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#82)); +#32= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#84)); +#33= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#85)); +#34= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#87)); +#35= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#88)); +#36= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#90)); +#37= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#91)); +#38= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#93)); +#39= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#94)); +#40= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#96)); +#41= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#97)); +#42= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#99)); +#43= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#100)); +#44= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#102)); +#45= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#103)); +#46= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#105)); +#47= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#106)); +#48= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#108)); +#49= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#109)); +#50= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#111)); +#51= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#112)); +#52= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#114)); +#53= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#115)); +#54= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#117)); +#55= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#118)); +#56= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#120)); +#57= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#121)); +#58= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#123)); +#59= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#124)); +#60= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#126)); +#61= IFCSHAPEREPRESENTATION(#21,'Body','SweptSolid',(#127)); +#62= IFCSHAPEREPRESENTATION(#21,'Axis','Curve3D',(#129)); +#63= IFCSIUNIT($,.LENGTHUNIT.,.MILLI.,.METRE.); +#64= IFCSIUNIT($,.PLANEANGLEUNIT.,$,.RADIAN.); +#65= IFCSIUNIT($,.MASSUNIT.,.KILO.,.GRAM.); +#66= IFCSIUNIT($,.TIMEUNIT.,$,.SECOND.); +#67= IFCSIUNIT($,.AREAUNIT.,$,.SQUARE_METRE.); +#68= IFCSIUNIT($,.PRESSUREUNIT.,$,.PASCAL.); +#69= IFCSIUNIT($,.FORCEUNIT.,$,.NEWTON.); +#70= IFCSIUNIT($,.THERMODYNAMICTEMPERATUREUNIT.,$,.DEGREE_CELSIUS.); +#71= IFCPERSON($,'Liebich',$,$,$,$,$,$); +#72= IFCORGANIZATION($,'buildingSMART',$,$,$); +#73= IFCCARTESIANPOINT((0.,0.,0.)); +#74= IFCDIRECTION((0.,0.,1.)); +#75= IFCDIRECTION((1.,0.,0.)); +#76= IFCEXTRUDEDAREASOLID(#10,#130,#131,2000.); +#77= IFCPRODUCTDEFINITIONSHAPE($,$,(#28,#27)); +#78= IFCPOLYLINE((#132,#133)); +#79= IFCEXTRUDEDAREASOLID(#10,#134,#135,2000.); +#80= IFCPRODUCTDEFINITIONSHAPE($,$,(#30,#29)); +#81= IFCPOLYLINE((#136,#137)); +#82= IFCEXTRUDEDAREASOLID(#10,#138,#139,2000.); +#83= IFCPRODUCTDEFINITIONSHAPE($,$,(#32,#31)); +#84= IFCPOLYLINE((#140,#141)); +#85= IFCEXTRUDEDAREASOLID(#10,#142,#143,2000.); +#86= IFCPRODUCTDEFINITIONSHAPE($,$,(#34,#33)); +#87= IFCPOLYLINE((#144,#145)); +#88= IFCEXTRUDEDAREASOLID(#10,#146,#147,2000.); +#89= IFCPRODUCTDEFINITIONSHAPE($,$,(#36,#35)); +#90= IFCPOLYLINE((#148,#149)); +#91= IFCEXTRUDEDAREASOLID(#10,#150,#151,2000.); +#92= IFCPRODUCTDEFINITIONSHAPE($,$,(#38,#37)); +#93= IFCPOLYLINE((#152,#153)); +#94= IFCEXTRUDEDAREASOLID(#10,#154,#155,2000.); +#95= IFCPRODUCTDEFINITIONSHAPE($,$,(#40,#39)); +#96= IFCPOLYLINE((#156,#157)); +#97= IFCEXTRUDEDAREASOLID(#10,#158,#159,2000.); +#98= IFCPRODUCTDEFINITIONSHAPE($,$,(#42,#41)); +#99= IFCPOLYLINE((#160,#161)); +#100= IFCEXTRUDEDAREASOLID(#10,#162,#163,2000.); +#101= IFCPRODUCTDEFINITIONSHAPE($,$,(#44,#43)); +#102= IFCPOLYLINE((#164,#165)); +#103= IFCEXTRUDEDAREASOLID(#166,#167,#168,3000.); +#104= IFCPRODUCTDEFINITIONSHAPE($,$,(#46,#45)); +#105= IFCPOLYLINE((#170,#171)); +#106= IFCEXTRUDEDAREASOLID(#166,#172,#173,3000.); +#107= IFCPRODUCTDEFINITIONSHAPE($,$,(#48,#47)); +#108= IFCPOLYLINE((#175,#176)); +#109= IFCEXTRUDEDAREASOLID(#166,#177,#178,3000.); +#110= IFCPRODUCTDEFINITIONSHAPE($,$,(#50,#49)); +#111= IFCPOLYLINE((#180,#181)); +#112= IFCEXTRUDEDAREASOLID(#166,#182,#183,3000.); +#113= IFCPRODUCTDEFINITIONSHAPE($,$,(#52,#51)); +#114= IFCPOLYLINE((#185,#186)); +#115= IFCEXTRUDEDAREASOLID(#166,#187,#188,3000.); +#116= IFCPRODUCTDEFINITIONSHAPE($,$,(#54,#53)); +#117= IFCPOLYLINE((#190,#191)); +#118= IFCEXTRUDEDAREASOLID(#166,#192,#193,3000.); +#119= IFCPRODUCTDEFINITIONSHAPE($,$,(#56,#55)); +#120= IFCPOLYLINE((#195,#196)); +#121= IFCEXTRUDEDAREASOLID(#166,#197,#198,3000.); +#122= IFCPRODUCTDEFINITIONSHAPE($,$,(#58,#57)); +#123= IFCPOLYLINE((#200,#201)); +#124= IFCEXTRUDEDAREASOLID(#166,#202,#203,3000.); +#125= IFCPRODUCTDEFINITIONSHAPE($,$,(#60,#59)); +#126= IFCPOLYLINE((#205,#206)); +#127= IFCEXTRUDEDAREASOLID(#166,#207,#208,3000.); +#128= IFCPRODUCTDEFINITIONSHAPE($,$,(#62,#61)); +#129= IFCPOLYLINE((#210,#211)); +#130= IFCAXIS2PLACEMENT3D(#212,$,$); +#131= IFCDIRECTION((0.,0.,1.)); +#132= IFCCARTESIANPOINT((0.,0.,0.)); +#133= IFCCARTESIANPOINT((0.,0.,2000.)); +#134= IFCAXIS2PLACEMENT3D(#213,$,$); +#135= IFCDIRECTION((0.,0.,1.)); +#136= IFCCARTESIANPOINT((0.,0.,0.)); +#137= IFCCARTESIANPOINT((0.,0.,2000.)); +#138= IFCAXIS2PLACEMENT3D(#214,$,$); +#139= IFCDIRECTION((0.,0.,1.)); +#140= IFCCARTESIANPOINT((0.,0.,0.)); +#141= IFCCARTESIANPOINT((0.,0.,2000.)); +#142= IFCAXIS2PLACEMENT3D(#215,$,$); +#143= IFCDIRECTION((0.,0.,1.)); +#144= IFCCARTESIANPOINT((0.,0.,0.)); +#145= IFCCARTESIANPOINT((0.,0.,2000.)); +#146= IFCAXIS2PLACEMENT3D(#216,$,$); +#147= IFCDIRECTION((0.,0.,1.)); +#148= IFCCARTESIANPOINT((0.,0.,0.)); +#149= IFCCARTESIANPOINT((0.,0.,2000.)); +#150= IFCAXIS2PLACEMENT3D(#217,$,$); +#151= IFCDIRECTION((0.,0.,1.)); +#152= IFCCARTESIANPOINT((0.,0.,0.)); +#153= IFCCARTESIANPOINT((0.,0.,2000.)); +#154= IFCAXIS2PLACEMENT3D(#218,$,$); +#155= IFCDIRECTION((0.,0.,1.)); +#156= IFCCARTESIANPOINT((0.,0.,0.)); +#157= IFCCARTESIANPOINT((0.,0.,2000.)); +#158= IFCAXIS2PLACEMENT3D(#219,$,$); +#159= IFCDIRECTION((0.,0.,1.)); +#160= IFCCARTESIANPOINT((0.,0.,0.)); +#161= IFCCARTESIANPOINT((0.,0.,2000.)); +#162= IFCAXIS2PLACEMENT3D(#220,$,$); +#163= IFCDIRECTION((0.,0.,1.)); +#164= IFCCARTESIANPOINT((0.,0.,0.)); +#165= IFCCARTESIANPOINT((0.,0.,2000.)); +#166= IFCTSHAPEPROFILEDEF(.AREA.,'1/2IPE300',$,150.,150.,7.1,10.7,15.,$,$,$,$); +#167= IFCAXIS2PLACEMENT3D(#221,$,$); +#168= IFCDIRECTION((0.,0.,1.)); +#170= IFCCARTESIANPOINT((0.,0.,0.)); +#171= IFCCARTESIANPOINT((0.,0.,3000.)); +#172= IFCAXIS2PLACEMENT3D(#222,$,$); +#173= IFCDIRECTION((0.,0.,1.)); +#175= IFCCARTESIANPOINT((0.,0.,0.)); +#176= IFCCARTESIANPOINT((0.,0.,3000.)); +#177= IFCAXIS2PLACEMENT3D(#223,$,$); +#178= IFCDIRECTION((0.,0.,1.)); +#180= IFCCARTESIANPOINT((0.,0.,0.)); +#181= IFCCARTESIANPOINT((0.,0.,3000.)); +#182= IFCAXIS2PLACEMENT3D(#224,$,$); +#183= IFCDIRECTION((0.,0.,1.)); +#185= IFCCARTESIANPOINT((0.,0.,0.)); +#186= IFCCARTESIANPOINT((0.,0.,3000.)); +#187= IFCAXIS2PLACEMENT3D(#225,$,$); +#188= IFCDIRECTION((0.,0.,1.)); +#190= IFCCARTESIANPOINT((0.,0.,0.)); +#191= IFCCARTESIANPOINT((0.,0.,3000.)); +#192= IFCAXIS2PLACEMENT3D(#226,$,$); +#193= IFCDIRECTION((0.,0.,1.)); +#195= IFCCARTESIANPOINT((0.,0.,0.)); +#196= IFCCARTESIANPOINT((0.,0.,3000.)); +#197= IFCAXIS2PLACEMENT3D(#227,$,$); +#198= IFCDIRECTION((0.,0.,1.)); +#200= IFCCARTESIANPOINT((0.,0.,0.)); +#201= IFCCARTESIANPOINT((0.,0.,3000.)); +#202= IFCAXIS2PLACEMENT3D(#228,$,$); +#203= IFCDIRECTION((0.,0.,1.)); +#205= IFCCARTESIANPOINT((0.,0.,0.)); +#206= IFCCARTESIANPOINT((0.,0.,3000.)); +#207= IFCAXIS2PLACEMENT3D(#229,$,$); +#208= IFCDIRECTION((0.,0.,1.)); +#210= IFCCARTESIANPOINT((0.,0.,0.)); +#211= IFCCARTESIANPOINT((0.,0.,3000.)); +#212= IFCCARTESIANPOINT((-55.,110.,0.)); +#213= IFCCARTESIANPOINT((0.,110.,0.)); +#214= IFCCARTESIANPOINT((55.,110.,0.)); +#215= IFCCARTESIANPOINT((-55.,0.,0.)); +#216= IFCCARTESIANPOINT((0.,0.,0.)); +#217= IFCCARTESIANPOINT((55.,0.,0.)); +#218= IFCCARTESIANPOINT((-55.,-110.,0.)); +#219= IFCCARTESIANPOINT((0.,-110.,0.)); +#220= IFCCARTESIANPOINT((55.,-110.,0.)); +#221= IFCCARTESIANPOINT((-75.,75.,0.)); +#222= IFCCARTESIANPOINT((0.,75.,0.)); +#223= IFCCARTESIANPOINT((75.,75.,0.)); +#224= IFCCARTESIANPOINT((-75.,0.,0.)); +#225= IFCCARTESIANPOINT((0.,0.,0.)); +#226= IFCCARTESIANPOINT((75.,0.,0.)); +#227= IFCCARTESIANPOINT((-75.,-75.,0.)); +#228= IFCCARTESIANPOINT((0.,-75.,0.)); +#229= IFCCARTESIANPOINT((75.,-75.,0.)); + +#6= IFCBEAMTYPE('0juf4qyggSstrxA20Qdisj',$,'1/2IPE300','Beam type',$,$,$,$,$,.BEAM.); + +#11= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20Qwnsj',$,'A-1','IPE220','Beam',#232,#77,'A-1',$); +#232= IFCLOCALPLACEMENT(#235,#236); +#235= IFCLOCALPLACEMENT(#237,#26); +#236= IFCAXIS2PLACEMENT3D(#256,#257,#258); +#237= IFCLOCALPLACEMENT($,#26); +#239= IFCLOCALPLACEMENT(#235,#260); +#240= IFCLOCALPLACEMENT(#235,#261); +#241= IFCLOCALPLACEMENT(#235,#262); +#242= IFCLOCALPLACEMENT(#235,#263); +#243= IFCLOCALPLACEMENT(#235,#264); +#244= IFCLOCALPLACEMENT(#235,#265); +#245= IFCLOCALPLACEMENT(#235,#266); +#246= IFCLOCALPLACEMENT(#235,#267); +#247= IFCLOCALPLACEMENT(#235,#268); +#248= IFCLOCALPLACEMENT(#235,#269); +#249= IFCLOCALPLACEMENT(#235,#270); +#250= IFCLOCALPLACEMENT(#235,#271); +#251= IFCLOCALPLACEMENT(#235,#272); +#252= IFCLOCALPLACEMENT(#235,#273); +#253= IFCLOCALPLACEMENT(#235,#274); +#254= IFCLOCALPLACEMENT(#235,#275); +#255= IFCLOCALPLACEMENT(#235,#276); +#256= IFCCARTESIANPOINT((0.,0.,0.)); +#257= IFCDIRECTION((1.,0.,0.)); +#258= IFCDIRECTION((0.,1.,0.)); +#260= IFCAXIS2PLACEMENT3D(#277,#278,#279); +#261= IFCAXIS2PLACEMENT3D(#280,#281,#282); +#262= IFCAXIS2PLACEMENT3D(#283,#284,#285); +#263= IFCAXIS2PLACEMENT3D(#286,#287,#288); +#264= IFCAXIS2PLACEMENT3D(#289,#290,#291); +#265= IFCAXIS2PLACEMENT3D(#292,#293,#294); +#266= IFCAXIS2PLACEMENT3D(#295,#296,#297); +#267= IFCAXIS2PLACEMENT3D(#298,#299,#300); +#268= IFCAXIS2PLACEMENT3D(#301,#302,#303); +#269= IFCAXIS2PLACEMENT3D(#304,#305,#306); +#270= IFCAXIS2PLACEMENT3D(#307,#308,#309); +#271= IFCAXIS2PLACEMENT3D(#310,#311,#312); +#272= IFCAXIS2PLACEMENT3D(#313,#314,#315); +#273= IFCAXIS2PLACEMENT3D(#316,#317,#318); +#274= IFCAXIS2PLACEMENT3D(#319,#320,#321); +#275= IFCAXIS2PLACEMENT3D(#322,#323,#324); +#276= IFCAXIS2PLACEMENT3D(#325,#326,#327); +#277= IFCCARTESIANPOINT((0.,1500.,0.)); +#278= IFCDIRECTION((1.,0.,0.)); +#279= IFCDIRECTION((0.,1.,0.)); +#280= IFCCARTESIANPOINT((0.,3000.,0.)); +#281= IFCDIRECTION((1.,0.,0.)); +#282= IFCDIRECTION((0.,1.,0.)); +#283= IFCCARTESIANPOINT((0.,4500.,0.)); +#284= IFCDIRECTION((1.,0.,0.)); +#285= IFCDIRECTION((0.,1.,0.)); +#286= IFCCARTESIANPOINT((0.,6000.,0.)); +#287= IFCDIRECTION((1.,0.,0.)); +#288= IFCDIRECTION((0.,1.,0.)); +#289= IFCCARTESIANPOINT((0.,7500.,0.)); +#290= IFCDIRECTION((1.,0.,0.)); +#291= IFCDIRECTION((0.,1.,0.)); +#292= IFCCARTESIANPOINT((0.,9000.,0.)); +#293= IFCDIRECTION((1.,0.,0.)); +#294= IFCDIRECTION((0.,1.,0.)); +#295= IFCCARTESIANPOINT((0.,10500.,0.)); +#296= IFCDIRECTION((1.,0.,0.)); +#297= IFCDIRECTION((0.,1.,0.)); +#298= IFCCARTESIANPOINT((0.,12000.,0.)); +#299= IFCDIRECTION((1.,0.,0.)); +#300= IFCDIRECTION((0.,1.,0.)); +#301= IFCCARTESIANPOINT((0.,0.,1500.)); +#302= IFCDIRECTION((0.98,0.081,0.182)); +#303= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#304= IFCCARTESIANPOINT((0.,1500.,1500.)); +#305= IFCDIRECTION((0.98,0.081,0.182)); +#306= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#307= IFCCARTESIANPOINT((0.,3000.,1500.)); +#308= IFCDIRECTION((0.98,0.081,0.182)); +#309= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#310= IFCCARTESIANPOINT((0.,4500.,1500.)); +#311= IFCDIRECTION((0.98,0.081,0.182)); +#312= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#313= IFCCARTESIANPOINT((0.,6000.,1500.)); +#314= IFCDIRECTION((0.98,0.081,0.182)); +#315= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#316= IFCCARTESIANPOINT((0.,7500.,1500.)); +#317= IFCDIRECTION((0.98,0.081,0.182)); +#318= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#319= IFCCARTESIANPOINT((0.,9000.,1500.)); +#320= IFCDIRECTION((0.98,0.081,0.182)); +#321= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#322= IFCCARTESIANPOINT((0.,10500.,1500.)); +#323= IFCDIRECTION((0.98,0.081,0.182)); +#324= IFCDIRECTION((-0.0001,0.9138,-0.40616)); +#325= IFCCARTESIANPOINT((0.,12000.,1500.)); +#326= IFCDIRECTION((0.98,0.081,0.182)); +#327= IFCDIRECTION((-0.0001,0.9138,-0.40616)); + +#12= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20sznsj',$,'A-2','IPE220','Beam',#239,#80,'A-2',$); + +#13= IFCBEAMSTANDARDCASE('0juf4qyggSI8s4A20sznsj',$,'A-3','IPE220','Beam',#240,#83,'A-3',$); + +#14= IFCBEAMSTANDARDCASE('0juf4qyggSI8s4A20sznw6',$,'A-4','IPE220','Beam',#241,#86,'A-4',$); + +#15= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20Qwnab',$,'A-5','IPE220','Beam',#242,#89,'A-5',$); + +#16= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20Qwng1',$,'A-6','IPE220','Beam',#243,#92,'A-6',$); + +#17= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20Qwn3s',$,'A-7','IPE220','Beam',#244,#95,'A-7',$); + +#18= IFCBEAMSTANDARDCASE('0juf4qyggSI8rxA20Qwnlq',$,'A-8','IPE220','Beam',#245,#98,'A-8',$); + +#19= IFCBEAMSTANDARDCASE('0juf4qyggSI8s4A20sznrt',$,'A-9','IPE220','Beam',#246,#101,'A-9',$); + +#23= IFCRELAGGREGATES('0ZdtoJM$VsHBqD_feRfxAg',$,$,$,#5,(#259)); + +#169= IFCBEAMSTANDARDCASE('3QbcAsYoB7Hvx$4VHzijYi',$,'B-1','1/2IPE300','Beam',#247,#104,'B-1',$); + +#174= IFCBEAMSTANDARDCASE('3Qb5gsYoB7Hvx$4VHzijYi',$,'B-2','1/2IPE300','Beam',#248,#107,'B-2',$); + +#179= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzijYi',$,'B-3','1/2IPE300','Beam',#249,#110,'B-3',$); + +#184= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzijGT',$,'B-4','1/2IPE300','Beam',#250,#113,'B-4',$); + +#189= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzijdF',$,'B-5','1/2IPE300','Beam',#251,#116,'B-5',$); + +#194= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzij3V',$,'B-6','1/2IPE300','Beam',#252,#119,'B-6',$); + +#199= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzij71',$,'B-7','1/2IPE300','Beam',#253,#122,'B-7',$); + +#204= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzijvb',$,'B-8','1/2IPE300','Beam',#254,#125,'B-8',$); + +#209= IFCBEAMSTANDARDCASE('3QbcAsYsg7Hvx$4VHzijp1',$,'B-9','1/2IPE300','Beam',#255,#128,'B-9',$); + +#230= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Q2fsj',$,$,$,(#6),#345); +#345= IFCMATERIALPROFILESET($,$,(#346),$); +#346= IFCMATERIALPROFILE('1/2IPE300',$,#347,#166,$,$); +#347= IFCMATERIAL('S275J2',$,'Steel'); + +#231= IFCRELDEFINESBYTYPE('2aq$Crcs_xJvN69lbm2bMM',$,'beam typing',$,(#169,#174,#179,#184,#189,#194,#199,#204,#209),#6); + +#233= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QfZsj',$,$,$,(#11),#348); +#348= IFCMATERIALPROFILESETUSAGE(#7,1,$); + +#234= IFCRELCONTAINEDINSPATIALSTRUCTURE('2aq$Crcs_xJvtg9lbm2bMM',$,'Physical model',$,(#11,#12,#13,#14,#15,#16,#17,#18,#19,#169,#174,#179,#184,#189,#194,#199,#204,#209),#238); + +#238= IFCBUILDING('0Xwup04AK2G8Mt0WNZVy_Z',$,'Building',$,$,#235,$,$,.ELEMENT.,$,$,$); + +#259= IFCSITE('10cTefjFQoJexBQrSqFcWZ',$,'Site',$,$,#237,$,$,.ELEMENT.,$,$,$,$,$); + +#328= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QgAsj',$,$,$,(#12),#350); +#350= IFCMATERIALPROFILESETUSAGE(#7,2,$); + +#329= IFCRELASSOCIATESMATERIAL('3x0gFSPAr5puQ5WI22xYOm',$,$,$,(#13),#351); +#351= IFCMATERIALPROFILESETUSAGE(#7,3,$); + +#330= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QOHsj',$,$,$,(#14),#352); +#352= IFCMATERIALPROFILESETUSAGE(#7,4,$); + +#331= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QRLsj',$,$,$,(#15),#353); +#353= IFCMATERIALPROFILESETUSAGE(#7,5,$); + +#332= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QMmsj',$,$,$,(#16),#354); +#354= IFCMATERIALPROFILESETUSAGE(#7,6,$); + +#333= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Qh4sj',$,$,$,(#17),#355); +#355= IFCMATERIALPROFILESETUSAGE(#7,7,$); + +#334= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QwKsj',$,$,$,(#18),#356); +#356= IFCMATERIALPROFILESETUSAGE(#7,8,$); + +#335= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QJksj',$,$,$,(#19),#357); +#357= IFCMATERIALPROFILESETUSAGE(#7,9,$); + +#336= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QbFsj',$,$,$,(#169),#358); +#358= IFCMATERIALPROFILESETUSAGE(#345,1,$); + +#337= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Qqlsj',$,$,$,(#174),#359); +#359= IFCMATERIALPROFILESETUSAGE(#345,2,$); + +#338= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Q7isj',$,$,$,(#179),#360); +#360= IFCMATERIALPROFILESETUSAGE(#345,3,$); + +#339= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Q4nsj',$,$,$,(#184),#361); +#361= IFCMATERIALPROFILESETUSAGE(#345,4,$); + +#340= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Q41sj',$,$,$,(#189),#362); +#362= IFCMATERIALPROFILESETUSAGE(#345,5,$); + +#341= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QURsj',$,$,$,(#194),#363); +#363= IFCMATERIALPROFILESETUSAGE(#345,6,$); + +#342= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Qw9sj',$,$,$,(#199),#364); +#364= IFCMATERIALPROFILESETUSAGE(#345,7,$); + +#343= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20QOcsj',$,$,$,(#204),#365); +#365= IFCMATERIALPROFILESETUSAGE(#345,8,$); + +#344= IFCRELASSOCIATESMATERIAL('0juf4qyggSstrxA20Qw6sj',$,$,$,(#209),#366); +#366= IFCMATERIALPROFILESETUSAGE(#345,9,$); + +#349= IFCRELAGGREGATES('1FUTCayKJbIQqDNnZzXu07',$,$,$,#259,(#238)); +ENDSEC; + +END-ISO-10303-21; diff --git a/src/ifcopenshell-python/test/util/test_unit_conversion.py b/src/ifcopenshell-python/test/util/test_unit_conversion.py new file mode 100644 index 0000000000..af84677647 --- /dev/null +++ b/src/ifcopenshell-python/test/util/test_unit_conversion.py @@ -0,0 +1,49 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2023 Dion Moult +# +# 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 . +import pathlib + +import pytest + +import ifcopenshell.util.unit + +UNITS_FIXTURE_DIR = pathlib.Path(__file__).parent.parent / "fixtures" / "units" + + +@pytest.mark.parametrize("ifc_file", UNITS_FIXTURE_DIR.glob("*.ifc")) +def test_file_units_length_convert(ifc_file): + f = ifcopenshell.open(ifc_file) + project_unit = ifcopenshell.util.unit.get_project_unit(f, "LENGTHUNIT") + if project_unit.Prefix == "MILLI": + target_units = "METERS" + scale = 0.001 + else: + target_units = "MILLIMETERS" + scale = 1000 + + new_f = ifcopenshell.util.unit.convert_file_length_units(f, target_units) + + for element, attr, val in ifcopenshell.util.unit.iter_element_and_attributes_per_type(new_f, "IfcLengthMeasure"): + elem_id = element.id() + original_element = f.by_id(elem_id) + original_val = getattr(original_element, attr.name()) + if isinstance(original_val, tuple): + # assert element is equal to original element times scale + assert val == tuple([v * scale for v in original_val]) + else: + # assert element is equal to original element times scale + assert val == original_val * scale