From 6cc528b397a3d5f76418c8aab588cc73d064c6cf Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 14:01:07 +0100 Subject: [PATCH 01/19] Naive add Output:Table:Monthly and Annual --- resources/model/OpenStudio.idd | 95 ++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index a45e73daca3..7ef8e1f1229 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -38451,6 +38451,101 @@ OS:Output:Table:SummaryReports, \key CO2ResilienceSummary \key VisualResilienceSummary +OS:Output:Table:Monthly, + \memo Provides a generic method of setting up tables of monthly results. The report + \memo has multiple columns that are each defined using a repeated group of fields for any + \memo number of columns. A single Output:Table:Monthly object often produces multiple + \memo tables in the output. A table is produced for every instance of a particular output + \memo variable. For example, a table defined with zone variables will be produced once for + \memo every zone. + \extensible:2 + A1, \field Handle + \type handle + \required-field + A2, \field Name + \required-field + \type alpha + N1, \field Digits After Decimal + \type integer + \default 2 + \minimum 0 + \maximum 10 + A3, \field Variable or Meter 1 Name + \begin-extensible + \type external-list + \external-list autoRDDvariableMeter + \note The name of an output variable or meter that is available in the RDD file. + A4; \field Aggregation Type for Variable or Meter 1 + \type choice + \key SumOrAverage + \key Maximum + \key Minimum + \key ValueWhenMaximumOrMinimum + \key HoursNonZero + \key HoursZero + \key HoursPositive + \key HoursNonPositive + \key HoursNegative + \key HoursNonNegative + \key SumOrAverageDuringHoursShown + \key MaximumDuringHoursShown + \key MinimumDuringHoursShown + \note The method of aggregation for the selected variable or meter. + +OS:Output:Table:Annual, + \memo Provides a generic method of setting up tables of annual results with one row per object. + \memo The report has multiple columns that are each defined using a repeated group of fields + \memo for any number of columns. A single Output:Table:Annual produces a single table in the + \memo output. + \extensible:3 + A1, \field Handle + \type handle + \required-field + A2, \field Name + \required-field + \type alpha + A3, \field Filter + \type alpha + \note An optional text string that is compared to the names of the objects referenced by the + \note variables and if they match are included in the table. A footnote will appear that indicates + \note that the objects shown may not be all the objects that of that type that occur in the file. + A4, \field Schedule Name + \type object-list + \object-list ScheduleNames + \note Optional schedule name. If left blank, aggregation is performed for all hours simulated. If + \note a schedule is specified, aggregation is performed for non-zero hours in the schedule. + A5, \field Variable or Meter or EMS Variable or Field 1 Name + \begin-extensible + \type external-list + \external-list autoRDDvariableMeter + \note contain the name of a variable (see Output:Variable and eplusout.rdd), meter (see Output:Meter + \note and eplusout.mdd), or EMS Internal Variable Name or IDF Object Field name. This value is shown + \note using the aggregation method specified. + A6, \field Aggregation Type for Variable or Meter 1 + \type choice + \key SumOrAverage + \key Maximum + \key Minimum + \key ValueWhenMaximumOrMinimum + \key HoursNonZero + \key HoursZero + \key HoursPositive + \key HoursNonPositive + \key HoursNegative + \key HoursNonNegative + \key HourInTenBinsMinToMax + \key HourInTenBinsZeroToMax + \key HourInTenBinsMinToZero + \key SumOrAverageDuringHoursShown + \key MaximumDuringHoursShown + \key MinimumDuringHoursShown + \note The method of aggregation for the selected variable or meter. + N1; \field Digits After Decimal 1 + \type integer + \default 2 + \minimum 0 + \maximum 10 + \group Energy Management System (EMS) OS:EnergyManagementSystem:Sensor, From a0d7948c1fd3f985dd5ce09260390bf2f5c87608 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 14:34:50 +0100 Subject: [PATCH 02/19] GenerateClass.rb ```bash $os_build3/Products/openstudio GenerateClass.rb -c "OutputTableMonthly" -b "ModelObject" -i "OS:Output:Table:Monthly" -s model -o /Users/julien/Software/Others/OpenStudio3/src/model/ -p -f -r ` `` --- src/model/OutputTableMonthly.cpp | 84 ++++++++++++++++++++ src/model/OutputTableMonthly.hpp | 85 +++++++++++++++++++++ src/model/OutputTableMonthly_Impl.hpp | 71 +++++++++++++++++ src/model/test/OutputTableMonthly_GTest.cpp | 29 +++++++ 4 files changed, 269 insertions(+) create mode 100644 src/model/OutputTableMonthly.cpp create mode 100644 src/model/OutputTableMonthly.hpp create mode 100644 src/model/OutputTableMonthly_Impl.hpp create mode 100644 src/model/test/OutputTableMonthly_GTest.cpp diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp new file mode 100644 index 00000000000..edc056ecbd1 --- /dev/null +++ b/src/model/OutputTableMonthly.cpp @@ -0,0 +1,84 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "OutputTableMonthly.hpp" +#include "OutputTableMonthly_Impl.hpp" + +#include "../utilities/core/Assert.hpp" + +#include +#include + +namespace openstudio { +namespace model { + + namespace detail { + + OutputTableMonthly_Impl::OutputTableMonthly_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == OutputTableMonthly::iddObjectType()); + } + + OutputTableMonthly_Impl::OutputTableMonthly_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == OutputTableMonthly::iddObjectType()); + } + + OutputTableMonthly_Impl::OutputTableMonthly_Impl(const OutputTableMonthly_Impl& other, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(other, model, keepHandle) {} + + const std::vector& OutputTableMonthly_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType OutputTableMonthly_Impl::iddObjectType() const { + return OutputTableMonthly::iddObjectType(); + } + + int OutputTableMonthly_Impl::digitsAfterDecimal() const { + boost::optional value = getInt(OS_Output_Table_MonthlyFields::DigitsAfterDecimal, true); + OS_ASSERT(value); + return value.get(); + } + + bool OutputTableMonthly_Impl::setDigitsAfterDecimal(int digitsAfterDecimal) { + const bool result = setInt(OS_Output_Table_MonthlyFields::DigitsAfterDecimal, digitsAfterDecimal); + return result; + } + + } // namespace detail + + OutputTableMonthly::OutputTableMonthly(const Model& model) : ModelObject(OutputTableMonthly::iddObjectType(), model) { + OS_ASSERT(getImpl()); + + // TODO: consider adding (overloaded or not) explicit ctors taking required objects as argument + + // TODO: Appropriately handle the following required object-list fields. + bool ok = true; + // ok = setDigitsAfterDecimal(); + OS_ASSERT(ok); + } + + IddObjectType OutputTableMonthly::iddObjectType() { + return {IddObjectType::OS_Output_Table_Monthly}; + } + + int OutputTableMonthly::digitsAfterDecimal() const { + return getImpl()->digitsAfterDecimal(); + } + + bool OutputTableMonthly::setDigitsAfterDecimal(int digitsAfterDecimal) { + return getImpl()->setDigitsAfterDecimal(digitsAfterDecimal); + } + + /// @cond + OutputTableMonthly::OutputTableMonthly(std::shared_ptr impl) : ModelObject(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/OutputTableMonthly.hpp b/src/model/OutputTableMonthly.hpp new file mode 100644 index 00000000000..d942f932a35 --- /dev/null +++ b/src/model/OutputTableMonthly.hpp @@ -0,0 +1,85 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_OUTPUTTABLEMONTHLY_HPP +#define MODEL_OUTPUTTABLEMONTHLY_HPP + +#include +#include "ModelObject.hpp" + +namespace openstudio { +namespace model { + + namespace detail { + + class OutputTableMonthly_Impl; + + } // namespace detail + + /** OutputTableMonthly is a ModelObject that wraps the OpenStudio IDD object 'OS:Output:Table:Monthly'. */ + class MODEL_API OutputTableMonthly : public ModelObject + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit OutputTableMonthly(const Model& model); + + virtual ~OutputTableMonthly() = default; + // Default the copy and move operators because the virtual dtor is explicit + OutputTableMonthly(const OutputTableMonthly& other) = default; + OutputTableMonthly(OutputTableMonthly&& other) = default; + OutputTableMonthly& operator=(const OutputTableMonthly&) = default; + OutputTableMonthly& operator=(OutputTableMonthly&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + /** @name Getters */ + //@{ + + int digitsAfterDecimal() const; + + // TODO: Handle this object's extensible fields. + + //@} + /** @name Setters */ + //@{ + + bool setDigitsAfterDecimal(int digitsAfterDecimal); + + // TODO: Handle this object's extensible fields. + + //@} + /** @name Other */ + //@{ + + //@} + protected: + /// @cond + using ImplType = detail::OutputTableMonthly_Impl; + + explicit OutputTableMonthly(std::shared_ptr impl); + + friend class detail::OutputTableMonthly_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.OutputTableMonthly"); + }; + + /** \relates OutputTableMonthly*/ + using OptionalOutputTableMonthly = boost::optional; + + /** \relates OutputTableMonthly*/ + using OutputTableMonthlyVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_OUTPUTTABLEMONTHLY_HPP diff --git a/src/model/OutputTableMonthly_Impl.hpp b/src/model/OutputTableMonthly_Impl.hpp new file mode 100644 index 00000000000..66eaf873615 --- /dev/null +++ b/src/model/OutputTableMonthly_Impl.hpp @@ -0,0 +1,71 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_OUTPUTTABLEMONTHLY_IMPL_HPP +#define MODEL_OUTPUTTABLEMONTHLY_IMPL_HPP + +#include +#include "ModelObject_Impl.hpp" + +namespace openstudio { +namespace model { + + namespace detail { + + /** OutputTableMonthly_Impl is a ModelObject_Impl that is the implementation class for OutputTableMonthly.*/ + class MODEL_API OutputTableMonthly_Impl : public ModelObject_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + OutputTableMonthly_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + OutputTableMonthly_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + OutputTableMonthly_Impl(const OutputTableMonthly_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~OutputTableMonthly_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + //@} + /** @name Getters */ + //@{ + + int digitsAfterDecimal() const; + + // TODO: Handle this object's extensible fields. + + //@} + /** @name Setters */ + //@{ + + bool setDigitsAfterDecimal(int digitsAfterDecimal); + + // TODO: Handle this object's extensible fields. + + //@} + /** @name Other */ + //@{ + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.OutputTableMonthly"); + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_OUTPUTTABLEMONTHLY_IMPL_HPP diff --git a/src/model/test/OutputTableMonthly_GTest.cpp b/src/model/test/OutputTableMonthly_GTest.cpp new file mode 100644 index 00000000000..971d96b8339 --- /dev/null +++ b/src/model/test/OutputTableMonthly_GTest.cpp @@ -0,0 +1,29 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../OutputTableMonthly.hpp" +#include "../OutputTableMonthly_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, OutputTableMonthly_GettersSetters) { + Model m; + // TODO: Check regular Ctor arguments + OutputTableMonthly outputTableMonthly(m); + // TODO: Or if a UniqueModelObject (and make sure _Impl is included) + // OutputTableMonthly outputTableMonthly = m.getUniqueModelObject(); + + outputTableMonthly.setName("My OutputTableMonthly"); + + // Digits After Decimal: Required Integer + EXPECT_TRUE(outputTableMonthly.setDigitsAfterDecimal(7)); + EXPECT_EQ(7, outputTableMonthly.digitsAfterDecimal()); + // Bad Value + EXPECT_FALSE(outputTableMonthly.setDigitsAfterDecimal(-10)); + EXPECT_EQ(7, outputTableMonthly.digitsAfterDecimal()); +} From f0bf1c1779e03358972a68b5c7577a6a585f5a42 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 17:03:02 +0100 Subject: [PATCH 03/19] Modify IDD --- resources/model/OpenStudio.idd | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd index 7ef8e1f1229..332f335af70 100644 --- a/resources/model/OpenStudio.idd +++ b/resources/model/OpenStudio.idd @@ -38459,6 +38459,7 @@ OS:Output:Table:Monthly, \memo variable. For example, a table defined with zone variables will be produced once for \memo every zone. \extensible:2 + \min-fields 5 A1, \field Handle \type handle \required-field @@ -38467,16 +38468,19 @@ OS:Output:Table:Monthly, \type alpha N1, \field Digits After Decimal \type integer - \default 2 + \required-field \minimum 0 \maximum 10 A3, \field Variable or Meter 1 Name \begin-extensible - \type external-list - \external-list autoRDDvariableMeter + \required-field + \type alpha + \memo type external-list + \memo external-list autoRDDvariableMeter \note The name of an output variable or meter that is available in the RDD file. A4; \field Aggregation Type for Variable or Meter 1 \type choice + \required-field \key SumOrAverage \key Maximum \key Minimum @@ -38498,6 +38502,7 @@ OS:Output:Table:Annual, \memo for any number of columns. A single Output:Table:Annual produces a single table in the \memo output. \extensible:3 + \min-fields 7 A1, \field Handle \type handle \required-field @@ -38516,13 +38521,16 @@ OS:Output:Table:Annual, \note a schedule is specified, aggregation is performed for non-zero hours in the schedule. A5, \field Variable or Meter or EMS Variable or Field 1 Name \begin-extensible - \type external-list - \external-list autoRDDvariableMeter + \type alpha + \required-field + \memo type external-list + \memo external-list autoRDDvariableMeter \note contain the name of a variable (see Output:Variable and eplusout.rdd), meter (see Output:Meter \note and eplusout.mdd), or EMS Internal Variable Name or IDF Object Field name. This value is shown \note using the aggregation method specified. A6, \field Aggregation Type for Variable or Meter 1 \type choice + \required-field \key SumOrAverage \key Maximum \key Minimum @@ -38542,7 +38550,7 @@ OS:Output:Table:Annual, \note The method of aggregation for the selected variable or meter. N1; \field Digits After Decimal 1 \type integer - \default 2 + \required-field \minimum 0 \maximum 10 From 28f2560dda47710530e3c9c364f1b644ef441cef Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 17:06:39 +0100 Subject: [PATCH 04/19] model API for OutputTableMonthly --- src/model/CMakeLists.txt | 3 + src/model/ConcreteModelObjects.hpp | 2 + src/model/Model.cpp | 2 + src/model/OutputTableMonthly.cpp | 197 +++++++++++++++++++++++++- src/model/OutputTableMonthly.hpp | 46 +++++- src/model/OutputTableMonthly_Impl.hpp | 19 ++- 6 files changed, 256 insertions(+), 13 deletions(-) diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 002d3461571..c21e2aba543 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -1117,6 +1117,9 @@ set(${target_name}_src OutputTableSummaryReports.hpp OutputTableSummaryReports_Impl.hpp OutputTableSummaryReports.cpp + OutputTableMonthly.hpp + OutputTableMonthly_Impl.hpp + OutputTableMonthly.cpp OutputSchedules.hpp OutputSchedules_Impl.hpp OutputSchedules.cpp diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index 579feb70d73..4adf5c427be 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -346,6 +346,7 @@ #include "OutputMeter.hpp" #include "OutputVariable.hpp" #include "OutputTableSummaryReports.hpp" +#include "OutputTableMonthly.hpp" #include "OutsideSurfaceConvectionAlgorithm.hpp" #include "People.hpp" #include "PeopleDefinition.hpp" @@ -902,6 +903,7 @@ #include "OutputMeter_Impl.hpp" #include "OutputVariable_Impl.hpp" #include "OutputTableSummaryReports_Impl.hpp" +#include "OutputTableMonthly_Impl.hpp" #include "OutsideSurfaceConvectionAlgorithm_Impl.hpp" #include "People_Impl.hpp" #include "PeopleDefinition_Impl.hpp" diff --git a/src/model/Model.cpp b/src/model/Model.cpp index ad2f92324ef..165bcbd82af 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4242,6 +4242,7 @@ namespace model { REGISTER_CONSTRUCTOR(FuelFactors); REGISTER_CONSTRUCTOR(OutputMeter); REGISTER_CONSTRUCTOR(OutputVariable); + REGISTER_CONSTRUCTOR(OutputTableMonthly); REGISTER_CONSTRUCTOR(OutputTableSummaryReports); REGISTER_CONSTRUCTOR(OutsideSurfaceConvectionAlgorithm); REGISTER_CONSTRUCTOR(People); @@ -4818,6 +4819,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(FuelFactors); REGISTER_COPYCONSTRUCTORS(OutputMeter); REGISTER_COPYCONSTRUCTORS(OutputVariable); + REGISTER_COPYCONSTRUCTORS(OutputTableMonthly); REGISTER_COPYCONSTRUCTORS(OutputTableSummaryReports); REGISTER_COPYCONSTRUCTORS(OutsideSurfaceConvectionAlgorithm); REGISTER_COPYCONSTRUCTORS(People); diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index edc056ecbd1..90d454acc91 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -6,14 +6,53 @@ #include "OutputTableMonthly.hpp" #include "OutputTableMonthly_Impl.hpp" +#include "ModelExtensibleGroup.hpp" + #include "../utilities/core/Assert.hpp" #include +#include #include +#include +#include + namespace openstudio { namespace model { + MonthlyVariableGroup::MonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType) + : m_variableOrMeterName(std::move(variableOrMeterName)), m_aggregationType(std::move(aggregationType)) { + if (m_variableOrMeterName.empty()) { + LOG_AND_THROW("Variable or Meter Name can't be empty"); + } + if (!OutputTableMonthly::isAggregationTypeValid(m_aggregationType)) { + LOG_AND_THROW(fmt::format("Invalid aggregation type '{}': {}", m_aggregationType, OutputTableMonthly::aggregationTypeValues())); + } + } + + std::string MonthlyVariableGroup::variableOrMeterName() const { + return m_variableOrMeterName; + } + + std::string MonthlyVariableGroup::aggregationType() const { + return m_aggregationType; + } + + bool MonthlyVariableGroup::operator==(const MonthlyVariableGroup& other) const { + return openstudio::istringEqual(variableOrMeterName(), other.variableOrMeterName()) + && openstudio::istringEqual(aggregationType(), other.aggregationType()); + } + + bool MonthlyVariableGroup::operator!=(const MonthlyVariableGroup& other) const { + return (!operator==(other)); + } + + std::ostream& operator<<(std::ostream& out, const openstudio::model::MonthlyVariableGroup& monthlyVariableGroup) { + out << "(Output Variable or Meter = '" << monthlyVariableGroup.variableOrMeterName() << "', " + << "Aggregation Type = " << monthlyVariableGroup.aggregationType() << "')"; + return out; + } + namespace detail { OutputTableMonthly_Impl::OutputTableMonthly_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) @@ -51,19 +90,131 @@ namespace model { return result; } + unsigned int OutputTableMonthly_Impl::numberofMonthlyVariableGroups() const { + return numExtensibleGroups(); + } + + std::vector OutputTableMonthly_Impl::monthlyVariableGroups() const { + std::vector result; + + for (unsigned i = 0; i < numberofMonthlyVariableGroups(); ++i) { + + boost::optional group_ = getMonthlyVariableGroup(i); + + // getMonthlyVariableGroup is responsible for handling error and issuing Error log messages. + // Here we add it to the result array if it worked, and if it didn't, we keep going + // We just issue a message about index so user can delete it easily + if (group_) { + result.push_back(std::move(*group_)); + } else { + LOG(Error, briefDescription() << " has an invalid MonthlyVariableGroup group at index " << i); + } + } + + return result; + } + + boost::optional OutputTableMonthly_Impl::monthlyVariableGroupIndex(const MonthlyVariableGroup& monthlyVariableGroup) const { + const std::vector groups = monthlyVariableGroups(); + auto it = std::find(groups.cbegin(), groups.cend(), monthlyVariableGroup); + if (it != groups.end()) { + return std::distance(groups.cbegin(), it); + } + return boost::none; + } + + boost::optional OutputTableMonthly_Impl::getMonthlyVariableGroup(unsigned groupIndex) const { + if (groupIndex >= numberofMonthlyVariableGroups()) { + LOG(Error, "Asked to get MonthlyVariableGroup with index " << groupIndex << ", but " << briefDescription() << " has just " + << numberofMonthlyVariableGroups() << " MonthlyVariableGroups."); + return boost::none; + } + auto group = getExtensibleGroup(groupIndex).cast(); + + boost::optional variableOrMeterName_ = group.getString(OS_Output_Table_MonthlyExtensibleFields::VariableorMeterName); + boost::optional aggregationType_ = group.getString(OS_Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter); + + if (!variableOrMeterName_) { + LOG(Error, "Could not retrieve VariableorMeterName_ for extensible group " << group.groupIndex() << "."); + return boost::none; + } + if (!aggregationType_) { + LOG(Error, "Could not retrieve Aggregation Type for extensible group " << group.groupIndex() << "."); + return boost::none; + } + + return {MonthlyVariableGroup(std::move(*variableOrMeterName_), std::move(*aggregationType_))}; + } + + bool OutputTableMonthly_Impl::addMonthlyVariableGroup(const MonthlyVariableGroup& monthlyVariableGroup) { + boost::optional existingIndex_ = monthlyVariableGroupIndex(monthlyVariableGroup); + if (existingIndex_) { + LOG(Warn, "For " << briefDescription() << ", MonthlyVariableGroup already exists: " << monthlyVariableGroup); + return false; + } + + IdfExtensibleGroup eg = pushExtensibleGroup(StringVector()); + eg.setString(OS_Output_Table_MonthlyExtensibleFields::VariableorMeterName, monthlyVariableGroup.variableOrMeterName()); + eg.setString(OS_Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter, monthlyVariableGroup.aggregationType()); + return true; + } + + bool OutputTableMonthly_Impl::addMonthlyVariableGroups(const std::vector& monthlyVariableGroups) { + bool result = true; + + for (const auto& monthlyVariableGroup : monthlyVariableGroups) { + bool thisResult = addMonthlyVariableGroup(monthlyVariableGroup); + if (!thisResult) { + LOG(Error, "Could not add MonthlyVariableGroup " << monthlyVariableGroup << " to " << briefDescription() << ". Continuing with others."); + // OS_ASSERT(false); + // result = false; + } + } + + return result; + } + + bool OutputTableMonthly_Impl::removeMonthlyVariableGroup(unsigned groupIndex) { + bool result = false; + + unsigned int num = numberofMonthlyVariableGroups(); + if (groupIndex < num) { + getObject().eraseExtensibleGroup(groupIndex); + result = true; + } + return result; + } + + void OutputTableMonthly_Impl::removeAllMonthlyVariableGroups() { + getObject().clearExtensibleGroups(); + } + } // namespace detail OutputTableMonthly::OutputTableMonthly(const Model& model) : ModelObject(OutputTableMonthly::iddObjectType(), model) { OS_ASSERT(getImpl()); - - // TODO: consider adding (overloaded or not) explicit ctors taking required objects as argument - - // TODO: Appropriately handle the following required object-list fields. bool ok = true; - // ok = setDigitsAfterDecimal(); + ok = setDigitsAfterDecimal(2); OS_ASSERT(ok); } + std::vector OutputTableMonthly::aggregationTypeValues() { + IddObject obj = IddFactory::instance().getObject(iddObjectType()).get(); + // Return IddKeyNames in extensible portion + return getIddKeyNames(obj, obj.numFields() + OS_Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter); + } + + std::vector OutputTableMonthly::validAggregationTypes() { + return aggregationTypeValues(); + } + + bool OutputTableMonthly::isAggregationTypeValid(const std::string& aggregationType) { + const auto vals = aggregationTypeValues(); + return std::find_if(vals.cbegin(), vals.cend(), + [&aggregationType](const std::string& choice) { return openstudio::istringEqual(aggregationType, choice); }) + != vals.cend(); + } + IddObjectType OutputTableMonthly::iddObjectType() { return {IddObjectType::OS_Output_Table_Monthly}; } @@ -76,6 +227,42 @@ namespace model { return getImpl()->setDigitsAfterDecimal(digitsAfterDecimal); } + std::vector OutputTableMonthly::monthlyVariableGroups() const { + return getImpl()->monthlyVariableGroups(); + } + + unsigned int OutputTableMonthly::numberofMonthlyVariableGroups() const { + return getImpl()->numberofMonthlyVariableGroups(); + } + + boost::optional OutputTableMonthly::monthlyVariableGroupIndex(const MonthlyVariableGroup& monthlyVariableGroup) const { + return getImpl()->monthlyVariableGroupIndex(monthlyVariableGroup); + } + + boost::optional OutputTableMonthly::getMonthlyVariableGroup(unsigned groupIndex) const { + return getImpl()->getMonthlyVariableGroup(groupIndex); + } + + bool OutputTableMonthly::addMonthlyVariableGroup(const MonthlyVariableGroup& group) { + return getImpl()->addMonthlyVariableGroup(group); + } + + bool OutputTableMonthly::addMonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType) { + return addMonthlyVariableGroup(MonthlyVariableGroup(std::move(variableOrMeterName), std::move(aggregationType))); + } + + bool OutputTableMonthly::addMonthlyVariableGroups(const std::vector& monthlyVariableGroups) { + return getImpl()->addMonthlyVariableGroups(monthlyVariableGroups); + } + + bool OutputTableMonthly::removeMonthlyVariableGroup(unsigned groupIndex) { + return getImpl()->removeMonthlyVariableGroup(groupIndex); + } + + void OutputTableMonthly::removeAllMonthlyVariableGroups() { + getImpl()->removeAllMonthlyVariableGroups(); + } + /// @cond OutputTableMonthly::OutputTableMonthly(std::shared_ptr impl) : ModelObject(std::move(impl)) {} /// @endcond diff --git a/src/model/OutputTableMonthly.hpp b/src/model/OutputTableMonthly.hpp index d942f932a35..15918f77e6b 100644 --- a/src/model/OutputTableMonthly.hpp +++ b/src/model/OutputTableMonthly.hpp @@ -18,6 +18,26 @@ namespace model { } // namespace detail + class MODEL_API MonthlyVariableGroup + { + public: + MonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType = "SumOrAverage"); + + std::string variableOrMeterName() const; + std::string aggregationType() const; + + bool operator==(const MonthlyVariableGroup& other) const; + bool operator!=(const MonthlyVariableGroup& other) const; + + private: + std::string m_variableOrMeterName; + std::string m_aggregationType; + REGISTER_LOGGER("openstudio.model.MonthlyVariableGroup"); + }; + + // Overload operator<< + MODEL_API std::ostream& operator<<(std::ostream& out, const openstudio::model::MonthlyVariableGroup& monthlyVariableGroup); + /** OutputTableMonthly is a ModelObject that wraps the OpenStudio IDD object 'OS:Output:Table:Monthly'. */ class MODEL_API OutputTableMonthly : public ModelObject { @@ -38,25 +58,43 @@ namespace model { static IddObjectType iddObjectType(); + static std::vector aggregationTypeValues(); + static std::vector validAggregationTypes(); + static bool isAggregationTypeValid(const std::string& aggregationType); + /** @name Getters */ //@{ int digitsAfterDecimal() const; - // TODO: Handle this object's extensible fields. - //@} /** @name Setters */ //@{ bool setDigitsAfterDecimal(int digitsAfterDecimal); - // TODO: Handle this object's extensible fields. - //@} /** @name Other */ //@{ + // Handle this object's extensible fields. + + std::vector monthlyVariableGroups() const; + + unsigned int numberofMonthlyVariableGroups() const; + + boost::optional monthlyVariableGroupIndex(const MonthlyVariableGroup& monthlyVariableGroup) const; + boost::optional getMonthlyVariableGroup(unsigned groupIndex) const; + + bool addMonthlyVariableGroup(const MonthlyVariableGroup& monthlyVariableGroup); + + // Convenience fucntion that will create a MonthlyVariableGroup + bool addMonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType = "SumOrAverage"); + + bool addMonthlyVariableGroups(const std::vector& monthlyVariableGroups); + bool removeMonthlyVariableGroup(unsigned groupIndex); + void removeAllMonthlyVariableGroups(); + //@} protected: /// @cond diff --git a/src/model/OutputTableMonthly_Impl.hpp b/src/model/OutputTableMonthly_Impl.hpp index 66eaf873615..f1a2bea7efc 100644 --- a/src/model/OutputTableMonthly_Impl.hpp +++ b/src/model/OutputTableMonthly_Impl.hpp @@ -12,6 +12,8 @@ namespace openstudio { namespace model { + class MonthlyVariableGroup; + namespace detail { /** OutputTableMonthly_Impl is a ModelObject_Impl that is the implementation class for OutputTableMonthly.*/ @@ -43,20 +45,29 @@ namespace model { int digitsAfterDecimal() const; - // TODO: Handle this object's extensible fields. - //@} /** @name Setters */ //@{ bool setDigitsAfterDecimal(int digitsAfterDecimal); - // TODO: Handle this object's extensible fields. - //@} /** @name Other */ //@{ + // Handle this object's extensible fields. + std::vector monthlyVariableGroups() const; + + unsigned int numberofMonthlyVariableGroups() const; + + boost::optional monthlyVariableGroupIndex(const MonthlyVariableGroup& monthlyVariableGroup) const; + boost::optional getMonthlyVariableGroup(unsigned groupIndex) const; + + bool addMonthlyVariableGroup(const MonthlyVariableGroup& group); + bool addMonthlyVariableGroups(const std::vector& monthlyVariableGroups); + bool removeMonthlyVariableGroup(unsigned groupIndex); + void removeAllMonthlyVariableGroups(); + //@} protected: private: From ae929d8196184ba66990c8fc8a2e3094a6b91cac Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 17:07:01 +0100 Subject: [PATCH 05/19] Model tests for OutputTableMonthly --- src/model/CMakeLists.txt | 1 + src/model/test/OutputTableMonthly_GTest.cpp | 102 +++++++++++++++++++- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index c21e2aba543..9b1caa5254c 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -2209,6 +2209,7 @@ set(${target_name}_test_src test/OutputMeter_GTest.cpp test/OutputVariable_GTest.cpp test/OutputTableSummaryReports_GTest.cpp + test/OutputTableMonthly_GTest.cpp test/OutputSchedules_GTest.cpp test/OutputConstructions_GTest.cpp test/ParentObject_GTest.cpp diff --git a/src/model/test/OutputTableMonthly_GTest.cpp b/src/model/test/OutputTableMonthly_GTest.cpp index 971d96b8339..6d6d148a350 100644 --- a/src/model/test/OutputTableMonthly_GTest.cpp +++ b/src/model/test/OutputTableMonthly_GTest.cpp @@ -13,13 +13,13 @@ using namespace openstudio::model; TEST_F(ModelFixture, OutputTableMonthly_GettersSetters) { Model m; - // TODO: Check regular Ctor arguments OutputTableMonthly outputTableMonthly(m); - // TODO: Or if a UniqueModelObject (and make sure _Impl is included) - // OutputTableMonthly outputTableMonthly = m.getUniqueModelObject(); outputTableMonthly.setName("My OutputTableMonthly"); + // default moved to ctor + EXPECT_EQ(2, outputTableMonthly.digitsAfterDecimal()); + // Digits After Decimal: Required Integer EXPECT_TRUE(outputTableMonthly.setDigitsAfterDecimal(7)); EXPECT_EQ(7, outputTableMonthly.digitsAfterDecimal()); @@ -27,3 +27,99 @@ TEST_F(ModelFixture, OutputTableMonthly_GettersSetters) { EXPECT_FALSE(outputTableMonthly.setDigitsAfterDecimal(-10)); EXPECT_EQ(7, outputTableMonthly.digitsAfterDecimal()); } + +TEST_F(ModelFixture, OutputTableMonthly_MonthlyVariableGroups) { + + Model m; + + // Wrong Aggregation Type (not in choices) + EXPECT_FALSE(OutputTableMonthly::isAggregationTypeValid("BADAGGREGATION")); + EXPECT_THROW(MonthlyVariableGroup("Zone Air System Sensible Cooling Energy", "BADAGGREGATION"), openstudio::Exception); + + // Name can't be empty + EXPECT_THROW(MonthlyVariableGroup("", "SumOrAverage"), openstudio::Exception); + + EXPECT_NO_THROW(MonthlyVariableGroup("Zone Air System Sensible Cooling Energy", "SumOrAverage")); + EXPECT_NO_THROW(MonthlyVariableGroup("Zone Air System Sensible Cooling Energy")); + + OutputTableMonthly table(m); + + EXPECT_EQ(0, table.numberofMonthlyVariableGroups()); + EXPECT_TRUE(table.addMonthlyVariableGroup("Zone Air System Sensible Cooling Energy")); + EXPECT_EQ(1, table.numberofMonthlyVariableGroups()); + EXPECT_EQ("SumOrAverage", table.getMonthlyVariableGroup(0)->aggregationType()); + + auto group2 = MonthlyVariableGroup("Zone Air System Sensible Cooling Energy", "Maximum"); + EXPECT_TRUE(table.addMonthlyVariableGroup(group2)); + EXPECT_EQ(2, table.numberofMonthlyVariableGroups()); + + auto group3 = MonthlyVariableGroup("Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"); + EXPECT_TRUE(table.addMonthlyVariableGroup(group3)); + EXPECT_EQ(3, table.numberofMonthlyVariableGroups()); + + boost::optional group_ = table.getMonthlyVariableGroup(1U); + ASSERT_TRUE(group_); + EXPECT_EQ("Zone Air System Sensible Cooling Energy", group_->variableOrMeterName()); + EXPECT_EQ("Maximum", group_->aggregationType()); + + // Test that you cannot add the same group twice + auto group2bis = MonthlyVariableGroup("Zone Air System Sensible Cooling Energy", "Maximum"); + // Test that we can locate an existing group correctly + boost::optional _existingIndex = table.monthlyVariableGroupIndex(group2bis); + ASSERT_TRUE(_existingIndex); + EXPECT_EQ(1U, _existingIndex.get()); + + // Now call add, which should return false + EXPECT_FALSE(table.addMonthlyVariableGroup(group2bis)); + // Should still have the same number + EXPECT_EQ(3, table.numberofMonthlyVariableGroups()); + + // Via the overloaded method, also check that it doesn't care about case-sensitivity + EXPECT_FALSE(table.addMonthlyVariableGroup("Zone Air System SENSIBLE Cooling Energy", "MAXIMUM")); + EXPECT_EQ(3, table.numberofMonthlyVariableGroups()); + + // Test that you cannot get a group by an index that's too high + EXPECT_FALSE(table.getMonthlyVariableGroup(table.numberofMonthlyVariableGroups())); + + // Test that you cannot find a group if it doesn't exist + auto groupother = MonthlyVariableGroup("Another surface", "Maximum"); + EXPECT_FALSE(table.monthlyVariableGroupIndex(groupother)); + + // Remove + EXPECT_FALSE(table.removeMonthlyVariableGroup(1000)); + EXPECT_EQ(3, table.numberofMonthlyVariableGroups()); + + EXPECT_TRUE(table.removeMonthlyVariableGroup(1)); + EXPECT_EQ(2, table.numberofMonthlyVariableGroups()); + + // check that remaining blocks moved correctly + std::vector groups = table.monthlyVariableGroups(); + EXPECT_EQ(2U, groups.size()); + EXPECT_EQ("Zone Air System Sensible Cooling Energy", groups[0].variableOrMeterName()); + EXPECT_EQ("SumOrAverage", groups[0].aggregationType()); + + EXPECT_EQ("Site Outdoor Air Drybulb Temperature", groups[1].variableOrMeterName()); + EXPECT_EQ("ValueWhenMaximumOrMinimum", groups[1].aggregationType()); + + // more remove checking + table.removeAllMonthlyVariableGroups(); + EXPECT_EQ(0, table.numberofMonthlyVariableGroups()); + EXPECT_FALSE(table.removeMonthlyVariableGroup(0)); + EXPECT_EQ(0, table.numberofMonthlyVariableGroups()); + + // Via a vector + EXPECT_TRUE(table.addMonthlyVariableGroups({group2bis, groupother, group3})); + groups = table.monthlyVariableGroups(); + EXPECT_EQ(3U, groups.size()); + EXPECT_EQ(group2bis.variableOrMeterName(), groups[0].variableOrMeterName()); + EXPECT_EQ(group2bis.aggregationType(), groups[0].aggregationType()); + EXPECT_EQ(group2bis, groups[0]); + + EXPECT_EQ(groupother.variableOrMeterName(), groups[1].variableOrMeterName()); + EXPECT_EQ(groupother.aggregationType(), groups[1].aggregationType()); + EXPECT_EQ(groupother, groups[1]); + + EXPECT_EQ(group3.variableOrMeterName(), groups[2].variableOrMeterName()); + EXPECT_EQ(group3.aggregationType(), groups[2].aggregationType()); + EXPECT_EQ(group3, groups[2]); +} From c6df0c03b2146bd210252d47f9acca5f97a2d815 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 17:07:11 +0100 Subject: [PATCH 06/19] SWIG it in ModelCore --- src/model/ModelCore.i | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/model/ModelCore.i b/src/model/ModelCore.i index 11e0a077870..a679a4bfd88 100644 --- a/src/model/ModelCore.i +++ b/src/model/ModelCore.i @@ -327,6 +327,7 @@ MODELOBJECT_TEMPLATES(ScheduleYear); MODELOBJECT_TEMPLATES(ScheduleRule); MODELOBJECT_TEMPLATES(ScheduleRuleset); MODELOBJECT_TEMPLATES(OutputVariable); +MODELOBJECT_TEMPLATES(OutputTableMonthly); MODELOBJECT_TEMPLATES(AdditionalProperties); MODELOBJECT_TEMPLATES(GenericModelObject); MODELOBJECT_TEMPLATES(ModelObjectList); @@ -380,6 +381,7 @@ SWIG_MODELOBJECT(ScheduleYear, 1); SWIG_MODELOBJECT(ScheduleRule, 1); SWIG_MODELOBJECT(ScheduleRuleset, 1); SWIG_MODELOBJECT(OutputVariable, 1); +SWIG_MODELOBJECT(OutputTableMonthly, 1); SWIG_MODELOBJECT(AdditionalProperties, 1); SWIG_MODELOBJECT(GenericModelObject, 0); SWIG_MODELOBJECT(ModelObjectList, 1); From acc2a5ad00e840fd9441b8d4fc12a06a8b292b4f Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 17:08:30 +0100 Subject: [PATCH 07/19] FT + RT + tests for OutputTableMonthly --- src/energyplus/CMakeLists.txt | 3 + src/energyplus/ForwardTranslator.cpp | 7 ++ src/energyplus/ForwardTranslator.hpp | 3 + .../ForwardTranslateOutputTableMonthly.cpp | 45 ++++++++ src/energyplus/ReverseTranslator.cpp | 3 +- src/energyplus/ReverseTranslator.hpp | 2 + .../ReverseTranslateOutputTableMonthly.cpp | 60 +++++++++++ .../Test/OutputTableMonthly_GTest.cpp | 101 ++++++++++++++++++ 8 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 src/energyplus/ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp create mode 100644 src/energyplus/ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp create mode 100644 src/energyplus/Test/OutputTableMonthly_GTest.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index fd1d301ed63..a06e014636c 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -275,6 +275,7 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateEnvironmentalImpactFactors.cpp ForwardTranslator/ForwardTranslateFuelFactors.cpp ForwardTranslator/ForwardTranslateOutputTableSummaryReports.cpp + ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp ForwardTranslator/ForwardTranslatePeople.cpp ForwardTranslator/ForwardTranslatePerformancePrecisionTradeoffs.cpp ForwardTranslator/ForwardTranslatePhotovoltaicPerformanceEquivalentOneDiode.cpp @@ -574,6 +575,7 @@ set(${target_name}_src ReverseTranslator/ReverseTranslateOutputMeterMeterFileOnly.cpp ReverseTranslator/ReverseTranslateOutputVariable.cpp ReverseTranslator/ReverseTranslateOutputTableSummaryReports.cpp + ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp ReverseTranslator/ReverseTranslatePeople.cpp ReverseTranslator/ReverseTranslatePerformancePrecisionTradeoffs.cpp ReverseTranslator/ReverseTranslatePhotovoltaicPerformanceSandia.cpp @@ -786,6 +788,7 @@ set(${target_name}_test_src Test/OutputSQLite_GTest.cpp Test/OutputEnvironmentalImpactFactors_GTest.cpp Test/OutputTableSummaryReports_GTest.cpp + Test/OutputTableMonthly_GTest.cpp Test/OutputSchedules_GTest.cpp Test/OutputConstructions_GTest.cpp Test/People_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 8eaef4ae050..1f56330e408 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -2380,6 +2380,11 @@ namespace energyplus { retVal = translateOutputTableSummaryReports(summaryReports); break; } + case openstudio::IddObjectType::OS_Output_Table_Monthly: { + auto mo = modelObject.cast(); + retVal = translateOutputTableMonthly(mo); + break; + } case openstudio::IddObjectType::OS_People: { auto people = modelObject.cast(); retVal = translatePeople(people); @@ -3387,6 +3392,8 @@ namespace energyplus { // IddObjectType::OS_FuelFactors, IddObjectType::OS_Output_Table_SummaryReports, + IddObjectType::OS_Output_Table_Monthly, + IddObjectType::OS_PerformancePrecisionTradeoffs, IddObjectType::OS_Site, diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index e2699e9f9ae..3a0e83cab42 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -311,6 +311,7 @@ namespace model { class OutputVariable; class OutputEnergyManagementSystem; class OutputTableSummaryReports; + class OutputTableMonthly; class People; class PerformancePrecisionTradeoffs; class PhotovoltaicPerformanceEquivalentOneDiode; @@ -1201,6 +1202,8 @@ namespace energyplus { boost::optional translateOutputTableSummaryReports(model::OutputTableSummaryReports& modelObject); + boost::optional translateOutputTableMonthly(model::OutputTableMonthly& modelObject); + boost::optional translatePeople(model::People& modelObject); boost::optional translatePerformancePrecisionTradeoffs(model::PerformancePrecisionTradeoffs& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp new file mode 100644 index 00000000000..573245ada77 --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp @@ -0,0 +1,45 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" + +#include "../../model/OutputTableMonthly.hpp" + +#include "../../utilities/idf/IdfExtensibleGroup.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ForwardTranslator::translateOutputTableMonthly(model::OutputTableMonthly& modelObject) { + + if (modelObject.numberofMonthlyVariableGroups() == 0) { + return boost::none; + } + + // Instantiate an IdfObject of the class to store the values + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::Output_Table_Monthly, modelObject); + + // Digits After Decimal: Optional Integer, we set it in ctor + const int digitsAfterDecimal = modelObject.digitsAfterDecimal(); + idfObject.setInt(Output_Table_MonthlyFields::DigitsAfterDecimal, digitsAfterDecimal); + + for (const auto& group : modelObject.monthlyVariableGroups()) { + IdfExtensibleGroup eg = idfObject.pushExtensibleGroup(); + eg.setString(Output_Table_MonthlyExtensibleFields::VariableorMeterName, group.variableOrMeterName()); + eg.setString(Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter, group.aggregationType()); + } + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/ReverseTranslator.cpp b/src/energyplus/ReverseTranslator.cpp index efbceb6d899..316b1a1965b 100644 --- a/src/energyplus/ReverseTranslator.cpp +++ b/src/energyplus/ReverseTranslator.cpp @@ -698,7 +698,8 @@ namespace energyplus { break; } case openstudio::IddObjectType::Output_Table_Monthly: { - break; // no-op + modelObject = translateOutputTableMonthly(workspaceObject); + break; } case openstudio::IddObjectType::Output_Table_SummaryReports: { modelObject = translateOutputTableSummaryReports(workspaceObject); diff --git a/src/energyplus/ReverseTranslator.hpp b/src/energyplus/ReverseTranslator.hpp index b3270564212..61431bd862c 100644 --- a/src/energyplus/ReverseTranslator.hpp +++ b/src/energyplus/ReverseTranslator.hpp @@ -251,6 +251,8 @@ namespace energyplus { boost::optional translateOutputTableSummaryReports(const WorkspaceObject& workspaceObject); + boost::optional translateOutputTableMonthly(const WorkspaceObject& workspaceObject); + boost::optional translateOtherEquipment(const WorkspaceObject& workspaceObject); boost::optional translateOutputEnergyManagementSystem(const WorkspaceObject& workspaceObject); diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp new file mode 100644 index 00000000000..e66021748cb --- /dev/null +++ b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp @@ -0,0 +1,60 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ReverseTranslator.hpp" + +#include "../../model/OutputTableMonthly.hpp" + +#include "../../utilities/idf/IdfExtensibleGroup.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ReverseTranslator::translateOutputTableMonthly(const WorkspaceObject& workspaceObject) { + + openstudio::model::OutputTableMonthly modelObject(m_model); + + // Name + modelObject.setName(workspaceObject.nameString()); + + // Digits After Decimal: Optional Integer + if (boost::optional digitsAfterDecimal_ = workspaceObject.getInt(Output_Table_MonthlyFields::DigitsAfterDecimal)) { + modelObject.setDigitsAfterDecimal(digitsAfterDecimal_.get()); + } + + // Extensible groups + for (const IdfExtensibleGroup& eg : workspaceObject.extensibleGroups()) { + auto variableorMeterName = eg.getString(Output_Table_MonthlyExtensibleFields::VariableorMeterName).get(); + if (variableorMeterName.empty()) { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) has an empty Variable or Meter Name field, skipping group"); + continue; + } + auto aggregationType = eg.getString(Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter).get(); + if (aggregationType.empty()) { + LOG(Warn, + "Extensible group " << eg.groupIndex() << "(0-indexed) has an empty Aggregation Type for VariableorMeter field, assuming SumOrAverage."); + aggregationType = "SumOrAverage"; + } else if (!OutputTableMonthly::isAggregationTypeValid(aggregationType)) { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) has an Aggregation Type = '" << aggregationType + << "' that is invalid. Skipping group."); + continue; + } + + if (!modelObject.addMonthlyVariableGroup(variableorMeterName, aggregationType)) { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) is already present, not adding it twice"); + } + } + + return modelObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/Test/OutputTableMonthly_GTest.cpp b/src/energyplus/Test/OutputTableMonthly_GTest.cpp new file mode 100644 index 00000000000..9aa75d71e3c --- /dev/null +++ b/src/energyplus/Test/OutputTableMonthly_GTest.cpp @@ -0,0 +1,101 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" +#include "../ReverseTranslator.hpp" + +#include "../../model/OutputTableMonthly.hpp" +#include "../../model/OutputTableMonthly_Impl.hpp" + +#include "../../utilities/idf/Workspace.hpp" +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +// E+ FieldEnums +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_OutputTableMonthly) { + + ForwardTranslator ft; + + Model m; + OutputTableMonthly outputTableMonthly(m); + outputTableMonthly.setName("Zone Temperature Oscillation Report"); + EXPECT_TRUE(outputTableMonthly.setDigitsAfterDecimal(6)); + + { + // Zero Extensible Groups = not translated + const Workspace w = ft.translateModel(m); + const auto idfObjs = w.getObjectsByType(IddObjectType::Output_Table_Monthly); + EXPECT_TRUE(idfObjs.empty()); + } + + { + std::vector groups{ + MonthlyVariableGroup("Zone Oscillating Temperatures Time", "HoursNonZero"), + MonthlyVariableGroup("Zone People Occupant Count", "SumOrAverageDuringHoursShown"), + }; + EXPECT_TRUE(outputTableMonthly.addMonthlyVariableGroups(groups)); + EXPECT_EQ(2, outputTableMonthly.numberofMonthlyVariableGroups()); + + // Zero Extensible Groups = not translated + const Workspace w = ft.translateModel(m); + const auto wos = w.getObjectsByType(IddObjectType::Output_Table_Monthly); + ASSERT_EQ(1u, wos.size()); + const auto& wo = wos.front(); + EXPECT_EQ("Zone Temperature Oscillation Report", wo.nameString()); + EXPECT_EQ(6, wo.getInt(Output_Table_MonthlyFields::DigitsAfterDecimal).get()); + ASSERT_EQ(2, wo.numExtensibleGroups()); + for (const auto& idf_eg : wo.extensibleGroups()) { + auto& group = groups[idf_eg.groupIndex()]; + + EXPECT_EQ(group.variableOrMeterName(), idf_eg.getString(Output_Table_MonthlyExtensibleFields::VariableorMeterName).get()); + EXPECT_EQ(group.aggregationType(), idf_eg.getString(Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter).get()); + } + } +} + +TEST_F(EnergyPlusFixture, ReverseTranslator_OutputTableMonthly) { + + ReverseTranslator rt; + + Workspace w(StrictnessLevel::Minimal, IddFileType::EnergyPlus); + + auto woOTM = w.addObject(IdfObject(IddObjectType::Output_Table_Monthly)).get(); + + woOTM.setName("My OutputTableMonthly"); + EXPECT_TRUE(woOTM.setInt(Output_Table_MonthlyFields::DigitsAfterDecimal, 6)); + woOTM.pushExtensibleGroup({"Boiler Heating Energy", "Maximum"}); + woOTM.pushExtensibleGroup({"Boiler NaturalGas Energy", "SumOrAverage"}); + + const Model m = rt.translateWorkspace(w); + const auto modelObjects = m.getConcreteModelObjects(); + ASSERT_EQ(1u, modelObjects.size()); + + const auto& modelObject = modelObjects.front(); + EXPECT_EQ(6, modelObject.digitsAfterDecimal()); + EXPECT_EQ(2, modelObject.numberofMonthlyVariableGroups()); + { + auto group = modelObject.getMonthlyVariableGroup(0).get(); + EXPECT_EQ("Boiler Heating Energy", group.variableOrMeterName()); + EXPECT_EQ("Maximum", group.aggregationType()); + } + { + auto group = modelObject.getMonthlyVariableGroup(1).get(); + EXPECT_EQ("Boiler NaturalGas Energy", group.variableOrMeterName()); + EXPECT_EQ("SumOrAverage", group.aggregationType()); + } +} From 1429817581dcdb2aa1c8524f73d8774b54d78604 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 20:40:32 +0100 Subject: [PATCH 08/19] Model Implementation for OutputTableAnnual --- src/model/CMakeLists.txt | 4 + src/model/ConcreteModelObjects.hpp | 2 + src/model/Model.cpp | 2 + src/model/ModelCore.i | 2 + src/model/OutputTableAnnual.cpp | 332 +++++++++++++++++++++ src/model/OutputTableAnnual.hpp | 140 +++++++++ src/model/OutputTableAnnual_Impl.hpp | 93 ++++++ src/model/ScheduleTypeRegistry.cpp | 1 + src/model/test/OutputTableAnnual_GTest.cpp | 153 ++++++++++ 9 files changed, 729 insertions(+) create mode 100644 src/model/OutputTableAnnual.cpp create mode 100644 src/model/OutputTableAnnual.hpp create mode 100644 src/model/OutputTableAnnual_Impl.hpp create mode 100644 src/model/test/OutputTableAnnual_GTest.cpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 9b1caa5254c..e11982d4107 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -1117,6 +1117,9 @@ set(${target_name}_src OutputTableSummaryReports.hpp OutputTableSummaryReports_Impl.hpp OutputTableSummaryReports.cpp + OutputTableAnnual.hpp + OutputTableAnnual_Impl.hpp + OutputTableAnnual.cpp OutputTableMonthly.hpp OutputTableMonthly_Impl.hpp OutputTableMonthly.cpp @@ -2209,6 +2212,7 @@ set(${target_name}_test_src test/OutputMeter_GTest.cpp test/OutputVariable_GTest.cpp test/OutputTableSummaryReports_GTest.cpp + test/OutputTableAnnual_GTest.cpp test/OutputTableMonthly_GTest.cpp test/OutputSchedules_GTest.cpp test/OutputConstructions_GTest.cpp diff --git a/src/model/ConcreteModelObjects.hpp b/src/model/ConcreteModelObjects.hpp index 4adf5c427be..df0cba1e357 100644 --- a/src/model/ConcreteModelObjects.hpp +++ b/src/model/ConcreteModelObjects.hpp @@ -346,6 +346,7 @@ #include "OutputMeter.hpp" #include "OutputVariable.hpp" #include "OutputTableSummaryReports.hpp" +#include "OutputTableAnnual.hpp" #include "OutputTableMonthly.hpp" #include "OutsideSurfaceConvectionAlgorithm.hpp" #include "People.hpp" @@ -903,6 +904,7 @@ #include "OutputMeter_Impl.hpp" #include "OutputVariable_Impl.hpp" #include "OutputTableSummaryReports_Impl.hpp" +#include "OutputTableAnnual_Impl.hpp" #include "OutputTableMonthly_Impl.hpp" #include "OutsideSurfaceConvectionAlgorithm_Impl.hpp" #include "People_Impl.hpp" diff --git a/src/model/Model.cpp b/src/model/Model.cpp index 165bcbd82af..7d43bf68abc 100644 --- a/src/model/Model.cpp +++ b/src/model/Model.cpp @@ -4242,6 +4242,7 @@ namespace model { REGISTER_CONSTRUCTOR(FuelFactors); REGISTER_CONSTRUCTOR(OutputMeter); REGISTER_CONSTRUCTOR(OutputVariable); + REGISTER_CONSTRUCTOR(OutputTableAnnual); REGISTER_CONSTRUCTOR(OutputTableMonthly); REGISTER_CONSTRUCTOR(OutputTableSummaryReports); REGISTER_CONSTRUCTOR(OutsideSurfaceConvectionAlgorithm); @@ -4819,6 +4820,7 @@ namespace model { REGISTER_COPYCONSTRUCTORS(FuelFactors); REGISTER_COPYCONSTRUCTORS(OutputMeter); REGISTER_COPYCONSTRUCTORS(OutputVariable); + REGISTER_COPYCONSTRUCTORS(OutputTableAnnual); REGISTER_COPYCONSTRUCTORS(OutputTableMonthly); REGISTER_COPYCONSTRUCTORS(OutputTableSummaryReports); REGISTER_COPYCONSTRUCTORS(OutsideSurfaceConvectionAlgorithm); diff --git a/src/model/ModelCore.i b/src/model/ModelCore.i index a679a4bfd88..05866f08f57 100644 --- a/src/model/ModelCore.i +++ b/src/model/ModelCore.i @@ -327,6 +327,7 @@ MODELOBJECT_TEMPLATES(ScheduleYear); MODELOBJECT_TEMPLATES(ScheduleRule); MODELOBJECT_TEMPLATES(ScheduleRuleset); MODELOBJECT_TEMPLATES(OutputVariable); +MODELOBJECT_TEMPLATES(OutputTableAnnual); MODELOBJECT_TEMPLATES(OutputTableMonthly); MODELOBJECT_TEMPLATES(AdditionalProperties); MODELOBJECT_TEMPLATES(GenericModelObject); @@ -381,6 +382,7 @@ SWIG_MODELOBJECT(ScheduleYear, 1); SWIG_MODELOBJECT(ScheduleRule, 1); SWIG_MODELOBJECT(ScheduleRuleset, 1); SWIG_MODELOBJECT(OutputVariable, 1); +SWIG_MODELOBJECT(OutputTableAnnual, 1); SWIG_MODELOBJECT(OutputTableMonthly, 1); SWIG_MODELOBJECT(AdditionalProperties, 1); SWIG_MODELOBJECT(GenericModelObject, 0); diff --git a/src/model/OutputTableAnnual.cpp b/src/model/OutputTableAnnual.cpp new file mode 100644 index 00000000000..80b7c420832 --- /dev/null +++ b/src/model/OutputTableAnnual.cpp @@ -0,0 +1,332 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "OutputTableAnnual.hpp" +#include "OutputTableAnnual_Impl.hpp" + +#include "ModelExtensibleGroup.hpp" + +#include "Schedule.hpp" +#include "Schedule_Impl.hpp" +#include "ScheduleTypeLimits.hpp" +#include "ScheduleTypeRegistry.hpp" + +#include "../utilities/core/Assert.hpp" + +#include +#include +#include +#include +#include + +namespace openstudio { +namespace model { + + AnnualVariableGroup::AnnualVariableGroup(std::string variableorMeterorEMSVariableorField, std::string aggregationType, int digitsAfterDecimal) + : m_variableorMeterorEMSVariableorField(std::move(variableorMeterorEMSVariableorField)), + m_aggregationType(std::move(aggregationType)), + m_digitsAfterDecimal(digitsAfterDecimal) { + if (m_variableorMeterorEMSVariableorField.empty()) { + LOG_AND_THROW("Variable or Meter or EMSVariable or Field Name can't be empty"); + } + if (!OutputTableAnnual::isAggregationTypeValid(m_aggregationType)) { + LOG_AND_THROW(fmt::format("Invalid aggregation type '{}': {}", m_aggregationType, OutputTableAnnual::aggregationTypeValues())); + } + } + + std::string AnnualVariableGroup::variableorMeterorEMSVariableorField() const { + return m_variableorMeterorEMSVariableorField; + } + + std::string AnnualVariableGroup::aggregationType() const { + return m_aggregationType; + } + + int AnnualVariableGroup::digitsAfterDecimal() const { + return m_digitsAfterDecimal; + } + + bool AnnualVariableGroup::operator==(const AnnualVariableGroup& other) const { + return openstudio::istringEqual(variableorMeterorEMSVariableorField(), other.variableorMeterorEMSVariableorField()) + && openstudio::istringEqual(aggregationType(), other.aggregationType()); + } + + bool AnnualVariableGroup::operator!=(const AnnualVariableGroup& other) const { + return (!operator==(other)); + } + + std::ostream& operator<<(std::ostream& out, const openstudio::model::AnnualVariableGroup& annualVariableGroup) { + out << "(Output Variable or Meter = '" << annualVariableGroup.variableorMeterorEMSVariableorField() << "', " + << "Aggregation Type = '" << annualVariableGroup.aggregationType() << "', Digits After Decimal = " << annualVariableGroup.digitsAfterDecimal() + << ")"; + return out; + } + + namespace detail { + + OutputTableAnnual_Impl::OutputTableAnnual_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(idfObject, model, keepHandle) { + OS_ASSERT(idfObject.iddObject().type() == OutputTableAnnual::iddObjectType()); + } + + OutputTableAnnual_Impl::OutputTableAnnual_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(other, model, keepHandle) { + OS_ASSERT(other.iddObject().type() == OutputTableAnnual::iddObjectType()); + } + + OutputTableAnnual_Impl::OutputTableAnnual_Impl(const OutputTableAnnual_Impl& other, Model_Impl* model, bool keepHandle) + : ModelObject_Impl(other, model, keepHandle) {} + + const std::vector& OutputTableAnnual_Impl::outputVariableNames() const { + static std::vector result; + if (result.empty()) { + } + return result; + } + + IddObjectType OutputTableAnnual_Impl::iddObjectType() const { + return OutputTableAnnual::iddObjectType(); + } + + std::vector OutputTableAnnual_Impl::getScheduleTypeKeys(const Schedule& schedule) const { + std::vector result; + const UnsignedVector fieldIndices = getSourceIndices(schedule.handle()); + if (std::find(fieldIndices.cbegin(), fieldIndices.cend(), OS_Output_Table_AnnualFields::ScheduleName) != fieldIndices.cend()) { + result.emplace_back("OutputTableAnnual", "Output Table Annual"); + } + return result; + } + + boost::optional OutputTableAnnual_Impl::filter() const { + return getString(OS_Output_Table_AnnualFields::Filter, true, true); + } + + boost::optional OutputTableAnnual_Impl::schedule() const { + return getObject().getModelObjectTarget(OS_Output_Table_AnnualFields::ScheduleName); + } + + bool OutputTableAnnual_Impl::setFilter(const std::string& filter) { + const bool result = setString(OS_Output_Table_AnnualFields::Filter, filter); + OS_ASSERT(result); + return result; + } + + void OutputTableAnnual_Impl::resetFilter() { + const bool result = setString(OS_Output_Table_AnnualFields::Filter, ""); + OS_ASSERT(result); + } + + bool OutputTableAnnual_Impl::setSchedule(Schedule& schedule) { + const bool result = + ModelObject_Impl::setSchedule(OS_Output_Table_AnnualFields::ScheduleName, "OutputTableAnnual", "Output Table Annual", schedule); + return result; + } + + void OutputTableAnnual_Impl::resetSchedule() { + const bool result = setString(OS_Output_Table_AnnualFields::ScheduleName, ""); + OS_ASSERT(result); + } + + unsigned int OutputTableAnnual_Impl::numberofAnnualVariableGroups() const { + return numExtensibleGroups(); + } + + std::vector OutputTableAnnual_Impl::annualVariableGroups() const { + std::vector result; + + for (unsigned i = 0; i < numberofAnnualVariableGroups(); ++i) { + + boost::optional group_ = getAnnualVariableGroup(i); + + // getAnnualVariableGroup is responsible for handling error and issuing Error log messages. + // Here we add it to the result array if it worked, and if it didn't, we keep going + // We just issue a message about index so user can delete it easily + if (group_) { + result.push_back(std::move(*group_)); + } else { + LOG(Error, briefDescription() << " has an invalid AnnualVariableGroup group at index " << i); + } + } + + return result; + } + + boost::optional OutputTableAnnual_Impl::annualVariableGroupIndex(const AnnualVariableGroup& annualVariableGroup) const { + const std::vector groups = annualVariableGroups(); + auto it = std::find(groups.cbegin(), groups.cend(), annualVariableGroup); + if (it != groups.end()) { + return std::distance(groups.cbegin(), it); + } + return boost::none; + } + + boost::optional OutputTableAnnual_Impl::getAnnualVariableGroup(unsigned groupIndex) const { + if (groupIndex >= numberofAnnualVariableGroups()) { + LOG(Error, "Asked to get AnnualVariableGroup with index " << groupIndex << ", but " << briefDescription() << " has just " + << numberofAnnualVariableGroups() << " AnnualVariableGroups."); + return boost::none; + } + auto group = getExtensibleGroup(groupIndex).cast(); + + boost::optional variableorMeterorEMSVariableorField_ = + group.getString(OS_Output_Table_AnnualExtensibleFields::VariableorMeterorEMSVariableorFieldName); + boost::optional aggregationType_ = group.getString(OS_Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter); + int digitsAfterDecimal = group.getInt(OS_Output_Table_AnnualExtensibleFields::DigitsAfterDecimal).value_or(2); + + if (!variableorMeterorEMSVariableorField_) { + LOG(Error, "Could not retrieve VariableorMeterName_ for extensible group " << group.groupIndex() << "."); + return boost::none; + } + if (!aggregationType_) { + LOG(Error, "Could not retrieve Aggregation Type for extensible group " << group.groupIndex() << "."); + return boost::none; + } + + return {AnnualVariableGroup(std::move(*variableorMeterorEMSVariableorField_), std::move(*aggregationType_), digitsAfterDecimal)}; + } + + bool OutputTableAnnual_Impl::addAnnualVariableGroup(const AnnualVariableGroup& annualVariableGroup) { + boost::optional existingIndex_ = annualVariableGroupIndex(annualVariableGroup); + if (existingIndex_) { + boost::optional group_ = getAnnualVariableGroup(existingIndex_.get()); + OS_ASSERT(group_); + LOG(Warn, "For " << briefDescription() << ", AnnualVariableGroup already exists, will be modified in place from " << group_.get() << " to " + << annualVariableGroup << "."); + } + + // If existing, get it, otherwise Push an extensible group. ModelExtensibleGroup cannot be default-constructed, so use a ternary operator + IdfExtensibleGroup eg = (existingIndex_ ? getExtensibleGroup(existingIndex_.get()) : pushExtensibleGroup({}, false)); + eg.setString(OS_Output_Table_AnnualExtensibleFields::VariableorMeterorEMSVariableorFieldName, + annualVariableGroup.variableorMeterorEMSVariableorField()); + eg.setString(OS_Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter, annualVariableGroup.aggregationType()); + eg.setInt(OS_Output_Table_AnnualExtensibleFields::DigitsAfterDecimal, annualVariableGroup.digitsAfterDecimal()); + return true; + } + + bool OutputTableAnnual_Impl::addAnnualVariableGroups(const std::vector& annualVariableGroups) { + bool result = true; + + for (const auto& annualVariableGroup : annualVariableGroups) { + bool thisResult = addAnnualVariableGroup(annualVariableGroup); + if (!thisResult) { + LOG(Error, "Could not add AnnualVariableGroup " << annualVariableGroup << " to " << briefDescription() << ". Continuing with others."); + // OS_ASSERT(false); + // result = false; + } + } + + return result; + } + + bool OutputTableAnnual_Impl::removeAnnualVariableGroup(unsigned groupIndex) { + bool result = false; + + unsigned int num = numberofAnnualVariableGroups(); + if (groupIndex < num) { + getObject().eraseExtensibleGroup(groupIndex); + result = true; + } + return result; + } + + void OutputTableAnnual_Impl::removeAllAnnualVariableGroups() { + getObject().clearExtensibleGroups(); + } + + } // namespace detail + + OutputTableAnnual::OutputTableAnnual(const Model& model) : ModelObject(OutputTableAnnual::iddObjectType(), model) { + OS_ASSERT(getImpl()); + } + + std::vector OutputTableAnnual::aggregationTypeValues() { + IddObject obj = IddFactory::instance().getObject(iddObjectType()).get(); + // Return IddKeyNames in extensible portion + return getIddKeyNames(obj, obj.numFields() + OS_Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter); + } + + std::vector OutputTableAnnual::validAggregationTypes() { + return aggregationTypeValues(); + } + + bool OutputTableAnnual::isAggregationTypeValid(const std::string& aggregationType) { + const auto vals = aggregationTypeValues(); + return std::find_if(vals.cbegin(), vals.cend(), + [&aggregationType](const std::string& choice) { return openstudio::istringEqual(aggregationType, choice); }) + != vals.cend(); + } + + IddObjectType OutputTableAnnual::iddObjectType() { + return {IddObjectType::OS_Output_Table_Annual}; + } + + boost::optional OutputTableAnnual::filter() const { + return getImpl()->filter(); + } + + boost::optional OutputTableAnnual::schedule() const { + return getImpl()->schedule(); + } + + bool OutputTableAnnual::setFilter(const std::string& filter) { + return getImpl()->setFilter(filter); + } + + void OutputTableAnnual::resetFilter() { + getImpl()->resetFilter(); + } + + bool OutputTableAnnual::setSchedule(Schedule& schedule) { + return getImpl()->setSchedule(schedule); + } + + void OutputTableAnnual::resetSchedule() { + getImpl()->resetSchedule(); + } + + std::vector OutputTableAnnual::annualVariableGroups() const { + return getImpl()->annualVariableGroups(); + } + + unsigned int OutputTableAnnual::numberofAnnualVariableGroups() const { + return getImpl()->numberofAnnualVariableGroups(); + } + + boost::optional OutputTableAnnual::annualVariableGroupIndex(const AnnualVariableGroup& annualVariableGroup) const { + return getImpl()->annualVariableGroupIndex(annualVariableGroup); + } + + boost::optional OutputTableAnnual::getAnnualVariableGroup(unsigned groupIndex) const { + return getImpl()->getAnnualVariableGroup(groupIndex); + } + + bool OutputTableAnnual::addAnnualVariableGroup(const AnnualVariableGroup& group) { + return getImpl()->addAnnualVariableGroup(group); + } + + bool OutputTableAnnual::addAnnualVariableGroup(std::string variableorMeterorEMSVariableorField, std::string aggregationType, + int digitsAfterDecimal) { + return addAnnualVariableGroup( + AnnualVariableGroup(std::move(variableorMeterorEMSVariableorField), std::move(aggregationType), digitsAfterDecimal)); + } + + bool OutputTableAnnual::addAnnualVariableGroups(const std::vector& annualVariableGroups) { + return getImpl()->addAnnualVariableGroups(annualVariableGroups); + } + + bool OutputTableAnnual::removeAnnualVariableGroup(unsigned groupIndex) { + return getImpl()->removeAnnualVariableGroup(groupIndex); + } + + void OutputTableAnnual::removeAllAnnualVariableGroups() { + getImpl()->removeAllAnnualVariableGroups(); + } + + /// @cond + OutputTableAnnual::OutputTableAnnual(std::shared_ptr impl) : ModelObject(std::move(impl)) {} + /// @endcond + +} // namespace model +} // namespace openstudio diff --git a/src/model/OutputTableAnnual.hpp b/src/model/OutputTableAnnual.hpp new file mode 100644 index 00000000000..6e96cab4e64 --- /dev/null +++ b/src/model/OutputTableAnnual.hpp @@ -0,0 +1,140 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_OUTPUTTABLEANNUAL_HPP +#define MODEL_OUTPUTTABLEANNUAL_HPP + +#include +#include "ModelObject.hpp" + +namespace openstudio { +namespace model { + + // TODO: Check the following class names against object getters and setters. + class Schedule; + + namespace detail { + + class OutputTableAnnual_Impl; + + } // namespace detail + + class MODEL_API AnnualVariableGroup + { + public: + AnnualVariableGroup(std::string variableorMeterorEMSVariableorField, std::string aggregationType = "SumOrAverage", int digitsAfterDecimal = 2); + + std::string variableorMeterorEMSVariableorField() const; + std::string aggregationType() const; + int digitsAfterDecimal() const; + + // Only checks on variableorMeterorEMSVariableorField and aggregationType, not digitsAfterDecimal + bool operator==(const AnnualVariableGroup& other) const; + bool operator!=(const AnnualVariableGroup& other) const; + + private: + std::string m_variableorMeterorEMSVariableorField; + std::string m_aggregationType; + int m_digitsAfterDecimal; + REGISTER_LOGGER("openstudio.model.AnnualVariableGroup"); + }; + + // Overload operator<< + MODEL_API std::ostream& operator<<(std::ostream& out, const openstudio::model::AnnualVariableGroup& annualVariableGroup); + + /** OutputTableAnnual is a ModelObject that wraps the OpenStudio IDD object 'OS:Output:Table:Annual'. */ + class MODEL_API OutputTableAnnual : public ModelObject + { + public: + /** @name Constructors and Destructors */ + //@{ + + explicit OutputTableAnnual(const Model& model); + + virtual ~OutputTableAnnual() = default; + // Default the copy and move operators because the virtual dtor is explicit + OutputTableAnnual(const OutputTableAnnual& other) = default; + OutputTableAnnual(OutputTableAnnual&& other) = default; + OutputTableAnnual& operator=(const OutputTableAnnual&) = default; + OutputTableAnnual& operator=(OutputTableAnnual&&) = default; + + //@} + + static IddObjectType iddObjectType(); + + static std::vector aggregationTypeValues(); + static std::vector validAggregationTypes(); + static bool isAggregationTypeValid(const std::string& aggregationType); + + /** @name Getters */ + //@{ + + boost::optional filter() const; + + boost::optional schedule() const; + + //@} + /** @name Setters */ + //@{ + + bool setFilter(const std::string& filter); + + void resetFilter(); + + bool setSchedule(Schedule& schedule); + + void resetSchedule(); + + // TODO: Handle this object's extensible fields. + + //@} + /** @name Other */ + //@{ + + // Handle this object's extensible fields. + + std::vector annualVariableGroups() const; + + unsigned int numberofAnnualVariableGroups() const; + + boost::optional annualVariableGroupIndex(const AnnualVariableGroup& annualVariableGroup) const; + boost::optional getAnnualVariableGroup(unsigned groupIndex) const; + + bool addAnnualVariableGroup(const AnnualVariableGroup& annualVariableGroup); + + // Convenience fucntion that will create a AnnualVariableGroup + bool addAnnualVariableGroup(std::string variableorMeterorEMSVariableorField, std::string aggregationType = "SumOrAverage", + int digitsAfterDecimal = 2); + + bool addAnnualVariableGroups(const std::vector& annualVariableGroups); + bool removeAnnualVariableGroup(unsigned groupIndex); + void removeAllAnnualVariableGroups(); + + //@} + protected: + /// @cond + using ImplType = detail::OutputTableAnnual_Impl; + + explicit OutputTableAnnual(std::shared_ptr impl); + + friend class detail::OutputTableAnnual_Impl; + friend class Model; + friend class IdfObject; + friend class openstudio::detail::IdfObject_Impl; + /// @endcond + private: + REGISTER_LOGGER("openstudio.model.OutputTableAnnual"); + }; + + /** \relates OutputTableAnnual*/ + using OptionalOutputTableAnnual = boost::optional; + + /** \relates OutputTableAnnual*/ + using OutputTableAnnualVector = std::vector; + +} // namespace model +} // namespace openstudio + +#endif // MODEL_OUTPUTTABLEANNUAL_HPP diff --git a/src/model/OutputTableAnnual_Impl.hpp b/src/model/OutputTableAnnual_Impl.hpp new file mode 100644 index 00000000000..68c7e7a7a36 --- /dev/null +++ b/src/model/OutputTableAnnual_Impl.hpp @@ -0,0 +1,93 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#ifndef MODEL_OUTPUTTABLEANNUAL_IMPL_HPP +#define MODEL_OUTPUTTABLEANNUAL_IMPL_HPP + +#include +#include "ModelObject_Impl.hpp" + +namespace openstudio { +namespace model { + + class AnnualVariableGroup; + class Schedule; + + namespace detail { + + /** OutputTableAnnual_Impl is a ModelObject_Impl that is the implementation class for OutputTableAnnual.*/ + class MODEL_API OutputTableAnnual_Impl : public ModelObject_Impl + { + public: + /** @name Constructors and Destructors */ + //@{ + + OutputTableAnnual_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle); + + OutputTableAnnual_Impl(const openstudio::detail::WorkspaceObject_Impl& other, Model_Impl* model, bool keepHandle); + + OutputTableAnnual_Impl(const OutputTableAnnual_Impl& other, Model_Impl* model, bool keepHandle); + + virtual ~OutputTableAnnual_Impl() = default; + + //@} + /** @name Virtual Methods */ + //@{ + + virtual const std::vector& outputVariableNames() const override; + + virtual IddObjectType iddObjectType() const override; + + virtual std::vector getScheduleTypeKeys(const Schedule& schedule) const override; + + //@} + /** @name Getters */ + //@{ + + boost::optional filter() const; + + boost::optional schedule() const; + + //@} + /** @name Setters */ + //@{ + + bool setFilter(const std::string& filter); + + void resetFilter(); + + bool setSchedule(Schedule& schedule); + + void resetSchedule(); + + //@} + /** @name Other */ + //@{ + + // Handle this object's extensible fields. + std::vector annualVariableGroups() const; + + unsigned int numberofAnnualVariableGroups() const; + + boost::optional annualVariableGroupIndex(const AnnualVariableGroup& annualVariableGroup) const; + boost::optional getAnnualVariableGroup(unsigned groupIndex) const; + + bool addAnnualVariableGroup(const AnnualVariableGroup& group); + bool addAnnualVariableGroups(const std::vector& annualVariableGroups); + bool removeAnnualVariableGroup(unsigned groupIndex); + void removeAllAnnualVariableGroups(); + + //@} + protected: + private: + REGISTER_LOGGER("openstudio.model.OutputTableAnnual"); + }; + + } // namespace detail + +} // namespace model +} // namespace openstudio + +#endif // MODEL_OUTPUTTABLEANNUAL_IMPL_HPP diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp index c8cff936208..123b45cfa31 100644 --- a/src/model/ScheduleTypeRegistry.cpp +++ b/src/model/ScheduleTypeRegistry.cpp @@ -326,6 +326,7 @@ namespace model { {"Lights", "Lighting", "schedule", true, "", 0.0, 1.0}, {"Luminaire", "Luminaire", "schedule", true, "", 0.0, 1.0}, {"OtherEquipment", "Other Equipment", "schedule", true, "", OptionalDouble(), OptionalDouble()}, + {"OutputTableAnnual", "Output Table Annual", "schedule", false, "Availability", 0.0, 1.0}, {"OutputVariable", "Active Hours", "schedule", false, "Availability", 0.0, 1.0}, {"People", "Number of People", "numberofPeopleSchedule", true, "", 0.0, 1.0}, {"People", "Activity Level", "activityLevelSchedule", true, "ActivityLevel", 0.0, OptionalDouble()}, diff --git a/src/model/test/OutputTableAnnual_GTest.cpp b/src/model/test/OutputTableAnnual_GTest.cpp new file mode 100644 index 00000000000..b9b6a344b23 --- /dev/null +++ b/src/model/test/OutputTableAnnual_GTest.cpp @@ -0,0 +1,153 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../OutputTableAnnual.hpp" +#include "../OutputTableAnnual_Impl.hpp" + +#include "../Schedule.hpp" +#include "../ScheduleConstant.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, OutputTableAnnual_GettersSetters) { + Model m; + OutputTableAnnual outputTableAnnual(m); + + outputTableAnnual.setName("My OutputTableAnnual"); + + // Filter: Optional String + EXPECT_FALSE(outputTableAnnual.filter()); + EXPECT_TRUE(outputTableAnnual.setFilter("KeyValue")); + ASSERT_TRUE(outputTableAnnual.filter()); + EXPECT_EQ("KeyValue", outputTableAnnual.filter().get()); + outputTableAnnual.resetFilter(); + EXPECT_FALSE(outputTableAnnual.filter()); + + // Schedule Name: Optional Object + EXPECT_FALSE(outputTableAnnual.schedule()); + ScheduleConstant sch(m); + EXPECT_TRUE(outputTableAnnual.setSchedule(sch)); + ASSERT_TRUE(outputTableAnnual.schedule()); + EXPECT_EQ(sch, outputTableAnnual.schedule().get()); + outputTableAnnual.resetSchedule(); + EXPECT_FALSE(outputTableAnnual.schedule()); +} + +TEST_F(ModelFixture, OutputTableAnnual_AnnualVariableGroups) { + + Model m; + + // Wrong Aggregation Type (not in choices) + EXPECT_FALSE(OutputTableAnnual::isAggregationTypeValid("BADAGGREGATION")); + EXPECT_THROW(AnnualVariableGroup("Zone Air System Sensible Cooling Energy", "BADAGGREGATION"), openstudio::Exception); + + // Name can't be empty + EXPECT_THROW(AnnualVariableGroup("", "SumOrAverage"), openstudio::Exception); + + EXPECT_NO_THROW(AnnualVariableGroup("Zone Air System Sensible Cooling Energy", "SumOrAverage", 2)); + EXPECT_NO_THROW(AnnualVariableGroup("Zone Air System Sensible Cooling Energy", "SumOrAverage")); + EXPECT_NO_THROW(AnnualVariableGroup("Zone Air System Sensible Cooling Energy")); + + OutputTableAnnual table(m); + + EXPECT_EQ(0, table.numberofAnnualVariableGroups()); + EXPECT_TRUE(table.addAnnualVariableGroup("Zone Air System Sensible Cooling Energy")); + EXPECT_EQ(1, table.numberofAnnualVariableGroups()); + EXPECT_EQ("SumOrAverage", table.getAnnualVariableGroup(0)->aggregationType()); + EXPECT_EQ(2, table.getAnnualVariableGroup(0)->digitsAfterDecimal()); + + auto group2 = AnnualVariableGroup("Zone Air System Sensible Cooling Energy", "Maximum", 3); + EXPECT_TRUE(table.addAnnualVariableGroup(group2)); + EXPECT_EQ(2, table.numberofAnnualVariableGroups()); + + auto group3 = AnnualVariableGroup("Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum", 4); + EXPECT_TRUE(table.addAnnualVariableGroup(group3)); + EXPECT_EQ(3, table.numberofAnnualVariableGroups()); + + boost::optional group_ = table.getAnnualVariableGroup(1U); + ASSERT_TRUE(group_); + EXPECT_EQ("Zone Air System Sensible Cooling Energy", group_->variableorMeterorEMSVariableorField()); + EXPECT_EQ("Maximum", group_->aggregationType()); + EXPECT_EQ(3, group_->digitsAfterDecimal()); + + // Test that you cannot add the same group twice, it'll replace it + auto group2bis = AnnualVariableGroup("Zone Air System Sensible Cooling Energy", "Maximum", 6); + // Test that we can locate an existing group correctly + boost::optional _existingIndex = table.annualVariableGroupIndex(group2bis); + ASSERT_TRUE(_existingIndex); + EXPECT_EQ(1U, _existingIndex.get()); + + // Now call add, which should return true + EXPECT_TRUE(table.addAnnualVariableGroup(group2bis)); + // Should still have the same number + EXPECT_EQ(3, table.numberofAnnualVariableGroups()); + group_ = table.getAnnualVariableGroup(1U); + ASSERT_TRUE(group_); + EXPECT_EQ("Zone Air System Sensible Cooling Energy", group_->variableorMeterorEMSVariableorField()); + EXPECT_EQ("Maximum", group_->aggregationType()); + EXPECT_EQ(6, group_->digitsAfterDecimal()); + + // Via the overloaded method, also check that it doesn't care about case-sensitivity + EXPECT_TRUE(table.addAnnualVariableGroup("Zone Air System SENSIBLE Cooling Energy", "MAXIMUM", 8)); + EXPECT_EQ(3, table.numberofAnnualVariableGroups()); + group_ = table.getAnnualVariableGroup(1U); + ASSERT_TRUE(group_); + EXPECT_EQ("Zone Air System SENSIBLE Cooling Energy", group_->variableorMeterorEMSVariableorField()); + EXPECT_EQ("MAXIMUM", group_->aggregationType()); + EXPECT_EQ(8, group_->digitsAfterDecimal()); + + // Test that you cannot get a group by an index that's too high + EXPECT_FALSE(table.getAnnualVariableGroup(table.numberofAnnualVariableGroups())); + + // Test that you cannot find a group if it doesn't exist + auto groupother = AnnualVariableGroup("Another var", "Maximum"); + EXPECT_FALSE(table.annualVariableGroupIndex(groupother)); + + // Remove + EXPECT_FALSE(table.removeAnnualVariableGroup(1000)); + EXPECT_EQ(3, table.numberofAnnualVariableGroups()); + + EXPECT_TRUE(table.removeAnnualVariableGroup(1)); + EXPECT_EQ(2, table.numberofAnnualVariableGroups()); + + // check that remaining blocks moved correctly + std::vector groups = table.annualVariableGroups(); + EXPECT_EQ(2U, groups.size()); + EXPECT_EQ("Zone Air System Sensible Cooling Energy", groups[0].variableorMeterorEMSVariableorField()); + EXPECT_EQ("SumOrAverage", groups[0].aggregationType()); + EXPECT_EQ(2, groups[0].digitsAfterDecimal()); + + EXPECT_EQ("Site Outdoor Air Drybulb Temperature", groups[1].variableorMeterorEMSVariableorField()); + EXPECT_EQ("ValueWhenMaximumOrMinimum", groups[1].aggregationType()); + EXPECT_EQ(4, groups[1].digitsAfterDecimal()); + + // more remove checking + table.removeAllAnnualVariableGroups(); + EXPECT_EQ(0, table.numberofAnnualVariableGroups()); + EXPECT_FALSE(table.removeAnnualVariableGroup(0)); + EXPECT_EQ(0, table.numberofAnnualVariableGroups()); + + // Via a vector + EXPECT_TRUE(table.addAnnualVariableGroups({group2bis, groupother, group3})); + groups = table.annualVariableGroups(); + EXPECT_EQ(3U, groups.size()); + EXPECT_EQ(group2bis.variableorMeterorEMSVariableorField(), groups[0].variableorMeterorEMSVariableorField()); + EXPECT_EQ(group2bis.aggregationType(), groups[0].aggregationType()); + EXPECT_EQ(group2bis.digitsAfterDecimal(), groups[0].digitsAfterDecimal()); + EXPECT_EQ(group2bis, groups[0]); + + EXPECT_EQ(groupother.variableorMeterorEMSVariableorField(), groups[1].variableorMeterorEMSVariableorField()); + EXPECT_EQ(groupother.aggregationType(), groups[1].aggregationType()); + EXPECT_EQ(groupother.digitsAfterDecimal(), groups[1].digitsAfterDecimal()); + EXPECT_EQ(groupother, groups[1]); + + EXPECT_EQ(group3.variableorMeterorEMSVariableorField(), groups[2].variableorMeterorEMSVariableorField()); + EXPECT_EQ(group3.aggregationType(), groups[2].aggregationType()); + EXPECT_EQ(group3.digitsAfterDecimal(), groups[2].digitsAfterDecimal()); + EXPECT_EQ(group3, groups[2]); +} From f48fccfa31af297fe3c6766ad2391c16cfc0748e Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:11:28 +0100 Subject: [PATCH 09/19] FT and RT + Tests for OutputTableAnnual --- src/energyplus/CMakeLists.txt | 7 +- src/energyplus/ForwardTranslator.cpp | 6 + src/energyplus/ForwardTranslator.hpp | 5 +- .../ForwardTranslateOutputTableAnnual.cpp | 55 ++++++++ src/energyplus/ReverseTranslator.cpp | 4 + src/energyplus/ReverseTranslator.hpp | 2 + .../ReverseTranslateOutputTableAnnual.cpp | 73 +++++++++++ .../Test/OutputTableAnnual_GTest.cpp | 121 ++++++++++++++++++ .../Test/OutputTableMonthly_GTest.cpp | 1 - 9 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 src/energyplus/ForwardTranslator/ForwardTranslateOutputTableAnnual.cpp create mode 100644 src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp create mode 100644 src/energyplus/Test/OutputTableAnnual_GTest.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index a06e014636c..388722f2c10 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -274,8 +274,9 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateOutputConstructions.cpp ForwardTranslator/ForwardTranslateEnvironmentalImpactFactors.cpp ForwardTranslator/ForwardTranslateFuelFactors.cpp - ForwardTranslator/ForwardTranslateOutputTableSummaryReports.cpp + ForwardTranslator/ForwardTranslateOutputTableAnnual.cpp ForwardTranslator/ForwardTranslateOutputTableMonthly.cpp + ForwardTranslator/ForwardTranslateOutputTableSummaryReports.cpp ForwardTranslator/ForwardTranslatePeople.cpp ForwardTranslator/ForwardTranslatePerformancePrecisionTradeoffs.cpp ForwardTranslator/ForwardTranslatePhotovoltaicPerformanceEquivalentOneDiode.cpp @@ -574,8 +575,9 @@ set(${target_name}_src ReverseTranslator/ReverseTranslateOutputMeterCumulativeMeterFileOnly.cpp ReverseTranslator/ReverseTranslateOutputMeterMeterFileOnly.cpp ReverseTranslator/ReverseTranslateOutputVariable.cpp - ReverseTranslator/ReverseTranslateOutputTableSummaryReports.cpp + ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp ReverseTranslator/ReverseTranslateOutputTableMonthly.cpp + ReverseTranslator/ReverseTranslateOutputTableSummaryReports.cpp ReverseTranslator/ReverseTranslatePeople.cpp ReverseTranslator/ReverseTranslatePerformancePrecisionTradeoffs.cpp ReverseTranslator/ReverseTranslatePhotovoltaicPerformanceSandia.cpp @@ -788,6 +790,7 @@ set(${target_name}_test_src Test/OutputSQLite_GTest.cpp Test/OutputEnvironmentalImpactFactors_GTest.cpp Test/OutputTableSummaryReports_GTest.cpp + Test/OutputTableAnnual_GTest.cpp Test/OutputTableMonthly_GTest.cpp Test/OutputSchedules_GTest.cpp Test/OutputConstructions_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 1f56330e408..4ab2a0b7b93 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -2380,6 +2380,11 @@ namespace energyplus { retVal = translateOutputTableSummaryReports(summaryReports); break; } + case openstudio::IddObjectType::OS_Output_Table_Annual: { + auto mo = modelObject.cast(); + retVal = translateOutputTableAnnual(mo); + break; + } case openstudio::IddObjectType::OS_Output_Table_Monthly: { auto mo = modelObject.cast(); retVal = translateOutputTableMonthly(mo); @@ -3392,6 +3397,7 @@ namespace energyplus { // IddObjectType::OS_FuelFactors, IddObjectType::OS_Output_Table_SummaryReports, + IddObjectType::OS_Output_Table_Annual, IddObjectType::OS_Output_Table_Monthly, IddObjectType::OS_PerformancePrecisionTradeoffs, diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index 3a0e83cab42..3752765324e 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -310,8 +310,9 @@ namespace model { class OutputMeter; class OutputVariable; class OutputEnergyManagementSystem; - class OutputTableSummaryReports; + class OutputTableAnnual; class OutputTableMonthly; + class OutputTableSummaryReports; class People; class PerformancePrecisionTradeoffs; class PhotovoltaicPerformanceEquivalentOneDiode; @@ -1202,6 +1203,8 @@ namespace energyplus { boost::optional translateOutputTableSummaryReports(model::OutputTableSummaryReports& modelObject); + boost::optional translateOutputTableAnnual(model::OutputTableAnnual& modelObject); + boost::optional translateOutputTableMonthly(model::OutputTableMonthly& modelObject); boost::optional translatePeople(model::People& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableAnnual.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableAnnual.cpp new file mode 100644 index 00000000000..6ee90123710 --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateOutputTableAnnual.cpp @@ -0,0 +1,55 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" + +#include "../../model/OutputTableAnnual.hpp" + +#include "../../model/Schedule.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ForwardTranslator::translateOutputTableAnnual(model::OutputTableAnnual& modelObject) { + + if (modelObject.numberofAnnualVariableGroups() == 0) { + return boost::none; + } + + // Instantiate an IdfObject of the class to store the values + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::Output_Table_Annual, modelObject); + + // Filter: boost::optional + if (boost::optional filter_ = modelObject.filter()) { + idfObject.setString(Output_Table_AnnualFields::Filter, filter_.get()); + } + + // Schedule Name: Optional Object + if (boost::optional schedule_ = modelObject.schedule()) { + if (boost::optional wo_ = translateAndMapModelObject(schedule_.get())) { + idfObject.setString(Output_Table_AnnualFields::ScheduleName, wo_->nameString()); + } + } + + for (const auto& group : modelObject.annualVariableGroups()) { + IdfExtensibleGroup eg = idfObject.pushExtensibleGroup(); + eg.setString(Output_Table_AnnualExtensibleFields::VariableorMeterorEMSVariableorFieldName, group.variableorMeterorEMSVariableorField()); + eg.setString(Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter, group.aggregationType()); + eg.setInt(Output_Table_AnnualExtensibleFields::DigitsAfterDecimal, group.digitsAfterDecimal()); + } + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/ReverseTranslator.cpp b/src/energyplus/ReverseTranslator.cpp index 316b1a1965b..6d9217425d7 100644 --- a/src/energyplus/ReverseTranslator.cpp +++ b/src/energyplus/ReverseTranslator.cpp @@ -697,6 +697,10 @@ namespace energyplus { modelObject = translateOutputMeterMeterFileOnly(workspaceObject); break; } + case openstudio::IddObjectType::Output_Table_Annual: { + modelObject = translateOutputTableAnnual(workspaceObject); + break; + } case openstudio::IddObjectType::Output_Table_Monthly: { modelObject = translateOutputTableMonthly(workspaceObject); break; diff --git a/src/energyplus/ReverseTranslator.hpp b/src/energyplus/ReverseTranslator.hpp index 61431bd862c..ed68331a740 100644 --- a/src/energyplus/ReverseTranslator.hpp +++ b/src/energyplus/ReverseTranslator.hpp @@ -251,6 +251,8 @@ namespace energyplus { boost::optional translateOutputTableSummaryReports(const WorkspaceObject& workspaceObject); + boost::optional translateOutputTableAnnual(const WorkspaceObject& workspaceObject); + boost::optional translateOutputTableMonthly(const WorkspaceObject& workspaceObject); boost::optional translateOtherEquipment(const WorkspaceObject& workspaceObject); diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp new file mode 100644 index 00000000000..c6feb120937 --- /dev/null +++ b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp @@ -0,0 +1,73 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ReverseTranslator.hpp" + +#include "../../model/OutputTableAnnual.hpp" + +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" + +#include "../../utilities/idf/IdfExtensibleGroup.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ReverseTranslator::translateOutputTableAnnual(const WorkspaceObject& workspaceObject) { + + openstudio::model::OutputTableAnnual modelObject(m_model); + modelObject.setName(workspaceObject.nameString()); + + // Filter: Optional String + if (boost::optional filter_ = workspaceObject.getString(Output_Table_AnnualFields::Filter)) { + modelObject.setFilter(filter_.get()); + } + + // Schedule Name: Optional Object + if (boost::optional wo_ = workspaceObject.getTarget(Output_Table_AnnualFields::ScheduleName)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { + // TODO: check return types + if (boost::optional schedule_ = mo_->optionalCast()) { + modelObject.setSchedule(schedule_.get()); + } else { + LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Schedule Name'"); + } + } + } + + // Extensible groups + for (const IdfExtensibleGroup& eg : workspaceObject.extensibleGroups()) { + auto variableorMeterorEMSVariableorField = eg.getString(Output_Table_AnnualExtensibleFields::VariableorMeterorEMSVariableorFieldName).get(); + if (variableorMeterorEMSVariableorField.empty()) { + LOG(Warn, "Extensible group " << eg.groupIndex() + << "(0-indexed) has an empty Variable or Meter or EMS Variable or Field Name field, skipping group"); + continue; + } + auto aggregationType = eg.getString(Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter).get(); + if (aggregationType.empty()) { + LOG(Warn, + "Extensible group " << eg.groupIndex() << "(0-indexed) has an empty Aggregation Type for VariableorMeter field, assuming SumOrAverage."); + aggregationType = "SumOrAverage"; + } else if (!OutputTableAnnual::isAggregationTypeValid(aggregationType)) { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) has an Aggregation Type = '" << aggregationType + << "' that is invalid. Skipping group."); + continue; + } + const int num_digits = eg.getInt(Output_Table_AnnualExtensibleFields::DigitsAfterDecimal).value_or(2); + + modelObject.addAnnualVariableGroup(variableorMeterorEMSVariableorField, aggregationType, num_digits); + } + + return modelObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/Test/OutputTableAnnual_GTest.cpp b/src/energyplus/Test/OutputTableAnnual_GTest.cpp new file mode 100644 index 00000000000..a1a86ef8b73 --- /dev/null +++ b/src/energyplus/Test/OutputTableAnnual_GTest.cpp @@ -0,0 +1,121 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include +#include "EnergyPlusFixture.hpp" + +#include "../ForwardTranslator.hpp" +#include "../ReverseTranslator.hpp" + +#include "../../model/OutputTableAnnual.hpp" +#include "../../model/OutputTableAnnual_Impl.hpp" +#include "../../model/Schedule.hpp" +#include "../../model/Schedule_Impl.hpp" +#include "../../model/ScheduleConstant.hpp" + +#include "../../utilities/idf/Workspace.hpp" +#include "../../utilities/idf/IdfObject.hpp" +#include "../../utilities/idf/WorkspaceObject.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +// E+ FieldEnums +#include +#include +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_OutputTableAnnual) { + + ForwardTranslator ft; + + Model m; + OutputTableAnnual outputTableAnnual(m); + + outputTableAnnual.setName("Zone Electric Annual Summary"); + EXPECT_TRUE(outputTableAnnual.setFilter("Zone 1")); + ScheduleConstant schedule(m); + schedule.setName("SCH"); + EXPECT_TRUE(outputTableAnnual.setSchedule(schedule)); + { + // Zero Extensible Groups = not translated + const Workspace w = ft.translateModel(m); + const auto wos = w.getObjectsByType(IddObjectType::Output_Table_Annual); + EXPECT_TRUE(wos.empty()); + } + + { + std::vector groups{ + AnnualVariableGroup("Zone Lights Electricity Energy", "SumOrAverage", 4), + AnnualVariableGroup("Zone Lights Electricity Energy", "Maximum", 1), + }; + EXPECT_TRUE(outputTableAnnual.addAnnualVariableGroups(groups)); + EXPECT_EQ(2, outputTableAnnual.numberofAnnualVariableGroups()); + + const Workspace w = ft.translateModel(m); + const auto wos = w.getObjectsByType(IddObjectType::Output_Table_Annual); + ASSERT_EQ(1u, wos.size()); + const auto& wo = wos.front(); + EXPECT_EQ("Zone Electric Annual Summary", wo.nameString()); + ASSERT_TRUE(wo.getTarget(Output_Table_AnnualFields::ScheduleName)); + EXPECT_EQ("SCH", wo.getTarget(Output_Table_AnnualFields::ScheduleName)->nameString()); + ASSERT_TRUE(wo.getString(Output_Table_AnnualFields::Filter)); + EXPECT_EQ("Zone 1", wo.getString(Output_Table_AnnualFields::Filter).get()); + + ASSERT_EQ(2, wo.numExtensibleGroups()); + for (const auto& idf_eg : wo.extensibleGroups()) { + auto& group = groups[idf_eg.groupIndex()]; + EXPECT_EQ(group.variableorMeterorEMSVariableorField(), + idf_eg.getString(Output_Table_AnnualExtensibleFields::VariableorMeterorEMSVariableorFieldName).get()); + EXPECT_EQ(group.aggregationType(), idf_eg.getString(Output_Table_AnnualExtensibleFields::AggregationTypeforVariableorMeter).get()); + EXPECT_EQ(group.digitsAfterDecimal(), idf_eg.getInt(Output_Table_AnnualExtensibleFields::DigitsAfterDecimal).get()); + } + } +} + +TEST_F(EnergyPlusFixture, ReverseTranslator_OutputTableAnnual) { + + ReverseTranslator rt; + + Workspace w(StrictnessLevel::Minimal, IddFileType::EnergyPlus); + + auto woOTA = w.addObject(IdfObject(IddObjectType::Output_Table_Annual)).get(); + + woOTA.setName("My OutputTableAnnual"); + EXPECT_TRUE(woOTA.setString(Output_Table_AnnualFields::Filter, "Zone 1")); + auto woschedule = w.addObject(IdfObject(IddObjectType::Schedule_Constant)).get(); + woschedule.setName("My schedule"); + EXPECT_TRUE(woOTA.setPointer(Output_Table_AnnualFields::ScheduleName, woschedule.handle())); + + woOTA.pushExtensibleGroup({"Zone Lights Electricity Energy", "SumOrAverage", ""}); + woOTA.pushExtensibleGroup({"Zone Electric Equipment Electricity Energy", "Maximum", "1"}); + + const Model m = rt.translateWorkspace(w); + const auto modelObjects = m.getConcreteModelObjects(); + ASSERT_EQ(1u, modelObjects.size()); + + const auto& modelObject = modelObjects.front(); + ASSERT_TRUE(modelObject.filter()); + EXPECT_EQ("Zone 1", modelObject.filter().get()); + ASSERT_TRUE(modelObject.schedule()); + EXPECT_EQ("My schedule", modelObject.schedule()->nameString()); + + EXPECT_EQ(2, modelObject.numberofAnnualVariableGroups()); + { + auto group = modelObject.getAnnualVariableGroup(0).get(); + EXPECT_EQ("Zone Lights Electricity Energy", group.variableorMeterorEMSVariableorField()); + EXPECT_EQ("SumOrAverage", group.aggregationType()); + EXPECT_EQ(2, group.digitsAfterDecimal()); + } + { + auto group = modelObject.getAnnualVariableGroup(1).get(); + EXPECT_EQ("Zone Electric Equipment Electricity Energy", group.variableorMeterorEMSVariableorField()); + EXPECT_EQ("Maximum", group.aggregationType()); + EXPECT_EQ(1, group.digitsAfterDecimal()); + } +} diff --git a/src/energyplus/Test/OutputTableMonthly_GTest.cpp b/src/energyplus/Test/OutputTableMonthly_GTest.cpp index 9aa75d71e3c..33a9b055a07 100644 --- a/src/energyplus/Test/OutputTableMonthly_GTest.cpp +++ b/src/energyplus/Test/OutputTableMonthly_GTest.cpp @@ -51,7 +51,6 @@ TEST_F(EnergyPlusFixture, ForwardTranslator_OutputTableMonthly) { EXPECT_TRUE(outputTableMonthly.addMonthlyVariableGroups(groups)); EXPECT_EQ(2, outputTableMonthly.numberofMonthlyVariableGroups()); - // Zero Extensible Groups = not translated const Workspace w = ft.translateModel(m); const auto wos = w.getObjectsByType(IddObjectType::Output_Table_Monthly); ASSERT_EQ(1u, wos.size()); From 74d0e06a2215eca94ad454bc0e62da0ad1239398 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:20:11 +0100 Subject: [PATCH 10/19] Swig the helper classes --- src/model/ModelCore.i | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/model/ModelCore.i b/src/model/ModelCore.i index 05866f08f57..8929e7c45d0 100644 --- a/src/model/ModelCore.i +++ b/src/model/ModelCore.i @@ -302,6 +302,25 @@ namespace model { } }; +%extend openstudio::model::AnnualVariableGroup { + // Use the overloaded operator<< for string representation + std::string __str__() { + std::ostringstream os; + os << *$self; + return os.str(); + } +}; + +%extend openstudio::model::MonthlyVariableGroup { + // Use the overloaded operator<< for string representation + std::string __str__() { + std::ostringstream os; + os << *$self; + return os.str(); + } +}; + + //MODELOBJECT_TEMPLATES(ModelObject); // swig preprocessor did not seem to see these for other objects so these are defined above MODELOBJECT_TEMPLATES(ScheduleTypeKey); MODELOBJECT_TEMPLATES(EMSActuatorNames); @@ -327,7 +346,9 @@ MODELOBJECT_TEMPLATES(ScheduleYear); MODELOBJECT_TEMPLATES(ScheduleRule); MODELOBJECT_TEMPLATES(ScheduleRuleset); MODELOBJECT_TEMPLATES(OutputVariable); +MODELOBJECT_TEMPLATES(AnnualVariableGroup); // helper for extensible fields for OutputTableAnnual MODELOBJECT_TEMPLATES(OutputTableAnnual); +MODELOBJECT_TEMPLATES(MonthlyVariableGroup); // helper for extensible fields for OutputTableMonthly MODELOBJECT_TEMPLATES(OutputTableMonthly); MODELOBJECT_TEMPLATES(AdditionalProperties); MODELOBJECT_TEMPLATES(GenericModelObject); From d125bc457d20d741755c36afa290385e6b123168 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:25:16 +0100 Subject: [PATCH 11/19] Update TDB release notes a bit --- ...=> OpenStudio_Release_Notes_3_10_0_TDB.md} | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) rename developer/doc/ReleaseNotes/{OpenStudio_Release_Notes_3_9_1_TDB.md => OpenStudio_Release_Notes_3_10_0_TDB.md} (76%) diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md similarity index 76% rename from developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md rename to developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md index 9b78ef62b7f..f813f33c460 100644 --- a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_1_TDB.md +++ b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md @@ -1,8 +1,8 @@ -# OpenStudio Version 3.9.1 +# OpenStudio Version 3.10.0 _Release Notes_ - _TDB_ -These release notes describe version 3.9.1 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections: +These release notes describe version 3.10.0 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections: - Overview - Where to Find OpenStudio Documentation @@ -15,7 +15,7 @@ As of April 2020, development and distribution of the OpenStudioApplication and Below is the list of components that is included in this SDK installer: -__**OpenStudio SDK 3.9.1**__ +__**OpenStudio SDK 3.10.0**__ - EnergyPlus - Command Line Interface (CLI) - Radiance @@ -34,16 +34,16 @@ __**OpenStudio SDK 3.9.1**__ # Installation Notes -OpenStudio SDK 3.9.1 is supported on: +OpenStudio SDK 3.10.0 is supported on: * 64-bit Windows 7 – 11 * macOS: 11.6+ x86_64, 12.1+ arm64 * Ubuntu: 20.04 x86_64, 22.04 x86_64, 22.04 arm64 * Centos7 -OpenStudio SDK 3.9.1 supports [EnergyPlus Release @EP_VERSION@](https://github.com/NREL/EnergyPlus/releases/tag/v@EP_VERSION@), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK 3.9.1. +OpenStudio SDK 3.10.0 supports [EnergyPlus Release @EP_VERSION@](https://github.com/NREL/EnergyPlus/releases/tag/v@EP_VERSION@), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK 3.10.0. -OpenStudio SDK 3.9.1 supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK 3.9.1. +OpenStudio SDK 3.10.0 supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK 3.10.0. As usual, you can refer to the **[OpenStudio SDK Compatibility Matrix](https://github.com/NREL/OpenStudio/wiki/OpenStudio-SDK-Version-Compatibility-Matrix)** for more information. @@ -57,7 +57,7 @@ For help with common installation problems please visit [Getting Started](http:/ # OpenStudio SDK: Changelog -The 3.9.1 is a **** release. This update includes several new features, performance improvements, and bug fixes. +The 3.10.0 is a **** release. This update includes several new features, performance improvements, and bug fixes. ## C++ Workflow code @@ -67,7 +67,7 @@ As of OpenStudio SDK 3.7.0 a re-written workflow written in C++ is used by defau As of OpenStudio SDK 3.2.0, Python bindings are officially supported and distributed through Python Package Index (PyPI). To install, users will need to have Python3 installed along with pip and simply run the following command in a terminal window. -`pip install openstudio==3.9.1` +`pip install openstudio==3.10.0` Please see [openstudio on PyPi](https://pypi.org/project/openstudio/) for further instructions on how to install. Users can also visit the test channel at [openstudio on TestPyPi](https://test.pypi.org/project/openstudio/) to install development bindings. @@ -79,11 +79,17 @@ You can also refer to the [OpenStudio SDK Python Binding Version Compatibility M * The object was wrapped in the SDK. * Note: in EnergyPlus 24.2.0, the `Zone Relief Air Node Name` is an optional field. The OpenStudio SDK always fills with the connected zone's Exhaust Air Node, meaning the airflow is always being balanced by EnergyPlus: the object will extract air from the zone to balance the air supplied to the zone by the cooler outlet node. +* [#5369](https://github.com/NREL/OpenStudio/pull/5369) - Wrap Output:Table:Annual and Output:Table:Monthly + +* [#5365](https://github.com/NREL/OpenStudio/pull/5365) - E+ 25.1.0: Wrap OutputControl:ResilienceSummaries + +* [#5312](https://github.com/NREL/OpenStudio/pull/5312) - Wrap PythonPlugin:SearchPaths + ## Minor changes and bug fixes -Refer to the changelog on the release page at [v3.9.1](https://github.com/NREL/OpenStudio/releases/v3.9.1) +Refer to the changelog on the release page at [v3.10.0](https://github.com/NREL/OpenStudio/releases/v3.10.0) -**Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.9.0...v3.9.1 +**Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.9.0...v3.10.0 **New Contributors**: @@ -101,7 +107,7 @@ In addition to some refactoring, this release also included conversion of 90.1 d --- # This YAML header controls the pandoc (via TeX) to PDF settings # To convert the markdown to pdf, do `pandoc release_notes.md -o release_notes.pdf` -title: 'OpenStudio Release Notes - 3.9.1' +title: 'OpenStudio Release Notes - 3.10.0' author: - National Renewable Energy Laboratory colorlinks: true From c0d7e8d85da977890c1abf5bd2d8eef0a3a9aeb6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:27:59 +0100 Subject: [PATCH 12/19] missing ' in ostream. --- src/model/OutputTableMonthly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index 90d454acc91..3f7fda39dee 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -49,7 +49,7 @@ namespace model { std::ostream& operator<<(std::ostream& out, const openstudio::model::MonthlyVariableGroup& monthlyVariableGroup) { out << "(Output Variable or Meter = '" << monthlyVariableGroup.variableOrMeterName() << "', " - << "Aggregation Type = " << monthlyVariableGroup.aggregationType() << "')"; + << "Aggregation Type = '" << monthlyVariableGroup.aggregationType() << "')"; return out; } From 958b921ff4ae500fd63b84d501091a790368a9d6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:29:03 +0100 Subject: [PATCH 13/19] clang-format --- src/model/OutputTableAnnual.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/model/OutputTableAnnual.cpp b/src/model/OutputTableAnnual.cpp index 80b7c420832..7482c806e27 100644 --- a/src/model/OutputTableAnnual.cpp +++ b/src/model/OutputTableAnnual.cpp @@ -58,9 +58,8 @@ namespace model { } std::ostream& operator<<(std::ostream& out, const openstudio::model::AnnualVariableGroup& annualVariableGroup) { - out << "(Output Variable or Meter = '" << annualVariableGroup.variableorMeterorEMSVariableorField() << "', " - << "Aggregation Type = '" << annualVariableGroup.aggregationType() << "', Digits After Decimal = " << annualVariableGroup.digitsAfterDecimal() - << ")"; + out << "(Output Variable or Meter = '" << annualVariableGroup.variableorMeterorEMSVariableorField() << "', " << "Aggregation Type = '" + << annualVariableGroup.aggregationType() << "', Digits After Decimal = " << annualVariableGroup.digitsAfterDecimal() << ")"; return out; } From e1475f3c1c07dbeaba45c069c58d8f7ba4c348a3 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 21:33:07 +0100 Subject: [PATCH 14/19] cppcheck --- src/model/OutputTableAnnual.cpp | 4 ++-- src/model/OutputTableAnnual.hpp | 2 +- src/model/OutputTableAnnual_Impl.hpp | 4 ++-- src/model/OutputTableMonthly.cpp | 4 ++-- src/model/OutputTableMonthly.hpp | 2 +- src/model/OutputTableMonthly_Impl.hpp | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/model/OutputTableAnnual.cpp b/src/model/OutputTableAnnual.cpp index 7482c806e27..ff2b73ccaa6 100644 --- a/src/model/OutputTableAnnual.cpp +++ b/src/model/OutputTableAnnual.cpp @@ -301,8 +301,8 @@ namespace model { return getImpl()->getAnnualVariableGroup(groupIndex); } - bool OutputTableAnnual::addAnnualVariableGroup(const AnnualVariableGroup& group) { - return getImpl()->addAnnualVariableGroup(group); + bool OutputTableAnnual::addAnnualVariableGroup(const AnnualVariableGroup& annualVariableGroup) { + return getImpl()->addAnnualVariableGroup(annualVariableGroup); } bool OutputTableAnnual::addAnnualVariableGroup(std::string variableorMeterorEMSVariableorField, std::string aggregationType, diff --git a/src/model/OutputTableAnnual.hpp b/src/model/OutputTableAnnual.hpp index 6e96cab4e64..c003254217e 100644 --- a/src/model/OutputTableAnnual.hpp +++ b/src/model/OutputTableAnnual.hpp @@ -53,7 +53,7 @@ namespace model { explicit OutputTableAnnual(const Model& model); - virtual ~OutputTableAnnual() = default; + virtual ~OutputTableAnnual() override = default; // Default the copy and move operators because the virtual dtor is explicit OutputTableAnnual(const OutputTableAnnual& other) = default; OutputTableAnnual(OutputTableAnnual&& other) = default; diff --git a/src/model/OutputTableAnnual_Impl.hpp b/src/model/OutputTableAnnual_Impl.hpp index 68c7e7a7a36..995ef7dc57d 100644 --- a/src/model/OutputTableAnnual_Impl.hpp +++ b/src/model/OutputTableAnnual_Impl.hpp @@ -30,7 +30,7 @@ namespace model { OutputTableAnnual_Impl(const OutputTableAnnual_Impl& other, Model_Impl* model, bool keepHandle); - virtual ~OutputTableAnnual_Impl() = default; + virtual ~OutputTableAnnual_Impl() override = default; //@} /** @name Virtual Methods */ @@ -74,7 +74,7 @@ namespace model { boost::optional annualVariableGroupIndex(const AnnualVariableGroup& annualVariableGroup) const; boost::optional getAnnualVariableGroup(unsigned groupIndex) const; - bool addAnnualVariableGroup(const AnnualVariableGroup& group); + bool addAnnualVariableGroup(const AnnualVariableGroup& annualVariableGroup); bool addAnnualVariableGroups(const std::vector& annualVariableGroups); bool removeAnnualVariableGroup(unsigned groupIndex); void removeAllAnnualVariableGroups(); diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index 3f7fda39dee..1a4b1aaa628 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -243,8 +243,8 @@ namespace model { return getImpl()->getMonthlyVariableGroup(groupIndex); } - bool OutputTableMonthly::addMonthlyVariableGroup(const MonthlyVariableGroup& group) { - return getImpl()->addMonthlyVariableGroup(group); + bool OutputTableMonthly::addMonthlyVariableGroup(const MonthlyVariableGroup& monthlyVariableGroup) { + return getImpl()->addMonthlyVariableGroup(monthlyVariableGroup); } bool OutputTableMonthly::addMonthlyVariableGroup(std::string variableOrMeterName, std::string aggregationType) { diff --git a/src/model/OutputTableMonthly.hpp b/src/model/OutputTableMonthly.hpp index 15918f77e6b..8c41d63530d 100644 --- a/src/model/OutputTableMonthly.hpp +++ b/src/model/OutputTableMonthly.hpp @@ -47,7 +47,7 @@ namespace model { explicit OutputTableMonthly(const Model& model); - virtual ~OutputTableMonthly() = default; + virtual ~OutputTableMonthly() override = default; // Default the copy and move operators because the virtual dtor is explicit OutputTableMonthly(const OutputTableMonthly& other) = default; OutputTableMonthly(OutputTableMonthly&& other) = default; diff --git a/src/model/OutputTableMonthly_Impl.hpp b/src/model/OutputTableMonthly_Impl.hpp index f1a2bea7efc..30db819c8cd 100644 --- a/src/model/OutputTableMonthly_Impl.hpp +++ b/src/model/OutputTableMonthly_Impl.hpp @@ -29,7 +29,7 @@ namespace model { OutputTableMonthly_Impl(const OutputTableMonthly_Impl& other, Model_Impl* model, bool keepHandle); - virtual ~OutputTableMonthly_Impl() = default; + virtual ~OutputTableMonthly_Impl() override = default; //@} /** @name Virtual Methods */ @@ -63,7 +63,7 @@ namespace model { boost::optional monthlyVariableGroupIndex(const MonthlyVariableGroup& monthlyVariableGroup) const; boost::optional getMonthlyVariableGroup(unsigned groupIndex) const; - bool addMonthlyVariableGroup(const MonthlyVariableGroup& group); + bool addMonthlyVariableGroup(const MonthlyVariableGroup& monthlyVariableGroup); bool addMonthlyVariableGroups(const std::vector& monthlyVariableGroups); bool removeMonthlyVariableGroup(unsigned groupIndex); void removeAllMonthlyVariableGroups(); From 586445b2342077f2339d753772412714e9e81db9 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 22:17:25 +0100 Subject: [PATCH 15/19] Handle advanced aggregation types, which should be allowed more than once. Technically speaking, there should be a non-advanced aggregation type in between both, but it's way overkilled to try to enforce that --- src/model/OutputTableAnnual.cpp | 35 +++++++++++++++++---- src/model/OutputTableAnnual.hpp | 6 ++-- src/model/OutputTableMonthly.cpp | 31 +++++++++++++++--- src/model/OutputTableMonthly.hpp | 4 +++ src/model/test/OutputTableAnnual_GTest.cpp | 21 +++++++++++++ src/model/test/OutputTableMonthly_GTest.cpp | 21 +++++++++++++ 6 files changed, 105 insertions(+), 13 deletions(-) diff --git a/src/model/OutputTableAnnual.cpp b/src/model/OutputTableAnnual.cpp index ff2b73ccaa6..cbe900ee76f 100644 --- a/src/model/OutputTableAnnual.cpp +++ b/src/model/OutputTableAnnual.cpp @@ -48,6 +48,10 @@ namespace model { return m_digitsAfterDecimal; } + bool AnnualVariableGroup::isAggregationTypeAdvanced() const { + return OutputTableAnnual::isAggregationTypeAdvanced(m_aggregationType); + } + bool AnnualVariableGroup::operator==(const AnnualVariableGroup& other) const { return openstudio::istringEqual(variableorMeterorEMSVariableorField(), other.variableorMeterorEMSVariableorField()) && openstudio::istringEqual(aggregationType(), other.aggregationType()); @@ -187,12 +191,16 @@ namespace model { } bool OutputTableAnnual_Impl::addAnnualVariableGroup(const AnnualVariableGroup& annualVariableGroup) { - boost::optional existingIndex_ = annualVariableGroupIndex(annualVariableGroup); - if (existingIndex_) { - boost::optional group_ = getAnnualVariableGroup(existingIndex_.get()); - OS_ASSERT(group_); - LOG(Warn, "For " << briefDescription() << ", AnnualVariableGroup already exists, will be modified in place from " << group_.get() << " to " - << annualVariableGroup << "."); + boost::optional existingIndex_; + if (!annualVariableGroup.isAggregationTypeAdvanced()) { + existingIndex_ = annualVariableGroupIndex(annualVariableGroup); + if (existingIndex_) { + boost::optional group_ = getAnnualVariableGroup(existingIndex_.get()); + OS_ASSERT(group_); + LOG(Warn, "For " << briefDescription() + << ", AnnualVariableGroup already exists and is not an Advanced Aggregation Type, will be modified in place from " + << group_.get() << " to " << annualVariableGroup << "."); + } } // If existing, get it, otherwise Push an extensible group. ModelExtensibleGroup cannot be default-constructed, so use a ternary operator @@ -257,6 +265,21 @@ namespace model { != vals.cend(); } + bool OutputTableAnnual::isAggregationTypeAdvanced(const std::string& aggregationType) { + auto const& vals = advancedAggregationTypes(); + return std::find_if(vals.cbegin(), vals.cend(), + [&aggregationType](const std::string& choice) { return openstudio::istringEqual(aggregationType, choice); }) + != vals.cend(); + } + + const std::vector& OutputTableAnnual::advancedAggregationTypes() { + static std::vector result; + if (result.empty()) { + result = {"ValueWhenMaximumOrMinimum", "SumOrAverageDuringHoursShown", "MaximumDuringHoursShown", "MinimumDuringHoursShown"}; + } + return result; + } + IddObjectType OutputTableAnnual::iddObjectType() { return {IddObjectType::OS_Output_Table_Annual}; } diff --git a/src/model/OutputTableAnnual.hpp b/src/model/OutputTableAnnual.hpp index c003254217e..0b507f2dd56 100644 --- a/src/model/OutputTableAnnual.hpp +++ b/src/model/OutputTableAnnual.hpp @@ -30,6 +30,8 @@ namespace model { std::string aggregationType() const; int digitsAfterDecimal() const; + bool isAggregationTypeAdvanced() const; + // Only checks on variableorMeterorEMSVariableorField and aggregationType, not digitsAfterDecimal bool operator==(const AnnualVariableGroup& other) const; bool operator!=(const AnnualVariableGroup& other) const; @@ -67,6 +69,8 @@ namespace model { static std::vector aggregationTypeValues(); static std::vector validAggregationTypes(); static bool isAggregationTypeValid(const std::string& aggregationType); + static bool isAggregationTypeAdvanced(const std::string& aggregationType); + static const std::vector& advancedAggregationTypes(); /** @name Getters */ //@{ @@ -87,8 +91,6 @@ namespace model { void resetSchedule(); - // TODO: Handle this object's extensible fields. - //@} /** @name Other */ //@{ diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index 1a4b1aaa628..0eb41a86659 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -47,6 +47,10 @@ namespace model { return (!operator==(other)); } + bool MonthlyVariableGroup::isAggregationTypeAdvanced() const { + return OutputTableMonthly::isAggregationTypeAdvanced(m_aggregationType); + } + std::ostream& operator<<(std::ostream& out, const openstudio::model::MonthlyVariableGroup& monthlyVariableGroup) { out << "(Output Variable or Meter = '" << monthlyVariableGroup.variableOrMeterName() << "', " << "Aggregation Type = '" << monthlyVariableGroup.aggregationType() << "')"; @@ -147,12 +151,14 @@ namespace model { } bool OutputTableMonthly_Impl::addMonthlyVariableGroup(const MonthlyVariableGroup& monthlyVariableGroup) { - boost::optional existingIndex_ = monthlyVariableGroupIndex(monthlyVariableGroup); - if (existingIndex_) { - LOG(Warn, "For " << briefDescription() << ", MonthlyVariableGroup already exists: " << monthlyVariableGroup); - return false; + if (!monthlyVariableGroup.isAggregationTypeAdvanced()) { + boost::optional existingIndex_ = monthlyVariableGroupIndex(monthlyVariableGroup); + if (existingIndex_) { + LOG(Warn, "For " << briefDescription() + << ", MonthlyVariableGroup already exists and is not an Advanced Aggregation Type: " << monthlyVariableGroup); + return false; + } } - IdfExtensibleGroup eg = pushExtensibleGroup(StringVector()); eg.setString(OS_Output_Table_MonthlyExtensibleFields::VariableorMeterName, monthlyVariableGroup.variableOrMeterName()); eg.setString(OS_Output_Table_MonthlyExtensibleFields::AggregationTypeforVariableorMeter, monthlyVariableGroup.aggregationType()); @@ -215,6 +221,21 @@ namespace model { != vals.cend(); } + bool OutputTableMonthly::isAggregationTypeAdvanced(const std::string& aggregationType) { + auto const& vals = advancedAggregationTypes(); + return std::find_if(vals.cbegin(), vals.cend(), + [&aggregationType](const std::string& choice) { return openstudio::istringEqual(aggregationType, choice); }) + != vals.cend(); + } + + const std::vector& OutputTableMonthly::advancedAggregationTypes() { + static std::vector result; + if (result.empty()) { + result = {"ValueWhenMaximumOrMinimum", "SumOrAverageDuringHoursShown", "MaximumDuringHoursShown", "MinimumDuringHoursShown"}; + } + return result; + } + IddObjectType OutputTableMonthly::iddObjectType() { return {IddObjectType::OS_Output_Table_Monthly}; } diff --git a/src/model/OutputTableMonthly.hpp b/src/model/OutputTableMonthly.hpp index 8c41d63530d..b11852ac4fc 100644 --- a/src/model/OutputTableMonthly.hpp +++ b/src/model/OutputTableMonthly.hpp @@ -26,6 +26,8 @@ namespace model { std::string variableOrMeterName() const; std::string aggregationType() const; + bool isAggregationTypeAdvanced() const; + bool operator==(const MonthlyVariableGroup& other) const; bool operator!=(const MonthlyVariableGroup& other) const; @@ -61,6 +63,8 @@ namespace model { static std::vector aggregationTypeValues(); static std::vector validAggregationTypes(); static bool isAggregationTypeValid(const std::string& aggregationType); + static bool isAggregationTypeAdvanced(const std::string& aggregationType); + static const std::vector& advancedAggregationTypes(); /** @name Getters */ //@{ diff --git a/src/model/test/OutputTableAnnual_GTest.cpp b/src/model/test/OutputTableAnnual_GTest.cpp index b9b6a344b23..139467753e6 100644 --- a/src/model/test/OutputTableAnnual_GTest.cpp +++ b/src/model/test/OutputTableAnnual_GTest.cpp @@ -151,3 +151,24 @@ TEST_F(ModelFixture, OutputTableAnnual_AnnualVariableGroups) { EXPECT_EQ(group3.digitsAfterDecimal(), groups[2].digitsAfterDecimal()); EXPECT_EQ(group3, groups[2]); } + +TEST_F(ModelFixture, OutputTableAnnual_AdvancedAggregationTypes) { + + Model m; + EXPECT_FALSE(OutputTableAnnual::isAggregationTypeAdvanced("SumOrAverage")); + EXPECT_TRUE(OutputTableAnnual::isAggregationTypeAdvanced("ValueWhenMaximumOrMinimum")); + + std::vector groups{ + {"Zone Air System Sensible Cooling Energy", "SumOrAverage"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Zone Total Internal Latent Gain Energy", "SumOrAverage"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + }; + + OutputTableAnnual table(m); + + for (const auto& group : groups) { + EXPECT_TRUE(table.addAnnualVariableGroup(group)); + } + EXPECT_EQ(4, table.numberofAnnualVariableGroups()); +} diff --git a/src/model/test/OutputTableMonthly_GTest.cpp b/src/model/test/OutputTableMonthly_GTest.cpp index 6d6d148a350..5179924e70e 100644 --- a/src/model/test/OutputTableMonthly_GTest.cpp +++ b/src/model/test/OutputTableMonthly_GTest.cpp @@ -123,3 +123,24 @@ TEST_F(ModelFixture, OutputTableMonthly_MonthlyVariableGroups) { EXPECT_EQ(group3.aggregationType(), groups[2].aggregationType()); EXPECT_EQ(group3, groups[2]); } + +TEST_F(ModelFixture, OutputTableMonthly_AdvancedAggregationTypes) { + + Model m; + EXPECT_FALSE(OutputTableMonthly::isAggregationTypeAdvanced("SumOrAverage")); + EXPECT_TRUE(OutputTableMonthly::isAggregationTypeAdvanced("ValueWhenMaximumOrMinimum")); + + std::vector groups{ + {"Zone Air System Sensible Cooling Energy", "SumOrAverage"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Zone Total Internal Latent Gain Energy", "SumOrAverage"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + }; + + OutputTableMonthly table(m); + + for (const auto& group : groups) { + EXPECT_TRUE(table.addMonthlyVariableGroup(group)); + } + EXPECT_EQ(4, table.numberofMonthlyVariableGroups()); +} From 9fe3714148b3eaecdb54175a218532cb510cb020 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 23:22:00 +0100 Subject: [PATCH 16/19] Add a Factory method to create the reports in E+ datasets/StandardReports.idf --- .../OpenStudio_Release_Notes_3_10_0_TDB.md | 4 + src/model/OutputTableMonthly.cpp | 633 ++++++++++++++++++ src/model/OutputTableMonthly.hpp | 7 + src/model/OutputTableMonthly_Impl.hpp | 4 + src/model/test/OutputTableMonthly_GTest.cpp | 12 + 5 files changed, 660 insertions(+) diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md index f813f33c460..bc62116ed37 100644 --- a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md +++ b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_10_0_TDB.md @@ -80,6 +80,10 @@ You can also refer to the [OpenStudio SDK Python Binding Version Compatibility M * Note: in EnergyPlus 24.2.0, the `Zone Relief Air Node Name` is an optional field. The OpenStudio SDK always fills with the connected zone's Exhaust Air Node, meaning the airflow is always being balanced by EnergyPlus: the object will extract air from the zone to balance the air supplied to the zone by the cooler outlet node. * [#5369](https://github.com/NREL/OpenStudio/pull/5369) - Wrap Output:Table:Annual and Output:Table:Monthly + * Both objects were wrapped in the SDK + * The OutputTableMonthly also includes a convenient factory methods to create the reports that are in the E+ datasets/StandardReports.idf + * See `std::vector OutputTableMonthly::validStandardReportNames` to get a list of valid methods + * and the factory method itself: `OutputTableMonthly OutputTableMonthly::fromStandardReports(const Model& model, const std::string& standardReportName)` * [#5365](https://github.com/NREL/OpenStudio/pull/5365) - E+ 25.1.0: Wrap OutputControl:ResilienceSummaries diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index 0eb41a86659..480b4f16b28 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -59,6 +59,628 @@ namespace model { namespace detail { + const std::vector>>& OutputTableMonthly_Impl::standardReports() { + // Generated from this python code, https://github.com/NREL/EnergyPlus/blob/v25.1.0-IOFreeze/datasets/StandardReports.idf + // + // ```python + // def generate_report(standard_report_name: str): + // wo = w.getObjectByTypeAndName('Output:Table:Monthly', standard_report_name).get() + // s = f' {{"{standard_report_name}",\n {{\n' + // for eg in wo.extensibleGroups(): + // s += f' {{"{eg.getString(0).get()}", "{eg.getString(1).get()}"}},\n' + // s += " }\n }," + // return s + // + // w = openstudio.Workspace.load('/path/to/EnergyPlus/datasets/StandardReports.idf').get() + // reports = sorted([wo for wo in w.getObjectsByType('Output:Table:Monthly')], key=lambda wo: wo.nameString()) + // + // print("static std::vector>> standard_reports{") + // + // for report in reports: + // print(generate_report(report.nameString())) + // + // print("};") + // ``` + + static std::vector>> standard_reports{ + {"Air Loop System Component Energy Use", + { + {"Air System Fan Electricity Energy", "SumOrAverage"}, + {"Air System Heating Coil Hot Water Energy", "SumOrAverage"}, + {"Air System Cooling Coil Chilled Water Energy", "SumOrAverage"}, + {"Air System DX Heating Coil Electricity Energy", "SumOrAverage"}, + {"Air System DX Cooling Coil Electricity Energy", "SumOrAverage"}, + {"Air System Heating Coil Electricity Energy", "SumOrAverage"}, + {"Air System Heating Coil NaturalGas Energy", "SumOrAverage"}, + {"Air System Heating Coil Steam Energy", "SumOrAverage"}, + {"Air System Humidifier Electricity Energy", "SumOrAverage"}, + {"Air System Evaporative Cooler Electricity Energy", "SumOrAverage"}, + {"Air System Desiccant Dehumidifier Electricity Energy", "SumOrAverage"}, + }}, + {"Air Loop System Component Loads", + { + {"Air System Fan Air Heating Energy", "SumOrAverage"}, + {"Air System Cooling Coil Total Cooling Energy", "SumOrAverage"}, + {"Air System Heating Coil Total Heating Energy", "SumOrAverage"}, + {"Air System Heat Exchanger Total Heating Energy", "SumOrAverage"}, + {"Air System Heat Exchanger Total Cooling Energy", "SumOrAverage"}, + {"Air System Humidifier Total Heating Energy", "SumOrAverage"}, + {"Air System Evaporative Cooler Total Cooling Energy", "SumOrAverage"}, + {"Air System Desiccant Dehumidifier Total Cooling Energy", "SumOrAverage"}, + }}, + {"Air Loop System Energy and Water Use", + { + {"Air System Hot Water Energy", "SumOrAverage"}, + {"Air System Steam Energy", "SumOrAverage"}, + {"Air System Chilled Water Energy", "SumOrAverage"}, + {"Air System Electricity Energy", "SumOrAverage"}, + {"Air System NaturalGas Energy", "SumOrAverage"}, + {"Air System Water Volume", "SumOrAverage"}, + }}, + {"Average Outdoor Conditions", + { + {"Site Outdoor Air Drybulb Temperature", "SumOrAverage"}, + {"Site Outdoor Air Wetbulb Temperature", "SumOrAverage"}, + {"Site Outdoor Air Dewpoint Temperature", "SumOrAverage"}, + {"Site Wind Speed", "SumOrAverage"}, + {"Site Sky Temperature", "SumOrAverage"}, + {"Site Diffuse Solar Radiation Rate per Area", "SumOrAverage"}, + {"Site Direct Solar Radiation Rate per Area", "SumOrAverage"}, + {"Raining", "SumOrAverage"}, + }}, + {"Boiler Report", + { + {"Boiler Heating Energy", "SumOrAverage"}, + {"Boiler NaturalGas Energy", "SumOrAverage"}, + {"Boiler Heating Energy", "HoursNonZero"}, + {"Boiler Heating Rate", "Maximum"}, + {"Boiler NaturalGas Rate", "Maximum"}, + {"Boiler Inlet Temperature", "SumOrAverage"}, + {"Boiler Outlet Temperature", "SumOrAverage"}, + {"Boiler Mass Flow Rate", "SumOrAverage"}, + {"Boiler Ancillary Electricity Rate", "SumOrAverage"}, + }}, + {"Chiller Report", + { + {"Chiller Electricity Energy", "SumOrAverage"}, + {"Chiller Electricity Rate", "Maximum"}, + {"Chiller Electricity Energy", "HoursNonZero"}, + {"Chiller Evaporator Cooling Energy", "SumOrAverage"}, + {"Chiller Condenser Heat Transfer Energy", "SumOrAverage"}, + {"Chiller COP", "SumOrAverage"}, + {"Chiller COP", "Minimum"}, + {"Chiller COP", "Maximum"}, + }}, + {"Coil Report", + { + {"Heating Coil Heating Energy", "SumOrAverage"}, + {"Heating Coil Heating Rate", "Maximum"}, + {"Cooling Coil Total Cooling Energy", "SumOrAverage"}, + {"Cooling Coil Sensible Cooling Energy", "SumOrAverage"}, + {"Cooling Coil Total Cooling Rate", "Maximum"}, + {"Cooling Coil Sensible Cooling Rate", "ValueWhenMaximumOrMinimum"}, + {"Cooling Coil Wetted Area Fraction", "SumOrAverage"}, + }}, + {"Comfort Report - Simple 55", + { + {"Zone Thermal Comfort ASHRAE 55 Simple Model Summer Clothes Not Comfortable Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Thermal Comfort ASHRAE 55 Simple Model Winter Clothes Not Comfortable Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Thermal Comfort ASHRAE 55 Simple Model Summer or Winter Clothes Not Comfortable Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + }}, + {"Cond Loop Demand Report", + { + {"Plant Supply Side Cooling Demand Rate", "SumOrAverage"}, + {"Plant Supply Side Cooling Demand Rate", "Maximum"}, + {"Plant Supply Side Inlet Temperature", "ValueWhenMaximumOrMinimum"}, + {"Plant Supply Side Outlet Temperature", "ValueWhenMaximumOrMinimum"}, + {"Plant Supply Side Heating Demand Rate", "SumOrAverage"}, + {"Plant Supply Side Heating Demand Rate", "Maximum"}, + }}, + {"DX Report", + { + {"Cooling Coil Total Cooling Energy", "SumOrAverage"}, + {"Cooling Coil Electricity Energy", "SumOrAverage"}, + {"Cooling Coil Total Cooling Energy", "HoursNonZero"}, + {"Cooling Coil Sensible Cooling Energy", "SumOrAverage"}, + {"Cooling Coil Latent Cooling Energy", "SumOrAverage"}, + {"Cooling Coil Crankcase Heater Electricity Energy", "SumOrAverage"}, + {"Cooling Coil Runtime Fraction", "Maximum"}, + {"Cooling Coil Runtime Fraction", "Minimum"}, + {"Cooling Coil Total Cooling Rate", "Maximum"}, + {"Cooling Coil Sensible Cooling Rate", "Maximum"}, + {"Cooling Coil Latent Cooling Rate", "Maximum"}, + {"Cooling Coil Electricity Rate", "Maximum"}, + {"Cooling Coil Crankcase Heater Electricity Rate", "Maximum"}, + }}, + {"Daylighting Report", + { + {"Site Exterior Beam Normal Illuminance", "HoursNonZero"}, + {"Daylighting Lighting Power Multiplier", "SumOrAverageDuringHoursShown"}, + {"Daylighting Lighting Power Multiplier", "MinimumDuringHoursShown"}, + {"Daylighting Reference Point 1 Illuminance", "SumOrAverageDuringHoursShown"}, + {"Daylighting Reference Point 1 Glare Index", "SumOrAverageDuringHoursShown"}, + {"Daylighting Reference Point 2 Illuminance", "SumOrAverageDuringHoursShown"}, + {"Daylighting Reference Point 2 Glare Index", "SumOrAverageDuringHoursShown"}, + }}, + {"Electric Components of Peak Demand", + { + {"Electricity:Facility", "Maximum"}, + {"InteriorLights:Electricity", "ValueWhenMaximumOrMinimum"}, + {"InteriorEquipment:Electricity", "ValueWhenMaximumOrMinimum"}, + {"ExteriorLights:Electricity", "ValueWhenMaximumOrMinimum"}, + {"ExteriorEquipment:Electricity", "ValueWhenMaximumOrMinimum"}, + {"Fans:Electricity", "ValueWhenMaximumOrMinimum"}, + {"Pumps:Electricity", "ValueWhenMaximumOrMinimum"}, + {"Heating:Electricity", "ValueWhenMaximumOrMinimum"}, + {"Cooling:Electricity", "ValueWhenMaximumOrMinimum"}, + {"HeatRejection:Electricity", "ValueWhenMaximumOrMinimum"}, + }}, + {"End-Use Energy Consumption - Coal", + { + {"ExteriorEquipment:Coal", "SumOrAverage"}, + {"Heating:Coal", "SumOrAverage"}, + {"WaterSystems:Coal", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Diesel", + { + {"ExteriorEquipment:Diesel", "SumOrAverage"}, + {"Cooling:Diesel", "SumOrAverage"}, + {"Heating:Diesel", "SumOrAverage"}, + {"WaterSystems:Diesel", "SumOrAverage"}, + {"Cogeneration:Diesel", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Electricity", + { + {"InteriorLights:Electricity", "SumOrAverage"}, + {"ExteriorLights:Electricity", "SumOrAverage"}, + {"InteriorEquipment:Electricity", "SumOrAverage"}, + {"ExteriorEquipment:Electricity", "SumOrAverage"}, + {"Fans:Electricity", "SumOrAverage"}, + {"Pumps:Electricity", "SumOrAverage"}, + {"Heating:Electricity", "SumOrAverage"}, + {"Cooling:Electricity", "SumOrAverage"}, + {"HeatRejection:Electricity", "SumOrAverage"}, + {"Humidifier:Electricity", "SumOrAverage"}, + {"HeatRecovery:Electricity", "SumOrAverage"}, + {"WaterSystems:Electricity", "SumOrAverage"}, + {"Cogeneration:Electricity", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Fuel Oil", + { + {"ExteriorEquipment:FuelOilNo1", "SumOrAverage"}, + {"Cooling:FuelOilNo1", "SumOrAverage"}, + {"Heating:FuelOilNo1", "SumOrAverage"}, + {"WaterSystems:FuelOilNo1", "SumOrAverage"}, + {"Cogeneration:FuelOilNo1", "SumOrAverage"}, + {"ExteriorEquipment:FuelOilNo2", "SumOrAverage"}, + {"Cooling:FuelOilNo2", "SumOrAverage"}, + {"Heating:FuelOilNo2", "SumOrAverage"}, + {"WaterSystems:FuelOilNo2", "SumOrAverage"}, + {"Cogeneration:FuelOilNo2", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Gasoline", + { + {"ExteriorEquipment:Gasoline", "SumOrAverage"}, + {"Cooling:Gasoline", "SumOrAverage"}, + {"Heating:Gasoline", "SumOrAverage"}, + {"WaterSystems:Gasoline", "SumOrAverage"}, + {"Cogeneration:Gasoline", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Natural Gas", + { + {"InteriorEquipment:NaturalGas", "SumOrAverage"}, + {"ExteriorEquipment:NaturalGas", "SumOrAverage"}, + {"Heating:NaturalGas", "SumOrAverage"}, + {"Cooling:NaturalGas", "SumOrAverage"}, + {"WaterSystems:NaturalGas", "SumOrAverage"}, + {"Cogeneration:NaturalGas", "SumOrAverage"}, + }}, + {"End-Use Energy Consumption - Propane", + { + {"ExteriorEquipment:Propane", "SumOrAverage"}, + {"Cooling:Propane", "SumOrAverage"}, + {"Heating:Propane", "SumOrAverage"}, + {"WaterSystems:Propane", "SumOrAverage"}, + {"Cogeneration:Propane", "SumOrAverage"}, + }}, + {"Energy Consumption - Purchased Heating & Cooling", + { + {"DistrictCooling:Facility", "SumOrAverage"}, + {"DistrictCooling:Facility", "Maximum"}, + {"DistrictHeatingWater:Facility", "SumOrAverage"}, + {"DistrictHeatingWater:Facility", "Maximum"}, + }}, + {"Energy Consumption - Coal & Gasoline", + { + {"COAL:Facility", "SumOrAverage"}, + {"COAL:Facility", "Maximum"}, + {"GASOLINE:Facility", "SumOrAverage"}, + {"GASOLINE:Facility", "Maximum"}, + }}, + {"Energy Consumption - Diesel & Fuel Oil", + { + {"Diesel:Facility", "SumOrAverage"}, + {"Diesel:Facility", "Maximum"}, + {"FuelOilNo1:Facility", "SumOrAverage"}, + {"FuelOilNo1:Facility", "Maximum"}, + {"FuelOilNo2:Facility", "SumOrAverage"}, + {"FuelOilNo2:Facility", "Maximum"}, + }}, + {"Energy Consumption - Electricity & Natural Gas", + { + {"Electricity:Facility", "SumOrAverage"}, + {"Electricity:Facility", "Maximum"}, + {"NaturalGas:Facility", "SumOrAverage"}, + {"NaturalGas:Facility", "Maximum"}, + }}, + {"Energy Consumption - Electricity Generated & Propane", + { + {"ElectricityProduced:Facility", "SumOrAverage"}, + {"ElectricityProduced:Facility", "Maximum"}, + {"Propane:Facility", "SumOrAverage"}, + {"Propane:Facility", "Maximum"}, + }}, + {"Fan Report", + { + {"Fan Electricity Energy", "SumOrAverage"}, + {"Fan Rise in Air Temperature", "SumOrAverage"}, + {"Fan Electricity Rate", "Maximum"}, + {"Fan Rise in Air Temperature", "ValueWhenMaximumOrMinimum"}, + }}, + {"Generator Report", + { + {"Generator Produced Electricity Energy", "SumOrAverage"}, + {"Generator Diesel Energy", "SumOrAverage"}, + {"Generator NaturalGas Energy", "SumOrAverage"}, + {"Generator Produced Electricity Energy", "HoursNonZero"}, + {"Generator Total Heat Recovery", "SumOrAverage"}, + {"Generator Jacket Heat Recovery Energy", "SumOrAverage"}, + {"Generator Lube Heat Recovery Energy", "SumOrAverage"}, + {"Generator Exhaust Heat Recovery Energy", "SumOrAverage"}, + {"Generator Exhaust Air Temperature", "SumOrAverage"}, + }}, + {"Mechanical Ventilation Loads", + { + {"Zone Mechanical Ventilation No Load Heat Removal Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Cooling Load Increase Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Cooling Load Increase Due to Overheating Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Cooling Load Decrease Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation No Load Heat Addition Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Heating Load Increase Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Heating Load Increase Due to Overcooling Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Heating Load Decrease Energy", "SumOrAverage"}, + {"Zone Mechanical Ventilation Air Changes per Hour", "SumOrAverage"}, + }}, + {"Occupant Comfort Data Summary", + { + {"Zone People Occupant Count", "HoursNonZero"}, + {"Zone Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Air Relative Humidity", "SumOrAverageDuringHoursShown"}, + {"Zone Thermal Comfort Fanger Model PMV", "SumOrAverageDuringHoursShown"}, + }}, + {"Outdoor Conditions Maximum Dew Point", + { + {"Site Outdoor Air Dewpoint Temperature", "Maximum"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Wetbulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Wind Speed", "ValueWhenMaximumOrMinimum"}, + {"Site Sky Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Diffuse Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + {"Site Direct Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + }}, + {"Outdoor Conditions Maximum Drybulb", + { + {"Site Outdoor Air Drybulb Temperature", "Maximum"}, + {"Site Outdoor Air Wetbulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Dewpoint Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Wind Speed", "ValueWhenMaximumOrMinimum"}, + {"Site Sky Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Diffuse Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + {"Site Direct Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + }}, + {"Outdoor Conditions Maximum Wetbulb", + { + {"Site Outdoor Air Wetbulb Temperature", "Maximum"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Dewpoint Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Wind Speed", "ValueWhenMaximumOrMinimum"}, + {"Site Sky Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Diffuse Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + {"Site Direct Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + }}, + {"Outdoor Conditions Minimum Drybulb", + { + {"Site Outdoor Air Drybulb Temperature", "Minimum"}, + {"Site Outdoor Air Wetbulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Dewpoint Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Wind Speed", "ValueWhenMaximumOrMinimum"}, + {"Site Sky Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Diffuse Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + {"Site Direct Solar Radiation Rate per Area", "ValueWhenMaximumOrMinimum"}, + }}, + {"Outdoor Ground Conditions", + { + {"Site Ground Temperature", "SumOrAverage"}, + {"Site Surface Ground Temperature", "SumOrAverage"}, + {"Site Deep Ground Temperature", "SumOrAverage"}, + {"Site Mains Water Temperature", "SumOrAverage"}, + {"Site Ground Reflected Solar Radiation Rate per Area", "SumOrAverage"}, + {"Snow On Ground", "SumOrAverage"}, + }}, + {"Peak Energy End-Use - Coal", + { + {"ExteriorEquipment:Coal", "Maximum"}, + {"Heating:Coal", "Maximum"}, + {"WaterSystems:Coal", "Maximum"}, + }}, + {"Peak Energy End-Use - Diesel", + { + {"ExteriorEquipment:Diesel", "Maximum"}, + {"Cooling:Diesel", "Maximum"}, + {"Heating:Diesel", "Maximum"}, + {"WaterSystems:Diesel", "Maximum"}, + {"Cogeneration:Diesel", "Maximum"}, + }}, + {"Peak Energy End-Use - Electricity Part 1", + { + {"InteriorLights:Electricity", "Maximum"}, + {"ExteriorLights:Electricity", "Maximum"}, + {"InteriorEquipment:Electricity", "Maximum"}, + {"ExteriorEquipment:Electricity", "Maximum"}, + {"Fans:Electricity", "Maximum"}, + {"Pumps:Electricity", "Maximum"}, + {"Heating:Electricity", "Maximum"}, + }}, + {"Peak Energy End-Use - Electricity Part 2", + { + {"Cooling:Electricity", "Maximum"}, + {"HeatRejection:Electricity", "Maximum"}, + {"Humidifier:Electricity", "Maximum"}, + {"HeatRecovery:Electricity", "Maximum"}, + {"WaterSystems:Electricity", "Maximum"}, + {"Cogeneration:Electricity", "Maximum"}, + }}, + {"Peak Energy End-Use - Fuel Oil", + { + {"ExteriorEquipment:FuelOilNo1", "Maximum"}, + {"Cooling:FuelOilNo1", "Maximum"}, + {"Heating:FuelOilNo1", "Maximum"}, + {"WaterSystems:FuelOilNo1", "Maximum"}, + {"Cogeneration:FuelOilNo1", "Maximum"}, + {"ExteriorEquipment:FuelOilNo2", "Maximum"}, + {"Cooling:FuelOilNo2", "Maximum"}, + {"Heating:FuelOilNo2", "Maximum"}, + {"WaterSystems:FuelOilNo2", "Maximum"}, + {"Cogeneration:FuelOilNo2", "Maximum"}, + }}, + {"Peak Energy End-Use - Gasoline", + { + {"ExteriorEquipment:Gasoline", "Maximum"}, + {"Cooling:Gasoline", "Maximum"}, + {"Heating:Gasoline", "Maximum"}, + {"WaterSystems:Gasoline", "Maximum"}, + {"Cogeneration:Gasoline", "Maximum"}, + }}, + {"Peak Energy End-Use - Natural Gas", + { + {"InteriorEquipment:NaturalGas", "Maximum"}, + {"ExteriorEquipment:NaturalGas", "Maximum"}, + {"Heating:NaturalGas", "Maximum"}, + {"Cooling:NaturalGas", "Maximum"}, + {"WaterSystems:NaturalGas", "Maximum"}, + {"Cogeneration:NaturalGas", "Maximum"}, + }}, + {"Peak Energy End-Use - Propane", + { + {"ExteriorEquipment:Propane", "Maximum"}, + {"Cooling:Propane", "Maximum"}, + {"Heating:Propane", "Maximum"}, + {"WaterSystems:Propane", "Maximum"}, + {"Cogeneration:Propane", "Maximum"}, + }}, + {"Peak Space Gains", + { + {"Zone People Total Heating Energy", "Maximum"}, + {"Zone Lights Total Heating Energy", "Maximum"}, + {"Zone Electric Equipment Total Heating Energy", "Maximum"}, + {"Zone Gas Equipment Total Heating Energy", "Maximum"}, + {"Zone Hot Water Equipment Total Heating Energy", "Maximum"}, + {"Zone Steam Equipment Total Heating Energy", "Maximum"}, + {"Zone Other Equipment Total Heating Energy", "Maximum"}, + {"Zone Infiltration Sensible Heat Gain Energy", "Maximum"}, + {"Zone Infiltration Sensible Heat Loss Energy", "Maximum"}, + }}, + {"Plant Loop Demand Report", + { + {"Plant Supply Side Cooling Demand Rate", "SumOrAverage"}, + {"Plant Supply Side Cooling Demand Rate", "Maximum"}, + {"Plant Supply Side Heating Demand Rate", "SumOrAverage"}, + {"Plant Supply Side Heating Demand Rate", "Maximum"}, + }}, + {"Pump Report", + { + {"Pump Electricity Energy", "SumOrAverage"}, + {"Pump Fluid Heat Gain Energy", "SumOrAverage"}, + {"Pump Electricity Rate", "Maximum"}, + {"Pump Shaft Power", "ValueWhenMaximumOrMinimum"}, + {"Pump Fluid Heat Gain Rate", "ValueWhenMaximumOrMinimum"}, + {"Pump Outlet Temperature", "ValueWhenMaximumOrMinimum"}, + {"Pump Mass Flow Rate", "ValueWhenMaximumOrMinimum"}, + }}, + {"Setpoints Not Met With Temperatures", + { + {"Zone Heating Setpoint Not Met Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Heating Setpoint Not Met While Occupied Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Cooling Setpoint Not Met Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + {"Zone Cooling Setpoint Not Met While Occupied Time", "HoursNonZero"}, + {"Zone Mean Air Temperature", "SumOrAverageDuringHoursShown"}, + }}, + {"Space Gain Components at Cooling Peak", + { + {"Zone Air System Sensible Cooling Rate", "Maximum"}, + {"Zone People Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Lights Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Electric Equipment Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Gas Equipment Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Hot Water Equipment Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Steam Equipment Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Other Equipment Total Heating Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Infiltration Sensible Heat Gain Energy", "ValueWhenMaximumOrMinimum"}, + {"Zone Infiltration Sensible Heat Loss Energy", "ValueWhenMaximumOrMinimum"}, + }}, + {"Space Gains", + { + {"Zone People Total Heating Energy", "SumOrAverage"}, + {"Zone Lights Total Heating Energy", "SumOrAverage"}, + {"Zone Electric Equipment Total Heating Energy", "SumOrAverage"}, + {"Zone Gas Equipment Total Heating Energy", "SumOrAverage"}, + {"Zone Hot Water Equipment Total Heating Energy", "SumOrAverage"}, + {"Zone Steam Equipment Total Heating Energy", "SumOrAverage"}, + {"Zone Other Equipment Total Heating Energy", "SumOrAverage"}, + {"Zone Infiltration Sensible Heat Gain Energy", "SumOrAverage"}, + {"Zone Infiltration Sensible Heat Loss Energy", "SumOrAverage"}, + }}, + {"Tower Report", + { + {"Cooling Tower Fan Electricity Energy", "SumOrAverage"}, + {"Cooling Tower Fan Electricity Energy", "HoursNonZero"}, + {"Cooling Tower Fan Electricity Rate", "Maximum"}, + {"Cooling Tower Heat Transfer Rate", "Maximum"}, + {"Cooling Tower Inlet Temperature", "SumOrAverage"}, + {"Cooling Tower Outlet Temperature", "SumOrAverage"}, + {"Cooling Tower Mass Flow Rate", "SumOrAverage"}, + }}, + {"Unglazed Transpired Solar Collector Summary", + { + {"Solar Collector System Efficiency", "HoursNonZero"}, + {"Solar Collector System Efficiency", "SumOrAverageDuringHoursShown"}, + {"Solar Collector Outside Face Suction Velocity", "SumOrAverageDuringHoursShown"}, + {"Solar Collector Sensible Heating Rate", "SumOrAverageDuringHoursShown"}, + }}, + {"Water Heater Report", + { + {"Water Heater Total Demand Heat Transfer Energy", "SumOrAverage"}, + {"Water Heater Use Side Heat Transfer Energy", "SumOrAverage"}, + {"Water Heater Burner Heating Energy", "SumOrAverage"}, + {"Water Heater NaturalGas Energy", "SumOrAverage"}, + {"Water Heater Total Demand Heat Transfer Energy", "HoursNonZero"}, + {"Water Heater Loss Demand Energy", "SumOrAverage"}, + {"Water Heater Heat Loss Energy", "SumOrAverage"}, + {"Water Heater Tank Temperature", "SumOrAverage"}, + {"Water Heater Heat Recovery Supply Energy", "SumOrAverage"}, + {"Water Heater Source Side Heat Transfer Energy", "SumOrAverage"}, + }}, + {"Window AC Report", + { + {"Zone Window Air Conditioner Total Cooling Energy", "SumOrAverage"}, + {"Zone Window Air Conditioner Electricity Energy", "SumOrAverage"}, + {"Zone Window Air Conditioner Total Cooling Energy", "HoursNonZero"}, + {"Zone Window Air Conditioner Sensible Cooling Energy", "SumOrAverage"}, + {"Zone Window Air Conditioner Latent Cooling Energy", "SumOrAverage"}, + {"Zone Window Air Conditioner Total Cooling Rate", "Maximum"}, + {"Zone Window Air Conditioner Sensible Cooling Rate", "ValueWhenMaximumOrMinimum"}, + {"Zone Window Air Conditioner Latent Cooling Rate", "ValueWhenMaximumOrMinimum"}, + {"Zone Window Air Conditioner Electricity Rate", "ValueWhenMaximumOrMinimum"}, + }}, + {"Window Energy Report", + { + {"Surface Window Transmitted Solar Radiation Energy", "SumOrAverage"}, + {"Surface Window Transmitted Beam Solar Radiation Energy", "SumOrAverage"}, + {"Surface Window Transmitted Diffuse Solar Radiation Energy", "SumOrAverage"}, + {"Surface Window Heat Gain Energy", "SumOrAverage"}, + {"Surface Window Heat Loss Energy", "SumOrAverage"}, + }}, + {"Window Energy Zone Summary", + { + {"Zone Windows Total Heat Gain Energy", "SumOrAverage"}, + {"Zone Windows Total Heat Loss Energy", "SumOrAverage"}, + {"Zone Windows Total Transmitted Solar Radiation Energy", "SumOrAverage"}, + {"Zone Exterior Windows Total Transmitted Beam Solar Radiation Energy", "SumOrAverage"}, + {"Zone Exterior Windows Total Transmitted Diffuse Solar Radiation Energy", "SumOrAverage"}, + {"Zone Interior Windows Total Transmitted Diffuse Solar Radiation Energy", "SumOrAverage"}, + {"Zone Interior Windows Total Transmitted Beam Solar Radiation Energy", "SumOrAverage"}, + }}, + {"Window Report", + { + {"Surface Window Transmitted Solar Radiation Rate", "SumOrAverage"}, + {"Surface Window Transmitted Beam Solar Radiation Rate", "SumOrAverage"}, + {"Surface Window Transmitted Diffuse Solar Radiation Rate", "SumOrAverage"}, + {"Surface Window Heat Gain Rate", "SumOrAverage"}, + {"Surface Window Heat Loss Rate", "SumOrAverage"}, + {"Surface Window Inside Face Glazing Condensation Status", "HoursNonZero"}, + {"Surface Shading Device Is On Time Fraction", "HoursNonZero"}, + {"Surface Storm Window On Off Status", "HoursNonZero"}, + }}, + {"Window Zone Summary", + { + {"Zone Windows Total Heat Gain Rate", "SumOrAverage"}, + {"Zone Windows Total Heat Loss Rate", "SumOrAverage"}, + {"Zone Windows Total Transmitted Solar Radiation Rate", "SumOrAverage"}, + {"Zone Exterior Windows Total Transmitted Beam Solar Radiation Rate", "SumOrAverage"}, + {"Zone Exterior Windows Total Transmitted Diffuse Solar Radiation Rate", "SumOrAverage"}, + {"Zone Interior Windows Total Transmitted Diffuse Solar Radiation Rate", "SumOrAverage"}, + {"Zone Interior Windows Total Transmitted Beam Solar Radiation Rate", "SumOrAverage"}, + }}, + {"Zone Cooling Summary", + { + {"Zone Air System Sensible Cooling Energy", "SumOrAverage"}, + {"Zone Air System Sensible Cooling Rate", "Maximum"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Wetbulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Zone Total Internal Latent Gain Energy", "SumOrAverage"}, + {"Zone Total Internal Latent Gain Energy", "Maximum"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + {"Site Outdoor Air Wetbulb Temperature", "ValueWhenMaximumOrMinimum"}, + }}, + {"Zone Electric Summary", + { + {"Zone Lights Electricity Energy", "SumOrAverage"}, + {"Zone Lights Electricity Energy", "Maximum"}, + {"Zone Electric Equipment Electricity Energy", "SumOrAverage"}, + {"Zone Electric Equipment Electricity Energy", "Maximum"}, + }}, + {"Zone Heating Summary", + { + {"Zone Air System Sensible Heating Energy", "SumOrAverage"}, + {"Zone Air System Sensible Heating Rate", "Maximum"}, + {"Site Outdoor Air Drybulb Temperature", "ValueWhenMaximumOrMinimum"}, + }}, + {"Zone Temperature Oscillation Report", + { + {"Zone Oscillating Temperatures Time", "HoursNonZero"}, + {"Zone People Occupant Count", "SumOrAverageDuringHoursShown"}, + }}, + }; + return standard_reports; + } + + const std::vector& OutputTableMonthly_Impl::validStandardReportNames() { + static std::vector result = [] { + std::vector result; + auto const& standard_reports = OutputTableMonthly_Impl::standardReports(); + result.reserve(standard_reports.size()); + std::transform(standard_reports.cbegin(), standard_reports.cend(), std::back_inserter(result), [](const auto& x) { return x.first; }); + return result; + }(); + return result; + } + + const std::vector& OutputTableMonthly_Impl::getGroupsForStandardReport(const std::string& standardReportName) { + auto const& standard_reports = OutputTableMonthly_Impl::standardReports(); + auto it = std::find_if(standard_reports.cbegin(), standard_reports.cend(), + [&standardReportName](const auto& x) { return openstudio::istringEqual(standardReportName, x.first); }); + if (it == standard_reports.cend()) { + LOG_AND_THROW("Could not find " << standardReportName << ", check OutputTableMonthly::standardReportNames"); + } + return it->second; + } + OutputTableMonthly_Impl::OutputTableMonthly_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) : ModelObject_Impl(idfObject, model, keepHandle) { OS_ASSERT(idfObject.iddObject().type() == OutputTableMonthly::iddObjectType()); @@ -288,5 +910,16 @@ namespace model { OutputTableMonthly::OutputTableMonthly(std::shared_ptr impl) : ModelObject(std::move(impl)) {} /// @endcond + const std::vector& OutputTableMonthly::validStandardReportNames() { + return detail::OutputTableMonthly_Impl::validStandardReportNames(); + } + OutputTableMonthly OutputTableMonthly::fromStandardReports(const Model& model, const std::string& standardReportName) { + const auto& groups = detail::OutputTableMonthly_Impl::getGroupsForStandardReport(standardReportName); + OutputTableMonthly result(model); + result.setName(standardReportName); + result.addMonthlyVariableGroups(groups); + return result; + } + } // namespace model } // namespace openstudio diff --git a/src/model/OutputTableMonthly.hpp b/src/model/OutputTableMonthly.hpp index b11852ac4fc..d2456e58ffa 100644 --- a/src/model/OutputTableMonthly.hpp +++ b/src/model/OutputTableMonthly.hpp @@ -66,6 +66,13 @@ namespace model { static bool isAggregationTypeAdvanced(const std::string& aggregationType); static const std::vector& advancedAggregationTypes(); + /// Factory method to create a OutputTableMonthly by looking up characteristics in the embedded database (from E+ datasets/StandardReports.idf) + /// by its name. + /// Please use the OutputTableMonthly::validStandardReportNames() static method to look up the valid names + // as it will throw if it cannot find it + static OutputTableMonthly fromStandardReports(const Model& model, const std::string& standardReportName); + static const std::vector& validStandardReportNames(); + /** @name Getters */ //@{ diff --git a/src/model/OutputTableMonthly_Impl.hpp b/src/model/OutputTableMonthly_Impl.hpp index 30db819c8cd..5beb8d5159f 100644 --- a/src/model/OutputTableMonthly_Impl.hpp +++ b/src/model/OutputTableMonthly_Impl.hpp @@ -68,6 +68,10 @@ namespace model { bool removeMonthlyVariableGroup(unsigned groupIndex); void removeAllMonthlyVariableGroups(); + static const std::vector>>& standardReports(); + static const std::vector& validStandardReportNames(); + static const std::vector& getGroupsForStandardReport(const std::string& standardReportName); + //@} protected: private: diff --git a/src/model/test/OutputTableMonthly_GTest.cpp b/src/model/test/OutputTableMonthly_GTest.cpp index 5179924e70e..bb58a2f05f2 100644 --- a/src/model/test/OutputTableMonthly_GTest.cpp +++ b/src/model/test/OutputTableMonthly_GTest.cpp @@ -144,3 +144,15 @@ TEST_F(ModelFixture, OutputTableMonthly_AdvancedAggregationTypes) { } EXPECT_EQ(4, table.numberofMonthlyVariableGroups()); } + +TEST_F(ModelFixture, OutputTableMonthly_FactoryMethod) { + Model m; + + std::vector standardReportNames = OutputTableMonthly::validStandardReportNames(); + EXPECT_GT(standardReportNames.size(), 50); + + for (const auto& standardReportName : standardReportNames) { + EXPECT_NO_THROW(OutputTableMonthly::fromStandardReports(m, standardReportName)); + } + EXPECT_EQ(standardReportNames.size(), m.getConcreteModelObjects().size()); +} From 6c57fa26c120bf5400d790ac3327b226f967b93c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 23:26:47 +0100 Subject: [PATCH 17/19] Minor tweaks --- src/model/OutputTableAnnual.cpp | 5 +++-- src/model/OutputTableMonthly.cpp | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/model/OutputTableAnnual.cpp b/src/model/OutputTableAnnual.cpp index cbe900ee76f..b32f294ee41 100644 --- a/src/model/OutputTableAnnual.cpp +++ b/src/model/OutputTableAnnual.cpp @@ -62,7 +62,7 @@ namespace model { } std::ostream& operator<<(std::ostream& out, const openstudio::model::AnnualVariableGroup& annualVariableGroup) { - out << "(Output Variable or Meter = '" << annualVariableGroup.variableorMeterorEMSVariableorField() << "', " << "Aggregation Type = '" + out << "(Output Variable or Meter = '" << annualVariableGroup.variableorMeterorEMSVariableorField() << "', Aggregation Type = '" << annualVariableGroup.aggregationType() << "', Digits After Decimal = " << annualVariableGroup.digitsAfterDecimal() << ")"; return out; } @@ -160,7 +160,7 @@ namespace model { const std::vector groups = annualVariableGroups(); auto it = std::find(groups.cbegin(), groups.cend(), annualVariableGroup); if (it != groups.end()) { - return std::distance(groups.cbegin(), it); + return static_cast(std::distance(groups.cbegin(), it)); } return boost::none; } @@ -217,6 +217,7 @@ namespace model { for (const auto& annualVariableGroup : annualVariableGroups) { bool thisResult = addAnnualVariableGroup(annualVariableGroup); + // cppcheck-suppress knownConditionTrueFalse if (!thisResult) { LOG(Error, "Could not add AnnualVariableGroup " << annualVariableGroup << " to " << briefDescription() << ". Continuing with others."); // OS_ASSERT(false); diff --git a/src/model/OutputTableMonthly.cpp b/src/model/OutputTableMonthly.cpp index 480b4f16b28..879a713a2a8 100644 --- a/src/model/OutputTableMonthly.cpp +++ b/src/model/OutputTableMonthly.cpp @@ -52,8 +52,8 @@ namespace model { } std::ostream& operator<<(std::ostream& out, const openstudio::model::MonthlyVariableGroup& monthlyVariableGroup) { - out << "(Output Variable or Meter = '" << monthlyVariableGroup.variableOrMeterName() << "', " - << "Aggregation Type = '" << monthlyVariableGroup.aggregationType() << "')"; + out << "(Output Variable or Meter = '" << monthlyVariableGroup.variableOrMeterName() << "', Aggregation Type = '" + << monthlyVariableGroup.aggregationType() << "')"; return out; } @@ -744,7 +744,7 @@ namespace model { const std::vector groups = monthlyVariableGroups(); auto it = std::find(groups.cbegin(), groups.cend(), monthlyVariableGroup); if (it != groups.end()) { - return std::distance(groups.cbegin(), it); + return static_cast(std::distance(groups.cbegin(), it)); } return boost::none; } From ea0e2a7b64f2f275f5f0f4d2257126d2bfecced4 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Mon, 17 Mar 2025 23:28:08 +0100 Subject: [PATCH 18/19] Remove TODOs --- .../ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp | 1 - src/model/OutputTableAnnual.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp index c6feb120937..c9054ac12b4 100644 --- a/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp +++ b/src/energyplus/ReverseTranslator/ReverseTranslateOutputTableAnnual.cpp @@ -34,7 +34,6 @@ namespace energyplus { // Schedule Name: Optional Object if (boost::optional wo_ = workspaceObject.getTarget(Output_Table_AnnualFields::ScheduleName)) { if (boost::optional mo_ = translateAndMapWorkspaceObject(wo_.get())) { - // TODO: check return types if (boost::optional schedule_ = mo_->optionalCast()) { modelObject.setSchedule(schedule_.get()); } else { diff --git a/src/model/OutputTableAnnual.hpp b/src/model/OutputTableAnnual.hpp index 0b507f2dd56..76ab8f62823 100644 --- a/src/model/OutputTableAnnual.hpp +++ b/src/model/OutputTableAnnual.hpp @@ -12,7 +12,6 @@ namespace openstudio { namespace model { - // TODO: Check the following class names against object getters and setters. class Schedule; namespace detail { From 1445362e6e049fe369979ba7683aaf8062e79083 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Wed, 19 Mar 2025 10:12:17 +0100 Subject: [PATCH 19/19] Ensure ScheduleTypeRegistry is working correctly. cf https://github.com/NREL/OpenStudio-resources/pull/217#issuecomment-2735712574 --- src/model/test/OutputTableAnnual_GTest.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/model/test/OutputTableAnnual_GTest.cpp b/src/model/test/OutputTableAnnual_GTest.cpp index 139467753e6..98d8ad383b4 100644 --- a/src/model/test/OutputTableAnnual_GTest.cpp +++ b/src/model/test/OutputTableAnnual_GTest.cpp @@ -10,6 +10,7 @@ #include "../Schedule.hpp" #include "../ScheduleConstant.hpp" +#include "../ScheduleTypeLimits.hpp" using namespace openstudio; using namespace openstudio::model; @@ -31,9 +32,23 @@ TEST_F(ModelFixture, OutputTableAnnual_GettersSetters) { // Schedule Name: Optional Object EXPECT_FALSE(outputTableAnnual.schedule()); ScheduleConstant sch(m); + EXPECT_FALSE(sch.scheduleTypeLimits()); EXPECT_TRUE(outputTableAnnual.setSchedule(sch)); ASSERT_TRUE(outputTableAnnual.schedule()); EXPECT_EQ(sch, outputTableAnnual.schedule().get()); + + // The Schedule Type Registry should have done it's thing and assigned a scheduleTypeLimits + ASSERT_TRUE(sch.scheduleTypeLimits()); + auto sch_type_lim = sch.scheduleTypeLimits().get(); + EXPECT_EQ("OnOff", sch_type_lim.nameString()); + ASSERT_TRUE(sch_type_lim.lowerLimitValue()); + EXPECT_EQ(0.0, sch_type_lim.lowerLimitValue().get()); + ASSERT_TRUE(sch_type_lim.upperLimitValue()); + EXPECT_EQ(1.0, sch_type_lim.upperLimitValue().get()); + ASSERT_TRUE(sch_type_lim.numericType()); + EXPECT_EQ("Discrete", sch_type_lim.numericType().get()); + EXPECT_EQ("Availability", sch_type_lim.unitType()); + outputTableAnnual.resetSchedule(); EXPECT_FALSE(outputTableAnnual.schedule()); }