Repository: BHoM/LadybugTools_Toolkit
Pull request #297: Condensation Risk Heatmap method and supporting execute command added
Author: login: enarhi
Labels: name: type:feature
description: New capability or enhancement
Created: 2025-02-07T00:10:22Z | Merged: 2025-05-09T16:43:02Z

<!-- PLEASE ENSURE YOU REVIEW THE CONTENT OF EACH PR CAREFULLY, INCLUDING SUBSEQUENT COMMENTS BY YOURSELF OR OTHERS. -->
<!-- IN PARTICULAR PLEASE ENSURE THAT SENSITIVE OR INAPPROPRIATE INFORMATION IS NOT UPLOADED -->

### NOTE: Depends on 
<!-- Link to any additional PRs in other repos required for this PR to function -->
<!-- Delete if not required -->

   
### Issues addressed by this PR
<!-- Add reference(s) to issue(s) solved by this PR. Please use keyword Fixes/Closes as per https://help.github.com/articles/closing-issues-using-keywords/ -->

Closes #293 

Added Condensation Risk Heatmap to indicate facade condensation risk when temperatures reach below certain thresholds.


### Test files
[Test script and EPW](https://burohappold.sharepoint.com/:f:/r/sites/BHoM/02_Current/12_Scripts/02_Pull%20Request/BHoM/LadybugTools_Toolkit/%23293-CondensationRiskHeatmap?csf=1&web=1&e=KaFvw2)

Linked issue #293: Add Condensation Risk Method to Generate Heatmap showing Condensation Risk on Facade
<!-- PLEASE ENSURE YOU REVIEW THE CONTENT OF EACH ISSUE CAREFULLY, INCLUDING SUBSEQUENT COMMENTS BY YOURSELF OR OTHERS. -->
<!-- IN PARTICULAR PLEASE ENSURE THAT SENSITIVE OR INAPPROPRIATE INFORMATION IS NOT UPLOADED -->

#### Description:
This is a standard plot used by Facades based on external temperatures and varying input range criteria, with a default set of ranges per industry standard. ThHe method should return a heatmap as shown below indicating hours of the year when the Facade is at risk of Condensation due to deltas between internal and external temperatures.

![Image](https://github.com/user-attachments/assets/ca9f629e-9671-47a7-bce9-13ba1d2808fd)





Diff:
diff --git a/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
new file mode 100644
index 00000000..f9a76c87
--- /dev/null
+++ b/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
@@ -0,0 +1,99 @@
+﻿/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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<object> RunCommand(FacadeCondensationRiskCommand 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;
+            }
+
+            List<double> thresholds;
+            if (command.Thresholds == null || command.Thresholds.Count() == 0)
+            {
+                thresholds = new List<double>{10, 7, 4, 1, -2, -5};
+            }
+            else
+            {
+                thresholds = command.Thresholds;
+            }
+            string thresholdsStr = string.Join(" ",  thresholds);  
+
+            string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName());
+            string script;
+
+            if (command.Heatmap)
+            {
+                script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped\\plot", "facade_condensation_risk_heatmap.py");
+            }              
+            else
+            {
+                script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped\\plot", "facade_condensation_risk_chart.py");
+            }
+                
+            string returnFile = Path.GetTempFileName();
+
+            // run the process
+            string cmdCommand = $"{m_environment.Executable} \"{script}\" -e \"{epwFile}\" -t {thresholdsStr} -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<object>();
+            }
+
+            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<object> { info };
+        }
+    }
+}
diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_chart.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_chart.py
new file mode 100644
index 00000000..b3c82a2a
--- /dev/null
+++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_chart.py
@@ -0,0 +1,84 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.facades.condensation_risk.heatmap import *
+
+
+def facade_condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None) -> None:
+
+    epw = EPW(epw_file)
+    hcc = epw.dry_bulb_temperature
+
+    fig = facade_condensation_risk_chart_table(epw_file, thresholds).get_figure()
+
+    return_dict = {"data": collection_metadata(hcc)}
+
+    if save_path == None or save_path == "":
+        base64 = figure_to_base64(fig,html=False)
+        return_dict["figure"] = base64
+    else:
+        fig.savefig(save_path, dpi=300, 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(
+        "-t",
+        "--thresholds",
+        help="thresholds to use.",
+        type = float,
+        nargs='*',
+        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")
+    facade_condensation_risk_chart(args.epw_file, args.thresholds, args.return_file, args.save_path)
diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_heatmap.py
new file mode 100644
index 00000000..74488f2d
--- /dev/null
+++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/facade_condensation_risk_heatmap.py
@@ -0,0 +1,84 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.facades.condensation_risk.heatmap import *
+
+
+def facade_condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None) -> None:
+
+    epw = EPW(epw_file)
+    hcc = epw.dry_bulb_temperature
+
+    fig = facade_condensation_risk_heatmap_histogram(epw_file, thresholds).get_figure()
+
+    return_dict = {"data": collection_metadata(hcc)}
+
+    if save_path == None or save_path == "":
+        base64 = figure_to_base64(fig,html=False)
+        return_dict["figure"] = base64
+    else:
+        fig.savefig(save_path, dpi=300, 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(
+        "-t",
+        "--thresholds",
+        help="thresholds to use.",
+        type = float,
+        nargs='*',
+        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")
+    facade_condensation_risk_heatmap(args.epw_file, args.thresholds, args.return_file, args.save_path)
diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
index 8fdc7296..bc2b7524 100644
--- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
+++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
@@ -8,6 +8,7 @@
 from typing import Any
 
 import matplotlib.ticker as mticker
+from matplotlib.font_manager import FontProperties
 
 # pylint: enable=E0401
 import numpy as np
@@ -22,6 +23,8 @@
 from mpl_toolkits.axes_grid1 import make_axes_locatable
 from python_toolkit.bhom.analytics import bhom_analytics
 from python_toolkit.plot.heatmap import heatmap
+from python_toolkit.plot.timeseries import timeseries
+
 
 from ..helpers import rolling_window, sunrise_sunset, validate_timeseries
 from ..plot.utilities import contrasting_color
@@ -123,7 +126,9 @@ def from_cmap(
         if np.isinf(_bins[-1]):
             _bins[-1] = bins[-2]
 
-        normalised = np.interp(_bins, [min(bins), max(bins)], [0, 1])
+        _bins_finite = [x for x in bins if not (np.isinf(x))]
+
+        normalised = np.interp(_bins, [min(_bins_finite), max(_bins_finite)], [0, 1])
         colors = [cmap(i) for i in normalised]
 
         return cls(bins=bins, bin_names=bin_names, colors=colors, name=name)
@@ -553,6 +558,50 @@ def annual_monthly_histogram(
 
         return ax
 
+    @bhom_analytics()
+    def annual_monthly_table(
+        self,
+        series: pd.Series,
+        ax: plt.Axes = None,
+        **kwargs,
+    ) -> plt.Axes:
+        """Create a monthly table from a pandas Series.
+
+        Args:
+            series (pd.Series):
+                The pandas Series to plot. Must have a datetime index.
+            ax (plt.Axes, optional):
+                An optional plt.Axes object to populate. Defaults to None,
+                which creates a new plt.Axes object.
+            **kwargs:
+                Additional keyword arguments to pass to plt.bar.
+
+        Returns:
+            plt.Axes:
+                The populated plt.Axes object.
+        """
+
+        validate_timeseries(series)
+
+        if ax is None:
+            ax = plt.gca()
+
+        t = self.timeseries_summary_monthly(series, density=False)
+        #t = t.iloc[:,:-1]
+        t = t.transpose().iloc[::-1]
+
+        # Hide axes
+        ax.axis('off')
+
+        # Create table
+        colors = self.colors[::-1]
+        table = ax.table(cellText=t.values,  rowLabels = t.index, colLabels = t.columns, rowColours = colors, loc='center', **kwargs)
+        for (row, col), cell in table.get_celld().items():
+            if (row == 0) or (col == -1):
+                cell.set_text_props(fontproperties=FontProperties(weight='bold'))
+
+        return ax
+
     @bhom_analytics()
     def annual_heatmap(self, series: pd.Series, ax: plt.Axes = None, **kwargs) -> plt.Axes:
         """Create a heatmap showing the annual hourly categorical assignment for the given series.
@@ -679,6 +728,44 @@ def annual_heatmap_histogram(
 
         return fig
 
+    @bhom_analytics()
+    def annual_threshold_chart(self, series: pd.Series, ax: plt.Axes = None, **kwargs) -> plt.Axes:
+        """Create an annual chart annotated with lines at the category's cutoff values for the given series.
+
+        Args:
+            series (pd.Series):
+                A time-indexed pandas Series object.
+            ax (plt.Axes, optional):
+                A matplotlib Axes object to plot on. Defaults to None.
+            **kwargs:
+                Additional keyword arguments to pass to the heatmap function.
+            show_legend (bool, optional):
+                Whether to show the legend. Defaults to True.
+
+        Returns:
+            plt.Axes:
+                A matplotlib Axes object.
+        """
+
+        validate_timeseries(series)
+
+        if ax is None:
+            ax = plt.gca()
+
+        plt = timeseries(
+            series,
+            ax=ax,
+            **kwargs,
+        )
+
+        for bin_name, interval in list(zip(*[self.bin_names, self.interval_index])):
+            val = interval.right
+            if np.isfinite(val) and val>ax.get_ylim()[0]:
+                col = self.color_from_bin_name(bin_name)
+                ax.axhline(val, 0, 1, color = col, ls="--", lw=2)
+                ax.text(0.5, val, val, va='center', ha='center', backgroundcolor='w', color = col, transform=ax.get_yaxis_transform())
+        return ax
+
 
 class ComfortClass(Enum):
     """Thermal comfort categories."""
diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/facades/condensation_risk/heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/facades/condensation_risk/heatmap.py
new file mode 100644
index 00000000..fc5ad636
--- /dev/null
+++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/facades/condensation_risk/heatmap.py
@@ -0,0 +1,134 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+DEFAULT_THRESHOLDS = [10,7,4,1,-2,-5]
+
+def condensation_categories_from_thresholds(thresholds: list[float]) -> Categorical:
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categorical: The resulting categorical object with condensation risk colouring.
+    """
+
+    thresholds = [i for i in thresholds if not np.isinf(i)]
+    if len(thresholds) < 2:
+        thresholds = DEFAULT_THRESHOLDS;
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+    thresholds_sorted = sorted(set(thresholds))
+
+    cmap = LinearSegmentedColormap.from_list("condensation", ["mediumvioletred", "indigo" , "blue", "white"], N=100)
+    return Categorical.from_cmap(thresholds_sorted, cmap)
+
+def facade_condensation_risk_chart_table(epw_file: str, thresholds: list[float] = DEFAULT_THRESHOLDS, **kwargs) -> Figure:
+    """Create a chart with thresholds of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use. This must be at least two values, and will revert to default values otherwise.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+    epw = EPW(epw_file)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    title = kwargs.pop("title", None)
+    figsize = kwargs.pop("figsize", (15, 8))
+
+    # Instantiate figure
+    fig = plt.figure(figsize=figsize, constrained_layout=True)
+    spec = fig.add_gridspec(ncols=1, nrows=2, width_ratios=[1], height_ratios=[6, 5], hspace=0)
+    chart_ax = fig.add_subplot(spec[0, 0])
+    table_ax = fig.add_subplot(spec[1, 0])
+
+    # Add Thresholds Chart
+    CATEGORIES.annual_threshold_chart(series, chart_ax, color = 'slategrey', **kwargs)
+   
+    # Add table
+    CATEGORIES.annual_monthly_table(series, table_ax, **kwargs)
+
+    title = f"{title}" if title is not None else series.name
+    chart_ax.set_title(title, y=1, ha="left", va="bottom", x=0)
+    chart_ax.set_anchor('W')
+
+    return fig
+
+
+def facade_condensation_risk_heatmap_histogram(epw_file: str, thresholds: list[float] = DEFAULT_THRESHOLDS, **kwargs) -> Figure:
+    """Create a histogram of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use. This must be at least two values, and will revert to default values otherwise.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+    epw = EPW(epw_file)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    title = kwargs.pop("title", None)
+    figsize = kwargs.pop("figsize", (15, 8))
+
+    # Instantiate figure
+    fig = plt.figure(figsize=figsize, constrained_layout=True)
+    spec = fig.add_gridspec(ncols=1, nrows=2, width_ratios=[1], height_ratios=[5, 3], hspace=0.0)
+    heatmap_ax = fig.add_subplot(spec[0, 0])
+    histogram_ax = fig.add_subplot(spec[1, 0])
+
+    # Add heatmap
+    CATEGORIES.annual_heatmap(series, heatmap_ax, **kwargs)
+
+    # Add stacked plot
+    CATEGORIES.annual_monthly_histogram(series, histogram_ax, False, True, **kwargs)
+
+    title = f"{series.name} - {title}" if title is not None else series.name
+    heatmap_ax.set_title(title, y=1, ha="left", va="bottom", x=0)
+
+    return fig
\ No newline at end of file
diff --git a/LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs b/LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
new file mode 100644
index 00000000..625d15e2
--- /dev/null
+++ b/LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
@@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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 FacadeCondensationRiskCommand : ISimulationCommand
+    {
+        [Description("The path to an EPW file.")]
+        public virtual FileSettings EPWFile { get; set; } = new FileSettings();
+
+        [Description("The list of thresholds to use. If no values are provided, default values of [10,7,4,1,-2,-5] will be used.")]
+        public virtual List<double> Thresholds { get; set; } = new List<double>();
+
+        [Description("Set to true to return a heatmap, or set to false to return a chart and table.")]
+        public virtual bool Heatmap { get; set; } = true;
+
+        [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; } = "";
+    }
+}
+


Reviews:
user: login: Tom-Kingstone
body: I can't comment as to whether the heatmap you are producing is correct, but from a code perspective there are a few changes that need to be made, namely to create a separate file in `bhom/wrapped/plot` that handles I/O for calling from the BHoM Adapter
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T09:47:53Z
user: login: Tom-Kingstone
body: not tested functionality, but the wrapped methods will not work properly as they are currently - I know you have asked @jamesramsden-bh so comments on that area are directed to him.

Otherwise, there are a few things which seem rushed but look like they would work when running in jupyter (ignoring wrapped). logically it's fine but there are a few changes to be made. Likely I won't have time today or tomorrow to test functionality.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T09:36:55Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:07:00Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:08:28Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:10:36Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:12:27Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:14:22Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:15:45Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:18:34Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:19:02Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T16:19:24Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T20:05:23Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T20:05:45Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T20:06:07Z
user: login: jamesramsden-bh
body: Changes requested - see suggestions. 
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T16:49:49Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T16:20:37Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T16:21:34Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T16:21:41Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T16:21:47Z
user: login: enarhi
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-06T16:21:56Z
user: login: Tom-Kingstone
body: Tested in grasshopper, and ran unit tests and all is passing and seems to be working as intended (except an unrelated unit test for the abc models for which I will raise a separate issue)
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T07:59:08Z

Review comments:
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
+    
+    thresholds = [-20, -15, -10, -5, 0]
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: Are thresholds meant to be an input or constant? as this method is, thresholds input by the user will be overwritten by these values here - did you mean for these to be defaults instead?

In addition, if you are planning to allow the user to define thresholds, you will need to include them in the argparse handling at the bottom of this file (or put them in a file c# side, and read the list using `json.loads()` here)
created_at: 2025-03-04T09:13:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: As this hcc is not used in creating the plot, the variable should be removed and the correct collection (`epw.dew_point_temperature`) should be returned instead.
created_at: 2025-03-04T09:16:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
+    
+    thresholds = [-20, -15, -10, -5, 0]
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    fig =   CATEGORIES.annual_heatmap(
+            series=collection_to_series(epw.dew_point_temperature),
+            ax=None,
+            title="Condensation Risk Heatmap",
+        ).get_figure()
+
+    return_dict = {"data": collection_metadata(hcc)}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: ```suggestion
    return_dict = {"data": collection_metadata(epw.dew_point_temperature)}
```
see above comment
created_at: 2025-03-04T09:18:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
+    
+    thresholds = [-20, -15, -10, -5, 0]
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    fig =   CATEGORIES.annual_heatmap(
+            series=collection_to_series(epw.dew_point_temperature),
+            ax=None,
+            title="Condensation Risk Heatmap",
+        ).get_figure()
+
+    return_dict = {"data": collection_metadata(hcc)}
+
+    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)
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: In general, we're trying to keep links to bhom within the `bhom/wrapped` folder. This means that for this method, you would need to create a separate method in `bhom/wrapped/plot` that does the argparse and file I/O handling, and keep the method in this file callable from a python script (as you might create a heatmap in jupyter).
created_at: 2025-03-04T09:41:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -553,6 +558,58 @@ def annual_monthly_histogram(
 
         return ax
 
+    @bhom_analytics()
+    def annual_monthly_table(
+        self,
+        series: pd.Series,
+        ax: plt.Axes = None,
+        show_legend: bool = False,
+        show_labels: bool = False,
+        **kwargs,
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
user: login: Tom-Kingstone
body: `show_legend`, `show_labels` and `**kwargs` are not used in this method - did you mean to use them?

Not sure what the first two would be used for in the table, but at least **kwargs could be passed to ax.table.
created_at: 2025-03-13T08:59:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -553,6 +558,58 @@ def annual_monthly_histogram(
 
         return ax
 
+    @bhom_analytics()
+    def annual_monthly_table(
+        self,
+        series: pd.Series,
+        ax: plt.Axes = None,
+        show_legend: bool = False,
+        show_labels: bool = False,
+        **kwargs,
+    ) -> plt.Axes:
+        """Create a monthly histogram of a pandas Series.
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
user: login: Tom-Kingstone
body: ```suggestion
        """Create a monthly table from a pandas Series.
```
Please update the docstrings to be accurate
created_at: 2025-03-13T09:01:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: ```suggestion
) -> Categorical:
```
This probably isn't being done properly throughout the toolkit, but ideally include return type hints where relevant
created_at: 2025-03-13T09:02:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["indigo", "royalblue", "white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: `return_file` and `save_path`  are not used in this method. They seem to be remnants of the wrapped method.

Additionally `**kwargs` could be passed to the threshold and table methods respectively
created_at: 2025-03-13T09:06:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["indigo", "royalblue", "white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a chart with thresholds of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+    epw = EPW(epw_file)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    hcc = epw.dry_bulb_temperature
+    
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    title = kwargs.pop("title", None)
+    figsize = kwargs.pop("figsize", (15, 8))
+
+    # Instantiate figure
+    fig = plt.figure(figsize=figsize, constrained_layout=True)
+    spec = fig.add_gridspec(ncols=1, nrows=2, width_ratios=[1], height_ratios=[6, 5], hspace=0)
+    chart_ax = fig.add_subplot(spec[0, 0])
+    table_ax = fig.add_subplot(spec[1, 0])
+
+    # Add Thresholds Chart
+    CATEGORIES.annual_threshold_chart(series, chart_ax, color = 'slategrey')
+   
+    # Add table
+    CATEGORIES.annual_monthly_table(series, table_ax, False, True)
+
+    title = f"{title}" if title is not None else series.name
+    chart_ax.set_title(title, y=1, ha="left", va="bottom", x=0)
+    chart_ax.set_anchor('W')
+
+    return fig
+
+
+def condensation_risk_heatmap_histogram(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: same here, `return_file`, `save_path` are not used, and `**kwargs` could be passed to plot methods.
created_at: 2025-03-13T09:09:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_chart.py
user: login: Tom-Kingstone
body: ```suggestion
def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None) -> None:
```
bhom/wrapped methods should not use kwargs and do not return anything, but everything else here is fine

Also not sure if you need to do `from typing import List` to be able to do hints for typed lists?
created_at: 2025-03-13T09:13:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_chart.py
user: login: Tom-Kingstone
body: (python typing is a bit strange in my opinion to be honest)
created_at: 2025-03-13T09:13:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a histogram of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+
+    epw = EPW(epw_file)
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    dataColl = CATEGORIES.timeseries_summary_monthly(series, density=False)
+
+    fig = condensation_risk_chart(epw_file, thresholds, return_file, save_path).get_figure()
+
+    return_dict = {"data": hcc}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_chart.py
user: login: Tom-Kingstone
body: This will cause errors when executed from the adapter, but you will still get the image at least - @jamesramsden-bh maybe it would be an idea to provide the data as `CustomData` in the resulting `PlotInformation` so nothing is lost if the adapter can't convert?

That or use the `collection_metadata` method here.
created_at: 2025-03-13T09:29:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: ```suggestion
def condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None) -> None:
```
same here
created_at: 2025-03-13T09:30:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,101 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a histogram of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+
+    epw = EPW(epw_file)
+    hcc = epw.dry_bulb_temperature
+
+    fig = condensation_risk_heatmap_histogram(epw_file, thresholds, return_file, save_path).get_figure()
+
+    return_dict = {"data": hcc}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_heatmap.py
user: login: Tom-Kingstone
body: and again, this will cause error c# side
created_at: 2025-03-13T09:30:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_chart.py
user: login: enarhi
body: fixed
created_at: 2025-03-13T16:06:59Z
author_association: MEMBER
diff_hunk: @@ -553,6 +558,58 @@ def annual_monthly_histogram(
 
         return ax
 
+    @bhom_analytics()
+    def annual_monthly_table(
+        self,
+        series: pd.Series,
+        ax: plt.Axes = None,
+        show_legend: bool = False,
+        show_labels: bool = False,
+        **kwargs,
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
user: login: enarhi
body: removed the first two, kwargs added to ax.table
created_at: 2025-03-13T16:08:27Z
author_association: MEMBER
diff_hunk: @@ -553,6 +558,58 @@ def annual_monthly_histogram(
 
         return ax
 
+    @bhom_analytics()
+    def annual_monthly_table(
+        self,
+        series: pd.Series,
+        ax: plt.Axes = None,
+        show_legend: bool = False,
+        show_labels: bool = False,
+        **kwargs,
+    ) -> plt.Axes:
+        """Create a monthly histogram of a pandas Series.
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py
user: login: enarhi
body: fixed
created_at: 2025-03-13T16:10:36Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: Added
created_at: 2025-03-13T16:12:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["indigo", "royalblue", "white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: removed redundant args, and added kwargs to plt calls
created_at: 2025-03-13T16:14:21Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,104 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a histogram of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+
+    epw = EPW(epw_file)
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    dataColl = CATEGORIES.timeseries_summary_monthly(series, density=False)
+
+    fig = condensation_risk_chart(epw_file, thresholds, return_file, save_path).get_figure()
+
+    return_dict = {"data": hcc}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_chart.py
user: login: enarhi
body: for now I have resolved by just returning the base epw collection metadata. Ideally in the future we will have a way to return relevant data for a df here.
created_at: 2025-03-13T16:15:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,101 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_heatmap.py
user: login: enarhi
body: fixed
created_at: 2025-03-13T16:18:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,136 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["indigo", "royalblue", "white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+def condensation_risk_chart(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a chart with thresholds of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+    epw = EPW(epw_file)
+    series = collection_to_series(epw.dry_bulb_temperature)
+
+    hcc = epw.dry_bulb_temperature
+    
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    title = kwargs.pop("title", None)
+    figsize = kwargs.pop("figsize", (15, 8))
+
+    # Instantiate figure
+    fig = plt.figure(figsize=figsize, constrained_layout=True)
+    spec = fig.add_gridspec(ncols=1, nrows=2, width_ratios=[1], height_ratios=[6, 5], hspace=0)
+    chart_ax = fig.add_subplot(spec[0, 0])
+    table_ax = fig.add_subplot(spec[1, 0])
+
+    # Add Thresholds Chart
+    CATEGORIES.annual_threshold_chart(series, chart_ax, color = 'slategrey')
+   
+    # Add table
+    CATEGORIES.annual_monthly_table(series, table_ax, False, True)
+
+    title = f"{title}" if title is not None else series.name
+    chart_ax.set_title(title, y=1, ha="left", va="bottom", x=0)
+    chart_ax.set_anchor('W')
+
+    return fig
+
+
+def condensation_risk_heatmap_histogram(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: removed, kwargs added
created_at: 2025-03-13T16:19:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,101 @@
+﻿"""Method to wrap creation of diurnal plots"""
+# pylint: disable=C0415,E0401,W0703
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.plot.condensation_risk_heatmap import *
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], return_file: str, save_path: str = None, **kwargs) -> Figure:
+    """Create a histogram of the condensation potential for a given set of
+    timeseries dry bulb temperatures from an EPW.
+
+    Args:
+        epw_file (string):
+            The input EPW file.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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.
+        **kwargs:
+            Additional keyword arguments to pass to the heatmap function.
+
+    Returns:
+        Figure: A matplotlib Figure object.
+    """
+
+    epw = EPW(epw_file)
+    hcc = epw.dry_bulb_temperature
+
+    fig = condensation_risk_heatmap_histogram(epw_file, thresholds, return_file, save_path).get_figure()
+
+    return_dict = {"data": hcc}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/condensation_risk_heatmap.py
user: login: enarhi
body: fixed using collection_metadata for now.
created_at: 2025-03-13T16:19:24Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
+    
+    thresholds = [-20, -15, -10, -5, 0]
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: resolved, thresholds are now user provided, with argparse handling for thresholds added.
created_at: 2025-03-13T20:05:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
+    
+    thresholds = [-20, -15, -10, -5, 0]
+    thresholds.insert(0,-np.inf)
+    thresholds.append(np.inf)
+
+    CATEGORIES = condensation_categories_from_thresholds(thresholds)
+
+    fig =   CATEGORIES.annual_heatmap(
+            series=collection_to_series(epw.dew_point_temperature),
+            ax=None,
+            title="Condensation Risk Heatmap",
+        ).get_figure()
+
+    return_dict = {"data": collection_metadata(hcc)}
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: fixed using collection_metadata for now.
created_at: 2025-03-13T20:05:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,111 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+from pathlib import Path
+import matplotlib
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+def condensation_categories_from_thresholds(
+    thresholds: tuple[float],
+):
+    """Create a categorical from provided threshold temperatures.
+
+    Args:
+        thresholds (tuple[float]):
+            The temperature thresholds to be used.
+
+    Returns:
+        Categories: The resulting categories object.
+    """
+    cmap = LinearSegmentedColormap.from_list("condensation", ["black","purple","blue","white"], N=100)
+    return Categorical.from_cmap(thresholds, cmap)
+
+
+def condensation_risk_heatmap(epw_file: str, thresholds: list[float], 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.
+        thresholds (list[float]):
+            The temperature thresholds to use.
+        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)
+    dbt_series = collection_to_series(epw.dry_bulb_temperature)
+
+    header = header_from_string("Condensation Thresholds (C)")
+    hcc = HourlyContinuousCollection(header = header, values = dbt_series.values)
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/condensation_risk_heatmap.py
user: login: enarhi
body: removed as noted
created_at: 2025-03-13T20:06:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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 FacadeCondensationRiskCommand : ISimulationCommand
+    {
+        [Description("The path to an EPW file.")]
+        public virtual FileSettings EPWFile { get; set; } = new FileSettings();
+
+        [Description("The list of thresholds to use.")]
+        public virtual List<double> Thresholds { get; set; } = new List<double>();
+
+        [Description("Boolean indicating whether to return heatmap or just the chart and table.")]
path: LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
user: login: jamesramsden-bh
body: ```suggestion
        [Description("Set to true to return a heatmap, or set to false to return a chart and table.")]
```
created_at: 2025-04-22T16:38:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,100 @@
+﻿/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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<object> RunCommand(FacadeCondensationRiskCommand 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;
+            }
+
+            List<double> thresholds;
+            if (command.Thresholds == null || command.Thresholds.Count() == 0)
+            {
+                BH.Engine.Base.Compute.RecordWarning($"{nameof(command.Thresholds)} input was null, and has been replaced with default values.");
+                thresholds = new List<double> { -10, -5, 0, 5, 10};
path: LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
user: login: jamesramsden-bh
body: If the user does not provide any thresholds, then this list is passed down the chain - it looks like it will eventually override the defaults in line 24 of facades\condensation_risk\heatmap.py. Is this correct?
created_at: 2025-04-22T16:43:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,100 @@
+﻿/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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<object> RunCommand(FacadeCondensationRiskCommand 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;
+            }
+
+            List<double> thresholds;
+            if (command.Thresholds == null || command.Thresholds.Count() == 0)
+            {
+                BH.Engine.Base.Compute.RecordWarning($"{nameof(command.Thresholds)} input was null, and has been replaced with default values.");
path: LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
user: login: jamesramsden-bh
body: This probably doesn't need to be a warning. Either just an info, or remove altogether and update the attribute text to say that if this field is left blank, then default values will be used instead. 
created_at: 2025-04-22T16:44:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,135 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+
+default_thresholds = [10,7,4,1,-2,-5]
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/facades/condensation_risk/heatmap.py
user: login: jamesramsden-bh
body: Does this produce expected behaviour? I would expect it is looking for default thresholds to be in ascending order. 

Also good practice to capitalise constants. (Remember to also update any references to this value below if you accept this change.)

```suggestion
DEFAULT_THRESHOLDS = [10,7,4,1,-2,-5]
```
created_at: 2025-04-22T16:46:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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 FacadeCondensationRiskCommand : ISimulationCommand
+    {
+        [Description("The path to an EPW file.")]
+        public virtual FileSettings EPWFile { get; set; } = new FileSettings();
+
+        [Description("The list of thresholds to use.")]
path: LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
user: login: jamesramsden-bh
body: Add something like "If no values are provided, default values of XYZ will be used instead."
created_at: 2025-04-22T16:49:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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 FacadeCondensationRiskCommand : ISimulationCommand
+    {
+        [Description("The path to an EPW file.")]
+        public virtual FileSettings EPWFile { get; set; } = new FileSettings();
+
+        [Description("The list of thresholds to use.")]
+        public virtual List<double> Thresholds { get; set; } = new List<double>();
+
+        [Description("Boolean indicating whether to return heatmap or just the chart and table.")]
path: LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
user: login: enarhi
body: Updated.
created_at: 2025-05-06T16:20:37Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,100 @@
+﻿/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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<object> RunCommand(FacadeCondensationRiskCommand 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;
+            }
+
+            List<double> thresholds;
+            if (command.Thresholds == null || command.Thresholds.Count() == 0)
+            {
+                BH.Engine.Base.Compute.RecordWarning($"{nameof(command.Thresholds)} input was null, and has been replaced with default values.");
+                thresholds = new List<double> { -10, -5, 0, 5, 10};
path: LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
user: login: enarhi
body: Default values aligned, but kept the defaults in both locations so that we still have the fallback defaults if the heatmap.py method is called from elsewhere or directly without threshold values.
created_at: 2025-05-06T16:21:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,100 @@
+﻿/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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<object> RunCommand(FacadeCondensationRiskCommand 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;
+            }
+
+            List<double> thresholds;
+            if (command.Thresholds == null || command.Thresholds.Count() == 0)
+            {
+                BH.Engine.Base.Compute.RecordWarning($"{nameof(command.Thresholds)} input was null, and has been replaced with default values.");
path: LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs
user: login: enarhi
body: Removed.
created_at: 2025-05-06T16:21:41Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,135 @@
+"""Plotting methods for condensation risk."""
+
+import argparse
+import textwrap
+
+from pathlib import Path
+import matplotlib
+import matplotlib.pyplot as plt
+from matplotlib.figure import Figure
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import json
+import numpy as np
+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
+from ladybugtools_toolkit.plot.utilities import figure_to_base64
+from ladybugtools_toolkit.categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical
+
+
+default_thresholds = [10,7,4,1,-2,-5]
path: LadybugTools_Engine/Python/src/ladybugtools_toolkit/plot/facades/condensation_risk/heatmap.py
user: login: enarhi
body: Updated.
created_at: 2025-05-06T16:21:47Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,48 @@
+/*
+ * 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 <https://www.gnu.org/licenses/lgpl-3.0.html>.      
+ */
+
+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 FacadeCondensationRiskCommand : ISimulationCommand
+    {
+        [Description("The path to an EPW file.")]
+        public virtual FileSettings EPWFile { get; set; } = new FileSettings();
+
+        [Description("The list of thresholds to use.")]
path: LadybugTools_oM/ExecuteCommands/FacadeCondensationRiskCommand.cs
user: login: enarhi
body: Added.
created_at: 2025-05-06T16:21:56Z
author_association: MEMBER

PR comments:
user: login: enarhi
created_at: 2025-05-08T15:55:03Z
body: @BHoMBot check ready-to-merge
author_association: MEMBER
user: login: bhombot-ci[bot]
created_at: 2025-05-08T15:55:06Z
body: <samp>@enarhi to confirm, the following actions are now queued:

 - check `ready-to-merge`
</samp>
author_association: NONE
user: login: enarhi
created_at: 2025-05-08T20:35:15Z
body: @BHoMBot check versioning
author_association: MEMBER
user: login: bhombot-ci[bot]
created_at: 2025-05-08T20:35:17Z
body: <samp>@enarhi to confirm, the following actions are now queued:

 - check `versioning`
</samp>
author_association: NONE
user: login: enarhi
created_at: 2025-05-09T16:41:12Z
body: @BHoMBot check ready-to-merge

author_association: MEMBER
user: login: bhombot-ci[bot]
created_at: 2025-05-09T16:41:14Z
body: <samp>@enarhi to confirm, the following actions are now queued:

 - check `ready-to-merge`
</samp>
author_association: NONE