Skip to content

02: Inspecting IFC instance objects

jakob-beetz edited this page Dec 21, 2016 · 6 revisions

Instance object attribute values

Once you have retrieved the desired object(s) from the model you can start to inspect its contents, values and relations to other objects. First, let's take a look at some simple values of our project, by accessing its attributes.

Accessing ENTITY attributes

There are a number of ways by which you can inspect and access the values of the attribute of an ENTITY instance:

Access attributes by name

Most IFC ENTITY have a number attributes. They are the same as the attributes listed on the in the specifification of the model, e.g. the documentation of the IfcProject ENTITY definition. Each attribute, including the inherited attributes and can be accesses using a . after the instance variable, e.g.

project = model.by_type("IfcProject")
print (project[0].Name)

which in our case prints out

Hello wall project

The Name actually is not an attribute specific to the IfcProject ENTITY, but since it is a superclass of IfcRoot, and thus inherits its attributes (see the schema at the end of the documentation it can be accessed just the same.

Similarly, all other attributes defined in the schema can be accessed

project = model.by_type("IfcProject")
print (project[0].Name)
print (project[0].GlobalId)
print (project[0].Description)
print (project[0].LongName)

yield the output

Hello wall project
0YvctVUKr0kugbFTf53O9L
Simple wall with door
None

Notice, that print (project[0].LongName) resulted in None. If we inspect the particular line in the SPFF file by print (project[0]) we can see that

#1=IfcProject('0YvctVUKr0kugbFTf53O9L',#2,'Hello wall project','Simple wall with door',$,$,$,(#20),#7)

The $ sign signifying an attribute that has not filled in. This is very often the case for attributes that have been made OPTIONAL in the ENTITY definition in the schema.

###Access attributes by index Sometimes, you cannot lookup all attributes of an entity instance in the schema (or are too lazy to do so). A handy way to IfcOpenShell provides a way to access the attributes by their order (in the schema, reflected in the SPFF part 21 file) If you want to list the names of all attributes that a particular object has you can e.g. use this:

project = model.by_type("IfcProject")[0]
for att_idx in range(0,len(project)):
    print(project.attribute_name(att_idx))

which will print

GlobalId
OwnerHistory
Name
Description
ObjectType
LongName
Phase
RepresentationContexts
UnitsInContext

Note that len() gives you the number of attributes of that particular entity without the inverse attributes, just like listed in the specification. It is the size of the list of all attributes defined for this particular ENTITY instance, so IfcProject in our case. Refer to the Python tutorial on lists to see how they can be handled.

This can be used to print any value of any ENTITY instance we encounter in our IFC file. Let's extend the above script by also printing the values of each attribute that we find using the index mechanism. This time, we're inspecting the door we have in our 'hello wall' project

door = model.by_type("IfcDoor")[0]
for att_idx in range(0,len(project)-1):
    field_width = 20
    # In case we are dealing with a different IFC version
    # there might by attributes that do not exist in a 2x3 file
    # handle the attempt to print out a value where there is no
    # attribute gracefully
    try:
        att_name = door.attribute_name(att_idx)
        att_type = door.attribute_type(att_name)
        att_value = door[att_name]
        print("{}\t{}\t{}".format(att_name.ljust(field_width),att_type.ljust(field_width), att_value))
    except:
        pass

Two things to notice here:

  1. IfcOpenShell is built against the IFC 4 schema. In the case of an IfcDoor for example, three new attributes have been added in IFC 4 to the ENTITY definition of IfcDoor: PredefinedType OperationType and UserDefinedOperationType. Compare the specs of IFC 4 with the IFX 2x3 version. To prevent the application from crashing, we have to catch the error (exception) that occurs when we are trying to access the value of e.g. door['PredefinedType'] by using the try ... except mechanism built into Python.
  2. In order to have a nice output, the results are printed to the console using the .format function that can be applied to any strings.

The output should look somthing like this:

GlobalId            	STRING              	0LV8Pid0X3IA3jJLVDPidY
OwnerHistory        	ENTITY INSTANCE     	#2=IfcOwnerHistory(#3,#6,$,.ADDED.,$,$,$,1217620436)
Name                	STRING              	A common door
Description         	STRING              	Description of a standard door
ObjectType          	STRING              	None
ObjectPlacement     	ENTITY INSTANCE     	#125=IfcLocalPlacement(#98,#126)
Representation      	ENTITY INSTANCE     	#130=IfcProductDefinitionShape($,$,(#150))
Tag                 	STRING              	None
OverallHeight       	DOUBLE              	1.4
OverallWidth        	DOUBLE              	0.7

Notice how the string representation of the of e.g. OwnerHistory is another line from our file? Well, that's because attributes can have simple, so-called 'literal' types such as STRING or DOUBLE or they can 'point to'/refer to other entity instances. The IFC model can be viewed as a graph that has connects the different entities among each other. In IFC SPFF files this is done by simply referring to the respective entity using the e.g. #2 form, that in this example point to an IfcOwnerHistory object, which captures meta data. Using IfcOpenShell, we can navigate through the graph very easily. If we e.g. want to find out, who created the door we can just 'navigate' or 'traverse' our graph/model.

Traversing ENTITY instances

Using the convenient IfcOpenShell possibility of the . way of accessing attributes, we can e.g. easily retrieve the creator of a particular element by navigating through the IFC file graph using

door = model.by_type("IfcDoor")[0]
creator = door.OwnerHistory.OwningUser.ThePerson.FamilyName
print (creator)

this prints out

Beetz

and is equivalent to

door = model.by_type("IfcDoor")[0]
creator = door.OwnerHistory.OwningUser.ThePerson.FamilyName
history = door.OwnerHistory
owningUser = history.OwningUser
thePerson = owningUser.ThePerson
familiyName = thePerson.FamilyName
print(familiyName)

which yields the same result.

This can e.g. be used to retrieve all properties in all IfcPropertySets that are a asscociated to the currently selected object:

if selection:
    #get the IfcProduct that is stored in the global variable 'selection'
    obj = selection
    
    for relDefinesByProperties in obj.IsDefinedBy:
         print("[{0}]".format(relDefinesByProperties.RelatingPropertyDefinition.Name))
         for prop in relDefinesByProperties.RelatingPropertyDefinition.HasProperties:
             print ("{:<20} :{}".format(prop.Name,prop.NominalValue.wrappedValue))
         print ("\n")
Clone this wiki locally