[IfcOpenShell] Knowledgesharing: Creating an IfcGrid

edited July 28 in General

After reading this thread and seeing this post on a Freecad forum from 2016.

It made me wonder if I could create an .ifc with Python starting from the basics with a grid.
Conclusion, it seems I can, I barely changed the script and loaded the .ifc in BlenderBIM


Here is the python code:


See the python code
import os
import sys
import time
import uuid
import math

import ifcopenshell

O = 0., 0., 0.
X = 1., 0., 0.
Y = 0., 1., 0.
Z = 0., 0., 1.

create_guid = lambda: ifcopenshell.guid.compress(uuid.uuid1().hex)


file_name = 'grid.ifc'
timestamp = time.time()
timestring = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(timestamp))
creator = "C. Claus"
organization = "Claus aannemingsmaatschappij"
application, application_version = "IfcOpenShell", "0.5"
project_globalid, project_name = create_guid(), "Grid"


# open for Blank
ifc_file = ifcopenshell.file()


#=============================================

org = ifc_file.createIfcOrganization()
org.Name = organization

app = ifc_file.createIfcApplication()
app.ApplicationDeveloper = org
app.Version = "3.2"
app.ApplicationFullName = "BlenderBIM"

person = ifc_file.createIfcPerson()
person.FamilyName="C. Claus"


person_org= ifc_file.createIfcPersonAndOrganization()
person_org.ThePerson=person
person_org.TheOrganization=org

owner_hist= ifc_file.createIfcOwnerHistory()
owner_hist.OwningUser = person_org
owner_hist.OwningApplication = app
owner_hist.ChangeAction= "NOCHANGE"
owner_hist.CreationDate= int(time.time())

#  Global unit definitions
LengthUnit = ifc_file.createIfcSIUnit()
LengthUnit.UnitType = "LENGTHUNIT"
LengthUnit.Prefix = "MILLI"
LengthUnit.Name="METRE"

#AreaUnit = ifc_file.createIfcSIUnit("AREAUNIT" , None, "SQUARE_METRE")
AreaUnit = ifc_file.createIfcSIUnit()
AreaUnit.UnitType = "AREAUNIT"
AreaUnit.Name="SQUARE_METRE"


VolumeUnit = ifc_file.createIfcSIUnit()
VolumeUnit.UnitType = "VOLUMEUNIT"
VolumeUnit.Name="CUBIC_METRE"


PlaneAngleUnit = ifc_file.createIfcSIUnit()
PlaneAngleUnit.UnitType = "PLANEANGLEUNIT"
PlaneAngleUnit.Name  ="RADIAN"

AngleUnit = ifc_file.createIfcMeasureWithUnit()
AngleUnit.UnitComponent =PlaneAngleUnit 
AngleUnit.ValueComponent = ifc_file.createIfcPlaneAngleMeasure(math.pi/180)


DimExp = ifc_file.createIfcDimensionalExponents(0,0,0,0,0,0,0)


ConvertBaseUnit = ifc_file.createIfcConversionBasedUnit()
ConvertBaseUnit.Dimensions = DimExp
ConvertBaseUnit.UnitType="PLANEANGLEUNIT"
ConvertBaseUnit.Name="DEGREE"
ConvertBaseUnit.ConversionFactor = AngleUnit


UnitAssignment=ifc_file.createIfcUnitAssignment([LengthUnit , AreaUnit , VolumeUnit ,ConvertBaseUnit])

axis_X = ifc_file.createIfcDirection(X)
axis_Y = ifc_file.createIfcDirection(Y)
axis_Z = ifc_file.createIfcDirection(Z)
Pnt_O = ifc_file.createIfcCartesianPoint(O)


# Defining project and representation contexts 
WorldCoordinateSystem = ifc_file.createIfcAxis2Placement3D()
WorldCoordinateSystem.Location=Pnt_O
WorldCoordinateSystem.Axis = axis_Z
WorldCoordinateSystem.RefDirection = axis_X

context = ifc_file.createIfcGeometricRepresentationContext()
context.ContextType = "Model"
context.CoordinateSpaceDimension = 3
context.Precision = 1.e-05
context.WorldCoordinateSystem = WorldCoordinateSystem


footprint_context = ifc_file.createIfcGeometricRepresentationSubContext()
footprint_context.ContextIdentifier = 'FootPrint'
footprint_context.ContextType = "Model"
footprint_context.ParentContext = context
footprint_context.TargetView = 'MODEL_VIEW'

myProject = ifc_file.createIfcProject(create_guid())
myProject.OwnerHistory = owner_hist
myProject.Name = "Test Grid"
myProject.RepresentationContexts = [context]
myProject.UnitsInContext = UnitAssignment


# Defining site, building and first story ------------
site_placement = ifc_file.createIfcLocalPlacement()
site_placement.RelativePlacement=WorldCoordinateSystem
mySite = ifc_file.createIfcSite( create_guid() )
mySite.OwnerHistory = owner_hist
mySite.Name = "My Site"
mySite.ObjectPlacement = site_placement
mySite.CompositionType="ELEMENT"

building_placement = ifc_file.createIfcLocalPlacement()
building_placement.PlacementRelTo = site_placement
building_placement.RelativePlacement = WorldCoordinateSystem

myBuilding = ifc_file.createIfcBuilding( create_guid(), owner_hist )
myBuilding.Name = "Test Building"
myBuilding.ObjectPlacement = building_placement
myBuilding.CompositionType="ELEMENT"

floor1_placement = ifc_file.createIfcLocalPlacement()
floor1_placement.PlacementRelTo = building_placement
floor1_placement.RelativePlacement = WorldCoordinateSystem

floor1 = ifc_file.createIfcBuildingStorey( create_guid(), owner_hist )
floor1.Name = "Floor 1"
floor1.ObjectPlacement = floor1_placement
floor1.CompositionType="ELEMENT"
floor1.Elevation = 1000


container_project = ifc_file.createIfcRelAggregates(create_guid() , owner_hist)
container_project.Name="Project Container"
container_project.RelatingObject = myProject 
container_project.RelatedObjects = [mySite]

container_site = ifc_file.createIfcRelAggregates(create_guid() , owner_hist)
container_site.Name = "Site Container"
container_site.RelatingObject = mySite
container_site.RelatedObjects = [myBuilding] 

container_storey = ifc_file.createIfcRelAggregates(create_guid() , owner_hist)
container_storey.Name = "Building Container"
container_storey.RelatingObject = myBuilding
container_storey.RelatedObjects = [floor1] 


#####################################################
###       Defining the grid axes geometry         ###
#####################################################
GridXList = [   {'id':'1' , 'distance':0.0 } ,
                {'id':'2' , 'distance':4000.0}  ]

GridYList = [   {'id':'A' , 'distance':0.0 } ,
                {'id':'B' , 'distance':4000.0}  ]

xMin = GridXList[0]['distance']-2000.0 ; xMax =  GridXList[-1]['distance']+2000.0 
yMin =  GridYList[0]['distance']-2000.0 ; yMax =  GridYList[-1]['distance']+2000.0 


polylineSet = []
gridX = []
for iGrid in GridXList:
    pnt1 = ifc_file.createIfcCartesianPoint( (iGrid['distance'],yMin) )
    pnt2 = ifc_file.createIfcCartesianPoint( (iGrid['distance'],yMax) )
    Line = ifc_file.createIfcPolyline( [pnt1 , pnt2] )
    polylineSet.append(Line)
    grid = ifc_file.createIfcGridAxis()
    grid.AxisTag = iGrid['id']
    grid.AxisCurve = Line
    grid.SameSense = True
    gridX.append(grid)

gridY = []
for iGrid in GridYList:
    pnt1 = ifc_file.createIfcCartesianPoint( (xMin ,iGrid['distance']) )
    pnt2 = ifc_file.createIfcCartesianPoint( (xMax,iGrid['distance']) )
    Line = ifc_file.createIfcPolyline( [pnt1 , pnt2] )
    polylineSet.append(Line)
    grid = ifc_file.createIfcGridAxis()
    grid.AxisTag = iGrid['id']
    grid.AxisCurve = Line
    grid.SameSense = True
    gridY.append(grid)

# Defining the grid 
PntGrid = ifc_file.createIfcCartesianPoint( O )

myGridCoordinateSystem = ifc_file.createIfcAxis2Placement3D()
myGridCoordinateSystem.Location= PntGrid
myGridCoordinateSystem.Axis = axis_Z
myGridCoordinateSystem.RefDirection = axis_X

grid_placement = ifc_file.createIfcLocalPlacement()
grid_placement.PlacementRelTo = floor1_placement
grid_placement.RelativePlacement = myGridCoordinateSystem

grid_curvedSet =  ifc_file.createIfcGeometricCurveSet(polylineSet)

gridShape_Reppresentation = ifc_file.createIfcShapeRepresentation()
gridShape_Reppresentation.ContextOfItems = footprint_context
gridShape_Reppresentation.RepresentationIdentifier = 'FootPrint'
gridShape_Reppresentation.RepresentationType = 'GeometricCurveSet'
gridShape_Reppresentation.Items = [grid_curvedSet]

grid_Reppresentation = ifc_file.createIfcProductDefinitionShape()
grid_Reppresentation.Representations  = [gridShape_Reppresentation]

myGrid = ifc_file.createIfcGrid(create_guid() , owner_hist)
myGrid.ObjectPlacement = grid_placement
myGrid.Representation = grid_Reppresentation
myGrid.UAxes=gridX
myGrid.VAxes=gridY

# assignment to spatial structure ------------------------------------------ 
container_SpatialStructure= ifc_file.createIfcRelContainedInSpatialStructure(create_guid() , owner_hist)
container_SpatialStructure.Name='BuildingStoreyContainer'
container_SpatialStructure.Description = 'BuildingStoreyContainer for Elements'
container_SpatialStructure.RelatingStructure = floor1
container_SpatialStructure.RelatedElements = [myGrid]


file_path = "C:\\Algemeen\\07_prive\\08_ifc_bestanden\\"
file_ifc = file_path  + file_name
print (file_ifc)
ifc_file.write(file_ifc)

How would I add the grid letter and number visibility in BlenderBIM? Like it is in BIMVision

theoryshaw

Comments

  • @Coen Is it an idea to start putting Snippets found here on the wiki?

  • @CadGiru said:
    @Coen Is it an idea to start putting Snippets found here on the wiki?

    i like that idea. Posting them to our https://gitlab.com/osarch/Example_Files repo, i think would be a better idea.
    ...
    @Coen noob question, is there anyway to run a script like this in BB... and have the results show 'live' in the 3D viewport? That is, instead of running the script, creating the IFC file, and then importing the IFC into BB.

  • @CadGiru said:
    @Coen Is it an idea to start putting Snippets found here on the wiki?

    That is probably a good idea,

    @theoryshaw said:
    @Coen noob question, is there anyway to run a script like this in BB... and have the results show 'live' in the 3D viewport? That is, instead of running the script, creating the IFC file, and then importing the IFC into BB.

    Live interaction with BlenderBIM would be great. BIMVision has this feature when a change is detected in IFC it prompts you to load the new version. Would be cool if BlenderBIM could do the same. I don't know if this is hard thing to do. I should look into it, I vaguely remember someone already made this before....? Or I dreamt it.

  • How would I add the grid letter and number visibility in BlenderBIM? Like it is in BIMVision

    How to do it via the UI... https://www.dropbox.com/s/ypcbn6nvxg5wmz3/2022-07-28_08-25-59_Blender_blender.mp4?dl=0

    Coen
  • @theoryshaw

    @Coen noob question, is there anyway to run a script like this in BB... and have the results show 'live' in the 3D viewport? That is, instead of running the script, creating the IFC file, and then importing the IFC into BB.

    I was thinking about this, I think I'm almost there.

    You could just put this line at the end of your script:

    bpy.ops.bim.load_project(filepath=file_ifc)
    

    And then then store the timestamp in a temporary variable, I need to do some more research on where it's stored usally in IFC and catch the exceptions, I now do relatively dumb string parsing, which is not really fool proof...

    def get_timestamp():
        ifc_file = ifcopenshell.open(IfcStore.path)
    
        with open(IfcStore.path) as ifc_text:
            for line_no, line_file in enumerate(ifc_text):
                if line_no  == 3:
                    if line_file.startswith("FILE_NAME"):
                        time_stamp = (line_file.split(',')[1])
    
        print (time_stamp)
    
    
    
    get_timestamp()   
    
    bpy.ops.bim.reload_ifc_file()
    

    If the user clicks save project, or somehow it detects the ifc file is being overwritten. It should start comparing the time stamps, if a difference in time stampts is detected it should unload the old ifc and load the the new ifc.

    theoryshaw
  • Just created a little workaround, this works, but be very careful! it throws an entire blender collection away where the IFC is located. Only store things in IFC and don't save the Blender file.

    project = ifc_file.by_type('IfcProject')
    
    for i in project:
        collection_name = 'IfcProject/' + i.Name
    
    collection = bpy.data.collections.get(str(collection_name))
    
    for obj in collection.objects:
        bpy.data.objects.remove(obj, do_unlink=True)
    
    bpy.data.collections.remove(collection)
    bpy.ops.bim.load_project(filepath=file_ifc)
    

    If you copy paste this at the end of your IfcOpenShell python scripts it automatically load the IFC file in BlenderBIM (Taking it you're coding in BlenderBIM ofcourse)..

  • nice.. this noob will play around. :)


  • Seems to work... 😁

    theoryshawvpajic
  • edited August 1

    Did some refactoring of creating an IfcGrid, now you can change the grid amount and grid distance through a function:

    Code to be found here:
    https://github.com/C-Claus/BlenderScripts/blob/master/BlenderBIMGrid/create_grid_refactored_clean.py
    Some-auto generated results:


    vpajictheoryshaw
  • Next step, trying to create an IfcSlab through IfcOpenShell and add it to certain grid points. Probably worth another thread.

  • edited August 1

    @Coen said:
    Next step, trying to create an IfcSlab through IfcOpenShell and add it to certain grid points. Probably worth another thread.

    i'm curious how this could be codified in the IFC, to survive an IFC roundtrip.
    Could it be through entitles like the following?

  • @Coen a few suggestions here
    I removed IFCPRODUCTDEFINITIONSHAPE and IFCGEOMETRICCURVESET, since the native gridlines in BB don't have these entities associated with them.
    You might have had a good reason to include them, if so, please disregard. I'm mostly doing this for practice. :)
    Best, Ryan

    Coen
  • It also looks like the IFCLOCALPLACEMENT is different. Not sure which one's right... local placement confuses me a little.

    In the create_grid_refactored_clean.py script...

      /*#101=IFCGRID('2dgA7o4TeHxRPiIF5$qabM',#5,$,$,$,#97,#100,(#46,#50,#54,#58),(#62,#66,#70,#74,#78,#82,#86,#90,#94),$,$);*/
        /*#97=IFCLOCALPLACEMENT(#38,#96);*/
          /*#38=IFCLOCALPLACEMENT(#32,#37);*/
            /*#32=IFCLOCALPLACEMENT(#26,#31);*/
              /*#26=IFCLOCALPLACEMENT($,#25);*/
                /*#25=IFCAXIS2PLACEMENT3D(#22,#23,#24);*/
                  /*#22=IFCCARTESIANPOINT((0.,0.,0.));*/
                  /*#23=IFCDIRECTION((0.,0.,1.));*/
                  /*#24=IFCDIRECTION((1.,0.,0.));*/
              /*#31=IFCAXIS2PLACEMENT3D(#28,#29,#30);*/
                /*#28=IFCCARTESIANPOINT((0.,0.,0.));*/
                /*#29=IFCDIRECTION((0.,0.,1.));*/
                /*#30=IFCDIRECTION((1.,0.,0.));*/
            /*#37=IFCAXIS2PLACEMENT3D(#34,#35,#36);*/
              /*#34=IFCCARTESIANPOINT((0.,0.,0.));*/
              /*#35=IFCDIRECTION((0.,0.,1.));*/
              /*#36=IFCDIRECTION((1.,0.,0.));*/
          /*#96=IFCAXIS2PLACEMENT3D(#95,#16,#14);*/
            /*#95=IFCCARTESIANPOINT((0.,0.,0.));*/
            /*#16=IFCDIRECTION((0.,0.,1.));*/
            /*#14=IFCDIRECTION((1.,0.,0.));*/
    
    

    In native BB....

    #60=IFCGRID('1FWhmuU9jB0u2KIDhbgjcq',$,'Grid',$,$,#71,$,(#72,#76,#80),(#84,#88,#92),$,$);
      /*#71=IFCLOCALPLACEMENT(#47,#70);*/
        /*#47=IFCLOCALPLACEMENT($,#46);*/
          /*#46=IFCAXIS2PLACEMENT3D(#43,#44,#45);*/
            /*#43=IFCCARTESIANPOINT((0.,0.,0.));*/
            /*#44=IFCDIRECTION((0.,0.,1.));*/
            /*#45=IFCDIRECTION((1.,0.,0.));*/
        /*#70=IFCAXIS2PLACEMENT3D(#67,#68,#69);*/
          /*#67=IFCCARTESIANPOINT((0.,0.,0.));*/
          /*#68=IFCDIRECTION((0.,0.,1.));*/
          /*#69=IFCDIRECTION((1.,0.,0.));*/
    
    Coen
  • edited August 2

    @theoryshaw
    Thanks for the info, I thought BlenderBIM had no curve support? But it seem to export succesfully.
    I am now placing beams on the grid as I seen in the code from @BIMrookie92

    Some screenshots of auto generated results:

    BlenderBIM is so insanely fast with creating an entire IFC model and loading it....When I click run script it almost creates the geometry instantly.

    Also make it easy to place columns along a grid,

    Ace
  • @theoryshaw

    i'm curious how this could be codified in the IFC, to survive an IFC roundtrip.

    Me as well, the method I'm using now is just removing and rewriting the entire IFC in BlenderBIM. (seems to work, because BlenderBIM is insanely fast with loading an IFC).

    I made some little experiments with a IfcWall and IfcSlab, but I barely can't find any good Python documentation on how to create geometry with IfcOpenShell... Or maybe I am looking in the wrong places?. The only sources I used are the FreeCAD Forum, snippets from this forum and ifcopenshell academy. It's a lot of trial and error... and reading BuildingSmart documentation and logically trying to translate it to python code.

    Anyway some results:

    IfcWall placement moves if you change the slab thickness.

    theoryshawNigel
  • edited August 3

    Imagine if IFC allowed us to do this?! :)

    Coen
  • I managed to get a slab and beam system to move along with the grid, probaly in a stupid way but it works.
    Still don't know how to create a IfcWall from a curve2D in ifcopenshell. 😥


  • Managed to create 1000 storey slabs. 😁

    Just amazing how fast BlenderBIM is with creating and loading...

  • For now I finished playing around with ifcopenshell and python :-)

    Here is the refactored code:
    https://github.com/C-Claus/BlenderScripts/blob/master/BlenderBIMGrid/create_grid_with_slabs_and_building_stories.py

    Removed a lot of stupid mistakes and unnecessary variables. Tried to keep it as simple as possible
    Scripts allow for creation of grids/building storeys and IfcSlabs created in IfcOpenShell and loaded in BlenderBIM. I think it could maybe serve as a basis for other things? . I highly appreciate feedback :-).

    And a screenshot:

    Don't know if this is worth a youtube demo?

    theoryshawvpajicGorgious
  • @Coen you do a lot of cool work, do the YouTube please. It helps people like me and closes off your mini project nicely

    vpajicCoen

  • It's kind of a challenge to create as much geometry as possible with as little config parameters as possible

    AceNigeltheoryshawbruno_perdigaoJesusbill
  • Hi @Coen this is cool, I downloaded the earlier code that makes the grids and builds the floors, thank you. Now I'm really interested in how the walls get built

    theoryshaw
Sign In or Register to comment.