diff --git a/LadybugTools_Adapter/AdapterActions/Execute/CondensationRiskCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/CondensationRiskCommand.cs new file mode 100644 index 00000000..2c5ca1f4 --- /dev/null +++ b/LadybugTools_Adapter/AdapterActions/Execute/CondensationRiskCommand.cs @@ -0,0 +1,80 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2025, 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.Adapter; +using BH.Engine.LadybugTools; +using BH.Engine.Serialiser; +using BH.oM.Adapter; +using BH.oM.Base; +using BH.oM.LadybugTools; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BH.Adapter.LadybugTools +{ + public partial class LadybugToolsAdapter : BHoMAdapter + { + private List RunCommand(CondensationRiskCommand command, ActionConfig actionConfig) + { + if (command.EPWFile == null) + { + BH.Engine.Base.Compute.RecordError($"{nameof(command.EPWFile)} input cannot be null."); + return null; + } + + if (!System.IO.File.Exists(command.EPWFile.GetFullFileName())) + { + BH.Engine.Base.Compute.RecordError($"File '{command.EPWFile.GetFullFileName()}' does not exist."); + return null; + } + + string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + + string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\plot", "condensation_risk_heatmap.py"); + + string returnFile = Path.GetTempFileName(); + + // run the process + string cmdCommand = $"{m_environment.Executable} \"{script}\" -e \"{epwFile}\" -r \"{returnFile.Replace('\\', '/')}\" -p \"{command.OutputLocation}\""; + string result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + + string resultFile = result.Split('\n').Last(); + + if (!File.Exists(resultFile)) + { + BH.Engine.Base.Compute.RecordError($"An error occurred while running the command: {result}"); + File.Delete(returnFile); + return new List(); + } + + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); + + m_executeSuccess = true; + return new List { info }; + } + } +} diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py new file mode 100644 index 00000000..8f71fe42 --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py @@ -0,0 +1,118 @@ +"""Plotting methods for condensation risk.""" + +import argparse +from pathlib import Path +import matplotlib +import json +from ladybug.epw import EPW +from python_toolkit.plot.heatmap import heatmap +from matplotlib.colors import LinearSegmentedColormap +from ladybugtools_toolkit.ladybug_extension.header import header_from_string +from ladybug.epw import AnalysisPeriod, HourlyContinuousCollection +from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series +from ladybugtools_toolkit.bhom.wrapped.metadata.collection import collection_metadata + +thresholds = [ + (0,0), + (1,3), + (2,6), + (3,12), + (4,15), + (5,18), + (6,21), + (7,24), + (8,27), + (9,30), + (10,33), + (11,999) +] + +def get_threshold(dbt_delta): + matches = [a for a in thresholds if a[1] >= dbt_delta] + return min([a[0] for a in matches]) + +def dbt_to_condensation_risk(dbt_series, internal_rh, internal_temp): + """Calculate condensation risk based on external temperature and internal dew point, using Mark G. Lawrence's DPT approximation for RH>50%. + + Args: + dbt_series (list[float]): + List of Dry Bulb Temperatures + internal_rh (int): + Internal Relative Humidity, as a percentage + internal_temp (float): + Internal Temperature of the building + """ + dew_point_temp = internal_temp-((100-internal_rh)/5) + dbt_delta = dew_point_temp - dbt_series + con_risk = dbt_delta.apply(get_threshold) + header = header_from_string("Condensation risk (Index)") + hcc = HourlyContinuousCollection(header = header, values = con_risk.values) + return hcc + +def condensation_risk_heatmap(epw_file: str, return_file: str, save_path: str = None) -> None: + """Create a heatmap of the condensation potential for a given set of + timeseries dry bulb temperatures from an EPW. + + Args: + epw_file (string): + The input EPW file. + return_file (string): + The filepath to write the resulting JSON to. + save_path (string): + The filepath to save the resulting image file of the heatmap to. + """ + epw = EPW(epw_file) + internal_temp = 21 #default value for internal temp + internal_rh = 40 #default value for internal rh + dbt_series = collection_to_series(epw.dry_bulb_temperature) + dpt_series = collection_to_series(epw.dew_point_temperature) + con_risk = dbt_to_condensation_risk(dbt_series, internal_rh, internal_temp) + cmap = LinearSegmentedColormap.from_list("condensation", ["white","blue","purple","black"], N=100) + fig = heatmap(collection_to_series(con_risk), vmin=0, vmax=11, cmap=cmap, ).get_figure() + + return_dict = {"data": collection_metadata(con_risk)} + + if save_path == None or save_path == "": + base64 = figure_to_base64(fig,html=False) + return_dict["figure"] = base64 + else: + fig.savefig(save_path, dpi=150, transparent=True) + return_dict["figure"] = save_path + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(return_dict, default=str)) + + print(return_file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description=( + "Given an EPW file path, extract a heatmap of condensation risk" + ) + ) + parser.add_argument( + "-e", + "--epw_file", + help="The EPW file to extract a heatmap from", + type=str, + required=True, + ) + parser.add_argument( + "-r", + "--return_file", + help="json file to write return data to.", + type=str, + required=True, + ) + parser.add_argument( + "-p", + "--save_path", + help="Path where to save the output image.", + type=str, + required=False, + ) + + args = parser.parse_args() + matplotlib.use("Agg") + condensation_risk_heatmap(args.epw_file, args.return_file, args.save_path) \ No newline at end of file diff --git a/LadybugTools_oM/ExecuteCommands/CondensationRiskCommand.cs b/LadybugTools_oM/ExecuteCommands/CondensationRiskCommand.cs new file mode 100644 index 00000000..22bacc88 --- /dev/null +++ b/LadybugTools_oM/ExecuteCommands/CondensationRiskCommand.cs @@ -0,0 +1,42 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2025, 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 System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using BH.oM.Adapter; +using System.ComponentModel; + +namespace BH.oM.LadybugTools +{ + [Description("Command that, when executed with the LadybugToolsAdapter, simulates Condensation Risk and outputs a heatmap.\nOutput is a string of either the path to the image (if OutputLocation is not set) or the base 64 string representation of it.")] + public class CondensationRiskCommand : ISimulationCommand + { + [Description("The path to an EPW file.")] + public virtual FileSettings EPWFile { get; set; } = new FileSettings(); + + [Description("Full file path (with file name) to save the plot to. Leave blank to output a base 64 string representation of the image instead.")] + public virtual string OutputLocation { get; set; } = ""; + } +} +