From f0a6c4f8dd9e02925a20b490f82a022af628c059 Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Wed, 15 Oct 2025 19:54:43 +0100 Subject: [PATCH] Github IFC validation and IDS checking --- .github/workflows/ids-lint.yml | 88 +++++++++++++++++++++++++ .github/workflows/ifc-lint.yml | 32 +++++++++ IDS/EN_Basic IDM Check.ids | 114 +++++++++++++++++++++++++++++++++ IDS/IDS_random_example.ids | 81 +++++++++++++++++++++++ model.ifc | 14 ++-- 5 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ids-lint.yml create mode 100644 .github/workflows/ifc-lint.yml create mode 100644 IDS/EN_Basic IDM Check.ids create mode 100644 IDS/IDS_random_example.ids diff --git a/.github/workflows/ids-lint.yml b/.github/workflows/ids-lint.yml new file mode 100644 index 0000000..2efe753 --- /dev/null +++ b/.github/workflows/ids-lint.yml @@ -0,0 +1,88 @@ +name: IDS Compliance Check + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ids-lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install ifctester and idssplit + run: | + pip install ifctester + pip install --no-deps https://github.com/brunopostle/idssplit/releases/download/0.1.0/idssplit-0.1.0-py3-none-any.whl + + - name: Run IDS validations + run: | + set -e + shopt -s globstar nullglob + + if [ ! -d IDS ]; then + echo "No IDS/ folder found" + exit 0 + fi + + ids_sources=(IDS/**/*.ids) + + if [ ${#ids_sources[@]} -eq 0 ]; then + echo "No IDS files found in IDS/ folder" + exit 0 + fi + + ifc_files=(**/*.ifc) + + if [ ${#ifc_files[@]} -eq 0 ]; then + echo "No IFC files found" + exit 0 + fi + + mkdir -p split_ids + + echo "Splitting IDS files..." + for ids in "${ids_sources[@]}"; do + idssplit "$ids" split_ids/ + done + + split_ids_files=(split_ids/*.ids) + if [ ${#split_ids_files[@]} -eq 0 ]; then + echo "No rules found after splitting IDS files" + exit 1 + fi + + echo "Running ifctester validations..." + failed=0 + + for rule_ids in "${split_ids_files[@]}"; do + for ifc in "${ifc_files[@]}"; do + echo "::group::Test: $rule_ids with $ifc" + echo "Testing: $rule_ids with $ifc" + output=$(python3 -m ifctester --no-color "$rule_ids" "$ifc" || true) + echo "$output" + echo "::endgroup::" + + if echo "$output" | grep -q '\[FAIL\]'; then + echo "FAIL: $rule_ids with $ifc" + failed=1 + else + echo "PASS: $rule_ids with $ifc" + fi + done + done + + if [ "$failed" -ne 0 ]; then + echo "One or more validations failed" + exit 1 + else + echo "All validations passed" + fi diff --git a/.github/workflows/ifc-lint.yml b/.github/workflows/ifc-lint.yml new file mode 100644 index 0000000..d6acac8 --- /dev/null +++ b/.github/workflows/ifc-lint.yml @@ -0,0 +1,32 @@ +name: IFC Validation + +on: + push: + pull_request: + +jobs: + lint-ifc: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install ifcopenshell + run: | + pip install pytest + pip install ifcopenshell + + - name: Run IFC lint checks + run: | + set -e + shopt -s globstar nullglob + for file in **/*.ifc; do + echo "Validating $file..." + python3 -m ifcopenshell.validate --rules "$file" + done diff --git a/IDS/EN_Basic IDM Check.ids b/IDS/EN_Basic IDM Check.ids new file mode 100644 index 0000000..98c8e62 --- /dev/null +++ b/IDS/EN_Basic IDM Check.ids @@ -0,0 +1,114 @@ + + + + EN_Basic IDM Check + Example IDS for model validation according to the BIM Basic Information Delivery Manual (IDM) + sgolchinfar@bimcollab.com + 2024-10-29 + Specify a set of agreements that IFC models must meet according to the basic IDM. + + + + + + + IFCBUILDINGELEMENTPROXY + + + + + + + GlobalId + + + ? + + + + + + + + + Pset_WallCommon + + + IsExternal + + + False + + + + + Pset_WallCommon + + + LoadBearing + + + True + + + + + + + Pset_WallCommon + + + FireRating + + + 30 + + + + + + + + + Pset_WallCommon + + + IsExternal + + + True + + + + + + + Pset_WallCommon + + + Thermal Transmitance + + + + + + + + + IFCFLOWSEGMENT + + + + + + + AC_Pset_RenovationAndPhasing + + + Renovation Status + + + + + + diff --git a/IDS/IDS_random_example.ids b/IDS/IDS_random_example.ids new file mode 100644 index 0000000..24fd04a --- /dev/null +++ b/IDS/IDS_random_example.ids @@ -0,0 +1,81 @@ + + + + random example to show technical capabilities and usage + buildingSMART International Ltd + 2021-09-17 + + + + + + + IFCWINDOW + + + + + + + The value should be a number; a dot; another number. For example '1.1', '2.4', etc. + + + + + + NL-Sfb + + + + + + + attribute + + + OverallWidth + + + + + + + + + + + AedesUVIP + + + FireRating + + + + + + + Thirty minutes + + + + + + + Sixty minutes + + + + + + + Ninety minutes + + + + + + + + + + diff --git a/model.ifc b/model.ifc index 89e1ae6..9875833 100644 --- a/model.ifc +++ b/model.ifc @@ -1,7 +1,7 @@ ISO-10303-21; HEADER; FILE_DESCRIPTION(('ViewDefinition[DesignTransferView]'),'2;1'); -FILE_NAME('model.ifc','2025-02-06T22:16:33+00:00',('',''),('',''),'IfcOpenShell 0.0.0','Bonsai 0.0.0','Nobody'); +FILE_NAME('model.ifc','2025-10-17T10:31:20+01:00',('',''),('',''),'IfcOpenShell 0.0.0','Bonsai 0.0.0','Nobody'); FILE_SCHEMA(('IFC4')); ENDSEC; DATA; @@ -8517,7 +8517,7 @@ DATA; #35959=IFCPOLYGONALFACESET(#35945,$,(#35946,#35947,#35948,#35949,#35950,#35951,#35952,#35953,#35954,#35955,#35956,#35957,#35958),$); #35960=IFCGROUP('3NpCHi67PBu9TmvZGd4Pbn',$,'BBIM_Linked_Aggregate',$,$); #35961=IFCRELASSIGNSTOGROUP('2YO79r$sH2qu3t6d1iy2r4',$,$,$,(#39529,#35843,#38874),$,#35960); -#35962=IFCPROPERTYSET('1pXbg_yYr7KPmJi1htT7g2',$,'BBIM_Linked_Aggregate',$,(#35964)); +#35962=IFCPROPERTYSET('1pXbg_yYr7KPmJi1htT7g2',$,'BBIM_Linked_Aggregate',$,(#35964,#41306,#41307)); #35963=IFCRELDEFINESBYPROPERTIES('1x8EvK_VvDFxpvkkdgg$w_',$,$,$,(#35843),#35962); #35964=IFCPROPERTYSINGLEVALUE('Index',$,IFCINTEGER(0),$); #35965=IFCPROPERTYSET('1YPtSHUJ9B_B9lz7ppy7yK',$,'BBIM_Linked_Aggregate',$,(#35967)); @@ -8956,7 +8956,7 @@ DATA; #39166=IFCPROPERTYSET('16fgijRGH0wQ4w9kJF$$Vr',$,'BBIM_Linked_Aggregate',$,(#39168)); #39167=IFCRELDEFINESBYPROPERTIES('2PlGVQ7pjEvw7E$79txECs',$,$,$,(#38849),#39166); #39168=IFCPROPERTYSINGLEVALUE('Index',$,IFCINTEGER(2),$); -#39169=IFCPROPERTYSET('12Npe0f6D0_BsrIZtUisSy',$,'BBIM_Linked_Aggregate',$,(#39171)); +#39169=IFCPROPERTYSET('12Npe0f6D0_BsrIZtUisSy',$,'BBIM_Linked_Aggregate',$,(#39171,#41308,#41309)); #39170=IFCRELDEFINESBYPROPERTIES('3GCIzzBb9ERx0AKtY_QKod',$,$,$,(#38874),#39169); #39171=IFCPROPERTYSINGLEVALUE('Index',$,IFCINTEGER(0),$); #39172=IFCPROPERTYSET('1O3gul9Gf0UfQIHuaY$zu$',$,'BBIM_Linked_Aggregate',$,(#39174)); @@ -9356,7 +9356,7 @@ DATA; #39821=IFCPROPERTYSET('3Bi4nPYrP86AQT5N7Cp5sX',$,'BBIM_Linked_Aggregate',$,(#39823)); #39822=IFCRELDEFINESBYPROPERTIES('1tOefteVD2UxoFC3U7Jb9l',$,$,$,(#39504),#39821); #39823=IFCPROPERTYSINGLEVALUE('Index',$,IFCINTEGER(2),$); -#39824=IFCPROPERTYSET('3lpBAgbw58qfyNULP3a8nO',$,'BBIM_Linked_Aggregate',$,(#39826)); +#39824=IFCPROPERTYSET('3lpBAgbw58qfyNULP3a8nO',$,'BBIM_Linked_Aggregate',$,(#39826,#41304,#41305)); #39825=IFCRELDEFINESBYPROPERTIES('2PMH7w56X5BQIVTIy0$XwZ',$,$,$,(#39529),#39824); #39826=IFCPROPERTYSINGLEVALUE('Index',$,IFCINTEGER(0),$); #39827=IFCPROPERTYSET('0uxOlwfAv0gfNQ_2gapcZC',$,'BBIM_Linked_Aggregate',$,(#39829)); @@ -9993,5 +9993,11 @@ DATA; #41301=IFCCSGSOLID(#41300); #41302=IFCSHAPEREPRESENTATION(#15,'Body','CSG',(#41301)); #41303=IFCDOCUMENTREFERENCE('sheets/A00 - GENERAL ARRANGEMENT.svg','X',$,'SHEET',#41290); +#41304=IFCPROPERTYSINGLEVALUE('Aggregate_Index',$,IFCINTEGER(1),$); +#41305=IFCPROPERTYSINGLEVALUE('Name',$,IFCLABEL('Truss'),$); +#41306=IFCPROPERTYSINGLEVALUE('Aggregate_Index',$,IFCINTEGER(0),$); +#41307=IFCPROPERTYSINGLEVALUE('Name',$,IFCLABEL('Truss'),$); +#41308=IFCPROPERTYSINGLEVALUE('Aggregate_Index',$,IFCINTEGER(2),$); +#41309=IFCPROPERTYSINGLEVALUE('Name',$,IFCLABEL('Truss'),$); ENDSEC; END-ISO-10303-21;