Armaturen tellen en stroomkringen controleren in Revit met Python (pyRevit)
Drie korte pyRevit-scripts voor de E-tekenaar: armaturen per type tellen, armaturen zonder kring vinden en kringen boven hun beveiliging opsporen. Plus de valkuil waar iedereen in loopt: een ElementSet is geen lijst.
Kort antwoord: armaturen haal je op met een FilteredElementCollector op BuiltInCategory.OST_LightingFixtures. De kringen van een armatuur geeft armatuur.MEPModel.GetElectricalSystems(); is die leeg, dan hangt het armatuur in geen enkele kring. Kringen zelf zijn ElectricalSystem-elementen: vergelijk ApparentCurrent met Rating en je ziet welke kring boven zijn beveiliging zit. Let op: kring.Elements is een ElementSet, geen lijst; tel met .Size.
Waar elektra in de Revit-API zit
Voor de E-tekenaar draait bijna alles om vier soorten elementen:
- Armaturen: categorie
OST_LightingFixtures. Wandcontactdozen en andere aansluitpunten zitten inOST_ElectricalFixtures. - Verdelers: categorie
OST_ElectricalEquipment, met de naam inRBS_ELEC_PANEL_NAME. - Kringen: de klasse
ElectricalSystemuitAutodesk.Revit.DB.Electrical. Een kring weet zijn verdeler (PanelName), zijn nummer (CircuitNumber), zijn omschrijving (LoadName), de stroom (ApparentCurrent) en de beveiliging (Rating). - Kabelgoten en buizen:
CableTrayenConduit, ook uitAutodesk.Revit.DB.Electrical.
Vermogen en spanning staan in Revit, net als lengtes, in interne eenheden. Lees ze daarom altijd met UnitUtils.ConvertFromInternalUnits en UnitTypeId.VoltAmperes of UnitTypeId.Volts, en nooit als kaal getal. De voorbeelden hieronder draaien op een nagebootst kassencomplex met drie afdelingen belichting, 270 armaturen en vier verdelers.
Armaturen per type tellen
De eerste vraag is bijna altijd: hoeveel armaturen van welk type, en hoeveel vermogen is dat? Dit script telt ze en telt het schijnbaar vermogen per type op:
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
armaturen = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_LightingFixtures)
.WhereElementIsNotElementType()
.ToElements())
per_type = {}
for armatuur in armaturen:
typenaam = armatuur.get_Parameter(
BuiltInParameter.ELEM_TYPE_PARAM).AsValueString()
va = armatuur.get_Parameter(
BuiltInParameter.RBS_ELEC_APPARENT_LOAD).AsDouble()
va = UnitUtils.ConvertFromInternalUnits(va, UnitTypeId.VoltAmperes)
aantal, totaal = per_type.get(typenaam, (0, 0.0))
per_type[typenaam] = (aantal + 1, totaal + va)
for typenaam in sorted(per_type):
aantal, totaal = per_type[typenaam]
print("{}: {} armaturen, {:.1f} kVA".format(
typenaam, aantal, totaal / 1000))
print("Totaal: {} armaturen".format(len(armaturen)))LED 520W 230V: 89 armaturen, 47.2 kVA
LED 645W 400V: 91 armaturen, 59.7 kVA
SON-T 1000W 400V: 90 armaturen, 96.5 kVA
Totaal: 270 armaturen- De typenaam lees je met
ELEM_TYPE_PARAMenAsValueString(). Dat werkt in elke taal van Revit, anders dan een parameter op zijn zichtbare naam zoeken. - Een dictionary met
.get(sleutel, standaard)is de kortste manier om per type op te tellen. - Opvallend: 91 armaturen "LED 645W 400V", terwijl er per afdeling 90 hangen. In het oefenmodel zit er één in de verkeerde afdeling. Precies dit soort afwijkingen vindt een telling eerder dan je oog.
Armaturen zonder kring vinden
Een armatuur dat in geen enkele kring hangt, telt niet mee in de belasting van de verdeler en ontbreekt in de groepenverklaring. GetElectricalSystems() op het MEPModel van het armatuur geeft zijn kringen; is die verzameling leeg, dan heb je er een gevonden.
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
armaturen = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_LightingFixtures)
.WhereElementIsNotElementType()
.ToElements())
zonder_kring = []
for armatuur in armaturen:
mep = armatuur.MEPModel
kringen = list(mep.GetElectricalSystems()) if mep else []
if not kringen:
zonder_kring.append(armatuur)
for armatuur in zonder_kring:
mark = armatuur.get_Parameter(
BuiltInParameter.ALL_MODEL_MARK).AsString()
typenaam = armatuur.get_Parameter(
BuiltInParameter.ELEM_TYPE_PARAM).AsValueString()
print("Zonder kring: {} ({})".format(mark, typenaam))
print("{} van de {} armaturen zonder kring".format(
len(zonder_kring), len(armaturen)))Zonder kring: A1-R2-08 (SON-T 1000W 400V)
1 van de 270 armaturen zonder kringGetElectricalSystems()geeft een .NET-verzameling. Metlist(...)eromheen kun je er in Python gewoon mee werken:if not kringen,len(kringen),kringen[0].- Niet elk element heeft een
MEPModel; daarom eerst de controleif mep. - Wil je ook wandcontactdozen nalopen, gebruik dan dezelfde lus met
OST_ElectricalFixtures.
Kringen boven hun beveiliging
Elke kring kent zijn stroom en zijn beveiliging. ApparentCurrent is de stroom in ampère: schijnbaar vermogen gedeeld door de spanning, en bij een 3-polige kring gedeeld door √3 keer de spanning. Rating is de waarde van de beveiliging in ampère. Is de stroom hoger dan de beveiliging, dan klopt er iets niet.
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Electrical import ElectricalSystem
from Autodesk.Revit.DB.Electrical import ElectricalSystemType
doc = __revit__.ActiveUIDocument.Document
te_zwaar = []
for kring in FilteredElementCollector(doc).OfClass(ElectricalSystem):
if kring.SystemType != ElectricalSystemType.PowerCircuit:
continue
if kring.ApparentCurrent > kring.Rating:
te_zwaar.append("{} kring {}: {:.2f} A op {:.0f} A".format(
kring.PanelName, kring.CircuitNumber,
kring.ApparentCurrent, kring.Rating))
for regel in sorted(te_zwaar):
print(regel)OV-A1 kring 14: 18.76 A op 16 A
OV-A2 kring 4: 8.20 A op 6 ARatingbestaat alleen bij vermogenskringen. Op een data- of brandmeldkring geeft Revit eenInvalidOperationException; daarom slaat het script alles over wat geenPowerCircuitis.- Veel bureaus houden een strengere eigen regel aan, zoals "hooguit 80 % van de beveiliging" of "hooguit vijf armaturen per kring". Zet zo'n grens als variabele bovenin het script, zodat iedereen ziet welke regel de knop toetst.
- Dit is een controle tegen jullie eigen regels, geen NEN 1010-berekening. Kabeldoorsnede, spanningsval en selectiviteit reken je definitief in je E-rekenpakket.
ElementSet of lijst: de valkuil in IronPython
De armaturen in een kring staan in kring.Elements. Dat lijkt een lijst, maar het is een ElementSet: een verzameling uit de Revit-API. Je kunt erover itereren, maar len() en [0] werken in pyRevit (IronPython) niet. Tel met .Size, of maak er eerst een lijst van:
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Electrical import ElectricalSystem
doc = __revit__.ActiveUIDocument.Document
kring = (FilteredElementCollector(doc)
.OfClass(ElectricalSystem)
.FirstElement())
leden = kring.Elements # een ElementSet, geen Python-lijst
print(leden.Size) # tellen met Size
eerste = list(leden)[0] # eerst een lijst, dan pas [0]
print(eerste.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).AsString())5
A1-R1-01Schrijf je toch len(kring.Elements), dan stopt het script met een fout als deze:
TypeError: object of type 'ElementSet' has no len()Andersom geldt hetzelfde: methodes die de kring wijzigen, willen een ElementSet en geen Python-lijst. Dit script hangt het armatuur zonder kring in de kring waar het hoort:
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Electrical import ElectricalSystem
doc = __revit__.ActiveUIDocument.Document
kringen = {}
for kring in FilteredElementCollector(doc).OfClass(ElectricalSystem):
kringen[kring.LoadName] = kring
doel = kringen["Belichting A1 R2 K2"]
nieuw = ElementSet()
for armatuur in (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_LightingFixtures)
.WhereElementIsNotElementType()):
if not list(armatuur.MEPModel.GetElectricalSystems()):
nieuw.Insert(armatuur)
t = Transaction(doc, "Armaturen aansluiten")
t.Start()
gelukt = doel.AddToCircuit(nieuw) # een ElementSet, geen Python-lijst
t.Commit()
print(gelukt)
print("{}: {} punten, {:.2f} A".format(
doel.LoadName, doel.Elements.Size, doel.ApparentCurrent))True
Belichting A1 R2 K2: 5 punten, 13.40 AAddToCircuitenRemoveFromCircuitwillen eenElementSet. Met een Python-lijst krijg je eenTypeError.AddToCircuitgeeftFalseals een armatuur niet in de kring past, bijvoorbeeld door een andere spanning of een ander aantal polen.- Een nieuwe kring maken met
ElectricalSystem.Createvraagt juist eenList[ElementId]uitSystem.Collections.Generic. Twee methodes, twee soorten verzamelingen: kijk bij twijfel in de documentatie op revitapidocs.com. - Wijzigen kan alleen in een transaction. Waarom, en wat er gebeurt als je hem vergeet, staat in "Attempt to modify the model outside of transaction".
Van script naar knop
Elk script hierboven wordt een knop op het lint als je het als script.py in een map Naam.pushbutton in je pyRevit-extensie zet; hoe dat gaat, staat in pyRevit installeren en je eerste knop maken. Drie gewoontes maken zo'n knop bruikbaar voor het hele bureau:
- Een instellingenblok bovenin met typenamen, parameternamen en grenzen. Wie de knop op een ander project gebruikt, past alleen dat blok aan.
- Geen f-strings. pyRevit gebruikt standaard IronPython 2.7; schrijf
"{}".format(waarde). - Lijsten naar Excel als CSV met een puntkomma, dan opent een Nederlandse Excel ze meteen in kolommen.
Dit is werk dat naast je E-pakket hoort: het telt, controleert en rapporteert uit het Revit-model, volgens jullie eigen regels. Het rekenen volgens de norm blijft in je E-rekenpakket.