diff --git a/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateClimateChangeEnvironmentalProductDeclarationTests.cs b/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateClimateChangeEnvironmentalProductDeclarationTests.cs new file mode 100644 index 0000000..a6f0f63 --- /dev/null +++ b/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateClimateChangeEnvironmentalProductDeclarationTests.cs @@ -0,0 +1,192 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Engine.LifeCycleAssessment; +using BH.oM.Base.Debugging; +using BH.oM.LifeCycleAssessment; +using BH.oM.LifeCycleAssessment.MaterialFragments; +using BH.oM.Test.NUnit; +using FluentAssertions; +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; +using LCAModule = BH.oM.LifeCycleAssessment.Module; + +namespace BH.Tests.Engine.LifeCycleAssessment +{ + public class CreateClimateChangeEnvironmentalProductDeclarationTests : NUnitTest + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Test] + [Description("Tests that the ClimateChangeEnvironmentalProductDeclaration create method assigns the provided properties and creates the five climate change metrics.")] + public void ClimateChangeEPDProperties() + { + EnvironmentalProductDeclaration epd = BH.Engine.LifeCycleAssessment.Create.ClimateChangeEnvironmentalProductDeclaration( + new List { LCAModule.A1toA3 }, + new List { 1 }, + new List { 2 }, + new List { 3 }, + QuantityType.Volume, + EPDType.Sector, + "Concrete"); + + epd.Should().NotBeNull(); + epd.Name.Should().Be("Concrete"); + epd.Type.Should().Be(EPDType.Sector); + epd.QuantityType.Should().Be(QuantityType.Volume); + epd.EnvironmentalMetrics.Select(x => x.GetType()).Should().BeEquivalentTo(new[] + { + typeof(ClimateChangeFossilMetric), + typeof(ClimateChangeBiogenicMetric), + typeof(ClimateChangeLandUseMetric), + typeof(ClimateChangeTotalMetric), + typeof(ClimateChangeTotalNoBiogenicMetric), + }); + } + + /***************************************************/ + + [Test] + [Description("Tests that the ClimateChangeEnvironmentalProductDeclaration create method uses Undefined EPDType and an empty name by default.")] + public void ClimateChangeEPDDefaults() + { + EnvironmentalProductDeclaration epd = BH.Engine.LifeCycleAssessment.Create.ClimateChangeEnvironmentalProductDeclaration( + new List { LCAModule.A1toA3 }, + new List { 1 }, + new List { 2 }, + new List { 3 }, + QuantityType.Mass); + + epd.Type.Should().Be(EPDType.Undefined); + epd.Name.Should().BeEmpty(); + } + + /***************************************************/ + + [Test] + [Description("Tests that the ClimateChangeEnvironmentalProductDeclaration create method stores fossil, biogenic and LULUC values per module and computes the totals correctly.")] + public void ClimateChangeEPDValues() + { + List modules = new List { LCAModule.A1toA3, LCAModule.C3toC4, LCAModule.D }; + List fossil = new List { 10, 1, -2 }; + List biogenic = new List { -4, 0.5, 0 }; + List luluc = new List { 0.25, 0.1, 0.05 }; + + EnvironmentalProductDeclaration epd = BH.Engine.LifeCycleAssessment.Create.ClimateChangeEnvironmentalProductDeclaration(modules, fossil, biogenic, luluc, QuantityType.Mass); + + double tol = 1e-12; + for (int i = 0; i < modules.Count; i++) + { + LCAModule m = modules[i]; + MetricIndicators(epd, MetricType.ClimateChangeFossil)[m].Should().BeApproximately(fossil[i], tol); + MetricIndicators(epd, MetricType.ClimateChangeBiogenic)[m].Should().BeApproximately(biogenic[i], tol); + MetricIndicators(epd, MetricType.ClimateChangeLandUse)[m].Should().BeApproximately(luluc[i], tol); + MetricIndicators(epd, MetricType.ClimateChangeTotal)[m].Should().BeApproximately(fossil[i] + biogenic[i] + luluc[i], tol); + MetricIndicators(epd, MetricType.ClimateChangeTotalNoBiogenic)[m].Should().BeApproximately(fossil[i] + luluc[i], tol); + } + + foreach (IEnvironmentalMetric metric in epd.EnvironmentalMetrics) + metric.Indicators.Keys.Should().BeEquivalentTo(modules); + } + + /***************************************************/ + + [Test] + [Description("Tests that the ClimateChangeEnvironmentalProductDeclaration create method returns an EPD with empty metrics for empty module lists.")] + public void ClimateChangeEPDNoModules() + { + EnvironmentalProductDeclaration epd = BH.Engine.LifeCycleAssessment.Create.ClimateChangeEnvironmentalProductDeclaration( + new List(), new List(), new List(), new List(), QuantityType.Mass); + + epd.Should().NotBeNull(); + epd.EnvironmentalMetrics.Should().HaveCount(5); + epd.EnvironmentalMetrics.Should().OnlyContain(x => x.Indicators.Count == 0); + } + + /***************************************************/ + + [TestCaseSource(nameof(FailingClimateChangeEPDInputs))] + [Description("Tests that the ClimateChangeEnvironmentalProductDeclaration create method returns null and records an error for invalid inputs, such as null lists, mismatched list lengths, duplicate modules and NaN factors.")] + public void ClimateChangeEPDFailingCases(List modules, List fossil, List biogenic, List luluc) + { + BH.Engine.Base.Compute.ClearCurrentEvents(); + + EnvironmentalProductDeclaration epd = BH.Engine.LifeCycleAssessment.Create.ClimateChangeEnvironmentalProductDeclaration(modules, fossil, biogenic, luluc, QuantityType.Mass); + + epd.Should().BeNull(); + ErrorCount().Should().Be(1); + } + + /***************************************************/ + /**** Test Case Sources ****/ + /***************************************************/ + + private static IEnumerable FailingClimateChangeEPDInputs() + { + List oneModule = new List { LCAModule.A1toA3 }; + List one = new List { 1 }; + List two = new List { 1, 2 }; + List nan = new List { double.NaN }; + List empty = new List(); + + yield return new TestCaseData(null, one, one, one).SetArgDisplayNames("Failing case: Null modules"); + yield return new TestCaseData(oneModule, null, one, one).SetArgDisplayNames("Failing case: Null fossil"); + yield return new TestCaseData(oneModule, one, null, one).SetArgDisplayNames("Failing case: Null biogenic"); + yield return new TestCaseData(oneModule, one, one, null).SetArgDisplayNames("Failing case: Null LULUC"); + + yield return new TestCaseData(oneModule, empty, one, one).SetArgDisplayNames("Failing case: Fewer fossil than modules"); + yield return new TestCaseData(oneModule, one, empty, one).SetArgDisplayNames("Failing case: Fewer biogenic than modules"); + yield return new TestCaseData(oneModule, one, one, empty).SetArgDisplayNames("Failing case: Fewer LULUC than modules"); + yield return new TestCaseData(oneModule, two, one, one).SetArgDisplayNames("Failing case: More fossil than modules"); + yield return new TestCaseData(oneModule, one, two, one).SetArgDisplayNames("Failing case: More biogenic than modules"); + yield return new TestCaseData(oneModule, one, one, two).SetArgDisplayNames("Failing case: More LULUC than modules"); + + yield return new TestCaseData(new List { LCAModule.A1toA3, LCAModule.A1toA3 }, two, two, two).SetArgDisplayNames("Failing case: Duplicate modules"); + + yield return new TestCaseData(oneModule, nan, one, one).SetArgDisplayNames("Failing case: NaN fossil"); + yield return new TestCaseData(oneModule, one, nan, one).SetArgDisplayNames("Failing case: NaN biogenic"); + yield return new TestCaseData(oneModule, one, one, nan).SetArgDisplayNames("Failing case: NaN LULUC"); + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + private static Dictionary MetricIndicators(EnvironmentalProductDeclaration epd, MetricType metricType) + { + return epd.EnvironmentalMetrics.Single(x => x.IMetricType() == metricType).Indicators; + } + + /***************************************************/ + + private static int ErrorCount() + { + return BH.Engine.Base.Query.CurrentEvents().Count(x => x.Type == EventType.Error); + } + + /***************************************************/ + } +} + diff --git a/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateEnvironmentalMetricTests.cs b/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateEnvironmentalMetricTests.cs new file mode 100644 index 0000000..ea6df7a --- /dev/null +++ b/.ci/unit-tests/LifeCycleAssessment_Engine_Tests/CreateEnvironmentalMetricTests.cs @@ -0,0 +1,176 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Engine.LifeCycleAssessment; +using BH.oM.Base.Debugging; +using BH.oM.LifeCycleAssessment; +using BH.oM.LifeCycleAssessment.MaterialFragments; +using BH.oM.Test.NUnit; +using FluentAssertions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + + +namespace BH.Tests.Engine.LifeCycleAssessment +{ + public class CreateEnvironmentalMetricTests : NUnitTest + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [TestCaseSource(nameof(DefinedMetricTypes))] + [Description("Tests that all overloads of the EnvironmentalMetric create method return a metric of the type matching the provided MetricType, with the provided indicators stored on it and an empty name by default.")] + public void EnvironmentalMetricMatchesMetricType(MetricType metricType) + { + Dictionary indicators = new Dictionary + { + { Module.A1toA3, 1.5 }, + { Module.C3toC4, 2.5 }, + }; + + IEnvironmentalMetric typeOnly = BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(metricType); + IEnvironmentalMetric fromDictionary = BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(metricType, indicators); + IEnvironmentalMetric fromLists = BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(metricType, indicators.Keys.ToList(), indicators.Values.ToList()); + + typeOnly.Indicators.Should().BeEmpty(); + fromDictionary.Indicators.Should().BeEquivalentTo(indicators); + fromLists.Indicators.Should().BeEquivalentTo(indicators); + + foreach (IEnvironmentalMetric metric in new[] { typeOnly, fromDictionary, fromLists }) + { + metric.Should().NotBeNull(); + metric.IMetricType().Should().Be(metricType); + metric.Name.Should().Be(""); + } + } + + /***************************************************/ + + [Test] + [Description("Tests that all overloads of the EnvironmentalMetric create method set the provided name, and fall back to an empty name for null.")] + public void EnvironmentalMetricName() + { + Dictionary indicators = new Dictionary { { Module.A1toA3, 1 } }; + + IEnvironmentalMetric[] metrics = new[] + { + BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, "My metric"), + BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, indicators, "My metric"), + BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, indicators.Keys.ToList(), indicators.Values.ToList(), "My metric"), + }; + + metrics.Select(x => x.Name).Should().OnlyContain(x => x == "My metric"); + + //Cast needed to resolve the overload, as null is also a valid indicators input + BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, (string)null).Name.Should().Be(""); + } + + /***************************************************/ + + [Test] + [Description("Tests that the EnvironmentalMetric create method creates the expected concrete type.")] + public void EnvironmentalMetricConcreteType() + { + IEnvironmentalMetric metric = BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, new Dictionary()); + metric.Should().BeOfType(); + } + + /***************************************************/ + + [Test] + [Description("Tests that the EnvironmentalMetric create method does not share the provided indicators dictionary with the created metric.")] + public void EnvironmentalMetricDoesNotShareIndicators() + { + Dictionary indicators = new Dictionary { { Module.A1toA3, 1 } }; + + IEnvironmentalMetric metric = BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, indicators); + + indicators[Module.A4] = 5; + metric.Indicators.Should().NotContainKey(Module.A4); + + metric.Indicators[Module.C1] = 7; + indicators.Should().NotContainKey(Module.C1); + } + + /***************************************************/ + + [TestCaseSource(nameof(InvalidInputs))] + [Description("Tests that the EnvironmentalMetric create method returns null and records a single error for invalid inputs, without throwing.")] + public void EnvironmentalMetricInvalidInputs(Func create) + { + BH.Engine.Base.Compute.ClearCurrentEvents(); + + IEnvironmentalMetric metric = null; + + create.Invoking(x => metric = x()).Should().NotThrow(); + metric.Should().BeNull(); + ErrorCount().Should().Be(1); + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + private static IEnumerable DefinedMetricTypes() + { + return Enum.GetValues(typeof(MetricType)).Cast().Where(x => x != MetricType.Undefined); + } + + /***************************************************/ + + private static IEnumerable InvalidInputs() + { + Dictionary indicators = new Dictionary { { Module.A1toA3, 1 } }; + List modules = new List { Module.A1toA3 }; + List values = new List { 1 }; + + //Cast needed to resolve the overload, as null is also a valid name + yield return Case("Undefined type", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Undefined)); + yield return Case("Undefined type with indicators", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Undefined, indicators)); + yield return Case("Undefined type with modules and indicators", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Undefined, modules, values)); + yield return Case("Null indicators", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, (Dictionary)null)); + yield return Case("Null modules", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, null, values)); + yield return Case("Null indicators with modules", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, modules, (List)null)); + yield return Case("Modules and indicators of different lengths", () => BH.Engine.LifeCycleAssessment.Create.EnvironmentalMetric(MetricType.Acidification, new List { Module.A1toA3, Module.C3toC4 }, values)); + } + + /***************************************************/ + + private static TestCaseData Case(string name, Func create) + { + return new TestCaseData(create).SetArgDisplayNames(name); + } + + /***************************************************/ + + private static int ErrorCount() + { + return BH.Engine.Base.Query.CurrentEvents().Count(x => x.Type == EventType.Error); + } + + /***************************************************/ + } +} + diff --git a/LifeCycleAssessment_Engine/Create/EnvironmentalProductDeclaration/ClimateChangeEnvironmentalProductDeclaration.cs b/LifeCycleAssessment_Engine/Create/EnvironmentalProductDeclaration/ClimateChangeEnvironmentalProductDeclaration.cs new file mode 100644 index 0000000..1dca73e --- /dev/null +++ b/LifeCycleAssessment_Engine/Create/EnvironmentalProductDeclaration/ClimateChangeEnvironmentalProductDeclaration.cs @@ -0,0 +1,111 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Base.Attributes; +using BH.oM.LifeCycleAssessment; +using BH.oM.LifeCycleAssessment.MaterialFragments; +using BH.oM.Quantities.Attributes; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace BH.Engine.LifeCycleAssessment +{ + public static partial class Create + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Creates an EnvironmentalProductDeclaration containing the climate change metrics, given the fossil, biogenic and land use and land use change (LULUC) factors per module. " + + "The ClimateChangeTotal (fossil + biogenic + LULUC) and ClimateChangeTotalNoBiogenic (fossil + LULUC) metrics are computed per module from the provided factors for each module.")] + [Input("modules", "The modules the provided factors correspond to. The factor lists need to be of the same length as this list, with the factor at each index corresponding to the module at the same index.")] + [Input("fossil", "Climate change fossil factors per module, in the same order as the provided modules. Values given should be per quantity set by the quantityType.", typeof(ClimateChangePerQuantity))] + [Input("biogenic", "Climate change biogenic factors per module, in the same order as the provided modules. Values given should be per quantity set by the quantityType.", typeof(ClimateChangePerQuantity))] + [Input("luluc", "Climate change land use and land use change (LULUC) factors per module, in the same order as the provided modules. Values given should be per quantity set by the quantityType.", typeof(ClimateChangePerQuantity))] + [Input("quantityType", "The quantity type the factors are given per, for example Mass for factors given per kg.")] + [Input("epdType", "The type of the EnvironmentalProductDeclaration.")] + [Input("name", "Name of the created EnvironmentalProductDeclaration.")] + [Output("epd", "The created EnvironmentalProductDeclaration containing the fossil, biogenic, LULUC, total and total without biogenic climate change metrics.")] + public static EnvironmentalProductDeclaration ClimateChangeEnvironmentalProductDeclaration(List modules, List fossil, List biogenic, List luluc, QuantityType quantityType, EPDType epdType = EPDType.Undefined, string name = "") + { + if (modules == null || fossil == null || biogenic == null || luluc == null) + { + Base.Compute.RecordError("Cannot create a climate change EnvironmentalProductDeclaration from null modules or factors."); + return null; + } + + if (fossil.Count != modules.Count || biogenic.Count != modules.Count || luluc.Count != modules.Count) + { + Base.Compute.RecordError($"The number of fossil ({fossil.Count}), biogenic ({biogenic.Count}) and LULUC ({luluc.Count}) factors need to match the number of modules ({modules.Count})."); + return null; + } + + if (modules.Distinct().Count() != modules.Count) + { + Base.Compute.RecordError("The provided modules contain duplicates. Each module can only be provided once."); + return null; + } + + if (fossil.Concat(biogenic).Concat(luluc).Any(double.IsNaN)) + { + Base.Compute.RecordError("The fossil, biogenic and LULUC factors need to be provided for all modules. NaN values are not allowed."); + return null; + } + + Dictionary fossilFactors = new Dictionary(); + Dictionary biogenicFactors = new Dictionary(); + Dictionary lulucFactors = new Dictionary(); + Dictionary totalFactors = new Dictionary(); + Dictionary totalNoBiogenicFactors = new Dictionary(); + + for (int i = 0; i < modules.Count; i++) + { + Module module = modules[i]; + fossilFactors[module] = fossil[i]; + biogenicFactors[module] = biogenic[i]; + lulucFactors[module] = luluc[i]; + totalFactors[module] = fossil[i] + biogenic[i] + luluc[i]; + totalNoBiogenicFactors[module] = fossil[i] + luluc[i]; + } + + EnvironmentalProductDeclaration epd = new EnvironmentalProductDeclaration() + { + Name = name, + Type = epdType, + QuantityType = quantityType, + EnvironmentalMetrics = new List() + { + new ClimateChangeFossilMetric() { Indicators = fossilFactors }, + new ClimateChangeBiogenicMetric() { Indicators = biogenicFactors }, + new ClimateChangeLandUseMetric() { Indicators = lulucFactors }, + new ClimateChangeTotalMetric() { Indicators = totalFactors }, + new ClimateChangeTotalNoBiogenicMetric() { Indicators = totalNoBiogenicFactors } + } + }; + + return epd; + } + + /***************************************************/ + } +} diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AbioticDepletionFossilResourcesMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AbioticDepletionFossilResourcesMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AbioticDepletionFossilResourcesMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AbioticDepletionFossilResourcesMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AbioticDepletionMineralsAndMetalsMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AbioticDepletionMineralsAndMetalsMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AbioticDepletionMineralsAndMetalsMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AbioticDepletionMineralsAndMetalsMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AcidificationMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AcidificationMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/AcidificationMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/AcidificationMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeBiogenicMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeBiogenicMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeBiogenicMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeBiogenicMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeFossilMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeFossilMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeFossilMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeFossilMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeLandUseMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeLandUseMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeLandUseMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeLandUseMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeTotalMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeTotalMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeTotalMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeTotalMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeTotalNoBiogenicMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeTotalNoBiogenicMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/ClimateChangeTotalNoBiogenicMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/ClimateChangeTotalNoBiogenicMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EnvironmentalMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EnvironmentalMetric.cs new file mode 100644 index 0000000..255869d --- /dev/null +++ b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EnvironmentalMetric.cs @@ -0,0 +1,182 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2026, 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.Engine.Base; +using BH.oM.Base.Attributes; +using BH.oM.LifeCycleAssessment; +using BH.oM.LifeCycleAssessment.MaterialFragments; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Linq.Expressions; + +namespace BH.Engine.LifeCycleAssessment +{ + public static partial class Create + { + /***************************************************/ + /**** Public Methods ****/ + /***************************************************/ + + [Description("Creates an IEnvironmentalMetric of a type matched to the provided MetricType without any indicator values set.")] + [Input("metricType", "The type of metric to create. Decides which type of IEnvironmentalMetric is returned, e.g. Acidification will return an AcidificationMetric.")] + [Input("name", "The name to give the created metric. Defaults to an empty string.")] + [Output("metric", "The created IEnvironmentalMetric.")] + public static IEnvironmentalMetric EnvironmentalMetric(MetricType metricType, string name = "") + { + if (metricType == MetricType.Undefined) + { + Base.Compute.RecordError($"Cannot create a {nameof(IEnvironmentalMetric)} from an undefined {nameof(MetricType)}."); + return null; + } + + //Get the pre-compiled default constructor for the metric corresponding to the provided type + Func cst = EnvironmentalMetricConstructor(metricType); + + if (cst == null) + return null; + + IEnvironmentalMetric metric = cst(); + metric.Name = name ?? ""; + return metric; + } + + /***************************************************/ + + [Description("Creates an IEnvironmentalMetric of a type matched to the provided MetricType, with the provided values per module stored as its indicators.")] + [Input("metricType", "The type of metric to create. Decides which type of IEnvironmentalMetric is returned, e.g. Acidification will return an AcidificationMetric.")] + [Input("indicators", "The values to be stored on the metric per each module. Values should be per quantity corresponding to the QuantityType on the epd that this metric is associated with.")] + [Input("name", "The name to give the created metric. Defaults to an empty string.")] + [Output("metric", "The created IEnvironmentalMetric.")] + public static IEnvironmentalMetric EnvironmentalMetric(MetricType metricType, Dictionary indicators, string name = "") + { + if (indicators == null) + { + Base.Compute.RecordError($"Cannot create a {nameof(IEnvironmentalMetric)} from null indicators."); + return null; + } + + //Call the create method that only takes the MetricType to get the correct type of metric created. + IEnvironmentalMetric metric = EnvironmentalMetric(metricType, name); + + if (metric == null) + return null; + + //Indicators is get-only on the interface. Fill the dictionary of the new metric, which also means the provided dictionary is not shared with the metric. + foreach (KeyValuePair indicator in indicators) + { + metric.Indicators[indicator.Key] = indicator.Value; + } + + return metric; + } + + /***************************************************/ + + [Description("Creates an IEnvironmentalMetric of a type matched to the provided MetricType, with the provided values stored as its indicators for the corresponding modules.")] + [Input("metricType", "The type of metric to create. Decides which type of IEnvironmentalMetric is returned, e.g. Acidification will return an AcidificationMetric.")] + [Input("modules", "The modules to store indicator values for. Needs to be the same length as indicators, where each module is matched to the indicator at the same index.")] + [Input("indicators", "The values to be stored on the metric per each module. Needs to be the same length as modules. Values should be per quantity corresponding to the QuantityType on the epd that this metric is associated with.")] + [Input("name", "The name to give the created metric. Defaults to an empty string.")] + [Output("metric", "The created IEnvironmentalMetric.")] + public static IEnvironmentalMetric EnvironmentalMetric(MetricType metricType, List modules, List indicators, string name = "") + { + if (modules == null || indicators == null) + { + Base.Compute.RecordError($"Cannot create a {nameof(IEnvironmentalMetric)} from null modules or indicators."); + return null; + } + + if (modules.Count != indicators.Count) + { + Base.Compute.RecordError($"Cannot create a {nameof(IEnvironmentalMetric)} from modules and indicators of different lengths."); + return null; + } + + Dictionary indicatorsDict = new Dictionary(); + + for (int i = 0; i < modules.Count; i++) + { + indicatorsDict[modules[i]] = indicators[i]; + } + + return EnvironmentalMetric(metricType, indicatorsDict, name); + } + + /***************************************************/ + /**** Private Methods ****/ + /***************************************************/ + + [Description("Gets a function corresponding to the default constructor for the IEnvironmentalMetric corresponding to the provided MetricType.")] + [Input("metricType", "The type of metric to find a matching constructor for.")] + [Output("cstFunc", "The function corresponding to the constructor of the IEnvironmentalMetric related to the MetricType.")] + private static Func EnvironmentalMetricConstructor(this MetricType metricType) + { + Func cstFunc; + + //Try get cached constructor func + if (!m_EnvironmentalMetricConstructors.TryGetValue(metricType, out cstFunc)) + { + System.Reflection.ConstructorInfo constructor = GetEnvironmentalMetricConstructorInfo(metricType); + if (constructor != null) + { + //Pre-compile "() => new XMetric()" to a parameterless function to increase performance. + //No object[] input or cast wrapper is needed as the constructor is known to have no parameters. + cstFunc = Expression.Lambda>(Expression.New(constructor)).Compile(); + } + else + cstFunc = null; + + m_EnvironmentalMetricConstructors[metricType] = cstFunc; + } + + if (cstFunc == null) + Base.Compute.RecordError($"Unable to find a constructor for a type of {nameof(IEnvironmentalMetric)} based on provided type {metricType}"); + + return cstFunc; + } + + /***************************************************/ + + [Description("Gets the default ConstructorInfo from an IEnvironmentalMetric type matching the provided MetricType.")] + private static System.Reflection.ConstructorInfo GetEnvironmentalMetricConstructorInfo(MetricType metricType) + { + Type metricClassType = BH.Engine.Base.Query.BHoMTypeList().Where(x => typeof(IEnvironmentalMetric).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract).FirstOrDefault(x => x.Name == metricType.ToString() + "Metric"); + + if (metricClassType == null) + return null; + + return metricClassType.GetConstructor(Type.EmptyTypes); + } + + /***************************************************/ + /**** Private Fields ****/ + /***************************************************/ + + //Storage of the pre-compiled functions for future usage + private static ConcurrentDictionary> m_EnvironmentalMetricConstructors = new ConcurrentDictionary>(); + + /***************************************************/ + } +} diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationAquaticFreshwaterMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationAquaticFreshwaterMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationAquaticFreshwaterMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationAquaticFreshwaterMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationAquaticMarineMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationAquaticMarineMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationAquaticMarineMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationAquaticMarineMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationCMLMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationCMLMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationCMLMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationCMLMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationTRACIMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationTRACIMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationTRACIMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationTRACIMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationTerrestrialMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationTerrestrialMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/EutrophicationTerrestrialMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/EutrophicationTerrestrialMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/FactorsDictionary.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/FactorsDictionary.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/FactorsDictionary.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/FactorsDictionary.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/OzoneDepletionMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/OzoneDepletionMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/OzoneDepletionMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/OzoneDepletionMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationCMLMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationCMLMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationCMLMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationCMLMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationTRACIMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationTRACIMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/PhotochemicalOzoneCreationTRACIMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/PhotochemicalOzoneCreationTRACIMetric.cs diff --git a/LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/WaterDeprivationMetric.cs b/LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/WaterDeprivationMetric.cs similarity index 100% rename from LifeCycleAssessment_Engine/Create/IEnvironmentalMetricFactors/WaterDeprivationMetric.cs rename to LifeCycleAssessment_Engine/Create/IEnvironmentalMetric/WaterDeprivationMetric.cs