We continue the rather exhaustive exploration of calculating net and gross wall areas per room, and two other announcements, pointers to interesting sources of information besides The Building Coder:
I hope you know and appreciate that Autodesk has a Labs, and why.
If not, please refer to Scott Sheppard's explanation why Autodesk has a Labs.
It is my pleasure to announce a new Italian AEC and BIM blog, dal BIM in poi, by Ilaria Lagazio and Stefano Toparini.
'Dal BIM in poi' means 'Beyond BIM'.
Stefano si occupa da molti anni di BIM per le Infrastrutture nonché di reti tecnologiche e di gestione del territorio, con esperienze professionali maturate sia in Italia che all'estero. Lavora nella filiale Italiana di Autodesk Inc. da oltre 10 anni dopo esperienze in varie altre aziende di informatica e servizi nel mondo della progettazione e delle pubbliche amministrazioni.
Stefano has been working with BIM for Infrastructure and GIS for many years, with professional experience both in Italy and abroad. He has been with the Italian subsidiary of Autodesk Inc. for over 10 years after experiences in several other computer companies and services in the design and public administration domains.
Ilaria e Laureata in Ingegneria Civile e specializzata in Strutture, dopo una breve esperienza nel campo della progettazione si dedica all’industrializzazione dei sistemi edilizi come Building System Development Manager, gestendo il flusso delle informazioni dei componenti edilizi dal modello al cantiere. L’interesse per l’industrializzazione del cantiere e la gestione del dato progettuale la porta ad una esperienza negli Emirati Arabi e dal 2008 in Autodesk, dove oggi ricopre il ruolo di Senior Technical Sales Specialist.
Ilaria graduated in civil engineering and specialised in structures. After a short experience in design she worked as Building System Development Manager, managing the information flow from BIM to construction site. Her interest in efficient construction site and project management took her to the UAE, and from 2008 onwards to Autodesk in the role as Senior Technical Sales Specialist.
I wish the new blog and all its readers all the best and many exciting stories!
As I pointed out last week in the preceding article on this topic, Håvard Dagsvik of the newly renamed Scandinavian AEC and BIM company Symetri, previously CAD-Q, invested significant effort in enhancing our joint efforts to determine wall opening areas per room.
Here are links to the previous discussions:
The results are captured in these two Revit add-in projects and GitHub repositories:
I integrated Håvard's final running test code into the SpatialElementGeometryCalculator project and am very happy to present the following results that you can now reproduce yourself as well.
In Håvard's own words:
Here it is.
Simplified and cleaned up.
I couldn’t use the existing dictionary so I implemented a small class SpatialBoundaryCache
to cache the core spatial data.
I demonstrate how you can structure and present the resulting data, areas square metres, in three different ways:
Material is my choice because I need to know if the surface is plaster or concrete – note that walls can have different layers on each side.
Just as before, I use both geometric analysis based on the wall solids as well as the IFC utility classes for the calculations.
I removed the GetAreaFromFamilyParameters
method because it is somewhat unreliable and I already use the IFC utils instead.
I also used your IsInRoom
method instead of mine.
In the long run, I think all of it will be just solid intersections also IsInRoom.
Both stacked walls and embedded curtain walls are handled, though probably not yet an embedded curtain wall within a stacked wall.
That could be solved following the same solid intersection logic.
I am quite sure there is still room for improvement in the opening handler.
Take a look and see what you make of it :-)
I am attaching the sample file that I worked with as well.
Many thanks to Håvard for his research, hard work, and sharing this!
I integrated Håvard's functionality in the public SpatialElementGeometryCalculator GitHub project, in release 2016.0.0.3.
The main execute method demonstrates how to:
SpatialElementGeometryCalculator
calculatorHere is the complete implementation:
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements ) { UIApplication uiapp = commandData.Application; Document doc = uiapp.ActiveUIDocument.Document; Result rc; try { SpatialElementBoundaryOptions sebOptions = new SpatialElementBoundaryOptions { SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish }; IEnumerable<Element> rooms = new FilteredElementCollector( doc ) .OfClass( typeof( SpatialElement ) ) .Where<Element>( e => (e is Room) ); List<string> compareWallAndRoom = new List<string>(); OpeningHandler openingHandler = new OpeningHandler(); List<SpatialBoundaryCache> lstSpatialBoundaryCache = new List<SpatialBoundaryCache>(); foreach( Room room in rooms ) { if( room == null ) continue; if( room.Location == null ) continue; if( room.Area.Equals( 0 ) ) continue; Autodesk.Revit.DB.SpatialElementGeometryCalculator calc = new Autodesk.Revit.DB.SpatialElementGeometryCalculator( doc, sebOptions ); SpatialElementGeometryResults results = calc.CalculateSpatialElementGeometry( room ); Solid roomSolid = results.GetGeometry(); foreach( Face face in results.GetGeometry().Faces ) { IList<SpatialElementBoundarySubface> boundaryFaceInfo = results.GetBoundaryFaceInfo( face ); foreach( var spatialSubFace in boundaryFaceInfo ) { if( spatialSubFace.SubfaceType != SubfaceType.Side ) { continue; } SpatialBoundaryCache spatialData = new SpatialBoundaryCache(); Wall wall = doc.GetElement( spatialSubFace .SpatialBoundaryElement.HostElementId ) as Wall; if( wall == null ) { continue; } WallType wallType = doc.GetElement( wall.GetTypeId() ) as WallType; if( wallType.Kind == WallKind.Curtain ) { // Leave out, as curtain walls are not painted. LogCreator.LogEntry( "WallType is CurtainWall" ); continue; } HostObject hostObject = wall as HostObject; IList<ElementId> insertsThisHost = hostObject.FindInserts( true, false, true, true ); double openingArea = 0; foreach( ElementId idInsert in insertsThisHost ) { string countOnce = room.Id.ToString() + wall.Id.ToString() + idInsert.ToString(); if( !compareWallAndRoom.Contains( countOnce ) ) { Element elemOpening = doc.GetElement( idInsert ) as Element; openingArea = openingArea + openingHandler.GetOpeningArea( wall, elemOpening, room, roomSolid ); compareWallAndRoom.Add( countOnce ); } } // Cache SpatialElementBoundarySubface info. spatialData.roomName = room.Name; spatialData.idElement = wall.Id; spatialData.idMaterial = spatialSubFace .GetBoundingElementFace().MaterialElementId; spatialData.dblNetArea = Util.sqFootToSquareM( spatialSubFace.GetSubface().Area ); spatialData.dblOpeningArea = Util.sqFootToSquareM( openingArea ); lstSpatialBoundaryCache.Add( spatialData ); } // end foreach subface from which room bounding elements are derived } // end foreach Face } // end foreach Room List<string> t = new List<string>(); List<SpatialBoundaryCache> groupedData = SortByRoom( lstSpatialBoundaryCache ); foreach( SpatialBoundaryCache sbc in groupedData ) { t.Add( sbc.roomName + "; all wall types and materials: " + sbc.AreaReport ); } Util.InfoMsg2( "Total Net Area in m2 by Room", string.Join(System.Environment.NewLine, t ) ); t.Clear(); groupedData = SortByRoomAndWallType( lstSpatialBoundaryCache ); foreach( SpatialBoundaryCache sbc in groupedData ) { Element elemWall = doc.GetElement( sbc.idElement ) as Element; t.Add( sbc.roomName + "; " + elemWall.Name + "(" + sbc.idElement.ToString() + "): " + sbc.AreaReport ); } Util.InfoMsg2( "Net Area in m2 by Wall Type", string.Join( System.Environment.NewLine, t ) ); t.Clear(); groupedData = SortByRoomAndMaterial( lstSpatialBoundaryCache ); foreach( SpatialBoundaryCache sbc in groupedData ) { string materialName = (sbc.idMaterial == ElementId.InvalidElementId) ? string.Empty : doc.GetElement( sbc.idMaterial ).Name; t.Add( sbc.roomName + "; " + materialName + ": " + sbc.AreaReport ); } Util.InfoMsg2( "Net Area in m2 by Outer Layer Material", string.Join( System.Environment.NewLine, t ) ); rc = Result.Succeeded; } catch( Exception ex ) { TaskDialog.Show( "Room Boundaries", ex.Message + "\r\n" + ex.StackTrace ); rc = Result.Failed; } return rc; } /// <summary> /// Convert square feet to square meters /// with two decimal places precision. /// </summary> static double SqFootToSquareM( double sqFoot ) { return Math.Round( sqFoot * 0.092903, 2 ); } List<SpatialBoundaryCache> SortByRoom( List<SpatialBoundaryCache> lstRawData ) { var sortedCache = from rawData in lstRawData group rawData by new { room = rawData.roomName } into sortedData select new SpatialBoundaryCache() { roomName = sortedData.Key.room, idElement = ElementId.InvalidElementId, dblNetArea = sortedData.Sum( x => x.dblNetArea ), dblOpeningArea = sortedData.Sum( y => y.dblOpeningArea ), }; return sortedCache.ToList(); } List<SpatialBoundaryCache> SortByRoomAndWallType( List<SpatialBoundaryCache> lstRawData ) { var sortedCache = from rawData in lstRawData group rawData by new { room = rawData.roomName, wallid = rawData.idElement } into sortedData select new SpatialBoundaryCache() { roomName = sortedData.Key.room, idElement = sortedData.Key.wallid, dblNetArea = sortedData.Sum( x => x.dblNetArea ), dblOpeningArea = sortedData.Sum( y => y.dblOpeningArea ), }; return sortedCache.ToList(); } List<SpatialBoundaryCache> SortByRoomAndMaterial( List<SpatialBoundaryCache> lstRawData ) { var sortedCache = from rawData in lstRawData group rawData by new { room = rawData.roomName, mid = rawData.idMaterial } into sortedData select new SpatialBoundaryCache() { roomName = sortedData.Key.room, idMaterial = sortedData.Key.mid, dblNetArea = sortedData.Sum( x => x.dblNetArea ), dblOpeningArea = sortedData.Sum( y => y.dblOpeningArea ), }; return sortedCache.ToList(); }
Here are the results from a test run on the sample model provided:
The simple test model looks like this in plan view:
In 3D view, you can see the stacked wall and the opening spanning several different spaces:
The results sorted by room, wall type and material are presented in three sequential task dialogues.
The first one, by room, is the simplest:
Each room is bounded by several different wall types:
Finally, here are the areas grouped by material:
For the sake of the search engines and legibility, here are the same results in text form as well, as reported on the Visual Studio debug console:
Total Net Area in m2 by Room Room 3; all wall types and materials: net 150; opening 9.2; gross 159.2 Room 5; all wall types and materials: net 42; opening 3.97; gross 45.97 Room 6; all wall types and materials: net 120; opening 7; gross 127 Room 7; all wall types and materials: net 120; opening 2; gross 122 Net Area in m2 by Wall Type Room 3; Exterior - Block on Mtl. Stud(308814): net 30; opening 0; gross 30 Room 3; CW 102-85-215p(309825): net 45; opening 4; gross 49 Room 3; Wall4(310052): net 30; opening 0; gross 30 Room 3; Wall1(308815): net 45; opening 5.2; gross 50.2 Room 5; Wall1(308815): net 9; opening 1.97; gross 10.97 Room 5; Exterior - Block on Mtl. Stud(311213): net 3.6; opening 0.9; gross 4.5 Room 5; Exterior - Brick on Mtl. Stud(311214): net 8.4; opening 1.1; gross 9.5 Room 5; Wall1(310409): net 9; opening 0; gross 9 Room 5; Generic - 225mm Masonry(308816): net 12; opening 0; gross 12 Room 6; Exterior - Block on Mtl. Stud(308814): net 30; opening 0; gross 30 Room 6; Wall1(308815): net 30; opening 3; gross 33 Room 6; Generic - 225mm Masonry(308816): net 30; opening 2; gross 32 Room 6; Wall3(308817): net 30; opening 2; gross 32 Room 7; Exterior - Block on Mtl. Stud(308814): net 30; opening 0; gross 30 Room 7; Wall3(308817): net 30; opening 2; gross 32 Room 7; Generic - 225mm Masonry(308816): net 30; opening 0; gross 30 Room 7; Generic - 225mm Masonry(309758): net 30; opening 0; gross 30 Net Area in m2 by Outer Layer Material Room 3; Gypsum Wall Board: net 75; opening 4; gross 79 Room 3; Default Wall: net 75; opening 5.2; gross 80.2 Room 5; Default Wall: net 18; opening 1.97; gross 19.97 Room 5; Gypsum Wall Board: net 12; opening 2; gross 14 Room 5; Concrete Masonry Units: net 12; opening 0; gross 12 Room 6; Gypsum Wall Board: net 30; opening 0; gross 30 Room 6; Default Wall: net 60; opening 5; gross 65 Room 6; Concrete Masonry Units: net 30; opening 2; gross 32 Room 7; Gypsum Wall Board: net 30; opening 0; gross 30 Room 7; Default Wall: net 30; opening 2; gross 32 Room 7; Concrete Masonry Units: net 60; opening 0; gross 60
Wonderful, isn't it?
Thank you again, Håvard!
Zhbing0322 added a comment on a commit:
The result of
spatialSubFace.GetSubface().Area
is the Gross area of the Wall in a room.