diff --git a/cs/addNodeOutputs.cs b/cs/addNodeOutputs.cs index 1dda07c..1c5d0ad 100644 --- a/cs/addNodeOutputs.cs +++ b/cs/addNodeOutputs.cs @@ -1,17 +1,26 @@ /* -Reporting all node outputs can be difficult as output files tend to be enormous due to -huge amount of data (especially for bigger models). +Targeted Node Output Variable Request Script -This script adds requested output ariables only for defined nodes: - "Supply Side Outlet Node Names" - "Plant Side Outlet Node Name" - "Condenser Side Outlet Node Name" +This DesignBuilder C# script adds Output:Variable objects for a limited set of node names to help avoid extremely large output files that can occur when reporting variables for all nodes in large models. -Default variables are: - System Node Temperature - System Node Mass Flow Rate +Purpose +The script reduces output size by requesting only selected node output variables for outlet nodes referenced by: +- AirLoopHVAC: "Supply Side Outlet Node Names" +- PlantLoop: "Plant Side Outlet Node Name" +- CondenserLoop: "Condenser Side Outlet Node Name" + +Main Steps +1) Load the EnergyPlus IDF using EpNet IdfReader. +2) Collect outlet node names from AirLoopHVAC, PlantLoop and CondenserLoop objects. + - If a node name ends with "List", it is assumed to reference a NodeList object, and the script uses the first node entry in that NodeList. +3) For each requested variable name, add Output:Variable objects for each collected node. +4) Save the modified IDF before the EnergyPlus simulation runs. -Additional outputs (below) can be added to 'variables' list: +How to Use + +Configuration +- requestedVariables: + Add/remove entries using the exact EnergyPlus variable names you want to request: System Node Temperature System Node Mass Flow Rate System Node Humidity Ratio @@ -36,48 +45,68 @@ System Node Minimum Available Mass Flow Rate System Node Maximum Available Mass Flow Rate System Node Setpoint Mass Flow Rate System Node Requested Mass Flow Rate +- reportingFrequency: + Set to a valid Output:Variable reporting frequency string. + +Prerequisites +Base model must contain the required objects/fields: +- AirLoopHVAC objects (Supply Side Outlet Node Names) +- PlantLoop objects (Plant Side Outlet Node Name) +- CondenserLoop objects (Condenser Side Outlet Node Name) +If any of these fields reference a NodeList, the NodeList object must exist in the IDF. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + // Adds Output:Variable requests for specific node variables and specific outlet nodes only. + public class AddTargetedNodeOutputVariables : ScriptBase, IScript { - private List FindNodes(IdfReader idfReader, string objectName, string fieldName) + // Collect node names from a given IDF object type and field name. + // If a value ends with "List", it is treated as a NodeList reference and the first node entry is used. + private List CollectOutletNodeNames(IdfReader idfReader, string idfObjectType, string nodeFieldName) { - List nodes = new List(); - IEnumerable idfObjects = idfReader[objectName]; + var nodeNames = new List(); + IEnumerable objectsOfType = idfReader[idfObjectType]; - foreach (IdfObject idfObject in idfObjects) + foreach (IdfObject obj in objectsOfType) { - string nodeName = idfObject[fieldName]; + string nodeOrListName = obj[nodeFieldName]; - if (nodeName.EndsWith("List")) + if (nodeOrListName.EndsWith("List")) { - IdfObject nodeList = idfReader["NodeList"].First(item => item[0] == nodeName); - nodeName = nodeList[1]; + IdfObject nodeList = idfReader["NodeList"].First(item => item[0] == nodeOrListName); + nodeOrListName = nodeList[1]; } - nodes.Add(nodeName); + + nodeNames.Add(nodeOrListName); } - return nodes; + + return nodeNames; } - private void AddOuputVariable(IdfReader idfReader, string key, string name, string frequency) + private void AddOutputVariable(IdfReader idfReader, string nodeName, string variableName, string reportingFrequency) { - string outputVariable = string.Format("Output:Variable, {0}, {1}, {2};", key, name, frequency); - idfReader.Load(outputVariable); + string outputVariableObject = string.Format( + "Output:Variable, {0}, {1}, {2};", + nodeName, + variableName, + reportingFrequency); + + idfReader.Load(outputVariableObject); } - private void AddNodeVariables(IdfReader idfReader, List nodes, string name, string frequency) + private void AddVariablesForNodes(IdfReader idfReader, List nodeNames, string variableName, string reportingFrequency) { - foreach (string node in nodes) + foreach (string nodeName in nodeNames) { - AddOuputVariable(idfReader, node, name, frequency); + AddOutputVariable(idfReader, nodeName, variableName, reportingFrequency); } } @@ -87,19 +116,29 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - List airLoopNodes = FindNodes(idfReader, "AirLoopHVAC", "Supply Side Outlet Node Names"); - List plantLoopNodes = FindNodes(idfReader, "PlantLoop", "Plant Side Outlet Node Name"); - List condenserLoopNodes = FindNodes(idfReader, "CondenserLoop", "Condenser Side Outlet Node Name"); + // Collect outlet nodes from the three loop types (Air, Plant, Condenser) + List airLoopOutletNodes = CollectOutletNodeNames(idfReader, "AirLoopHVAC", "Supply Side Outlet Node Names"); + List plantLoopOutletNodes = CollectOutletNodeNames(idfReader, "PlantLoop", "Plant Side Outlet Node Name"); + List condenserLoopOutletNodes = CollectOutletNodeNames(idfReader, "CondenserLoop", "Condenser Side Outlet Node Name"); + + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Add additional EnergyPlus Output:Variable names to the list below as required. + List requestedVariables = new List + { + "System Node Temperature", + "System Node Mass Flow Rate" + }; - // Request node variables (listed in description above) by adding them into list below - List variables = new List { "System Node Temperature", "System Node Mass Flow Rate" }; - const string frequency = "hourly"; + // Add desired reporting frequency + const string reportingFrequency = "hourly"; - foreach (string variable in variables) + foreach (string variableName in requestedVariables) { - AddNodeVariables(idfReader, plantLoopNodes, variable, frequency); - AddNodeVariables(idfReader, airLoopNodes, variable, frequency); - AddNodeVariables(idfReader, condenserLoopNodes, variable, frequency); + AddVariablesForNodes(idfReader, plantLoopOutletNodes, variableName, reportingFrequency); + AddVariablesForNodes(idfReader, airLoopOutletNodes, variableName, reportingFrequency); + AddVariablesForNodes(idfReader, condenserLoopOutletNodes, variableName, reportingFrequency); } idfReader.Save(); diff --git a/cs/addPipe.cs b/cs/addPipe.cs index 7ae41bd..30bf223 100644 --- a/cs/addPipe.cs +++ b/cs/addPipe.cs @@ -1,9 +1,46 @@ /* -This C# script is designed to enhance the functionality by providing an option to -add Pipe:Indoor and Pipe:Outdoor objects to an IDF file. +Add Pipe:Indoor and Pipe:Outdoor Objects to the EnergyPlus IDF + +Purpose + +This DesignBuilder script runs before the EnergyPlus simulation and modifies the generated IDF file by: +- Creating insulated pipe constructions (Construction + Material layers) +- Inserting Pipe:Indoor and Pipe:Outdoor objects into specified plant Branch objects +- Updating PlantLoop outlet node references to match the new pipe outlet nodes + +How to Use + +1) Open BeforeEnergySimulation() and edit the "Configuration" section: + - Define one or more pipe constructions via AddInsulatedPipeConstruction(...) + - Call AddIndoorPipeToBranch(...) and/or AddOutdoorPipeToBranch(...) for each pipe you want to add +2) Ensure the Branch names and Zone names match exactly what exists in the IDF generated by DesignBuilder. +3) Run the DesignBuilder simulation; the script will edit the IDF before EnergyPlus executes. + +Configuration Options + +- Pipe constructions: + - pipeConstructionName: name for the Construction and associated Material layers + - insulationThickness: insulation layer thickness [m] +- Indoor pipes: + - zoneName: the thermal zone used for ambient temperature (Environment Type = ZONE) +- Indoor/Outdoor pipes: + - branchName: the target Branch to modify + - pipeName: name of the pipe object that will be inserted + - pipeConstructionName: Construction name to assign + - pipeInsideDiameter: inside diameter [m] + - pipeLength: length [m] + +Prerequisites + +- The IDF must contain: + - Branch objects with names matching branchName inputs. + - PlantLoop objects (if you expect PlantLoop outlet node references to be updated). + - For Pipe:Indoor, the specified zoneName must exist (Zone object). +- NOTE: The inlet node name for the inserted pipe is taken from the last field in the Branch object (see AddIndoorPipeToBranch / AddOutdoorPipeToBranch). + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; using System; using System.Linq; using System.Windows.Forms; @@ -12,7 +49,6 @@ This C# script is designed to enhance the functionality by providing an option t using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { public class AddPipes : ScriptBase, IScript @@ -23,84 +59,99 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - // define pump constructions - string pipeConstruction1 = "pipe construction 1"; - AddInsulatedPipe(idfReader, pipeConstruction1, 0.025); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + + // Define pipe constructions (Construction + Material layers) + string indoorPipeConstructionName = "pipe construction 1"; + AddInsulatedPipeConstruction(idfReader, indoorPipeConstructionName, insulationThickness: 0.025); - string pipeConstruction2 = "pipe construction 2"; - AddInsulatedPipe(idfReader, pipeConstruction2, 0.05); + string outdoorPipeConstructionName = "pipe construction 2"; + AddInsulatedPipeConstruction(idfReader, outdoorPipeConstructionName, insulationThickness: 0.05); - // define pipes to be added - AddIndoorPipe( + // Define pipes to be added (target by Branch name) + AddIndoorPipeToBranch( reader: idfReader, zoneName: "BASEMENT:ZONE1", branchName: "HW Loop Demand Side Inlet Branch", pipeName: "pipe 1", - pipeConstructionName: pipeConstruction1, + pipeConstructionName: indoorPipeConstructionName, pipeInsideDiameter: 0.03, pipeLength: 30); - AddIndoorPipe( + AddIndoorPipeToBranch( reader: idfReader, zoneName: "BASEMENT:ZONE1", branchName: "HW Loop Demand Side Outlet Branch", pipeName: "pipe 2", - pipeConstructionName: pipeConstruction1, + pipeConstructionName: indoorPipeConstructionName, pipeInsideDiameter: 0.03, pipeLength: 30); - AddOutdoorPipe( + AddOutdoorPipeToBranch( reader: idfReader, branchName: "HW Loop Supply Side Inlet Branch", pipeName: "pipe 3", - pipeConstructionName: pipeConstruction2, + pipeConstructionName: outdoorPipeConstructionName, pipeInsideDiameter: 0.03, pipeLength: 50); - AddOutdoorPipe( + AddOutdoorPipeToBranch( reader: idfReader, branchName: "HW Loop Supply Side Outlet Branch", pipeName: "pipe 4", - pipeConstructionName: pipeConstruction2, + pipeConstructionName: outdoorPipeConstructionName, pipeInsideDiameter: 0.03, pipeLength: 50); - idfReader.Save(); } - public IdfObject FindObject(IdfReader reader, string objectType, string objectName) + private IdfObject GetRequiredObject(IdfReader reader, string objectType, string objectName) { try { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - public void UpdatePlantLoopNode(IdfReader reader, string originalOutletNodeName, string newNodeName) + // Updates PlantLoop outlet node name fields if they match a provided node name. + private void UpdatePlantLoopOutletNodeReferences(IdfReader reader, string oldOutletNodeName, string newOutletNodeName) { - string[] fieldNames = new string[] { "Plant Side Outlet Node Name", "Demand Side Outlet Node Name" }; + string[] plantLoopOutletFieldNames = new string[] + { + "Plant Side Outlet Node Name", + "Demand Side Outlet Node Name" + }; + foreach (var plantLoop in reader["PlantLoop"]) { - foreach (var fieldName in fieldNames) + foreach (var fieldName in plantLoopOutletFieldNames) { - if (string.Equals(plantLoop[fieldName].Value, originalOutletNodeName, - StringComparison.OrdinalIgnoreCase)) + if (string.Equals(plantLoop[fieldName].Value, oldOutletNodeName, StringComparison.OrdinalIgnoreCase)) { - plantLoop[fieldName].Value = newNodeName; + plantLoop[fieldName].Value = newOutletNodeName; } } } } - - public string GetIndoorPipe(string name, string inletNodeName, string outletNodeName, string pipeConstructionName, string zoneName, double pipeInsideDiameter, double pipeLength) + private string BuildIndoorPipeIdfText( + string pipeName, + string inletNodeName, + string outletNodeName, + string pipeConstructionName, + string zoneName, + double pipeInsideDiameter, + double pipeLength) { - string pipe = @" + string pipeTemplate = @" Pipe:Indoor, {0}, !- Name {1}, !- Construction Name @@ -112,12 +163,28 @@ public string GetIndoorPipe(string name, string inletNodeName, string outletNode , !- Ambient Air Velocity Schedule Name {5}, !- Pipe Inside Diameter m {6}; !- Pipe Length m"; - return String.Format(CultureInfo.InvariantCulture, pipe, name, pipeConstructionName, inletNodeName, outletNodeName, zoneName, pipeInsideDiameter, pipeLength); + + return string.Format( + CultureInfo.InvariantCulture, + pipeTemplate, + pipeName, + pipeConstructionName, + inletNodeName, + outletNodeName, + zoneName, + pipeInsideDiameter, + pipeLength); } - public string GetOutdoorPipe(string name, string inletNodeName, string outletNodeName, string pipeConstructionName, double pipeInsideDiameter, double pipeLength) + private string BuildOutdoorPipeIdfText( + string pipeName, + string inletNodeName, + string outletNodeName, + string pipeConstructionName, + double pipeInsideDiameter, + double pipeLength) { - string pipe = @" + string pipeTemplate = @" Pipe:Outdoor, {0}, !- Construction Name {1}, !- Fluid Inlet Node Name @@ -129,23 +196,48 @@ public string GetOutdoorPipe(string name, string inletNodeName, string outletNod OutdoorAir:Node, {0} Outdoor Air Node; !- Name"; - return String.Format(CultureInfo.InvariantCulture, pipe, name, pipeConstructionName, inletNodeName, outletNodeName, pipeInsideDiameter, pipeLength); - } + return string.Format( + CultureInfo.InvariantCulture, + pipeTemplate, + pipeName, + pipeConstructionName, + inletNodeName, + outletNodeName, + pipeInsideDiameter, + pipeLength); + } - public void AddIndoorPipe(IdfReader reader, string zoneName, string branchName, string pipeName, string pipeConstructionName, double pipeInsideDiameter, double pipeLength) + private void AddIndoorPipeToBranch( + IdfReader reader, + string zoneName, + string branchName, + string pipeName, + string pipeConstructionName, + double pipeInsideDiameter, + double pipeLength) { try { - IdfObject branch = FindObject(reader, "Branch", branchName); + IdfObject branch = GetRequiredObject(reader, "Branch", branchName); + + // NOTE: This assumes the last field in the Branch is the node name to use as the pipe inlet node. string inletNodeName = branch[branch.Count - 1].Value; - string outletNodeName = pipeName + " Outlet Node"; - string pipe = GetIndoorPipe(pipeName, inletNodeName, outletNodeName, pipeConstructionName, zoneName, pipeInsideDiameter, pipeLength); + string newOutletNodeName = pipeName + " Outlet Node"; + + string pipeIdfText = BuildIndoorPipeIdfText( + pipeName, + inletNodeName, + newOutletNodeName, + pipeConstructionName, + zoneName, + pipeInsideDiameter, + pipeLength); - UpdatePlantLoopNode(reader, inletNodeName, outletNodeName); + UpdatePlantLoopOutletNodeReferences(reader, inletNodeName, newOutletNodeName); - branch.AddFields("Pipe:Indoor", pipeName, inletNodeName, outletNodeName); - reader.Load(pipe); + branch.AddFields("Pipe:Indoor", pipeName, inletNodeName, newOutletNodeName); + reader.Load(pipeIdfText); } catch (Exception e) { @@ -153,19 +245,36 @@ public void AddIndoorPipe(IdfReader reader, string zoneName, string branchName, } } - public void AddOutdoorPipe(IdfReader reader, string branchName, string pipeName, string pipeConstructionName, double pipeInsideDiameter, double pipeLength) + private void AddOutdoorPipeToBranch( + IdfReader reader, + string branchName, + string pipeName, + string pipeConstructionName, + double pipeInsideDiameter, + double pipeLength) { try { - IdfObject branch = FindObject(reader, "Branch", branchName); + IdfObject branch = GetRequiredObject(reader, "Branch", branchName); + + // NOTE: This assumes the last field in the Branch is the node name to use as the pipe inlet node. string inletNodeName = branch[branch.Count - 1].Value; - string outletNodeName = pipeName + " Outlet Node"; - string pipe = GetOutdoorPipe(pipeName, inletNodeName, outletNodeName, pipeConstructionName, pipeInsideDiameter, pipeLength); - UpdatePlantLoopNode(reader, inletNodeName, outletNodeName); + string newOutletNodeName = pipeName + " Outlet Node"; + + string pipeIdfText = BuildOutdoorPipeIdfText( + pipeName, + inletNodeName, + newOutletNodeName, + pipeConstructionName, + pipeInsideDiameter, + pipeLength); + + // Redirect PlantLoop outlet node references if they match the old node + UpdatePlantLoopOutletNodeReferences(reader, inletNodeName, newOutletNodeName); - branch.AddFields("Pipe:Outdoor", pipeName, inletNodeName, outletNodeName); - reader.Load(pipe); + branch.AddFields("Pipe:Outdoor", pipeName, inletNodeName, newOutletNodeName); + reader.Load(pipeIdfText); } catch (Exception e) { @@ -173,10 +282,12 @@ public void AddOutdoorPipe(IdfReader reader, string branchName, string pipeName, } } - public void AddInsulatedPipe(IdfReader reader, string pipeConstructionName, double insulationThickness) + private void AddInsulatedPipeConstruction(IdfReader reader, string pipeConstructionName, double insulationThickness) { - string pipeInsulationName = pipeConstructionName + " insulation"; - string pipeSteelName = pipeConstructionName + " steel"; + // Construction is composed of an insulation layer + steel layer + string pipeInsulationMaterialName = pipeConstructionName + " insulation"; + string pipeSteelMaterialName = pipeConstructionName + " steel"; + string pipeConstructionTemplate = @" Construction, {0}, !-Name @@ -205,8 +316,16 @@ public void AddInsulatedPipe(IdfReader reader, string pipeConstructionName, doub 0.5, !- Solar Absorptance 0.5; !- Visible Absorptance "; - string pipeConstruction = String.Format(CultureInfo.InvariantCulture, pipeConstructionTemplate, pipeConstructionName, pipeInsulationName, pipeSteelName, insulationThickness); - reader.Load(pipeConstruction); + + string pipeConstructionIdfText = string.Format( + CultureInfo.InvariantCulture, + pipeConstructionTemplate, + pipeConstructionName, + pipeInsulationMaterialName, + pipeSteelMaterialName, + insulationThickness); + + reader.Load(pipeConstructionIdfText); } } -} +} \ No newline at end of file diff --git a/cs/addReturnRegenerationCoil.cs b/cs/addReturnRegenerationCoil.cs index 1c9040e..c0fb682 100644 --- a/cs/addReturnRegenerationCoil.cs +++ b/cs/addReturnRegenerationCoil.cs @@ -1,25 +1,53 @@ /* -Add Coil:Heating:Electric to the air loop return air stream. +Add Electric Heating Coil to an Air Loop Branch (Return Air Stream) -The script adds the component to air loops specified in "airLoopNames" array. -Coil parameters and setpoint can be set in the object boilerplate. +This DesignBuilder C# script inserts a Coil:Heating:Electric into the air loop branch for selected air loops by editing the EnergyPlus IDF before simulation. + +Purpose +- For each target air loop name: + - Find the corresponding Branch object (following naming convention " AHU Main Branch") + - Identify the existing downstream node currently referenced by the branch + - Insert a new coil component into the branch equipment list + - Add required supporting objects (coil, availability schedule, setpoint schedule, scheduled setpoint manager) + - Add a couple of Output:Variable requests + +How to Use + +Configuration +- targetAirLoopNames: + - List of air loop names to modify (exact match required). +- coilAndControlsIdfTemplate: + - IDF template injected for each air loop, including: + - Coil:Heating:Electric + - Schedule:Compact (Availability) + - Schedule:Compact (Setpoint) + - SetpointManager:Scheduled + - Output:Variable entries + +Prerequisites + +- Each target air loop must already contain a Branch object named " AHU Main Branch". + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Collections.Generic; +using System; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript + public class AddElectricHeatingCoilToAirLoopBranch : ScriptBase, IScript { - string[] airLoopNames = new string[] { "Air Loop" }; - - string coilBoilerPlate = @" + // Configure which air loops to modify (names must match the model exactly) + private readonly string[] targetAirLoopNames = new string[] { "Air Loop" }; + + // IDF template injected per air loop: + // {0} = coil name + // {1} = coil inlet node name + // {2} = coil outlet node name (also used as setpoint node) + private readonly string coilAndControlsIdfTemplate = @" Coil:Heating:Electric, {0}, !- Name {0} Availability, !- Availability Schedule Name @@ -52,41 +80,50 @@ public class IdfFindAndReplace : ScriptBase, IScript {2}; !- Setpoint Node or NodeList Name Output:Variable, {0}, Heating Coil Electricity Rate, Hourly; -Output:Variable, {2}, System Node Temperature, Hourly;"; - +Output:Variable, {2}, System Node Temperature, Hourly; +"; - private IdfObject FindObject(IdfReader idfReader, string objectType, string objectName) + // Helper: Find a single IDF object by type + name (name is typically field 0) + private IdfObject FindObjectByName(IdfReader idf, string objectType, string objectName) { - return idfReader[objectType].First(o => o[0] == objectName); + return idf[objectType].First(o => o[0] == objectName); } public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( - ApiEnvironment.EnergyPlusInputIdfPath, - ApiEnvironment.EnergyPlusInputIddPath - ); + IdfReader idf = new IdfReader( + ApiEnvironment.EnergyPlusInputIdfPath, + ApiEnvironment.EnergyPlusInputIddPath + ); - - foreach (string airLoopName in airLoopNames) + foreach (string airLoopName in targetAirLoopNames) { - string branchName = airLoopName + " AHU Main Branch"; - IdfObject branch = FindObject(idfReader, "Branch", branchName); + string mainBranchName = airLoopName + " AHU Main Branch"; + IdfObject mainBranch = FindObjectByName(idf, "Branch", mainBranchName); string coilName = airLoopName + " AHU Regeneration Coil"; - string coilInletNode = branch[4].Value; + // Assumption: branch[4] is the node currently used downstream in the branch definition. + // Preserve it as the coil inlet node, then replace branch[4] with the new coil outlet node. + string coilInletNode = mainBranch[4].Value; string coilOutletNode = airLoopName + " AHU Regeneration Coil Outlet Node"; - branch[4].Value = coilOutletNode; - - string[] newFields = new string[] { "Coil:Heating:Electric", coilName, coilInletNode, coilOutletNode }; - branch.InsertFields(2, newFields); + mainBranch[4].Value = coilOutletNode; - string coil = String.Format(coilBoilerPlate, coilName, coilInletNode, coilOutletNode); + // Insert the new coil into the branch equipment list. + string[] newBranchFields = new string[] + { + "Coil:Heating:Electric", + coilName, + coilInletNode, + coilOutletNode + }; + mainBranch.InsertFields(2, newBranchFields); - idfReader.Load(coil); + string idfTextToLoad = String.Format(coilAndControlsIdfTemplate, coilName, coilInletNode, coilOutletNode); + idf.Load(idfTextToLoad); } - idfReader.Save(); + + idf.Save(); } } } \ No newline at end of file diff --git a/cs/addReturnStreamCooler.cs b/cs/addReturnStreamCooler.cs index b178fec..e9eed36 100644 --- a/cs/addReturnStreamCooler.cs +++ b/cs/addReturnStreamCooler.cs @@ -1,25 +1,52 @@ /* -Add EvaporativeCooler:Direct:ResearchSpecial to air loop return air stream. +Add EvaporativeCooler:Direct:ResearchSpecial to an air loop branch. -The script adds the component to air loops specified in "airLoopNames" array. -Cooler parameters and setpoint can be set in the object boilerplate. +This DesignBuilder C# script modifies the IDF by inserting an EvaporativeCooler:Direct:ResearchSpecial into a specified Branch for each target air loop name. + +Purpose + +The script edits the IDF to: +- Locate the target Branch object for each air loop (by name convention) +- Break the existing node connection at a specific Branch field +- Insert the evaporative cooler (type/name/inlet/outlet) into the Branch equipment list +- Add supporting objects (availability schedule, setpoint schedule, setpoint manager, and output variables) + +How to Use + +Configuration +- airLoopNames: + Add one or more air loop base names, e.g. { "Air Loop 1", "Air Loop 2" }. +- Branch naming convention: + For each airLoopName, the script expects a Branch named " AHU Main Branch". +- Boilerplate template: + The EvaporativeCooler object name is " AHU Cooler" and related schedules/setpoint manager are created + using that name as a prefix. + +Prerequisites + +Base model must contain a Branch object named " AHU Main Branch" for each entry in airLoopNames. +These act as placeholders that allow the script to identify where to insert the new component. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Collections.Generic; +using System; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript - { - string[] airLoopNames = new string[] { "Air Loop" }; - - string coolerBoilerPlate = @" + public class IdfFindAndReplace : ScriptBase, IScript + { + // -------------------- + // USER CONFIGURATION + // -------------------- + // List of target air loop base names to modify. + private readonly string[] targetAirLoopNames = new string[] { "Air Loop" }; + + // IDF template that will be injected into the model for each target air loop. + private readonly string coolerBoilerplateIdf = @" EvaporativeCooler:Direct:ResearchSpecial, {0}, !- Name {0} Availability, !- Availability Schedule Name @@ -64,39 +91,51 @@ public class IdfFindAndReplace : ScriptBase, IScript Output:Variable, {2}, System Node Temperature, Hourly;"; - - private IdfObject FindObject(IdfReader idfReader, string objectType, string objectName) - { - return idfReader[objectType].First(o => o[0] == objectName); - } - - public override void BeforeEnergySimulation() - { - IdfReader idfReader = new IdfReader( - ApiEnvironment.EnergyPlusInputIdfPath, - ApiEnvironment.EnergyPlusInputIddPath - ); - - - foreach (string airLoopName in airLoopNames) - { - string branchName = airLoopName + " AHU Main Branch"; - IdfObject branch = FindObject(idfReader, "Branch", branchName); - - string coolerName = airLoopName + " AHU Cooler"; - string coolerInletNode = branch[4].Value; - string coolerOutletNode = airLoopName + " AHU Cooler Outlet Node"; - - branch[4].Value = coolerOutletNode; - - string[] newFields = new string[] { "EvaporativeCooler:Direct:ResearchSpecial", coolerName, coolerInletNode, coolerOutletNode }; - branch.InsertFields(2, newFields); - - string cooler = String.Format(coolerBoilerPlate, coolerName, coolerInletNode, coolerOutletNode); - - idfReader.Load(cooler); - } - idfReader.Save(); + private IdfObject FindObject(IdfReader reader, string objectType, string objectName) + { + return reader[objectType].First(o => o[0] == objectName); + } + + public override void BeforeEnergySimulation() + { + IdfReader reader = new IdfReader( + ApiEnvironment.EnergyPlusInputIdfPath, + ApiEnvironment.EnergyPlusInputIddPath + ); + + foreach (string airLoopName in targetAirLoopNames) + { + // Expected Branch naming convention in the IDF (must match exactly). + string branchName = airLoopName + " AHU Main Branch"; + IdfObject branch = FindObject(reader, "Branch", branchName); + + // New evaporative cooler object name for this air loop. + string coolerName = airLoopName + " AHU Cooler"; + + // The script assumes branch[4] is the node currently connected where you want to insert the cooler. + string coolerInletNode = branch[4].Value; + + // Create a new downstream node name to become the branch node after inserting the cooler. + string coolerOutletNode = airLoopName + " AHU Cooler Outlet Node"; + + // Rewire the branch so the cooler outlet becomes the node used at this position. + branch[4].Value = coolerOutletNode; + + // Insert the cooler into the Branch equipment list + string[] coolerBranchFields = new string[] + { + "EvaporativeCooler:Direct:ResearchSpecial", + coolerName, + coolerInletNode, + coolerOutletNode + }; + branch.InsertFields(2, coolerBranchFields); + + string coolerObjectsIdfText = String.Format(coolerBoilerplateIdf, coolerName, coolerInletNode, coolerOutletNode); + reader.Load(coolerObjectsIdfText); + } + + reader.Save(); + } } - } } \ No newline at end of file diff --git a/cs/addSolarTranspiredCollector.cs b/cs/addSolarTranspiredCollector.cs index 0a95f28..e6602cc 100644 --- a/cs/addSolarTranspiredCollector.cs +++ b/cs/addSolarTranspiredCollector.cs @@ -1,26 +1,30 @@ /* - Description: - Adds a SolarCollector:UnglazedTranspired object to the EnergyPlus input file. - Integrates the solar collector into specified air handling units (AHUs) by updating the outdoor air system equipment list and building surface boundary conditions. - Automatically configures output variables for solar collector performance reporting. - - Workflow (BeforeEnergySimulation): - 1. Initializes the IDF reader with EnergyPlus input and data dictionary files. - 2. Adds a free heating setpoint schedule (user can specify schedule name and setpoint value). - 3. Creates a SolarCollectorProperties object with user-defined parameters: - - Collector name - - Availability schedule name - - Free heating setpoint schedule name - - List of building surface names - - (Optional physical properties can be customized) - 4. Defines air loop to control zone mappings via AirloopControlZone array (user specifies air loop and zone names). - 5. Integrates the solar collector into each air loop: - - Updates the outdoor air system equipment list to include the collector. - - Updates building surface boundary conditions to reference the collector's conditions model. - 6. Adds output variables for solar collector performance reporting. - 7. Saves all changes to the EnergyPlus input file. - - Expected User Inputs: +Unglazed Transpired Solar Collector (UTSC) Integration Script + +This DesignBuilder C# script adds a SolarCollector:UnglazedTranspired object to the EnergyPlus input file. +It integrates the solar collector into specified air handling units (AHUs) by updating the outdoor air system equipment list and building surface boundary conditions. +Output variables are automatically configured for solar collector performance reporting. + +How to Use / Workflow (BeforeEnergySimulation) + +1. Initializes the IDF reader with EnergyPlus input and data dictionary files. +2. Adds a free heating setpoint schedule (user can specify schedule name and setpoint value). +3. Creates a SolarCollectorProperties object with user-defined parameters: + - Collector name + - Availability schedule name + - Free heating setpoint schedule name + - List of building surface names + - (Optional physical properties can be customized) +4. Defines air loop to control zone mappings via AirloopControlZone array (user specifies air loop and zone names). +5. Integrates the solar collector into each air loop: + - Updates the outdoor air system equipment list to include the collector. + - Updates building surface boundary conditions to reference the collector's conditions model. +6. Adds output variables for solar collector performance reporting. +7. Saves all changes to the EnergyPlus input file. + + +Prerequisites (expected User Inputs) + - Solar collector name - Availability schedule name - Free heating setpoint schedule name @@ -28,57 +32,67 @@ 7. Saves all changes to the EnergyPlus input file. - Air loop and control zone names - (Optional) Physical properties for the collector - DesignBuilder ssage: - - Create a new C# script - - Copy the content to the editor - - Update air loop + control zone pairs and surface references - - Enable the script and run a simulation +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; + +using System; using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; using System.Globalization; +using System.Linq; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript + // Adds and connects a SolarCollector:UnglazedTranspired to one or more outdoor air systems + public class AddUnglazedTranspiredCollector : ScriptBase, IScript { - private IdfReader Reader; + private IdfReader idf; public override void BeforeEnergySimulation() { - Reader = new IdfReader( + idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + + // Free heating schedule string freeHeatingScheduleName = "Free Heating Setpoint Schedule"; AddFreeHeatingSetpointSchedule(freeHeatingScheduleName, 20.0); + // UTSC definition + target surfaces that will reference the UTSC boundary conditions model SolarCollectorProperties collectorProperties = new SolarCollectorProperties( - "Unglazed Transpired Collector 1", - "On 24/7", - freeHeatingScheduleName, - new List { "BLOCK1:ZONE1_Wall_5_0_0", "BLOCK2:ZONE1_Wall_5_0_0" }); + name: "Unglazed Transpired Collector 1", + availabilityScheduleName: "On 24/7", + setpointScheduleName: freeHeatingScheduleName, + surfaceNames: new List + { + "BLOCK1:ZONE1_Wall_5_0_0", + "BLOCK2:ZONE1_Wall_5_0_0" + }); - AirloopControlZone[] airLoopControlZones = new AirloopControlZone[] + // - AirLoop is used to find OA controller and OA equipment list via naming conventions + // - ControlZone is used to derive the zone node name " Zone Air Node" + AirLoopControlZone[] airLoopControlZones = new AirLoopControlZone[] { - new AirloopControlZone { AirLoop = "Air Loop", ControlZone = "Block1:Zone1" }, - new AirloopControlZone { AirLoop = "Air Loop 1", ControlZone = "Block2:Zone1" } + new AirLoopControlZone { AirLoop = "Air Loop", ControlZone = "Block1:Zone1" }, + new AirLoopControlZone { AirLoop = "Air Loop 1", ControlZone = "Block2:Zone1" } }; - AddUnglazedTranspiredMultisystem(collectorProperties, airLoopControlZones); + AddUnglazedTranspiredCollectorToMultipleAirLoops(collectorProperties, airLoopControlZones); AddSolarCollectorOutputs(); - Reader.Save(); + idf.Save(); } - public struct AirloopControlZone + public struct AirLoopControlZone { + // Name of the AirLoop (used to build object names via prerequisites naming convention) public string AirLoop { get; set; } + + // Zone name used to build the zone node " Zone Air Node" public string ControlZone { get; set; } } @@ -105,8 +119,8 @@ public class SolarCollectorProperties public double EffectivePlenumGapThickness { get; set; } public double EffectivePlenumCrossSectionArea { get; set; } - string HoleLayoutPattern { get; set; } - string HeatExchangerEffectivenessCorrelation { get; set; } + public string HoleLayoutPattern { get; set; } + public string HeatExchangerEffectivenessCorrelation { get; set; } public double RatioOfActualCollectorAreaToGrossArea { get; set; } public string Roughness { get; set; } @@ -114,9 +128,11 @@ public class SolarCollectorProperties public double WindPerforationEffectiveness { get; set; } public double DischargeCoefficient { get; set; } + public List SurfaceNames { get; set; } - public SolarCollectorProperties(string name, + public SolarCollectorProperties( + string name, string availabilityScheduleName, string setpointScheduleName, List surfaceNames, @@ -166,12 +182,13 @@ public SolarCollectorProperties(string name, public string GetIdfString() { + // Node connections are provided via SolarCollector:UnglazedTranspired:Multisystem. string solarCollectorTemplate = @" SurfaceProperty:OtherSideConditionsModel, {0} Conditions Model, !- Name GapConvectionRadiation; !- Type of Modeling - SolarCollector:UnglazedTranspired, +SolarCollector:UnglazedTranspired, {0}, !- Name {0} Conditions Model, !- Boundary Conditions Model Name {1}, !- Availability Schedule Name @@ -199,31 +216,34 @@ public string GetIdfString() { bool isLast = (i == SurfaceNames.Count - 1); string terminator = isLast ? ";" : ","; - solarCollectorTemplate += string.Format(CultureInfo.InvariantCulture, + solarCollectorTemplate += string.Format( + CultureInfo.InvariantCulture, ",\n {0}{1} !- Surface {2} Name", SurfaceNames[i], terminator, i + 1); } - return string.Format(CultureInfo.InvariantCulture, solarCollectorTemplate, - Name, // {0} - AvailabilityScheduleName, // {1} - FreeHeatingSetpointScheduleName, // {2} - DiameterOfPerforations.ToString("F3", CultureInfo.InvariantCulture), // {3} - DistanceBetweenPerforations.ToString("F3", CultureInfo.InvariantCulture), // {4} - ThermalEmissivity.ToString("F3", CultureInfo.InvariantCulture), // {5} - SolarAbsorptivity.ToString("F3", CultureInfo.InvariantCulture), // {6} - EffectiveOverallHeight.ToString("F3", CultureInfo.InvariantCulture), // {7} - EffectivePlenumGapThickness.ToString("F3", CultureInfo.InvariantCulture), // {8} + return string.Format( + CultureInfo.InvariantCulture, + solarCollectorTemplate, + Name, // {0} + AvailabilityScheduleName, // {1} + FreeHeatingSetpointScheduleName, // {2} + DiameterOfPerforations.ToString("F3", CultureInfo.InvariantCulture), // {3} + DistanceBetweenPerforations.ToString("F3", CultureInfo.InvariantCulture), // {4} + ThermalEmissivity.ToString("F3", CultureInfo.InvariantCulture), // {5} + SolarAbsorptivity.ToString("F3", CultureInfo.InvariantCulture), // {6} + EffectiveOverallHeight.ToString("F3", CultureInfo.InvariantCulture), // {7} + EffectivePlenumGapThickness.ToString("F3", CultureInfo.InvariantCulture), // {8} EffectivePlenumCrossSectionArea.ToString("F3", CultureInfo.InvariantCulture), // {9} - HoleLayoutPattern, // {10} - HeatExchangerEffectivenessCorrelation, // {11} + HoleLayoutPattern, // {10} + HeatExchangerEffectivenessCorrelation, // {11} RatioOfActualCollectorAreaToGrossArea.ToString("F3", CultureInfo.InvariantCulture), // {12} - Roughness, // {13} - Thickness.ToString("F3", CultureInfo.InvariantCulture), // {14} - WindPerforationEffectiveness.ToString("F3", CultureInfo.InvariantCulture), // {15} - DischargeCoefficient.ToString("F3", CultureInfo.InvariantCulture) // {16} + Roughness, // {13} + Thickness.ToString("F3", CultureInfo.InvariantCulture), // {14} + WindPerforationEffectiveness.ToString("F3", CultureInfo.InvariantCulture), // {15} + DischargeCoefficient.ToString("F3", CultureInfo.InvariantCulture) // {16} ); } } @@ -231,7 +251,7 @@ public string GetIdfString() public class SolarCollectorMultisystem { public string SolarCollectorName { get; set; } - public List NodeSpecification = new List(); + public List NodeSpecifications = new List(); public SolarCollectorMultisystem(string solarCollectorName) { @@ -250,12 +270,14 @@ public string GetIdfString() {2}, !- Outdoor Air System {5} Mixed Air Node {3}{4} !- Outdoor Air System {5} Zone Node" + Environment.NewLine; - for (int i = 0; i < NodeSpecification.Count; i++) + for (int i = 0; i < NodeSpecifications.Count; i++) { - bool isLast = (i == NodeSpecification.Count - 1); + bool isLast = (i == NodeSpecifications.Count - 1); string terminator = isLast ? ";" : ","; - MultisystemNodeSpecification specification = NodeSpecification[i]; - template += string.Format(nodesTemplate, + MultisystemNodeSpecification specification = NodeSpecifications[i]; + + template += string.Format( + nodesTemplate, specification.InletNode, specification.OutletNode, specification.MixedNode, @@ -270,6 +292,7 @@ public string GetIdfString() public void AddFreeHeatingSetpointSchedule(string name, double setpoint) { + // Simple constant schedule used by the UTSC “Free Heating Setpoint Schedule Name” field string template = @" Schedule:Compact, {0}, !- Name @@ -277,30 +300,38 @@ public void AddFreeHeatingSetpointSchedule(string name, double setpoint) Through: 12/31, !- Field 1 For: AllDays, !- Field 2 Until: 24:00,{1}; !- Field 3"; - Reader.Load(string.Format(CultureInfo.InvariantCulture, template, name, setpoint)); + + idf.Load(string.Format(CultureInfo.InvariantCulture, template, name, setpoint)); } - public void AddUnglazedTranspiredMultisystem(SolarCollectorProperties collectorProperties, AirloopControlZone[] airloopControlZones) + public void AddUnglazedTranspiredCollectorToMultipleAirLoops( + SolarCollectorProperties collectorProperties, + AirLoopControlZone[] airLoopControlZones) { SolarCollectorMultisystem multisystem = new SolarCollectorMultisystem(collectorProperties.Name); - foreach (var airloopControlZone in airloopControlZones) + + foreach (var airLoopControlZone in airLoopControlZones) { - string airLoopName = airloopControlZone.AirLoop; - string controlZoneNode = airloopControlZone.ControlZone; + string airLoopName = airLoopControlZone.AirLoop; + string controlZoneName = airLoopControlZone.ControlZone; + + MultisystemNodeSpecification nodeSpec = + AddTranspiredCollectorToAirLoop(collectorProperties.Name, airLoopName, controlZoneName); - MultisystemNodeSpecification nodeSpec = AddTranspiredCollectorToAirLoop(collectorProperties.Name, airLoopName, controlZoneNode); - multisystem.NodeSpecification.Add(nodeSpec); + multisystem.NodeSpecifications.Add(nodeSpec); } UpdateBuildingSurfaceConditions(collectorProperties.SurfaceNames, collectorProperties.Name); - Reader.Load(collectorProperties.GetIdfString()); - Reader.Load(multisystem.GetIdfString()); + idf.Load(collectorProperties.GetIdfString()); + idf.Load(multisystem.GetIdfString()); } - public void UpdateBuildingSurfaceConditions(List surfaceNames, string collectorName) + public void UpdateBuildingSurfaceConditions(List surfaceNames, string collectorName) { + // Link each surface to the UTSC-generated OtherSideConditionsModel string conditionsModelName = collectorName + " Conditions Model"; + foreach (string surfaceName in surfaceNames) { IdfObject surface = FindObject("BuildingSurface:Detailed", surfaceName); @@ -309,8 +340,12 @@ public void UpdateBuildingSurfaceConditions(List surfaceNames, string co } } - public MultisystemNodeSpecification AddTranspiredCollectorToAirLoop(string solarCollectorName, string airLoopName, string controlZoneName) + public MultisystemNodeSpecification AddTranspiredCollectorToAirLoop( + string solarCollectorName, + string airLoopName, + string controlZoneName) { + // Naming convention prerequisite: OA controller must match this exact name string oaControllerName = airLoopName + " AHU Outdoor Air Controller"; IdfObject oaController = FindObject("Controller:OutdoorAir", oaControllerName); @@ -327,9 +362,9 @@ public MultisystemNodeSpecification AddTranspiredCollectorToAirLoop(string solar ZoneNode = zoneNode }; - string oaEquipmentName = airLoopName + " AHU Outdoor air Equipment List"; - - IdfObject oaEquipmentList = FindObject("AirLoopHVAC:OutdoorAirSystem:EquipmentList", oaEquipmentName); + // Naming convention prerequisite: OA equipment list must match this exact name + string oaEquipmentListName = airLoopName + " AHU Outdoor air Equipment List"; + IdfObject oaEquipmentList = FindObject("AirLoopHVAC:OutdoorAirSystem:EquipmentList", oaEquipmentListName); string firstComponentType = oaEquipmentList["Component 1 Object Type"].Value; string firstComponentName = oaEquipmentList["Component 1 Name"].Value; @@ -355,7 +390,7 @@ public MultisystemNodeSpecification AddTranspiredCollectorToAirLoop(string solar public void AddSolarCollectorOutputs() { - string variables = @" + string outputVariablesIdf = @" Output:Variable,*,Solar Collector Heat Exchanger Effectiveness,hourly; !- HVAC Average [] Output:Variable,*,Solar Collector Leaving Air Temperature,hourly; !- HVAC Average [C] Output:Variable,*,Solar Collector Outside Face Suction Velocity,hourly; !- HVAC Average [m/s] @@ -370,18 +405,19 @@ public void AddSolarCollectorOutputs() Output:Variable,*,Solar Collector Incident Solar Radiation,hourly; !- HVAC Average [W/m2] Output:Variable,*,Solar Collector System Efficiency,hourly; !- HVAC Average [] Output:Variable,*,Solar Collector Surface Efficiency,hourly; !- HVAC Average []"; - Reader.Load(variables); + + idf.Load(outputVariablesIdf); } public IdfObject FindObject(string objectType, string objectName) { try { - return Reader[objectType].First(c => c[0] == objectName); + return idf[objectType].First(c => c[0] == objectName); } catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } } diff --git a/cs/addSwimmingPool.cs b/cs/addSwimmingPool.cs index beb0ef1..19a0f10 100644 --- a/cs/addSwimmingPool.cs +++ b/cs/addSwimmingPool.cs @@ -1,14 +1,44 @@ /* -Add the "SwimmingPool:Indoor" object into the idf file and connect its nodes to the specified HW Loop. +Indoor Swimming Pool (EnergyPlus) – IDF Injection + HW Loop Demand Connection Script - */ +This DesignBuilder C# script adds an EnergyPlus `SwimmingPool:Indoor` object to the exported IDF and connects it to a specified Hot Water (HW) plant loop demand side. +It runs automatically before the EnergyPlus simulation starts and modifies the IDF in-place. + +Purpose + +1) Load the exported EnergyPlus IDF/IDD +2) Create a new demand-side `Branch` containing the `SwimmingPool:Indoor` component and its inlet/outlet nodes. +3) Insert that branch into the target HW loop demand side. +4) Load shared schedules used by the pool object (activity, cover, make-up water, occupancy, setpoint). +5) Add Output:Variable requests relevant to Indoor Swimming Pool reporting. +6) Save the modified IDF. + +How to Use +- Edit the `AddSwimmingPool(...)` call(s) inside `BeforeEnergySimulation()`. + +Configuration (parameters) + +- swimmingPoolName: Unique pool name used for the SwimmingPool:Indoor object and to generate node names. +- floorSurfaceName: Name of the floor surface that represents the pool surface (must match an existing Surface name in the IDF). +- hwLoopName: Base name of the HW PlantLoop used to locate demand-side objects. +- averageDepth: Pool average depth in meters. +- setpointScheduleName: Name of a temperature schedule (must exist or be created by this script). + +Prerequisites (required placeholder objects) + +The base model / exported IDF must already contain: +- A How Water Loop (referenced as "HW Loop" in the example case). +- A surface with name exactly matching your model's surface in which the pool will be located + (referenced as "BLOCK1:SWIMMINGPOOL_GroundFloor_6_0_0" in the example case). + NOTE: The surface name can be found Miscellaneous model data tab under the Name in last EnergyPlus calculation. +- A setpoint schedule for the pool water temperature (referenced as "PoolSetpointTempSched1" in the example case) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. +*/ -using System.Runtime; using System; -using System.Collections.Generic; using System.Linq; using DB.Extensibility.Contracts; - using EpNet; namespace DB.Extensibility.Scripts @@ -23,9 +53,10 @@ public IdfObject FindObject(string objectType, string objectName) { return Reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } @@ -36,7 +67,17 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIddPath ); - AddSwimmingPool("Pool 1", "BLOCK1:SWIMMINGPOOL_GroundFloor_6_0_0", "HW Loop", 1.5, "PoolSetpointTempSched1"); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Add one or more pools by calling AddSwimmingPool() with the required names and parameters. + AddSwimmingPool( + "Pool 1", // Pool object name + "BLOCK1:SWIMMINGPOOL_GroundFloor_6_0_0", // Surface name (as defined in the model) + "HW Loop", // Hot Water loop name (as defined in the model) + 1.5, // Pool average depth, in meters + "PoolSetpointTempSched1" // Pool water temperature schedule (as defined in the model or included in the script) + ); LoadSharedSchedules(); LoadSwimmingPoolOutputs(); @@ -47,23 +88,29 @@ public override void BeforeEnergySimulation() private void AddSwimmingPool(string swimmingPoolName, string floorSurfaceName, string hwLoopName, double averageDepth, string setpointScheduleName) { string branchName = swimmingPoolName + " Branch"; + + // Create the Branch + SwimmingPool:Indoor IDF text and load it into the model. string swimmingPoolObjects = GetSwimmingPoolContent(swimmingPoolName, branchName, floorSurfaceName, averageDepth, setpointScheduleName); Reader.Load(swimmingPoolObjects); + // Connect the new branch into the HW PlantLoop demand BranchList / Splitter / Mixer. ConnectSwimmingPoolBranches(branchName, hwLoopName); } + // Inserts the pool branch into the HW loop demand-side BranchList and the corresponding splitter/mixer. private void ConnectSwimmingPoolBranches(string branchName, string hwLoopName) { IdfObject hwBranchList = FindObject("BranchList", hwLoopName + " Demand Side Branches"); IdfObject splitter = FindObject("Connector:Splitter", hwLoopName + " Demand Splitter"); IdfObject mixer = FindObject("Connector:Mixer", hwLoopName + " Demand Mixer"); + // Insert the new demand branch name into the demand BranchList and connector nodes list. hwBranchList.InsertField(hwBranchList.Count - 2, branchName); splitter.InsertField(splitter.Count - 2, branchName); mixer.InsertField(mixer.Count - 2, branchName); } + // Returns the IDF text for yhe SwimmingPool:Indoor and the respective demand-side branch. private string GetSwimmingPoolContent(string swimmingPoolName, string branchName, string floorSurfaceName, double poolDepth, string setpointScheduleName) { string template = @" @@ -95,14 +142,18 @@ private string GetSwimmingPoolContent(string swimmingPoolName, string branchName PoolOccupancySched, !- People Schedule PoolOccHeatGainSched; !- People Heat Gain Schedule"; - return String.Format(template, + return String.Format( + template, swimmingPoolName, floorSurfaceName, branchName, poolDepth.ToString(System.Globalization.CultureInfo.InvariantCulture), - setpointScheduleName); + setpointScheduleName + ); } + // Loads schedules referenced by the SwimmingPool:Indoor object(s) created by this script. + // NOTE: If you change schedule names in GetSwimmingPoolContent(), update them here as well. private void LoadSharedSchedules() { string template = @" @@ -173,6 +224,7 @@ private void LoadSharedSchedules() Reader.Load(template); } + // Requests common output variables for Indoor Swimming Pool reporting at hourly frequency. private void LoadSwimmingPoolOutputs() { string template = @" diff --git a/cs/additionalCoolingOUtputs.cs b/cs/additionalCoolingOUtputs.cs index 4e23c1f..772adc5 100644 --- a/cs/additionalCoolingOUtputs.cs +++ b/cs/additionalCoolingOUtputs.cs @@ -1,21 +1,32 @@ /* - Adds additional EnergyPlus output variables to the cooling - simulation IDF. Actions performed by the script: - - Loads an Output:Variable for "People Latent Gain Rate" at Timestep - reporting frequency. - - Saves the modified IDF before the cooling simulation runs. - Usage: - - Attach this script to run before the cooling simulation. +Adds additional EnergyPlus output variables to the cooling simulation IDF + +This DesignBuilder C# script adds an EnergyPlus Output:Variable request to the cooling simulation IDF. + +Purpose +- Open the EnergyPlus input IDF used for the cooling simulation with the BeforeCoolingSimulation() hookpoint +- Insert an Output:Variable object requesting variables at a given frequency +- Save the modified IDF so EnergyPlus uses the added output request during the cooling simulation + +How to Use + +Configuration + +- Add an Output:Variable definition in this order: + - Object Name: Output:Variable + - Key Value: The specific instance name (e.g., Zone Name, Component Name). Use "*" to request the variable for all applicable keys. + - Variable Name: The exact name of the EnergyPlus output requested, as found in the .rdd file (e.g., "People Latent Gain Rate"). + - Reporting Frequency: the desired reporting interval supported by EnergyPlus ("Timestep", "Hourly", "Daily", "Monthly", "RunPeriod", "Annual") + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; -using System.Collections.Generic; using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + public class AddCoolingPeopleLatentGainOutput : ScriptBase, IScript { public override void BeforeCoolingSimulation() { @@ -23,7 +34,12 @@ public override void BeforeCoolingSimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Add Output:Variable requests idfReader.Load("Output:Variable, *, People Latent Gain Rate, Timestep;"); + idfReader.Save(); } } diff --git a/cs/ahuNightVentilationManager.cs b/cs/ahuNightVentilationManager.cs index 40bb26d..b96d0f9 100644 --- a/cs/ahuNightVentilationManager.cs +++ b/cs/ahuNightVentilationManager.cs @@ -1,10 +1,33 @@ /* -This script applies the "AvailabilityManager:NightVentilation" control to specified air loops. +Night Ventilation Availability Manager Assignment Script +This DesignBuilder C# script adds an EnergyPlus AvailabilityManager:NightVentilation controller to one or more specified air loops. + +Purpose + +The script executes before the EnergyPlus simulation starts and modifies the IDF by: + - Locating each target air loop’s supply fan (via the air loop main Branch object) + - Extracting the fan Availability Schedule Name + - Creating an AvailabilityManager:NightVentilation object using that fan schedule and a user-specified control zone + - Assigning the new AvailabilityManager to the air loop’s AvailabilityManagerAssignmentList + - Adding shared schedules used by the night ventilation manager (Applicability + Setpoint) + +How to Use + +Configuration + +- Target selection: air loops are selected by the dictionary keys in loopControlZonePairs. +- Control zone: set per air loop using the dictionary values. + +Prerequisites (Required Placeholder Objects / Naming Assumptions) + +- One or more Air Loops must be included in the base model. +- The air lopps must have supply fans which support the night ventilation control (Fan:VariableVolume, Fan:ConstantVolume, Fan:OnOff) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System; -using System.Runtime; using System.Collections.Generic; using System.Linq; using System.Text; @@ -16,7 +39,8 @@ namespace DB.Extensibility.Scripts public class IdfFindAndReplace : ScriptBase, IScript { // Define IDF templates - readonly string nightVentilationTemplate = @" + // Parameterized fields: {0} = manager name, {1} = fan availability schedule name, {2} = control zone name + private readonly string nightVentilationTemplate = @" AvailabilityManager:NightVentilation, {0}, !- Name Night Ventilation Applicability Schedule, !- Applicability Schedule Name @@ -27,7 +51,8 @@ public class IdfFindAndReplace : ScriptBase, IScript 0.5, !- Night Venting Flow Fraction {2}; !- Control Zone Name"; - readonly string commonObjects = @" + // Shared schedules used by AvailabilityManager:NightVentilation. + private readonly string sharedSchedulesIdf = @" Schedule:Compact, Night Ventilation Setpoint Schedule, !- Name Any Number, !- Schedule Type Limits Name @@ -50,83 +75,86 @@ private IdfObject FindObject(IdfReader reader, string objectType, string objectN { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } private string FindFanScheduleName(IdfReader reader, string airLoopName) { IdfObject mainBranch = FindObject(reader, "Branch", airLoopName + " AHU Main Branch"); - string[] fanTypes = { "Fan:VariableVolume", "Fan:ConstantVolume", "Fan:OnOff" }; - var result = mainBranch + // Supported fan types to search for within the Branch equipment list fields + string[] fanObjectTypes = { "Fan:VariableVolume", "Fan:ConstantVolume", "Fan:OnOff" }; + var fanRefField = mainBranch .Select((field, idx) => new { field, idx }) - .FirstOrDefault(x => fanTypes.Contains(x.field.Value)); - if (result == null) + .FirstOrDefault(x => fanObjectTypes.Contains(x.field.Value)); + + if (fanRefField == null) { throw new Exception("Cannot find fan in air loop: " + airLoopName); } - int fanTypeIndex = result.idx; - string fanType = mainBranch[fanTypeIndex].Value; - string fanName = mainBranch[fanTypeIndex + 1].Value; - IdfObject fan = FindObject(reader, fanType, fanName); + int fanTypeFieldIndex = fanRefField.idx; + string fanObjectType = mainBranch[fanTypeFieldIndex].Value; + string fanObjectName = mainBranch[fanTypeFieldIndex + 1].Value; + IdfObject fan = FindObject(reader, fanObjectType, fanObjectName); return fan[1].Value; } - private void UpdateAssignmentList(IdfReader reader, string airLoopName, string managerCls, string managerName) + private void UpdateAssignmentList(IdfReader reader, string airLoopName, string managerObjectType, string managerInstanceName) { - string availabilityAssignmentListCls = "AvailabilityManagerAssignmentList"; - string availabilityAssignmentListName = airLoopName + " AvailabilityManager List"; + string assignmentListObjectType = "AvailabilityManagerAssignmentList"; + string assignmentListName = airLoopName + " AvailabilityManager List"; - IdfObject availabilityAssignmentList = FindObject(reader, availabilityAssignmentListCls, availabilityAssignmentListName); - if (availabilityAssignmentList[1] == "AvailabilityManager:Scheduled") + IdfObject assignmentList = FindObject(reader, assignmentListObjectType, assignmentListName); + if (assignmentList[1] == "AvailabilityManager:Scheduled") { - availabilityAssignmentList[1].Value = managerCls; - availabilityAssignmentList[2].Value = managerName; + assignmentList[1].Value = managerObjectType; + assignmentList[2].Value = managerInstanceName; } else { - availabilityAssignmentList.AddFields(managerCls, managerName); + assignmentList.AddFields(managerObjectType, managerInstanceName); } } public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - // Define key (air loop name) value (control zone name) pairs - // Check IDF to get exact names + // Map: Define key (air loop name) value (control zone name) pairs (check IDF to get exact names) var loopControlZonePairs = new Dictionary - { - { "Air Loop", "Block1:Zone1" }, - // { "Air Loop 1", "Block1:Zone2" } - }; + { + { "Air Loop", "Block1:Zone1" }, + // { "Air Loop 1", "Block1:Zone2" } + }; - StringBuilder idfContent = new StringBuilder(); + StringBuilder idfToAdd = new StringBuilder(); foreach (var pair in loopControlZonePairs) { string airLoopName = pair.Key; string controlZoneName = pair.Value; - string nightVentilationCls = "AvailabilityManager:NightVentilation"; - string nightVentilationName = airLoopName + " Night Ventilation"; + string nightVentManagerObjectType = "AvailabilityManager:NightVentilation"; + string nightVentManagerName = airLoopName + " Night Ventilation"; + + // Determine the fan availability schedule by locating the supply fan on the air loop main branch + string fanScheduleName = FindFanScheduleName(idf, airLoopName); - string fanScheduleName = FindFanScheduleName(idfReader, airLoopName); - idfContent.AppendFormat(nightVentilationTemplate, nightVentilationName, fanScheduleName, controlZoneName); - idfContent.Append(Environment.NewLine); + idfToAdd.AppendFormat(nightVentilationTemplate, nightVentManagerName, fanScheduleName, controlZoneName); + idfToAdd.Append(Environment.NewLine); - UpdateAssignmentList(idfReader, airLoopName, nightVentilationCls, nightVentilationName); + UpdateAssignmentList(idf, airLoopName, nightVentManagerObjectType, nightVentManagerName); } - idfContent.Append(commonObjects); + idfToAdd.Append(sharedSchedulesIdf); - idfReader.Load(idfContent.ToString()); - idfReader.Save(); + idf.Load(idfToAdd.ToString()); + idf.Save(); } } } \ No newline at end of file diff --git a/cs/applyDesiccantDehumidifier.cs b/cs/applyDesiccantDehumidifier.cs index 4509652..9575633 100644 --- a/cs/applyDesiccantDehumidifier.cs +++ b/cs/applyDesiccantDehumidifier.cs @@ -1,120 +1,146 @@ /* -Add Dehumidifier:Desiccant:NoFans object into air handling unit. +Add Desiccant Dehumidifier to an Air Handling Unit (AHU) Supply Path -The AddDesiccantDehumidifier method requires the following parameters: - - air loop name - - position (component position in the supply path) - - regeneration coil type (Water, Fuel) - - hw loop name (if using water coil) +This DesignBuilder C# script inserts an EnergyPlus Dehumidifier:Desiccant:NoFans component into an existing air loop supply branch +at a specified position, then rewires the downstream component inlet node so airflow passes through the dehumidifier. +Purpose + +1) Locate the AHU main supply Branch for a given air loop +2) Insert Dehumidifier:Desiccant:NoFans into the Branch component list at a given position +3) Create required supporting objects: + * Schedule:Compact (availability) + * Fan:VariableVolume (regeneration fan) + * Regeneration heating coil (Coil:Heating:Fuel or Coil:Heating:Water) + * SetpointManager:MultiZone:Humidity:Maximum + * OutdoorAir:Node (regeneration air inlet node) +4) Update the following component’s inlet node (and related inlet fields for certain systems) + to match the dehumidifier process outlet node, ensuring correct node continuity. + +How to Use + +Configuration +AddDesiccantDehumidifier parameters: + - idfReader: IdfReader instance for the current simulation IDF/IDD + - airLoopName: Name of the target EnergyPlus AirLoopHVAC (used to derive object names) + - position: Component position in the AHU main Branch supply path (1-based) + - regenerationCoilCapacity: Nominal regen coil capacity, in W + - regenerationCoilType: RegenerationCoilType.Fuel or RegenerationCoilType.Water (default Fuel) + - hwLoopName: Plant loop name (required when regenerationCoilType = Water) + +Prerequisites (required placeholders) + +This script expects an Air Loop to be in place in the model (airLoopName referenced in AddDesiccantDehumidifier()) +A hot water water loop must be in place if using RegenerationCoilType.Water (hwLoopName referenced in AddDesiccantDehumidifier()) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; + +using System; using System.Collections.Generic; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript + public class AddDesiccantDehumidifierToAhu : ScriptBase, IScript { - string dehumidifierBoilerplate = @" + // IDF text templates for objects created by this script. + private readonly string dehumidifierIdfTemplate = @" Dehumidifier:Desiccant:NoFans, - {0} Desiccant Dehumidifier,!- Name - {0} Dehumidifier Schedule, !- Availability Schedule Name - {1}, !- Process Air Inlet Node Name - {2}, !- Process Air Outlet Node Name - {0} Desiccant Heating Coil Air Outlet Node, !- Regeneration Air Inlet Node Name - {0} Outside Air Inlet Node 2,!- Regeneration Fan Inlet Node Name - SystemNodeMaximumHumidityRatioSetpoint, !- Control Type alt LeavingMaximumHumidityRatioSetpoint - 0.007, !- Leaving Maximum Humidity Ratio Setpoint kgWater/kgDryAir - 1.5, !- Nominal Process Air Flow Rate m3/s - 2.5, !- Nominal Process Air Velocity m/s - 50, !- Rotor Power W - {3}, !- Regeneration Coil Object Type - {0} Desiccant Regen Coil,!- Regeneration Coil Name - Fan:VariableVolume, !- Regeneration Fan Object Type - {0} Desiccant Regen Fan, !- Regeneration Fan Name - DEFAULT; !- Performance Model Type - - OutdoorAir:Node,{0} Outside Air Inlet Node 2;"; - - string dehumidifierFanBoilerplate = @" + {0} Desiccant Dehumidifier, !- Name + {0} Dehumidifier Schedule, !- Availability Schedule Name + {1}, !- Process Air Inlet Node Name + {2}, !- Process Air Outlet Node Name + {0} Desiccant Heating Coil Air Outlet Node, !- Regeneration Air Inlet Node Name + {0} Outside Air Inlet Node 2, !- Regeneration Fan Inlet Node Name + SystemNodeMaximumHumidityRatioSetpoint, !- Control Type + 0.007, !- Leaving Maximum Humidity Ratio Setpoint [kgWater/kgDryAir] + 1.5, !- Nominal Process Air Flow Rate [m3/s] + 2.5, !- Nominal Process Air Velocity [m/s] + 50, !- Rotor Power [W] + {3}, !- Regeneration Coil Object Type + {0} Desiccant Regen Coil, !- Regeneration Coil Name + Fan:VariableVolume, !- Regeneration Fan Object Type + {0} Desiccant Regen Fan, !- Regeneration Fan Name + DEFAULT; !- Performance Model Type + +OutdoorAir:Node, + {0} Outside Air Inlet Node 2;"; + + private readonly string regenFanIdfTemplate = @" Fan:VariableVolume, - {0} Desiccant Regen Fan, !- Name - {0} Dehumidifier Schedule, !- Availability Schedule Name - 0.7, !- Fan Total Efficiency - 600.0, !- Pressure Rise Pa - Autosize, !- Maximum Flow Rate m3/s - FixedFlowRate, !- Fan Power Minimum Flow Rate Input Method - , !- Fan Power Minimum Flow Fraction - 0.0, !- Fan Power Minimum Air Flow Rate m3/s - 0.9, !- Motor Efficiency - 1.0, !- Motor In Airstream Fraction - 0, !- Fan Power Coefficient 1 - 1, !- Fan Power Coefficient 2 - 0, !- Fan Power Coefficient 3 - 0, !- Fan Power Coefficient 4 - 0, !- Fan Power Coefficient 5 - {0} Outside Air Inlet Node 2,!- Air Inlet Node Name - {0} Regen Fan Outlet Node; !- Air Outlet Node Name"; - - string dehumidifierWaterCoilBoilerplate = @" + {0} Desiccant Regen Fan, !- Name + {0} Dehumidifier Schedule, !- Availability Schedule Name + 0.7, !- Fan Total Efficiency + 600.0, !- Pressure Rise [Pa] + Autosize, !- Maximum Flow Rate [m3/s] + FixedFlowRate, !- Fan Power Minimum Flow Rate Input Method + , !- Fan Power Minimum Flow Fraction + 0.0, !- Fan Power Minimum Air Flow Rate [m3/s] + 0.9, !- Motor Efficiency + 1.0, !- Motor In Airstream Fraction + 0, !- Fan Power Coefficient 1 + 1, !- Fan Power Coefficient 2 + 0, !- Fan Power Coefficient 3 + 0, !- Fan Power Coefficient 4 + 0, !- Fan Power Coefficient 5 + {0} Outside Air Inlet Node 2, !- Air Inlet Node Name + {0} Regen Fan Outlet Node; !- Air Outlet Node Name"; + + private readonly string regenWaterCoilAndBranchIdfTemplate = @" Coil:Heating:Water, - {0} Desiccant Regen Coil, ! - Component name - {0} Dehumidifier Schedule, ! - Availability schedule - autosize, ! - U-factor times area value of coil (W/K) - autosize, ! - Max water flow rate of coil (m3/s) - {0} Desiccant Heating Coil Water Inlet Node, ! - Water inlet node name - {0} Desiccant Heating Coil Water Outlet Node, ! - Water outlet node name - {0} Regen Fan Outlet Node, ! - Air inlet node name - {0} Desiccant Heating Coil Air Outlet Node, ! - Air outlet node name - NominalCapacity, ! - Coil performance input method - {1}, ! - Rated capacity (W) - 80, ! - Rated inlet water temperature (C) - 16, ! - Rated inlet air temperature (C) - 70, ! - Rated outlet water temperature (C) - 35, ! - Rated outlet air temperature (C) - 0.50; ! - Rated ratio for air and water convection + {0} Desiccant Regen Coil, !- Name + {0} Dehumidifier Schedule, !- Availability Schedule Name + autosize, !- U-Factor Times Area Value [W/K] + autosize, !- Maximum Water Flow Rate [m3/s] + {0} Desiccant Heating Coil Water Inlet Node, !- Water Inlet Node Name + {0} Desiccant Heating Coil Water Outlet Node, !- Water Outlet Node Name + {0} Regen Fan Outlet Node, !- Air Inlet Node Name + {0} Desiccant Heating Coil Air Outlet Node, !- Air Outlet Node Name + NominalCapacity, !- Performance Input Method + {1}, !- Rated Capacity [W] + 80, !- Rated Inlet Water Temperature [C] + 16, !- Rated Inlet Air Temperature [C] + 70, !- Rated Outlet Water Temperature [C] + 35, !- Rated Outlet Air Temperature [C] + 0.50; !- Rated Ratio for Air and Water Convection Branch, - {0} Desiccant Regen Coil Branch, ! - Branch name - , ! - Pressure drop curve name - Coil:Heating:Water, ! - Component 1 object type - {0} Desiccant Regen Coil, ! - Component 1 name - {0} Desiccant Heating Coil Water Inlet Node, ! - Component 1 inlet node name - {0} Desiccant Heating Coil Water Outlet Node; ! - Component 1 outlet node name"; - - string dehumidifierCoilBoilerplate = @" + {0} Desiccant Regen Coil Branch, !- Name + , !- Pressure Drop Curve Name + Coil:Heating:Water, !- Component 1 Object Type + {0} Desiccant Regen Coil, !- Component 1 Name + {0} Desiccant Heating Coil Water Inlet Node, !- Component 1 Inlet Node Name + {0} Desiccant Heating Coil Water Outlet Node; !- Component 1 Outlet Node Name"; + + private readonly string regenFuelCoilIdfTemplate = @" Coil:Heating:Fuel, - {0} Desiccant Regen Coil, !- Name - {0} Dehumidifier Schedule, !- Availability Schedule Name - NaturalGas, !- Fuel Type - 0.80, !- Burner Efficiency - {1}, !- Nominal Capacity W - {0} Regen Fan Outlet Node, !- Air Inlet Node Name - {0} Desiccant Heating Coil Air Outlet Node; !- Air Outlet Node Name"; - - string dehumidifierScheduleBoilerplate = @" -Schedule:Compact, - {0} Dehumidifier Schedule,! Name - Any Number, ! Type - Through: 12/31, ! Type - For: AllDays, ! All days in year - Until: 24:00, ! All hours in day - 1;"; - - string dehumidifierSpmBoilerplate = @" - + {0} Desiccant Regen Coil, !- Name + {0} Dehumidifier Schedule, !- Availability Schedule Name + NaturalGas, !- Fuel Type + 0.80, !- Burner Efficiency + {1}, !- Nominal Capacity [W] + {0} Regen Fan Outlet Node, !- Air Inlet Node Name + {0} Desiccant Heating Coil Air Outlet Node; !- Air Outlet Node Name"; + + private readonly string availabilityScheduleIdfTemplate = @" +Schedule:Compact, + {0} Dehumidifier Schedule, !- Name + Any Number, !- Schedule Type Limits Name + Through: 12/31, + For: AllDays, + Until: 24:00, + 1;"; + + private readonly string humiditySpmIdfTemplate = @" SetpointManager:MultiZone:Humidity:Maximum, - {0} Humidity Setpoint Manager, ! - Component name - {0}, ! - HVAC air loop name - .005, ! - Minimum setpoint humidity ratio (kg/kg) - .012, ! - Maximum setpoint humidity ratio (kg/kg) - {1}; ! - Setpoint node list"; + {0} Humidity Setpoint Manager, !- Name + {0}, !- HVAC Air Loop Name + .005, !- Minimum Setpoint Humidity Ratio [kg/kg] + .012, !- Maximum Setpoint Humidity Ratio [kg/kg] + {1}; !- Setpoint Node List Name (or Node Name, depending on model)"; public enum RegenerationCoilType { @@ -128,7 +154,16 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - AddDesiccantDehumidifier(idfReader, "Air Loop", 3, 20000, RegenerationCoilType.Fuel); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // This example usage inserts a dehumidifier into "Air Loop" main branch at position 3, using a fuel regeneration coil of 20 kW capacity. + AddDesiccantDehumidifier( + idfReader, + airLoopName: "Air Loop", + position: 3, + regenerationCoilCapacity: 20000, + regenerationCoilType: RegenerationCoilType.Fuel); idfReader.Save(); } @@ -139,77 +174,104 @@ public void AddDesiccantDehumidifier( int position, int regenerationCoilCapacity, RegenerationCoilType regenerationCoilType = RegenerationCoilType.Fuel, - string hwLoopName = "" - ) + string hwLoopName = "") { - string desiccantDehumidifierComponent = "Dehumidifier:Desiccant:NoFans"; - string ahuMainBranchName = airLoopName + " AHU Main Branch"; - - IdfObject ahuMainBranch = FindObject(idfReader, "Branch", ahuMainBranchName); - - // calculate index of the field set in the main branch - int dehumidfierBranchIndex = 2 + (position - 1) * 4; - - // update component nodes - string processInletNode = ahuMainBranch[dehumidfierBranchIndex - 1].Value; - string processOutletNode = airLoopName + " Desiccant Process Outlet Node"; - - string nextComponentType = ahuMainBranch[dehumidfierBranchIndex].Value; - string nextComponentName = ahuMainBranch[dehumidfierBranchIndex + 1].Value; - - IdfObject nextComponent = FindObject(idfReader, nextComponentType, nextComponentName); - - // component node field names may vary depending on the component type - if (nextComponentType.Equals("CoilSystem:Heating:DX", StringComparison.OrdinalIgnoreCase)) + const string dehumidifierObjectType = "Dehumidifier:Desiccant:NoFans"; + string mainSupplyBranchName = airLoopName + " AHU Main Branch"; + IdfObject mainSupplyBranch = FindObject(idfReader, "Branch", mainSupplyBranchName); + + // Branch objects store components in repeating groups of 4 fields (ObjectType, ObjectName, InletNode, OutletNode) + // The first component group starts at field index 2 (0-based). + int dehumidifierBranchFieldIndex = 2 + (position - 1) * 4; + + // Determine process air inlet/outlet nodes for the new dehumidifier. + // The inlet is the node that previously fed the component at 'position'. + string processAirInletNode = mainSupplyBranch[dehumidifierBranchFieldIndex - 1].Value; + string processAirOutletNode = airLoopName + " Desiccant Process Outlet Node"; + + // Identify the component currently at this position to rewire its inlet to the new outlet. + string downstreamComponentType = mainSupplyBranch[dehumidifierBranchFieldIndex].Value; + string downstreamComponentName = mainSupplyBranch[dehumidifierBranchFieldIndex + 1].Value; + IdfObject downstreamComponent = FindObject(idfReader, downstreamComponentType, downstreamComponentName); + + // Component node field names may vary depending on the component type + if (downstreamComponentType.Equals("CoilSystem:Heating:DX", StringComparison.OrdinalIgnoreCase)) { - IdfObject coil = FindObject(idfReader, nextComponent["Heating Coil Object Type"].Value, nextComponent["Heating Coil Name"].Value); - coil["Air Inlet Node Name"].Value = processOutletNode; + IdfObject heatingCoil = FindObject( + idfReader, + downstreamComponent["Heating Coil Object Type"].Value, + downstreamComponent["Heating Coil Name"].Value); + + heatingCoil["Air Inlet Node Name"].Value = processAirOutletNode; } - else if (nextComponentType.Equals("CoilSystem:Cooling:DX", StringComparison.OrdinalIgnoreCase)) + else if (downstreamComponentType.Equals("CoilSystem:Cooling:DX", StringComparison.OrdinalIgnoreCase)) { - IdfObject coil = FindObject(idfReader, nextComponent["Cooling Coil Object Type"].Value, nextComponent["Cooling Coil Name"].Value); - coil["Air Inlet Node Name"].Value = processOutletNode; - nextComponent["DX Cooling Coil System Inlet Node Name"].Value = processOutletNode; + IdfObject coolingCoil = FindObject( + idfReader, + downstreamComponent["Cooling Coil Object Type"].Value, + downstreamComponent["Cooling Coil Name"].Value); + + coolingCoil["Air Inlet Node Name"].Value = processAirOutletNode; + downstreamComponent["DX Cooling Coil System Inlet Node Name"].Value = processAirOutletNode; } else { - nextComponent["Air Inlet Node Name"].Value = processOutletNode; + downstreamComponent["Air Inlet Node Name"].Value = processAirOutletNode; } - ahuMainBranch[dehumidfierBranchIndex + 2].Value = processOutletNode; - // insert dehumidifier component into AHU main branch - string[] newBranchFields = new string[] { desiccantDehumidifierComponent, airLoopName + " Desiccant Dehumidifier", processInletNode, processOutletNode }; - ahuMainBranch.InsertFields(dehumidfierBranchIndex, newBranchFields); + // Update the Branch entry for the downstream component so its inlet node matches the new node. + mainSupplyBranch[dehumidifierBranchFieldIndex + 2].Value = processAirOutletNode; - // create object idf content - string dehumidifierSchedule = String.Format(dehumidifierScheduleBoilerplate, airLoopName); - string dehumidifierFan = String.Format(dehumidifierFanBoilerplate, airLoopName); - string dehumidifierSpm = String.Format(dehumidifierSpmBoilerplate, airLoopName, processOutletNode); - string dehumidifierCoil = ""; - string dehumidifierCoilType = ""; + // Insert dehumidifier into the AHU main supply branch component list. + string[] newBranchFields = new string[] + { + dehumidifierObjectType, + airLoopName + " Desiccant Dehumidifier", + processAirInletNode, + processAirOutletNode + }; + mainSupplyBranch.InsertFields(dehumidifierBranchFieldIndex, newBranchFields); + + // Build IDF text for all supporting objects (schedule, fan, coil, setpoint manager, OA node). + string scheduleIdfText = string.Format(availabilityScheduleIdfTemplate, airLoopName); + string regenFanIdfText = string.Format(regenFanIdfTemplate, airLoopName); + string humiditySpmIdfText = string.Format(humiditySpmIdfTemplate, airLoopName, processAirOutletNode); + + string regenCoilIdfText; + string regenCoilObjectType; switch (regenerationCoilType) { case RegenerationCoilType.Water: - dehumidifierCoil = String.Format(dehumidifierWaterCoilBoilerplate, airLoopName, regenerationCoilCapacity); - dehumidifierCoilType = "Coil:Heating:Water"; - AddBranch(idfReader, hwLoopName, airLoopName + " Desiccant Regen Coil Branch"); + regenCoilIdfText = string.Format(regenWaterCoilAndBranchIdfTemplate, airLoopName, regenerationCoilCapacity); + regenCoilObjectType = "Coil:Heating:Water"; + + // For water regen coils, the script also expects to connect the new coil branch + // into the HW loop demand side branch list/splitter/mixer (by naming convention). + AddDemandSideBranchToPlantLoop(idfReader, hwLoopName, airLoopName + " Desiccant Regen Coil Branch"); break; + case RegenerationCoilType.Fuel: - dehumidifierCoil = String.Format(dehumidifierCoilBoilerplate, airLoopName, regenerationCoilCapacity); - dehumidifierCoilType = "Coil:Heating:Fuel"; + regenCoilIdfText = string.Format(regenFuelCoilIdfTemplate, airLoopName, regenerationCoilCapacity); + regenCoilObjectType = "Coil:Heating:Fuel"; break; + default: throw new ArgumentException("Invalid regeneration coil type specified."); } - string dehumidifier = String.Format(dehumidifierBoilerplate, airLoopName, processInletNode, processOutletNode, dehumidifierCoilType); - - idfReader.Load(dehumidifier); - idfReader.Load(dehumidifierSchedule); - idfReader.Load(dehumidifierFan); - idfReader.Load(dehumidifierCoil); - idfReader.Load(dehumidifierSpm); + string dehumidifierIdfText = string.Format( + dehumidifierIdfTemplate, + airLoopName, + processAirInletNode, + processAirOutletNode, + regenCoilObjectType); + + idfReader.Load(dehumidifierIdfText); + idfReader.Load(scheduleIdfText); + idfReader.Load(regenFanIdfText); + idfReader.Load(regenCoilIdfText); + idfReader.Load(humiditySpmIdfText); } public IdfObject FindObject(IdfReader reader, string objectType, string objectName) @@ -218,15 +280,15 @@ public IdfObject FindObject(IdfReader reader, string objectType, string objectNa { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private void AddBranch(IdfReader reader, string loopName, string branchName) + private void AddDemandSideBranchToPlantLoop(IdfReader reader, string loopName, string branchName) { - IdfObject branchList = FindObject(reader, "branchList", loopName + " Demand Side Branches"); + IdfObject branchList = FindObject(reader, "BranchList", loopName + " Demand Side Branches"); branchList.InsertField(branchList.Count - 1, branchName); IdfObject splitter = FindObject(reader, "Connector:Splitter", loopName + " Demand Splitter"); diff --git a/cs/applyDesiccantDehumidifierBalancedFlow.cs b/cs/applyDesiccantDehumidifierBalancedFlow.cs index 1441795..19b404f 100644 --- a/cs/applyDesiccantDehumidifierBalancedFlow.cs +++ b/cs/applyDesiccantDehumidifierBalancedFlow.cs @@ -1,23 +1,46 @@ /* -Add HeatExchanger:Desiccant:BalancedFlow object into air handling unit. +Add Desiccant Dehumidifier (of type Balanced Flow) to an AHU branch -The component is added at the specified index to the "DesiccantDehumidifier" air loop (the air loop name needs to match). +This DesignBuilder C# script inserts an EnergyPlus HeatExchanger:Desiccant:BalancedFlow component into an existing air loop supply branch. +Purpose + +1) Locate the AHU main supply Branch for a given air loop +2) Determine the process-side inlet node from the branch field list at the chosen insertion point +3) Create a new process outlet node name and rewire the downstream component to use it +4) Insert a HeatExchanger:Desiccant:BalancedFlow component into the Branch (type/name/inlet/outlet) +5) Add the HeatExchanger:Desiccant:BalancedFlow object plus its PerformanceDataType1 object to the IDF +6) Save the modified IDF + +How to Use + +Configuration +- insertAtBranchFieldIndex refers to the Branch field list ordering. In a Branch, components are stored in repeated quads: + (Component 1 Object Type, Component 1 Name, Component 1 Inlet Node, Component 1 Outlet Node, + Component 2 Object Type, Component 2 Name, Component 2 Inlet Node, Component 2 Outlet Node, ...) + This script: + - Uses (insertAtBranchFieldIndex - 1) to read the process inlet node from the existing branch fields + - Inserts a new quad at insertAtBranchFieldIndex + - Updates downstream component inlet node and branch outlet-node field using fixed offsets + +Prerequisites (required placeholders) + +This script expects an Air Loop to be in place in the model, listed in the USER CONFIGURATION SECTION. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; -using System.Collections.Generic; + +using System; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { public class IdfFindAndReplace : ScriptBase, IScript { - string desiccantHxBoilerplate = @" + // IDF text template: creates the desiccant HX object and a matching performance object (HXDesPerf1). + private readonly string desiccantHxAndPerfIdfTemplate = @" HeatExchanger:Desiccant:BalancedFlow, {0}, !- Name ON 24/7, !- Availability Schedule Name @@ -82,15 +105,15 @@ public class IdfFindAndReplace : ScriptBase, IScript 70.0, !- Minimum Process Inlet Air Relative Humidity for Humidity Ratio Equation percent 100.0; !- Maximum Process Inlet Air Relative Humidity for Humidity Ratio Equation percent"; - public IdfObject FindObject(IdfReader reader, string objectType, string objectName) + private static IdfObject FindObject(IdfReader reader, string objectType, string objectName) { try { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } @@ -100,40 +123,55 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - string ahuName = "Air Loop"; // specify air loop name - int dehumidfierBranchIndex = 18; // place the component into the nth index in the AHU Main Branch + // --------------------------- + // USER CONFIGURATION SECTION + // --------------------------- + string airLoopName = "Air Loop"; // Specify air loop name + int insertAtBranchFieldIndex = 18; // Branch field index where the new component is inserted. - string desiccantDehumidifierComponent = "HeatExchanger:Desiccant:BalancedFlow"; - string desiccantDehumidifierName = "Desiccant Unit"; - string ahuMainBranchName = ahuName + " AHU Main Branch"; + string desiccantHxObjectType = "HeatExchanger:Desiccant:BalancedFlow"; + string desiccantHxName = "Desiccant Unit"; + string mainBranchName = airLoopName + " AHU Main Branch"; - IdfObject ahuMainBranch = FindObject(idfReader, "Branch", ahuMainBranchName); + // Locate the Branch that represents the AHU main branch equipment list. + IdfObject mainBranch = FindObject(idfReader, "Branch", mainBranchName); + string processInletNodeName = mainBranch[insertAtBranchFieldIndex - 1].Value; + string processOutletNodeName = "Desiccant Process Outlet Node"; - string processInletNode = ahuMainBranch[dehumidfierBranchIndex - 1].Value; - string processOutletNode = "Desiccant Process Outlet Node"; + string existingHrHxName = airLoopName + " AHU Heat Recovery Device"; + IdfObject existingHeatRecoveryHx = + FindObject(idfReader, "HeatExchanger:AirToAir:SensibleAndLatent", existingHrHxName); - string hxName = ahuName + " AHU Heat Recovery Device"; - IdfObject heatExchanger = FindObject(idfReader, "HeatExchanger:AirToAir:SensibleAndLatent", hxName); + string regenerationInletNodeName = existingHeatRecoveryHx["Exhaust Air Outlet Node Name"].Value; // Return fan inlet node name + string regenerationOutletNodeName = "Desiccant Return Outlet Node"; - string regenerationInletNode = heatExchanger["Exhaust Air Outlet Node Name"].Value; // return fan inlet node name - string regenerationOutletNode = "Desiccant Return Outlet Node"; - - string nextComponentType = ahuMainBranch[dehumidfierBranchIndex].Value; - string nextComponentName = ahuMainBranch[dehumidfierBranchIndex + 1].Value; + string nextComponentType = mainBranch[insertAtBranchFieldIndex].Value; + string nextComponentName = mainBranch[insertAtBranchFieldIndex + 1].Value; IdfObject nextComponent = FindObject(idfReader, nextComponentType, nextComponentName); - nextComponent["Air Inlet Node Name"].Value = processOutletNode; - ahuMainBranch[dehumidfierBranchIndex + 2].Value = processOutletNode; - - string[] newBranchFields = new string[] { desiccantDehumidifierComponent, desiccantDehumidifierName, processInletNode, processOutletNode }; - - ahuMainBranch.InsertFields(dehumidfierBranchIndex, newBranchFields); - - string dehumidifier = String.Format(desiccantHxBoilerplate, desiccantDehumidifierName, regenerationInletNode, regenerationOutletNode, processInletNode, processOutletNode); - - idfReader.Load(dehumidifier); + nextComponent["Air Inlet Node Name"].Value = processOutletNodeName; + mainBranch[insertAtBranchFieldIndex + 2].Value = processOutletNodeName; + // Insert new Branch fields as a quad (Object Type, Object Name, Inlet Node, Outlet Node): + string[] newBranchFields = new[] + { + desiccantHxObjectType, + desiccantHxName, + processInletNodeName, + processOutletNodeName + }; + mainBranch.InsertFields(insertAtBranchFieldIndex, newBranchFields); + + string desiccantHxIdfText = string.Format( + desiccantHxAndPerfIdfTemplate, + desiccantHxName, + regenerationInletNodeName, + regenerationOutletNodeName, + processInletNodeName, + processOutletNodeName); + + idfReader.Load(desiccantHxIdfText); idfReader.Save(); } } diff --git a/cs/applyDesiccantDehumidifierOaSystem.cs b/cs/applyDesiccantDehumidifierOaSystem.cs index 663b2c6..ab9e839 100644 --- a/cs/applyDesiccantDehumidifierOaSystem.cs +++ b/cs/applyDesiccantDehumidifierOaSystem.cs @@ -1,24 +1,49 @@ /* -Add Dehumidifier:Desiccant:NoFans object into an air handling unit. +Add Dehumidifier:Desiccant:NoFans to an AirLoopHVAC outdoor air system (OA System). -The component is right before the air loop heat exchanger. -Note that it's needed to specify the index of the heat exchanger position in the OA equipment list. +Purpose +This DesignBuilder C# script inserts a desiccant dehumidifier component into an existing AirLoopHVAC outdoor air system. +The dehumidifier is inserted immediately upstream of an air-to-air heat exchanger in the AirLoopHVAC:OutdoorAirSystem:EquipmentList. +Main Steps + +1) Locate the target AirLoopHVAC:OutdoorAirSystem:EquipmentList for a given air loop name +2) Identify the heat exchanger and the the equipment object immediately upstream +3) Insert Dehumidifier:Desiccant:NoFans into the equipment list before the heat exchanger +4) Rewire nodes so the upstream equipment outlet feeds the dehumidifier, and the dehumidifier feeds the heat exchanger +5) Create and load required supporting objects: + - Schedule:Compact (availability) + - Fan:VariableVolume (regeneration fan) + - Coil:Heating:Electric (regeneration heater) + - SetpointManager:MultiZone:Humidity:Maximum (humidity setpoint control) + +How to Use + +Configuration + +Defined in the AddDesiccantDehumidifierToOaSystem(): +- airLoopName: Must match the model's IDF naming. +- heatExchangerEquipmentFieldIndex: The field index where the heat exchanger object type appears in the equipment list + +Prerequisites (required placeholders) + +This script expects an Air Loop of type AirLoopHVAC:OutdoorAirSystem:EquipmentList to be in place in the model. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; -using System.Collections.Generic; + +using System; using System.Linq; -using System.Windows.Forms; +using System.Text; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript + public class AddDesiccantDehumidifierToOaSystemScript : ScriptBase, IScript { - string dehumidifierBoilerplate = @" + // IDF text templates for objects created by this script. + private readonly string dehumidifierIdfTemplate = @" Dehumidifier:Desiccant:NoFans, {0}, !- Name {1}, !- Availability Schedule Name @@ -37,9 +62,9 @@ public class IdfFindAndReplace : ScriptBase, IScript {7}, !- Regeneration Fan Name DEFAULT; !- Performance Model Type - OutdoorAir:Node, Outside Air Inlet Node 2;"; +OutdoorAir:Node, Outside Air Inlet Node 2;"; - string dehumidifierFanBoilerplate = @" + private readonly string regenFanIdfTemplate = @" Fan:VariableVolume, {0}, !- Name {1}, !- Availability Schedule Name @@ -59,7 +84,7 @@ public class IdfFindAndReplace : ScriptBase, IScript {2}, !- Air Inlet Node Name Regen Fan Outlet Node; !- Air Outlet Node Name"; - string dehumidifierCoilBoilerplate = @" + private readonly string regenCoilIdfTemplate = @" Coil:Heating:Electric, {0}, !- Name {1}, !- Availability Schedule Name @@ -68,7 +93,7 @@ public class IdfFindAndReplace : ScriptBase, IScript Regen Fan Outlet Node, !- Air Inlet Node Name Desiccant Heating Coil Air Outlet Node; !- Air Outlet Node Name"; - string dehumidifierScheduleBoilerplate = @" + private readonly string availabilityScheduleIdfTemplate = @" Schedule:Compact, {0}, ! Name Any Number, ! Type Through: 12/31, ! Type @@ -76,7 +101,7 @@ public class IdfFindAndReplace : ScriptBase, IScript Until: 24:00, ! All hours in day 1;"; - string dehumidifierSpmBoilerplate = @" + private readonly string humiditySpmIdfTemplate = @" SetpointManager:MultiZone:Humidity:Maximum, Humidity Setpoint Manager, ! - Component name {0}, ! - HVAC air loop name @@ -84,91 +109,98 @@ public class IdfFindAndReplace : ScriptBase, IScript .012, ! - Maximum setpoint humidity ratio (kg/kg) {1}; ! - Setpoint node list"; - - public IdfObject FindObject(IdfReader reader, string objectType, string objectName) + // Helper to find an IDF object by type and name. + private IdfObject FindObject(IdfReader reader, string objectType, string objectName) { try { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - public void AddDesiccantDehumifierToOaSystem(IdfReader reader, string airLoopName, int hxEquipmentIndex) + // Inserts a desiccant dehumidifier into the OA equipment list upstream of the heat exchanger + private void AddDesiccantDehumidifierToOaSystem(IdfReader reader, string airLoopName, int heatExchangerEquipmentFieldIndex) { // The air loop outdoor air system equipment list will include the desiccant dehumidifier string oaEquipmentListName = airLoopName + " AHU Outdoor air Equipment List"; IdfObject equipmentList = FindObject(reader, "AirLoopHVAC:OutdoorAirSystem:EquipmentList", oaEquipmentListName); // Find the air loop heat exchanger - string hxType = equipmentList[hxEquipmentIndex].Value; - string hxName = equipmentList[hxEquipmentIndex + 1].Value; - IdfObject hxObject = FindObject(reader, hxType, hxName); + string heatExchangerObjectType = equipmentList[heatExchangerEquipmentFieldIndex].Value; + string heatExchangerName = equipmentList[heatExchangerEquipmentFieldIndex + 1].Value; + IdfObject heatExchanger = FindObject(reader, heatExchangerObjectType, heatExchangerName); - // The desiccant dehumidifier will be placed right before the heat exchanger - int originalObjectIndex = hxEquipmentIndex - 2; - string originalObjectType = equipmentList[originalObjectIndex].Value; - string originalObjectname = equipmentList[originalObjectIndex + 1].Value; - IdfObject originalObject = FindObject(reader, originalObjectType, originalObjectname); + // The desiccant unit is inserted immediately before the heat exchanger + int upstreamEquipmentFieldIndex = heatExchangerEquipmentFieldIndex - 2; + string upstreamEquipmentObjectType = equipmentList[upstreamEquipmentFieldIndex].Value; + string upstreamEquipmentName = equipmentList[upstreamEquipmentFieldIndex + 1].Value; + IdfObject upstreamEquipment = FindObject(reader, upstreamEquipmentObjectType, upstreamEquipmentName); // Specify process inlet and outlet nodes - string processInletNode = originalObject["Air Outlet Node Name"].Value; - string processOutletNode = airLoopName + " Desiccant Process Outlet Node"; + string processAirInletNode = upstreamEquipment["Air Outlet Node Name"].Value; + string processAirOutletNode = airLoopName + " Desiccant Process Outlet Node"; // The heat exchanger supply inlet node will be connected to the desiccant dehumidifier - hxObject["Supply Air Inlet Node Name"].Value = processOutletNode; + heatExchanger["Supply Air Inlet Node Name"].Value = processAirOutletNode; - // Add the desiccant dehumidifier to the air loop outdoor air equipment list - string desiccantDehumidifierComponent = "Dehumidifier:Desiccant:NoFans"; + // Insert the dehumidifier in the OA equipment list (as a type/name pair). + string desiccantDehumidifierObjectType = "Dehumidifier:Desiccant:NoFans"; string desiccantDehumidifierName = airLoopName + " Desiccant Unit"; - string[] newEquipmentFields = new string[] { desiccantDehumidifierComponent, desiccantDehumidifierName }; - equipmentList.InsertFields(originalObjectIndex, newEquipmentFields); + equipmentList.InsertFields( + upstreamEquipmentFieldIndex, + new string[] { desiccantDehumidifierObjectType, desiccantDehumidifierName }); // Define the regeneration inlet node, this can be either the air loop relief or outdoor air node - string regenerationInletNode = hxObject["Exhaust Air Outlet Node Name"].Value; + string regenAirInletNode = heatExchanger["Exhaust Air Outlet Node Name"].Value; // Load all desiccant dehumidifer related objects string scheduleName = airLoopName + " Desiccant unit schedule"; - string dehumidifierSchedule = String.Format(dehumidifierScheduleBoilerplate, scheduleName); - string dehumidiferFanName = airLoopName + " Desiccant Fan"; + string availabilitySchedule = string.Format(availabilityScheduleIdfTemplate, scheduleName); + + string regenFanName = airLoopName + " Desiccant Fan"; + string regenFan = string.Format(regenFanIdfTemplate, regenFanName, scheduleName, regenAirInletNode); - string dehumidifierFan = String.Format(dehumidifierFanBoilerplate, dehumidiferFanName, scheduleName, regenerationInletNode); - string dehumidifierSpm = String.Format(dehumidifierSpmBoilerplate, airLoopName, processOutletNode); + string regenCoilName = airLoopName + " Desiccant Heating Coil"; + string regenCoil = string.Format(regenCoilIdfTemplate, regenCoilName, scheduleName); + string regenCoilObjectType = "Coil:Heating:Electric"; - string dehumidifierCoilName = airLoopName + " Desiccant Heating Coil"; - string dehumidifierCoil = String.Format(dehumidifierCoilBoilerplate, dehumidifierCoilName, scheduleName); - string dehumidifierCoilType = "Coil:Heating:Electric"; + string humiditySpm = string.Format(humiditySpmIdfTemplate, airLoopName, processAirOutletNode); - string dehumidifier = String.Format( - dehumidifierBoilerplate, + // Build the dehumidifier object. + string dehumidifier = string.Format( + dehumidifierIdfTemplate, desiccantDehumidifierName, scheduleName, - processInletNode, - processOutletNode, - dehumidifierCoilType, - regenerationInletNode, - dehumidifierCoilName, - dehumidiferFanName - ); + processAirInletNode, + processAirOutletNode, + regenCoilObjectType, + regenAirInletNode, + regenCoilName, + regenFanName); reader.Load(dehumidifier); - reader.Load(dehumidifierSchedule); - reader.Load(dehumidifierFan); - reader.Load(dehumidifierCoil); - reader.Load(dehumidifierSpm); + reader.Load(availabilitySchedule); + reader.Load(regenFan); + reader.Load(regenCoil); + reader.Load(humiditySpm); } - public override void BeforeEnergySimulation() { IdfReader idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - AddDesiccantDehumifierToOaSystem(idfReader, "Air loop", 5); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // "Air loop" must match the air loop name used to form the OA equipment list name. + // Field index (e.g., 5) is where the heat exchanger object type appears in the equipment list. + AddDesiccantDehumidifierToOaSystem(idfReader, "Air loop", 5); idfReader.Save(); } diff --git a/cs/applyDesiccantSystemDehumidifier.cs b/cs/applyDesiccantSystemDehumidifier.cs index 8d51fdf..fc63fb8 100644 --- a/cs/applyDesiccantSystemDehumidifier.cs +++ b/cs/applyDesiccantSystemDehumidifier.cs @@ -1,27 +1,53 @@ /* -Add Dehumidifier:Desiccant:System object into air handling unit. +Add Desiccant Dehumidifier (type System) to an AirLoopHVAC main branch. +This DesignBuilder C# script inserts a Dehumidifier:Desiccant:System downstream of a DX cooling coil (Coilsystem:Cooling:DX) in an AHU main branch. +It also loads the supporting heat exchanger, fan, curves, outdoor air nodes, schedule, and humidity setpoint manager objects. + +Purpose +1) Find the target branch based on AHU name. +2) Locate the CoilSystem:Cooling:DX on that branch and resolve the referenced cooling coil object. +3) Re-wire nodes so the dehumidifier sits downstream of the DX coil system on the branch: + - If DX coil system is the last component: append dehumidifier at end of branch. + - If not last: insert dehumidifier and update the next component’s inlet node. +4) Load the required boilerplate IDF text blocks (dehumidifier, HX, fan, curves, schedule, OA nodes, setpoint manager) + and load the HX performance data. + +How to Use + +Configuration +Defined in the AddDesiccantDehumidifierToAirLoop(): +- airLoopName: Must match the model's IDF naming. + +Prerequisites (required placeholders) + +This script expects an Air Loop to be in place in the model defined in AddDesiccantDehumidifierToAirLoop(). The component is meant to be used in conjunction with a DX cooling coil. The script looks up a DX coil coil and places the dehumidifer downstream of the coil. -The AddDesiccantDehumidifier method requires the following parameters: - - air loop name - +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; + +using System; using System.Collections.Generic; using System.Linq; +using System.Runtime; using System.Windows.Forms; + using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript + public class AddDesiccantDehumidifier : ScriptBase, IScript { - string dehumidifierBoilerplate = @" + // IDF template that defines: + // - Dehumidifier:Desiccant:System + // - HeatExchanger:Desiccant:BalancedFlow + // - Fan:SystemModel + curves + // - OutdoorAir:Node placeholders + // - Schedule + SetpointManager:MultiZone:Humidity:Maximum + private readonly string dehumidifierIdfTemplate = @" Dehumidifier:Desiccant:System, {0} Desiccant Dehumidifier,!- Name {0} Dehumidifier Schedule, !- Availability Schedule Name @@ -109,7 +135,8 @@ public class IdfFindAndReplace : ScriptBase, IScript .012, ! - Maximum setpoint humidity ratio (kg/kg) {2}; ! - Setpoint node list"; - string desiccantHeatExchangerPerf = @" + // Performance data required by HeatExchanger:Desiccant:BalancedFlow + private readonly string hxPerformanceDataIdf = @" HeatExchanger:Desiccant:BalancedFlow:PerformanceDataType1, HXDesPerf1, !- Name 1.05, !- Nominal Air Flow Rate {m3/s} @@ -166,81 +193,110 @@ public class IdfFindAndReplace : ScriptBase, IScript public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - AddDesiccantDehumidifier(idfReader, "Air Loop"); - idfReader.Load(desiccantHeatExchangerPerf); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Target air loop name must match the AirLoopHVAC name in the IDF. + AddDesiccantDehumidifierToAirLoop(idf, "Air Loop"); + idf.Load(hxPerformanceDataIdf); - idfReader.Save(); + idf.Save(); } - public void AddDesiccantDehumidifier(IdfReader idfReader, string airLoopName) + public void AddDesiccantDehumidifierToAirLoop(IdfReader idf, string airLoopName) { - string desiccantDehumidifierComponent = "Dehumidifier:Desiccant:System"; - string ahuMainBranchName = airLoopName + " AHU Main Branch"; + const string dehumidifierObjectType = "Dehumidifier:Desiccant:System"; - IdfObject ahuMainBranch = FindObject(idfReader, "Branch", ahuMainBranchName); + string ahuMainBranchName = airLoopName + " AHU Main Branch"; + IdfObject ahuMainBranch = FindObject(idf, "Branch", ahuMainBranchName); - int dxCoilSystemIndex = FindDxCoilSystemIndex(ahuMainBranch); - if (dxCoilSystemIndex < 0) + // Locate CoilSystem:Cooling:DX in the Branch object fields. + int dxCoolingCoilSystemFieldIndex = FindDxCoilSystemIndex(ahuMainBranch); + if (dxCoolingCoilSystemFieldIndex < 0) { - throw new Exception("Cannot find DX coil in the AHU main branch."); + throw new Exception("Cannot find DX coil (CoilSystem:Cooling:DX) in the AHU main branch."); } - string dxCoilSystemType = ahuMainBranch[dxCoilSystemIndex].Value; - string dxCoilSystemName = ahuMainBranch[dxCoilSystemIndex + 1].Value; + string dxCoilSystemType = ahuMainBranch[dxCoolingCoilSystemFieldIndex].Value; + string dxCoilSystemName = ahuMainBranch[dxCoolingCoilSystemFieldIndex + 1].Value; - IdfObject dxCoilSystem = FindObject(idfReader, dxCoilSystemType, dxCoilSystemName); + IdfObject dxCoilSystem = FindObject(idf, dxCoilSystemType, dxCoilSystemName); string dxCoilType = dxCoilSystem["Cooling Coil Object Type"].Value; string dxCoilName = dxCoilSystem["Cooling Coil Name"].Value; - IdfObject dxCoil = FindObject(idfReader, dxCoilType, dxCoilName); + IdfObject dxCoil = FindObject(idf, dxCoilType, dxCoilName); dxCoil["Condenser Air Inlet Node Name"].Value = airLoopName + " Outside Air Inlet Node 2"; - // update component nodes - string processInletNode = ""; - string processOutletNode = ""; + // Nodes between DX coil and dehumidifier: + // - processInletNode = dehumidifier process inlet (downstream of DX coil outlet) + // - processOutletNode = dehumidifier process outlet (upstream of next component or branch outlet) + string processInletNode; + string processOutletNode; - int nextComponentIndex = dxCoilSystemIndex + 4; - bool isLastComponent = nextComponentIndex == ahuMainBranch.Count; - if (isLastComponent) + int nextComponentFieldIndex = dxCoolingCoilSystemFieldIndex + 4; + bool isDxCoilSystemLastOnBranch = nextComponentFieldIndex == ahuMainBranch.Count; + + if (isDxCoilSystemLastOnBranch) { - // update nodes + // DX system is last: append dehumidifier and connect its outlet to the branch outlet node. processInletNode = airLoopName + " Desiccant Process Inlet Node"; processOutletNode = ahuMainBranch[ahuMainBranch.Count - 1].Value; - ahuMainBranch[dxCoilSystemIndex + 3].Value = processInletNode; + ahuMainBranch[dxCoolingCoilSystemFieldIndex + 3].Value = processInletNode; dxCoilSystem["DX Cooling Coil System Outlet Node Name"].Value = processInletNode; dxCoil["Air Outlet Node Name"].Value = processInletNode; - // append dehumidifier component into AHU main branch - string[] newBranchFields = new string[] { desiccantDehumidifierComponent, airLoopName + " Desiccant Dehumidifier", processInletNode, processOutletNode }; + // Append dehumidifier as a new component group in the branch. + string[] newBranchFields = new string[] + { + dehumidifierObjectType, + airLoopName + " Desiccant Dehumidifier", + processInletNode, + processOutletNode + }; ahuMainBranch.AddFields(newBranchFields); } else { - processInletNode = ahuMainBranch[dxCoilSystemIndex + 3].Value; + // DX system is not last: insert dehumidifier and re-wire the next component inlet. + processInletNode = ahuMainBranch[dxCoolingCoilSystemFieldIndex + 3].Value; processOutletNode = airLoopName + " Desiccant Process Outlet Node"; - // find the next component - string nextComponentType = ahuMainBranch[nextComponentIndex].Value; - string nextComponentName = ahuMainBranch[nextComponentIndex + 1].Value; + // Identify the next component so we can update its inlet node to the dehumidifier outlet. + string nextComponentType = ahuMainBranch[nextComponentFieldIndex].Value; + string nextComponentName = ahuMainBranch[nextComponentFieldIndex + 1].Value; - // update nodes - IdfObject nextComponent = FindObject(idfReader, nextComponentType, nextComponentName); + IdfObject nextComponent = FindObject(idf, nextComponentType, nextComponentName); nextComponent["Air Inlet Node Name"].Value = processOutletNode; - ahuMainBranch[nextComponentIndex + 2].Value = processOutletNode; - // insert dehumidifier component into AHU main branch - string[] newBranchFields = new string[] { desiccantDehumidifierComponent, airLoopName + " Desiccant Dehumidifier", processInletNode, processOutletNode }; - ahuMainBranch.InsertFields(nextComponentIndex, newBranchFields); + // Update the branch’s “inlet node” field for the next component group. + ahuMainBranch[nextComponentFieldIndex + 2].Value = processOutletNode; + + // Insert dehumidifier component group before the next component group. + string[] newBranchFields = new string[] + { + dehumidifierObjectType, + airLoopName + " Desiccant Dehumidifier", + processInletNode, + processOutletNode + }; + ahuMainBranch.InsertFields(nextComponentFieldIndex, newBranchFields); } - // create object idf content - string dehumidifier = String.Format(dehumidifierBoilerplate, airLoopName, processInletNode, processOutletNode, dxCoilType, dxCoilName); - idfReader.Load(dehumidifier); + // Load the dehumidifier + associated objects into the IDF. + string dehumidifierIdf = String.Format( + dehumidifierIdfTemplate, + airLoopName, + processInletNode, + processOutletNode, + dxCoilType, + dxCoilName); + + idf.Load(dehumidifierIdf); } public IdfObject FindObject(IdfReader reader, string objectType, string objectName) @@ -249,7 +305,7 @@ public IdfObject FindObject(IdfReader reader, string objectType, string objectNa { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch { throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } @@ -257,6 +313,7 @@ public IdfObject FindObject(IdfReader reader, string objectType, string objectNa private int FindDxCoilSystemIndex(IdfObject mainBranch) { + // Search across Branch fields for a CoilSystem:Cooling:DX entry. for (int i = 2; i < mainBranch.Count; i += 1) { if (mainBranch[i].Value.Equals("CoilSystem:Cooling:DX", StringComparison.OrdinalIgnoreCase)) diff --git a/cs/applyDesiccantSystemDehumidifierOaSystem.cs b/cs/applyDesiccantSystemDehumidifierOaSystem.cs index b8bc11c..5bca748 100644 --- a/cs/applyDesiccantSystemDehumidifierOaSystem.cs +++ b/cs/applyDesiccantSystemDehumidifierOaSystem.cs @@ -1,27 +1,49 @@ /* -Add Dehumidifier:Desiccant:System object into the Outdoor Air component in air handling unit. +Add Desiccant Dehumidifier (type System) to an AirLoopHVAC outdoor air system (OA System). -The component is meant to be used in conjunction with a DX cooling coil. -The script looks up a DX coil and places the dehumidifer downstream of the coil. +This DesignBuilder C# script inserts a Dehumidifier:Desiccant:System into an air loop’s AirLoopHVAC:OutdoorAirSystem:EquipmentList, +downstream of a CoilSystem:Cooling:DX component. +It also loads all supporting IDF objects required by the desiccant system plus a HX performance object. + +Purpose +1) Find the CoilSystem:Cooling:DX entry in that OA equipment list. +2) Identify the companion DX cooling coil referenced by the DX coil system. +3) Rewire nodes so the new desiccant component sits downstream of the DX coil and upstream + of the next OA component (special handling if the next component is OutdoorAir:Mixer). +4) Insert Dehumidifier:Desiccant:System into the OA equipment list and load the IDF text for the new objects. + +How to Use + +Configuration +Defined in the AddDesiccantDehumidifier(): +- airLoopName: Must match the model's IDF naming. -The AddDesiccantDehumidifier method requires the following parameters: - - air loop name +Prerequisites (required placeholders) +This script expects an Air Loop to be in place in the model defined in AddDesiccantDehumidifier(). +The component is meant to be used in conjunction with a DX cooling coil. +The script looks up a DX coil coil and places the dehumidifer downstream of the coil. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; -using System.Collections.Generic; + +using System; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; -using System; using EpNet; namespace DB.Extensibility.Scripts - { public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript { - string dehumidifierBoilerplate = @" + // IDF template for the full desiccant system "package" (dehumidifier + HX + regen fan + curves + schedule + OA nodes + setpoint managers). + // Placeholders: + // {0} = airLoopName + // {1} = process inlet node name + // {2} = process outlet node name + // {3} = companion cooling coil object type + // {4} = companion cooling coil name + private readonly string desiccantSystemIdfTemplate = @" Dehumidifier:Desiccant:System, {0} Desiccant Dehumidifier,!- Name {0} Dehumidifier Schedule, !- Availability Schedule Name @@ -94,7 +116,7 @@ public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript OutdoorAir:Node,{0} Outside Air Inlet Node 2; OutdoorAir:Node,{0} Outside Air Inlet Node 3; - Schedule:Compact, +Schedule:Compact, {0} Dehumidifier Schedule,! Name Any Number, ! Type Through: 12/31, ! Type @@ -105,15 +127,15 @@ public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript SetpointManager:OutdoorAirPretreat, {0} Desiccant SetPoint Manager, !- Name MaximumHumidityRatio, !- Control Variable - -99, !- Minimum Setpoint Temperature C + -99, !- Minimum Setpoint Temperature C 99, !- Maximum Setpoint Temperature C 0.00001, !- Minimum Setpoint Humidity Ratio kgWater/kgDryAir - 1.0, !- Maximum Setpoint Humidity Ratio kgWater/kgDryAir - {0} Air Loop AHU Mixed Air Outlet, !- Reference Setpoint Node Name - {0} Air Loop AHU Mixed Air Outlet, !- Mixed Air Stream Node Name - {2}, !- Outdoor Air Stream Node Name - {0} Air Loop AHU Extract Fan Air Outlet Node, !- Return Air Stream Node Name - {2}; !- Setpoint Node or NodeList Name + 1.0, !- Maximum Setpoint Humidity Ratio kgWater/kgDryAir + {0} Air Loop AHU Mixed Air Outlet, !- Reference Setpoint Node Name + {0} Air Loop AHU Mixed Air Outlet, !- Mixed Air Stream Node Name + {2}, !- Outdoor Air Stream Node Name + {0} Air Loop AHU Extract Fan Air Outlet Node, !- Return Air Stream Node Name + {2}; !- Setpoint Node or NodeList Name SetpointManager:MultiZone:Humidity:Maximum, {0} Maximum Mzone HUMRAT setpoint,!- Name @@ -122,12 +144,12 @@ public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript 0.057, !- Maximum Setpoint Humidity Ratio (kgWater/kgDryAir) {0} Air Loop AHU Mixed Air Outlet;!- Setpoint Node or NodeList Name"; - - string desiccantHeatExchangerPerf = @" HeatExchanger:Desiccant:BalancedFlow:PerformanceDataType1, + // HX performance object referenced by the desiccant heat exchanger above (name fixed as HXDesPerf1). + private readonly string desiccantHxPerformanceIdf = @"HeatExchanger:Desiccant:BalancedFlow:PerformanceDataType1, HXDesPerf1, !- Name 1.16, !- Nominal Air Flow Rate {m3/s} 3.24, !- Nominal Air Face Velocity {m/s} - 120, !- Nominal Electric Power {W} + 120, !- Nominal Electric Power {W} -2.53636E+00, !- Temperature Equation Coefficient 1 2.13247E+01, !- Temperature Equation Coefficient 2 9.23308E-01, !- Temperature Equation Coefficient 3 @@ -136,22 +158,22 @@ public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript -4.27465E-02, !- Temperature Equation Coefficient 6 1.12204E+02, !- Temperature Equation Coefficient 7 7.78252E-01, !- Temperature Equation Coefficient 8 - 0.001, !- Minimum Regeneration Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} - 0.0238, !- Maximum Regeneration Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} - 33.90, !- Minimum Regeneration Inlet Air Temperature for Temperature Equation {C} - 34.00, !- Maximum Regeneration Inlet Air Temperature for Temperature Equation {C} - 0.008, !- Minimum Process Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} - 0.00801, !- Maximum Process Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} - 10.62, !- Minimum Process Inlet Air Temperature for Temperature Equation {C} - 10.72, !- Maximum Process Inlet Air Temperature for Temperature Equation {C} - 3.14, !- Minimum Regeneration Air Velocity for Temperature Equation {m/s} - 3.24, !- Maximum Regeneration Air Velocity for Temperature Equation {m/s} + 0.001, !- Minimum Regeneration Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} + 0.0238, !- Maximum Regeneration Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} + 33.90, !- Minimum Regeneration Inlet Air Temperature for Temperature Equation {C} + 34.00, !- Maximum Regeneration Inlet Air Temperature for Temperature Equation {C} + 0.008, !- Minimum Process Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} + 0.00801, !- Maximum Process Inlet Air Humidity Ratio for Temperature Equation {kgWater/kgDryAir} + 10.62, !- Minimum Process Inlet Air Temperature for Temperature Equation {C} + 10.72, !- Maximum Process Inlet Air Temperature for Temperature Equation {C} + 3.14, !- Minimum Regeneration Air Velocity for Temperature Equation {m/s} + 3.24, !- Maximum Regeneration Air Velocity for Temperature Equation {m/s} 44.9, !- Minimum Regeneration Outlet Air Temperature for Temperature Equation {C} 45.0, !- Maximum Regeneration Outlet Air Temperature for Temperature Equation {C} - 69, !- Minimum Regeneration Inlet Air Relative Humidity for Temperature Equation {percent} - 70, !- Maximum Regeneration Inlet Air Relative Humidity for Temperature Equation {percent} + 69, !- Minimum Regeneration Inlet Air Relative Humidity for Temperature Equation {percent} + 70, !- Maximum Regeneration Inlet Air Relative Humidity for Temperature Equation {percent} 99.8, !- Minimum Process Inlet Air Relative Humidity for Temperature Equation {percent} - 99.9, !- Maximum Process Inlet Air Relative Humidity for Temperature Equation {percent} + 99.9, !- Maximum Process Inlet Air Relative Humidity for Temperature Equation {percent} -2.25547E+01, !- Humidity Ratio Equation Coefficient 1 9.76839E-01, !- Humidity Ratio Equation Coefficient 2 4.89176E-01, !- Humidity Ratio Equation Coefficient 3 @@ -160,22 +182,22 @@ public class DesiccantDehumidifierOaSystemScript : ScriptBase, IScript 5.17134E-05, !- Humidity Ratio Equation Coefficient 6 4.94917E-02, !- Humidity Ratio Equation Coefficient 7 -2.59417E-04, !- Humidity Ratio Equation Coefficient 8 - 0.0238, !- Minimum Regeneration Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 0.0238001, !- Maximum Regeneration Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 33.9, !- Minimum Regeneration Inlet Air Temperature for Humidity Ratio Equation {C} - 34.00, !- Maximum Regeneration Inlet Air Temperature for Humidity Ratio Equation {C} - 0.007, !- Minimum Process Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 0.008, !- Maximum Process Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 10.62, !- Minimum Process Inlet Air Temperature for Humidity Ratio Equation {C} - 10.720, !- Maximum Process Inlet Air Temperature for Humidity Ratio Equation {C} - 3.14, !- Minimum Regeneration Air Velocity for Humidity Ratio Equation {m/s} - 3.24, !- Maximum Regeneration Air Velocity for Humidity Ratio Equation {m/s} - 0.0228, !- Minimum Regeneration Outlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 0.02380, !- Maximum Regeneration Outlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} - 69, !- Minimum Regeneration Inlet Air Relative Humidity for Humidity Ratio Equation {percent} + 0.0238, !- Minimum Regeneration Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 0.0238001, !- Maximum Regeneration Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 33.9, !- Minimum Regeneration Inlet Air Temperature for Humidity Ratio Equation {C} + 34.00, !- Maximum Regeneration Inlet Air Temperature for Humidity Ratio Equation {C} + 0.007, !- Minimum Process Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 0.008, !- Maximum Process Inlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 10.62, !- Minimum Process Inlet Air Temperature for Humidity Ratio Equation {C} + 10.720, !- Maximum Process Inlet Air Temperature for Humidity Ratio Equation {C} + 3.14, !- Minimum Regeneration Air Velocity for Humidity Ratio Equation {m/s} + 3.24, !- Maximum Regeneration Air Velocity for Humidity Ratio Equation {m/s} + 0.0228, !- Minimum Regeneration Outlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 0.02380, !- Maximum Regeneration Outlet Air Humidity Ratio for Humidity Ratio Equation {kgWater/kgDryAir} + 69, !- Minimum Regeneration Inlet Air Relative Humidity for Humidity Ratio Equation {percent} 70.0, !- Maximum Regeneration Inlet Air Relative Humidity for Humidity Ratio Equation {percent} 99.8, !- Minimum Process Inlet Air Relative Humidity for Humidity Ratio Equation {percent} - 99.9; !- Maximum Process Inlet Air Relative Humidity for Humidity Ratio Equation {percent}"; + 99.9; !- Maximum Process Inlet Air Relative Humidity for Humidity Ratio Equation {percent}"; public override void BeforeEnergySimulation() { @@ -183,64 +205,81 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Configure target air loop(s) here: AddDesiccantDehumidifier(idfReader, "DOAS 1"); - idfReader.Load(desiccantHeatExchangerPerf); + idfReader.Load(desiccantHxPerformanceIdf); idfReader.Save(); } public void AddDesiccantDehumidifier(IdfReader idfReader, string airLoopName) { - string desiccantDehumidifierComponent = "Dehumidifier:Desiccant:System"; - string oaSystemName = airLoopName + " Air Loop AHU Outdoor air Equipment List"; + const string desiccantSystemObjectType = "Dehumidifier:Desiccant:System"; + string oaEquipmentListName = airLoopName + " Air Loop AHU Outdoor air Equipment List"; - IdfObject oaSystem = FindObject(idfReader, "AirLoopHVAC:OutdoorAirSystem:EquipmentList", oaSystemName); + IdfObject oaEquipmentList = FindObject(idfReader, "AirLoopHVAC:OutdoorAirSystem:EquipmentList", oaEquipmentListName); - int dxCoilSystemIndex = FindDxCoilSystemIndex(oaSystem); - if (dxCoilSystemIndex < 0) + // Locate the CoilSystem:Cooling:DX entry within the equipment list fields. + int dxCoilSystemFieldIndex = FindDxCoilSystemIndex(oaEquipmentList); + if (dxCoilSystemFieldIndex < 0) { - throw new Exception("Cannot find DX coil in the OA Equipment list."); + throw new Exception("Cannot find DX coil system (CoilSystem:Cooling:DX) in the OA Equipment list."); } - string dxCoilSystemType = oaSystem[dxCoilSystemIndex].Value; - string dxCoilSystemName = oaSystem[dxCoilSystemIndex + 1].Value; + string dxCoilSystemObjectType = oaEquipmentList[dxCoilSystemFieldIndex].Value; + string dxCoilSystemObjectName = oaEquipmentList[dxCoilSystemFieldIndex + 1].Value; - IdfObject dxCoilSystem = FindObject(idfReader, dxCoilSystemType, dxCoilSystemName); - string dxCoilType = dxCoilSystem["Cooling Coil Object Type"].Value; - string dxCoilName = dxCoilSystem["Cooling Coil Name"].Value; + IdfObject dxCoilSystem = FindObject(idfReader, dxCoilSystemObjectType, dxCoilSystemObjectName); + string dxCoolingCoilObjectType = dxCoilSystem["Cooling Coil Object Type"].Value; + string dxCoolingCoilObjectName = dxCoilSystem["Cooling Coil Name"].Value; - IdfObject dxCoil = FindObject(idfReader, dxCoilType, dxCoilName); - dxCoil["Condenser Air Inlet Node Name"].Value = airLoopName + " Outside Air Inlet Node 2"; + IdfObject dxCoolingCoil = FindObject(idfReader, dxCoolingCoilObjectType, dxCoolingCoilObjectName); + dxCoolingCoil["Condenser Air Inlet Node Name"].Value = airLoopName + " Outside Air Inlet Node 2"; - // update component nodes - string processInletNode = dxCoil["Air Outlet Node Name"].Value; - string processOutletNode = airLoopName + " Process Air Outlet Node"; + // The desiccant process inlet is the DX coil outlet. The process outlet is a new node inserted into the OA path. + string processInletNodeName = dxCoolingCoil["Air Outlet Node Name"].Value; + string processOutletNodeName = airLoopName + " Process Air Outlet Node"; - // find the next component - int nextComponentIndex = dxCoilSystemIndex + 2; - string nextComponentType = oaSystem[nextComponentIndex].Value; - string nextComponentName = oaSystem[nextComponentIndex + 1].Value; + // Identify the component that currently follows the DX coil system in the OA equipment list. + int downstreamComponentFieldIndex = dxCoilSystemFieldIndex + 2; + string downstreamComponentObjectType = oaEquipmentList[downstreamComponentFieldIndex].Value; + string downstreamComponentObjectName = oaEquipmentList[downstreamComponentFieldIndex + 1].Value; - // update nodes - IdfObject nextComponent = FindObject(idfReader, nextComponentType, nextComponentName); + // Rewire the downstream component to take air from the new process outlet node (dehumidifier outlet). + IdfObject downstreamComponent = FindObject(idfReader, downstreamComponentObjectType, downstreamComponentObjectName); - if (nextComponent.IdfClass.Equals("OutdoorAir:Mixer", StringComparison.OrdinalIgnoreCase)) + if (downstreamComponent.IdfClass.Equals("OutdoorAir:Mixer", StringComparison.OrdinalIgnoreCase)) { - nextComponent["Outdoor Air Stream Node Name"].Value = processOutletNode; + // OutdoorAir:Mixer uses "Outdoor Air Stream Node Name" for the OA stream inlet. + downstreamComponent["Outdoor Air Stream Node Name"].Value = processOutletNodeName; } else { - nextComponent["Air Outlet Node Name"].Value = processOutletNode; + // Many OA components use a generic "Air Outlet Node Name" style field for inlet/outlet connectivity. + downstreamComponent["Air Outlet Node Name"].Value = processOutletNodeName; } - // insert dehumidifier component into OA System - string[] newBranchFields = new string[] { desiccantDehumidifierComponent, airLoopName + " Desiccant Dehumidifier" }; - oaSystem.InsertFields(nextComponentIndex, newBranchFields); - - - // create object idf content - string dehumidifier = String.Format(dehumidifierBoilerplate, airLoopName, processInletNode, processOutletNode, dxCoilType, dxCoilName); - idfReader.Load(dehumidifier); + // Insert the dehumidifier into the OA equipment list immediately before the downstream component. + string[] newEquipmentFields = new string[] + { + desiccantSystemObjectType, + airLoopName + " Desiccant Dehumidifier" + }; + oaEquipmentList.InsertFields(downstreamComponentFieldIndex, newEquipmentFields); + + // Load all supporting objects for the desiccant system (HX, fan, curves, schedule, OA nodes, SPMs). + string desiccantSystemIdf = string.Format( + desiccantSystemIdfTemplate, + airLoopName, + processInletNodeName, + processOutletNodeName, + dxCoolingCoilObjectType, + dxCoolingCoilObjectName); + + idfReader.Load(desiccantSystemIdf); } public IdfObject FindObject(IdfReader reader, string objectType, string objectName) @@ -249,17 +288,18 @@ public IdfObject FindObject(IdfReader reader, string objectType, string objectNa { return reader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private int FindDxCoilSystemIndex(IdfObject oaEquipment) + private int FindDxCoilSystemIndex(IdfObject oaEquipmentList) { - for (int i = 1; i < oaEquipment.Count; i += 1) + // Searches for the CoilSystem:Cooling:DX object type within the OA equipment list fields. + for (int i = 1; i < oaEquipmentList.Count; i += 1) { - if (oaEquipment[i].Value.Equals("CoilSystem:Cooling:DX", StringComparison.OrdinalIgnoreCase)) + if (oaEquipmentList[i].Value.Equals("CoilSystem:Cooling:DX", StringComparison.OrdinalIgnoreCase)) { return i; } diff --git a/cs/applyDesuperheaterGenericAhu.cs b/cs/applyDesuperheaterGenericAhu.cs index 609f5bd..2befa95 100644 --- a/cs/applyDesuperheaterGenericAhu.cs +++ b/cs/applyDesuperheaterGenericAhu.cs @@ -1,96 +1,167 @@ /* -Replace electric heating coil in all generic air handling units with "desuperheater" coils. +Replace Electric Heating Coils with Desuperheater Coils (for Generic AHUs) -The AHU needs to contain DX cooling coil and use "desuperheater" string in its name. +This DesignBuilder C# script scans EnergyPlus Branch objects and replaces +Coil:Heating:Electric entries with Coil:Heating:Desuperheater for selected AHUs. +Purpose +1) Identify target Branch objects by name filters: + - Branch Name contains "desuperheater" and "AHU Main Branch" +2) For each target Branch: + - Confirm the Branch includes a CoilSystem:Cooling:DX component (DX coil system). + - Resolve the actual DX cooling coil referenced by the DX coil system. + - Replace the first Coil:Heating:Electric found in the Branch with Coil:Heating:Desuperheater. + - Create a new Coil:Heating:Desuperheater object using the replaced electric coil’s nodes/schedule, + and referencing the resolved DX cooling coil. + - Remove the original Coil:Heating:Electric object from the IDF. +3) Save the modified IDF before the EnergyPlus simulation runs. + +How to Use + +Configuration +- Target selection is controlled by Branch Name text matching: + - Must contain: "desuperheater" and "AHU Main Branch" +- Define desuperheater coil efficiency (see AddDesuperheaterCoil()). + +Prerequisites (required placeholders) + +Target Branches must include: +- A "CoilSystem:Cooling:DX" component (the script uses this to find the referenced DX cooling coil). +- A "Coil:Heating:Electric" component to be replaced by the Desuperheater. It also provides: + - Availability schedule (as defined in the model) + - Temperature setpoint node name (as defined in the model) + - Inlet / outlet node names (automatically obtained) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ +using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -using System; + using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript - { - - private IdfObject FindObject(IdfReader reader, string objectType, string objectName) - { - return reader[objectType].First(c => c[0] == objectName); - } - - private void UpdateBranches(IdfReader idfReader) - { - IEnumerable branches = idfReader["Branch"]; - foreach (IdfObject branch in branches) - { - if (branch[0].Value.Contains("desuperheater") && branch[0].Value.Contains("AHU Main Branch")) - { - UpdateBranch(idfReader, branch); - } - } - } - - private void UpdateBranch(IdfReader reader, IdfObject branch) - { - const string DesuperHeaterObject = "Coil:Heating:Desuperheater"; - const string ElectricCoilObject = "Coil:Heating:Electric"; - const string DxSystemObjectName = "CoilSystem:Cooling:DX"; - IdfObject DxCoil = new IdfObject(); - bool DxIncluded = false; - - foreach (int i in Enumerable.Range(1, (branch.Count - 2))) - { - Field ThisField = branch.Fields[i]; - Field NextField = branch.Fields[i + 1]; - if (ThisField.Value.Contains(DxSystemObjectName)) - { - MessageBox.Show("DX Coil System included " + branch[0]); - DxIncluded = true; - IdfObject CoilSystem = FindObject(reader, DxSystemObjectName, NextField.Value); - DxCoil = FindObject(reader, CoilSystem["Cooling Coil Object Type"], CoilSystem["Cooling Coil Name"]); - - } - - if (DxIncluded && ThisField.Comment.ToLower().Contains("object type") && ThisField.Value.ToLower() == ElectricCoilObject.ToLower()) - { - IdfObject ElectricCoil = FindObject(reader, ElectricCoilObject, NextField.Value); - ThisField.Value = DesuperHeaterObject; - AddDesuperheater(reader, ElectricCoil, DxCoil); - MessageBox.Show("Replacing electric heating coil: " + ElectricCoil[0] + " with desuperheater coil."); - reader.Remove(ElectricCoil); - break; - } - } - } - private void AddDesuperheater(IdfReader reader, IdfObject electricCoil, IdfObject dxCoil) - { - string name = electricCoil[0].Value; - string availability = electricCoil[1].Value; - string inlet = electricCoil[4].Value; - string outlet = electricCoil[5].Value; - string dxClass = dxCoil.IdfClass; - string dxName = dxCoil[0].Value; - string tempNode = electricCoil[6].Value; - double efficiency = 0.3; - - string desuperheater = String.Format("Coil:Heating:Desuperheater,{0},{1},{2},{3},{4},{5},{6},{7},0;", name, availability, efficiency, inlet, outlet, dxClass, dxName, tempNode); - - reader.Load(desuperheater); - } - public override void BeforeEnergySimulation() - { - IdfReader idfReader = new IdfReader( - ApiEnvironment.EnergyPlusInputIdfPath, - ApiEnvironment.EnergyPlusInputIddPath); - - UpdateBranches(idfReader); - - idfReader.Save(); - } - } -} + public class ReplaceElectricHeatingWithDesuperheaterCoils : ScriptBase, IScript + { + // Returns the first object of the given type with Name == objectName. + private IdfObject GetObjectByName(IdfReader reader, string objectType, string objectName) + { + return reader[objectType].First(c => c[0] == objectName); + } + + // Entry point for IDF edits: find all candidate Branch objects and update them. + private void ReplaceCoilsInTargetBranches(IdfReader idfReader) + { + IEnumerable branches = idfReader["Branch"]; + + foreach (IdfObject branch in branches) + { + // Target selection based on Branch Name text matching. + string branchName = branch[0].Value; + + if (branchName.Contains("desuperheater") && branchName.Contains("AHU Main Branch")) + { + ReplaceCoilInBranchIfEligible(idfReader, branch); + } + } + } + + + // Replace Coil:Heating:Electric with Coil:Heating:Desuperheater (if CoilSystem:Cooling:DX exists). + private void ReplaceCoilInBranchIfEligible(IdfReader reader, IdfObject branch) + { + const string DesuperheaterObjectType = "Coil:Heating:Desuperheater"; + const string ElectricHeatingCoilObjectType = "Coil:Heating:Electric"; + const string DxCoilSystemObjectType = "CoilSystem:Cooling:DX"; + + IdfObject dxCoolingCoil = new IdfObject(); + bool hasDxCoilSystem = false; + + // Branch objects typically list equipment in pairs + foreach (int i in Enumerable.Range(1, (branch.Count - 2))) + { + Field objectTypeField = branch.Fields[i]; + Field objectNameField = branch.Fields[i + 1]; + + // Confirm if the branch contains a DX cooling coil system and resolve its cooling coil. + if (objectTypeField.Value.Contains(DxCoilSystemObjectType)) + { + MessageBox.Show("DX Coil System included " + branch[0]); + + hasDxCoilSystem = true; + + IdfObject dxCoilSystem = GetObjectByName(reader, DxCoilSystemObjectType, objectNameField.Value); + + dxCoolingCoil = GetObjectByName( + reader, + dxCoilSystem["Cooling Coil Object Type"], + dxCoilSystem["Cooling Coil Name"]); + } + + // Once DX is confirmed, replace the electric heating coil object type in the branch list. + if (hasDxCoilSystem + && objectTypeField.Comment.ToLower().Contains("object type") + && objectTypeField.Value.ToLower() == ElectricHeatingCoilObjectType.ToLower()) + { + IdfObject electricHeatingCoil = GetObjectByName(reader, ElectricHeatingCoilObjectType, objectNameField.Value); + + // Replace the object type in the Branch equipment list (object name stays the same). + objectTypeField.Value = DesuperheaterObjectType; + + AddDesuperheaterCoil(reader, electricHeatingCoil, dxCoolingCoil); + + MessageBox.Show( + "Replacing electric heating coil: " + electricHeatingCoil[0] + " with desuperheater coil."); + + reader.Remove(electricHeatingCoil); + + break; + } + } + } + + private void AddDesuperheaterCoil(IdfReader reader, IdfObject electricCoil, IdfObject dxCoil) + { + // Placeholder electric coil fields are used to populate the new desuperheater coil. + string name = electricCoil[0].Value; + string availabilityScheduleName = electricCoil[1].Value; + string airInletNodeName = electricCoil[4].Value; + string airOutletNodeName = electricCoil[5].Value; + string temperatureSetpointNodeName = electricCoil[6].Value; + + // The desuperheater coil references the DX cooling coil (object type/class + name). + string dxCoolingCoilObjectType = dxCoil.IdfClass; + string dxCoolingCoilName = dxCoil[0].Value; + + double desuperheaterEfficiency = 0.3; // USER CONFIGURATION: Define the efficiency of the Desuperheater (default is set to 0.3) + + string desuperheaterIdf = String.Format( + "Coil:Heating:Desuperheater,{0},{1},{2},{3},{4},{5},{6},{7},0;", + name, + availabilityScheduleName, + desuperheaterEfficiency, + airInletNodeName, + airOutletNodeName, + dxCoolingCoilObjectType, + dxCoolingCoilName, + temperatureSetpointNodeName); + + reader.Load(desuperheaterIdf); + } + + public override void BeforeEnergySimulation() + { + IdfReader idfReader = new IdfReader( + ApiEnvironment.EnergyPlusInputIdfPath, + ApiEnvironment.EnergyPlusInputIddPath); + + ReplaceCoilsInTargetBranches(idfReader); + + idfReader.Save(); + } + } +} \ No newline at end of file diff --git a/cs/applyDesuperheaterGenericUnitaryAhu.cs b/cs/applyDesuperheaterGenericUnitaryAhu.cs index 9fa1c1f..6919f87 100644 --- a/cs/applyDesuperheaterGenericUnitaryAhu.cs +++ b/cs/applyDesuperheaterGenericUnitaryAhu.cs @@ -1,102 +1,150 @@ /* -Apply desuperheater coil to Unitary Generic AHU systems. +Apply Desuperheater Coil to Generic Unitary System Objects -The update is applied to all Unitary Generic AHU systems with desuperheater substring in their name. -The unit must include the CoolReheat humidity control. +This DesignBuilder C# script runs before the EnergyPlus simulation and modifies the IDF to replace a unitary +system’s supplemental (reheat) heating coil with a Coil:Heating:Desuperheater for eligible systems. + +Purpose +1) Find all AirLoopHVAC:UnitarySystem objects whose Name contains a configurable substring (default: "desuperheater"). +2) For each match: + - Read the unitary system’s Supplemental Heating Coil and Cooling Coil references + - Build a new Coil:Heating:Desuperheater object using the reheat coil nodes/schedule and the cooling coil as heat source + - Update the unitary system to reference the new desuperheater coil + - Insert the new desuperheater coil object into the IDF + - Remove the original reheat coil object from the IDF + +How to Use + +Configuration +- Name your target AirLoopHVAC:UnitarySystem objects so their Name contains the trigger substring. + Default trigger substring is: "desuperheater" (case-insensitive). +- Define desuperheater coil efficiency (see desuperheaterCoilIdfTemplate). +- Availability schedule, temperature setpoint, and node connections are taken from the existing reheat coil (placeholder). + +Prerequisites (required placeholders) + +- Eligible AirLoopHVAC:UnitarySystem objects must have the trigger substring ("desuperheater") in the name +- The unit must include CoolReheat humidity control so that a supplemental (reheat) coil is present and referenced. + This setting can be controled in 'Unitary System settings > Control > Dehumidification control type' in DB model. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Collections.Generic; -using System.Linq; using System; +using System.Linq; using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + public class ApplyDesuperheaterToUnitarySystems : ScriptBase, IScript { - private IdfReader Reader; + private IdfReader idfReader; - public IdfObject FindObject(string objectType, string objectName) + private IdfObject FindIdfObject(string objectType, string objectName) { try { - return Reader[objectType].First(c => c[0] == objectName); + return idfReader[objectType].First(o => o[0] == objectName); } - catch (Exception e) + catch { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private string GetDesuperheaterCoil(string name, string coolingCoilType, string coolingCoilName, IdfObject reheatCoil) + // Generates the IDF text for a Coil:Heating:Desuperheater object. + // Availability schedule, temperature setpoint, and node connections are taken from the existing reheat coil (placeholder). + private string BuildDesuperheaterCoilIdf( + string desuperheaterCoilName, + string coolingCoilObjectType, + string coolingCoilObjectName, + IdfObject existingReheatCoil) { - string template = @" + // USER CONFIGURATION: Define the efficiency of the Desuperheater (default is set to 0.3) + string desuperheaterCoilIdfTemplate = @" Coil:Heating:Desuperheater, {0}, !- Coil Name {1}, !- Availability Schedule - 0.3, !- Heat Reclaim Recovery Efficiency {2}, !- Coil Air Inlet Node Name {3}, !- Coil Air Outlet Node Name {4}, !- Heating Source Type {5}, !- Heating Source Name {6}, !- Coil Temperature Setpoint Node Name 0.1; !- Parasitic Electric Load W"; + return string.Format( - template, - name, - reheatCoil["Availability Schedule Name"].Value, - reheatCoil["Air Inlet Node Name"].Value, - reheatCoil["Air Outlet Node Name"].Value, - coolingCoilType, - coolingCoilName, - reheatCoil["Temperature Setpoint Node Name"].Value); + desuperheaterCoilIdfTemplate, + desuperheaterCoilName, + existingReheatCoil["Availability Schedule Name"].Value, + existingReheatCoil["Air Inlet Node Name"].Value, + existingReheatCoil["Air Outlet Node Name"].Value, + coolingCoilObjectType, + coolingCoilObjectName, + existingReheatCoil["Temperature Setpoint Node Name"].Value); } - private void AddDesuperheaterToUnitary(IdfObject unitary) + // Replaces the unitary system's "Supplemental Heating Coil" reference with a new desuperheater coil + private void ApplyDesuperheaterToUnitarySystem(IdfObject unitarySystem) { - string unitaryName = unitary["Name"].Value; + string unitarySystemName = unitarySystem["Name"].Value; + + string reheatCoilObjectType = unitarySystem["Supplemental Heating Coil Object Type"].Value; + string reheatCoilObjectName = unitarySystem["Supplemental Heating Coil Name"].Value; - string reheatCoilType = unitary["Supplemental Heating Coil Object Type"].Value; - string reheatCoilName = unitary["Supplemental Heating Coil Name"].Value; - string coolingCoilType = unitary["Cooling Coil Object Type"].Value; - string coolingCoilName = unitary["Cooling Coil Name"].Value; + string coolingCoilObjectType = unitarySystem["Cooling Coil Object Type"].Value; + string coolingCoilObjectName = unitarySystem["Cooling Coil Name"].Value; - if (string.IsNullOrEmpty(reheatCoilType)) + if (string.IsNullOrEmpty(reheatCoilObjectType)) { - MessageBox.Show("Skipping unit: " + unitaryName + ", reheat coil is not included." + - "\nMake sure that the unit uses CoolReheat humidity control."); + MessageBox.Show( + "Skipping unit: " + unitarySystemName + + "\nSupplemental (reheat) coil is not included." + + "\nMake sure that the unit uses CoolReheat humidity control."); return; } - IdfObject reheatCoil = FindObject(reheatCoilType, reheatCoilName); + // Placeholder reheat coil object is used to copy schedule + node connections + IdfObject existingReheatCoil = FindIdfObject(reheatCoilObjectType, reheatCoilObjectName); - string desuperheaterName = unitaryName + " Desuperheater Coil"; - string desuperheater = GetDesuperheaterCoil(desuperheaterName, coolingCoilType, coolingCoilName, reheatCoil); + string desuperheaterCoilName = unitarySystemName + " Desuperheater Coil"; + string desuperheaterCoilIdf = BuildDesuperheaterCoilIdf( + desuperheaterCoilName, + coolingCoilObjectType, + coolingCoilObjectName, + existingReheatCoil); - unitary["Supplemental Heating Coil Object Type"].Value = "Coil:Heating:Desuperheater"; - unitary["Supplemental Heating Coil Name"].Value = desuperheaterName; + // Update unitary system to reference the new desuperheater coil as the supplemental heating coil + unitarySystem["Supplemental Heating Coil Object Type"].Value = "Coil:Heating:Desuperheater"; + unitarySystem["Supplemental Heating Coil Name"].Value = desuperheaterCoilName; - Reader.Load(desuperheater); - Reader.Remove(reheatCoil); + idfReader.Load(desuperheaterCoilIdf); + idfReader.Remove(existingReheatCoil); } public override void BeforeEnergySimulation() { - Reader = new IdfReader( + idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - foreach (IdfObject unitary in Reader["AirLoopHVAC:UnitarySystem"]) + // Configuration: select unitary systems by substring match in Name (case-insensitive). + const string nameFilterSubstring = "desuperheater"; + + foreach (IdfObject unitarySystem in idfReader["AirLoopHVAC:UnitarySystem"]) { - string name = unitary[0].Value; - if (name.ToLower().Contains("desuperheater")) + string unitarySystemName = unitarySystem[0].Value; + + // Only apply to systems whose name contains the configured substring. + if (unitarySystemName.IndexOf(nameFilterSubstring, StringComparison.OrdinalIgnoreCase) >= 0) { - AddDesuperheaterToUnitary(unitary); + ApplyDesuperheaterToUnitarySystem(unitarySystem); } } - Reader.Save(); + + idfReader.Save(); } } -} +} \ No newline at end of file diff --git a/cs/applyDesuperheaterUnitaryHeatCool.cs b/cs/applyDesuperheaterUnitaryHeatCool.cs index 6bf93ab..47ee7f5 100644 --- a/cs/applyDesuperheaterUnitaryHeatCool.cs +++ b/cs/applyDesuperheaterUnitaryHeatCool.cs @@ -1,13 +1,36 @@ /* -Apply desuperheater coil to UnitaryHeatCool systems. +Apply Desuperheater Reheat Coil to AirLoopHVAC:UnitaryHeatCool Systems -The update is applied to all Unitary HeatCool systems with desuperheater substring in their name. -The unit must include the CoolReheat humidity control. +This DesignBuilder C# script modifies the EnergyPlus IDF to replace the existing reheat coil on selected AirLoopHVAC:UnitaryHeatCool units +with a Coil:Heating:Desuperheater that reclaims heat from the unit’s cooling coil. + +Purpose +1) Scan all AirLoopHVAC:UnitaryHeatCool objects in the IDF +2) Select target units by name (must contain a configurable substring, by default set as "desuperheater") +3) For each target unit: + - Read the existing reheat coil fields (used as a placeholder for nodes/schedule/setpoint node) + - Create a new Coil:Heating:Desuperheater IDF object using the placeholder reheat coil data + - Update the unitary “Reheat Coil Object Type/Name” fields to point to the new desuperheater coil +4) Save the updated IDF + +How to Use + +Configuration +- Name your target unitary systems so their Name contains the selection substring (default: "desuperheater"). +- Define desuperheater coil efficiency (see desuperheaterCoilTemplate). +- Availability schedule, temperature setpoint, and node connections are taken from the existing reheat coil (placeholder). + +Prerequisites (required placeholders) + +- Eligible AirLoopHVAC:UnitaryHeatCool objects must have the trigger substring ("desuperheater") in the name +- The unit must include CoolReheat humidity control so that a supplemental (reheat) coil is present and referenced. + This setting can be controled in 'Unitary System settings > Control > Dehumidification control type' in DB model. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Collections.Generic; -using System.Linq; using System; +using System.Linq; using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; @@ -16,23 +39,34 @@ namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - private IdfReader Reader; + // Selection rule for target units (case-insensitive substring match on AirLoopHVAC:UnitaryHeatCool Name). + private const string SelectionSubstring = "desuperheater"; - public IdfObject FindObject(string objectType, string objectName) + private IdfReader idf; + + private IdfObject FindObject(string objectType, string objectName) { try { - return Reader[objectType].First(c => c[0] == objectName); + return idf[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private string GetDesuperheaterCoil(string name, string coolingCoilType, string coolingCoilName, IdfObject reheatCoil) + // Generates the IDF text for a Coil:Heating:Desuperheater object. + // Availability schedule, temperature setpoint, and node connections are taken from the existing reheat coil (placeholder). + private string GetDesuperheaterCoilIdfText( + string desuperheaterCoilName, + string coolingCoilType, + string coolingCoilName, + IdfObject placeholderReheatCoil) { - string template = @" + // USER CONFIGURATION: Define the efficiency of the Desuperheater (default is set to 0.3) + string desuperheaterCoilTemplate = @" Coil:Heating:Desuperheater, {0}, !- Coil Name {1}, !- Availability Schedule @@ -43,60 +77,71 @@ private string GetDesuperheaterCoil(string name, string coolingCoilType, string {5}, !- Heating Source Name {6}, !- Coil Temperature Setpoint Node Name 0.1; !- Parasitic Electric Load W"; + return string.Format( - template, - name, - reheatCoil["Availability Schedule Name"].Value, - reheatCoil["Air Inlet Node Name"].Value, - reheatCoil["Air Outlet Node Name"].Value, + desuperheaterCoilTemplate, + desuperheaterCoilName, + placeholderReheatCoil["Availability Schedule Name"].Value, + placeholderReheatCoil["Air Inlet Node Name"].Value, + placeholderReheatCoil["Air Outlet Node Name"].Value, coolingCoilType, coolingCoilName, - reheatCoil["Temperature Setpoint Node Name"].Value); + placeholderReheatCoil["Temperature Setpoint Node Name"].Value); } - private void AddDesuperheaterToUnitary(IdfObject unitary) + // Replaces the unitary reheat coil reference with a new Coil:Heating:Desuperheater and loads the new object into the IDF. + private void AddDesuperheaterToUnitaryHeatCool(IdfObject unitaryHeatCool) { - string unitaryName = unitary["Name"].Value; + string unitaryName = unitaryHeatCool["Name"].Value; - string reheatCoilType = unitary["Reheat Coil Object Type"].Value; - string reheatCoilName = unitary["Reheat Coil Name"].Value; - string coolingCoilType = unitary["Cooling Coil Object Type"].Value; - string coolingCoilName = unitary["Cooling Coil Name"].Value; + string reheatCoilType = unitaryHeatCool["Reheat Coil Object Type"].Value; + string reheatCoilName = unitaryHeatCool["Reheat Coil Name"].Value; + string coolingCoilType = unitaryHeatCool["Cooling Coil Object Type"].Value; + string coolingCoilName = unitaryHeatCool["Cooling Coil Name"].Value; if (string.IsNullOrEmpty(reheatCoilType)) { - MessageBox.Show("Skipping unit: " + unitaryName + ", reheat coil is not included." + - "\nMake sure that the unit uses CoolReheat humidity control."); + MessageBox.Show( + "Skipping unit: " + unitaryName + ", reheat coil is not included." + + "\nMake sure that the unit uses CoolReheat humidity control."); return; } - IdfObject reheatCoil = FindObject(reheatCoilType, reheatCoilName); + // Placeholder reheat coil provides nodes/schedule/setpoint node for the new desuperheater coil. + IdfObject placeholderReheatCoil = FindObject(reheatCoilType, reheatCoilName); - string desuperheaterName = unitaryName + " Desuperheater Coil"; - string desuperheater = GetDesuperheaterCoil(desuperheaterName, coolingCoilType, coolingCoilName, reheatCoil); + string desuperheaterCoilName = unitaryName + " Desuperheater Coil"; + string desuperheaterCoilIdfText = GetDesuperheaterCoilIdfText( + desuperheaterCoilName, + coolingCoilType, + coolingCoilName, + placeholderReheatCoil); - unitary["Reheat Coil Object Type"].Value = "Coil:Heating:Desuperheater"; - unitary["Reheat Coil Name"].Value = desuperheaterName; + // Update the unitary to point to the new desuperheater coil object. + unitaryHeatCool["Reheat Coil Object Type"].Value = "Coil:Heating:Desuperheater"; + unitaryHeatCool["Reheat Coil Name"].Value = desuperheaterCoilName; - Reader.Load(desuperheater); + idf.Load(desuperheaterCoilIdfText); } public override void BeforeEnergySimulation() { - Reader = new IdfReader( + idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - foreach (IdfObject unitary in Reader["AirLoopHVAC:UnitaryHeatCool"]) + foreach (IdfObject unitaryHeatCool in idf["AirLoopHVAC:UnitaryHeatCool"]) { - string name = unitary[0].Value; - if (name.ToLower().Contains("desuperheater")) + string unitaryName = unitaryHeatCool[0].Value; + + // Target units are identified by substring match in the unit name (case-insensitive). + if (unitaryName.IndexOf(SelectionSubstring, StringComparison.OrdinalIgnoreCase) >= 0) { - AddDesuperheaterToUnitary(unitary); + AddDesuperheaterToUnitaryHeatCool(unitaryHeatCool); } } - Reader.Save(); + + idf.Save(); } } -} - +} \ No newline at end of file diff --git a/cs/applyFlowExteriorWallAreaInfiltration.cs b/cs/applyFlowExteriorWallAreaInfiltration.cs index c512ed4..aeb8c7e 100644 --- a/cs/applyFlowExteriorWallAreaInfiltration.cs +++ b/cs/applyFlowExteriorWallAreaInfiltration.cs @@ -1,18 +1,35 @@ /* -Apply the Flow/ExteriorWallArea infiltration method. +Infiltration Conversion Script (Flow/ExteriorWallArea) -The infiltration rate is retrieved from DesignBuilder interface. +This DesignBuilder C# script converts ZoneInfiltration:DesignFlowRate objects to use the "Flow/ExteriorWallArea" calculation method +and populates the "Flow per Exterior Surface Area" field using the infiltration value defined in the DesignBuilder UI. -This script requires a specific "Infiltration units" option (3-m3/h-m2 at 4Pa). +Purpose (main steps) +1) Retrieve each zone’s infiltration value from DesignBuilder attributes (InfiltrationValueI4). +2) Update every ZoneInfiltration:DesignFlowRate object: + - Set Design Flow Rate Calculation Method = "Flow/ExteriorWallArea" + - Set Flow per Exterior Surface Area = (DB value) / 3600 [m3/h-m2 to m3/s-m2] +3) Save the modified IDF back to disk before EnergyPlus starts. +How to Use + +Configuration +- The infiltration rate is retrieved from DesignBuilder interface. + This setting can be controled in 'Construction > Airtightness > Model Infiltration'. + +Prerequisites (required placeholders) +- This script requires the DesignBuilder "Infiltration units" option set to "3 - m3/h-m2 at 4Pa". + This setting can be controled in 'Model Options > Natural Ventilation and Infiltration > Infiltration Units'. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ + using System; -using System.Runtime; using System.Collections.Generic; using System.Linq; -using DB.Extensibility.Contracts; using DB.Api; +using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts @@ -21,35 +38,44 @@ public class InfiltrationChange : ScriptBase, IScript { public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath ); Site site = ApiEnvironment.Site; Building building = site.Buildings[ApiEnvironment.CurrentBuildingIndex]; - Dictionary zoneInfiltrationRates = GetZoneInfiltrationRates(building); - IEnumerable infiltrationObjects = idfReader["ZONEINFILTRATION:DESIGNFLOWRATE"]; + // Read infiltration values from DesignBuilder zone attributes (InfiltrationValueI4) + Dictionary zoneInfiltrationValueI4ByZoneName = + GetZoneInfiltrationValueI4ByZoneName(building); + + // Target EnergyPlus objects: ZoneInfiltration:DesignFlowRate + IEnumerable zoneInfiltrationDesignFlowObjects = + idf["ZONEINFILTRATION:DESIGNFLOWRATE"]; - foreach (IdfObject infiltration in infiltrationObjects) + foreach (IdfObject zoneInfilObj in zoneInfiltrationDesignFlowObjects) { - string zoneName = infiltration[1]; - infiltration["Design Flow Rate Calculation Method"].Value = "Flow/ExteriorWallArea"; - infiltration["Flow per Exterior Surface Area"].Value = (double.Parse(zoneInfiltrationRates[zoneName]) / 3600).ToString(); + string zoneName = zoneInfilObj[1]; + + // Force EnergyPlus to use exterior wall area-based infiltration. + zoneInfilObj["Design Flow Rate Calculation Method"].Value = "Flow/ExteriorWallArea"; + + // Convert from m3/h-m2 (DesignBuilder UI) to m3/sm2 (EnergyPlus field units) + zoneInfilObj["Flow per Exterior Surface Area"].Value = + (double.Parse(zoneInfiltrationValueI4ByZoneName[zoneName]) / 3600.0).ToString(); } - idfReader.Save(); + idf.Save(); } - private Dictionary GetZoneInfiltrationRates(Building building) + private Dictionary GetZoneInfiltrationValueI4ByZoneName(Building building) { - Dictionary zoneInfiltrationRates = building.BuildingBlocks + return building.BuildingBlocks .SelectMany(block => block.Zones) .ToDictionary( zone => zone.IdfName, zone => zone.GetAttribute("InfiltrationValueI4") ); - return zoneInfiltrationRates; } } -} +} \ No newline at end of file diff --git a/cs/applyLoopReturnSpm.cs b/cs/applyLoopReturnSpm.cs index e657f07..cdfaa9a 100644 --- a/cs/applyLoopReturnSpm.cs +++ b/cs/applyLoopReturnSpm.cs @@ -1,27 +1,56 @@ /* -Replace loop supply side "Scheduled" setpoint manager with the SetpointManager:ReturnTemperature:* equivalent. - -SetpointManager:Scheduled objects with: -- "CHW Return" key will be replaced by "SetpointManager:ReturnTemperature:ChilledWater" -- "HW Return" key will be replaced by "SetpointManager:ReturnTemperature:HotWater" - -The return setpoint manager follows the original suply side schedule. +Apply Loop Return Setpoint Manager Script (Plant Loops) + +This DesignBuilder C# script replaces loop supply-side SetpointManager:Scheduled objects (with specific name keys) +by creating the equivalent SetpointManager:ReturnTemperature:* object for plant loops. + +Purpose +1) Identify SetpointManager:Scheduled objects whose name contains: + - "CHW Return" -> create SetpointManager:ReturnTemperature:ChilledWater + - "HW Return" -> create SetpointManager:ReturnTemperature:HotWater +2) For each matched scheduled SPM: + - Read its NodeList reference + - Extract the supply outlet node name from the NodeList + - Infer supply inlet node name by replacing "Outlet" with "Inlet" + - Create and load the appropriate ReturnTemperature setpoint manager object into the IDF + - (Side-effect) Update NodeList to point to the inferred inlet node (see notes in code) + +How to Use + +Configuration +- Matching keys are controlled by the constants: + chilledWaterKey = "CHW Return" + hotWaterKey = "HW Return" + (matching is case-insensitive) +- Default minimum/maximum supply temperature limits are defined in the templates: + - CHW: 7°C min, 10°C max + - HW : 57°C min, 60°C max + Adjust these values in the templates if needed. + +Prerequisites (required placeholders) + +- The base model must include SetpointManager:Scheduled objects used as placeholders, + with Names containing either "CHW Return" or "HW Return". + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System; -using System.Runtime; using System.Linq; -using System.Windows.Forms; -using EpNet; using DB.Extensibility.Contracts; - +using EpNet; namespace DB.Extensibility.Scripts { public class ApplyLoopReturnSpm : ScriptBase, IScript { - private string hwReturnSpmTemplate = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // IDF templates for the two possible return-temperature setpoint managers. + // Adjust minimum/maximum supply temperature limits if needed. + private readonly string hotWaterReturnSpmTemplate = @" SetpointManager:ReturnTemperature:HotWater, {0}, !- Name {1}, !- Plant Loop Supply Outlet Node @@ -32,7 +61,7 @@ public class ApplyLoopReturnSpm : ScriptBase, IScript , !- Return Temperature Setpoint Constant Value ; !- Return Temperature Setpoint Schedule Name"; - private string chwReturnSpmTemplate = @" + private readonly string chilledWaterReturnSpmTemplate = @" SetpointManager:ReturnTemperature:ChilledWater, {0}, !- Name {1}, !- Plant Loop Supply Outlet Node @@ -43,67 +72,74 @@ public class ApplyLoopReturnSpm : ScriptBase, IScript , !- Return Temperature Setpoint Constant Value ; !- Return Temperature Setpoint Schedule Name"; - public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - // Define look-up keys, check is case agnostic - const string chwKey = "chw return"; - const string hwKey = "hw return"; + // Lookup keys used to decide whether a SetpointManager:Scheduled is a placeholder (case-insensitive). + const string chilledWaterKey = "CHW Return"; + const string hotWaterKey = "HW Return"; - ApplyReturnSpms(idfReader, chwKey, hwKey); + ApplyReturnSetpointManagers(idf, chilledWaterKey, hotWaterKey); - idfReader.Save(); + idf.Save(); } - private IdfObject FindObject(IdfReader reader, string objectType, string objectName) + private static IdfObject FindObject(IdfReader reader, string objectType, string objectName) { try { - return reader[objectType].First(c => c[0] == objectName); + return reader[objectType].First(o => o[0] == objectName); } - catch (Exception e) + catch { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private void ApplyReturnSpms(IdfReader reader, string chwKey, string hwKey) + private void ApplyReturnSetpointManagers(IdfReader reader, string chilledWaterKey, string hotWaterKey) { - var spms = reader["SetpointManager:Scheduled"]; - foreach (var spm in spms) + // Iterate through all scheduled setpoint managers and select those with matching keys. + var scheduledSpms = reader["SetpointManager:Scheduled"]; + + foreach (var scheduledSpm in scheduledSpms) { - string name = spm[0].Value; - string template = ""; - if (name.IndexOf(chwKey, StringComparison.OrdinalIgnoreCase) >= 0) + string scheduledSpmName = scheduledSpm[0].Value; + string returnSpmTemplate = null; + + if (scheduledSpmName.IndexOf(chilledWaterKey, StringComparison.OrdinalIgnoreCase) >= 0) { - template = chwReturnSpmTemplate; + returnSpmTemplate = chilledWaterReturnSpmTemplate; } - else if (name.IndexOf(hwKey, StringComparison.OrdinalIgnoreCase) >= 0) + else if (scheduledSpmName.IndexOf(hotWaterKey, StringComparison.OrdinalIgnoreCase) >= 0) { - template = hwReturnSpmTemplate; + returnSpmTemplate = hotWaterReturnSpmTemplate; } else { continue; } - string returnSpm = GetReturnSpm(reader, spm, template); - reader.Load(returnSpm); + + // Build the ReturnTemperature setpoint manager IDF snippet and load it into the model. + string returnSpmIdfText = BuildReturnSpm(reader, scheduledSpm, returnSpmTemplate); + reader.Load(returnSpmIdfText); } } - private string GetReturnSpm(IdfReader reader, IdfObject spm, string template) + private static string BuildReturnSpm(IdfReader reader, IdfObject scheduledSpm, string returnSpmTemplate) { - string nodeListName = spm[3].Value; + string nodeListName = scheduledSpm[3].Value; IdfObject nodeList = FindObject(reader, "NodeList", nodeListName); - string supplySideOutletNodeName = nodeList[1].Value; + string supplyOutletNodeName = nodeList[1].Value; + + string supplyInletNodeName = supplyOutletNodeName.Replace("Outlet", "Inlet"); + nodeList[1].Value = supplyInletNodeName; + string returnSpmName = "Main " + scheduledSpm[0].Value; - string supplySideInletNodeName = supplySideOutletNodeName.Replace("Outlet", "Inlet"); - nodeList[1].Value = supplySideInletNodeName; - return string.Format(template, "Main " + spm[0].Value, supplySideOutletNodeName, supplySideInletNodeName); + return string.Format(returnSpmTemplate, returnSpmName, supplyOutletNodeName, supplyInletNodeName); } } } \ No newline at end of file diff --git a/cs/applyMixedAirTerminal.cs b/cs/applyMixedAirTerminal.cs index f231cb6..c46afb8 100644 --- a/cs/applyMixedAirTerminal.cs +++ b/cs/applyMixedAirTerminal.cs @@ -1,53 +1,62 @@ /* -This DesignBuilder script automates upgrading EnergyPlus IDF files by replacing -AirTerminal:SingleDuct:ConstantVolume:NoReheat (CAV:NoReheat) terminals in zones -that also contain ZoneHVAC:TerminalUnit:VariableRefrigerantFlow (VRF) units. - -It substitutes them with AirTerminal:SingleDuct:Mixer objects to enable mixing -primary air (from central DOAS) with secondary VRF exhaust air at the zone inlet. +CAV to Mixer Replacement for Zones with VRF Terminal Units + +This DesignBuilder C# script modifies the EnergyPlus IDF by replacing AirTerminal:SingleDuct:ConstantVolume:NoReheat (CAV NoReheat) +terminals with AirTerminal:SingleDuct:Mixer terminals, in zones that contain a ZoneHVAC:TerminalUnit:VariableRefrigerantFlow (VRF terminal unit). + +Purpose (main steps) +The goal is to allow mixed air delivery at the zone inlet by combining: +- Primary air from a central system (e.g., DOAS) via the terminal inlet node +- Secondary air derived from the VRF terminal unit inlet node (treated here as the mixer secondary inlet) + +1) Scan ZoneHVAC:EquipmentList objects and find zones that include both: + - A ZoneHVAC:AirDistributionUnit referencing an AirTerminal:SingleDuct:ConstantVolume:NoReheat terminal + - A ZoneHVAC:TerminalUnit:VariableRefrigerantFlow terminal unit +2) For each matching zone: + - Create and insert an AirTerminal:SingleDuct:Mixer object + - Update ZoneHVAC:AirDistributionUnit to reference the Mixer terminal type + - Rewire the VRF cooling coil inlet node to the (old) terminal outlet node + - Remove the old CAV NoReheat terminal and outlet node from the zone inlet NodeList (if present) + +How to Use + +Configuration +- Target objects are detected by object types (constants at top of the script): + - Old terminal type: AirTerminal:SingleDuct:ConstantVolume:NoReheat + - New terminal type: AirTerminal:SingleDuct:Mixer + - Zone unit type: ZoneHVAC:TerminalUnit:VariableRefrigerantFlow + - Distribution unit type: ZoneHVAC:AirDistributionUnit + +Prerequisites (required placeholders) +Base model must contain, for each target zone: +- ZoneHVAC:EquipmentList that includes: + - ZoneHVAC:AirDistributionUnit (points to an AirTerminal:SingleDuct:ConstantVolume:NoReheat) + - ZoneHVAC:TerminalUnit:VariableRefrigerantFlow + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; using System; using System.Collections.Generic; using System.Linq; -using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript { - IdfReader Reader; - const string ditributionUnitType = "ZoneHVAC:AirDistributionUnit"; - const string oldTerminalType = "AirTerminal:SingleDuct:ConstantVolume:NoReheat"; - const string newTerminalType = "AirTerminal:SingleDuct:Mixer"; - const string zoneUnitType = "ZoneHVAC:TerminalUnit:VariableRefrigerantFlow"; + private IdfReader idf; - public struct ZoneEquipmentSpecification - { - public string ZoneEquipmentListName; - public string DistributionUnitName; - public string TerminalType; - public string TerminalName; - public string ZoneUnitType; - public string ZoneUnitName; - public string ZoneInletNodeListName - { - get - { - if (ZoneEquipmentListName != null) - return ZoneEquipmentListName.Replace(" Equipment", " Air Inlet Node List"); - return null; - } - } - } + // Object types used to identify targets and apply replacements + private const string DistributionUnitType = "ZoneHVAC:AirDistributionUnit"; + private const string OldTerminalType = "AirTerminal:SingleDuct:ConstantVolume:NoReheat"; + private const string NewTerminalType = "AirTerminal:SingleDuct:Mixer"; + private const string VrfTerminalUnitType = "ZoneHVAC:TerminalUnit:VariableRefrigerantFlow"; - // Boilerplate for new Mixer terminal - string terminalBoilerPlate = @" + // IDF template for the replacement Mixer terminal + private static readonly string MixerTerminalTemplate = @" AirTerminal:SingleDuct:Mixer, {0}, !- Name {1}, !- ZoneHVAC Unit Object Type @@ -61,71 +70,79 @@ public string ZoneInletNodeListName public override void BeforeEnergySimulation() { - Reader = new IdfReader( + idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - ApplyMixedAirTerminals(); - Reader.Save(); + ReplaceCavTerminalsWithMixers(); + + idf.Save(); } - // Main routine: replace terminals and update all linked objects - public void ApplyMixedAirTerminals() + // Main routine: find target zones and replace the CAV terminal with a Mixer terminal + private void ReplaceCavTerminalsWithMixers() { - // Identify the zones containing the old terminal (CAV:NoReheat) and a VRF terminal unit - List specs = FindZoneEquipment(oldTerminalType, zoneUnitType); - foreach (ZoneEquipmentSpecification spec in specs) + // Find zones that contain BOTH the old terminal (CAV:NoReheat) and a VRF terminal unit + List zoneSpecs = FindZonesWithCavAndVrf(OldTerminalType, VrfTerminalUnitType); + + foreach (ZoneEquipmentSpecification zoneSpec in zoneSpecs) { - IdfObject oldTerminal = FindObject(spec.TerminalType, spec.TerminalName); - IdfObject zoneUnit = FindObject(spec.ZoneUnitType, spec.ZoneUnitName); + IdfObject oldTerminal = FindObject(zoneSpec.TerminalType, zoneSpec.TerminalName); + IdfObject vrfTerminalUnit = FindObject(zoneSpec.ZoneUnitType, zoneSpec.ZoneUnitName); - string exhaustNodeName = zoneUnit["Terminal Unit Air Inlet Node Name"].Value; + string secondaryInletNodeName = vrfTerminalUnit["Terminal Unit Air Inlet Node Name"].Value; - // Build new Mixer object with required nodes - string mixerText = String.Format(terminalBoilerPlate, + // Build the new Mixer with required nodes + string mixerIdfText = String.Format( + MixerTerminalTemplate, oldTerminal["Name"].Value, - spec.ZoneUnitType, - spec.ZoneUnitName, + zoneSpec.ZoneUnitType, + zoneSpec.ZoneUnitName, oldTerminal["Air Outlet Node Name"].Value, oldTerminal["Air Inlet Node Name"].Value, - exhaustNodeName, + secondaryInletNodeName, oldTerminal["Design Specification Outdoor Air Object Name"].Value); - IdfObject distributionUnit = FindObject(ditributionUnitType, spec.DistributionUnitName); - distributionUnit["Air Terminal Object Type"].Value = newTerminalType; + IdfObject distributionUnit = FindObject(DistributionUnitType, zoneSpec.DistributionUnitName); + distributionUnit["Air Terminal Object Type"].Value = NewTerminalType; + + IdfObject coolingCoil = FindObject( + vrfTerminalUnit["Cooling Coil Object Type"].Value, + vrfTerminalUnit["Cooling Coil Object Name"].Value); - IdfObject coolingCoil = FindObject(zoneUnit["Cooling Coil Object Type"].Value, zoneUnit["Cooling Coil Object Name"].Value); - coolingCoil["Coil Air Inlet Node"].Value = oldTerminal["Air Outlet Node Name"]; + coolingCoil["Coil Air Inlet Node"].Value = oldTerminal["Air Outlet Node Name"].Value; - IdfObject zoneNodeList = FindObject("NodeList", spec.ZoneInletNodeListName); - RemoveTerminalOutletNode(zoneNodeList, oldTerminal["Air Outlet Node Name"].Value); + IdfObject zoneInletNodeList = FindObject("NodeList", zoneSpec.ZoneInletNodeListName); + RemoveNodeFromNodeList(zoneInletNodeList, oldTerminal["Air Outlet Node Name"].Value); - // Insert Mixer and remove old terminal - Reader.Load(mixerText); - Reader.Remove(oldTerminal); + // Insert mixer then remove old terminal + idf.Load(mixerIdfText); + idf.Remove(oldTerminal); - zoneUnit["Terminal Unit Air Inlet Node Name"].Value = oldTerminal["Air Outlet Node Name"].Value; + // Update VRF terminal unit inlet node reference to align with the new arrangement + vrfTerminalUnit["Terminal Unit Air Inlet Node Name"].Value = oldTerminal["Air Outlet Node Name"].Value; } } - public IdfObject FindObject(string objectType, string objectName) + private IdfObject FindObject(string objectType, string objectName) { try { - return Reader[objectType].First(c => c[0] == objectName); + return idf[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new MissingFieldException( + String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - // Remove the terminal outlet node from the zone inlet - public void RemoveTerminalOutletNode(IdfObject nodeList, string terminalOutletNodeName) + // Remove a specific node name from a NodeList (if present) + private void RemoveNodeFromNodeList(IdfObject nodeList, string nodeName) { for (int i = 0; i < nodeList.Fields.Count; i++) { - if (nodeList[i].Value == terminalOutletNodeName) + if (nodeList[i].Value == nodeName) { nodeList.RemoveField(i); break; @@ -133,55 +150,88 @@ public void RemoveTerminalOutletNode(IdfObject nodeList, string terminalOutletNo } } + // Holds the names of the relevant objects found for a given zone + public struct ZoneEquipmentSpecification + { + public string ZoneEquipmentListName; + public string DistributionUnitName; - // Filter Zone equipments (terminal to be replaced + VRF terminal) - public List FindZoneEquipment(string terminalType, string zoneUnitType) + public string TerminalType; + public string TerminalName; + + public string ZoneUnitType; + public string ZoneUnitName; + + // Assumes NodeList naming convention derived from the equipment list name + public string ZoneInletNodeListName + { + get + { + if (ZoneEquipmentListName != null) + return ZoneEquipmentListName.Replace(" Equipment", " Air Inlet Node List"); + return null; + } + } + } + + // Scan ZoneHVAC:EquipmentList and return zones that contain both: + // - a distribution unit whose air terminal type matches terminalType + // - a zone unit object whose type matches zoneUnitType (VRF terminal unit) + private List FindZonesWithCavAndVrf(string terminalType, string zoneUnitType) { List specifications = new List(); - IEnumerable allZoneEquipment = Reader["ZoneHVAC:EquipmentList"]; - foreach (IdfObject zoneEquipment in allZoneEquipment) + + IEnumerable allZoneEquipmentLists = idf["ZoneHVAC:EquipmentList"]; + foreach (IdfObject zoneEquipmentList in allZoneEquipmentLists) { - int i = 0; - string zoneEquipmentListName = ""; - string terminalName = ""; - string zoneUnitName = ""; - string distributionUnitName = ""; + string terminalName = null; + string zoneUnitName = null; + string distributionUnitName = null; + bool includesTerminal = false; bool includesZoneUnit = false; - foreach (var field in zoneEquipment.Fields) + + for (int i = 0; i < zoneEquipmentList.Fields.Count - 1; i++) { - if (field.Equals(ditributionUnitType)) + string fieldValue = zoneEquipmentList[i].Value; + + // Detect distribution unit entry + if (String.Equals(fieldValue, DistributionUnitType, StringComparison.OrdinalIgnoreCase)) { - distributionUnitName = zoneEquipment[i + 1].Value; - IdfObject distributionUnit = FindObject(ditributionUnitType, distributionUnitName); + distributionUnitName = zoneEquipmentList[i + 1].Value; + + IdfObject distributionUnit = FindObject(DistributionUnitType, distributionUnitName); string airTerminalType = distributionUnit["Air Terminal Object Type"].Value; - if (airTerminalType == terminalType) + + if (String.Equals(airTerminalType, terminalType, StringComparison.OrdinalIgnoreCase)) { terminalName = distributionUnit["Air Terminal Name"].Value; includesTerminal = true; } } - if (field.Equals(zoneUnitType)) + + // Detect VRF terminal unit entry + if (String.Equals(fieldValue, zoneUnitType, StringComparison.OrdinalIgnoreCase)) { - zoneUnitName = zoneEquipment[i + 1].Value; + zoneUnitName = zoneEquipmentList[i + 1].Value; includesZoneUnit = true; } - i++; } + if (includesTerminal && includesZoneUnit) { - ZoneEquipmentSpecification spec = new ZoneEquipmentSpecification + specifications.Add(new ZoneEquipmentSpecification { - ZoneEquipmentListName = zoneEquipment[0].Value, + ZoneEquipmentListName = zoneEquipmentList[0].Value, DistributionUnitName = distributionUnitName, TerminalType = terminalType, TerminalName = terminalName, ZoneUnitType = zoneUnitType, ZoneUnitName = zoneUnitName - }; - specifications.Add(spec); + }); } } + return specifications; } } diff --git a/cs/applyNightCycleFCUs.cs b/cs/applyNightCycleFCUs.cs index 8610d63..8638f6f 100644 --- a/cs/applyNightCycleFCUs.cs +++ b/cs/applyNightCycleFCUs.cs @@ -1,14 +1,36 @@ /* -This script adds the "Night Cycle Operation" control for zone fan-coil units. +Zone Fan-Coil Night Cycle Operation (AvailabilityManager:NightCycle) + +Purpose +This DesignBuilder C# script enables Night Cycle Operation control for zone fan-coil units +(ZoneHVAC:FourPipeFanCoil) by adding an AvailabilityManager:NightCycle and assignment list. DesignBuilder automatically applies the unit availability schedule to the child fan. This setup would not work well for night cycle as the manager overrides only the fan availability. -To allow the night cycling, the script forces the unit schedule to be "ON 24/7". +Purpose + +1) Find all ZoneHVAC:FourPipeFanCoil objects in the IDF. +2) For each fan-coil unit: + - Assign an AvailabilityManagerAssignmentList to the unit. + - Force the unit Availability Schedule Name to "On 24/7" (so the availability manager can drive operation). + - Read the fan’s Availability Schedule Name and use it in the AvailabilityManager:NightCycle object. +3) Load the generated IDF objects and save the modified IDF. + +How to Use + +Configuration +- Constant schedule name used to force unit availability ("On 24/7"). +- Control zone name derivation: + - The script derives the control zone name from the fan-coil name by removing " Fan Coil Unit". + +Prerequisites (required placeholders) +- The IDF must contain one or more ZoneHVAC:FourPipeFanCoil objects. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System; -using System.Runtime; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,15 +39,19 @@ DesignBuilder automatically applies the unit availability schedule to the child namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + public class FanCoilNightCycleOperation : ScriptBase, IScript { - string nightCycleObjects = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // IDF template for AvailabilityManagerAssignmentList and AvailabilityManager:NightCycle + private static readonly string NightCycleIdfTemplate = @" AvailabilityManagerAssignmentList, {0}, !- Name AvailabilityManager:NightCycle, !- Availability Manager 1 Object Type {1} Night Cycle Operation; !- Availability Manager 1 Name - AvailabilityManager:NightCycle, +AvailabilityManager:NightCycle, {1} Night Cycle Operation, !- Name On 24/7, !- Applicability Schedule Name {3}, !- Fan Schedule Name @@ -33,17 +59,18 @@ public class IdfFindAndReplace : ScriptBase, IScript 1, !- Thermostat Tolerance deltaC FixedRunTime, !- Cycling Run Time Control Type 3600, !- Cycling Run Time s - {2}; !- Control zone name"; + {2}; !- Control zone name +"; - public IdfObject FindObject(IdfReader reader, string objectType, string objectName) + private IdfObject FindObject(IdfReader reader, string objectType, string objectName) { try { - return reader[objectType].First(c => c[0] == objectName); + return reader[objectType].First(o => o[0] == objectName); } - catch (Exception e) + catch { - throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); + throw new Exception(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } @@ -60,20 +87,35 @@ public override void BeforeEnergySimulation() { // Add assignment list and nigt cycle manager to the idf string fanCoilName = fanCoil["Name"].Value; - string name = fanCoilName + " Assignment List"; + string assignmentListName = fanCoilName + " Assignment List"; string zoneName = fanCoilName.Replace(" Fan Coil Unit", ""); - fanCoil.AddField(name, "! - Availability Manager List Name"); + + // Attach the availability manager list to the fan-coil unit + fanCoil.AddField(assignmentListName, "!- Availability Manager List Name"); + + // Force the unit schedule to always available so Night Cycle can control the fan operation fanCoil["Availability Schedule Name"].Value = "On 24/7"; - // Get fan availability - IdfObject fan = FindObject(idfReader, fanCoil["Supply Air Fan Object Type"].Value, fanCoil["Supply Air Fan Name"].Value); - string fanSchedule = fan["Availability Schedule Name"]; + // Get fan object and its availability schedule (used by AvailabilityManager:NightCycle) + IdfObject fan = FindObject( + idfReader, + fanCoil["Supply Air Fan Object Type"].Value, + fanCoil["Supply Air Fan Name"].Value); + + string fanScheduleName = fan["Availability Schedule Name"].Value; - idfContent.AppendFormat(nightCycleObjects, name, fanCoilName, zoneName, fanSchedule); - idfContent.Append(Environment.NewLine); + idfContent.AppendFormat( + NightCycleIdfTemplate, + assignmentListName, + fanCoilName, + zoneName, + fanScheduleName); + + idfContent.AppendLine(); } + idfReader.Load(idfContent.ToString()); idfReader.Save(); } } -} +} \ No newline at end of file diff --git a/cs/checkProposed.cs b/cs/checkProposed.cs index 9fd1571..287ce96 100644 --- a/cs/checkProposed.cs +++ b/cs/checkProposed.cs @@ -1,11 +1,22 @@ -/* -Check if currently simulated buidling is Proposed or Baseline. +/* +Check Proposed vs Baseline (ASHRAE 90.1) for the Active Building -This can be used apply a script only to specific building. +Purpose +- Determine whether the currently simulated building is the ASHRAE 90.1 Proposed case or Baseline case. +- NOTE: This can be used to conditionally apply other script actions only to specific building variants. +How to Use + +Configuration +- Attribute key checked: "ASHRAE901Type" +- Proposed value expected: "1-Proposed" + +Prerequisites +- The model must be configured to to an ASHRAE 90.1 Model (Proposed/ Baseline vesrions) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Collections.Generic; using System.Windows.Forms; using System; using DB.Api; @@ -17,12 +28,14 @@ public class CheckProposed : ScriptBase, IScript { private bool IsCurrentProposed() { - string ashraeType = ActiveBuilding.GetAttribute("ASHRAE901Type"); - return ashraeType == "1-Proposed"; + // Read ASHRAE 90.1 case type from the Active Building attribute + string ashrae901Type = ActiveBuilding.GetAttribute("ASHRAE901Type"); + return ashrae901Type == "1-Proposed"; } public override void BeforeEnergyIdfGeneration() { + // Hook executed before EnergyPlus IDF generation; use this to gate other script actions if (IsCurrentProposed()) { MessageBox.Show("Proposed"); diff --git a/cs/dlControloff.cs b/cs/dlControloff.cs index 1342306..3b2a8f7 100644 --- a/cs/dlControloff.cs +++ b/cs/dlControloff.cs @@ -1,7 +1,33 @@ -/* -Override Daylighting controls schedule to disable daylighting control for sizing calculations. +/* +Daylighting Controls Sizing Override (Disable Daylighting on Cooling Design Days) +Purpose +This DesignBuilder C# script disables daylighting controls for sizing-related calculations by +overriding the Availability Schedule referenced by all Daylighting:Controls objects. + +1) Find all Daylighting:Controls objects +2) Force their "Availability Schedule Name" to a custom schedule (default: "OnSddOff") +3) Inject a Schedule:Compact object named "OnSddOff" that: + - Sets availability to 0 during SummerDesignDay (daylighting disabled for sizing design day) + - Sets availability to 1 for all other days (daylighting enabled for normal simulation periods) +4) Save the modified IDF back to disk before EnergyPlus runs + +How to Use +- Add this script to your DesignBuilder model as a C# extensibility script. +- The script runs in these hooks: + - BeforeEnergySimulation() + - BeforeCoolingSimulation() + This ensures the IDF is updated before the relevant simulation phases that may include sizing. + +Configuration +- Schedule name: "OnSddOff" + +Prerequisites +- The model/IDF must contain one or more Daylighting:Controls objects. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ + using System.Collections.Generic; using System.Windows.Forms; using DB.Extensibility.Contracts; @@ -17,16 +43,31 @@ private void UpdateDlSchedule() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - IEnumerable dlControls = idfReader["Daylighting:Controls"]; + // Target all daylighting control objects and override their availability schedule reference. + IEnumerable daylightingControls = idfReader["Daylighting:Controls"]; - MessageBox.Show("Updating daylighting control availability"); + MessageBox.Show("Updating daylighting control availability"); // Comment this line to disable the message box. - foreach (IdfObject dlControl in dlControls) + // Force each daylighting control object to use the override schedule. + foreach (IdfObject daylightingControl in daylightingControls) { - dlControl["Availability Schedule Name"].Value = "OnSddOff"; + daylightingControl["Availability Schedule Name"].Value = "OnSddOff"; } - idfReader.Load("Schedule:Compact,\nOnSddOff,\nFraction,\nThrough: 12/31,\nFor: SummerDesignDay,\nUntil: 24:00,\n0,\nFor: AllOtherDays,\nUntil: 24:00,\n1;"); + // "OnSddOff" is defined as: 0 on SummerDesignDay, 1 on all other days. + const string onSddOffScheduleCompact = + "Schedule:Compact,\n" + + "OnSddOff,\n" + + "Fraction,\n" + + "Through: 12/31,\n" + + "For: SummerDesignDay,\n" + + "Until: 24:00,\n" + + "0,\n" + + "For: AllOtherDays,\n" + + "Until: 24:00,\n" + + "1;"; + + idfReader.Load(onSddOffScheduleCompact); idfReader.Save(); } @@ -41,5 +82,4 @@ public override void BeforeCoolingSimulation() UpdateDlSchedule(); } } - } \ No newline at end of file diff --git a/cs/energyRecoveryVentilator.cs b/cs/energyRecoveryVentilator.cs index d8aa156..1dfe251 100644 --- a/cs/energyRecoveryVentilator.cs +++ b/cs/energyRecoveryVentilator.cs @@ -1,8 +1,31 @@ /* -Replace all Fan:ZoneExhaust with ZoneHVAC:EnergyRecoveryVentilators (ERV). +Replace all zone exhaust fans with ERVs. -ERV availability schedule and nominal air flow is read from Zone exhaust fan inputs in DesignBuilder. -Remaining attributes can be set in 'boilerplate' IDF text below. +This DesignBuilder C# script edits the IDF to replace Fan:ZoneExhaust objects with ZoneHVAC:EnergyRecoveryVentilator (ERV) systems. + +Purpose + +1) Find all Fan:ZoneExhaust objects that are referenced inside ZoneHVAC:EquipmentList +2) Create a ZoneHVAC:EnergyRecoveryVentilator, its Controller, two Fan:SystemModel objects, and a HeatExchanger object per exhaust fan +3) Adjust the required NodeList +4) Replace the equipment reference in ZoneHVAC:EquipmentList from Fan:ZoneExhaust to ZoneHVAC:EnergyRecoveryVentilator +5) Remove the original Fan:ZoneExhaust objects from the IDF +6) Add a scheduled SetpointManager + NodeList and populating it with all heat exchanger supply outlet nodes + +How to Use + +Configuration +- Configure ERV parameters by editing the 'boilerplate' IDF text below: + - ervBoilerplateIdf: ERV, controller, fans, heat exchanger performance/controls + - spmBoilerplateIdf: setpoint manager / schedule / node list for HX outlet nodes +- ERV availability schedule and nominal air flow is read from Zone exhaust fan inputs in DesignBuilder. + +Prerequisites (required placeholders) + +- Ensure your model contains one or more Fan:ZoneExhaust objects assigned to zones +- The availability schedule and nominal air flow defined in the Fan:ZoneExhaust via user interface is applied in the ERV + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -13,11 +36,14 @@ Remaining attributes can be set in 'boilerplate' IDF text below. using EpNet; namespace DB.Extensibility.Scripts - { public class IdfFindAndReplace : ScriptBase, IScript { - string ervBoilerPlate = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF used to create ERV + controller + fans + heat exchanger. + string ervBoilerplateIdf = @" ZoneHVAC:EnergyRecoveryVentilator, {0}, !- Name {1}, !- Availability Schedule Name @@ -99,14 +125,18 @@ public class IdfFindAndReplace : ScriptBase, IScript MinimumExhaustTemperature, !- Frost Control Type 1.7; !- Threshold Temperature"; - string spmBoilerPlate = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF used to add a setpoint manager/schedule and a NodeList to collect HX outlet nodes. + string spmBoilerplateIdf = @" SetpointManager:Scheduled, Heat Exchanger Supply Air Temp Manager, !- Name Temperature, !- Control Variable Heat Exchanger Supply Air Temp Sch, !- Schedule Name {0}; !- Setpoint Node or NodeList Name - NodeList, +NodeList, {0}; !- Name Schedule:Compact, @@ -121,6 +151,7 @@ private IdfObject FindObject(IdfReader idfReader, string objectType, string obje return idfReader[objectType].First(o => o[0] == objectName); } + // Scan ZoneHVAC:EquipmentList for entries of a given object type and return the referenced objects. private List FindObjectsInZoneEquipment(IdfReader idfReader, string objectType) { List objects = new List(); @@ -144,6 +175,7 @@ private List FindObjectsInZoneEquipment(IdfReader idfReader, string o return objects; } + // Replace the matching (type,name) pairs in ZoneHVAC:EquipmentList. private void ReplaceObjectsInZoneEquipment(IdfReader idfReader, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) { IEnumerable allZoneEquipment = idfReader["ZoneHVAC:EquipmentList"]; @@ -171,19 +203,20 @@ private void ReplaceObjectsInZoneEquipment(IdfReader idfReader, string oldObject } } - private List FindNodes(IdfReader idfReader, string objectName, string fieldName) + // Collect node names from objects of a given type using the given field name. + private List FindNodes(IdfReader idfReader, string objectType, string fieldName) { List nodes = new List(); - IEnumerable idfObjects = idfReader[objectName]; + IEnumerable idfObjects = idfReader[objectType]; foreach (IdfObject idfObject in idfObjects) { - string nodeName = idfObject[fieldName]; + string nodeName = idfObject[fieldName].Value; if (nodeName.EndsWith("List")) { IdfObject nodeList = idfReader["NodeList"].First(item => item[0] == nodeName); - nodeName = nodeList[1]; + nodeName = nodeList[1].Value; } nodes.Add(nodeName); } @@ -196,6 +229,7 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // Target selection: only Fan:ZoneExhaust objects referenced in ZoneHVAC:EquipmentList are converted. IEnumerable exhaustFans = FindObjectsInZoneEquipment(idfReader, "Fan:ZoneExhaust"); string oldObjectType = "Fan:ZoneExhaust"; @@ -203,36 +237,55 @@ public override void BeforeEnergySimulation() foreach (var exhaustFan in exhaustFans) { - string name = exhaustFan[0].Value; - string schedule = exhaustFan[1].Value; - string flowRate = exhaustFan[4].Value; - string zoneExhaustNode = exhaustFan[5].Value; - string zoneName = name.Split(' ')[0]; - string ervName = name.Split(' ')[0] + " ERV"; + // Read key inputs from the exhaust fan placeholder. + string exhaustFanName = exhaustFan[0].Value; + string availabilityScheduleName = exhaustFan[1].Value; + string nominalFlowRate = exhaustFan[4].Value; + string zoneExhaustAirNode = exhaustFan[5].Value; + string zoneName = exhaustFanName.Split(' ')[0]; + + // Create new ERV and related nodes using a predictable naming convention. + string ervName = zoneName + " ERV"; string zoneInletNode = ervName + " Supply Fan Outlet Node"; - string ervOANode = ervName + " Supply ERV Inlet Node"; + string ervOutdoorAirInletNode = ervName + " Supply ERV Inlet Node"; + + // Add ERV + controller + fans + HX to the IDF. + string ervIdfText = String.Format( + ervBoilerplateIdf, + ervName, + availabilityScheduleName, + nominalFlowRate, + zoneExhaustAirNode, + zoneInletNode, + ervOutdoorAirInletNode); - string newComponents = String.Format(ervBoilerPlate, ervName, schedule, flowRate, zoneExhaustNode, zoneInletNode, ervOANode); - idfReader.Load(newComponents); + idfReader.Load(ervIdfText); - IdfObject oaNodes = idfReader["OutdoorAir:NodeList"][0]; - oaNodes.AddField(ervOANode); + // Ensure the ERV OA inlet node is included in the global outdoor air node list. + IdfObject outdoorAirNodeList = idfReader["OutdoorAir:NodeList"][0]; + outdoorAirNodeList.AddField(ervOutdoorAirInletNode); - IdfObject nodeList = FindObject(idfReader, "NodeList", zoneName + " Air Inlet Node List"); - nodeList.AddField(zoneInletNode); + // Add the ERV supply outlet node to the zone air inlet node list. + IdfObject zoneAirInletNodeList = FindObject(idfReader, "NodeList", zoneName + " Air Inlet Node List"); + zoneAirInletNodeList.AddField(zoneInletNode); - ReplaceObjectsInZoneEquipment(idfReader, oldObjectType, exhaustFan[0], newObjectType, ervName); + ReplaceObjectsInZoneEquipment(idfReader, oldObjectType, exhaustFan[0].Value, newObjectType, ervName); + // Remove the original exhaust fan object from the IDF. idfReader.Remove(exhaustFan); } - string nodeListName = "ERV HR Outlets"; - string spmObjects = String.Format(spmBoilerPlate, nodeListName); + // Add setpoint manager + schedule + node list for HX outlet nodes. + string ervOutletNodeListName = "ERV HR Outlets"; + string spmObjects = String.Format(spmBoilerplateIdf, ervOutletNodeListName); idfReader.Load(spmObjects); - List outletNodes = FindNodes(idfReader, "HeatExchanger:AirToAir:SensibleAndLatent", "Supply Air Outlet Node Name"); + List heatExchangerSupplyOutletNodes = FindNodes( + idfReader, + "HeatExchanger:AirToAir:SensibleAndLatent", + "Supply Air Outlet Node Name"); - IdfObject ervNodeList = FindObject(idfReader, "NodeList", nodeListName); - ervNodeList.AddFields(outletNodes.ToArray()); + IdfObject ervOutletNodeList = FindObject(idfReader, "NodeList", ervOutletNodeListName); + ervOutletNodeList.AddFields(heatExchangerSupplyOutletNodes.ToArray()); idfReader.Save(); } diff --git a/cs/flatPlateHeatExchanger.cs b/cs/flatPlateHeatExchanger.cs index 1765338..a1b1b06 100644 --- a/cs/flatPlateHeatExchanger.cs +++ b/cs/flatPlateHeatExchanger.cs @@ -1,7 +1,34 @@ /* -Replace HeatExchanger:AirToAir:SensibleAndLatent with HeatExchanger:AirToAir:FlatPlate. -Object name needs to reference the HeatExchanger:AirToAir:SensibleAndLatent object name. -Attributes can be set in 'boilerplate' IDF text below. +Heat Exchanger Conversion Script (Sensible+Latent to Flat Plate) + +This DesignBuilder C# script replaces specified EnergyPlus HeatExchanger:AirToAir:SensibleAndLatent objects +with HeatExchanger:AirToAir:FlatPlate objects, while keeping the same object Name. + +Purpose + +1) Locate each target HeatExchanger:AirToAir:SensibleAndLatent object by name +2) Update AirLoopHVAC:OutdoorAirSystem:EquipmentList references for the HeatExchanger:AirToAir:* from SensibleAndLatent to FlatPlate +3) Create a new HeatExchanger:AirToAir:FlatPlate object using a boilerplate IDF template + (with key attributes copied from the original HX) +4) Remove the original HX object and loading the new FlatPlate object into the IDF + +How to Use + +Configuration +- targetHeatExchangerNames: + List of exact object names to convert (must match the IDF "Name" field exactly). +- flatPlateBoilerplateIdf: + IDF text template for HeatExchanger:AirToAir:FlatPlate. + Placeholders {0}..{8} are populated from the original HX object fields via the user interface. + +Prerequisites (required placeholders) + +Base model must contain, for each target name: + - A HeatExchanger:AirToAir:SensibleAndLatent object with that exact Name + - The original HX object must have values for fields read by this script: + Availability Schedule Name, Economizer Lockout, Nominal Supply Air Flow Rate, Nominal Electric Power, and the four node names. + + DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -12,12 +39,21 @@ Attributes can be set in 'boilerplate' IDF text below. using EpNet; namespace DB.Extensibility.Scripts - { - public class IdfFindAndReplace : ScriptBase, IScript - { - string[] objectNames = new string[] { "Air Loop AHU Heat Recovery Device" }; - string hxBoilerPlate = @" + public class IdfFindAndReplace : ScriptBase, IScript + { + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // List of HeatExchanger:AirToAir:SensibleAndLatent object names to convert + string[] targetHeatExchangerNames = new string[] { "Air Loop AHU Heat Recovery Device" }; + + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF template for the replacement HeatExchanger:AirToAir:FlatPlate object. + // Edit fixed attributes here if needed; placeholders {0}..{8} are filled from the original HX. + string flatPlateBoilerplateIdf = @" HeatExchanger:AirToAir:FlatPlate, {0}, !- Name {1}, !- Availability Schedule Name @@ -35,71 +71,99 @@ public class IdfFindAndReplace : ScriptBase, IScript {7}, !- Secondary Air Inlet Node Name {8}; !- Secondary Air Outlet Node Name"; + private IdfObject FindObject(IdfReader idfReader, string objectType, string objectName) + { + return idfReader[objectType].First(o => o[0] == objectName); + } - private IdfObject FindObject(IdfReader idfReader, string objectType, string objectName) - { - return idfReader[objectType].First(o => o[0] == objectName); - } + private void ReplaceObjectTypeInList( + IdfReader idfReader, + string listName, + string oldObjectType, + string oldObjectName, + string newObjectType, + string newObjectName) + { + // Updates an object type+name pair inside a list object (e.g., EquipmentList) + IEnumerable allEquipment = idfReader[listName]; - private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) - { - IEnumerable allEquipment = idfReader[listName]; + bool objectFound = false; - bool objectFound = false; + foreach (IdfObject equipment in allEquipment) + { + if (!objectFound) + { + for (int i = 0; i < (equipment.Count - 1); i++) + { + Field field = equipment[i]; + Field nextField = equipment[i + 1]; + + if (field.Value == oldObjectType && nextField.Value == oldObjectName) + { + field.Value = newObjectType; + nextField.Value = newObjectName; + objectFound = true; + break; + } + } + } + } + } - foreach (IdfObject equipment in allEquipment) - { - if (!objectFound) + public override void BeforeEnergySimulation() { - for (int i = 0; i < (equipment.Count - 1); i++) - { - Field field = equipment[i]; - Field nextField = equipment[i + 1]; + IdfReader idfReader = new IdfReader( + ApiEnvironment.EnergyPlusInputIdfPath, + ApiEnvironment.EnergyPlusInputIddPath + ); + + string oldObjectType = "HeatExchanger:AirToAir:SensibleAndLatent"; + string newObjectType = "HeatExchanger:AirToAir:FlatPlate"; - if (field.Value == oldObjectType && nextField.Value == oldObjectName) + foreach (string objectName in targetHeatExchangerNames) { - field.Value = newObjectType; - nextField.Value = newObjectName; - objectFound = true; - break; + // 1) Find the existing Sensible+Latent HX (must exist as a placeholder) + IdfObject oldHx = FindObject(idfReader, oldObjectType, objectName); + + // 2) Update the Outdoor Air System equipment list to reference FlatPlate instead + ReplaceObjectTypeInList( + idfReader, + "AirLoopHVAC:OutdoorAirSystem:EquipmentList", + oldObjectType, + objectName, + newObjectType, + objectName); + + // 3) Copy required fields from old HX to populate the FlatPlate template + string name = oldHx["Name"].Value; + string availabilityScheduleName = oldHx["Availability Schedule Name"].Value; + string economizerLockout = oldHx["Economizer Lockout"].Value; + string nominalSupplyAirFlowRate = oldHx["Nominal Supply Air Flow Rate"].Value; + string nominalElectricPower = oldHx["Nominal Electric Power"].Value; + + string supplyInletNode = oldHx["Supply Air Inlet Node Name"].Value; + string supplyOutletNode = oldHx["Supply Air Outlet Node Name"].Value; + string exhaustInletNode = oldHx["Exhaust Air Inlet Node Name"].Value; + string exhaustOutletNode = oldHx["Exhaust Air Outlet Node Name"].Value; + + // 4) Create FlatPlate IDF text and replace the object in the IDF + string newFlatPlateIdf = String.Format( + flatPlateBoilerplateIdf, + name, + availabilityScheduleName, + economizerLockout, + nominalSupplyAirFlowRate, + nominalElectricPower, + supplyInletNode, + supplyOutletNode, + exhaustInletNode, + exhaustOutletNode); + + idfReader.Remove(oldHx); + idfReader.Load(newFlatPlateIdf); } - } - } - } - } - public override void BeforeEnergySimulation() - { - IdfReader idfReader = new IdfReader( - ApiEnvironment.EnergyPlusInputIdfPath, - ApiEnvironment.EnergyPlusInputIddPath - ); - - string oldObjectType = "HeatExchanger:AirToAir:SensibleAndLatent"; - string newObjectType = "HeatExchanger:AirToAir:FlatPlate"; - - foreach (string objectName in objectNames) - { - IdfObject hx = FindObject(idfReader, oldObjectType, objectName); - - ReplaceObjectTypeInList(idfReader, "AirLoopHVAC:OutdoorAirSystem:EquipmentList", oldObjectType, objectName, newObjectType, objectName); - - string name = hx["Name"].Value; - string availability = hx["Availability Schedule Name"].Value; - string lockout = hx["Economizer Lockout"].Value; - string flowRate = hx["Nominal Supply Air Flow Rate"].Value; - string electricPower = hx["Nominal Electric Power"].Value; - string supplyInletNode = hx["Supply Air Inlet Node Name"].Value; - string supplyOutletNode = hx["Supply Air Outlet Node Name"].Value; - string extractInletNode = hx["Exhaust Air Inlet Node Name"].Value; - string extractOutletNode = hx["Exhaust Air Outlet Node Name"].Value; - - string flatPlate = String.Format(hxBoilerPlate, name, availability, lockout, flowRate, electricPower, supplyInletNode, supplyOutletNode, extractInletNode, extractOutletNode); - - idfReader.Remove(hx); - idfReader.Load(flatPlate); - } - idfReader.Save(); + idfReader.Save(); + } } - } } \ No newline at end of file diff --git a/cs/gasFiredChiller.cs b/cs/gasFiredChiller.cs index 8b357ca..7f48bc7 100644 --- a/cs/gasFiredChiller.cs +++ b/cs/gasFiredChiller.cs @@ -1,13 +1,38 @@ /* -Replace District Heating and District Cooling source plant components with ChillerHeater:Absorption:DirectFired object. +Direct-Fired Absorption Chiller-Heater Replacement Script (District Heating/Cooling) -ChillerHeater properties can be adjusted in the string boilerplate. +This DesignBuilder C# script replaces DistrictHeating and DistrictCooling plant components with a ChillerHeater:Absorption:DirectFired object. - */ +Purpose + +1) Replace references to DistrictHeating and DistrictCooling in Branch and PlantEquipmentList + so they point to ChillerHeater:Absorption:DirectFired with the specified chiller name +2) Read node names from the DistrictHeating and DistrictCooling placeholder objects +3) Insert a boilerplate ChillerHeater:Absorption:DirectFired object (plus required curves and OA node list) +4) Remove the original DistrictHeating and DistrictCooling placeholder objects from the IDF +5) Save the modified IDF + +How to Use + +Configuration (edit values in BeforeEnergySimulation) + +- chillerName: name assigned to the new ChillerHeater:Absorption:DirectFired object +- districtHeatingName: placeholder DistrictHeating object name to be replaced +- districtCoolingName: placeholder DistrictCooling object name to be replaced +- hwLoopName / chwLoopName: currently not used by the script logic (kept as reference) + +ChillerHeater properties (edit boilerplate in GetChillerHeaterIdfObjects) +- Adjust performance ratios, temperatures, curves, and any autosizing fields inside the IDF template string. + +Prerequisites (required placeholder objects) +The base model must contain: +- A DistrictHeating object named exactly as districtHeatingName (default: "District Heating") +- A DistrictCooling object named exactly as districtCoolingName (default: "District Cooling") + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. +*/ -using System.Runtime; using System; -using System.Collections.Generic; using System.Linq; using DB.Extensibility.Contracts; using EpNet; @@ -16,17 +41,21 @@ namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - private IdfReader Reader; + private IdfReader idfReader; public override void BeforeEnergySimulation() { - Reader = new IdfReader( + idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Ensure these names match objects in the model/IDF const string chillerName = "Big Chiller"; - const string hwLoopName = "HW Loop"; - const string chwLoopName = "CHW Loop"; + const string hwLoopName = "HW Loop"; // Not used in current logic + const string chwLoopName = "CHW Loop"; // Not used in current logic const string districtHeatingName = "District Heating"; const string districtCoolingName = "District Cooling"; @@ -37,7 +66,7 @@ public override void BeforeEnergySimulation() districtHeatingName, districtCoolingName); - Reader.Save(); + idfReader.Save(); } public void ApplyDirectFiredChiller( @@ -51,18 +80,14 @@ public void ApplyDirectFiredChiller( const string heatingPlaceholderObjectType = "DistrictHeating"; const string coolingPlaceholderObjectType = "DistrictCooling"; - // update placeholder list and branch references - ReplaceObjectTypeInList("Branch", heatingPlaceholderObjectType, districtHeatingName, chillerType, - chillerName); - ReplaceObjectTypeInList("PlantEquipmentList", heatingPlaceholderObjectType, districtHeatingName, - chillerType, chillerName); + // Replace all Branch and PlantEquipmentList references so the plant now points to the new chiller-heater. + ReplaceObjectTypeInList("Branch", heatingPlaceholderObjectType, districtHeatingName, chillerType, chillerName); + ReplaceObjectTypeInList("PlantEquipmentList", heatingPlaceholderObjectType, districtHeatingName, chillerType, chillerName); - ReplaceObjectTypeInList("Branch", coolingPlaceholderObjectType, districtCoolingName, chillerType, - chillerName); - ReplaceObjectTypeInList("PlantEquipmentList", coolingPlaceholderObjectType, districtCoolingName, - chillerType, chillerName); + ReplaceObjectTypeInList("Branch", coolingPlaceholderObjectType, districtCoolingName, chillerType, chillerName); + ReplaceObjectTypeInList("PlantEquipmentList", coolingPlaceholderObjectType, districtCoolingName, chillerType, chillerName); - // modify nodes + // Read the hot and chilled water nodes from the DistrictHeating and DistrictCooling placeholders, respectively. IdfObject districtHeating = FindObject(heatingPlaceholderObjectType, districtHeatingName); string hwInletNode = districtHeating["Hot Water Inlet Node Name"].Value; string hwOutletNode = districtHeating["Hot Water Outlet Node Name"].Value; @@ -71,20 +96,30 @@ public void ApplyDirectFiredChiller( string chwInletNode = districtCooling["Chilled Water Inlet Node Name"].Value; string chwOutletNode = districtCooling["Chilled Water Outlet Node Name"].Value; - string chillerHeater = GetChillerHeaterIdfObjects(chillerName, hwInletNode, hwOutletNode, chwInletNode, chwOutletNode); - Reader.Load(chillerHeater); + // Inject the new ChillerHeater:Absorption:DirectFired object (plus curves and OA node list). + string chillerHeaterIdf = GetChillerHeaterIdfObjects(chillerName, hwInletNode, hwOutletNode, chwInletNode, chwOutletNode); + idfReader.Load(chillerHeaterIdf); - Reader.Remove(districtHeating); - Reader.Remove(districtCooling); + // Remove the placeholders after the new object has been created and referenced. + idfReader.Remove(districtHeating); + idfReader.Remove(districtCooling); } - public string GetChillerHeaterIdfObjects(string chillerName, string hwInletNode, - string hwOutletNode, string chwInletNode, string chwOutletNode) + public string GetChillerHeaterIdfObjects( + string chillerName, + string hwInletNode, + string hwOutletNode, + string chwInletNode, + string chwOutletNode) { - string template = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF objects inserted into the model (ChillerHeater:Absorption:DirectFired, OutdoorAir:Nodelist, Performance curves) + string chillerHeaterTemplate = @" ChillerHeater:Absorption:DirectFired, {0}, !- Name - Autosize, !- Nominal Cooling Capacity W + Autosize, !- Nominal Cooling Capacity W 0.8, !- Heating to Cooling Capacity Ratio 0.97, !- Fuel Input to Cooling Output Ratio 1.25, !- Fuel Input to Heating Output Ratio @@ -118,48 +153,53 @@ public string GetChillerHeaterIdfObjects(string chillerName, string hwInletNode, NaturalGas, !- Fuel Type ; !- Sizing Factor - - OutdoorAir:Nodelist, {0} Chiller OA Node; ! - Outside air node +OutdoorAir:Nodelist, + {0} Chiller OA Node; !- Outside air node Curve:Biquadratic, {0} GasAbsFlatBiQuad, !- Name - 1.000000000, !- Coefficient1 Constant - 0.000000000, !- Coefficient2 x - 0.000000000, !- Coefficient3 x**2 - 0.000000000, !- Coefficient4 y - 0.000000000, !- Coefficient5 y**2 - 0.000000000, !- Coefficient6 x*y - 0., !- Minimum Value of x - 50., !- Maximum Value of x - 0., !- Minimum Value of y - 50.; !- Maximum Value of y + 1.000000000, !- Coefficient1 Constant + 0.000000000, !- Coefficient2 x + 0.000000000, !- Coefficient3 x**2 + 0.000000000, !- Coefficient4 y + 0.000000000, !- Coefficient5 y**2 + 0.000000000, !- Coefficient6 x*y + 0., !- Minimum Value of x + 50., !- Maximum Value of x + 0., !- Minimum Value of y + 50.; !- Maximum Value of y Curve:Quadratic, {0} GasAbsFlatQuad, !- Name - 1.000000000, !- Coefficient1 Constant - 0.000000000, !- Coefficient2 x - 0.000000000, !- Coefficient3 x**2 - 0., !- Minimum Value of x - 50.; !- Maximum Value of x + 1.000000000, !- Coefficient1 Constant + 0.000000000, !- Coefficient2 x + 0.000000000, !- Coefficient3 x**2 + 0., !- Minimum Value of x + 50.; !- Maximum Value of x Curve:Quadratic, {0} GasAbsLinearQuad, !- Name - 0.000000000, !- Coefficient1 Constant - 1.000000000, !- Coefficient2 x - 0.000000000, !- Coefficient3 x**2 - 0., !- Minimum Value of x - 50.; !- Maximum Value of x + 0.000000000, !- Coefficient1 Constant + 1.000000000, !- Coefficient2 x + 0.000000000, !- Coefficient3 x**2 + 0., !- Minimum Value of x + 50.; !- Maximum Value of x Curve:Quadratic, {0} GasAbsInvLinearQuad, !- Name - 1.000000000, !- Coefficient1 Constant - -1.000000000, !- Coefficient2 x - 0.000000000, !- Coefficient3 x**2 - 0., !- Minimum Value of x - 50.; !- Maximum Value of x - + 1.000000000, !- Coefficient1 Constant + -1.000000000, !- Coefficient2 x + 0.000000000, !- Coefficient3 x**2 + 0., !- Minimum Value of x + 50.; !- Maximum Value of x "; - return String.Format(template, chillerName, hwInletNode, hwOutletNode, chwInletNode, + + return string.Format( + chillerHeaterTemplate, + chillerName, + hwInletNode, + hwOutletNode, + chwInletNode, chwOutletNode); } @@ -167,38 +207,44 @@ public IdfObject FindObject(string objectType, string objectName) { try { - return Reader[objectType].First(c => c[0] == objectName); + return idfReader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { - throw new MissingFieldException(String.Format("Cannot find object: {0}, type: {1}", objectName, - objectType)); + throw new MissingFieldException(string.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - private void ReplaceObjectTypeInList(string listName, string oldObjectType, string oldObjectName, - string newObjectType, string newObjectName) + private void ReplaceObjectTypeInList( + string listName, + string oldObjectType, + string oldObjectName, + string newObjectType, + string newObjectName) { - IEnumerable allEquipment = Reader[listName]; + var idfObjects = idfReader[listName]; - bool objectFound = false; + bool replacementMade = false; - foreach (IdfObject equipment in allEquipment) + foreach (IdfObject idfObject in idfObjects) { - if (!objectFound) + if (replacementMade) { - for (int i = 0; i < (equipment.Count - 1); i++) + break; + } + + for (int i = 0; i < (idfObject.Count - 1); i++) + { + Field currentField = idfObject[i]; + Field nextField = idfObject[i + 1]; + + // Note: comparison is case-sensitive here. + if (currentField.Value == oldObjectType && nextField.Value == oldObjectName) { - Field field = equipment[i]; - Field nextField = equipment[i + 1]; - - if (field.Value == oldObjectType && nextField.Value == oldObjectName) - { - field.Value = newObjectType; - nextField.Value = newObjectName; - objectFound = true; - break; - } + currentField.Value = newObjectType; + nextField.Value = newObjectName; + replacementMade = true; + break; } } } diff --git a/cs/headeredPumps.cs b/cs/headeredPumps.cs index 65c84b9..5a045ef 100644 --- a/cs/headeredPumps.cs +++ b/cs/headeredPumps.cs @@ -1,73 +1,104 @@ /* -This script replaces Pump:VariableSpeed and Pump:ConstantSpeed objects with their -headered equivalents. +Headered Pump Replacement Script -All pump attributes are cherry-picked from DesignBuilder HVAC layout. +This DesignBuilder C# script converts standard Pump:VariableSpeed and Pump:ConstantSpeed pump objects into headered pump equivalents (HeaderedPumps:*). +Purpose + +For each configured pump: +1) Locate the original Pump:* object by name. +2) Create an equivalent HeaderedPumps:* object by taking fields from the original pump. +3) Update references in Branch objects from Pump:* to HeaderedPumps:* for the same pump name. +4) Insert (Load) the new HeaderedPumps:* object and remove the original Pump:* object. + +How to Use + +Configuration +- Configure pumps to be replaced (pumpType, pumpName, number of pumps in headered pump bank) +- All pump attributes are taken from DesignBuilder User Interface (HVAC layout). + +Prerequisites (required placeholders) +- The base model must already contain the target pump objects: + - Pump:ConstantSpeed and/or Pump:VariableSpeed with names matching your configuration. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; + using System.Linq; using System.Collections.Generic; using System; -using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - // define pumps to be replaced - ReplacePump(idfReader, "Pump:ConstantSpeed", "HW Loop Supply Pump", nPumpsInBank: 3); - ReplacePump(idfReader, "Pump:VariableSpeed", "CHW Loop Supply Pump", nPumpsInBank: 3); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Configure pumps to be replaced (pumpType, pumpName, pumps-in-bank) + ReplacePump(idf, "Pump:ConstantSpeed", "HW Loop Supply Pump", nPumpsInBank: 3); + ReplacePump(idf, "Pump:VariableSpeed", "CHW Loop Supply Pump", nPumpsInBank: 3); - idfReader.Save(); + idf.Save(); } - public IdfObject FindObject(IdfReader reader, string objectType, string objectName) + + public IdfObject FindObject(IdfReader idf, string objectType, string objectName) { try { - return reader[objectType].First(c => c[0] == objectName); + return idf[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } } - public void ReplacePump(IdfReader reader, string pumpType, string pumpName, int nPumpsInBank) + public void ReplacePump(IdfReader idf, string pumpType, string pumpName, int nPumpsInBank) { - IdfObject pump = FindObject(reader, pumpType, pumpName); - string headeredPump; + // Locate the original pump placeholder object + IdfObject pump = FindObject(idf, pumpType, pumpName); - if (pumpType.ToLower() == "pump:variablespeed") + // Build the replacement HeaderedPumps:* IDF text from the original pump fields + string headeredPumpIdfText; + string pumpTypeLower = pumpType.ToLower(); + + if (pumpTypeLower == "pump:variablespeed") { - headeredPump = GetVariablePump(pump, nPumpsInBank); - ReplaceObjectTypeInList(reader, "Branch", "Pump:VariableSpeed", pumpName, "HeaderedPumps:VariableSpeed", pumpName); + headeredPumpIdfText = GetVariablePump(pump, nPumpsInBank); + + // Update Branch references from Pump:VariableSpeed -> HeaderedPumps:VariableSpeed (same name) + ReplaceObjectTypeInList(idf, "Branch", "Pump:VariableSpeed", pumpName, "HeaderedPumps:VariableSpeed", pumpName); } - else if (pumpType.ToLower() == "pump:constantspeed") + else if (pumpTypeLower == "pump:constantspeed") { - headeredPump = GetConstantPump(pump, nPumpsInBank); - ReplaceObjectTypeInList(reader, "Branch", "Pump:ConstantSpeed", pumpName, "HeaderedPumps:ConstantSpeed", pumpName); + headeredPumpIdfText = GetConstantPump(pump, nPumpsInBank); + + // Update Branch references from Pump:ConstantSpeed -> HeaderedPumps:ConstantSpeed (same name) + ReplaceObjectTypeInList(idf, "Branch", "Pump:ConstantSpeed", pumpName, "HeaderedPumps:ConstantSpeed", pumpName); } else { throw new Exception(String.Format("Invalid pump type {0}", pumpType)); } - reader.Load(headeredPump); - reader.Remove(pump); + + // Insert the new headered pump object, then remove the original pump object to avoid duplicates + idf.Load(headeredPumpIdfText); + idf.Remove(pump); } public string GetConstantPump(IdfObject pump, int nPumpsInBank) { - string headeredPump = @" + // Maps fields from Pump:ConstantSpeed into HeaderedPumps:ConstantSpeed (by position) + string headeredPumpTemplate = @" HeaderedPumps:ConstantSpeed, {0}, !- Name {1}, !- Inlet Node Name @@ -80,12 +111,16 @@ public string GetConstantPump(IdfObject pump, int nPumpsInBank) {7}, !- Motor Efficiency {8}, !- Fraction of Motor Inefficiencies to Fluid Stream {9}; !- Pump Control Type"; - return String.Format(headeredPump, pump[0].Value, pump[1].Value, pump[2].Value, pump[3].Value, nPumpsInBank.ToString(), pump[4].Value, pump[5].Value, pump[6].Value, pump[7].Value, pump[8].Value); + + return String.Format( + headeredPumpTemplate, + pump[0].Value, pump[1].Value, pump[2].Value, pump[3].Value, nPumpsInBank.ToString(), pump[4].Value, pump[5].Value, pump[6].Value, pump[7].Value, pump[8].Value); } public string GetVariablePump(IdfObject pump, int nPumpsInBank) { - string headeredPump = @" + // Maps fields from Pump:VariableSpeed into HeaderedPumps:VariableSpeed (by position) + string headeredPumpTemplate = @" HeaderedPumps:VariableSpeed, {0}, !- Name {1}, !- Inlet Node Name @@ -104,17 +139,18 @@ public string GetVariablePump(IdfObject pump, int nPumpsInBank) {13}, !- Minimum Flow Rate m3/s {14}; !- Pump Control Type"; - - return String.Format(headeredPump, pump[0].Value, pump[1].Value, pump[2].Value, pump[3].Value, nPumpsInBank, pump[4].Value, pump[5].Value, pump[6].Value, pump[7].Value, pump[8].Value, pump[9].Value, pump[10].Value, pump[11].Value, pump[12].Value, pump[13].Value); + return String.Format( + headeredPumpTemplate, + pump[0].Value, pump[1].Value, pump[2].Value, pump[3].Value, nPumpsInBank, pump[4].Value, pump[5].Value, pump[6].Value, pump[7].Value, pump[8].Value, pump[9].Value, pump[10].Value, pump[11].Value, pump[12].Value, pump[13].Value); } - private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) + private void ReplaceObjectTypeInList(IdfReader idf, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) { - IEnumerable allEquipment = idfReader[listName]; - + // This scans objects of type listName (currently used with "Branch") and replaces (type,name) pairs in fields + IEnumerable listObjects = idf[listName]; bool objectFound = false; - foreach (IdfObject equipment in allEquipment) + foreach (IdfObject equipment in listObjects) { if (!objectFound) { @@ -123,7 +159,8 @@ private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, strin Field field = equipment[i]; Field nextField = equipment[i + 1]; - if (field.Value.ToLower() == oldObjectType.ToLower() && nextField.Value.ToLower() == oldObjectName.ToLower()) + if (field.Value.ToLower() == oldObjectType.ToLower() && + nextField.Value.ToLower() == oldObjectName.ToLower()) { field.Value = newObjectType; nextField.Value = newObjectName; @@ -135,4 +172,4 @@ private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, strin } } } -} +} \ No newline at end of file diff --git a/cs/includeSizingResults.cs b/cs/includeSizingResults.cs index 5bd18a3..d9405df 100644 --- a/cs/includeSizingResults.cs +++ b/cs/includeSizingResults.cs @@ -1,28 +1,30 @@ /* -Include cooling and heating sizing in results set. +Include cooling and heating sizing in the DesignBuilder results set. -The results can be accessed via DesignBuilder Results Viewer. +This DesignBuilder C# script enables "Run Simulation for Sizing Periods" in EnergyPlus so that heating and +cooling sizing-related outputs are available and can be accessed via DesignBuilder Results Viewer. +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; -using System.Collections.Generic; using DB.Extensibility.Contracts; using EpNet; namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + public class EnableSizingPeriodsInResults : ScriptBase, IScript { public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - IdfObject simulationControl = idfReader["SimulationControl"][0]; + // Enable sizing period simulations so sizing-related outputs can appear in results. + IdfObject simulationControl = idf["SimulationControl"][0]; simulationControl["Run Simulation for Sizing Periods"].Value = "Yes"; - idfReader.Save(); + + idf.Save(); } } -} +} \ No newline at end of file diff --git a/cs/optimisationUpdateHeatingCoolingCop.cs b/cs/optimisationUpdateHeatingCoolingCop.cs index 6337003..3e2ae7c 100644 --- a/cs/optimisationUpdateHeatingCoolingCop.cs +++ b/cs/optimisationUpdateHeatingCoolingCop.cs @@ -1,24 +1,41 @@ /* - Reads optimisation variables from the "OptimisationVariables" table - (expected keys: "heatingCOP" and "coolingEER") and updates the EnergyPlus - IDF accordingly. Actions performed by the script: - - Sets the "Rated COP" field on the - Coil:WaterHeating:AirToWaterHeatPump:Pumped object named - "HP Water Heater HP Water Heating Coil" when a valid heating COP is present. - - Sets the "Reference COP" field on Chiller:Electric:EIR objects when a valid cooling EER is present. - Behavior: - - If a variable value equals "UNKNOWN", a MessageBox is shown and that value is not applied. - - The IDF is saved after modifications. - Usage: - - Ensure the `OptimisationVariables` table contains the current values before running this script. +Reads optimisation variables from the "OptimisationVariables" table + +This DesignBuilder C# script reads optimisation variables from the DesignBuilder site table named "OptimisationVariables" +and applies them to the EnergyPlus input IDF just before simulation starts. + +Purpose +1) Read "heatingCOP" and "coolingEER" records from the "OptimisationVariables" table (field: "VariableCurrentValue"). +2) Open the generated EnergyPlus IDF using EpNet. +3) If a variable value is "UNKNOWN", a MessageBox is shown and that value is not applied. +4) If a value is not "UNKNOWN", update: + - Coil:WaterHeating:AirToWaterHeatPump:Pumped (Name = "HP Water Heater HP Water Heating Coil", Field = "Rated COP") + - Chiller:Electric:EIR (all instances) (Field = "Reference COP") +5) Save the IDF after modifications. + +How to Use + +Configuration +- Required record keys: + - "heatingCOP": used to set "Rated COP" on the named water-heating HP coil + - "coolingEER": used to set "Reference COP" on Chiller:Electric:EIR objects +- Default object name targeted for heating COP update: + - "HP Water Heater HP Water Heating Coil" + +Prerequisites (required placeholders) +- DesignBuilder Site table "OptimisationVariables" expects keys "heatingCOP" and "coolingEER". +- The EnergyPlus model must contain: + - Coil:WaterHeating:AirToWaterHeatPump:Pumped object with Name exactly:"HP Water Heater HP Water Heating Coil" (default key) + - One or more Chiller:Electric:EIR objects (all will be updated) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Windows.Forms; using System.Collections.Generic; +using System.Windows.Forms; +using DB.Api; using DB.Extensibility.Contracts; using EpNet; -using DB.Api; -using System.Windows.Forms; namespace DB.Extensibility.Scripts { @@ -26,47 +43,58 @@ public class IdfFindAndReplace : ScriptBase, IScript { public override void BeforeEnergySimulation() { + // Access the current DesignBuilder site and read optimisation variables Site site = ApiEnvironment.Site; - Table table = site.GetTable("OptimisationVariables"); - Record recordHeating = table.Records["heatingCOP"]; - string heatingCop = recordHeating["VariableCurrentValue"]; + Table optimisationVariablesTable = site.GetTable("OptimisationVariables"); + Record heatingCopRecord = optimisationVariablesTable.Records["heatingCOP"]; + string heatingCopValue = heatingCopRecord["VariableCurrentValue"]; - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - IEnumerable heatingCoils = idfReader["Coil:WaterHeating:AirToWaterHeatPump:Pumped"]; - foreach (IdfObject coil in heatingCoils) + // Update the specific water heating heat pump coil COP, if present and not UNKNOWN + IEnumerable waterHeatingHeatPumpCoils = idf["Coil:WaterHeating:AirToWaterHeatPump:Pumped"]; + foreach (IdfObject coil in waterHeatingHeatPumpCoils) { + // USER CONFIGURATION: Define name of water heater heating coil + // Only apply to the expected placeholder object by exact Name match if (coil["Name"].Equals("HP Water Heater HP Water Heating Coil")) { - if (heatingCop.Equals("UNKNOWN")) + if (heatingCopValue.Equals("UNKNOWN")) { - MessageBox.Show("Cannot set heating COP, UNKNOWN value in OptimisationVariables table. "); + // Skip applying this value if optimisation table contains UNKNOWN + MessageBox.Show("Cannot set heating COP: UNKNOWN value in OptimisationVariables table."); } else { - coil["Rated COP"].Value = heatingCop; + // Apply optimisation variable value to the EnergyPlus input field + coil["Rated COP"].Value = heatingCopValue; } } } - Record recordCooling = table.Records["coolingEER"]; - string coolingEer = recordCooling["VariableCurrentValue"]; + // Read cooling EER value (record key: "coolingEER", field: "VariableCurrentValue") + Record coolingEerRecord = optimisationVariablesTable.Records["coolingEER"]; + string coolingEerValue = coolingEerRecord["VariableCurrentValue"]; - IEnumerable chillers = idfReader["Chiller:Electric:EIR"]; - foreach (IdfObject chiller in chillers) + // Update all Chiller:Electric:EIR objects (Reference COP field), if value is not UNKNOWN + IEnumerable electricEirChillers = idf["Chiller:Electric:EIR"]; + foreach (IdfObject chiller in electricEirChillers) { - if (coolingEer.Equals("UNKNOWN")) + if (coolingEerValue.Equals("UNKNOWN")) { - MessageBox.Show("Cannot set cooling COP, UNKNOWN value in OptimisationVariables table. "); + // Skip applying this value if optimisation table contains UNKNOWN + MessageBox.Show("Cannot set cooling value: UNKNOWN value in OptimisationVariables table."); } else { - chiller["Reference COP"].Value = coolingEer; + // Apply optimisation variable value to the EnergyPlus input field + chiller["Reference COP"].Value = coolingEerValue; } } - idfReader.Save(); + + idf.Save(); } } -} +} \ No newline at end of file diff --git a/cs/overridePeopleRadiantFraction.cs b/cs/overridePeopleRadiantFraction.cs index 3f2a642..98a0d3b 100644 --- a/cs/overridePeopleRadiantFraction.cs +++ b/cs/overridePeopleRadiantFraction.cs @@ -1,8 +1,22 @@ /* -Override radiant heat gain by people. +Override radiant heat gain fraction for People objects. -DesignBuilder always applies default 0.3. +This DesignBuilder C# script overrides the EnergyPlus People object "Fraction Radiant" field. +DesignBuilder typically applies a default radiant fraction (commonly 0.3). +This script enforces a user-defined value for all People objects in the IDF just before the EnergyPlus simulation runs. +Purpose +1) Find all People objects +2) Set "Fraction Radiant" to a configured value +3) Save the modified IDF + +How to Use + +Configuration +- radiantFraction: Radiant fraction of sensible heat gains from people (dimensionless, 0–1). + This value will be applied to every People object found in the IDF. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -11,23 +25,28 @@ Override radiant heat gain by people. namespace DB.Extensibility.Scripts { - public class IdfFindAndReplace : ScriptBase, IScript + public class OverridePeopleRadiantFraction : ScriptBase, IScript { public override void BeforeEnergySimulation() { - IdfReader idfReader = new IdfReader( + IdfReader idf = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Target radiant fraction (dimensionless, 0.0 to 1.0). const double radiantFraction = 0.15; - IEnumerable peopleObjects = idfReader["People"]; - foreach (IdfObject people in peopleObjects) + // Retrieve all People objects and set the "Fraction Radiant" field. + IEnumerable peopleObjects = idf["People"]; + foreach (IdfObject peopleObj in peopleObjects) { - people["Fraction Radiant"].Number = radiantFraction; + peopleObj["Fraction Radiant"].Number = radiantFraction; } - idfReader.Save(); + idf.Save(); } } -} +} \ No newline at end of file diff --git a/cs/replaceScheduleByScheduleFile.cs b/cs/replaceScheduleByScheduleFile.cs index 7086040..97f7b1c 100644 --- a/cs/replaceScheduleByScheduleFile.cs +++ b/cs/replaceScheduleByScheduleFile.cs @@ -1,44 +1,74 @@ /* -This script replaces Schedule:Compact with schedule:File objects in the IDF file. +Replaces Schedule:Compact with schedule:File objects + +This DesignBuilder C# script replaces one or more Schedule:Compact objects with Schedule:File objects +in the EnergyPlus IDF generated by DesignBuilder. + +Purpose +For each configured schedule name: +1) Find the Schedule:Compact object (acts as the required placeholder) +2) Create an equivalent Schedule:File object (from a user-specified CSV file/column) +3) Load the new Schedule:File object into the IDF +4) Remove the original Schedule:Compact object +Save the modified IDF before EnergyPlus simulation starts + +How to Use + +Configuration +- Add one or more ReplaceCompactSchedule(...) calls in BeforeEnergySimulation(). +- For each call, set: + - scheduleName: Name of the existing Schedule:Compact object to replace (placeholder name in the IDF) + - filePath: Full path to the schedule CSV/text file to be used by Schedule:File + - columnNumber: Which column contains the schedule values (1 = first column) + - rowsToSkip: Header rows to skip before data begins + - columnSeparator (optional): e.g. "comma", "tab", "fixed", or "semicolon" (default is "comma") +NOTE: EnergyPlus is case-insensitive for many keywords, but matching standard casing is recommended. + +Prerequisites (Required Placeholder Objects) +- The base model must contain a Schedule:Compact object with the exact name passed into ReplaceCompactSchedule(...). + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. +Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ -using System.Runtime; + using System.Linq; -using System.Collections.Generic; using System; -using System.Windows.Forms; using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - private IdfReader reader; + private IdfReader idfReader; public override void BeforeEnergySimulation() { - reader = new IdfReader( + idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); - // define schedules to be replaced + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Define schedules to be replaced (repeat this call for each schedule) ReplaceCompactSchedule( - scheduleName: "test schedule", - filePath: @"C:\path\to\your\file.csv", + scheduleName: "test schedule", // Name of existing Schedule:Compact (placeholder) + filePath: @"C:\path\to\your\file.csv", // Path to Schedule:File columnNumber: 1, rowsToSkip: 0); - reader.Save(); + idfReader.Save(); } + public IdfObject FindObject(string objectType, string objectName) { try { - return reader[objectType].First(c => c[0] == objectName); + return idfReader[objectType].First(c => c[0] == objectName); } - catch (Exception e) + catch (Exception) { throw new Exception(String.Format("Cannot find object: {0}, type: {1}", objectName, objectType)); } @@ -47,15 +77,16 @@ public IdfObject FindObject(string objectType, string objectName) public void ReplaceCompactSchedule(string scheduleName, string filePath, int columnNumber, int rowsToSkip, string columnSeparator = "comma") { IdfObject schedule = FindObject("Schedule:Compact", scheduleName); - string scheduleFile = GetScheduleFile(scheduleName, filePath, columnNumber, rowsToSkip, columnSeparator); + string scheduleFileIdfText = BuildScheduleFile(scheduleName, filePath, columnNumber, rowsToSkip, columnSeparator); - reader.Load(scheduleFile); - reader.Remove(schedule); + // Add the new Schedule:File object, then remove the old Schedule:Compact object + idfReader.Load(scheduleFileIdfText); + idfReader.Remove(schedule); } - - public string GetScheduleFile(string scheduleName, string filePath, int columnNumber, int rowsToSkip, string columnSeparator) + public string BuildScheduleFile(string scheduleName, string filePath, int columnNumber, int rowsToSkip, string columnSeparator) { + // Builds the IDF text for a Schedule:File object using the provided parameters. string scheduleTemplate = @"Schedule:File, {0}, !- Name Any Number, !- Schedule Type Limits Name @@ -66,7 +97,8 @@ public string GetScheduleFile(string scheduleName, string filePath, int columnNu {4}, !- Column Separator , !- Interpolate to Timestep 60; !- Minutes per Item"; + return String.Format(scheduleTemplate, scheduleName, filePath, columnNumber, rowsToSkip, columnSeparator); } } -} +} \ No newline at end of file diff --git a/cs/setDesignSpecificationOutdoorAirSchedule.cs b/cs/setDesignSpecificationOutdoorAirSchedule.cs index 692e6f0..df84a4b 100644 --- a/cs/setDesignSpecificationOutdoorAirSchedule.cs +++ b/cs/setDesignSpecificationOutdoorAirSchedule.cs @@ -1,18 +1,37 @@ /* -Apply the mechanical ventilation "Operation Schedule" in Detailed HVAC. +UpdateDesignSpecification – Apply Mechanical Ventilation “Operation Schedule” to Detailed HVAC Outdoor Air Specs + +Purpose +This DesignBuilder C# script reads each zone’s Mechanical Ventilation “Operation Schedule” from the model +and assigns the corresponding EnergyPlus schedule name to the zone’s DesignSpecification:OutdoorAir object. + +Main Steps +1) Read the DesignBuilder “Schedules” table (model data). +2) Build a map of Zone IDF Name to MechanicalVentilationSchedule handle (string) from model attributes. +3) Parse the EnergyPlus IDF to find existing schedule names (Schedule:Compact). +4) For each DesignSpecification:OutdoorAir: + - Derive the zone name from the object name (assumes standard naming convention). + - Find the schedule record in the “Schedules” table using the stored handle. + - If the schedule does not exist in the IDF, load it from the schedule CompactData. + - Set “Outdoor Air Schedule Name” to that schedule name. +NOTE: he script currently only checks Schedule:Compact objects. + +How to Use + +Prerequisites (required placeholders) +- Zones must have the attribute “MechanicalVentilationSchedule” populated via model user interface. +- The EnergyPlus IDF must contain DesignSpecification:OutdoorAir objects created by Detailed HVAC. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. +*/ -The script reads the schedule value from model data and assigns it to the "DesignSpecification:OutdoorAir" object. - - */ using System; -using System.Runtime; using System.Collections.Generic; using System.Linq; -using System.Windows.Forms; -using EpNet; using DB.Api; using DB.Extensibility.Contracts; +using EpNet; namespace DB.Extensibility.Scripts { @@ -21,47 +40,58 @@ public class UpdateDesignSpecification : ScriptBase, IScript public override void BeforeEnergySimulation() { Site site = ApiEnvironment.Site; - Table table = site.GetTable("Schedules"); + Table schedulesTable = site.GetTable("Schedules"); IdfReader idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + // Build mapping: Zone IdfName -> schedule record handle stored in the zone attribute Building building = site.Buildings[ApiEnvironment.CurrentBuildingIndex]; - Dictionary zoneOAScheduleIds = GetZoneOAScheduleIds(building); + Dictionary zoneToScheduleHandle = GetZoneOAScheduleIds(building); HashSet scheduleNames = GetScheduleNames(idfReader); - string result = string.Join(", ", zoneOAScheduleIds.Select(x => x.Key + ": " + x.Value)); + string result = string.Join(", ", zoneToScheduleHandle.Select(x => x.Key + ": " + x.Value)); - IEnumerable oaDesignSpecs = idfReader["DesignSpecification:OutdoorAir"]; + // All DesignSpecification:OutdoorAir objects that will receive “Outdoor Air Schedule Name” + IEnumerable outdoorAirDesignSpecs = idfReader["DesignSpecification:OutdoorAir"]; - foreach (IdfObject designSpecification in oaDesignSpecs) + foreach (IdfObject designSpecification in outdoorAirDesignSpecs) { string zoneName = designSpecification["Name"].Value.Replace(" Design Specification Outdoor Air Object", ""); - string scheduleHandle = zoneOAScheduleIds[zoneName]; + string scheduleHandle = zoneToScheduleHandle[zoneName]; - Record scheduleRecord = table.Records.GetRecordFromHandle(int.Parse(scheduleHandle)); + // Retrieve schedule record from model data using its handle + Record scheduleRecord = schedulesTable.Records.GetRecordFromHandle(int.Parse(scheduleHandle)); + // Schedule name as it should appear in the EnergyPlus IDF string scheduleName = scheduleRecord["Name"]; + // If schedule is not already in the IDF, reconstruct Schedule:Compact text and load it if (!scheduleNames.Contains(scheduleName)) { string scheduleContent = ParseScheduleData(scheduleRecord["CompactData"], scheduleName); idfReader.Load(scheduleContent); scheduleNames.Add(scheduleName); } + + // Apply the schedule to the DesignSpecification:OutdoorAir object designSpecification["Outdoor Air Schedule Name"].Value = scheduleName; } + idfReader.Save(); } private string ParseScheduleData(string content, string name) { - char pipe = (char)124; // ASCII code for pipe - char caret = (char)94; // ASCII code for caret + // CompactData appears to encode newlines using the two-character delimiter. + // Replace that delimiter with actual line breaks so it becomes valid IDF text. + char pipe = (char)124; // ASCII code for vertical bar: | + char caret = (char)94; // ASCII code for caret: ^ string delimiter = new string(new[] { pipe, caret }); content = content.Replace(delimiter, System.Environment.NewLine); + // Replace the schedule name in the Schedule:Compact object. int firstCommaIndex = content.IndexOf(','); int secondCommaIndex = content.IndexOf(',', firstCommaIndex + 1); @@ -74,22 +104,26 @@ private string ParseScheduleData(string content, string name) private HashSet GetScheduleNames(IdfReader reader) { string[] scheduleTypes = new string[] { "Schedule:Compact" }; - HashSet scheduleNames = new HashSet(scheduleTypes - .SelectMany(scheduleType => reader[scheduleType] - .Select(schedule => schedule[0].Value))); + + HashSet scheduleNames = new HashSet( + scheduleTypes + .SelectMany(scheduleType => reader[scheduleType] + .Select(schedule => schedule[0].Value))); + return scheduleNames; } private Dictionary GetZoneOAScheduleIds(Building building) { + // Read the per-zone attribute “MechanicalVentilationSchedule” which stores a handle (string) Dictionary zoneOAScheduleIds = building.BuildingBlocks .SelectMany(block => block.Zones) .ToDictionary( zone => zone.IdfName, zone => zone.GetAttribute("MechanicalVentilationSchedule") ); + return zoneOAScheduleIds; } - } -} +} \ No newline at end of file diff --git a/cs/slinky.cs b/cs/slinky.cs index 6711ea0..efdf95f 100644 --- a/cs/slinky.cs +++ b/cs/slinky.cs @@ -1,8 +1,36 @@ /* -Replace GroundHeatExchanger:Surface with GroundHeatExchanger:Slinky. - -Object name needs to reference the GroundHeatExchanger:Surface object name. -Attributes can be set in 'boilerplate' IDF text below. +Replace Ground Heat Exchanger of type Surface with Slinky type. + +Purpose +This DesignBuilder C# script replaces a single GroundHeatExchanger:Surface object with a +GroundHeatExchanger:Slinky object, keeping the same object name and reusing the original inlet/outlet nodes. + +Main steps +1) Find the target GroundHeatExchanger:Surface by name +2) Update references in: + - CondenserEquipmentList (object type + name pair) + - Branch (object type + name pair) +3) Add: + - GroundHeatExchanger:Slinky (generated from the boilerplate template) + - Site:GroundTemperature:Undisturbed:KusudaAchenbach (boilerplate object) +4) Remove the original GroundHeatExchanger:Surface +5) Save the modified IDF + +How to Use + +Configuration +- Set the target object name in: groundHxName + This must match the Name field of the GroundHeatExchanger:Surface in the model. +- Adjust the Slinky parameters in: slinkyBoilerplateIdf + (e.g., design flow rate, soil properties, trench geometry, etc.) +- Adjust / replace the undisturbed ground temperature object in: undisturbedGroundTempsIdf + Ensure the object name matches the reference used by the slinky boilerplate. + +Prerequisites (required placeholders) +Base model must contain a GroundHeatExchanger:Surface object (referenced in groundHxName) + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. +Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -13,12 +41,17 @@ Attributes can be set in 'boilerplate' IDF text below. using EpNet; namespace DB.Extensibility.Scripts - { public class IdfFindAndReplace : ScriptBase, IScript { - string objectName = "Ground Heat Exchanger"; - string slinkyBoilerPlate = @" + // USER CONFIGURATION:name of GroundHeatExchanger:Surface object (must exactly match the IDF object's Name). + private string groundHxName = "Ground Heat Exchanger"; + + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF template for the replacement GroundHeatExchanger:Slinky. Edit attribute values here as needed. + private string slinkyBoilerplateIdf = @" GroundHeatExchanger:Slinky, {0}, !- Name {1}, !- Inlet Node @@ -43,7 +76,12 @@ public class IdfFindAndReplace : ScriptBase, IScript KATemps, !- Name of Undisturbed Ground Temperature Object 10; !- Maximum length of simulation [years]"; - string ground = @" + // ---------------------------- + // USER CONFIGURATION SECTION + // ---------------------------- + // Boilerplate IDF for undisturbed ground temperature object referenced by the slinky HX above. + // Ensure the object name matches the reference used in slinkyBoilerplateIdf (e.g., "KATemps"). + private string undisturbedGroundTempsIdf = @" Site:GroundTemperature:Undisturbed:KusudaAchenbach, KATemps, !- Name 1.8, !- Soil Thermal Conductivity {W/m-K} @@ -58,8 +96,15 @@ private IdfObject FindObject(IdfReader idfReader, string objectType, string obje return idfReader[objectType].First(o => o[0] == objectName); } - private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) + private void ReplaceObjectTypeInList( + IdfReader idfReader, + string listName, + string oldObjectType, + string oldObjectName, + string newObjectType, + string newObjectName) { + // Updates (Object Type, Object Name) pairs inside list-like objects. IEnumerable allEquipment = idfReader[listName]; bool objectFound = false; @@ -95,26 +140,26 @@ public override void BeforeEnergySimulation() string oldObjectType = "GroundHeatExchanger:Surface"; string newObjectType = "GroundHeatExchanger:Slinky"; - IdfObject groundHX = FindObject(idfReader, oldObjectType, objectName); + // Required placeholder object: GroundHeatExchanger:Surface with Name == groundHxName + IdfObject surfaceGroundHx = FindObject(idfReader, oldObjectType, groundHxName); - ReplaceObjectTypeInList(idfReader, "CondenserEquipmentList", oldObjectType, objectName, newObjectType, objectName); - ReplaceObjectTypeInList(idfReader, "Branch", oldObjectType, objectName, newObjectType, objectName); + // Update reference locations where the ground HX is referenced by (type, name). + ReplaceObjectTypeInList(idfReader, "CondenserEquipmentList", oldObjectType, groundHxName, newObjectType, groundHxName); + ReplaceObjectTypeInList(idfReader, "Branch", oldObjectType, groundHxName, newObjectType, groundHxName); - string inletNode = groundHX["Fluid Inlet Node Name"].Value; - string outletNode = groundHX["Fluid Outlet Node Name"].Value; + // Reuse inlet/outlet node names from the original surface HX so connectivity remains consistent. + string inletNode = surfaceGroundHx["Fluid Inlet Node Name"].Value; + string outletNode = surfaceGroundHx["Fluid Outlet Node Name"].Value; - string slinky = String.Format(slinkyBoilerPlate, objectName, inletNode, outletNode); + // Create the replacement slinky object using the boilerplate template. + string slinkyGroundHxIdf = String.Format(slinkyBoilerplateIdf, groundHxName, inletNode, outletNode); - idfReader.Remove(groundHX); - idfReader.Load(slinky); - idfReader.Load(ground); + // Replace objects in the IDF: remove old, add new + supporting ground temperature object. + idfReader.Remove(surfaceGroundHx); + idfReader.Load(slinkyGroundHxIdf); + idfReader.Load(undisturbedGroundTempsIdf); idfReader.Save(); } } -} - - - - - +} \ No newline at end of file diff --git a/cs/steamHumidifier.cs b/cs/steamHumidifier.cs index 2d7288c..28935c8 100644 --- a/cs/steamHumidifier.cs +++ b/cs/steamHumidifier.cs @@ -1,8 +1,39 @@ /* -Replace electric steam humidifiers with equivalent gas component. +Replace Electric Steam Humidifiers with Gas Steam Humidifiers -Air loop needs to contain 'steamgas' keyword in order to be updated. +Purpose +This DesignBuilder C# script modifies the EnergyPlus IDF by replacing Humidifier:Steam:Electric +objects with Humidifier:Steam:Gas objects on selected air loops. +Main Steps +1) Find Branch objects that match the naming convention: + - Branch name contains both "steamgas" and "AHU Main Branch" +2) Within matching Branch objects, locate equipment entries where: + - The field comment contains an object type "Humidifier:Steam:Electric" +3) For each matched humidifier: + - Update the Branch reference from Humidifier:Steam:Electric to Humidifier:Steam:Gas + - Create and load a new Humidifier:Steam:Gas object using key fields from the electric humidifier + - Remove the original Humidifier:Steam:Electric object from the IDF +4) Add Output:Variable requests for humidifier natural gas rate. +5) Save the updated IDF. + +How to Use + +Configuration +- The script only updates Branch objects where the Branch name contains both "steamgas" and "AHU Main Branch" + To change the selection rule, edit the string checks in UpdateBranches(). +- Gas humidifier assumptions: + - Thermal efficiency is set by default to 0.9 in GetHumidifierIdfText(). + - Other key fields are copied from the original electric humidifier object by index. + If you need different default gas humidifier inputs, edit GetHumidifierIdfText(). +- Output "Humidifier NaturalGas Rate" is added by default for hourly and Runperiod timestamps (edit for other periods). + +Prerequisites / Placeholders +- The model must already include one or more Humidifier:Steam:Electric objects. +- Branch naming must include the required keywords ("steamgas" and "AHU Main Branch") to be modified. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. +Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -12,12 +43,10 @@ Air loop needs to contain 'steamgas' keyword in order to be updated. using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - private IdfObject FindObject(IdfReader reader, string objectType, string objectName) { return reader[objectType].First(c => c[0] == objectName); @@ -28,6 +57,7 @@ private void UpdateBranches(IdfReader idfReader) IEnumerable branches = idfReader["Branch"]; foreach (IdfObject branch in branches) { + // Selection rule is based on Branch name strings (naming convention dependency). if (branch[0].Value.Contains("steamgas") && branch[0].Value.Contains("AHU Main Branch")) { UpdateBranch(idfReader, branch); @@ -37,44 +67,71 @@ private void UpdateBranches(IdfReader idfReader) private void UpdateBranch(IdfReader reader, IdfObject branch) { - const string GasHumidifierObject = "Humidifier:Steam:Gas"; - const string EleHumidifierObject = "Humidifier:Steam:Electric"; + const string gasHumidifierObjectType = "Humidifier:Steam:Gas"; + const string electricHumidifierObjectType = "Humidifier:Steam:Electric"; + // Scan Branch fields to find an equipment "Object Type" field equal to Humidifier:Steam:Electric. foreach (int i in Enumerable.Range(1, (branch.Count - 2))) { - Field ThisField = branch.Fields[i]; - Field NextField = branch.Fields[i + 1]; - if (ThisField.Comment.ToLower().Contains("object type") && ThisField.Value.ToLower() == EleHumidifierObject.ToLower()) + Field objectTypeField = branch.Fields[i]; + Field objectNameField = branch.Fields[i + 1]; + + if (objectTypeField.Comment.ToLower().Contains("object type") && + objectTypeField.Value.ToLower() == electricHumidifierObjectType.ToLower()) { - IdfObject EleHumidifier = FindObject(reader, EleHumidifierObject, NextField.Value); - ThisField.Value = GasHumidifierObject; - string humidifierText = GetHumidifierIdfText(EleHumidifier, GasHumidifierObject); + IdfObject electricHumidifier = FindObject(reader, electricHumidifierObjectType, objectNameField.Value); + + // Update Branch reference to point to the gas humidifier object type (name stays the same). + objectTypeField.Value = gasHumidifierObjectType; + + // Create a new Humidifier:Steam:Gas object by copying key fields from the electric humidifier. + string humidifierText = GetHumidifierIdfText(electricHumidifier, gasHumidifierObjectType); reader.Load(humidifierText); - MessageBox.Show("Replacing electric humidifier: " + EleHumidifier[0] + " with gas humidifier."); - reader.Remove(EleHumidifier); + + MessageBox.Show("Replacing electric humidifier: " + electricHumidifier[0] + " with gas humidifier."); // Comment this line to remove message box + + // Remove the original electric humidifier object from the IDF. + reader.Remove(electricHumidifier); break; } } } - private string GetHumidifierIdfText(IdfObject EleHumidifier, string GasHumidifier) + + private string GetHumidifierIdfText(IdfObject electricHumidifier, string gasHumidifierObjectType) { - string name = EleHumidifier[0].Value; - string availability = EleHumidifier[1].Value; - string ratedCapacity = EleHumidifier[2].Value; - string ratedGasRate = EleHumidifier[3].Value; - string thermalEfficiency = "0.9"; + // Electric humidifier fields are used to populate the gas humidifier fields. + string name = electricHumidifier[0].Value; + string availability = electricHumidifier[1].Value; + string ratedCapacity = electricHumidifier[2].Value; + string ratedGasRate = electricHumidifier[3].Value; + string thermalEfficiency = "0.9"; // USER CONFIGURATION: Set thermal efficiency (default: 0.9) string thermalEfficiencyCurve = ""; - string ratedFanPower = EleHumidifier[4].Value; - string auxPower = EleHumidifier[5].Value; - string inlet = EleHumidifier[6].Value; - string outlet = EleHumidifier[7].Value; + string ratedFanPower = electricHumidifier[4].Value; + string auxPower = electricHumidifier[5].Value; + string inletNodeName = electricHumidifier[6].Value; + string outletNodeName = electricHumidifier[7].Value; + + string[] fields = + { + gasHumidifierObjectType, + name, + availability, + ratedCapacity, + ratedGasRate, + thermalEfficiency, + thermalEfficiencyCurve, + ratedFanPower, + auxPower, + inletNodeName, + outletNodeName + }; - string[] fields = { GasHumidifier, name, availability, ratedCapacity, ratedGasRate, thermalEfficiency, thermalEfficiencyCurve, ratedFanPower, auxPower, inlet, outlet }; return String.Join(",", fields) + ";"; } private void AddOutputs(IdfReader reader) { + // USER CONFIGURATION: Add gas-rate reporting for humidifiers (by default, hourly and run-period timestamps). reader.Load("Output:Variable,*,Humidifier NaturalGas Rate,hourly;"); reader.Load("Output:Variable,*,Humidifier NaturalGas Rate,Runperiod;"); } @@ -91,4 +148,4 @@ public override void BeforeEnergySimulation() idfReader.Save(); } } -} +} \ No newline at end of file diff --git a/cs/temperatureSourceReplaceDistrictHeating.cs b/cs/temperatureSourceReplaceDistrictHeating.cs index 191b369..6497301 100644 --- a/cs/temperatureSourceReplaceDistrictHeating.cs +++ b/cs/temperatureSourceReplaceDistrictHeating.cs @@ -1,8 +1,30 @@ /* -Replace "DistrictHeating" with "PlantComponent:TemperatureSource". +Replace District Heating with Plant Temperature Source component -Object name needs to reference the DistrictHeating object name. -Attributes can be set in 'boilerplate' IDF text below. +Purpose +This DesignBuilder C# script replaces "DistrictHeating" with "PlantComponent:TemperatureSource". +This object used to simulate systems with a known supply temperature (e.g., rivers, wells, and other configurations where a known temperature is pumped back into the plant system). + +Main Steps +1) Finds a placeholder DistrictHeating object by name +2) Replaces references to that DistrictHeating object in PlantEquipmentList and Branch +3) Creates a PlantComponent:TemperatureSource object using: + - The same object name as the placeholder DistrictHeating object + - The inlet/outlet nodes taken from the DistrictHeating object + - A constant source temperature defined in this script +4) Removes the original DistrictHeating object and saves the updated IDF + +How to Use + +Configuration +- Set 'objectName' to the Name of the DistrictHeating placeholder you want to convert. +- Set 'sourceTemperatureC' to the desired constant temperature in °C. + +Prerequisites (required placeholders) +- The base model must contain a DistrictHeating placeholder object. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. +Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -13,12 +35,11 @@ Attributes can be set in 'boilerplate' IDF text below. using EpNet; namespace DB.Extensibility.Scripts - { public class IdfFindAndReplace : ScriptBase, IScript { - - string boilerplate = @" + // IDF template for the new PlantComponent:TemperatureSource object. + private readonly string temperatureSourceIdfTemplate = @" PlantComponent:TemperatureSource, {0}, !- Name {1}, !- Inlet Node @@ -33,7 +54,13 @@ private IdfObject FindObject(IdfReader idfReader, string objectType, string obje return idfReader[objectType].First(o => o[0] == objectName); } - private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) + private void ReplaceObjectTypeInList( + IdfReader idfReader, + string listName, + string oldObjectType, + string oldObjectName, + string newObjectType, + string newObjectName) { IEnumerable allEquipment = idfReader[listName]; @@ -59,11 +86,16 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath ); + + // USER CONFIGURATION: Name of the placeholder DistrictHeating object to replace string objectName = "TemperatureSource"; string oldObjectType = "DistrictHeating"; string newObjectType = "PlantComponent:TemperatureSource"; - int temperature = 10; + // USER CONFIGURATION: Constant temperature to apply for PlantComponent:TemperatureSource + int sourceTemperatureC = 10; + + // Find the DistrictHeating placeholder object that provides inlet/outlet nodes IdfObject districtHeating = FindObject(idfReader, oldObjectType, objectName); ReplaceObjectTypeInList(idfReader, "PlantEquipmentList", oldObjectType, objectName, newObjectType, objectName); @@ -72,8 +104,15 @@ public override void BeforeEnergySimulation() string inletNode = districtHeating["Hot Water Inlet Node Name"].Value; string outletNode = districtHeating["Hot Water Outlet Node Name"].Value; - string temperatureSource = String.Format(boilerplate, objectName, inletNode, outletNode, temperature); + // Build the replacement object IDF text + string temperatureSource = String.Format( + temperatureSourceIdfTemplate, + objectName, + inletNode, + outletNode, + sourceTemperatureC); + // Replace the placeholder object with the new component and save the modified IDF idfReader.Remove(districtHeating); idfReader.Load(temperatureSource); diff --git a/cs/temperatureSourceReplaceGroundHX.cs b/cs/temperatureSourceReplaceGroundHX.cs index 9dd6c07..7f232f9 100644 --- a/cs/temperatureSourceReplaceGroundHX.cs +++ b/cs/temperatureSourceReplaceGroundHX.cs @@ -1,8 +1,30 @@ /* -Replace "GroundHeatExchanger:System" with "PlantComponent:TemperatureSource". +Replace Ground Heat Exchanger of type System with Plant Temperature Source component -Object name needs to reference the GroundHeatExchanger:System object name. -Attributes can be set in 'boilerplate' IDF text below. +Purpose +This DesignBuilder C# script replaces "GroundHeatExchanger:System" with "PlantComponent:TemperatureSource". +This object used to simulate systems with a known supply temperature (e.g., rivers, wells, and other configurations where a known temperature is pumped back into the plant system). + +Main Steps +1) Finds a placeholder GroundHeatExchanger:System object by name +2) Replaces references to that GroundHeatExchanger:System object in PlantEquipmentList and Branch +3) Creates a PlantComponent:TemperatureSource object using: + - The same object name as the placeholder GroundHeatExchanger:System object + - The inlet/outlet nodes taken from the GroundHeatExchanger:System object + - A constant source temperature defined in this script +4) Removes the original GroundHeatExchanger:System object and saves the updated IDF + +How to Use + +Configuration +- Set 'objectName' to the Name of the GroundHeatExchanger:System placeholder you want to convert. +- Set 'sourceTemperatureC' to the desired constant temperature in °C. + +Prerequisites (required placeholders) +- The base model must contain a GroundHeatExchanger:System placeholder object. + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. +Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -17,7 +39,7 @@ namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - + // IDF template for the new PlantComponent:TemperatureSource object. string boilerplate = @" PlantComponent:TemperatureSource, {0}, !- Name @@ -33,7 +55,13 @@ private IdfObject FindObject(IdfReader idfReader, string objectType, string obje return idfReader[objectType].First(o => o[0] == objectName); } - private void ReplaceObjectTypeInList(IdfReader idfReader, string listName, string oldObjectType, string oldObjectName, string newObjectType, string newObjectName) + private void ReplaceObjectTypeInList( + IdfReader idfReader, + string listName, + string oldObjectType, + string oldObjectName, + string newObjectType, + string newObjectName) { IEnumerable allEquipment = idfReader[listName]; @@ -59,9 +87,13 @@ public override void BeforeEnergySimulation() ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath ); + + // USER CONFIGURATION: Name of the placeholder GroundHeatExchanger:System object to replace string objectName = "TemperatureSource"; string oldObjectType = "GroundHeatExchanger:System"; string newObjectType = "PlantComponent:TemperatureSource"; + + // USER CONFIGURATION: Constant temperature to apply for PlantComponent:TemperatureSource int temperature = 10; IdfObject groundHX = FindObject(idfReader, oldObjectType, objectName); @@ -79,8 +111,10 @@ public override void BeforeEnergySimulation() idfReader.Remove(responseFactors); idfReader.Remove(properties); + // Build the replacement object IDF text string temperatureSource = String.Format(boilerplate, objectName, inletNode, outletNode, temperature); + // Replace the placeholder object with the new component and save the modified IDF idfReader.Load(temperatureSource); idfReader.Save(); } diff --git a/cs/warmestTemperatureFlow.cs b/cs/warmestTemperatureFlow.cs index 0fb4ad5..51884e4 100644 --- a/cs/warmestTemperatureFlow.cs +++ b/cs/warmestTemperatureFlow.cs @@ -1,8 +1,28 @@ /* -Replace SetpointManager:Warmest with SetpointManager:WarmestTemperatureFlow. +Replaces SetpointManager from type Warmest to TemperatureFlow. -Setpoint manager name must contain 'WarmestTemperatureFlow' keyword in order to be replaced. +Purpose +This DesignBuilder C# script replaces SetpointManager:Warmest with SetpointManager:WarmestTemperatureFlow. +This object provides a setpoint manager that attempts to establish a supply air setpoint that will meet +the cooling load of the zone needing the coldest air at the maximum zone supply air flowrate. +Main Steps: +1) Find all SetpointManager:Warmest objects +2) Select only those whose Name contains keyword (default: "WarmestTemperatureFlow") +3) Replace each selected SetpointManager:Warmest with a SetpointManager:WarmestTemperatureFlow object + using the same core fields (Name, Control Variable, Air Loop, Min/Max setpoint, Setpoint Node/NodeList), + and applying the configured Strategy and Minimum Turndown Ratio. + +How to Use + +Configuration +- Choose strategy type: "TemperatureFirst" (default) or "FlowFirst" +- defined Minimum turndown ratio: 0-1 + +Prerequisites / Placeholders +Base model must already contain at least one SetpointManager:Warmest with name containing "WarmestTemperatureFlow" + +DISCLAIMER: This script is provided as-is without warranty. DesignBuilder takes no responsibility for simulation results, accuracy, or any issues arising from the use of this script. Users are responsible for validating all outputs and ensuring the script meets their specific modeling requirements. */ using System.Collections.Generic; @@ -12,12 +32,10 @@ Setpoint manager name must contain 'WarmestTemperatureFlow' keyword in order to using DB.Extensibility.Contracts; using EpNet; - namespace DB.Extensibility.Scripts { public class IdfFindAndReplace : ScriptBase, IScript { - private IdfObject FindObject(IdfReader reader, string objectType, string objectName) { return reader[objectType].First(c => c[0] == objectName); @@ -25,21 +43,27 @@ private IdfObject FindObject(IdfReader reader, string objectType, string objectN private void ReplaceWarmest(IdfReader idfReader, string strategy, float turnDownRatio) { - IEnumerable spms = idfReader["SetpointManager:Warmest"]; - foreach (IdfObject spm in spms) + IEnumerable warmestSpms = idfReader["SetpointManager:Warmest"]; + + foreach (IdfObject warmestSpm in warmestSpms) { - if (spm[0].Value.ToLower().Contains("warmesttemperatureflow")) + // Selection rule: only replace Warmest SPMs with the keyword in the Name field (default: "WarmestTemperatureFlow") + if (warmestSpm[0].Value.ToLower().Contains("warmesttemperatureflow")) { - string newSpm = GetSpmText(spm, strategy, turnDownRatio); - MessageBox.Show("Replacing Warmest spm: " + spm[0].Value + " with WarmestTemperatureFlow spm."); + // Build replacement object text and load into IDF, then remove old object + string newSpm = GetSpmText(warmestSpm, strategy, turnDownRatio); + + MessageBox.Show("Replacing Warmest spm: " + warmestSpm[0].Value + " with WarmestTemperatureFlow spm."); // Comment this line to remove message box + idfReader.Load(newSpm); - idfReader.Remove(spm); + idfReader.Remove(warmestSpm); } } } private string GetSpmText(IdfObject warmestSpm, string strategy, float turnDownRatio) { + // Builds a SetpointManager:WarmestTemperatureFlow object line. string objectName = "SetpointManager:WarmestTemperatureFlow"; string name = warmestSpm[0].Value; string controlVariable = warmestSpm[1].Value; @@ -47,7 +71,20 @@ private string GetSpmText(IdfObject warmestSpm, string strategy, float turnDownR string minTemp = warmestSpm[3].Value; string maxTemp = warmestSpm[4].Value; string node = warmestSpm[6].Value; - string[] fields = { objectName, name, controlVariable, airLoop, minTemp, maxTemp, strategy, node, turnDownRatio.ToString() }; + + string[] fields = + { + objectName, + name, + controlVariable, + airLoop, + minTemp, + maxTemp, + strategy, + node, + turnDownRatio.ToString() + }; + return String.Join(",", fields) + ";"; } @@ -56,11 +93,16 @@ public override void BeforeEnergySimulation() IdfReader idfReader = new IdfReader( ApiEnvironment.EnergyPlusInputIdfPath, ApiEnvironment.EnergyPlusInputIddPath); + + // USER CONFIGURATION: Strategy options ("TemperatureFirst" / "FlowFirst") string strategy = "TemperatureFirst"; // string strategy = "FlowFirst"; - float turnDownRatio = 0.3f; - ReplaceWarmest(idfReader, strategy, turnDownRatio); + + // USER CONFIGURATION: minimum Turndown Ratio (0-1) + float minimumTurndownRatio = 0.3f; + + ReplaceWarmest(idfReader, strategy, minimumTurndownRatio); idfReader.Save(); } } -} +} \ No newline at end of file