Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Physical_Engine: Make physical surfaces return material and volume of all content #3008

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 20 additions & 43 deletions Physical_Engine/Query/MaterialComposition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
using BH.oM.Physical.FramingProperties;
using BH.oM.Physical.Constructions;
using BH.oM.Quantities.Attributes;
using BH.Engine.Matter;

namespace BH.Engine.Physical
{
Expand Down Expand Up @@ -67,19 +68,11 @@ public static MaterialComposition MaterialComposition(this IFramingElement frami
[Output("materialComposition", "The kind of matter the ISurface is composed of and in which ratios.")]
public static MaterialComposition MaterialComposition(this ISurface surface)
{
if (surface == null)
{
BH.Engine.Base.Compute.RecordError("Cannot query the material composition of a null surface.");
return null;
}

if (surface.Construction == null)
{
Engine.Base.Compute.RecordError("The MaterialComposition could not be queried as no IConstruction has been assigned to the ISurface.");
VolumetricMaterialTakeoff takeoff = surface.IVolumetricMaterialTakeoff();
if (takeoff == null)
return null;
}

return surface.Construction.IMaterialComposition();
return Matter.Create.MaterialComposition(takeoff);
}

/***************************************************/
Expand All @@ -89,36 +82,11 @@ public static MaterialComposition MaterialComposition(this ISurface surface)
[Output("materialComposition", "The kind of matter the IOpening is composed of and in which ratios.")]
public static MaterialComposition MaterialComposition(this IOpening opening)
{
MaterialComposition materialComposition = null;
if (opening is Window)
{
if ((opening as Window).Construction == null)
{
Engine.Base.Compute.RecordError("The IOpening MaterialComposition could not be calculated as no IConstruction has been assigned.");
return null;
}

materialComposition = (opening as Window).Construction.IMaterialComposition();
}

if (opening is Door)
{
if ((opening as Door).Construction == null)
{
Engine.Base.Compute.RecordError("The IOpening MaterialComposition could not be calculated as no IConstruction has been assigned.");
return null;
}

materialComposition = (opening as Door).Construction.IMaterialComposition();
}

if (opening is BH.oM.Physical.Elements.Void)
{
Engine.Base.Compute.RecordError("Void's do not support constructions and therefore, contain no material composition. Returning null.");
VolumetricMaterialTakeoff takeoff = opening.IVolumetricMaterialTakeoff();
if (takeoff == null)
return null;
}

return materialComposition;
return Matter.Create.MaterialComposition(takeoff);
}

/***************************************************/
Expand Down Expand Up @@ -201,22 +169,31 @@ private static MaterialComposition MaterialComposition(this Construction prop)
{
if (prop == null)
{
Compute.RecordError("Cannot evaluate MaterialComposition because the Construction was null.");
Base.Compute.RecordError("Cannot evaluate MaterialComposition because the Construction was null.");
return null;
}

if (prop.Layers.IsNullOrEmpty()) //.IsNullOrEmpty raises it's own error
if (prop.Layers == null) //.IsNullOrEmpty raises it's own error
{
Base.Compute.RecordError("Cannote evaluate MaterialComposition because the layers are null.");
return null;
}

if (prop.Layers.Count == 0)
{
Base.Compute.RecordWarning($"Construction {(string.IsNullOrEmpty(prop.Name) ? "NoName" : prop.Name)} does not conatin any layers. An empty MaterialComposition is returned in its place.");
return new MaterialComposition(new List<Material>(), new List<double>());
}

if (prop.Layers.All(x => x.Material == null))
{
Compute.RecordError("Cannote evaluate MaterialComposition because all of the materials are null.");
Base.Compute.RecordError("Cannote evaluate MaterialComposition because all of the materials are null.");
return null;
}

if (prop.Layers.Any(x => x.Material == null))
{
Compute.RecordWarning("At least one Material in a Layered surface property was null. MaterialConstruction excludes this layer, assuming it is void space.");
Base.Compute.RecordWarning("At least one Material in a Layered surface property was null. MaterialConstruction excludes this layer, assuming it is void space.");
}

IEnumerable<Layer> layers = prop.Layers.Where(x => x.Material != null);
Expand Down
48 changes: 10 additions & 38 deletions Physical_Engine/Query/SolidVolume.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
using BH.oM.Base;
using BH.oM.Quantities.Attributes;
using BH.Engine.Reflection;
using BH.oM.Physical.Materials;
using BH.Engine.Matter;

namespace BH.Engine.Physical
{
Expand Down Expand Up @@ -70,23 +72,11 @@ public static double SolidVolume(this IFramingElement framingElement)
[Output("volume", "The ISurface's solid material volume.", typeof(Volume))]
public static double SolidVolume(this oM.Physical.Elements.ISurface surface)
{
if (surface == null)
{
BH.Engine.Base.Compute.RecordError("Cannot query the solid volume of a null surface.");
return 0;
}

if (surface.Construction == null)
{
Engine.Base.Compute.RecordError("The ISurface Solid Volume could not be calculated as no IConstruction has been assigned. Returning zero volume.");
return 0;
}
VolumetricMaterialTakeoff takeoff = surface.IVolumetricMaterialTakeoff();
if (takeoff == null)
return double.NaN;

if (surface.Offset != Offset.Centre && !surface.Location.IIsPlanar())
Base.Compute.RecordWarning("The SolidVolume for non-Planar ISurfaces with offsets other than Centre is approxamite at best");
double area = surface.Location.IArea();
area -= surface.Openings.Sum(x => x.Location.IArea());
return area * surface.Construction.IVolumePerArea();
return takeoff.SolidVolume();
}

/***************************************************/
Expand Down Expand Up @@ -140,29 +130,11 @@ public static double SolidVolume(this ExplicitBulk explicitBulk)
[Output("volume", "The window's solid material volume.", typeof(Volume))]
public static double SolidVolume(this IOpening opening)
{
if (opening is BH.oM.Physical.Elements.Void)
{
Engine.Base.Compute.RecordError("Voids contain no solid volume. Try querying the desired value another way.");
return 0;
}

double area = opening.IArea();
double thickness = 0;

if (opening is Window)
thickness = (opening as Window).Construction.IVolumePerArea();
else if (opening is Door)
thickness = (opening as Door).Construction.IVolumePerArea();

double solidVolume = area * thickness;

if (solidVolume <= 0)
{
Engine.Base.Compute.RecordError("Solid volume cannot be calculated for element of type :" + opening.GetType() + ". Returning zero volume.");
return 0;
}
VolumetricMaterialTakeoff takeoff = opening.IVolumetricMaterialTakeoff();
if (takeoff == null)
return double.NaN;

return solidVolume;
return takeoff.SolidVolume();
}

/***************************************************/
Expand Down
180 changes: 180 additions & 0 deletions Physical_Engine/Query/VolumetricMaterialTakeoff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2023, 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.Engine.Geometry;
using BH.Engine.Spatial;
using BH.oM.Base;
using BH.oM.Base.Attributes;
using BH.oM.Physical.Constructions;
using BH.oM.Physical.Elements;
using BH.oM.Physical.Materials;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace BH.Engine.Physical
{
public static partial class Query
{
/***************************************************/
/**** Public Methods ****/
/***************************************************/

[Description("Gets the volumetric material takeoff from the ISurface object. The takeoff will contain materials and volumes from the surface itself, as well as takeoffs from any openings, such as doors and windows.")]
[Input("surface", "The physical surface object to extract the volumetric material takeoff from.")]
[Output("volTakeoff", "The volumetric material takeoff based on buildup of the surface object as well as any of its inner objects, such as doors and windows.")]
public static VolumetricMaterialTakeoff VolumetricMaterialTakeoff(this ISurface surface)
{
if (surface == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a null {nameof(ISurface)}.");
return null;
}

if (surface.Location == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a {nameof(ISurface)} with null geometry.");
return null;
}

if (surface.Construction == null)
{
Engine.Base.Compute.RecordError($"The {nameof(VolumetricMaterialTakeoff)} could not be queried as no {nameof(IConstruction)} has been assigned to the {nameof(ISurface)}.");
return null;
}

double openingAreas = 0;

List<VolumetricMaterialTakeoff> takeoffs = new List<VolumetricMaterialTakeoff>();

foreach (IOpening opening in surface.Openings)
{
VolumetricMaterialTakeoff opeingTakeoff = opening.IVolumetricMaterialTakeoff();
if (opeingTakeoff == null)
return null;

takeoffs.Add(opeingTakeoff);
openingAreas += opening.Area();

}
takeoffs.Add(TakeOff(surface.Location.IArea() - openingAreas, surface.Construction));

return Matter.Compute.AggregateVolumetricMaterialTakeoff(takeoffs);
}

/***************************************************/

[Description("Gets the volumetric material takeoff from the Void object. This will always return an empty takeoff as a void represent an area of no materiality.")]
[Input("opening", "The physical opening object to extract the volumetric material takeoff from.")]
[Output("volTakeoff", "The volumetric material takeoff for the opening. For a void this will alwys be an empty Material Takeoff.")]
public static VolumetricMaterialTakeoff VolumetricMaterialTakeoff(this BH.oM.Physical.Elements.Void opening)
{
//Voids represent empty space, hence returning a completely empty composition
return new VolumetricMaterialTakeoff(new List<Material>(), new List<double>());
}

/***************************************************/

[Description("Gets the volumetric material takeoff from the Window.")]
[Input("opening", "The physical opening object to extract the volumetric material takeoff from.")]
[Output("volTakeoff", "The volumetric material takeoff for the opening, made of up the volume and materiality of the construction and surface area.")]
public static VolumetricMaterialTakeoff VolumetricMaterialTakeoff(this Window opening)
{
if (opening == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a null {nameof(Window)}.");
return null;
}

if (opening.Location == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a {nameof(Window)} with null geometry.");
return null;
}

if (opening.Construction == null)
{
Engine.Base.Compute.RecordError($"The {nameof(VolumetricMaterialTakeoff)} could not be queried as no {nameof(IConstruction)} has been assigned to the {nameof(Window)}.");
return null;
}

return TakeOff(opening.Location.IArea(), opening.Construction);
}

/***************************************************/

[Description("Gets the volumetric material takeoff from the Door.")]
[Input("opening", "The physical opening object to extract the volumetric material takeoff from.")]
[Output("volTakeoff", "The volumetric material takeoff for the opening, made of up the volume and materiality of the construction and surface area.")]
public static VolumetricMaterialTakeoff VolumetricMaterialTakeoff(this Door opening)
{
if (opening == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a null {nameof(Door)}.");
return null;
}

if (opening.Location == null)
{
BH.Engine.Base.Compute.RecordError($"Cannot query the {nameof(VolumetricMaterialTakeoff)} of a {nameof(Door)} with null geometry.");
return null;
}

if (opening.Construction == null)
{
Engine.Base.Compute.RecordError($"The {nameof(VolumetricMaterialTakeoff)} could not be queried as no {nameof(IConstruction)} has been assigned to the {nameof(Door)}.");
return null;
}

return TakeOff(opening.Location.IArea(), opening.Construction);
}

/***************************************************/
/**** Public Methods - Interface ****/
/***************************************************/

[Description("Gets the volumetric material takeoff from the IOpening.")]
[Input("opening", "The physical opening object to extract the volumetric material takeoff from.")]
[Output("volTakeoff", "The volumetric material takeoff for the opening, made of up the volume and materiality of the construction and surface area.")]
public static VolumetricMaterialTakeoff IVolumetricMaterialTakeoff(this IOpening opening)
{
return VolumetricMaterialTakeoff(opening as dynamic);
}

/***************************************************/
/**** private Methods ****/
/***************************************************/

private static VolumetricMaterialTakeoff TakeOff(double area, IConstruction construction)
{
MaterialComposition comp = construction.IMaterialComposition();
if (comp == null)
return null;
if(comp.Materials.Count == 0)
return new VolumetricMaterialTakeoff(new List<Material>(), new List<double>());
return Matter.Create.VolumetricMaterialTakeoff(construction.IMaterialComposition(), construction.IThickness() * area);
}

/***************************************************/
}
}