diff --git a/LadybugTools_Adapter/AdapterActions/Execute.cs b/LadybugTools_Adapter/AdapterActions/Execute.cs index 9c2fed03..2b86a36c 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute.cs @@ -37,7 +37,6 @@ using BH.Engine.Base; using System.Drawing; using BH.Engine.Serialiser; -using BH.Engine.LadyBugTools; using System.Reflection; namespace BH.Adapter.LadybugTools @@ -312,12 +311,25 @@ private List RunCommand(HeatPlotCommand command, ActionConfig actionConf if (colourMap.ColourMapValidity()) colourMap = colourMap.ToColourMap().FromColourMap(); + string returnFile = Path.GetTempFileName(); + // run the process - string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -dtk \"{command.EPWKey.ToText()}\" -cmap \"{colourMap}\" -p \"{command.OutputLocation}\""; + string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -dtk \"{command.EPWKey.ToText()}\" -cmap \"{colourMap}\" -r \"{returnFile.Replace('\\', '/')}\" -p \"{command.OutputLocation}\""; string result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + if (!File.Exists(result)) + { + BH.Engine.Base.Compute.RecordError($"An error occurred while running the command: {result}"); + File.Delete(returnFile); + return new List(); + } + + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); + m_executeSuccess = true; - return new List() { result }; + return new List() { info }; } /**************************************************/ @@ -351,12 +363,25 @@ private List RunCommand(WindroseCommand command, ActionConfig actionConf if (colourMap.ColourMapValidity()) colourMap = colourMap.ToColourMap().FromColourMap(); + string returnFile = Path.GetTempFileName(); + // run the process - string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -ap \"{command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\"")}\" -cmap \"{colourMap}\" -bins \"{command.NumberOfDirectionBins}\" -p \"{command.OutputLocation}\""; + string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -ap \"{command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\"")}\" -cmap \"{colourMap}\" -bins \"{command.NumberOfDirectionBins}\" -r \"{returnFile.Replace('\\', '/')}\" -p \"{command.OutputLocation}\""; string result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + if (!File.Exists(result)) + { + BH.Engine.Base.Compute.RecordError($"An error occurred while running the command: {result}"); + File.Delete(returnFile); + return new List(); + } + + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + PlotInformation info = Convert.ToPlotInformation(obj, new WindroseData()); + m_executeSuccess = true; - return new List { result }; + return new List { info }; } /**************************************************/ @@ -397,6 +422,7 @@ private List RunCommand(UTCIHeatPlotCommand command, ActionConfig action BH.Engine.Base.Compute.RecordError($"When overriding bin colours 10 colours must be provided, but {command.BinColours.Count} colours were provided instead."); return null; } + List colours = command.BinColours.Select(x => x.ToHexCode()).ToList(); string hexColours = $"[\"{string.Join("\",\"", colours)}\"]"; @@ -418,25 +444,29 @@ private List RunCommand(UTCIHeatPlotCommand command, ActionConfig action string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped\\plot", "utci_heatmap.py"); + string returnFile = Path.GetTempFileName(); + // run the process - string cmdCommand = $"{m_environment.Executable} \"{script}\" -e \"{epwFile}\" -in \"{argFile}\" -ws \"{command.WindSpeedMultiplier}\" -sp \"{command.OutputLocation}\""; - string result = ""; + string cmdCommand = $"{m_environment.Executable} \"{script}\" -e \"{epwFile}\" -in \"{argFile}\" -ws \"{command.WindSpeedMultiplier}\" -r \"{returnFile.Replace('\\', '/')}\" -sp \"{command.OutputLocation}\""; + string result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); - try - { - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); - } - catch (Exception ex) - { - BH.Engine.Base.Compute.RecordError(ex, "An error occurred while running some python."); - } - finally + 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); File.Delete(argFile); + return new List(); } + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + File.Delete(argFile); + PlotInformation info = Convert.ToPlotInformation(obj, new UTCIData()); + m_executeSuccess = true; - return new List { result.Split('\n').Last() }; + return new List { info }; } /**************************************************/ @@ -473,12 +503,27 @@ private List RunCommand(DiurnalPlotCommand command, ActionConfig actionC string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped\\plot", "diurnal.py"); + string returnFile = Path.GetTempFileName(); + // run the process - string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -dtk \"{command.EPWKey.ToText()}\" -c \"{command.Colour.ToHexCode()}\" -t \"{command.Title}\" -ap \"{command.Period.ToString().ToLower()}\" -p \"{command.OutputLocation}\""; + string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -dtk \"{command.EPWKey.ToText()}\" -c \"{command.Colour.ToHexCode()}\" -t \"{command.Title}\" -ap \"{command.Period.ToString().ToLower()}\" -r \"{returnFile.Replace('\\', '/')}\" -p \"{command.OutputLocation}\""; string result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + string resultFile = result.Split('\n').Last(); + + if (!File.Exists(resultFile)) + { + BH.Engine.Base.Compute.RecordError($"An error occurred while running the command: {result}"); + File.Delete(returnFile); + return new List(); + } + + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); + m_executeSuccess = true; - return new List() { result.Split('\n').Last() }; + return new List() { info }; } /**************************************************/ @@ -513,12 +558,27 @@ private List RunCommand(SunPathPlotCommand command, ActionConfig actionC string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped\\plot", "sunpath.py"); + string returnFile = Path.GetTempFileName(); + //run the process - string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -s {command.SunSize} -ap \"{command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\"")}\" -p \"{command.OutputLocation}\""; + string cmdCommand = $"{m_environment.Executable} {script} -e \"{epwFile}\" -s {command.SunSize} -ap \"{command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\"")}\" -r \"{returnFile.Replace('\\', '/')}\" -p \"{command.OutputLocation}\""; string result = Engine.Python.Compute.RunCommandStdout(cmdCommand, hideWindows: true); + string resultFile = result.Split('\n').Last(); + + if (!File.Exists(resultFile)) + { + BH.Engine.Base.Compute.RecordError($"An error occurred while running the command: {result}"); + File.Delete(returnFile); + return new List(); + } + + CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(System.IO.File.ReadAllText(returnFile)); + File.Delete(returnFile); + PlotInformation info = Convert.ToPlotInformation(obj, new SunPathData()); + m_executeSuccess = true; - return new List() { result.Split('\n').Last() }; + return new List() { info }; } /**************************************************/ diff --git a/LadybugTools_Adapter/Convert/MetaData/PlotInformation.cs b/LadybugTools_Adapter/Convert/MetaData/PlotInformation.cs new file mode 100644 index 00000000..e6569a44 --- /dev/null +++ b/LadybugTools_Adapter/Convert/MetaData/PlotInformation.cs @@ -0,0 +1,362 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.oM.LadybugTools; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Text; + +namespace BH.Adapter.LadybugTools +{ + public static partial class Convert + { + + /**************************************************/ + /**** Public Methods: Interface ****/ + /**************************************************/ + + public static PlotInformation ToPlotInformation(this CustomObject oldObject, ISimulationData toUpdate) + { + PlotInformation plotInformation = new PlotInformation(); + + plotInformation.Image = oldObject.CustomData["figure"].ToString(); + + plotInformation.OtherData = ToSimulationData((oldObject.CustomData["data"] as CustomObject).CustomData, toUpdate as dynamic); + + return plotInformation; + + } + + /**************************************************/ + /**** Private Methods: Deserialise ****/ + /**************************************************/ + + private static CollectionData ToSimulationData(this Dictionary oldData, CollectionData toUpdate) + { + + if (!double.TryParse(oldData["highest"].ToString(), out double result)) + result = double.NaN; + + toUpdate.HighestValue = result; + + if (!double.TryParse(oldData["lowest"].ToString(), out result)) + result = double.NaN; + + toUpdate.LowestValue = result; + + if (!DateTime.TryParse(oldData["highest_index"].ToString(), out DateTime date)) + date = DateTime.MinValue; + + toUpdate.HighestIndex = date; + + if (!DateTime.TryParse(oldData["lowest_index"].ToString(), out date)) + date = DateTime.MinValue; + + toUpdate.LowestIndex = date; + + if (!double.TryParse(oldData["median"].ToString(), out result)) + result = double.NaN; + + toUpdate.MedianValue = result; + + if (!double.TryParse(oldData["mean"].ToString(), out result)) + result = double.NaN; + + toUpdate.MeanValue = result; + + try + { + List means = oldData["month_means"] as List; + + int monthIndex = 0; + + foreach (object mean in means) + { + if (!double.TryParse(mean.ToString(), out double value)) + value = double.NaN; + + toUpdate.MonthlyMeans[monthIndex] = value; + monthIndex++; + } + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the monthly means:"); + } + + return toUpdate; + } + + /**************************************************/ + + private static WindroseData ToSimulationData(this Dictionary oldData, WindroseData toUpdate) + { + if (!double.TryParse(oldData["prevailing_95percentile"].ToString(), out double result)) + result = double.NaN; + toUpdate.PrevailingPercentile95 = result; + + try + { + List tuple = oldData["prevailing_direction"] as List; + int index = 0; + + foreach (object value in tuple) + { + if (!double.TryParse(value.ToString(), out result)) + result = double.NaN; + + toUpdate.PrevailingDirection[index] = result; + index++; + } + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the prevailing wind direction:"); + } + + if (!double.TryParse(oldData["prevailing_50percentile"].ToString(), out result)) + result = double.NaN; + toUpdate.PrevailingPercentile50 = result; + + if (!double.TryParse(oldData["95percentile"].ToString(), out result)) + result = double.NaN; + toUpdate.Percentile95 = result; + + if (!double.TryParse(oldData["50percentile"].ToString(), out result)) + result = double.NaN; + toUpdate.Percentile50 = result; + + if (!double.TryParse(oldData["calm_percent"].ToString(), out result)) + result = double.NaN; + toUpdate.RatioOfCalmHours = result; + + return toUpdate; + } + + /**************************************************/ + + private static SunPathData ToSimulationData(this Dictionary oldData, SunPathData toUpdate) + { + try + { + Dictionary decemberObject = (oldData["december_solstice"] as CustomObject).CustomData; + + Dictionary sunset = (decemberObject["sunset"] as CustomObject).CustomData; + + if (!double.TryParse(sunset["azimuth"].ToString(), out double result)) + result = double.NaN; + toUpdate.DecemberSolstice.SunsetAzimuth = result; + + if (!DateTime.TryParse(sunset["time"].ToString(), out DateTime date)) + date = DateTime.MinValue; + toUpdate.DecemberSolstice.SunsetTime = date; + + Dictionary sunrise = (decemberObject["sunrise"] as CustomObject).CustomData; + + if (!double.TryParse(sunrise["azimuth"].ToString(), out result)) + result = double.NaN; + toUpdate.DecemberSolstice.SunriseAzimuth = result; + + if (!DateTime.TryParse(sunrise["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.DecemberSolstice.SunriseTime = date; + + Dictionary noon = (decemberObject["noon"] as CustomObject).CustomData; + + if (!double.TryParse(noon["altitude"].ToString(), out result)) + result = double.NaN; + toUpdate.DecemberSolstice.NoonAltitude = result; + + if (!DateTime.TryParse(noon["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.DecemberSolstice.NoonTime = date; + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the December solstice:"); + } + + try + { + Dictionary marchObject = (oldData["march_equinox"] as CustomObject).CustomData; + + Dictionary sunset = (marchObject["sunset"] as CustomObject).CustomData; + + if (!double.TryParse(sunset["azimuth"].ToString(), out double result)) + result = double.NaN; + toUpdate.MarchEquinox.SunsetAzimuth = result; + + if (!DateTime.TryParse(sunset["time"].ToString(), out DateTime date)) + date = DateTime.MinValue; + toUpdate.MarchEquinox.SunsetTime = date; + + Dictionary sunrise = (marchObject["sunrise"] as CustomObject).CustomData; + + if (!double.TryParse(sunrise["azimuth"].ToString(), out result)) + result = double.NaN; + toUpdate.MarchEquinox.SunriseAzimuth = result; + + if (!DateTime.TryParse(sunrise["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.MarchEquinox.SunriseTime = date; + + Dictionary noon = (marchObject["noon"] as CustomObject).CustomData; + + if (!double.TryParse(noon["altitude"].ToString(), out result)) + result = double.NaN; + toUpdate.MarchEquinox.NoonAltitude = result; + + if (!DateTime.TryParse(noon["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.MarchEquinox.NoonTime = date; + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the March equinox:"); + } + + try + { + Dictionary juneObject = (oldData["june_solstice"] as CustomObject).CustomData; + + Dictionary sunset = (juneObject["sunset"] as CustomObject).CustomData; + + if (!double.TryParse(sunset["azimuth"].ToString(), out double result)) + result = double.NaN; + toUpdate.JuneSolstice.SunsetAzimuth = result; + + if (!DateTime.TryParse(sunset["time"].ToString(), out DateTime date)) + date = DateTime.MinValue; + toUpdate.JuneSolstice.SunsetTime = date; + + Dictionary sunrise = (juneObject["sunrise"] as CustomObject).CustomData; + + if (!double.TryParse(sunrise["azimuth"].ToString(), out result)) + result = double.NaN; + toUpdate.JuneSolstice.SunriseAzimuth = result; + + if (!DateTime.TryParse(sunrise["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.JuneSolstice.SunriseTime = date; + + Dictionary noon = (juneObject["noon"] as CustomObject).CustomData; + + if (!double.TryParse(noon["altitude"].ToString(), out result)) + result = double.NaN; + toUpdate.JuneSolstice.NoonAltitude = result; + + if (!DateTime.TryParse(noon["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.JuneSolstice.NoonTime = date; + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the June solstice:"); + } + + try + { + Dictionary septemberObject = (oldData["september_equinox"] as CustomObject).CustomData; + + Dictionary sunset = (septemberObject["sunset"] as CustomObject).CustomData; + + if (!double.TryParse(sunset["azimuth"].ToString(), out double result)) + result = double.NaN; + toUpdate.SeptemberEquinox.SunsetAzimuth = result; + + if (!DateTime.TryParse(sunset["time"].ToString(), out DateTime date)) + date = DateTime.MinValue; + toUpdate.SeptemberEquinox.SunsetTime = date; + + Dictionary sunrise = (septemberObject["sunrise"] as CustomObject).CustomData; + + if (!double.TryParse(sunrise["azimuth"].ToString(), out result)) + result = double.NaN; + toUpdate.SeptemberEquinox.SunriseAzimuth = result; + + if (!DateTime.TryParse(sunrise["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.SeptemberEquinox.SunriseTime = date; + + Dictionary noon = (septemberObject["noon"] as CustomObject).CustomData; + + if (!double.TryParse(noon["altitude"].ToString(), out result)) + result = double.NaN; + toUpdate.SeptemberEquinox.NoonAltitude = result; + + if (!DateTime.TryParse(noon["time"].ToString(), out date)) + date = DateTime.MinValue; + toUpdate.SeptemberEquinox.NoonTime = date; + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, "An error occurred while deserialising the September equinox:"); + } + + return toUpdate; + } + + /**************************************************/ + + private static UTCIData ToSimulationData(this Dictionary oldData, UTCIData toUpdate) + { + if (!double.TryParse(oldData["comfortable_ratio"].ToString(), out double result)) + result = double.NaN; + toUpdate.ComfortableRatio = result; + + if (!double.TryParse(oldData["hot_ratio"].ToString(), out result)) + result = double.NaN; + toUpdate.HeatStressRatio = result; + + if (!double.TryParse(oldData["cold_ratio"].ToString(), out result)) + result = double.NaN; + toUpdate.ColdStressRatio = result; + + if (!double.TryParse(oldData["daytime_comfortable"].ToString(), out result)) + result = double.NaN; + toUpdate.DaytimeComfortableRatio = result; + + if (!double.TryParse(oldData["daytime_hot"].ToString(), out result)) + result = double.NaN; + toUpdate.DaytimeHeatStressRatio = result; + + if (!double.TryParse(oldData["daytime_cold"].ToString(), out result)) + result = double.NaN; + toUpdate.DaytimeColdStressRatio = result; + + return toUpdate; + } + + /**************************************************/ + /**** Private Methods: Fallback ****/ + /**************************************************/ + + private static ISimulationData ToSimulationData(Dictionary oldObject, ISimulationData toUpdate) + { + BH.Engine.Base.Compute.RecordError($"The simulation data type {toUpdate.GetType().FullName} is not supported with the LadybugToolsAdapter. No simulation data has been returned with this action."); + return null; + } + } +} \ No newline at end of file diff --git a/LadybugTools_Engine/Convert/HexCode.cs b/LadybugTools_Engine/Convert/HexCode.cs index c79db3b3..f2b65e0c 100644 --- a/LadybugTools_Engine/Convert/HexCode.cs +++ b/LadybugTools_Engine/Convert/HexCode.cs @@ -30,13 +30,14 @@ using System.ComponentModel; using BH.oM.Base.Attributes; -namespace BH.Engine.LadyBugTools +namespace BH.Engine.LadybugTools { public static partial class Convert { [Description("Converts a colour to its respective RGB hexadecimal code (eg. white => #ffffff).")] [Input("colour", "The colour to convert into a hex code.")] [Output("hex", "The corresponding hex code.")] + [PreviousVersion("7.3", "BH.Engine.LadyBugTools.Convert.ToHexCode(System.Drawing.Color)")] public static string ToHexCode(this Color colour) { return $"#{colour.R.ToString("X2")}{colour.G.ToString("X2")}{colour.B.ToString("X2")}"; @@ -47,6 +48,7 @@ public static string ToHexCode(this Color colour) [Description("Converts a string that is in the RGB hexadecimal format into a colour. (eg. #ffffff => white).")] [Input("hex", "The hexadecimal representation of a colour.")] [Output("colour", "The corresponding colour.")] + [PreviousVersion("7.3", "BH.Engine.LadyBugTools.Convert.FromHexCode(System.String)")] public static Color? FromHexCode(this string hex) { if (hex.IsNullOrEmpty()) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/collection.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/collection.py new file mode 100644 index 00000000..ce9a498b --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/collection.py @@ -0,0 +1,47 @@ +from ladybug.datacollection import BaseCollection +from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series + +def collection_metadata(collection: BaseCollection) -> dict: + """Returns a dictionary containing useful metadata about the series. + + Args: + collection (BaseCollection): + ladybug data collection object + + Returns: + dict: + A dictionary containing metadata about the collection, structured: + { + "lowest": lowest, + "lowest_index": lowest_index, + "highest": highest, + "highest_index": highest_index, + "median": median, + "mean": mean, + "month_means": [month_means], + } + where month_means is a list of means indexed by month, and month_ranges is a list of diurnal month ranges as tuples: (min, max). + """ + + series = collection_to_series(collection) + lowest = series.min() + highest = series.max() + lowest_index = series.idxmin() + highest_index = series.idxmax() + median = series.quantile(0.5) + mean = series.mean() + + month_means = [] + for month in range(12): + month_series = series[series.index.month == month + 1] + month_means.append(month_series.mean()) + + return { + "lowest": lowest, + "lowest_index": lowest_index, + "highest": highest, + "highest_index": highest_index, + "median": median, + "mean": mean, + "month_means": month_means, + } \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/sunpath_metadata.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/sunpath_metadata.py new file mode 100644 index 00000000..a44a7c9a --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/sunpath_metadata.py @@ -0,0 +1,34 @@ +from ladybug.sunpath import Sunpath +from ladybugtools_toolkit.ladybug_extension.sunpath import sunrise_sunset_azimuths +from datetime import datetime + +def sunpath_metadata(sunpath: Sunpath) -> dict: + """Return a dictionary containing equinox and solstice azimuths and altitudes at sunrise, noon and sunset for the given sunpath. + + Args: + sunpath (Sunpath): + A Ladybug sunpath object. + + Returns: + dict: + A dictionary containing the azimuths and altitudes in the following structure: + + { + 'december_solstice': {'sunrise': azimuth, 'noon': altitude, 'sunset': azimuth}, + 'march_equinox': {...}, + 'june_solstice': {...}, + 'september_equinox': {...} + } + """ + + december_solstice = sunrise_sunset_azimuths(sunpath, 2023, 12, 22) + march_equinox = sunrise_sunset_azimuths(sunpath, 2023, 3, 20) + june_solstice = sunrise_sunset_azimuths(sunpath, 2023, 6, 21) + september_equinox = sunrise_sunset_azimuths(sunpath, 2023, 9, 22) + + return { + "december_solstice": december_solstice, + "march_equinox": march_equinox, + "june_solstice": june_solstice, + "september_equinox": september_equinox + } \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/utci_metadata.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/utci_metadata.py new file mode 100644 index 00000000..a537e56b --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/utci_metadata.py @@ -0,0 +1,63 @@ +from ladybug.datacollection import HourlyContinuousCollection +from ladybug.datatype.temperature import ( + UniversalThermalClimateIndex as LB_UniversalThermalClimateIndex, +) +from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series + +def utci_metadata(utci_collection: HourlyContinuousCollection, comfort_lower: float = 9, comfort_higher: float = 26, use_start_hour: int=7, use_end_hour: int=23) -> dict: + """Returns a dictionary of useful metadata for the given collection dependant on the given comfortable range. + + Args: + utci_collection (HourlyContinuousCollection): + utci headered ladybug hourly collection + + comfort_lower (float): + lower value for the comfortable temperature range, where temperatures exclusively below this are too cold. + + comfort_higher (float): + higher value for the comfortable temperature range, where temperatures above and equal to this are too hot. + + use_start_hour (int): + start hour to filter usage time, inclusive + + use_end_hour (int): + end hour to filter usage time, exclusive + + Returns: + dict: + dictionary containing comfortable, hot and cold ratios, structured as follows: + { + 'comfortable_ratio': ratio_of_comfortable_hours, + 'hot_ratio': ratio_of_hot_hours, + 'cold_ratio': ratio_of_cold_hours, + 'daytime_comfortable': daytime_comfortable_ratio, + 'daytime_hot': daytime_hot_ratio, + 'daytime_cold': daytime_cold_ratio + } + """ + if not isinstance(utci_collection.header.data_type, LB_UniversalThermalClimateIndex): + raise ValueError("Input collection is not a UTCI collection.") + + if not comfort_lower < comfort_higher: + raise ValueError(f"The lower comfort temperature {comfort_lower}, must be less than the higher comfort temperature {comfort_higher}.") + + series = collection_to_series(utci_collection) + + daytime = series.loc[(series.index.hour >= use_start_hour) & (series.index.hour < use_end_hour)] + + comfortable_ratio = ((series >= comfort_lower) & (series < comfort_higher)).sum() / len(series) + hot_ratio = (series >= comfort_higher).sum() / len(series) + cold_ratio = (series < comfort_lower).sum() / len(series) + + day_comfortable = ((daytime >= comfort_lower) & (daytime < comfort_higher)).sum() / len(daytime) + day_hot = (daytime >= comfort_higher).sum() / len(daytime) + day_cold = (daytime < comfort_lower).sum() / len(daytime) + + return { + "comfortable_ratio": comfortable_ratio, + "hot_ratio": hot_ratio, + "cold_ratio": cold_ratio, + "daytime_comfortable": day_comfortable, + "daytime_hot": day_hot, + "daytime_cold": day_cold + } \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/wind_metadata.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/wind_metadata.py new file mode 100644 index 00000000..25b9000a --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/wind_metadata.py @@ -0,0 +1,41 @@ +from ladybugtools_toolkit.wind import Wind + +def wind_metadata(wind_object: Wind, directions: int=36, ignore_calm: bool=True, threshold: float = 1e-10) -> dict: + """Provides a dictionary containing metadata of this wind object. + + Args: + directions (int, optional): + The number of directions to use. Defaults to 36. + ignore_calm (bool, optional): + Whether or not to ignore wind speed values before the threshold, allowing a more accurate prevailing direction and quantile wind speeds. Defaults to True + threshold (float, optional): + The threshold to use for calm wind speeds. Defaults to 1e-10. + + Returns: + dict: + The resultant metadata dictionary, which has the following structure: + { + "95percentile": 95 percentile wind speed, + "50percentile": 50 percentile wind speed, + "calm_percent": the proportion of calm hours (not affected by ignore_calm bool), + "prevailing_direction": direction of prevailing wind, + "prevailing_95percentile": prevailing 95 percentile wind speed, + "prevailing_50percentile": prevailing 50 percentile wind speed + } + + """ + ws = wind_object.ws + + prevailing_wind_speeds, prevailing_directions = wind_object.prevailing_wind_speeds(n=1, directions=directions, ignore_calm=ignore_calm, threshold=threshold) + + prevailing_wind_speed = prevailing_wind_speeds[0] + prevailing_direction = prevailing_directions[0] + + return { + "95percentile": ws.quantile(0.95), + "50percentile": ws.quantile(0.50), + "calm_percent": wind_object.calm(), + "prevailing_direction": prevailing_direction, + "prevailing_95percentile": prevailing_wind_speed.quantile(0.95), + "prevailing_50percentile": prevailing_wind_speed.quantile(0.5) + } \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/diurnal.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/diurnal.py index bce5a5e9..4409a3c0 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/diurnal.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/diurnal.py @@ -1,28 +1,40 @@ """Method to wrap creation of diurnal plots""" # pylint: disable=C0415,E0401,W0703 import argparse +import json import traceback from pathlib import Path -def diurnal(epw_file, data_type_key="Dry Bulb Temperature", color="#000000", title=None, period="monthly", save_path = None): +def diurnal(epw_file, return_file: str, data_type_key="Dry Bulb Temperature", color="#000000", title=None, period="monthly", save_path = None): try: from ladybug.epw import EPW, AnalysisPeriod from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series from ladybugtools_toolkit.plot._diurnal import diurnal from ladybug.datacollection import HourlyContinuousCollection from ladybugtools_toolkit.plot.utilities import figure_to_base64 + from ladybugtools_toolkit.bhom.wrapped.metadata.collection import collection_metadata import matplotlib.pyplot as plt - + epw = EPW(epw_file) data_type_key = data_type_key.replace("_"," ") coll = HourlyContinuousCollection.from_dict([a for a in epw.to_dict()["data_collections"] if a["header"]["data_type"]["name"] == data_type_key][0]) - fig = diurnal(collection_to_series(coll),title=title, period=period, color=color).get_figure() + fig = diurnal(collection_to_series(coll), title=title, period=period, color=color).get_figure() + + + return_dict = {"data": collection_metadata(coll)} + if save_path == None or save_path == "": base64 = figure_to_base64(fig, html=False) - print(base64) + return_dict["figure"] = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - print(save_path) + return_dict["figure"] = save_path + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(return_dict, default=str)) + + print(return_file) + except Exception as e: print(traceback.format_exc()) @@ -67,6 +79,13 @@ def diurnal(epw_file, data_type_key="Dry Bulb Temperature", color="#000000", tit 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", @@ -76,4 +95,4 @@ def diurnal(epw_file, data_type_key="Dry Bulb Temperature", color="#000000", tit ) args = parser.parse_args() - diurnal(args.epw_file, args.data_type_key, args.colour, args.title, args.period, args.save_path) + diurnal(args.epw_file, args.return_file, args.data_type_key, args.colour, args.title, args.period, args.save_path) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/heatmap.py index a8055ed4..adf74e40 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/heatmap.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/heatmap.py @@ -3,15 +3,17 @@ import argparse import traceback from pathlib import Path +import json -def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = None) -> None: +def heatmap(epw_file: str, data_type_key: str, colour_map: str, return_file: str, save_path:str = None) -> None: """Create a CSV file version of an EPW.""" try: from ladybug.epw import EPW from ladybug.datacollection import HourlyContinuousCollection from ladybugtools_toolkit.plot._heatmap import heatmap 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 import matplotlib.pyplot as plt @@ -21,12 +23,22 @@ def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = epw = EPW(epw_file) coll = HourlyContinuousCollection.from_dict([a for a in epw.to_dict()["data_collections"] if a["header"]["data_type"]["name"] == data_type_key][0]) fig = heatmap(collection_to_series(coll), cmap=colour_map).get_figure() + + return_dict = {} + if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - print(base64) + return_dict["figure"] = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - print(save_path) + return_dict["figure"] = save_path + + return_dict["data"] = collection_metadata(coll) + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(return_dict, default=str)) + + print(return_file) except Exception as e: @@ -61,6 +73,13 @@ def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = 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", @@ -70,4 +89,4 @@ def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = ) args = parser.parse_args() - heatmap(args.epw_file, args.data_type_key, args.colour_map, args.save_path) + heatmap(args.epw_file, args.data_type_key, args.colour_map, args.return_file, args.save_path) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/sunpath.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/sunpath.py index 8e2e9b24..43b79361 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/sunpath.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/sunpath.py @@ -4,11 +4,13 @@ import traceback from pathlib import Path -def sun_path(epw_file, analysis_period, size, save_path): +def sun_path(epw_file, analysis_period, size, return_file: str, save_path): try: from ladybugtools_toolkit.plot._sunpath import sunpath from ladybug.epw import EPW, AnalysisPeriod from ladybug.datacollection import HourlyContinuousCollection + from ladybug.sunpath import Sunpath + from ladybugtools_toolkit.bhom.wrapped.metadata.sunpath_metadata import sunpath_metadata from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt from pathlib import Path @@ -16,19 +18,26 @@ def sun_path(epw_file, analysis_period, size, save_path): analysis_period = AnalysisPeriod.from_dict(json.loads(analysis_period)) epw = EPW(epw_file) - collection = epw.global_horizontal_radiation fig = sunpath( location=epw.location, analysis_period=analysis_period, sun_size=size, ).get_figure() + return_dict = {"data": sunpath_metadata(Sunpath.from_location(epw.location))} + if save_path is None or save_path == "": base64 = figure_to_base64(fig, html=False) - print(base64) + return_dict["figure"] = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - print(save_path) + return_dict["figure"] = save_path + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(return_dict, default=str)) + + print(return_file) + except Exception as e: print(e) @@ -59,6 +68,13 @@ def sun_path(epw_file, analysis_period, size, save_path): 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", @@ -68,4 +84,4 @@ def sun_path(epw_file, analysis_period, size, save_path): ) args = parser.parse_args() - sun_path(args.epw_file, args.analysis_period, args.size, args.save_path) \ No newline at end of file + sun_path(args.epw_file, args.analysis_period, args.size, args.return_file, args.save_path) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/utci_heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/utci_heatmap.py index 9fdf871f..fccbc49b 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/utci_heatmap.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/utci_heatmap.py @@ -8,6 +8,7 @@ def utci_heatmap(epw_file:str, json_file:str, + return_file: str, wind_speed_multiplier:float = 1, save_path = None) -> None: from ladybugtools_toolkit.external_comfort.material import Materials @@ -15,6 +16,7 @@ def utci_heatmap(epw_file:str, from ladybugtools_toolkit.external_comfort._typologybase import Typology from ladybugtools_toolkit.external_comfort.simulate import SimulationResult from ladybugtools_toolkit.external_comfort.externalcomfort import ExternalComfort + from ladybugtools_toolkit.bhom.wrapped.metadata.utci_metadata import utci_metadata from ladybugtools_toolkit.plot.utilities import figure_to_base64 from ladybugtools_toolkit.categorical.categories import Categorical, UTCI_DEFAULT_CATEGORIES from honeybee_energy.dictutil import dict_to_material @@ -39,7 +41,6 @@ def utci_heatmap(epw_file:str, custom_bins = UTCI_DEFAULT_CATEGORIES bin_colours = json.loads(argsDict["bin_colours"]) - [print(a) for a in bin_colours] if len(bin_colours) == 10: custom_bins = Categorical( @@ -50,14 +51,24 @@ def utci_heatmap(epw_file:str, fig, ax = plt.subplots(1, 1, figsize=(10, 4)) ec.plot_utci_heatmap(utci_categories = custom_bins) + utci_collection = ec.universal_thermal_climate_index + + return_dict = {"data": utci_metadata(utci_collection)} + plt.tight_layout() + if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - print(base64) + return_dict["figure"] = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - print(save_path) - plt.close(fig) + return_dict["figure"] = base64 + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(return_dict, default=str)) + + print(return_file) + if __name__ == "__main__": parser = argparse.ArgumentParser( @@ -86,6 +97,13 @@ def utci_heatmap(epw_file:str, type=float, required=False, ) + parser.add_argument( + "-r", + "--return_file", + help="json file to write return data to.", + type=str, + required=True, + ) parser.add_argument( "-sp", "--save_path", @@ -96,4 +114,4 @@ def utci_heatmap(epw_file:str, args = parser.parse_args() - utci_heatmap(args.epw_path, args.json_args, args.wind_speed_multiplier, args.save_path) \ No newline at end of file + utci_heatmap(args.epw_path, args.json_args, args.return_file, args.wind_speed_multiplier, args.save_path) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/windrose.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/windrose.py index da0ac818..9834b925 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/windrose.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/windrose.py @@ -5,12 +5,13 @@ from pathlib import Path -def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, save_path: str = None) -> None: +def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, return_file: str, save_path: str = None) -> None: """Method to wrap for creating wind roses from epw files.""" try: from ladybug.epw import EPW, AnalysisPeriod from ladybug.datacollection import HourlyContinuousCollection from ladybugtools_toolkit.wind import Wind + from ladybugtools_toolkit.bhom.wrapped.metadata.wind_metadata import wind_metadata from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt from pathlib import Path @@ -24,21 +25,29 @@ def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, sa w_epw = Wind.from_epw(epw_file) fig, ax = plt.subplots(1, 1, figsize=(6, 6), subplot_kw={"projection": "polar"}) + + wind_filtered = w_epw.filter_by_analysis_period(analysis_period=analysis_period) + w_epw.filter_by_analysis_period(analysis_period=analysis_period).plot_windrose(ax=ax, directions=bins, ylim=(0, 3.6/bins), colors=colour_map) + output_dict = {"data": wind_metadata(wind_filtered, directions=bins)} + plt.tight_layout() if save_path == None or save_path == "": - base64 = figure_to_base64(fig,html=False) - print(base64) + output_dict["figure"] = figure_to_base64(fig,html=False) else: fig.savefig(save_path, dpi=150, transparent=True) - print(save_path) + output_dict["figure"] = save_path + + with open(return_file, "w") as rtn: + rtn.write(json.dumps(output_dict, default=str)) + + print(return_file) except Exception as e: print(e) - if __name__ == "__main__": parser = argparse.ArgumentParser( description=( @@ -73,6 +82,13 @@ def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, sa type=int, 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", @@ -82,4 +98,4 @@ def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, sa ) args = parser.parse_args() - windrose(args.epw_file, args.analysis_period, args.colour_map, args.bins, args.save_path) \ No newline at end of file + windrose(args.epw_file, args.analysis_period, args.colour_map, args.bins, args.return_file, args.save_path) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/helpers.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/helpers.py index 430b37bb..774942f2 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/helpers.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/helpers.py @@ -350,7 +350,7 @@ def scrape_weather( df.interpolate(limit=4, inplace=True) if resample: - df = df.resample("30T").mean() + df = df.resample("30min").mean() return df diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/groundtemperature.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/groundtemperature.py index 99d1edd9..ca9e1e6b 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/groundtemperature.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/groundtemperature.py @@ -203,7 +203,7 @@ def ground_temperature_at_depth( ], index=dbt.resample("D").min().index, ) - .resample("60T") + .resample("60min") .mean() .interpolate() .values diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/sunpath.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/sunpath.py new file mode 100644 index 00000000..6bb8b025 --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/sunpath.py @@ -0,0 +1,39 @@ +from ladybug.sunpath import Sunpath +from ladybug.location import Location +from ..bhom.analytics import bhom_analytics + +import pandas as pd +from datetime import datetime + +@bhom_analytics() +def sunrise_sunset_azimuths(sunpath: Sunpath, year: int, month: int, day: int) -> dict: + """Return a dictionary containing azimuths at sunrise and sunset, and altitude at solar noon. Sunrise and sunset are defined as actual sunrise/sunset, which is the point at which the full height of the sun has passed the horizon. + + Args: + sunpath (Sunpath): + a ladybug Sunpath object + + month (int): + month as an int, starting at 1 and ending at 12 + + day (int): + day as an int starting at 1, and ending between 28 and 31 depending on the month + + Returns: + dict: + A dictionary containing the azimuths and altitude in the following structure: + { + "sunrise": {"time": sunrise time, "azimuth": azimuth at sunrise }, + "noon": {"time": noon time, "altitude" altitude at noon }, + "sunset": {"time": sunset time, "azimuth": azimuth at sunset }, + } + + """ + + sunrise_sunset = sunpath.calculate_sunrise_sunset_from_datetime(datetime(year=year, month=month, day=day)) + + azimuths_altitude = { + k: {"time": datetime(year, v.month, v.day, v.hour, v.minute), "azimuth": sunpath.calculate_sun_from_date_time(v).azimuth} if k in ["sunrise", "sunset"] else {"time": datetime(year, v.month, v.day, v.hour, v.minute), "altitude": sunpath.calculate_sun_from_date_time(v).altitude} for k, v in sunrise_sunset.items() + } + + return azimuths_altitude \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py index 08bfd234..4dd30125 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py @@ -1201,6 +1201,8 @@ def prevailing( directions: int = 36, n: int = 1, as_cardinal: bool = False, + ignore_calm: bool = True, + threshold: float = 1e-10, ) -> list[float] | list[str]: """Calculate the prevailing wind direction/s for this object. @@ -1218,6 +1220,10 @@ def prevailing( """ binned = self.bin_data(directions=directions) + + if ignore_calm: + binned = binned.loc[self.ws > threshold] + prevailing_angles = binned.iloc[:, 0].value_counts().index[:n] if as_cardinal: @@ -1506,6 +1512,34 @@ def summarise(self, directions: int = 36) -> list[str]: ) return return_strings # pylint: enable=line-too-long + + def prevailing_wind_speeds(self, n: int=1, directions: int=36, ignore_calm: bool=True, threshold: float=1e-10) -> tuple[list[pd.Series], list[tuple[float, float]]]: + """Gets the wind speeds for the prevailing directions + + Args: + n (int): + Number of prevailing directions to return. Defaults to 1 + + directions (int): + Number of direction bins to use when calculating the prevailing directions. Defaults to 36 + + ignore_calm (bool): + Whether to ignore calm hours when getting the prevailing directions. Defaults to True + + threshold (float): + The threshold for calm hours. Defaults to 1e-10 + + Returns: + (list[pandas.Series], list[(float, float)]): + Tuple containing a list of time-indexed series containing wind speed data for each prevailing direction, from most to least prevailing, + and a list of wind directions corresponding to the serieses. + """ + prevailing_directions = self.prevailing(n=n, directions=directions, ignore_calm=ignore_calm, threshold=threshold) + + prevailing_wind_speeds = [self.ws.loc[self.bin_data(directions=directions)["Wind Direction (degrees)"] == direction] for direction in prevailing_directions] + + return (prevailing_wind_speeds, prevailing_directions) + def weibull_pdf(self) -> tuple[float]: """Calculate the parameters of an exponentiated Weibull continuous random variable. diff --git a/LadybugTools_oM/MetaData/CollectionData.cs b/LadybugTools_oM/MetaData/CollectionData.cs new file mode 100644 index 00000000..edd15238 --- /dev/null +++ b/LadybugTools_oM/MetaData/CollectionData.cs @@ -0,0 +1,56 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class CollectionData : ISimulationData + { + [Description("The maximum value in the collection.")] + public virtual double HighestValue { get; set; } = double.NaN; + + [Description("The minimum value in the collection.")] + public virtual double LowestValue { get; set; } = double.NaN; + + [Description("The date and time at which the maximum value occurs.")] + public virtual DateTime HighestIndex { get; set; } = DateTime.MinValue; + + [Description("The date and time at which the minimum value occurs.")] + public virtual DateTime LowestIndex { get; set; } = DateTime.MinValue; + + [Description("The median (50 percentile) value in the collection.")] + public virtual double MedianValue { get; set; } = double.NaN; + + [Description("The mean value of the collection.")] + public virtual double MeanValue { get; set; } = double.NaN; + + [Description("The mean values for each month.")] + public virtual List MonthlyMeans { get; set; } = Enumerable.Repeat(double.NaN, 12).ToList(); + } +} diff --git a/LadybugTools_oM/MetaData/ISimulationData.cs b/LadybugTools_oM/MetaData/ISimulationData.cs new file mode 100644 index 00000000..e2fef973 --- /dev/null +++ b/LadybugTools_oM/MetaData/ISimulationData.cs @@ -0,0 +1,33 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BH.oM.LadybugTools +{ + public interface ISimulationData : IObject + { + } +} diff --git a/LadybugTools_oM/MetaData/PlotInformation.cs b/LadybugTools_oM/MetaData/PlotInformation.cs new file mode 100644 index 00000000..79791bc8 --- /dev/null +++ b/LadybugTools_oM/MetaData/PlotInformation.cs @@ -0,0 +1,41 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base; +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class PlotInformation : BHoMObject + { + [Description("The image produced, either as a filepath to the file created, or as a base64 string representation of the image.")] + public virtual string Image { get; set; } = ""; + + [Description("Metadata about the data used to produce the image.")] + public virtual ISimulationData OtherData { get; set; } + } +} diff --git a/LadybugTools_oM/MetaData/SunData.cs b/LadybugTools_oM/MetaData/SunData.cs new file mode 100644 index 00000000..c15abb4f --- /dev/null +++ b/LadybugTools_oM/MetaData/SunData.cs @@ -0,0 +1,52 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class SunData + { + [Description("The azimuth angle at sunrise in degrees. Sunrise is defined as the time at which the sun is first visible above the horizon, ignoring atmospheric effects.")] + public virtual double SunriseAzimuth { get; set; } = double.NaN; + + [Description("The time of actual sunrise, defined as the time at which the sun is first visible above the horizon, ignoring atmospheric effects.")] + public virtual DateTime SunriseTime { get; set; } = DateTime.MinValue; + + [Description("The altitude angle at solar noon (when the sun is at its highest point) in degrees.")] + public virtual double NoonAltitude { get; set; } = double.NaN; + + [Description("The time that the altitude is highest.")] + public virtual DateTime NoonTime { get; set; } = DateTime.MinValue; + + [Description("The azimuth angle at sunset in degrees. Sunset is defined as the time at which the sun has just finished passing the horizon, ignoring atmospheric effects.")] + public virtual double SunsetAzimuth { get; set; } = double.NaN; + + [Description("The time of actual sunset, defined as the time at which the sun has just finished passing the horizon, ignoring atmospheric effects.")] + public virtual DateTime SunsetTime { get; set; } = DateTime.MinValue; + } +} diff --git a/LadybugTools_oM/MetaData/SunPathData.cs b/LadybugTools_oM/MetaData/SunPathData.cs new file mode 100644 index 00000000..0a466e46 --- /dev/null +++ b/LadybugTools_oM/MetaData/SunPathData.cs @@ -0,0 +1,46 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class SunPathData : ISimulationData + { + [Description("Data describing the December (winter) solstice.")] + public virtual SunData DecemberSolstice { get; set; } = new SunData(); + + [Description("Data describing the March (spring) equinox.")] + public virtual SunData MarchEquinox { get; set; } = new SunData(); + + [Description("Data describing the June (summer) solstice.")] + public virtual SunData JuneSolstice { get; set; } = new SunData(); + + [Description("Data describing the September (autumn) equinox.")] + public virtual SunData SeptemberEquinox { get; set; } = new SunData(); + } +} diff --git a/LadybugTools_oM/MetaData/UTCIData.cs b/LadybugTools_oM/MetaData/UTCIData.cs new file mode 100644 index 00000000..a6f6afb7 --- /dev/null +++ b/LadybugTools_oM/MetaData/UTCIData.cs @@ -0,0 +1,52 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class UTCIData : ISimulationData + { + [Description("The ratio of comfortable hours to total hours. Comfortable hours are hours between 9 and 26°C.")] + public virtual double ComfortableRatio { get; set; } = double.NaN; + + [Description("The ratio of heat stress hours to total hours. Heat stress hours are hours greater than 26°C.")] + public virtual double HeatStressRatio { get; set; } = double.NaN; + + [Description("The ratio of cold stress hours to total hours. Cold stress hours are hours less than 9°C.")] + public virtual double ColdStressRatio { get; set; } = double.NaN; + + [Description("The ratio of daytime comfortable hours to daytime hours. Daytime comfortable hours are hours between 9 and 26°C and between 07:00-22:59.")] + public virtual double DaytimeComfortableRatio { get; set; } = double.NaN; + + [Description("The ratio of daytime heat stress hours to daytime hours. Daytime heat stress hours are hours greater than 26°C and between 07:00-22:59.")] + public virtual double DaytimeHeatStressRatio { get; set; } = double.NaN; + + [Description("The ratio of daytime cold stress hours to daytime hours. Daytime cold stress hours are hours less than 9°C and between 07:00-22:59.")] + public virtual double DaytimeColdStressRatio { get; set; } = double.NaN; + } +} diff --git a/LadybugTools_oM/MetaData/WindroseData.cs b/LadybugTools_oM/MetaData/WindroseData.cs new file mode 100644 index 00000000..f520749e --- /dev/null +++ b/LadybugTools_oM/MetaData/WindroseData.cs @@ -0,0 +1,53 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2024, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace BH.oM.LadybugTools +{ + [NoAutoConstructor] + public class WindroseData : ISimulationData + { + [Description("The direction bin of the prevailing wind, defined as two values (in degrees) for the lower and upper values for the bin, where 0 degrees is north.")] + public virtual List PrevailingDirection { get; set; } = Enumerable.Repeat(double.NaN, 2).ToList(); + + [Description("The 95th percentile wind speed value in the prevailing direction.")] + public virtual double PrevailingPercentile95 { get; set; } = double.NaN; + + [Description("The median (50th percentile) wind speed value in the prevailing direction.")] + public virtual double PrevailingPercentile50 { get; set; } = double.NaN; + + [Description("The 95th percentile wind speed value.")] + public virtual double Percentile95 { get; set; } = double.NaN; + + [Description("The median (50th percentile) wind speed value.")] + public virtual double Percentile50 { get; set; } = double.NaN; + + [Description("The ratio of calm hours to total hours. Calm hours are hours with a wind speed of 1e-10 or less.")] + public virtual double RatioOfCalmHours { get; set; } = double.NaN; + } +}