diff --git a/LadybugTools_Adapter/AdapterActions/Execute.cs b/LadybugTools_Adapter/AdapterActions/Execute.cs index 8f35c4a7..75bafeca 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute.cs @@ -21,24 +21,25 @@ */ using BH.Engine.Adapter; +using BH.Engine.Base; using BH.Engine.LadybugTools; +using BH.Engine.Python; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Adapter.Commands; using BH.oM.Base; using BH.oM.Data.Requests; using BH.oM.LadybugTools; using BH.oM.Python; -using BH.Engine.Python; using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; -using System.Text; -using BH.Engine.Base; -using System.Drawing; -using BH.Engine.Serialiser; -using System.Reflection; using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; namespace BH.Adapter.LadybugTools { @@ -85,6 +86,33 @@ private List RunCommand(IExecuteCommand command, ActionConfig actionConf BH.Engine.Base.Compute.RecordError($"The command {command.GetType().FullName} is not valid for the LadybugTools Adapter. Please use a LadybugCommand, or use the correct adapter for the input command."); return new List(); } + + private (string, bool) ExecutePython(List args, string json) + { + string result = ""; + bool success; + + if (m_httpClient != null) + { + Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args, json); + task.Wait(); + (result, success) = task.Result; + } + else + { + //if the server was not running or some other error happened, try running the python directly. + string script = Path.Combine(Engine.Python.Query.DirectoryCode(), m_environment.Name, "src", m_environment.Name.ToLower() , "bhom", "run_wrapped.py"); + string tempFileName = System.IO.Path.GetTempFileName(); + System.IO.File.WriteAllText(tempFileName, json); + string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)} -in \"{tempFileName}\""; + result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + System.IO.File.Delete(tempFileName); + } + + success = !result.Contains("Traceback (most recent call last):"); + + return (result, success); + } } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/CompareEPWKeyPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/CompareEPWKeyPlotCommand.cs index 61081c23..03da2092 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/CompareEPWKeyPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/CompareEPWKeyPlotCommand.cs @@ -22,6 +22,7 @@ using BH.Engine.Adapter; using BH.Engine.Base; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -75,48 +76,43 @@ private List RunCommand(CompareEPWKeyPlotCommand command, ActionConfig a string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()).Replace('\\', '/'); List epwFileList = command.EPWCompareFiles.Select(e => e.GetFullFileName().Replace('\\', '/')).ToList(); + Dictionary dict = new Dictionary() + { + { "epw_list", epwFileList }, + { "data_type_key", command.EPWKey.ToText() }, + { "line", command.PlotTimeseries }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; + + string json = dict.ToJson(); + // run the process List args = new List { "--command", "plot/epw_comparison", - "-e", epwFile, - "-dtk", command.EPWKey.ToText(), - "-p", command.OutputLocation.Replace('\\', '/'), - "-el" //append compare epw file list here + "-e", epwFile }; - args.AddRange(epwFileList); - if (command.PlotTimeseries) - args.Add("-l"); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - string result = ""; - bool success; - - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new NoData()); //this plot type doesn't have collection metadata yet... - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/DiurnalPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/DiurnalPlotCommand.cs index 76801915..fbb88f9e 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/DiurnalPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/DiurnalPlotCommand.cs @@ -23,6 +23,7 @@ using BH.Engine.Adapter; using BH.Engine.Base; using BH.Engine.LadybugTools; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -73,37 +74,44 @@ private List RunCommand(DiurnalPlotCommand command, ActionConfig actionC //string returnFile = Path.GetTempFileName(); + Dictionary dict = new Dictionary() + { + { "data_type_key", command.EPWKey.ToText() }, + { "colour", command.Colour.ToHexCode() }, + { "title", command.Title }, + { "period", command.Period.ToString().ToLower() }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; + + string json = dict.ToJson(); + // run the process - List args = new List() { "--command", "plot/diurnal", "-e", epwFile.Replace('\\', '/'), "-dtk", command.EPWKey.ToText(), "--colour", command.Colour.ToHexCode(), "-t", command.Title, "-ap", command.Period.ToString().ToLower(), "-p", command.OutputLocation.Replace('\\', '/') }; + List args = new List() + { + "--command", "plot/epw_diurnal", + "-e", epwFile.Replace('\\', '/') + }; - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/EPWToCSVCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/EPWToCSVCommand.cs index eaeebc3d..72d27ca2 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/EPWToCSVCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/EPWToCSVCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.LadybugTools; using System; @@ -59,28 +60,20 @@ private List RunCommand(EPWToCSVCommand command, ActionConfig actionConf return null; } - List args = new List() { "--command", "epw_to_csv", "-e", command.EPWFile.GetFullFileName().Replace('\\', '/'), "-a", command.IncludeAdditionalCalculated.ToString() }; + Dictionary dict = new Dictionary() + { + { "include_additional", command.IncludeAdditionalCalculated } + }; - string result = ""; - bool success = true; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; //in this case, result is the text of the csv file. - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); - } + "--command", "epw_to_csv", + "-e", command.EPWFile.GetFullFileName().Replace('\\', '/'), + }; - //as the file output is hard to verify by itself, check that no errors got output to stderr log - success &= !result.Contains("Traceback (most recent call last):"); + (string result, bool success) = ExecutePython(args, json); if (!success) { diff --git a/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs index d43e5599..063fbe67 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/FacadeCondensationRiskCommand.cs @@ -73,39 +73,42 @@ private List RunCommand(FacadeCondensationRiskCommand command, ActionCon else commandArg = "plot/facade_condensation_risk_chart"; + Dictionary dict = new Dictionary() + { + { "thresholds", thresholds }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; + + string json = dict.ToJson(); + //construct args: insert thresholds as a range as concatenating them into a space delimited string causes the numbers to be wrapped in quotes which breaks the python argument parser - List args = new List() { "-command", commandArg, "-e", epwFile.Replace('\\', '/'), "-t", "-p", command.OutputLocation.Replace('\\', '/') }; - args.InsertRange(args.IndexOf("-t") + 1, thresholds.Select(x => x.ToString())); + List args = new List() + { + "-command", commandArg, + "-e", epwFile.Replace('\\', '/') + }; // run the process - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/GEMToHBJSONCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/GEMToHBJSONCommand.cs index d1aef4d7..604b7ab1 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/GEMToHBJSONCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/GEMToHBJSONCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.LadybugTools; using System; @@ -58,28 +59,19 @@ public List RunCommand(GEMToHBJSONCommand command, ActionConfig actionCo return null; } - List args = new List() { "--command", "gem_to_hbjson", "-g", command.GEMFile.GetFullFileName().Replace('\\', '/') }; + Dictionary dict = new Dictionary() + { + { "gem_file", command.GEMFile.GetFullFileName().Replace('\\', '/') } + }; - string result = ""; - bool success = true; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; //in this case, result is the text of the csv file. - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); - } + "--command", "gem_to_hbjson" + }; - //as the file output is hard to verify by itself, check that no errors got output to stderr log - success &= !result.Contains("Traceback (most recent call last):"); + (string result, bool success) = ExecutePython(args, json); if (!success) { diff --git a/LadybugTools_Adapter/AdapterActions/Execute/GetMaterialCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/GetMaterialCommand.cs index 4f500bea..894321d4 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/GetMaterialCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/GetMaterialCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Data.Requests; using BH.oM.LadybugTools; @@ -56,33 +57,35 @@ private List RunCommand(GetMaterialCommand command, ActionConfig actionC // run the process if (!File.Exists(config.JsonFile.GetFullFileName())) { - List args = new List() { "--command", "get_material", "-j", config.JsonFile.GetFullFileName().Replace('\\', '/') }; + Dictionary dict = new Dictionary() + { + { "json_file", config.JsonFile.GetFullFileName().Replace('\\', '/') } + }; - string result = ""; - bool success; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; + "--command", "get_material" + }; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; + + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while getting materials. Python output:\n{result}"); + return new List(); } + result = result.Split('\n').Last(); File.WriteAllText(config.JsonFile.GetFullFileName(), result); } List materialObjects = Pull(new FilterRequest(), actionConfig: config).ToList(); m_executeSuccess = true; - return materialObjects.Where(m => (m as IEnergyMaterialOpaque).Name.Contains(command.Filter)).ToList(); + return materialObjects.Where(m => (m as IEnergyMaterialOpaque).Identifier.Contains(command.Filter)).ToList(); } } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/GetTypologyCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/GetTypologyCommand.cs index 048048cb..70925abd 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/GetTypologyCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/GetTypologyCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Data.Requests; using BH.oM.LadybugTools; @@ -57,26 +58,28 @@ private List RunCommand(GetTypologyCommand command, ActionConfig actionC // run the process if (!File.Exists(config.JsonFile.GetFullFileName())) { - List args = new List() { "--command", "get_typology", "-j", config.JsonFile.GetFullFileName().Replace('\\', '/') }; + Dictionary dict = new Dictionary() + { + { "json_file", config.JsonFile.GetFullFileName().Replace('\\', '/') } + }; - string result = ""; - bool success; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; + "--command", "get_typology" + }; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; + + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while getting typologies. Python output:\n{result}"); + return new List(); } + result = result.Split('\n').Last(); File.WriteAllText(config.JsonFile.GetFullFileName(), result); } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/HBJSONToGEMCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/HBJSONToGEMCommand.cs index 8aaa0c6e..663bdbaa 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/HBJSONToGEMCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/HBJSONToGEMCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.LadybugTools; using System; @@ -58,28 +59,19 @@ public List RunCommand(HBJSONToGEMCommand command, ActionConfig actionCo return null; } - List args = new List() { "--command", "hbjson_to_gem", "-j", command.HBJSONFile.GetFullFileName().Replace('\\', '/') }; + Dictionary dict = new Dictionary() + { + { "hbjson_file", command.HBJSONFile.GetFullFileName().Replace('\\', '/') } + }; - string result = ""; - bool success = true; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; //in this case, result is the text of the csv file. - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); - } + "--command", "hbjson_to_gem" + }; - //as the file output is hard to verify by itself, check that no errors got output to stderr log - success &= (!result.Contains("Traceback (most recent call last):") || result.Length == 0); + (string result, bool success) = ExecutePython(args, json); if (!success) { diff --git a/LadybugTools_Adapter/AdapterActions/Execute/HeatPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/HeatPlotCommand.cs index 2bfeb8b2..75045e81 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/HeatPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/HeatPlotCommand.cs @@ -22,6 +22,7 @@ using BH.Engine.Adapter; using BH.Engine.Base; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -62,37 +63,42 @@ private List RunCommand(HeatPlotCommand command, ActionConfig actionConf if (colourMap.ColourMapValidity()) colourMap = colourMap.ToColourMap().FromColourMap(); + Dictionary dict = new Dictionary() + { + { "data_type_key", command.EPWKey.ToText() }, + { "colour_map", colourMap }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; + + string json = dict.ToJson(); + // run the process - List args = new List() { "-command", "plot/heatmap", "-e", epwFile.Replace('\\', '/'), "-dtk", command.EPWKey.ToText(), "-cmap", colourMap, "-p", command.OutputLocation.Replace('\\', '/') }; + List args = new List() + { + "-command", "plot/epw_heatmap", + "-e", epwFile.Replace('\\', '/'), + }; - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new CollectionData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/RunExternalComfortCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/RunExternalComfortCommand.cs index ecabb83c..d2a06510 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/RunExternalComfortCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/RunExternalComfortCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Data.Requests; using BH.oM.LadybugTools; @@ -48,15 +49,6 @@ private List RunCommand(RunExternalComfortCommand command, ActionConfig return null; } - LadybugConfig config = new LadybugConfig() - { - JsonFile = new FileSettings() - { - FileName = $"LBTBHoM_{Guid.NewGuid()}.json", - Directory = Path.GetTempPath() - } - }; - // construct the base object ExternalComfort externalComfort = new ExternalComfort() { @@ -64,24 +56,42 @@ private List RunCommand(RunExternalComfortCommand command, ActionConfig Typology = command.Typology, }; - // push objects to json file - Push(new List() { externalComfort }, actionConfig: config); + Dictionary dict = new Dictionary() + { + { "external_comfort", externalComfort } + }; - // locate the Python file containing the simulation code - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped", "external_comfort.py"); + string json = dict.ToJson(); - // run the calculation - string cmdCommand = $"{m_environment.Executable} {script} -j \"{config.JsonFile.GetFullFileName()}\""; - Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + List args = new List() + { + "-c", "external_comfort" + }; - // reload from Python results - List externalComfortPopulated = Pull(new FilterRequest(), actionConfig: config).ToList(); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - // remove temporary file - File.Delete(config.JsonFile.GetFullFileName()); + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); + } + + string resultJson = result.Split('\n').Last(); + ExternalComfort ec = null; + + try + { + ec = (ExternalComfort)BH.Engine.Serialiser.Convert.FromJson(resultJson); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, $"Could not deserialise python output into ExternalComfort. Python output:\n{result}"); + m_executeSuccess = false; + return new List(); + } - m_executeSuccess = true; - return externalComfortPopulated; + return new List() { ec }; } } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/RunSimulationCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/RunSimulationCommand.cs index d4d9159f..4303eec2 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/RunSimulationCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/RunSimulationCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Data.Requests; using BH.oM.LadybugTools; @@ -61,43 +62,51 @@ private List RunCommand(RunSimulationCommand command, ActionConfig actio return null; } - // construct adapter and config - LadybugConfig config = new LadybugConfig() - { - JsonFile = new FileSettings() - { - FileName = $"LBTBHoM_{Guid.NewGuid()}.json", - Directory = Path.GetTempPath() - } - }; - // construct the base object and file to be passed to Python for simulation SimulationResult simulationResult = new SimulationResult() { EpwFile = command.EPWFile, GroundMaterial = command.GroundMaterial, ShadeMaterial = command.ShadeMaterial, - Name = Engine.LadybugTools.Compute.SimulationID(command.EPWFile.GetFullFileName(), command.GroundMaterial, command.ShadeMaterial) + Identifier = Engine.LadybugTools.Compute.SimulationID(command.EPWFile.GetFullFileName(), command.GroundMaterial, command.ShadeMaterial) }; - // push object to json file - Push(new List() { simulationResult }, actionConfig: config); + Dictionary dict = new Dictionary() + { + { "simulation_result", simulationResult } + }; - // locate the Python file containing the simulation code - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom\\wrapped", "simulation_result.py"); + string json = dict.ToJson(); - // run the simulation - string cmdCommand = $"{m_environment.Executable} {script} -j \"{config.JsonFile.GetFullFileName()}\""; - Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true); + List args = new List() + { + "-c", "simulation_result" + }; - // reload from Python results - List simulationResultPopulated = Pull(new FilterRequest(), actionConfig: config).ToList(); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - // remove temporary file - File.Delete(config.JsonFile.GetFullFileName()); + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); + } + + string resultJson = result.Split('\n').Last(); + SimulationResult sr = null; + + try + { + sr = (SimulationResult)BH.Engine.Serialiser.Convert.FromJson(resultJson); + } + catch (Exception ex) + { + BH.Engine.Base.Compute.RecordError(ex, $"Could not deserialise python output into SimulationResult. Python output:\n{result}"); + m_executeSuccess = false; + return new List(); + } - m_executeSuccess = true; - return simulationResultPopulated; + return new List() { sr }; } } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/SolarRadiationPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/SolarRadiationPlotCommand.cs index bd6830bd..60258f80 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/SolarRadiationPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/SolarRadiationPlotCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -84,37 +85,47 @@ private List RunCommand(SolarRadiationPlotCommand command, ActionConfig string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + Dictionary dict = new Dictionary() + { + { "directions", command.Directions }, + { "tilts", command.Tilts }, + { "irradiance_type", command.IrradianceType.ToString() }, + { "cmap", colourMap }, + { "analysis_period", command.AnalysisPeriod }, + { "title", command.Title }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + + }; + + string json = dict.ToJson(); + // run the process - List args = new List() { "-command", "plot/directional_solar_radiation", "-e", epwFile.Replace('\\', '/'), "-d", command.Directions.ToString(), "-ti", command.Tilts.ToString(), "-ir", command.IrradianceType.ToString(), "-cmap", colourMap, "-t", command.Title, "-ap", command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\""), "-p", command.OutputLocation.Replace('\\', '/') }; + List args = new List() + { + "-command", "plot/directional_solar_radiation", + "-e", epwFile + }; - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new SolarRadiationData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/SunPathPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/SunPathPlotCommand.cs index 321826cd..65c97aa3 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/SunPathPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/SunPathPlotCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -67,37 +68,42 @@ private List RunCommand(SunPathPlotCommand command, ActionConfig actionC string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + Dictionary dict = new Dictionary() + { + { "size", command.SunSize }, + { "analysis_period", command.AnalysisPeriod }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; + + string json = dict.ToJson(); + // run the process - List args = new List() { "-command", "plot/sunpath", "-e", epwFile.Replace('\\', '/'), "-s", command.SunSize.ToString(), "-ap", command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\""), "-p", command.OutputLocation.Replace('\\', '/') }; + List args = new List() + { + "-command", "plot/sunpath", + "-e", epwFile.Replace('\\', '/') + }; - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - } + result = result.Split('\n').Last(); try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new SunPathData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/UTCIHeatPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/UTCIHeatPlotCommand.cs index 1388c8d3..d57c56aa 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/UTCIHeatPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/UTCIHeatPlotCommand.cs @@ -69,57 +69,46 @@ private List RunCommand(UTCIHeatPlotCommand command, ActionConfig action List colours = command.BinColours.Select(x => x.ToHexCode()).ToList(); - string hexColours = $"[\"{string.Join("\",\"", colours)}\"]"; - if (hexColours == "[\"\"]") - hexColours = "[]"; + string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); - Dictionary inputObjects = new Dictionary() + Dictionary dict = new Dictionary() { - { "external_comfort", command.ExternalComfort.FromBHoM() }, - { "bin_colours", hexColours } + { "external_comfort", command.ExternalComfort }, + { "bin_colours", colours }, + { "save_path", command.OutputLocation.Replace('\\', '/') } }; - string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + string json = dict.ToJson(); // run the process - List args = new List() { "-command", "plot/utci_heatmap", "-e", epwFile.Replace('\\', '/'), "-sp", command.OutputLocation.Replace('\\', '/') }; - - string result = ""; - bool success; - - if (m_httpClient != null) + List args = new List() { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args, inputObjects.ToJson()); - task.Wait(); - (result, success) = task.Result; - } - else - { - //if the server was not running or some other error happened, try running the python directly. - string argFile = Path.GetTempFileName(); - File.WriteAllText(argFile, inputObjects.ToJson()); - args.Add("-in"); - args.Add(argFile); - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; + "-command", "plot/utci_heatmap", + "-e", epwFile.Replace('\\', '/') + }; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - System.IO.File.Delete(argFile); + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } + result = result.Split('\n').Last(); try { CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new UTCIData()); - ExternalComfort ec = Convert.ToExternalComfort((obj.CustomData["external_comfort"] as CustomObject).CustomData); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)obj.CustomData["info"]; + ExternalComfort ec = (ExternalComfort)obj.CustomData["external_comfort"]; return new List() { info, ec }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/WalkabilityPlotCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/WalkabilityPlotCommand.cs index f91db0e0..c433d88f 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/WalkabilityPlotCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/WalkabilityPlotCommand.cs @@ -60,50 +60,44 @@ private List RunCommand(WalkabilityPlotCommand command, ActionConfig act return null; } - Dictionary inputObjects = new Dictionary() + string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + + Dictionary dict = new Dictionary() { - { "external_comfort", command.ExternalComfort.FromBHoM() } + { "external_comfort", command.ExternalComfort }, + { "save_path", command.OutputLocation.Replace('\\', '/') } }; - string epwFile = System.IO.Path.GetFullPath(command.EPWFile.GetFullFileName()); + string json = dict.ToJson(); - // run the process - List args = new List() { "-command", "plot/walkability_heatmap", "-e", epwFile.Replace('\\', '/'), "-sp", command.OutputLocation.Replace('\\', '/') }; + List args = new List() + { + "-command", "plot/walkability_heatmap", + "-e", epwFile.Replace('\\', '/') + }; - string result = ""; - bool success; + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - if (m_httpClient != null) + if (!success) { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args, inputObjects.ToJson()); - task.Wait(); - (result, success) = task.Result; + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } - else - { - //if the server was not running or some other error happened, try running the python directly. - string argFile = Path.GetTempFileName(); - File.WriteAllText(argFile, inputObjects.ToJson()); - args.Add("-in"); - args.Add(argFile); - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); - System.IO.File.Delete(argFile); - } + result = result.Split('\n').Last(); try { CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new UTCIData()); - ExternalComfort ec = Convert.ToExternalComfort((obj.CustomData["external_comfort"] as CustomObject).CustomData); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)obj.CustomData["info"]; + ExternalComfort ec = (ExternalComfort)obj.CustomData["external_comfort"]; return new List() { info, ec }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/AdapterActions/Execute/WindroseCommand.cs b/LadybugTools_Adapter/AdapterActions/Execute/WindroseCommand.cs index 799fb2af..b03215ca 100644 --- a/LadybugTools_Adapter/AdapterActions/Execute/WindroseCommand.cs +++ b/LadybugTools_Adapter/AdapterActions/Execute/WindroseCommand.cs @@ -21,6 +21,7 @@ */ using BH.Engine.Adapter; +using BH.Engine.Serialiser; using BH.oM.Adapter; using BH.oM.Base; using BH.oM.LadybugTools; @@ -66,37 +67,42 @@ private List RunCommand(WindroseCommand command, ActionConfig actionConf if (colourMap.ColourMapValidity()) colourMap = colourMap.ToColourMap().FromColourMap(); - // run the process - List args = new List() { "-command", "plot/windrose", "-e", epwFile.Replace('\\', '/'), "-ap", command.AnalysisPeriod.FromBHoM().Replace("\"", "\\\""), "-cmap", colourMap, "-bins", command.NumberOfDirectionBins.ToString(), "-p", command.OutputLocation.Replace('\\', '/') }; + Dictionary dict = new Dictionary() + { + { "analysis_period", command.AnalysisPeriod }, + { "colour_map", colourMap }, + { "bins", command.NumberOfDirectionBins }, + { "save_path", command.OutputLocation.Replace('\\', '/') } + }; - string result = ""; - bool success; + string json = dict.ToJson(); - if (m_httpClient != null) - { - Task<(string, bool)> task = Compute.SendHttp(m_httpClient, args); - task.Wait(); - (result, success) = task.Result; - } - else + List args = new List() { - //if the server was not running or some other error happened, try running the python directly. - string script = Path.Combine(Engine.LadybugTools.Query.PythonCodeDirectory(), "LadybugTools_Toolkit\\src\\ladybugtools_toolkit\\bhom", "run_wrapped.py"); - string cmdCommand = $"{m_environment.Executable} {script} {args.Select(x => x.Contains(' ') || string.IsNullOrEmpty(x) ? '"' + x + '"' : x).Aggregate((a, b) => a + " " + b)}"; + "-command", "plot/windrose", + "-e", epwFile.Replace('\\', '/') + }; + + (string result, bool success) = ExecutePython(args, json); + m_executeSuccess = success; - result = Engine.Python.Compute.RunCommandStdout(command: cmdCommand, hideWindows: true).Split('\n').Last(); + if (!success) + { + BH.Engine.Base.Compute.RecordError($"A python error occurred while running the command `{command.GetType().Name}`. Python output:\n{result}"); + return new List(); } + result = result.Split('\n').Last(); + try { - CustomObject obj = (CustomObject)BH.Engine.Serialiser.Convert.FromJson(result); - PlotInformation info = Convert.ToPlotInformation(obj, new WindroseData()); - m_executeSuccess = true; + PlotInformation info = (PlotInformation)BH.Engine.Serialiser.Convert.FromJson(result); return new List() { info }; } catch (Exception ex) { BH.Engine.Base.Compute.RecordError(ex, $"An error occurred when deserialising the output from the script.\n Python output: {result}"); + m_executeSuccess = false; return new List(); } } diff --git a/LadybugTools_Adapter/Convert/Constructions/EnergyMaterial.cs b/LadybugTools_Adapter/Convert/Constructions/EnergyMaterial.cs index a1ea3d31..d2ae772b 100644 --- a/LadybugTools_Adapter/Convert/Constructions/EnergyMaterial.cs +++ b/LadybugTools_Adapter/Convert/Constructions/EnergyMaterial.cs @@ -120,7 +120,7 @@ public static BH.oM.LadybugTools.EnergyMaterial ToEnergyMaterial(Dictionary FromEnergyMaterial(BH.oM.LadybugTools.E return new Dictionary() { { "type", "EnergyMaterial" }, - { "identifier", energyMaterial.Name }, + { "identifier", energyMaterial.Identifier }, { "thickness", energyMaterial.Thickness }, { "conductivity", energyMaterial.Conductivity }, { "density", energyMaterial.Density }, diff --git a/LadybugTools_Adapter/Convert/Constructions/EnergyMaterialVegetation.cs b/LadybugTools_Adapter/Convert/Constructions/EnergyMaterialVegetation.cs index 44bf307b..186f9dac 100644 --- a/LadybugTools_Adapter/Convert/Constructions/EnergyMaterialVegetation.cs +++ b/LadybugTools_Adapter/Convert/Constructions/EnergyMaterialVegetation.cs @@ -179,7 +179,7 @@ public static BH.oM.LadybugTools.EnergyMaterialVegetation ToEnergyMaterialVegeta return new oM.LadybugTools.EnergyMaterialVegetation() { - Name = name, + Identifier = name, Thickness = thickness, Conductivity = conductivity, Density = density, @@ -201,7 +201,7 @@ public static Dictionary FromEnergyMaterialVegetation(BH.oM.Lady return new Dictionary { { "type", "EnergyMaterialVegetation" }, - { "identifier", energyMaterial.Name }, + { "identifier", energyMaterial.Identifier }, { "thickness", energyMaterial.Thickness }, { "conductivity", energyMaterial.Conductivity }, { "density", energyMaterial.Density }, diff --git a/LadybugTools_Adapter/Convert/MetaData/AnalysisPeriod.cs b/LadybugTools_Adapter/Convert/MetaData/AnalysisPeriod.cs index 551e24ec..1381bfeb 100644 --- a/LadybugTools_Adapter/Convert/MetaData/AnalysisPeriod.cs +++ b/LadybugTools_Adapter/Convert/MetaData/AnalysisPeriod.cs @@ -122,7 +122,7 @@ public static BH.oM.LadybugTools.AnalysisPeriod ToAnalysisPeriod(Dictionary FromAnalysisPeriod(BH.oM.LadybugTools.A { "end_day", analysisPeriod.EndDay }, { "end_hour", analysisPeriod.EndHour }, { "is_leap_year", analysisPeriod.IsLeapYear }, - { "timestep", analysisPeriod.TimeStep } + { "timestep", analysisPeriod.Timestep } }; } } diff --git a/LadybugTools_Adapter/Convert/Simulation/SimulationResult.cs b/LadybugTools_Adapter/Convert/Simulation/SimulationResult.cs index 1fb26f59..593a455c 100644 --- a/LadybugTools_Adapter/Convert/Simulation/SimulationResult.cs +++ b/LadybugTools_Adapter/Convert/Simulation/SimulationResult.cs @@ -166,7 +166,7 @@ public static string FromSimulationResult(SimulationResult simulationResult) string epwFile = $"\"epw_file\": \"{simulationResult.EpwFile.GetFullFileName().Replace("\\", "/")}\", "; string groundMaterial = $"\"ground_material\": {FromBHoM(simulationResult.GroundMaterial)}, "; string shadeMaterial = $"\"shade_material\": {FromBHoM(simulationResult.ShadeMaterial)}, "; - string name = $"\"identifier\": \"{simulationResult.Name}\""; + string name = $"\"identifier\": \"{simulationResult.Identifier}\""; List properties = new List(); if (simulationResult.ShadedDownTemperature != null) diff --git a/LadybugTools_Engine/Create/AnalysisPeriod.cs b/LadybugTools_Engine/Create/AnalysisPeriod.cs index 22b12511..df76f119 100644 --- a/LadybugTools_Engine/Create/AnalysisPeriod.cs +++ b/LadybugTools_Engine/Create/AnalysisPeriod.cs @@ -75,7 +75,7 @@ public static AnalysisPeriod AnalysisPeriod(int startMonth = 1, int startDay = 1 EndDay = endDay, EndHour = endHour, IsLeapYear = isLeapYear, - TimeStep = timestep + Timestep = timestep }; } } diff --git a/LadybugTools_Engine/Create/EnergyMaterial.cs b/LadybugTools_Engine/Create/EnergyMaterial.cs index 7f114f86..af65529f 100644 --- a/LadybugTools_Engine/Create/EnergyMaterial.cs +++ b/LadybugTools_Engine/Create/EnergyMaterial.cs @@ -113,7 +113,7 @@ public static EnergyMaterial EnergyMaterial( return new oM.LadybugTools.EnergyMaterial() { - Name = identifier, + Identifier = identifier, Thickness = thickness, Conductivity = conductivity, Density = density, diff --git a/LadybugTools_Engine/Create/EnergyMaterialVegetation.cs b/LadybugTools_Engine/Create/EnergyMaterialVegetation.cs index 9b69ea95..5ffa4e48 100644 --- a/LadybugTools_Engine/Create/EnergyMaterialVegetation.cs +++ b/LadybugTools_Engine/Create/EnergyMaterialVegetation.cs @@ -153,7 +153,7 @@ public static EnergyMaterialVegetation EnergyMaterialVegetation( return new oM.LadybugTools.EnergyMaterialVegetation() { - Name = identifier, + Identifier = identifier, Thickness = thickness, Conductivity = conductivity, Density = density, diff --git a/LadybugTools_Engine/Create/SimulationResult.cs b/LadybugTools_Engine/Create/SimulationResult.cs index 8e3db819..d5a37f02 100644 --- a/LadybugTools_Engine/Create/SimulationResult.cs +++ b/LadybugTools_Engine/Create/SimulationResult.cs @@ -43,7 +43,7 @@ public static SimulationResult SimulationResult(FileSettings epwFile, string ide return new SimulationResult() { EpwFile = epwFile, - Name = identifier, + Identifier = identifier, GroundMaterial = groundMaterial, ShadeMaterial = shadeMaterial }; diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/from_bhom.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/from_bhom.py new file mode 100644 index 00000000..558b09a7 --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/from_bhom.py @@ -0,0 +1,73 @@ +from honeybee_energy.material.opaque import EnergyMaterial, EnergyMaterialVegetation +from ladybug.analysisperiod import AnalysisPeriod as AnalysisPeriodBase +from ladybug.datacollection import HourlyContinuousCollection as HC +from ladybug.epw import EPW, Location +from ladybug.header import DataTypeBase, Header as HeaderBase +from ladybug_geometry.geometry3d.pointvector import Point3D +from python_toolkit.bhom.bhom_object import BHoMObject, IObject, BHoMJSONDecoder + +#make custom classes for converting to ladybug objects from bhom objects (where type names and some other differences occur) +class Point(): + @classmethod + def from_dict(cls, d) -> Point3D: + d["type"] = "Point3D" + + return Point3D.from_dict(d) + +class DataType(): + @classmethod + def from_dict(cls, d) -> dict: + d["type"] = "DataTypeBase" + d["data_type"] = d["data__type"] + return DataTypeBase.from_dict(d) + +class AnalysisPeriod(): + @classmethod + def from_dict(cls, d) -> dict: + d["st_hour"] = d["start_hour"] + d["st_day"] = d["start_day"] + d["st_month"] = d["start_month"] + return AnalysisPeriodBase.from_dict(d) + +class HourlyContinuousCollection(): + @classmethod + def from_dict(cls, d) -> dict: + d["type"] = "HourlyContinuous" + #see comment in Header() below for the reason the header is converted to a dict. + d["header"] = d["header"].to_dict() + return HC.from_dict(d) + +class Header(): + @classmethod + def from_dict(cls, d) -> dict: + #convert parts of header from class to dictionary so that HeaderBase.from_dict() still works (for some reason ladybug hasn't used a JSONDecoder for json decoding...) + #this works because the python json decoder works depth first. + d["data_type"] = d["data_type"].to_dict() + d["analysis_period"] = d["analysis_period"].to_dict() + return HeaderBase.from_dict(d) + +_TYPES: list[type] = [ + EnergyMaterial, + EnergyMaterialVegetation, + AnalysisPeriod, + HourlyContinuousCollection, + Location, + DataType, + Header, + Point +] + +class LBTBHoMJSONDecoder(BHoMJSONDecoder): + def deserialise_unknown(self, obj:BHoMObject | IObject | dict): + """custom object-hook method for BHoMJSONDecoder""" + if isinstance(obj, BHoMObject) or isinstance(obj, IObject): + _type = obj._t.split(".")[-1] + + klass = [t for t in _TYPES if t.__name__ == _type] + + if len(klass) == 1: + setattr(obj, "type", _type) + return klass[0].from_dict(obj.to_dict()) + + #default to returning a bhom object if tha above did not work + return obj diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/run_wrapped.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/run_wrapped.py index e0351591..65aa61e1 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/run_wrapped.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/run_wrapped.py @@ -1,79 +1,42 @@ import sys import argparse from pathlib import Path -from typing import List +from typing import List, Callable +import json +from python_toolkit.bhom.decorators import bhom_wrapper import matplotlib matplotlib.use("Agg") #use a gui-less backend to avoid memory leaking figures #big import list that covers all methods in bhom/wrapped -from ladybugtools_toolkit.external_comfort.externalcomfort import ExternalComfort -from ladybugtools_toolkit.bhom.wrapped.metadata.utci_metadata import utci_metadata -from ladybugtools_toolkit.bhom.wrapped.plot.utci_heatmap import utci_heatmap - -#import methods and parsers -from ladybugtools_toolkit.bhom.wrapped.plot.walkability_heatmap import PARSER as walkability_heatmap_parser, walkability_heatmap -from ladybugtools_toolkit.bhom.wrapped.plot.epw_comparison import PARSER as epw_comparison_parser, epw_comparison -from ladybugtools_toolkit.bhom.wrapped.plot.windrose import PARSER as windrose_parser, windrose -from ladybugtools_toolkit.bhom.wrapped.plot.directional_solar_radiation import PARSER as directional_solar_radiation_parser, directional_solar_radiation -from ladybugtools_toolkit.bhom.wrapped.plot.diurnal import PARSER as diurnal_parser, diurnal -from ladybugtools_toolkit.bhom.wrapped.plot.facade_condensation_risk_chart import PARSER as facade_condensation_risk_chart_parser, facade_condensation_risk_chart -from ladybugtools_toolkit.bhom.wrapped.plot.facade_condensation_risk_heatmap import PARSER as facade_condensation_risk_heatmap_parser, facade_condensation_risk_heatmap -from ladybugtools_toolkit.bhom.wrapped.plot.heatmap import PARSER as heatmap_parser, heatmap -from ladybugtools_toolkit.bhom.wrapped.plot.sunpath import PARSER as sunpath_parser, sunpath -from ladybugtools_toolkit.bhom.wrapped.plot.utci_heatmap import PARSER as utci_heatmap_parser, utci_heatmap -from ladybugtools_toolkit.bhom.wrapped.epw_to_csv import PARSER as epw_to_csv_parser, epw_to_csv -from ladybugtools_toolkit.bhom.wrapped.gem_to_hbjson import PARSER as gem_to_hbjson_parser, gem_to_hbjson -from ladybugtools_toolkit.bhom.wrapped.get_material import PARSER as get_material_parser, get_material -from ladybugtools_toolkit.bhom.wrapped.get_typology import PARSER as get_typology_parser, get_typology -from ladybugtools_toolkit.bhom.wrapped.hbjson_to_gem import PARSER as hbjson_to_gem_parser, hbjson_to_gem - -from ladybugtools_toolkit.plot.utilities import figure_to_base64 -from ladybugtools_toolkit.categorical.categories import Categorical, UTCI_DEFAULT_CATEGORIES -import matplotlib.pyplot as plt -import numpy as np -import json +from python_toolkit.bhom import wrapped +from ladybugtools_toolkit.bhom import wrapped -#dictionary containing all the parsers for bhom/wrapped commands -PARSERS = { - "plot/walkability_heatmap": (walkability_heatmap_parser, walkability_heatmap), - "plot/epw_comparison": (epw_comparison_parser, epw_comparison), - "plot/windrose": (windrose_parser, windrose), - "plot/directional_solar_radiation": (directional_solar_radiation_parser, directional_solar_radiation), - "plot/diurnal": (diurnal_parser, diurnal), - "plot/facade_condensation_risk_chart": (facade_condensation_risk_chart_parser, facade_condensation_risk_chart), - "plot/facade_condensation_risk_heatmap": (facade_condensation_risk_heatmap_parser, facade_condensation_risk_heatmap), - "plot/heatmap": (heatmap_parser, heatmap), - "plot/sunpath": (sunpath_parser, sunpath), - "plot/utci_heatmap": (utci_heatmap_parser, utci_heatmap), - "epw_to_csv": (epw_to_csv_parser, epw_to_csv), - "gem_to_hbjson": (gem_to_hbjson_parser, gem_to_hbjson), - "get_material": (get_material_parser, get_material), - "get_typology": (get_typology_parser, get_typology), - "hbjson_to_gem": (hbjson_to_gem_parser, hbjson_to_gem), -} +COMMAND_PARSER = argparse.ArgumentParser(description="argument parser for commands.") +COMMAND_PARSER.add_argument("-command", "--command") +COMMAND_PARSER.add_argument("-in", "--input_json") +COMMAND_PARSER.add_argument("-e", "--epw_file", required=False) def resolve(data: List[str], epw_folder: Path = Path("C:/epws")) -> str: - """Parses the given data (that looks like sys.argv[1:]), and gets the command arg which is then used to get the parser for that command, - parse the rest of the args and finally run the command, then return the output of those commands. + """Parses the given data (that looks like sys.argv[1:]), and gets the command arg which is an identifier for the command which is requested, + and the input json string (or file) to be given to the BHoMJSONDecoder wrapped method. + + Also if the given epw file doesn't exist, assume that it is a file name and append it to the epw folder as a backup. """ #parse data as args - command_parser = argparse.ArgumentParser(description="Command parser") - command_parser.add_argument("-command", "--command") - command_arg, unknown_args = command_parser.parse_known_args(data) - - parser_function = PARSERS[command_arg.command] - args = vars(parser_function[0].parse_args(unknown_args)) + command_args, unknown_args = COMMAND_PARSER.parse_known_args(data) - if "epw_file" in args: + if command_args.epw_file is not None: #check if the epw file exists, if not prepend the epw_folder and try to run - epw = Path(args["epw_file"]) + epw = Path(command_args.epw_file) if not epw.exists(): epw = epw_folder / epw.name - args["epw_file"] = str(epw) + command_args.epw_file = str(epw) + + method = bhom_wrapper.get_registered_method(command_args.command) - ret = parser_function[1](**args) + ret = method(epw_file = command_args.epw_file, __input_json__ = command_args.input_json) return ret #gets the function for the requested command, and runs it with arguments parsed with the desired parser. def deconstruct(data: str) -> List[str]: diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/to_bhom.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/to_bhom.py index 9d3f647d..5aecb66b 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/to_bhom.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/to_bhom.py @@ -6,7 +6,24 @@ from ladybug.epw import EPW, Location from ladybug.header import DataTypeBase, Header from ladybug_geometry.geometry3d.pointvector import Point3D - +from python_toolkit.bhom.bhom_object import BHoMJSONEncoder + +class LBTBHoMJSONEncoder(BHoMJSONEncoder): + def serialise_unknown(self, obj): + if isinstance(obj, EnergyMaterial) or isinstance(obj, EnergyMaterialVegetation): + return material_to_bhom(obj) + if isinstance(obj, Point3D): + return point3d_to_bhom(obj) + if isinstance(obj, AnalysisPeriod): + return analysisperiod_to_bhom(obj) + if isinstance(obj, DataTypeBase): + return datatype_to_bhom(obj) + if isinstance(obj, Header): + return header_to_bhom(obj) + if isinstance(obj, HourlyContinuousCollection): + return hourlycontinuouscollection_to_bhom(obj) + + return super().serialise_unknown(obj) def material_to_bhom(obj: EnergyMaterial | EnergyMaterialVegetation) -> dict: """Convert this object into a BHOM deserialisable dictionary.""" @@ -76,11 +93,11 @@ def analysisperiod_to_bhom(obj: AnalysisPeriod) -> dict: return { "_t": "BH.oM.LadybugTools.AnalysisPeriod", "Type": "AnalysisPeriod", - "StHour": obj.st_hour, + "StartHour": obj.st_hour, "EndHour": obj.end_hour, - "StDay": obj.st_day, + "StartDay": obj.st_day, "EndDay": obj.end_day, - "StMonth": obj.st_month, + "StartMonth": obj.st_month, "EndMonth": obj.end_month, "IsLeapYear": obj.is_leap_year, "Timestep": obj.timestep, diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/__init__.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/__init__.py index e69de29b..7543761f 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/__init__.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/__init__.py @@ -0,0 +1,13 @@ +from importlib import import_module +from pathlib import Path +import os + +python_files = Path(__file__).parent.glob("**/*.py") + +for file in python_files: + if file.name == "__init__.py": + continue + + rel = file.relative_to(Path(__file__).parent) + module = "." + str(rel).replace(".py", "").replace(os.path.sep, ".") + import_module(module, "ladybugtools_toolkit.bhom.wrapped") \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/epw_to_csv.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/epw_to_csv.py index 06695fa4..566e8d36 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/epw_to_csv.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/epw_to_csv.py @@ -1,33 +1,12 @@ """Method to wrap for conversion of EPW to CSV file.""" # pylint: disable=C0415,E0401,W0703 -import argparse -import sys import traceback -from pathlib import Path from ..logger import CONSOLE_LOGGER from ladybugtools_toolkit.ladybug_extension.epw import epw_to_dataframe, EPW +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, convert to CSV with optional inclusion of calculated additional data." - ) - ) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to write as a CSV.", - type=str, - required=True, -) -PARSER.add_argument( - "-a", - "--include_additional", - help="Whether to include additional calculated data (such as hourly ground temperature, sky temperature, sun position, ...).", - type=bool, - required=True, -) - -def epw_to_csv(epw_file: str, include_additional: bool) -> str: +@bhom_wrapper.bhom_callable("epw_to_csv") +def epw_to_csv(epw_file: str, include_additional: bool, **kwargs) -> str: """Create a CSV file version of an EPW.""" try: epw = EPW(epw_file) @@ -37,7 +16,3 @@ def epw_to_csv(epw_file: str, include_additional: bool) -> str: except Exception: CONSOLE_LOGGER.error("CSV file could not be created.", exc_info=1) return traceback.format_exc() - -if __name__ == "__main__": - args = PARSER.parse_args() - epw_to_csv(args.epw_file, args.include_additional) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/external_comfort.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/external_comfort.py index 090c7f00..0d71feef 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/external_comfort.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/external_comfort.py @@ -1,36 +1,13 @@ -"""Method to wrap for access to pre-defined materials.""" # pylint: disable=C0415,E0401,W0703 -import argparse -import traceback - - -def main(json_file: str) -> None: - """From a json file represention of an ExternalComfort, run the calculation.""" - try: - from ladybugtools_toolkit.external_comfort._externalcomfortbase import ( - ExternalComfort, - ) - - ec = ExternalComfort.from_file(json_file) - ec.to_file(json_file) - - except Exception as e: - print(traceback.format_exc()) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=( - "Given a JSON file containing the string represention of a ExternalComfort object, " - "run all calculations Python-side for that object." - ) - ) - parser.add_argument( - "-j", - "--json_file", - help="The JSON file to convert into a ExternalComfort object Python-side.", - type=str, - required=True, - ) - args = parser.parse_args() - main(args.json_file) +from python_toolkit.bhom.decorators import bhom_wrapper +from ladybugtools_toolkit.external_comfort._externalcomfortbase import ExternalComfort +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder + +#Note: All this method does is return the external comfort object given. +#Originally this method converted from json and then back to json, however the bhom_callable decorator does this automatically. +#In order to allow this to still exist as callable from BHoM, this method was simplified to just return. + +@bhom_wrapper.bhom_callable("external_comfort", argument_types = {"external_comfort": ExternalComfort}, encoder_cls=LBTBHoMJSONEncoder, decoder_cls=LBTBHoMJSONDecoder) +def main(external_comfort: ExternalComfort, **kwargs) -> None: + return external_comfort diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/gem_to_hbjson.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/gem_to_hbjson.py index a608ed3e..98fa0135 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/gem_to_hbjson.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/gem_to_hbjson.py @@ -1,26 +1,15 @@ """Method to wrap for conversion of IES GEM to HBJSON file.""" # pylint: disable=C0415,E0401,W0703 -import argparse -import sys import traceback from pathlib import Path import tempfile import json from honeybee_ies.reader import model_from_ies from ..logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=("Given a GEM file path, convert to a HBJSON file.") -) -PARSER.add_argument( - "-g", - "--gem_file", - help="The GEM file to convert to HBJSON.", - type=str, - required=True, -) - -def gem_to_hbjson(gem_file: str) -> None: +@bhom_wrapper.bhom_callable("gem_to_hbjson") +def gem_to_hbjson(gem_file: str, **kwargs) -> None: """Create a HBJSON file from an IES GEM file.""" try: file_path = None @@ -46,8 +35,3 @@ def gem_to_hbjson(gem_file: str) -> None: except Exception: CONSOLE_LOGGER.error("HBJSON file could not be created.", exc_info=1) return traceback.format_exc() - - -if __name__ == "__main__": - args = PARSER.parse_args() - gem_to_hbjson(args.gem_file) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_material.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_material.py index 532b254d..28abc00d 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_material.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_material.py @@ -1,24 +1,12 @@ """Method to wrap for access to pre-defined materials.""" # pylint: disable=C0415,E0401,W0703 -import argparse import traceback import json from ladybugtools_toolkit.external_comfort.material import Materials +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given a JSON file path, write the pre-defined materials for the External Comfort workflow." - ) -) -PARSER.add_argument( - "-j", - "--json_file", - help="The JSON file to write material objects into.", - type=str, - required=True, -) - -def get_material(json_file: str) -> None: +@bhom_wrapper.bhom_callable("get_material") +def get_material(json_file: str, **kwargs) -> None: """Create a file containing all default materials.""" try: json_str = json.dumps([material.value.to_dict() for material in Materials]) @@ -30,8 +18,3 @@ def get_material(json_file: str) -> None: except Exception as e: return traceback.format_exc() - - -if __name__ == "__main__": - args = PARSER.parse_args() - get_material(args.json_file) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_typology.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_typology.py index eb9cb50a..d9dd448f 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_typology.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/get_typology.py @@ -1,24 +1,12 @@ """Method to wrap for access to pre-defined typologies.""" # pylint: disable=C0415,E0401,W0703 -import argparse import traceback import json from ladybugtools_toolkit.external_comfort.typology import Typologies +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given a JSON file path, write the pre-defined typologies for the External Comfort workflow." - ) -) -PARSER.add_argument( - "-j", - "--json_file", - help="The JSON file to write Typology objects into.", - type=str, - required=True, -) - -def get_typology(json_file: str) -> None: +@bhom_wrapper.bhom_callable("get_typology") +def get_typology(json_file: str, **kwargs) -> None: """Create a file containing all default typologies.""" try: json_str = json.dumps([typology.value.to_dict() for typology in Typologies]) @@ -30,8 +18,3 @@ def get_typology(json_file: str) -> None: except Exception as e: return traceback.format_exc() - - -if __name__ == "__main__": - args = PARSER.parse_args() - get_typology(args.json_file) diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/hbjson_to_gem.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/hbjson_to_gem.py index 72a7c4a1..fc7e390f 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/hbjson_to_gem.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/hbjson_to_gem.py @@ -1,9 +1,6 @@ """Method to wrap for conversion of HBJSON to GEM file.""" # pylint: disable=C0415,E0401,W0703 -import argparse import json -import random -import sys import traceback from pathlib import Path import uuid @@ -11,19 +8,10 @@ import tempfile from honeybee.model import Model from honeybee_ies.writer import model_to_ies +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=("Given an HBJSON file path, convert to a GEM file.") -) -PARSER.add_argument( - "-j", - "--hbjson_file", - help="The HBJSON file to convert to GEM.", - type=str, - required=True, -) - -def hbjson_to_gem(hbjson_file: str) -> None: +@bhom_wrapper.bhom_callable("hbjson_to_gem") +def hbjson_to_gem(hbjson_file: str, **kwargs) -> None: """Create an IES GEM file from an HBJSON file.""" try: hbjson_dict = None @@ -52,8 +40,3 @@ def hbjson_to_gem(hbjson_file: str) -> None: except Exception: CONSOLE_LOGGER.error("Could not convert the hbjson file to a gem file.", exc_info=1) return traceback.format_exc() - - -if __name__ == "__main__": - args = PARSER.parse_args() - hbjson_to_gem(args.hbjson_file) 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 index ce9a498b..5d8d1ad7 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/collection.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/collection.py @@ -1,7 +1,8 @@ from ladybug.datacollection import BaseCollection from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series +from python_toolkit.bhom.bhom_object import IObject -def collection_metadata(collection: BaseCollection) -> dict: +def collection_metadata(collection: BaseCollection) -> IObject: """Returns a dictionary containing useful metadata about the series. Args: @@ -36,12 +37,13 @@ def collection_metadata(collection: BaseCollection) -> dict: 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 + return IObject( + _t = "BH.oM.LadybugTools.CollectionData", + lowest_value = lowest, + lowest_index = lowest_index, + highest_value = highest, + highest_index = highest_index, + median_value = median, + mean_value = mean, + monthly_means = month_means, + ) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/plot_information.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/plot_information.py new file mode 100644 index 00000000..a3a2abb8 --- /dev/null +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/plot_information.py @@ -0,0 +1,15 @@ +from python_toolkit.bhom.bhom_object import BHoMObject, IObject + +class PlotInformation(BHoMObject): + _t: str = "BH.oM.LadybugTools.PlotInformation" + image: str + other_data: dict + + def __init__(self, image:str = "", other_data:IObject = None, **kwargs): + if other_data is None: + other_data = IObject(_t = "BH.oM.LadybugTools.NoData") + + self.other_data = other_data + self.image = image + + super().__init__(_t=self._t, **kwargs) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/solar_radiation_metadata.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/solar_radiation_metadata.py index 9ba38ddb..9bce6d91 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/solar_radiation_metadata.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/metadata/solar_radiation_metadata.py @@ -1,14 +1,16 @@ import pandas as pd +from python_toolkit.bhom.bhom_object import IObject -def solar_radiation_metadata(values, directions, tilts): +def solar_radiation_metadata(values, directions, tilts) -> IObject: df = pd.DataFrame(values) df.index = tilts df.columns = directions - return { - "max_value": df.max().max(), - "max_direction": df.max().idxmax(), - "max_tilt": df.idxmax()[df.max().idxmax()], - "min_value": df.min().min(), - "min_direction": df.min().idxmin(), - "min_tilt": df.idxmin()[df.min().idxmin()] - } \ No newline at end of file + return IObject( + _t = "BH.oM.LadybugTools.SolarRadiationData", + max_value = df.max().max(), + max_direction = df.max().idxmax(), + max_tilt = df.idxmax()[df.max().idxmax()], + min_value = df.min().min(), + min_direction = df.min().idxmin(), + min_tilt = df.idxmin()[df.min().idxmin()] + ) \ 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 index a44a7c9a..351eb9c3 100644 --- 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 @@ -1,8 +1,19 @@ from ladybug.sunpath import Sunpath from ladybugtools_toolkit.ladybug_extension.sunpath import sunrise_sunset_azimuths -from datetime import datetime +from python_toolkit.bhom.bhom_object import IObject -def sunpath_metadata(sunpath: Sunpath) -> dict: +def convert_to_bhom(d) -> IObject: + return IObject( + _t = "BH.oM.LadybugTools.SunData", + sunrise_azimuth = d["sunrise"]["azimuth"], + sunrise_time = d["sunrise"]["time"], + noon_altitude = d["noon"]["altitude"], + noon_time = d["noon"]["time"], + sunset_azimuth = d["sunset"]["azimuth"], + sunset_time = d["sunset"]["time"] + ) + +def sunpath_metadata(sunpath: Sunpath) -> IObject: """Return a dictionary containing equinox and solstice azimuths and altitudes at sunrise, noon and sunset for the given sunpath. Args: @@ -10,25 +21,18 @@ def sunpath_metadata(sunpath: Sunpath) -> dict: 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': {...} - } + IObject: an IObject of type "BH.oM.LadybugTools.SunPathData", see the oM definition in LadybugTools_oM/MetaData/SunPathData.cs for the structure. """ - 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) + december_solstice = convert_to_bhom(sunrise_sunset_azimuths(sunpath, 2023, 12, 22)) + march_equinox = convert_to_bhom(sunrise_sunset_azimuths(sunpath, 2023, 3, 20)) + june_solstice = convert_to_bhom(sunrise_sunset_azimuths(sunpath, 2023, 6, 21)) + september_equinox = convert_to_bhom(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 + return IObject( + _t = "BH.oM.LadybugTools.SunPathData", + 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 index a537e56b..adbc4dc7 100644 --- 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 @@ -3,8 +3,9 @@ UniversalThermalClimateIndex as LB_UniversalThermalClimateIndex, ) from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series +from python_toolkit.bhom.bhom_object import IObject -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: +def utci_metadata(utci_collection: HourlyContinuousCollection, comfort_lower: float = 9, comfort_higher: float = 26, use_start_hour: int=7, use_end_hour: int=23) -> IObject: """Returns a dictionary of useful metadata for the given collection dependant on the given comfortable range. Args: @@ -53,11 +54,12 @@ def utci_metadata(utci_collection: HourlyContinuousCollection, comfort_lower: fl 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 + return IObject( + _t = "BH.oM.LadybugTools.UTCIData", + comfortable_ratio = comfortable_ratio, + heat_stress_ratio = hot_ratio, + cold_stress_ratio = cold_ratio, + daytime_comfortable_ratio = day_comfortable, + daytime_heat_stress_ratio = day_hot, + daytime_cold_stress_ratio = 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 index 25b9000a..4fe16b88 100644 --- 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 @@ -1,6 +1,7 @@ from ladybugtools_toolkit.wind import Wind +from python_toolkit.bhom.bhom_object import IObject -def wind_metadata(wind_object: Wind, directions: int=36, ignore_calm: bool=True, threshold: float = 1e-10) -> dict: +def wind_metadata(wind_object: Wind, directions: int=36, ignore_calm: bool=True, threshold: float = 1e-10) -> IObject: """Provides a dictionary containing metadata of this wind object. Args: @@ -31,11 +32,12 @@ def wind_metadata(wind_object: Wind, directions: int=36, ignore_calm: bool=True, 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 + return IObject( + _t = "BH.oM.LadybugTools.WindroseData", + percentile95 = ws.quantile(0.95), + percentile50 = ws.quantile(0.50), + ratio_of_calm_hours = wind_object.calm(), + prevailing_direction = prevailing_direction, + prevailing_percentile95 = prevailing_wind_speed.quantile(0.95), + prevailing_percentile50 = prevailing_wind_speed.quantile(0.5) + ) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/directional_solar_radiation.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/directional_solar_radiation.py index 900d0169..be2af86d 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/directional_solar_radiation.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/directional_solar_radiation.py @@ -1,91 +1,27 @@ """Method to wrap creation of panel orientation plots""" # pylint: disable=C0415,E0401,W0703 -import argparse -import sys import traceback from pathlib import Path import os -import matplotlib from ladybugtools_toolkit.solar import IrradianceType, tilt_orientation_factor, create_radiation_matrix +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder from ladybug.wea import AnalysisPeriod from ladybugtools_toolkit.plot.utilities import figure_to_base64 from ladybugtools_toolkit.bhom.wrapped.metadata.solar_radiation_metadata import solar_radiation_metadata import matplotlib.pyplot as plt -from pathlib import Path -import json from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, extract a heatmap" - ) - ) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to extract a heatmap from", - type=str, - required=True, -) -PARSER.add_argument( - "-d", - "--directions", - help="The number of directions to use when plotting orientations.", - type=int, - required=True, -) -PARSER.add_argument( - "-ti", - "--tilts", - help="The number of tilts to use when plotting orientations.", - type=int, - required=True, -) -PARSER.add_argument( - "-ir", - "--irradiance_type", - help="The irradiance type to use.", - type=str, - required=True, -) -PARSER.add_argument( - "-cmap", - "--cmap", - help="Matplotlib colour map to use.", - type=str, - required=True, - ) -PARSER.add_argument( - "-ap", - "--analysis_period", - help="Analysis period", - type=str, - required=True, -) -PARSER.add_argument( - "-t", - "--title", - help="The title to be displayed on the plot.", - type=str, - required=True, -) -PARSER.add_argument( - "-p", - "--save_path", - help="Path to save the output image.", - type=str, - required=False, - ) - -def directional_solar_radiation(epw_file, directions, tilts, irradiance_type, analysis_period, cmap, title, save_path) -> str: +@bhom_wrapper.bhom_callable("plot/directional_solar_radiation", argument_types = { "analysis_period": AnalysisPeriod }, decoder_cls=LBTBHoMJSONDecoder) +def directional_solar_radiation(epw_file: str, directions: int, tilts: int, irradiance_type: str, analysis_period: AnalysisPeriod, cmap: str, title: str = None, save_path:str = None) -> PlotInformation: try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") if cmap not in plt.colormaps(): cmap = "YlOrRd" - analysis_period = AnalysisPeriod.from_dict(json.loads(analysis_period)) - if irradiance_type == "Total": irradiance_type = IrradianceType.TOTAL elif irradiance_type == "Diffuse": @@ -94,37 +30,29 @@ def directional_solar_radiation(epw_file, directions, tilts, irradiance_type, an irradiance_type = IrradianceType.DIRECT elif irradiance_type == "Reflected": irradiance_type = IrradianceType.REFLECTED + + values, dirs, tts = create_radiation_matrix(Path(epw_file), rad_type=irradiance_type, analysis_period=analysis_period, directions=directions, tilts=tilts) with plt.style.context(style): fig, ax = plt.subplots(1, 1, figsize=(22.8/2, 7.6/2)) - values, dirs, tts = create_radiation_matrix(Path(epw_file), rad_type=irradiance_type, analysis_period=analysis_period, directions=directions, tilts=tilts) tilt_orientation_factor(Path(epw_file), ax=ax, rad_type=irradiance_type, analysis_period=analysis_period, directions=directions, tilts=tilts, cmap=cmap, style_context=style) if not (title == "" or title is None): ax.set_title(title) + plt.tight_layout() - return_dict = {} + pi = PlotInformation(other_data = solar_radiation_metadata(values, dirs, tts)) + image: str = "" if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path - return_dict["data"] = solar_radiation_metadata(values, dirs, tts) - plt.close(fig) - - return json.dumps(return_dict, default=str) - + pi.image = image + return pi except Exception: CONSOLE_LOGGER.error("Solar Radiation plot could not be created.", exc_info=1) - return traceback.format_exc() - -if __name__ == "__main__": - args = PARSER.parse_args() - - os.environ["TQDM_DISABLE"] = "1" # set an environment variable so that progress bars are disabled for the simulation process - matplotlib.use("Agg") - directional_solar_radiation(args.epw_file, args.directions, args.tilts, args.irradiance_type, args.analysis_period, args.colour_map, args.title, args.save_path) - del os.environ["TQDM_DISABLE"] # unset the env variable \ No newline at end of file + return traceback.format_exc() \ 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 df99c6f0..bb85ce83 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,14 +1,10 @@ """Method to wrap creation of diurnal plots""" # pylint: disable=C0415,E0401,W0703 -import argparse -import json import os -import sys import traceback -from pathlib import Path -import matplotlib -from ladybug.epw import EPW, AnalysisPeriod +from ladybug.epw import EPW from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.ladybug_extension.epw import wet_bulb_temperature from python_toolkit.plot.diurnal import diurnal as dnal from ladybug.datacollection import HourlyContinuousCollection @@ -16,56 +12,10 @@ from ladybugtools_toolkit.bhom.wrapped.metadata.collection import collection_metadata import matplotlib.pyplot as plt from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, extract a diurnal plot" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to extract a diurnal plot from", - type=str, - required=True, -) -PARSER.add_argument( - "-dtk", - "--data_type_key", - help="Key in EPW data to create a plot from.", - type=str, - required=True, -) -PARSER.add_argument( - "-colour", - "--colour", - help="Colour of the line", - type=str, - required=True, - ) -PARSER.add_argument( - "-t", - "--title", - help="Title that the plot will have", - type=str, - required=True, - ) -PARSER.add_argument( - "-ap", - "--period", - help="Period that will be plotted on the diurnal plot", - type=str, - required=True, - ) -PARSER.add_argument( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - -def diurnal(epw_file, data_type_key="Dry Bulb Temperature", colour="#000000", title=None, period="monthly", save_path = None) -> str: +@bhom_wrapper.bhom_callable("plot/epw_diurnal") +def diurnal(epw_file: str, data_type_key: str="Dry Bulb Temperature", colour: str="#000000", title: str=None, period: str="monthly", save_path: str=None, **kwargs) -> PlotInformation: try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") epw = EPW(epw_file) @@ -77,26 +27,22 @@ def diurnal(epw_file, data_type_key="Dry Bulb Temperature", colour="#000000", ti with plt.style.context(style): fig, ax = plt.subplots() - dnal(collection_to_series(coll), ax=ax, title=title, period=period, color=colour, style_context=style) - return_dict = {"data": collection_metadata(coll)} + pi = PlotInformation(other_data = collection_metadata(coll)) + image: str = "" + if save_path == None or save_path == "": base64 = figure_to_base64(fig, html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path plt.close(fig) - - return json.dumps(return_dict, default=str) + pi.image = image + return pi except Exception: CONSOLE_LOGGER.error("Diurnal plot could not be created.", exc_info=1) return traceback.format_exc() - -if __name__ == "__main__": - args = PARSER.parse_args() - matplotlib.use("Agg") - 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/epw_comparison.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/epw_comparison.py index 1b085e08..59aa81be 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/epw_comparison.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/epw_comparison.py @@ -1,100 +1,45 @@ """Method to wrap for conversion of EPW to CSV file.""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -import json -import sys import traceback -import matplotlib -import matplotlib.figure from ladybug.epw import EPW from ladybugtools_toolkit.plot.compare import compare_epw_key_line, compare_epw_key_hist +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt from ...logger import CONSOLE_LOGGER from typing import List +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, and a list of epws to compare to, construct a line chart for a specific epw key." - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to compare from", - type=str, - required=True, -) -PARSER.add_argument( - "-el", - "--epw_list", - help="List of EPW files to compare with the base", - type=str, - nargs='*', - action="extend", - required=True, -) -PARSER.add_argument( - "-dtk", - "--data_type_key", - help="Key to compare.", - type=str, - required=True, -) -PARSER.add_argument( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) -PARSER.add_argument( - "-l", - "--line", - help="Produce a line plot instead of a histogram", - action="store_true", - default=False - ) - -def epw_comparison(epw_file: str, epw_list: List[str], data_type_key: str, line:bool, save_path:str = None) -> str: +@bhom_wrapper.bhom_callable("plot/epw_comparison") +def epw_comparison(epw_file: str, epw_list: List[str], data_type_key: str, line:bool, save_path:str = None) -> PlotInformation: """Create a timeseries plot with a line for each epw file for the specified data key and return it in a format readable by the LadybugToolsAdapter.""" try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") + epws = [EPW(epw_file)] + epws.extend([EPW(f) for f in epw_list]) with plt.style.context(style): fig, ax = plt.subplots() - epws = [EPW(epw_file)] - epws.extend([EPW(f) for f in epw_list]) - if line: compare_epw_key_line(epws, key=data_type_key.lower().strip().replace(" ", "_"), style_context=style, ax=ax) else: compare_epw_key_hist(epws, key=data_type_key.lower().strip().replace(" ", "_"), style_context=style, ax=ax) - return_dict = {} + pi = PlotInformation() #Unsure of how to create representative collection metadata for a comparison plot type that doesn't simply list every epw file compared + image: str = "" if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path plt.close(fig) - - return_dict["data"] = None #Unsure of how to create representative collection metadata for a comparison plot type that doesn't simply list every epw file compared - - return json.dumps(return_dict, default=str) - + pi.image = image + return pi except Exception: CONSOLE_LOGGER.error("Timeseries comparison could not be created.", exc_info=1) return traceback.format_exc() - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - - epw_comparison(args.epw_file, args.epw_list, args.data_type_key, args.line, args.save_path) 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 index 95e9f55d..89695ba2 100644 --- 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 @@ -1,56 +1,16 @@ """Method to wrap creation of diurnal plots""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -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.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.plot.utilities import figure_to_base64 from ladybugtools_toolkit.plot.facades.condensation_risk.heatmap import facade_condensation_risk_chart_table +from python_toolkit.bhom.decorators import bhom_wrapper -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( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - - -def facade_condensation_risk_chart(epw_file: str, thresholds: list[float], save_path: str = None) -> None: +@bhom_wrapper.bhom_callable("plot/facade_condensation_risk_chart") +def facade_condensation_risk_chart(epw_file: str, thresholds: list[float], save_path: str = None, **kwargs) -> PlotInformation: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") epw = EPW(epw_file) @@ -58,22 +18,17 @@ def facade_condensation_risk_chart(epw_file: str, thresholds: list[float], save_ fig = facade_condensation_risk_chart_table(epw_file, thresholds, style_context=style) - return_dict = {"data": collection_metadata(hcc)} + pi = PlotInformation(other_data = collection_metadata(hcc)) + image: str = "" if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=300, transparent=True) - return_dict["figure"] = save_path + image = save_path plt.close(fig) + pi.image = image - return json.dumps(return_dict, default=str) - - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - facade_condensation_risk_chart(args.epw_file, args.thresholds, args.save_path) + return pi 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 index 231641eb..a6b7bcb7 100644 --- 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 @@ -1,55 +1,16 @@ """Method to wrap creation of diurnal plots""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -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.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.plot.utilities import figure_to_base64 from ladybugtools_toolkit.plot.facades.condensation_risk.heatmap import facade_condensation_risk_heatmap_histogram +from python_toolkit.bhom.decorators import bhom_wrapper -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( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - -def facade_condensation_risk_heatmap(epw_file: str, thresholds: list[float], save_path: str = None) -> None: +@bhom_wrapper.bhom_callable("plot/facade_condensation_risk_heatmap") +def facade_condensation_risk_heatmap(epw_file: str, thresholds: list[float], save_path: str = None, **kwargs) -> PlotInformation: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") epw = EPW(epw_file) @@ -57,22 +18,16 @@ def facade_condensation_risk_heatmap(epw_file: str, thresholds: list[float], sav fig = facade_condensation_risk_heatmap_histogram(epw_file, thresholds, style_context=style) - return_dict = {"data": collection_metadata(hcc)} + pi = PlotInformation(other_data = collection_metadata(hcc)) + image: str = "" if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=300, transparent=True) - return_dict["figure"] = save_path + image = save_path plt.close(fig) - - return json.dumps(return_dict, default=str) - - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - facade_condensation_risk_heatmap(args.epw_file, args.thresholds, args.save_path) + pi.image = image + return pi \ No newline at end of file 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 a1368c60..188c1d5d 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 @@ -1,98 +1,53 @@ """Method to wrap for conversion of EPW to CSV file.""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -from pathlib import Path -import json -import sys import traceback -import matplotlib -import matplotlib.figure from ladybug.epw import EPW from ladybug.datacollection import HourlyContinuousCollection from python_toolkit.plot.heatmap import heatmap as hmap from ladybugtools_toolkit.ladybug_extension.datacollection import collection_to_series +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.bhom.wrapped.metadata.collection import collection_metadata from ladybugtools_toolkit.ladybug_extension.epw import wet_bulb_temperature from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, extract a heatmap" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to extract a heatmap from", - type=str, - required=True, -) -PARSER.add_argument( - "-dtk", - "--data_type_key", - help="Key in EPW data to create a plot from.", - type=str, - required=True, -) -PARSER.add_argument( - "-cmap", - "--colour_map", - help="Matplotlib colour map to use.", - type=str, - required=True, - ) -PARSER.add_argument( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - -def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = None) -> str: +@bhom_wrapper.bhom_callable("plot/epw_heatmap") +def heatmap(epw_file: str, data_type_key: str, colour_map: str, save_path:str = None, **kwargs) -> PlotInformation: """Create a CSV file version of an EPW.""" try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") + if colour_map not in plt.colormaps(): colour_map = "YlGnBu" + epw = EPW(epw_file) + + if data_type_key == "Wet Bulb Temperature": + coll = wet_bulb_temperature(epw) + else: + coll = HourlyContinuousCollection.from_dict([a for a in epw.to_dict()["data_collections"] if a["header"]["data_type"]["name"] == data_type_key][0]) + with plt.style.context(style): fig, ax = plt.subplots() - - epw = EPW(epw_file) - - if data_type_key == "Wet Bulb Temperature": - coll = wet_bulb_temperature(epw) - else: - coll = HourlyContinuousCollection.from_dict([a for a in epw.to_dict()["data_collections"] if a["header"]["data_type"]["name"] == data_type_key][0]) - hmap(collection_to_series(coll), ax=ax, cmap=colour_map, style_context=style) + plt.tight_layout() - return_dict = {} + pi = PlotInformation(other_data = collection_metadata(coll)) + image: str = "" if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path plt.close(fig) - - return_dict["data"] = collection_metadata(coll) - - return json.dumps(return_dict, default=str) - + pi.image = image + return pi except Exception: CONSOLE_LOGGER.error("Heatmap could not be created.", exc_info=1) return traceback.format_exc() - - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - heatmap(args.epw_file, args.data_type_key, args.colour_map, 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 f0155acd..615eea05 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 @@ -1,85 +1,44 @@ """Method to wrap creation of sunpath plots""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -import sys import traceback -from pathlib import Path -import matplotlib from ladybugtools_toolkit.plot._sunpath import sunpath as spath 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.bhom.wrapped.metadata.plot_information import PlotInformation +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt -from pathlib import Path -import json from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, create a plot of its' sun path" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to extract a sun path plot from", - type=str, - required=True, -) -PARSER.add_argument( - "-s", - "--size", - help="Size of the sun", - type=float, - required=True, - ) -PARSER.add_argument( - "-ap", - "--analysis_period", - help="Analysis perioderiod of the sun path", - type=str, - required=True, - ) -PARSER.add_argument( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - -def sunpath(epw_file, analysis_period, size, save_path) -> str: +@bhom_wrapper.bhom_callable("plot/sunpath", argument_types = { "analysis_period": AnalysisPeriod }, decoder_cls=LBTBHoMJSONDecoder) +def sunpath(epw_file: str, analysis_period: AnalysisPeriod, size: int, save_path: str = None, **kwargs) -> PlotInformation: try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") + epw = EPW(epw_file) + with plt.style.context(style): fig, ax = plt.subplots() - - analysis_period = AnalysisPeriod.from_dict(json.loads(analysis_period)) - epw = EPW(epw_file) spath(location=epw.location, analysis_period=analysis_period, sun_size=size, ax=ax, style_context=style) + plt.tight_layout() - return_dict = {"data": sunpath_metadata(Sunpath.from_location(epw.location))} + pi = PlotInformation(other_data = sunpath_metadata(Sunpath.from_location(epw.location))) + image: str = "" if save_path is None or save_path == "": base64 = figure_to_base64(fig, html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path + pi.image = image plt.close(fig) - - return json.dumps(return_dict, default=str) + return pi except Exception: CONSOLE_LOGGER.error("Sunpath could not be created.", exc_info=1) return traceback.format_exc() - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - sunpath(args.epw_file, args.analysis_period, args.size, 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 410e2950..f3348901 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 @@ -1,62 +1,27 @@ """Method to wrap UTCI plots""" # pylint: disable=C0415,E0401,W0703 -import argparse import os -import sys import traceback +from typing import Dict import matplotlib from ladybugtools_toolkit.external_comfort.externalcomfort import ExternalComfort +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation from ladybugtools_toolkit.bhom.wrapped.metadata.utci_metadata import utci_metadata +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder from ladybugtools_toolkit.plot.utilities import figure_to_base64 from ladybugtools_toolkit.categorical.categories import Categorical, UTCI_DEFAULT_CATEGORIES import matplotlib.pyplot as plt import numpy as np -import json from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, extract a heatmap" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="helptext", - type=str, - required=False -) -PARSER.add_argument( - "-in", - "--input_json", - help="helptext", - type=str, - required=True, -) -PARSER.add_argument( - "-sp", - "--save_path", - help="helptext", - type=str, - required=False, -) - -def utci_heatmap(input_json:str, save_path = None, epw_file:str = None) -> str: +@bhom_wrapper.bhom_callable("plot/utci_heatmap", argument_types = { "external_comfort": ExternalComfort }, encoder_cls=LBTBHoMJSONEncoder, decoder_cls=LBTBHoMJSONDecoder) +def utci_heatmap(external_comfort: ExternalComfort, bin_colours: list[str], save_path: str = "", **kwargs) -> Dict[str, object]: try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") - - if not input_json.startswith("{"): #assume it's a path - with open(input_json, "r") as f: - input_json = f.read() - - argsDict = json.loads(input_json) - - ec = ExternalComfort.from_dict(json.loads(argsDict["external_comfort"])) - custom_bins = UTCI_DEFAULT_CATEGORIES - bin_colours = json.loads(argsDict["bin_colours"]) - if len(bin_colours) == 10: custom_bins = Categorical( bins=(-np.inf, -40, -27, -13, 0, 9, 26, 32, 38, 46, np.inf), @@ -65,29 +30,29 @@ def utci_heatmap(input_json:str, save_path = None, epw_file:str = None) -> str: with plt.style.context(style): fig, ax = plt.subplots(1, 1, figsize=(10, 4)) - ec.plot_utci_heatmap(utci_categories = custom_bins, ax=ax, style_context=style) - - utci_collection = ec.universal_thermal_climate_index - - return_dict = {"data": utci_metadata(utci_collection), "external_comfort": ec.to_dict()} - + external_comfort.plot_utci_heatmap(utci_categories = custom_bins, ax=ax, style_context=style) plt.tight_layout() + utci_collection = external_comfort.universal_thermal_climate_index + pi = PlotInformation(other_data = utci_metadata(utci_collection)) + + image:str = "" + if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path - + image = save_path + plt.close(fig) + pi.image = image + return_dict = { + "info": pi, + "external_comfort": external_comfort + } + return return_dict - return json.dumps(return_dict, default=str) except Exception: CONSOLE_LOGGER.error("UTCI Heatmap could not be created.", exc_info=1) - return traceback.format_exc() - -if __name__ == "__main__": - args = PARSER.parse_args() - matplotlib.use("Agg") - utci_heatmap(args.input_json, args.save_path) \ No newline at end of file + return traceback.format_exc() \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/walkability_heatmap.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/walkability_heatmap.py index 4dcd4662..ec80e9d3 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/walkability_heatmap.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/plot/walkability_heatmap.py @@ -1,82 +1,48 @@ -import argparse -import os -import sys +import os +from typing import Dict import matplotlib import traceback from ladybugtools_toolkit.external_comfort.externalcomfort import ExternalComfort from ladybugtools_toolkit.bhom.wrapped.metadata.utci_metadata import utci_metadata +from ladybugtools_toolkit.bhom.wrapped.metadata.plot_information import PlotInformation +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder from ladybugtools_toolkit.plot.utilities import figure_to_base64 import json import matplotlib.pyplot as plt from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an external comfort object, extract a walkability heatmap" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="helptext", - type=str, - required=False -) -PARSER.add_argument( - "-in", - "--input_json", - help="helptext", - type=str, - required=True, -) -PARSER.add_argument( - "-sp", - "--save_path", - help="helptext", - type=str, - required=False, -) - -def walkability_heatmap(input_json: str, save_path: str, epw_file:str = None) -> str: +@bhom_wrapper.bhom_callable("plot/walkability_heatmap", argument_types = { "external_comfort": ExternalComfort }, encoder_cls=LBTBHoMJSONEncoder, decoder_cls=LBTBHoMJSONDecoder) +def walkability_heatmap(external_comfort: ExternalComfort, save_path: str, **kwargs) -> Dict[str, object]: try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") - if not input_json.startswith("{"): #assume it's a path - with open(input_json, "r") as f: - input_json = f.read() - - argsDict = json.loads(input_json) - - ec = ExternalComfort.from_dict(json.loads(argsDict["external_comfort"])) - with plt.style.context(style): fig, ax = plt.subplots(1, 1, figsize=(10, 4)) - ec.plot_walkability_heatmap(ax=ax, style_context=style) - - #TODO: create walkability collection metadata - utci_collection = ec.universal_thermal_climate_index + external_comfort.plot_walkability_heatmap(ax=ax, style_context=style) + plt.tight_layout() + + image:str = "" - return_dict = {"data": utci_metadata(utci_collection), "external_comfort": ec.to_dict()} + utci_collection = external_comfort.universal_thermal_climate_index + pi = PlotInformation(other_data = utci_metadata(utci_collection)) - plt.tight_layout() - if save_path == None or save_path == "": base64 = figure_to_base64(fig,html=False) - return_dict["figure"] = base64 + image = base64 else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path - + image = save_path + plt.close(fig) - - return json.dumps(return_dict, default=str) + pi.image = image + return_dict = { + "info": pi, + "external_comfort": external_comfort + } + return return_dict except Exception: CONSOLE_LOGGER.error("Walkability plot could not be created.", exc_info=1) return traceback.format_exc() - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - walkability_heatmap(args.json_args, 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 b1282c70..497bedf5 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 @@ -10,91 +10,45 @@ 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.bhom.wrapped.metadata.plot_information import PlotInformation +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder from ladybugtools_toolkit.plot.utilities import figure_to_base64 import matplotlib.pyplot as plt from pathlib import Path import json from ...logger import CONSOLE_LOGGER +from python_toolkit.bhom.decorators import bhom_wrapper -PARSER = argparse.ArgumentParser( - description=( - "Given an EPW file path, extract a heatmap" - ) -) -PARSER.add_argument( - "-e", - "--epw_file", - help="The EPW file to extract a heatmap from", - type=str, - required=True, -) -PARSER.add_argument( - "-ap", - "--analysis_period", - help="Analysis period", - type=str, - required=True, -) -PARSER.add_argument( - "-cmap", - "--colour_map", - help="Matplotlib colour map to use.", - type=str, - required=True, - ) -PARSER.add_argument( - "-bins", - "--bins", - help="Number of bins", - type=int, - required=True, -) -PARSER.add_argument( - "-p", - "--save_path", - help="Path where to save the output image.", - type=str, - required=False, - ) - -def windrose(epw_file: str, analysis_period: str, colour_map: str, bins: int, save_path: str = None) -> str: +@bhom_wrapper.bhom_callable("plot/windrose", argument_types = { "analysis_period": AnalysisPeriod }, decoder_cls=LBTBHoMJSONDecoder) +def windrose(epw_file: str, analysis_period: AnalysisPeriod, colour_map: str, bins: int, save_path: str = None, **kwargs) -> PlotInformation: """Method to wrap for creating wind roses from epw files.""" try: style = os.environ.get("BHOM_style_context", "python_toolkit.bhom") + if colour_map not in plt.colormaps(): colour_map = "YlGnBu" - epw = EPW(epw_file) - analysis_period = AnalysisPeriod.from_dict(json.loads(analysis_period)) w_epw = Wind.from_epw(epw_file) + wind_filtered = w_epw.filter_by_analysis_period(analysis_period=analysis_period) with plt.style.context(style): 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) - wind_filtered.plot_windrose(ax=ax, directions=bins, ylim=(0, 3.6/bins), colors=colour_map, style_context=style) - - return_dict = {"data": wind_metadata(wind_filtered, directions=bins)} - plt.tight_layout() + + pi = PlotInformation(other_data = wind_metadata(wind_filtered, directions=bins)) + image:str = "" + if save_path == None or save_path == "": - return_dict["figure"] = figure_to_base64(fig,html=False) + image = figure_to_base64(fig,html=False) else: fig.savefig(save_path, dpi=150, transparent=True) - return_dict["figure"] = save_path + image = save_path + pi.image = image plt.close(fig) - - return json.dumps(return_dict, default=str) - + return pi except Exception: CONSOLE_LOGGER.error("Windrose could not be created.", exc_info=1) return traceback.format_exc() - - -if __name__ == "__main__": - - args = PARSER.parse_args() - matplotlib.use("Agg") - windrose(args.epw_file, args.analysis_period, args.colour_map, args.bins, args.save_path) \ No newline at end of file diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/simulation_result.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/simulation_result.py index a3459c58..574bcb8c 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/simulation_result.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/bhom/wrapped/simulation_result.py @@ -1,33 +1,11 @@ -"""Method to wrap for access to pre-defined materials.""" # pylint: disable=C0415,E0401,W0703 -import argparse -import traceback +from python_toolkit.bhom.decorators import bhom_wrapper +from ladybugtools_toolkit.external_comfort._simulatebase import SimulationResult +from ladybugtools_toolkit.bhom.from_bhom import LBTBHoMJSONDecoder +from ladybugtools_toolkit.bhom.to_bhom import LBTBHoMJSONEncoder +#see external_comfort.py -def main(json_file: str) -> None: - """From a json file represention of a SimulationResult, run the simulation.""" - try: - from ladybugtools_toolkit.external_comfort._simulatebase import SimulationResult - - res = SimulationResult.from_file(json_file) - res.to_file(json_file) - - except Exception as e: # pylint: disable=W0703 - print(traceback.format_exc()) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=( - "Given a JSON file containing the string represention of a SimulationResult, run the simulation." - ) - ) - parser.add_argument( - "-j", - "--json_file", - help="The JSON file to convert into a SimulationResult object Python-side.", - type=str, - required=True, - ) - args = parser.parse_args() - main(args.json_file) +@bhom_wrapper.bhom_callable("simulation_result", argument_types = {"simulation_result": SimulationResult}, encoder_cls=LBTBHoMJSONEncoder, decoder_cls=LBTBHoMJSONDecoder) +def main(simulation_result: SimulationResult, **kwargs) -> None: + return simulation_result diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py index e5c1fb6b..331e15b7 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/categorical/categorical.py @@ -22,6 +22,7 @@ from matplotlib.legend import Legend from mpl_toolkits.axes_grid1 import make_axes_locatable from python_toolkit.bhom.analytics import bhom_analytics +from python_toolkit.bhom.bhom_object import BHoMObject from python_toolkit.plot.heatmap import heatmap from python_toolkit.plot.timeseries import timeseries @@ -30,8 +31,8 @@ from ..plot.utilities import contrasting_color -@dataclass(init=True, repr=True) -class Categorical: +@dataclass(init=False, repr=True) +class Categorical(BHoMObject): """A class to hold categorical data. Args: @@ -51,7 +52,14 @@ class Categorical: colors: tuple[str] = field(default_factory=tuple, repr=True) name: str = field(default="GenericCategories") - def __post_init__(self): + def __init__(self, bins = (), bin_names = (), colors = (), name = "GenericCategories", **kwargs): + self.bins = bins + self.bin_names = bin_names + self.colors = colors + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.Categorical") + super().__init__(_t, name=name, **kwargs) + # ensure colors are valid if len(self.colors) == 0: cycle = tuple(plt.rcParams["axes.prop_cycle"].by_key()["color"]) @@ -811,7 +819,7 @@ def text(self) -> str: return d[self] -@dataclass(init=True, repr=True) +@dataclass(init=False, repr=True) class CategoricalComfort(Categorical): """A class to hold categorical comfort data. @@ -822,12 +830,15 @@ class CategoricalComfort(Categorical): comfort_classes: tuple[ComfortClass] = field(default_factory=tuple, repr=False) - def __post_init__(self): + def __init__(self, comfort_classes: tuple[ComfortClass] = (), **kwargs): + self.comfort_classes = comfort_classes + + super().__init__(**kwargs) + if len(self.comfort_classes) == 0: raise ValueError("The comfort classes cannot be empty.") if len(self.comfort_classes) != len(self): raise ValueError("The number of comfort classes must match the number of bins.") - return super().__post_init__() @bhom_analytics() def simplify(self) -> "CategoricalComfort": diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_externalcomfortbase.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_externalcomfortbase.py index 9bbd4897..f5d75eaf 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_externalcomfortbase.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_externalcomfortbase.py @@ -15,12 +15,14 @@ from matplotlib.colors import LinearSegmentedColormap from ..bhom.logger import CONSOLE_LOGGER -from ..bhom.to_bhom import hourlycontinuouscollection_to_bhom +from ..bhom.to_bhom import hourlycontinuouscollection_to_bhom, LBTBHoMJSONEncoder +from ..bhom.from_bhom import LBTBHoMJSONDecoder from ..categorical.categories import UTCI_DEFAULT_CATEGORIES, Categorical from ..helpers import convert_keys_to_snake_case from ..ladybug_extension.analysisperiod import describe_analysis_period from ..ladybug_extension.datacollection import collection_to_series from python_toolkit.plot.heatmap import heatmap +from python_toolkit.bhom.bhom_object import BHoMObject from ..plot._utci import utci_day_comfort_metrics, utci_heatmap_histogram from ..plot.colormaps import ( DBT_COLORMAP, @@ -44,8 +46,8 @@ ] -@dataclass(init=True, repr=True, eq=True) -class ExternalComfort: +@dataclass(init=False, repr=True, eq=True) +class ExternalComfort(BHoMObject): """_""" simulation_result: SimulationResult @@ -57,8 +59,34 @@ class ExternalComfort: mean_radiant_temperature: HourlyContinuousCollection = None universal_thermal_climate_index: HourlyContinuousCollection = None - def __post_init__(self): - """_""" + def __init__( + self, + simulation_result: SimulationResult | BHoMObject, + typology: Typology | BHoMObject, + dry_bulb_temperature: HourlyContinuousCollection = None, + relative_humidity: HourlyContinuousCollection = None, + wind_speed: HourlyContinuousCollection = None, + mean_radiant_temperature: HourlyContinuousCollection = None, + universal_thermal_climate_index: HourlyContinuousCollection = None, + **kwargs + ) -> "ExternalComfort": + if type(simulation_result) is BHoMObject: + simulation_result = SimulationResult._from_bhom_object(simulation_result) + + if type(typology) is BHoMObject: + typology = Typology._from_bhom_object(typology) + + self.simulation_result = simulation_result + self.typology = typology + + self.dry_bulb_temperature = dry_bulb_temperature + self.relative_humidity = relative_humidity + self.wind_speed = wind_speed + self.mean_radiant_temperature = mean_radiant_temperature + self.universal_thermal_climate_index = universal_thermal_climate_index + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.ExternalComfort") + super().__init__(_t, **kwargs) # validation if not isinstance(self.simulation_result, SimulationResult): @@ -68,15 +96,23 @@ def __post_init__(self): if isinstance(self.typology, Typologies): self.typology = self.typology.value + if not isinstance(self.typology, Typology): raise ValueError("typology must be an instance of Typology.") for attr in _ATTRIBUTES: + a = getattr(self, attr) + + if isinstance(a, BHoMObject): + setattr(self, attr, collection_from_bhom_object(a)) + elif isinstance(a, dict): + setattr(self, attr, HourlyContinuousCollection.from_dict(a)) + if not isinstance( getattr(self, attr), (HourlyContinuousCollection, type(None)) ): raise ValueError( - f"{attr} must be an instance of HourlyContinuousCollection or None." + f"{attr} must be either an HourlyContinuousCollection, or None." ) CONSOLE_LOGGER.info( @@ -123,6 +159,13 @@ def __post_init__(self): def __repr__(self) -> str: return f"{self.__class__.__name__}({self.simulation_result}, {self.typology})" + def to_json(self): + return super().to_json(encoder_class=LBTBHoMJSONEncoder) + + @classmethod + def from_json(cls, j): + return super().from_json(j, decoder_class=LBTBHoMJSONDecoder) + def to_dict(self) -> str: """Convert this object to a dictionary.""" attr_dict = {} @@ -168,16 +211,6 @@ def from_dict(cls, d: dict) -> "ExternalComfort": universal_thermal_climate_index=d["universal_thermal_climate_index"], ) - def to_json(self) -> str: - """Convert this object to a JSON string.""" - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_string: str) -> "SimulationResult": - """Create this object from a JSON string.""" - - return cls.from_dict(json.loads(json_string)) - def to_file(self, path: Path) -> Path: """Write this object to a JSON file.""" diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_shelterbase.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_shelterbase.py index a7c7ac37..ad149700 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_shelterbase.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_shelterbase.py @@ -2,7 +2,7 @@ # pylint: disable=E0401 import json from pathlib import Path -from typing import Any +from typing import Any, Union from dataclasses import dataclass # pylint: enable=E0401 @@ -29,23 +29,44 @@ from python_toolkit.bhom.analytics import bhom_analytics -from ..bhom.to_bhom import point3d_to_bhom +from python_toolkit.bhom.bhom_object import BHoMObject, IObject +from ..bhom.to_bhom import LBTBHoMJSONEncoder, point3d_to_bhom +from ..bhom.from_bhom import LBTBHoMJSONDecoder from ..ladybug_extension.epw import sun_position_list from ..helpers import convert_keys_to_snake_case SENSOR_LOCATION = Point3D(0, 0, 1.2) -@dataclass(init=True, eq=True) -class Shelter: +@dataclass(init=False, eq=True) +class Shelter(BHoMObject): """_""" vertices: tuple[Point3D] wind_porosity: tuple[float] = (0,) * 8760 radiation_porosity: tuple[float] = (0,) * 8760 - def __post_init__(self): - """_""" + def __init__(self, + vertices: tuple[Point3D], + wind_porosity: tuple[float] = None, + radiation_porosity: tuple[float] = None, + **kwargs + ) -> "Shelter": + self.vertices = list(vertices) + + for i, item in enumerate(self.vertices): + if isinstance(item, dict): + self.vertices[i] = Point3D.from_dict(item) + elif not isinstance(item, Point3D): + raise ValueError("All vertices must be Point3D objects, dictionaries, or BHoM IObjects that can be converted to Point3D.") + + self.vertices = tuple(self.vertices) + + self.wind_porosity = (0,) * 8760 if wind_porosity is None else wind_porosity + self.radiation_porosity = (0,) * 8760 if radiation_porosity is None else radiation_porosity + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.Shelter") + super().__init__(_t, **kwargs) # validation if len(self.wind_porosity) != 8760: @@ -81,9 +102,6 @@ def __post_init__(self): if len(self.vertices) < 3: raise ValueError("A shelter must have at least 3 vertices.") - if not all(isinstance(item, Point3D) for item in self.vertices): - raise ValueError("All vertices must be Point3D objects.") - _plane = Plane.from_three_points(*self.vertices[:3]) for vertex in self.vertices[3:]: if not np.isclose(a=_plane.distance_to_point(point=vertex), b=0): @@ -98,7 +116,15 @@ def __repr__(self) -> str: f"avg_radiation_porosity={self.average_radiation_porosity:0.2f}" ")" ) + + def to_json(self): + return super().to_json(encoder_class=LBTBHoMJSONEncoder) + + @classmethod + def from_json(cls, j): + return super().from_json(j, decoder_class=LBTBHoMJSONDecoder) + #TODO: maybe these methods aren't needed with the BHoMObject class implemented def to_dict(self) -> str: """Convert this object to a dictionary.""" point_dicts = [] @@ -130,16 +156,6 @@ def from_dict(cls, d: dict) -> "Shelter": radiation_porosity=d["radiation_porosity"], ) - def to_json(self) -> str: - """Convert this object to a JSON string.""" - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_string: str) -> "Shelter": - """Create this object from a JSON string.""" - - return cls.from_dict(json.loads(json_string)) - def to_file(self, path: Path) -> Path: """Convert this object to a JSON file.""" diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_simulatebase.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_simulatebase.py index e7ae8483..05a41bc6 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_simulatebase.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_simulatebase.py @@ -40,15 +40,18 @@ from ladybug_comfort.collection.solarcal import OutdoorSolarCal, SolarCalParameter from lbt_recipes.version import check_openstudio_version +from python_toolkit.bhom.bhom_object import BHoMObject from ..bhom.logger import CONSOLE_LOGGER +from ..bhom.from_bhom import LBTBHoMJSONDecoder from ..bhom.to_bhom import ( + LBTBHoMJSONEncoder, hourlycontinuouscollection_to_bhom, material_to_bhom, ) from ..honeybee_extension.results import load_sql from ..ladybug_extension.datacollection import ( collection_from_series, - collection_to_series, + collection_to_series ) from ..ladybug_extension.epw import epw_to_dataframe from ..ladybug_extension.epw import equality as epw_equality @@ -604,9 +607,8 @@ def radiant_temperature( "unshaded_mean_radiant_temperature", ] - -@dataclass(init=True, repr=True, eq=True) -class SimulationResult: +@dataclass(init=False, repr=True, eq=True) +class SimulationResult(BHoMObject): """_""" epw_file: Path @@ -630,11 +632,70 @@ class SimulationResult: unshaded_shortwave_mean_radiant_temperature_delta: HourlyContinuousCollection = None unshaded_mean_radiant_temperature: HourlyContinuousCollection = None - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.identifier})" + def __init__( + self, + epw_file: Path | BHoMObject, + ground_material: EnergyMaterial | EnergyMaterialVegetation, + shade_material: EnergyMaterial | EnergyMaterialVegetation, + identifier: str = None, + + shaded_down_temperature: HourlyContinuousCollection = None, + shaded_up_temperature: HourlyContinuousCollection = None, + + unshaded_down_temperature: HourlyContinuousCollection = None, + unshaded_up_temperature: HourlyContinuousCollection = None, + + shaded_radiant_temperature: HourlyContinuousCollection = None, + shaded_longwave_mean_radiant_temperature_delta: HourlyContinuousCollection = None, + shaded_shortwave_mean_radiant_temperature_delta: HourlyContinuousCollection = None, + shaded_mean_radiant_temperature: HourlyContinuousCollection = None, + + unshaded_radiant_temperature: HourlyContinuousCollection = None, + unshaded_longwave_mean_radiant_temperature_delta: HourlyContinuousCollection = None, + unshaded_shortwave_mean_radiant_temperature_delta: HourlyContinuousCollection = None, + unshaded_mean_radiant_temperature: HourlyContinuousCollection = None, + **kwargs + ) -> "SimulationResult": + + if isinstance(epw_file, BHoMObject): + if epw_file._t == "BH.oM.Adapter.FileSettings": + epw_file = Path(epw_file.directory) / epw_file.file_name + + self.epw_file = epw_file + + if isinstance(ground_material, dict): + ground_material = dict_to_material(ground_material) + self.ground_material = ground_material + + if isinstance(shade_material, dict): + shade_material = dict_to_material(shade_material) + self.shade_material = shade_material + + name = kwargs.pop("name", None) + + if name is not None and name != '' and identifier is None: + identifier = name + + self.identifier = identifier + + self.shaded_down_temperature = shaded_down_temperature + self.shaded_up_temperature = shaded_up_temperature + + self.unshaded_down_temperature = unshaded_down_temperature + self.unshaded_up_temperature = unshaded_up_temperature - def __post_init__(self): - """_""" + self.shaded_radiant_temperature = shaded_radiant_temperature + self.shaded_longwave_mean_radiant_temperature_delta = shaded_longwave_mean_radiant_temperature_delta + self.shaded_shortwave_mean_radiant_temperature_delta = shaded_shortwave_mean_radiant_temperature_delta + self.shaded_mean_radiant_temperature = shaded_mean_radiant_temperature + + self.unshaded_radiant_temperature = unshaded_radiant_temperature + self.unshaded_longwave_mean_radiant_temperature_delta = unshaded_longwave_mean_radiant_temperature_delta + self.unshaded_shortwave_mean_radiant_temperature_delta = unshaded_shortwave_mean_radiant_temperature_delta + self.unshaded_mean_radiant_temperature = unshaded_mean_radiant_temperature + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.SimulationResult") + super().__init__(_t, **kwargs) # validation if not isinstance(self.epw_file, (Path, str)): @@ -648,6 +709,7 @@ def __post_init__(self): if isinstance(self.shade_material, Materials): self.shade_material = self.shade_material.value + if not isinstance( self.ground_material, (EnergyMaterial, EnergyMaterialVegetation) ): @@ -667,6 +729,13 @@ def __post_init__(self): ) for attr in _ATTRIBUTES: + a = getattr(self, attr) + + if isinstance(a, BHoMObject): + setattr(self, attr, collection_from_bhom_object(a)) + elif isinstance(a, dict): + setattr(self, attr, HourlyContinuousCollection.from_dict(a)) + if not isinstance( getattr(self, attr), (HourlyContinuousCollection, type(None)) ): @@ -756,6 +825,16 @@ def __post_init__(self): for attr in _ATTRIBUTES: setattr(self, f"{attr}_series", collection_to_series(getattr(self, attr))) + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.identifier})" + + def to_json(self): + return super().to_json(encoder_class=LBTBHoMJSONEncoder) + + @classmethod + def from_json(cls, j): + return super().from_json(j, decoder_class=LBTBHoMJSONDecoder) + def to_dict(self) -> dict[str, Any]: """Convert this object to a dictionary.""" ground_material_dict = self.ground_material.to_dict() @@ -822,16 +901,6 @@ def from_dict(cls, d: dict[str, Any]) -> "SimulationResult": unshaded_mean_radiant_temperature=d["unshaded_mean_radiant_temperature"], ) - def to_json(self) -> str: - """Create a JSON string from this object.""" - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_string: str) -> "SimulationResult": - """Create this object from a JSON string.""" - - return cls.from_dict(json.loads(json_string)) - def to_file(self, path: Path) -> Path: """Write this object to a JSON file.""" diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_typologybase.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_typologybase.py index ba07fee2..fd68c6ea 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_typologybase.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/external_comfort/_typologybase.py @@ -1,5 +1,6 @@ """Base class for typology objects.""" # pylint: disable=E0401 +from ctypes import ArgumentError import json from dataclasses import dataclass from pathlib import Path @@ -11,6 +12,9 @@ from ladybug.epw import EPW, HourlyContinuousCollection from python_toolkit.bhom.analytics import bhom_analytics +from python_toolkit.bhom.bhom_object import BHoMObject +from ..bhom.to_bhom import LBTBHoMJSONEncoder +from ..bhom.from_bhom import LBTBHoMJSONDecoder from ..helpers import ( convert_keys_to_snake_case, decay_rate_smoother, @@ -29,8 +33,8 @@ from .simulate import SimulationResult -@dataclass(init=True, repr=True, eq=True) -class Typology: +@dataclass(init=False, repr=True, eq=True) +class Typology(BHoMObject): """_""" identifier: str @@ -40,8 +44,39 @@ class Typology: wind_speed_multiplier: float = 1 radiant_temperature_adjustment: tuple[float] = (0,) * 8760 - def __post_init__(self): - """_""" + def __init__(self, + identifier: str = None, + shelters: tuple[Shelter | BHoMObject] = (), + evaporative_cooling_effect: tuple[float] = None, + target_wind_speed: tuple[float] = None, + wind_speed_multiplier: float = 1, + radiant_temperature_adjustment: tuple[float] = None, + **kwargs + ) -> "Typology": + + identifier = kwargs.pop("name", None) if identifier is None else identifier + + if identifier is None: + raise ArgumentError("Missing required key word argument 'identifier' or 'name.") + + self.identifier = identifier + self.shelters = list((None,) * len(shelters)) + + for i, shelter in enumerate(shelters): + if type(shelter) is BHoMObject: + self.shelters[i] = Shelter._from_bhom_object(shelter) + else: + self.shelters[i] = shelter + + self.shelters = tuple(self.shelters) + + self.evaporative_cooling_effect = (0,) * 8760 if evaporative_cooling_effect is None else evaporative_cooling_effect + self.target_wind_speed = (None,) * 8760 if target_wind_speed is None else target_wind_speed + self.wind_speed_multiplier = wind_speed_multiplier + self.radiant_temperature_adjustment = (0,) * 8760 if radiant_temperature_adjustment is None else radiant_temperature_adjustment + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.Typology") + super().__init__(_t, **kwargs) # validation if len(self.shelters) > 0: @@ -82,6 +117,13 @@ def __post_init__(self): def __repr__(self) -> str: return f"{self.__class__.__name__}({self.identifier})" + + def to_json(self): + return super().to_json(encoder_class=LBTBHoMJSONEncoder) + + @classmethod + def from_json(cls, j): + return super().from_json(j, decoder_class=LBTBHoMJSONDecoder) def to_dict(self) -> str: """Convert this object to a dictionary.""" @@ -118,15 +160,6 @@ def from_dict(cls, d: dict) -> "Shelter": radiant_temperature_adjustment=d["radiant_temperature_adjustment"], ) - def to_json(self) -> str: - """Convert this object to a JSON string.""" - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_string: str) -> "Shelter": - """Create this object from a JSON string.""" - return cls.from_dict(json.loads(json_string)) - def to_file(self, path: Path) -> Path: """Convert this object to a JSON file.""" if Path(path).suffix != ".json": diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/datacollection.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/datacollection.py index b432de1d..c37f7fe6 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/datacollection.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/ladybug_extension/datacollection.py @@ -19,12 +19,12 @@ from ladybug.datatype.angle import Angle from ladybug.dt import DateTime from python_toolkit.bhom.analytics import bhom_analytics +from python_toolkit.bhom.bhom_object import BHoMObject from ..helpers import circular_weighted_mean from .analysisperiod import analysis_period_to_datetimes from .analysisperiod import describe_analysis_period from .header import header_from_string, header_to_string - def collection_to_series(collection: BaseCollection, name: str = None) -> pd.Series: """Convert a Ladybug hourlyContinuousCollection object into a Pandas Series object. diff --git a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py index 7d7734c8..531e1122 100644 --- a/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py +++ b/LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py @@ -42,13 +42,14 @@ describe_analysis_period, ) from python_toolkit.plot.timeseries import timeseries +from python_toolkit.bhom.bhom_object import BHoMObject from .plot.utilities import contrasting_color, format_polar_plot # pylint: enable=E0401 -@dataclass(init=True, eq=True, repr=True) -class Wind: +@dataclass(eq=True, repr=True) +class Wind(BHoMObject): """An object containing historic, time-indexed wind data. Args: @@ -64,14 +65,22 @@ class Wind: source (str, optional): A source string to describe where the input data comes from. Defaults to None. """ - wind_speeds: list[float] wind_directions: list[float] datetimes: list[datetime] | pd.DatetimeIndex height_above_ground: float = 10.0 source: str = None - def __post_init__(self): + def __init__(self, wind_speeds: list[float], wind_directions: list[float], datetimes: list[datetime] | pd.DatetimeIndex, height_above_ground: float = 10.0, source: str = None, **kwargs): + self.wind_speeds = wind_speeds + self.wind_directions = wind_directions + self.datetimes = datetimes + self.height_above_ground = height_above_ground + self.source = source + + _t = kwargs.pop("_t", "BH.oM.LadybugTools.Wind") + super().__init__(_t, **kwargs) + if self.height_above_ground < 0.1: raise ValueError("Height above ground must be >= 0.1m.") @@ -150,16 +159,6 @@ def from_dict(cls, d: dict) -> "Wind": source=d["source"], ) - def to_json(self) -> str: - """Convert this object to a JSON string.""" - return json.dumps(self.to_dict()) - - @classmethod - def from_json(cls, json_string: str) -> "Wind": - """Create this object from a JSON string.""" - - return cls.from_dict(json.loads(json_string)) - def to_file(self, path: Path) -> Path: """Convert this object to a JSON file.""" diff --git a/LadybugTools_Engine/Python/tests/test_bhom/test_to_bhom.py b/LadybugTools_Engine/Python/tests/test_bhom/test_to_bhom.py index 9a27c06f..4465433c 100644 --- a/LadybugTools_Engine/Python/tests/test_bhom/test_to_bhom.py +++ b/LadybugTools_Engine/Python/tests/test_bhom/test_to_bhom.py @@ -47,7 +47,6 @@ visible_absorptance=0.8, ) - def test_energymaterialvegetation_to_bhom(): """_""" @@ -125,11 +124,11 @@ def test_analysisperiod_to_bhom(): assert result["_t"] == "BH.oM.LadybugTools.AnalysisPeriod" assert result["Type"] == "AnalysisPeriod" - assert result["StHour"] == 0 + assert result["StartHour"] == 0 assert result["EndHour"] == 23 - assert result["StDay"] == 1 + assert result["StartDay"] == 1 assert result["EndDay"] == 31 - assert result["StMonth"] == 1 + assert result["StartMonth"] == 1 assert result["EndMonth"] == 12 assert result["IsLeapYear"] is False assert result["Timestep"] == 1 diff --git a/LadybugTools_Engine/Python/tests/test_ladybug_extension/test_analysis_period.py b/LadybugTools_Engine/Python/tests/test_ladybug_extension/test_analysis_period.py index e6cf0468..31c0a2c9 100644 --- a/LadybugTools_Engine/Python/tests/test_ladybug_extension/test_analysis_period.py +++ b/LadybugTools_Engine/Python/tests/test_ladybug_extension/test_analysis_period.py @@ -9,7 +9,6 @@ describe_analysis_period, ) - def test_from_datetimes(): """_""" datetimes = [ diff --git a/LadybugTools_Toolkit.sln b/LadybugTools_Toolkit.sln index 2267205a..a9e3c73a 100644 --- a/LadybugTools_Toolkit.sln +++ b/LadybugTools_Toolkit.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34202.233 +# Visual Studio Version 18 +VisualStudioVersion = 18.6.11822.322 stable MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LadybugTools_oM", "LadybugTools_oM\LadybugTools_oM.csproj", "{ABC2CD49-3DCF-46C3-9249-EB09C88ECFFD}" EndProject diff --git a/LadybugTools_oM/Collections/HourlyContinuousCollection.cs b/LadybugTools_oM/Collections/HourlyContinuousCollection.cs index 01eb0ecf..489b1236 100644 --- a/LadybugTools_oM/Collections/HourlyContinuousCollection.cs +++ b/LadybugTools_oM/Collections/HourlyContinuousCollection.cs @@ -36,6 +36,8 @@ public class HourlyContinuousCollection : BHoMObject, ILadybugTools [Description("A list of values.")] public virtual List Values { get; set; } = Enumerable.Repeat(null, 8760).ToList(); + + public virtual string Type { get; set; } = "HourlyContinuousCollection"; } } diff --git a/LadybugTools_oM/Constructions/EnergyMaterial.cs b/LadybugTools_oM/Constructions/EnergyMaterial.cs index 4615f8c7..d7e5ed07 100644 --- a/LadybugTools_oM/Constructions/EnergyMaterial.cs +++ b/LadybugTools_oM/Constructions/EnergyMaterial.cs @@ -32,7 +32,7 @@ namespace BH.oM.LadybugTools public class EnergyMaterial : BHoMObject, IEnergyMaterialOpaque { [Description("The name of this EnergyMaterial.")] - public override string Name { get; set; } = string.Empty; + public virtual string Identifier { get; set; } = string.Empty; [Description("Thickness of material (m).")] [Length] @@ -63,6 +63,8 @@ public class EnergyMaterial : BHoMObject, IEnergyMaterialOpaque [DisplayText("Visible Absorptance")] [Description("Light absorptivity (1 - albedo) of material (0-1).")] public virtual double VisibleAbsorptance { get; set; } + + public virtual string Type { get; set; } = "EnergyMaterial"; } } diff --git a/LadybugTools_oM/Constructions/EnergyMaterialVegetation.cs b/LadybugTools_oM/Constructions/EnergyMaterialVegetation.cs index 360a6d85..554bb33d 100644 --- a/LadybugTools_oM/Constructions/EnergyMaterialVegetation.cs +++ b/LadybugTools_oM/Constructions/EnergyMaterialVegetation.cs @@ -32,7 +32,7 @@ namespace BH.oM.LadybugTools public class EnergyMaterialVegetation : BHoMObject, IEnergyMaterialOpaque { [Description("The name of this EnergyMaterialVegetation.")] - public override string Name { get; set; } = string.Empty; + public virtual string Identifier { get; set; } = string.Empty; [Description("Thickness of material (m).")] [Length] @@ -83,6 +83,8 @@ public class EnergyMaterialVegetation : BHoMObject, IEnergyMaterialOpaque [DisplayText("Minimum Stomatal Resistance")] [Description("A number between 50 and 300 for the resistance of the plants to moisture transport [s/m]. Plants with low values of stomatal resistance will result in higher evapotranspiration rates than plants with high resistance.")] public virtual double MinimumStomatalResistance { get; set; } + + public virtual string Type { get; set; } = "EnergyMaterialVegetation"; } } diff --git a/LadybugTools_oM/Constructions/IEnergyMaterialOpaque.cs b/LadybugTools_oM/Constructions/IEnergyMaterialOpaque.cs index 12588133..f70e6f97 100644 --- a/LadybugTools_oM/Constructions/IEnergyMaterialOpaque.cs +++ b/LadybugTools_oM/Constructions/IEnergyMaterialOpaque.cs @@ -29,6 +29,8 @@ namespace BH.oM.LadybugTools [Description("An interface for opaque energy materials.")] public interface IEnergyMaterialOpaque : ILadybugTools { + [Description("Unique identifier for this material.")] + string Identifier { get; set; } } } diff --git a/LadybugTools_oM/MetaData/AnalysisPeriod.cs b/LadybugTools_oM/MetaData/AnalysisPeriod.cs index 8811f7ed..1c53d938 100644 --- a/LadybugTools_oM/MetaData/AnalysisPeriod.cs +++ b/LadybugTools_oM/MetaData/AnalysisPeriod.cs @@ -60,7 +60,9 @@ public class AnalysisPeriod : BHoMObject, ILadybugTools [DisplayText("Time Step")] [Description("The number of timesteps per hour.")] - public virtual int TimeStep { get; set; } = 1; + public virtual int Timestep { get; set; } = 1; + + public virtual string Type { get; set; } = "AnalysisPeriod"; } } diff --git a/LadybugTools_oM/MetaData/DataType.cs b/LadybugTools_oM/MetaData/DataType.cs index 33caca39..3b3847d9 100644 --- a/LadybugTools_oM/MetaData/DataType.cs +++ b/LadybugTools_oM/MetaData/DataType.cs @@ -39,6 +39,8 @@ public class DataType : BHoMObject, ILadybugTools [DisplayText("Base Unit")] [Description(@"The base type of this data type. This is used if Data_Type is set to ""GenericDataType"".")] public virtual string BaseUnit { get; set; } = string.Empty; + + public virtual string Type { get; set; } = "DataType"; } } diff --git a/LadybugTools_oM/MetaData/Header.cs b/LadybugTools_oM/MetaData/Header.cs index ce8be7b8..25fc36b0 100644 --- a/LadybugTools_oM/MetaData/Header.cs +++ b/LadybugTools_oM/MetaData/Header.cs @@ -43,6 +43,8 @@ public class Header : BHoMObject, ILadybugTools [Description("The metadata associated with this header object.")] public virtual Dictionary Metadata { get; set; } = new Dictionary(); + + public virtual string Type { get; set; } = "Header"; } } diff --git a/LadybugTools_oM/Simulation/SimulationResult.cs b/LadybugTools_oM/Simulation/SimulationResult.cs index 9d2847b2..789a9c6f 100644 --- a/LadybugTools_oM/Simulation/SimulationResult.cs +++ b/LadybugTools_oM/Simulation/SimulationResult.cs @@ -43,7 +43,7 @@ public class SimulationResult : BHoMObject, ILadybugTools, IImmutable public virtual IEnergyMaterialOpaque ShadeMaterial { get; set; } [Description("The identifier used to distinguish existing results for this object.")] - public override string Name { get; set; } + public virtual string Identifier { get; set; } // simulated properties @@ -95,12 +95,12 @@ public class SimulationResult : BHoMObject, ILadybugTools, IImmutable [Description("The Unshaded Mean Radiant Temperature used in the processing of this object")] public virtual HourlyContinuousCollection UnshadedMeanRadiantTemperature { get; } = null; - public SimulationResult(FileSettings epwFile = null, IEnergyMaterialOpaque groundMaterial = null, IEnergyMaterialOpaque shadeMaterial = null, string name = null, HourlyContinuousCollection shadedDownTemperature = null, HourlyContinuousCollection shadedUpTemperature = null, HourlyContinuousCollection shadedRadiantTemperature = null, HourlyContinuousCollection shadedLongwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection shadedShortwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection shadedMeanRadiantTemperature = null, HourlyContinuousCollection unshadedDownTemperature = null, HourlyContinuousCollection unshadedUpTemperature = null, HourlyContinuousCollection unshadedRadiantTemperature = null, HourlyContinuousCollection unshadedLongwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection unshadedShortwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection unshadedMeanRadiantTemperature = null) + public SimulationResult(FileSettings epwFile = null, IEnergyMaterialOpaque groundMaterial = null, IEnergyMaterialOpaque shadeMaterial = null, string identifier = null, HourlyContinuousCollection shadedDownTemperature = null, HourlyContinuousCollection shadedUpTemperature = null, HourlyContinuousCollection shadedRadiantTemperature = null, HourlyContinuousCollection shadedLongwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection shadedShortwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection shadedMeanRadiantTemperature = null, HourlyContinuousCollection unshadedDownTemperature = null, HourlyContinuousCollection unshadedUpTemperature = null, HourlyContinuousCollection unshadedRadiantTemperature = null, HourlyContinuousCollection unshadedLongwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection unshadedShortwaveMeanRadiantTemperatureDelta = null, HourlyContinuousCollection unshadedMeanRadiantTemperature = null) { EpwFile = epwFile; GroundMaterial = groundMaterial; ShadeMaterial = shadeMaterial; - Name = name; + Identifier = identifier; ShadedDownTemperature = shadedDownTemperature; ShadedUpTemperature = shadedUpTemperature; ShadedRadiantTemperature = shadedRadiantTemperature; diff --git a/LadybugTools_oM/Versioning93.json b/LadybugTools_oM/Versioning93.json new file mode 100644 index 00000000..ff4ffb92 --- /dev/null +++ b/LadybugTools_oM/Versioning93.json @@ -0,0 +1,10 @@ +{ + "Property": { + "ToNew": { + "BH.oM.LadybugTools.AnalysisPeriod.TimeStep": "BH.oM.LadybugTools.AnalysisPeriod.Timestep" + }, + "ToOld": { + "BH.oM.LadybugTools.AnalysisPeriod.Timestep": "BH.oM.LadybugTools.AnalysisPeriod.TimeStep" + } + } +}