mirror of
https://github.com/jeremytammik/RevitSdkSamples.git
synced 2026-08-06 05:01:35 +00:00
271 lines
10 KiB
C#
271 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Autodesk.Revit.DB;
|
|
using Autodesk.Revit.DB.Analysis;
|
|
|
|
namespace Revit.SDK.Samples.NetworkPressureLossReport
|
|
{
|
|
public class NetworkInfo
|
|
{
|
|
private Document m_doc;
|
|
private string m_name; // A recognizable name from design system or fabrication service.
|
|
private double m_maxFlow; // The maximum flow value of any segment on the entire network.
|
|
private string m_flow;
|
|
private ConnectorDomainType m_domainType;
|
|
private IDictionary<int, SectionInfo> m_sections;
|
|
|
|
public NetworkInfo(Document doc)
|
|
{
|
|
m_doc = doc;
|
|
m_maxFlow = 0.0;
|
|
m_flow = null;
|
|
m_domainType = ConnectorDomainType.Undefined;
|
|
m_sections = new SortedDictionary<int, SectionInfo>();
|
|
}
|
|
public int NumberOfSections
|
|
{
|
|
get { return m_sections.Count; }
|
|
}
|
|
public Document Document
|
|
{
|
|
get { return m_doc; }
|
|
}
|
|
public string Name
|
|
{
|
|
get { return m_name; }
|
|
set { m_name = value; }
|
|
}
|
|
|
|
public string FlowDisplay
|
|
{
|
|
get { return m_flow; }
|
|
}
|
|
|
|
public ConnectorDomainType DomainType
|
|
{
|
|
get { return m_domainType; }
|
|
set { m_domainType = value; }
|
|
}
|
|
|
|
public static IList<NetworkInfo> FindValidNetworks(Document doc)
|
|
{
|
|
IList<NetworkInfo> validNetworks = new List<NetworkInfo>();
|
|
|
|
HashSet<MEPNetworkSegmentId> visitedSegments = new HashSet<MEPNetworkSegmentId>(new CompareNetworkSegmentId());
|
|
|
|
// Find all elements that may drive the pipe or duct flow calculations.
|
|
List<BuiltInCategory> categories = new List<BuiltInCategory>();
|
|
categories.Add(BuiltInCategory.OST_MechanicalEquipment);
|
|
categories.Add(BuiltInCategory.OST_PlumbingEquipment);
|
|
categories.Add(BuiltInCategory.OST_DuctTerminal);
|
|
|
|
ElementMulticategoryFilter multiCatFilter = new ElementMulticategoryFilter(categories);
|
|
FilteredElementCollector elemCollector = new FilteredElementCollector(doc).WherePasses(multiCatFilter).WhereElementIsNotElementType();
|
|
foreach (Element elem in elemCollector.ToElements())
|
|
{
|
|
MEPAnalyticalModelData data = MEPAnalyticalModelData.GetMEPAnalyticalModelData(elem);
|
|
if (data == null)
|
|
continue;
|
|
|
|
int nSeg = data.GetNumberOfSegments();
|
|
for (int ii = 0; ii < nSeg; ii++)
|
|
{
|
|
MEPAnalyticalSegment seg = data.GetSegmentByIndex(ii);
|
|
MEPNetworkSegmentId idSegment = new MEPNetworkSegmentId(elem.Id, seg.Id);
|
|
if (visitedSegments.Contains(idSegment))
|
|
continue;
|
|
|
|
// Start from this analytical segment to traverse the entire network.
|
|
NetworkInfo newNetwork = new NetworkInfo(doc);
|
|
newNetwork.DomainType = seg.DomainType;
|
|
|
|
// First start from the side of the start node.
|
|
MEPAnalyticalNode startNode = data.GetNodeById(seg.StartNode);
|
|
MEPNetworkIterator iter = new MEPNetworkIterator(doc, startNode, seg);
|
|
for (iter.Start(); !iter.End(); iter.Next())
|
|
{
|
|
MEPAnalyticalSegment currentSegment = iter.GetAnalyticalSegment();
|
|
if (currentSegment == null)
|
|
continue;
|
|
|
|
MEPAnalyticalModelData currentModelData = iter.GetAnalyticalModelData();
|
|
if (currentModelData == null)
|
|
continue;
|
|
|
|
// Mark this segment so not to create the duplicate network.
|
|
MEPNetworkSegmentId currentSegmentId = new MEPNetworkSegmentId(currentSegment.RevitElementId, currentSegment.Id);
|
|
visitedSegments.Add(currentSegmentId);
|
|
|
|
MEPNetworkSegmentData segFlowData = currentModelData.GetSegmentData(currentSegment.Id);
|
|
// Grow the network information by filling in one segment.
|
|
newNetwork.AddSegment(currentSegment, segFlowData);
|
|
|
|
// Refine the network name based on the straight segments.
|
|
if (currentSegment.SegmentType == MEPAnalyticalSegmentType.Segment)
|
|
newNetwork.RefineName(currentSegment.RevitElementId);
|
|
}
|
|
|
|
// If this is not a close loop, we must include the other side as well!
|
|
MEPAnalyticalNode endNode = data.GetNodeById(seg.EndNode);
|
|
iter = new MEPNetworkIterator(doc, endNode, seg);
|
|
for (iter.Start(); !iter.End(); iter.Next())
|
|
{
|
|
MEPAnalyticalSegment currentSegment = iter.GetAnalyticalSegment();
|
|
if (currentSegment == null)
|
|
continue;
|
|
MEPNetworkSegmentId currentSegmentId = new MEPNetworkSegmentId(currentSegment.RevitElementId, currentSegment.Id);
|
|
// Check if the segment was already visited.
|
|
if (visitedSegments.Contains(currentSegmentId))
|
|
continue;
|
|
visitedSegments.Add(currentSegmentId);
|
|
|
|
MEPAnalyticalModelData currentModelData = iter.GetAnalyticalModelData();
|
|
if (currentModelData == null)
|
|
continue;
|
|
MEPNetworkSegmentData segFlowData = currentModelData.GetSegmentData(currentSegment.Id);
|
|
newNetwork.AddSegment(currentSegment, segFlowData);
|
|
if (currentSegment.SegmentType == MEPAnalyticalSegmentType.Segment)
|
|
newNetwork.RefineName(currentSegment.RevitElementId);
|
|
}
|
|
|
|
// Collect this new network
|
|
if (newNetwork.NumberOfSections > 0)
|
|
{
|
|
newNetwork.ResetFlowDisplay();
|
|
validNetworks.Add(newNetwork);
|
|
}
|
|
}
|
|
}
|
|
return validNetworks;
|
|
}
|
|
|
|
public void AddSegment(MEPAnalyticalSegment segment, MEPNetworkSegmentData segmentData)
|
|
{
|
|
int sectionNumber = segmentData.SectionNumber;
|
|
// The equipment segment may not belong to any section. Skip those?!
|
|
if (sectionNumber < 0)
|
|
return;
|
|
|
|
double segmentFlow = Math.Abs(segmentData.Flow);
|
|
if (segmentFlow > m_maxFlow)
|
|
m_maxFlow = segmentFlow;
|
|
|
|
if (!m_sections.ContainsKey(sectionNumber))
|
|
{
|
|
m_sections.Add(sectionNumber, new SectionInfo());
|
|
}
|
|
SegmentInfo newSegmentInfo = m_sections[sectionNumber].AddSegment(m_doc, segment, segmentData);
|
|
}
|
|
|
|
private void RefineName(ElementId idElem)
|
|
{
|
|
Element elem = m_doc.GetElement(idElem);
|
|
MEPCurve aCurve = elem as MEPCurve;
|
|
if (aCurve != null)
|
|
{
|
|
MEPSystem sys = aCurve.MEPSystem;
|
|
if (sys != null)
|
|
{
|
|
AppendName(sys.Name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FabricationPart aPart = elem as FabricationPart;
|
|
if (aPart != null)
|
|
{
|
|
string serviceName = aPart.ServiceName;
|
|
// Get the full name of fabrication service by its id.
|
|
FabricationConfiguration fabConfig = FabricationConfiguration.GetFabricationConfiguration(m_doc);
|
|
if (fabConfig != null)
|
|
{
|
|
FabricationService fabService = fabConfig.GetService(aPart.ServiceId);
|
|
if (fabService != null)
|
|
{
|
|
serviceName = fabService.Name;
|
|
}
|
|
}
|
|
AppendName(serviceName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AppendName(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(m_name))
|
|
{
|
|
m_name = name;
|
|
}
|
|
else
|
|
{
|
|
if (!m_name.Contains(name))
|
|
{
|
|
m_name += " + " + name;
|
|
}
|
|
}
|
|
}
|
|
private void ResetFlowDisplay()
|
|
{
|
|
ForgeTypeId specId = SpecTypeId.Flow;
|
|
if (DomainType == ConnectorDomainType.Hvac)
|
|
specId = SpecTypeId.AirFlow;
|
|
|
|
// Reset the flow display value based on the maximum number after adding all segments.
|
|
m_flow = UnitFormatUtils.Format(m_doc.GetUnits(), specId, m_maxFlow, false);
|
|
}
|
|
|
|
public void ExportCSV(CSVExporter ex)
|
|
{
|
|
// Export the header line.
|
|
string str = string.Format("Section, Type/No, Element, Flow ({0}), Size/Hydraulic Diameter ({1}), Velocity ({2}), Velocity Pressure ({3}), Length ({4}), Coefficients, Friction ({5}), Pressure Loss ({3}), Section Pressure Loss ({3})",
|
|
ex.GetFlowUnitSymbol(), ex.GetSizeUnitSymbol(), ex.GetVelocityUnitSymbol(), ex.GetPressureUnitSymbol(), ex.GetLengthUnitSymbol(), ex.GetFrictionUnitSymbol());
|
|
ex.Writer.WriteLine(str);
|
|
foreach (var item in m_sections)
|
|
{
|
|
item.Value.ExportCSV(ex, item.Key);
|
|
}
|
|
// Critical path if available
|
|
double dCriticalLoss = 0.0;
|
|
string path = null;
|
|
foreach(var item in m_sections)
|
|
{
|
|
if(item.Value.IsCriticalPath)
|
|
{
|
|
dCriticalLoss += item.Value.TotalPressureLoss;
|
|
if(string.IsNullOrEmpty(path))
|
|
{
|
|
path = item.Key.ToString();
|
|
}
|
|
else
|
|
{
|
|
path += @" - " + item.Key.ToString();
|
|
}
|
|
}
|
|
}
|
|
ex.Writer.WriteLine(string.Format("Critical Pressure Loss: {0}, {1}", path, dCriticalLoss));
|
|
}
|
|
public void UpdateView(AVFViewer viewer)
|
|
{
|
|
List<XYZ> points = new List<XYZ>();
|
|
List<VectorAtPoint> valList = new List<VectorAtPoint>();
|
|
|
|
// Safety check.
|
|
if (m_maxFlow < 0.0000001)
|
|
return;
|
|
|
|
foreach (var item in m_sections)
|
|
{
|
|
int sectionNum = item.Key;
|
|
item.Value.UpdateView(viewer, points, valList, m_maxFlow);
|
|
}
|
|
|
|
if(points.Count > 0)
|
|
{
|
|
viewer.AddData(points, valList);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|