From b5d97f3173bcad9e634821371c63934236b15ae2 Mon Sep 17 00:00:00 2001 From: Erik Narhi Date: Mon, 2 Dec 2024 15:13:12 -0800 Subject: [PATCH] Added continuous UValue calc to SAM method and Overarching UValue Methods --- Facade_Engine/Compute/UValueAW.cs | 20 +++-- Facade_Engine/Compute/UValueOpeningSAM.cs | 8 ++ Facade_Engine/Compute/UValuePanelAW.cs | 92 ++++++++++++-------- Facade_Engine/Compute/UValuePanelSAM.cs | 100 ++++++++++++++++++++++ Facade_Engine/Compute/UValueSAM.cs | 100 ++++++++++++++++++++++ 5 files changed, 276 insertions(+), 44 deletions(-) create mode 100644 Facade_Engine/Compute/UValuePanelSAM.cs create mode 100644 Facade_Engine/Compute/UValueSAM.cs diff --git a/Facade_Engine/Compute/UValueAW.cs b/Facade_Engine/Compute/UValueAW.cs index af97cbd28..706a62703 100644 --- a/Facade_Engine/Compute/UValueAW.cs +++ b/Facade_Engine/Compute/UValueAW.cs @@ -55,28 +55,36 @@ public static OverallUValue UValueAW(this List objs) foreach (IFacadeObject obj in objs) { double area = 0; - double uValue = 0; + double? uValue = 0; if (obj is Panel panel) { area = panel.Area(); - uValue = UValuePanelAW(panel).UValue; + uValue = UValuePanelAW(panel)?.UValue; } else if (obj is Opening opening) { area = opening.Area(); - uValue = UValueOpeningAW(opening).UValue; + uValue = UValueOpeningAW(opening)?.UValue; } else { Base.Compute.RecordWarning($"Object {obj.BHoM_Guid} is of a type currently not supported for UValue methods. It has been excluded from the calculation."); continue; } - uValueProduct += uValue * area; + + if (uValue == null) + { + Base.Compute.RecordWarning($"UValue calculation failed for Object {obj.BHoM_Guid}. It has been excluded from the calculation."); + continue; + } + + uValueProduct += uValue.Value * area; totalArea += area; } - if (totalArea == 0) + + if (totalArea == 0 || uValueProduct == 0) { - Base.Compute.RecordError("Objects have a total calculated area of 0. Ensure Objects are valid with associated edges defining their geometry and try again."); + Base.Compute.RecordError("No valid objects for UValue calculation were provided. Ensure Objects are valid with required UValue properties and associated edges defining their geometry and try again."); return null; } diff --git a/Facade_Engine/Compute/UValueOpeningSAM.cs b/Facade_Engine/Compute/UValueOpeningSAM.cs index 66b3d773e..739597074 100644 --- a/Facade_Engine/Compute/UValueOpeningSAM.cs +++ b/Facade_Engine/Compute/UValueOpeningSAM.cs @@ -68,8 +68,16 @@ public static OverallUValue UValueOpeningSAM(this Opening opening) BH.Engine.Base.Compute.RecordError($"Opening {opening.BHoM_Guid} has more than one U-value assigned."); return null; } + double uValue = (uValues[0] as UValueGlassCentre).UValue; + List uValuesCont = opening.OpeningConstruction.GetAllFragments(typeof(UValueContinuous)); + if (uValuesCont.Count == 1) + { + double contUValue = (uValuesCont[0] as UValueContinuous).UValue; + uValue = 1 / (1 / uValue + 1 / contUValue); + } + List frameEdges = opening.Edges; List psiValues = new List(); List lengths = new List(); diff --git a/Facade_Engine/Compute/UValuePanelAW.cs b/Facade_Engine/Compute/UValuePanelAW.cs index e46c88386..4ad49c219 100644 --- a/Facade_Engine/Compute/UValuePanelAW.cs +++ b/Facade_Engine/Compute/UValuePanelAW.cs @@ -35,6 +35,7 @@ using BH.oM.Base.Attributes; using System.ComponentModel; +using BH.oM.Physical.Constructions; namespace BH.Engine.Facade { @@ -55,22 +56,65 @@ public static OverallUValue UValuePanelAW(this Panel panel) return null; } - List glassUValues = panel.Construction.GetAllFragments(typeof(UValueGlassCentre)); + double panelArea = panel.Area(); + if (panelArea == 0) + { + BH.Engine.Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has a calculated area of 0. Ensure the panel is valid with associated edges defining its geometry and try again."); + return null; + } + + double panelUValue = panel.PanelEffectiveUValue(); + if (panelUValue == double.NaN) + return null; + + double effectiveUValue = panelUValue; + List panelOpenings = panel.Openings; + if (panelOpenings.Count > 0) + { + double uValueProduct = panelUValue * panelArea; + double totalArea = panelArea; + foreach (Opening opening in panelOpenings) + { + double area = opening.Area(); + uValueProduct += opening.UValueOpeningAW().UValue * area; + totalArea += area; + } + if (totalArea == 0) + { + Base.Compute.RecordError("Openings have a total calculated area of 0. Ensure Openings are valid with associated edges defining their geometry and try again."); + return null; + } + effectiveUValue = uValueProduct / totalArea; + } + + OverallUValue result = new OverallUValue(effectiveUValue, new List { panel.BHoM_Guid }); + return result; + } + + /***************************************************/ + /**** Helper Methods ****/ + /***************************************************/ + + private static double PanelEffectiveUValue(this Panel panel) + { + IConstruction construction = panel.Construction; + + List glassUValues = construction.GetAllFragments(typeof(UValueGlassCentre)); if (glassUValues.Count > 0) { BH.Engine.Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has Glass U-value assigned. Panels can only receive Continuous U-value and/or Cavity U-value."); - return null; + return double.NaN; } - List glassEdgeUValues = panel.Construction.GetAllFragments(typeof(UValueGlassEdge)); + List glassEdgeUValues = construction.GetAllFragments(typeof(UValueGlassEdge)); if (glassEdgeUValues.Count > 0) { BH.Engine.Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has Glass edge U-value assigned. Panels can only receive Continuous U-value"); - return null; + return double.NaN; } - List contUValues = panel.Construction.GetAllFragments(typeof(UValueContinuous)); - List cavityUValues = panel.Construction.GetAllFragments(typeof(UValueCavity)); + List contUValues = construction.GetAllFragments(typeof(UValueContinuous)); + List cavityUValues = construction.GetAllFragments(typeof(UValueCavity)); double contUValue = 0; double cavityUValue = 0; double panelUValue = 0; @@ -78,17 +122,17 @@ public static OverallUValue UValuePanelAW(this Panel panel) if ((contUValues.Count <= 0) && (cavityUValues.Count <= 0)) { Base.Compute.RecordError($"Panel {panel.BHoM_Guid} does not have Continuous U-value or Cavity U-value assigned."); - return null; + return double.NaN; } if (contUValues.Count > 1) { Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has more than one Continuous U-value assigned."); - return null; + return double.NaN; } if (cavityUValues.Count > 1) { Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has more than one Cavity U-value assigned."); - return null; + return double.NaN; } if ((contUValues.Count == 1) && (cavityUValues.Count == 1)) { @@ -118,37 +162,9 @@ public static OverallUValue UValuePanelAW(this Panel panel) continue; } } - - double panelArea = panel.Area(); - if (panelArea == 0) - { - BH.Engine.Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has a calculated area of 0. Ensure the panel is valid with associated edges defining its geometry and try again."); - } - double effectiveUValue = panelUValue; - - List panelOpenings = panel.Openings; - if (panelOpenings.Count > 0) - { - double uValueProduct = panelUValue * panelArea; - double totalArea = panelArea; - foreach (Opening opening in panelOpenings) - { - double area = opening.Area(); - uValueProduct += opening.UValueOpeningAW().UValue * area; - totalArea += area; - } - if (totalArea == 0) - { - Base.Compute.RecordError("Openings have a total calculated area of 0. Ensure Openings are valid with associated edges defining their geometry and try again."); - return null; - } - effectiveUValue = uValueProduct / totalArea; - } - OverallUValue result = new OverallUValue(effectiveUValue, new List { panel.BHoM_Guid }); - return result; + return panelUValue; } - /***************************************************/ } } diff --git a/Facade_Engine/Compute/UValuePanelSAM.cs b/Facade_Engine/Compute/UValuePanelSAM.cs new file mode 100644 index 000000000..e9eefbaf0 --- /dev/null +++ b/Facade_Engine/Compute/UValuePanelSAM.cs @@ -0,0 +1,100 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Geometry; +using BH.oM.Dimensional; +using System; +using System.Collections.Generic; +using System.Linq; +using BH.oM.Facade.Elements; +using BH.oM.Base; +using BH.Engine.Geometry; +using BH.Engine.Spatial; +using BH.Engine.Base; +using BH.oM.Facade.Fragments; +using BH.oM.Facade.Results; + +using BH.oM.Base.Attributes; +using System.ComponentModel; +using BH.oM.Facade.SectionProperties; +using BH.oM.Quantities.Attributes; + +namespace BH.Engine.Facade +{ + public static partial class Compute + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Returns effective U - Value of a panel calculated using the Single Assessment Method(Using Psi-tj). Requires center of opening U-value and frame Psi-tj value as OpeningConstruction and FrameEdgeProperty fragments on any Openings within the Panel.")] + [Input("panel", "Panel to find U-value for.")] + [Output("effectiveUValue", "Effective U-value result of panel calculated using SAM.")] + public static OverallUValue UValuePanelSAM(this Panel panel) + { + if (panel == null) + { + Base.Compute.RecordError($"U-Value can not be calculated for null panel."); + return null; + } + + double panelArea = panel.Area(); + if (panelArea == 0) + { + BH.Engine.Base.Compute.RecordError($"Panel {panel.BHoM_Guid} has a calculated area of 0. Ensure the panel is valid with associated edges defining its geometry and try again."); + return null; + } + + double panelUValue = panel.PanelEffectiveUValue(); + if (panelUValue == double.NaN) + return null; + + Base.Compute.RecordNote("Panels assessed using SAM method will use SAM method for any contained Openings, but will only assess the Panel itself based on its assigned Continuous and/or Cavity U Values."); + double effectiveUValue = panelUValue; + List panelOpenings = panel.Openings; + if (panelOpenings.Count > 0) + { + double uValueProduct = panelUValue * panelArea; + double totalArea = panelArea; + foreach (Opening opening in panelOpenings) + { + double area = opening.Area(); + uValueProduct += opening.UValueOpeningSAM().UValue * area; + totalArea += area; + } + if (totalArea == 0) + { + Base.Compute.RecordError("Openings have a total calculated area of 0. Ensure Openings are valid with associated edges defining their geometry and try again."); + return null; + } + effectiveUValue = uValueProduct / totalArea; + } + + OverallUValue result = new OverallUValue(effectiveUValue, new List { panel.BHoM_Guid }); + return result; + } + + /***************************************************/ + + } +} + diff --git a/Facade_Engine/Compute/UValueSAM.cs b/Facade_Engine/Compute/UValueSAM.cs new file mode 100644 index 000000000..67d2b3ba3 --- /dev/null +++ b/Facade_Engine/Compute/UValueSAM.cs @@ -0,0 +1,100 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Geometry; +using BH.oM.Dimensional; +using System; +using System.Collections.Generic; +using System.Linq; +using BH.oM.Facade; +using BH.oM.Facade.Elements; +using BH.oM.Base; +using BH.Engine.Geometry; +using BH.Engine.Spatial; +using BH.Engine.Base; +using BH.oM.Facade.Fragments; + +using BH.oM.Base.Attributes; +using BH.oM.Facade.Results; +using System.ComponentModel; + +namespace BH.Engine.Facade +{ + public static partial class Compute + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Returns effective U-Value of a collection of Facade objects calculated using the Single Assessment Method (Using Psi-tj). Requires center of opening U-value and frame Psi-tj value as OpeningConstruction and FrameEdgeProperty fragments.")] + [Input("objs", "Objects to find U-value for.")] + [Output("effectiveUValue", "Effective total U-value result of objects calculated using area weighting.")] + public static OverallUValue UValueSAM(this List objs) + { + double uValueProduct = 0; + double totalArea = 0; + foreach (IFacadeObject obj in objs) + { + double area = 0; + double? uValue = 0; + if (obj is Panel panel) + { + area = panel.Area(); + uValue = UValuePanelSAM(panel)?.UValue; + } + else if (obj is Opening opening) + { + area = opening.Area(); + uValue = UValueOpeningSAM(opening)?.UValue; + } + else + { + Base.Compute.RecordWarning($"Object {obj.BHoM_Guid} is of a type currently not supported for UValue methods. It has been excluded from the calculation."); + continue; + } + + if (uValue == null) + { + Base.Compute.RecordWarning($"UValue calculation failed for Object {obj.BHoM_Guid}. It has been excluded from the calculation."); + continue; + } + + uValueProduct += uValue.Value * area; + totalArea += area; + } + + if (totalArea == 0 || uValueProduct == 0) + { + Base.Compute.RecordError("No valid objects for UValue calculation were provided. Ensure Objects are valid with required UValue properties and associated edges defining their geometry and try again."); + return null; + } + + double effectiveUValue = uValueProduct / totalArea; + OverallUValue result = new OverallUValue(effectiveUValue, objs.Select(x => x.BHoM_Guid as IComparable).ToList()); + return result; + } + + /***************************************************/ + + } +} +