30 sept. 2026

[Dynamo + Python] Revit 2027 : HVAC Zone → GenericZone (Migration API)

 





Comment retrouver les zones systèmes (ex CVC) qui sont attachées aux espaces avec Revit 2027 


#DynamoBIM #Revit #Python #RevitAPI #GenericZone #Revit API 2027     #AutodeskExpertElite #AutodeskCommunity 

if this article is not in your language, use the Google Translate widget ⬈ (bottom of page for Mobile version ⬇)




Les Zones Système (GenericZones) viennent remplacer les Zones HVAC . 

Sur des projets existants, elles sont désormais automatiquement converties lors de l’ouverture des projets Revit 2027.

Parmi les changements notables :

  • les HVAC Zones deviennent obsolètes

  • Les espaces MEP peuvent être situés dans plusieurs zones. 

  • Les zones dépendent d'un seul niveau (impossibilité de créer une zone sur plusieurs niveaux).

  • Les zones peuvent être :
      • soit attaché aux espaces
      • soit basées sur une esquisse

  • Les Zones Système (GenericZones) sont des zones analytiques.



Il en résulte plusieurs modifications dans l'API

Migration / Item Details
Propriété et BIC dépréciés Autodesk.Revit.DB.Mechanical.Space.Zone, BuiltInCategory:OST_HVAC_Zones
a utilisé en remplacement GenericZone + GetSpaceIds()



Voici un exemple qui combine deux méthodes pour retrouver les espaces, tout en stockant le tout dans une Datatable pour faire une recherche rapide par ElementId via Select() 

1. Zones basées sur des espaces

La méthode la plus rapide consiste à utiliser :

spaceIds = genericZone.GetSpaceIds()

Chaque ElementId correspond à un espace associé à la zone.

2. Zones basées sur une esquisse

Les zones créées à partir d'un contour ne possèdent pas d'association directe avec les espaces.

L'alternative consiste à :
      • récupérer le polygone de la zone 
      • récupérer le point de calcul de chaque espace et vérifier si ce dernier est dans le polygone (avec l'API Dynamo  Polygon.ContainmentTest() )


import clr
import System
from System.Collections.Generic import List
from System.Linq import Enumerable
from System.Collections.Generic import IEnumerable

clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS

clr.AddReference('RevitAPI')
import Autodesk
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Analysis import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)

clr.AddReference('System.Data')
from System.Data import *

clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)


import traceback
input_space = UnwrapElement(IN[0])

fecSpace = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_MEPSpaces).WhereElementIsNotElementType().ToElements()

def get_ZoneGetSpaceIds(zone):
    try:
        if zone.GeometricDefinition == ZoneGeometricDefinition.Spaces:
            return zone.GetSpaceIds()
        else:
            zone_sketch = doc.GetElement(zone.GetSketchId())
            profile : DB.CurveArrArray = zone_sketch.Profile
            zone_polygon = DS.Polygon.ByPoints([c.GetEndPoint(0).ToPoint() for c in profile[0]])
            level1Filter = ElementLevelFilter(zone.LevelId)
            fecLvlSpaceId = (
                FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_MEPSpaces).WhereElementIsNotElementType().WherePasses(level1Filter)
                .Where(System.Func[System.Object, System.Boolean](lambda space: space.Area > 0 and isinstance(space.Location, DB.LocationPoint)) )
                .Where(System.Func[System.Object, System.Boolean](lambda space: zone_polygon.ContainmentTest(space.Location.Point.ToPoint() ) ))
                .Select[System.Object, DB.ElementId](System.Func[System.Object, DB.ElementId](lambda x: x.Id))
                .ToList()
            )
            return fecLvlSpaceId
    except Exception as ex:
        print(traceback.format_exc())
        return List[ElementId]()

if sdkNumber < 2027:
    OUT = [[s.Name , s.Zone.Name ] for s in fecSpace]
else:
    # Global variable DataTable                                
    dt_zone = DataTable("GZone")
    dt_zone.Columns.Add('SpaceId', DB.ElementId)
    dt_zone.Columns.Add('SpaceIdValue', System.Int64)
    dt_zone.Columns.Add('Zone', DB.Analysis.GenericZone)     
    dt_zone.Columns.Add('ZoneName', System.String)  
    #
    dt_zone.BeginLoadData()
    lst_zones = (
                FilteredElementCollector(doc).OfClass(GenericZone)
                .SelectMany[DB.Element, DB.ElementId, DataRow](
                                                System.Func[DB.Element, IEnumerable[DB.ElementId]](lambda z : get_ZoneGetSpaceIds(z) ),
                                                System.Func[DB.Element, DB.ElementId, DataRow](lambda z, space_id : dt_zone.Rows.Add(space_id, space_id.Value, z, z.Name)
                                                ))
                .ToList()
    )
    
    dt_zone.EndLoadData()
    #
    #OUT = [[s.Name , dt_zone.Select(f"[SpaceIdValue] = '{s.Id.Value}'").Cast[DataRow]().FirstOrDefault() ] for s in fecSpace]
    # 
    out = []
    for s in fecSpace:
        dataRow_spaces = dt_zone.Select(f"[SpaceIdValue] = '{s.Id.Value}'").Cast[DataRow]()#.FirstOrDefault() 
        if dataRow_spaces is not None:
            out.append([s.Name, [r['ZoneName'] for r in dataRow_spaces] ])
            
    if input_space is not None:
        dataRow_spaces = dt_zone.Select(f"[SpaceIdValue] = '{input_space.Id.Value}'").Cast[DataRow]()
        print(len(list(dataRow_spaces)))
        dataRow_ZDM = dt_zone.Select(f"[SpaceIdValue] = '{input_space.Id.Value}' AND [ZoneName] Like '%ZDM%'").Cast[DataRow]().FirstOrDefault() 
        dataRow_ZDA = dt_zone.Select(f"[SpaceIdValue] = '{input_space.Id.Value}' AND [ZoneName] Like '%ZDA%'").Cast[DataRow]().FirstOrDefault() 
        if dataRow_spaces is not None:
            out.insert(0, [
                            input_space.Name, 
                            [r['ZoneName'] for r in dataRow_spaces], 
                            dataRow_ZDM['ZoneName'] if dataRow_ZDM is not None else None,
                            dataRow_ZDA['ZoneName'] if dataRow_ZDA is not None else None
                        ])
    OUT = out
. .


« Ce n’est pas que nous ayons peu de temps, c’est que nous en perdons beaucoup. »
Sénèque
...

0 commentaires:

Enregistrer un commentaire