Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IVS-369 SPS002 behaviour update #344

Merged
merged 6 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion features/SPS002_Correct-spatial-breakdown.feature
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@implementer-agreement
@SPS
@version2
@version3
@E00100
Feature: SPS002 - Correct spatial breakdown
The rule verifies that spatial elements are aggregated as per the Spatial Composition Table.
Expand Down
7 changes: 4 additions & 3 deletions features/steps/thens/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,23 @@ def step_impl(context, inst, relationship, table):
# raise Exception(f'Entity {entity} was not found in the {table}')
continue

applicable_entity = ifc.order_by_ifc_inheritance(applicable_entities, base_class_last = True)[0]
expected_relationship_objects = aggregated_table[applicable_entity]
# For all applicable entities (could be multiple e.g IfcRoad, IfcFacility) we union the allowed types
expected_relationship_objects = sorted(set(functools.reduce(operator.or_, map(set, [aggregated_table[e] for e in applicable_entities]))))
try:
relation = getattr(inst, stmt_to_op[relationship], True)[0]
except IndexError: # no relationship found for the entity
if is_required:
yield ValidationOutcome(inst=inst, expected={"oneOf": expected_relationship_objects, "context": context}, severity=OutcomeSeverity.ERROR)
continue

relationship_objects = getattr(relation, relationship_tbl_header, True)
if not isinstance(relationship_objects, tuple):
relationship_objects = (relationship_objects,)


for relationship_object in relationship_objects:
is_correct = any(relationship_object.is_a(expected_relationship_object) for expected_relationship_object in expected_relationship_objects)
if not is_correct:
# related object not of the correct type
yield ValidationOutcome(inst=inst, expected={"oneOf": expected_relationship_objects, "context": context}, observed=relationship_object, severity=OutcomeSeverity.ERROR)


Expand Down
11 changes: 0 additions & 11 deletions features/steps/utils/ifc.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,6 @@ def instance_getter(i, representation_id, representation_type, negative=False):
if condition(i, representation_id, representation_type):
return i

def order_by_ifc_inheritance(instances, base_class_last):
import ifcopenshell
ifc = ifcopenshell.file(schema='IFC4X3')
inheritance_nr = {}
for instance in instances:
ifc_instance = ifc.create_entity(instance)
result = sum(1 for str_instance in instances if ifc_instance.is_a(str_instance))
inheritance_nr[instance] = result
inheritance_nr = dict(sorted(inheritance_nr.items(), key=lambda item: item[1], reverse=base_class_last))
return list(inheritance_nr.keys())


def recurrently_get_entity_attr(ifc_context, inst, entity_to_look_for, attr_to_get, attr_found=None):
if attr_found is None:
Expand Down
69 changes: 69 additions & 0 deletions test/files/sps002/pass-sps002-road_facilitypart.ifc
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
ISO-10303-21;
/* Testfile: pass-sps002-correct-spatial-breakdown.ifc */
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
/* IFC-Requirement: sps002; Correct spatial breakdown */
HEADER;
FILE_DESCRIPTION(('ViewDefinition [Ifc4X3NotAssigned]'),'2;1');
FILE_NAME ('pass-sps002-correct-spatial-breakdown-ifc4.ifc', '2023-03-14T15:36:48', ('bSI Railway Room - Technical Services'), ('aIFC4Rail'), 'Python+IfcOpenShell', 'BC-002; Correct spatial breakdown','');
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
FILE_SCHEMA(('IFC4X3_ADD2'));
ENDSEC;
DATA;
#1= IFCAPPLICATION(#2,'1.0.0.0','ggRhinoIFC - Geometry Gym Plug-in for Rhino3d','ggRhinoIFC');
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
#2= IFCORGANIZATION($,'Geometry Gym Pty Ltd',$,$,$);
#3= IFCPERSONANDORGANIZATION(#4,#5,$);
#4= IFCPERSON('Jon','Jon',$,$,$,$,$,$);
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
#5= IFCORGANIZATION($,'Geometry Gym Pty Ltd',$,$,$);
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
#6= IFCOWNERHISTORY(#3,#1,$,.ADDED.,1418084874,$,$,1418084874);
#7= IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,0.0001,#8,#10);
#8= IFCAXIS2PLACEMENT3D(#9,$,$);
#9= IFCCARTESIANPOINT((0.0,0.0,0.0));
#10= IFCDIRECTION((0.0,1.0));
#11= IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Axis','Model',*,*,*,*,#7,$,.MODEL_VIEW.,$);
#12= IFCGEOMETRICREPRESENTATIONSUBCONTEXT('Body','Model',*,*,*,*,#7,$,.MODEL_VIEW.,$);
#13= IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,0.0001,#8,#14);
#14= IFCDIRECTION((0.0,1.0));
#50= IFCBUILDING('0Cd2Mw3cP09wW6qWHK8v2f',$,'IfcBuilding',$,$,#51,$,$,.ELEMENT.,$,$,#57);
#51= IFCLOCALPLACEMENT($,#52);
#52= IFCAXIS2PLACEMENT3D(#53,$,$);
#53= IFCCARTESIANPOINT((0.0,0.0,0.0));
#54= IFCRELCONTAINEDINSPATIALSTRUCTURE('3ffS1zvV94ExNgP6hOHMLr',$,'Building','Building Container for Elements',(#302),#50);
#57= IFCPOSTALADDRESS($,$,$,$,$,$,$,'Unknown',$,$);
#100= IFCPROJECT('3KEb34nozBu9ezspX8gM9d',#6,'IfcProject',$,$,'IfcProject','',(#13),#101);
#101= IFCUNITASSIGNMENT((#102,#103,#104));
#102= IFCSIUNIT(*,.LENGTHUNIT.,.MILLI.,.METRE.);
#103= IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.);
#104= IFCSIUNIT(*,.TIMEUNIT.,$,.SECOND.);
#200= IFCMATERIAL('Masonry - Brick - Brown',$,$);
#203= IFCMATERIAL('Masonry',$,$);
#206= IFCMATERIALLAYER(#200,110.0,.F.,'Finish',$,$,$);
#208= IFCMATERIALLAYER($,50.0,.T.,'Air Infiltration Barrier',$,$,$);
#210= IFCMATERIALLAYER(#203,110.0,.F.,'Core',$,$,$);
#212= IFCMATERIALLAYERSET((#206,#208,#210),'Double Brick - 270',$);
#213= IFCRELASSOCIATESMATERIAL('2D3xie$bD8YgahfQF1htfq',$,'MatAssoc','Material Associates',(#300),#212);
#300= IFCWALLTYPE('2GdZ7nhi52Geyiua9QcAH9',$,'Double Brick - 270',$,$,$,$,$,$,.NOTDEFINED.);
#301= IFCRELDEFINESBYTYPE('3Gcd0t0WTDWe18S5rhROgf',$,'Double Brick - 270',$,(#302),#300);
#302= IFCWALLSTANDARDCASE('0czCsOQ5z4dg8QGBRFInu2',$,$,$,$,#305,#320,$,$);
#303= IFCMATERIALLAYERSETUSAGE(#212,.AXIS2.,.POSITIVE.,0.0,$);
#304= IFCRELASSOCIATESMATERIAL('0PFtmoJBHDOvVAl6M9w55u',$,'MatAssoc','Material Associates',(#302),#303);
#305= IFCLOCALPLACEMENT($,#306);
#306= IFCAXIS2PLACEMENT3D(#307,#308,#309);
#307= IFCCARTESIANPOINT((0.0,0.0,0.0));
#308= IFCDIRECTION((0.0,0.0,1.0));
#309= IFCDIRECTION((1.0,0.0,0.0));
#310= IFCCARTESIANPOINT((5000.0,0.0));
#311= IFCCARTESIANPOINT((0.0,0.0));
#312= IFCPOLYLINE((#311,#310));
#313= IFCSHAPEREPRESENTATION(#11,'Axis','Curve2D',(#312));
#314= IFCAXIS2PLACEMENT2D(#315,$);
#315= IFCCARTESIANPOINT((2500.0,135.0));
#316= IFCRECTANGLEPROFILEDEF(.AREA.,'Wall Perim',#314,5000.0,270.0);
#317= IFCDIRECTION((0.0,0.0,1.0));
#318= IFCEXTRUDEDAREASOLID(#316,$,#317,2000.0);
#319= IFCSHAPEREPRESENTATION(#12,'Body','SweptSolid',(#318));
#320= IFCPRODUCTDEFINITIONSHAPE($,$,(#313,#319));
#321=IFCROAD('0hb5vCxjv2ZetiycRLI_Fx',#6,'','',$,$,$,$,$,$);
#322=IFCFACILITYPARTCOMMON('0hb5vCxjv2ZetiycRLI_Fy',#6,'','',$,$,$,$,$,.NOTDEFINED.,$);
#323= IFCRELAGGREGATES('2G17HB5orFmhdpTVYmbgKX',$,'RailwayContainer','Container for Railway Parts',#321,(#322));
#324= IFCRELAGGREGATES('2G17HB5orFmhdpTVYmbgCX',$,'RailwayContainer','Container for Railway Parts',#100,(#321,#50));
civilx64 marked this conversation as resolved.
Show resolved Hide resolved
ENDSEC;

END-ISO-10303-21;