From 05fdb5f9020eb35e2ab6dd9457b11765c552bcab Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 15:38:58 +0200 Subject: [PATCH 1/6] #1697 - Properly wrap ThermochromicGlazing (WindowMaterial:GlazingGroup:Thermochromic) with extensible fields --- src/model/ModelResources.i | 10 + src/model/ThermochromicGlazing.cpp | 367 ++++++++++++++---------- src/model/ThermochromicGlazing.hpp | 60 +++- src/model/ThermochromicGlazing_Impl.hpp | 84 ++---- 4 files changed, 305 insertions(+), 216 deletions(-) diff --git a/src/model/ModelResources.i b/src/model/ModelResources.i index 8acd51daec..07f47e456a 100644 --- a/src/model/ModelResources.i +++ b/src/model/ModelResources.i @@ -95,6 +95,15 @@ class HeatExchangerDesiccantBalancedFlow; } }; +%extend openstudio::model::ThermochromicGroup { + // Use the overloaded operator<< for string representation + std::string __str__() { + std::ostringstream os; + os << *$self; + return os.str(); + } +}; + MODELOBJECT_TEMPLATES(ScheduleType) MODELOBJECT_TEMPLATES(ScheduleInterval); MODELOBJECT_TEMPLATES(ScheduleFixedInterval); @@ -137,6 +146,7 @@ MODELOBJECT_TEMPLATES(SimpleGlazing); MODELOBJECT_TEMPLATES(StandardGlazing); MODELOBJECT_TEMPLATES(StandardOpaqueMaterial); MODELOBJECT_TEMPLATES(ThermochromicGlazing); +MODELOBJECT_TEMPLATES(ThermochromicGroup); // helper for extensible fields for ThermochromicGlazing MODELOBJECT_TEMPLATES(StandardsInformationMaterial); MODELOBJECT_TEMPLATES(ConstructionBase); MODELOBJECT_TEMPLATES(LayeredConstruction); diff --git a/src/model/ThermochromicGlazing.cpp b/src/model/ThermochromicGlazing.cpp index 921bf6785f..1e4801bbff 100644 --- a/src/model/ThermochromicGlazing.cpp +++ b/src/model/ThermochromicGlazing.cpp @@ -5,17 +5,61 @@ #include "ThermochromicGlazing.hpp" #include "ThermochromicGlazing_Impl.hpp" +#include "StandardGlazing.hpp" +#include "StandardGlazing_Impl.hpp" #include "ModelExtensibleGroup.hpp" -#include -#include - #include "../utilities/core/Assert.hpp" #include "../utilities/math/FloatCompare.hpp" +#include +#include + namespace openstudio { namespace model { + // Helper class for the extensible groups + ThermochromicGroup::ThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature) + : m_standardGlazing(standardGlazing), m_opticalDataTemperature(opticalDataTemperature) {} + + StandardGlazing ThermochromicGroup::standardGlazing() const { + return m_standardGlazing; + } + + double ThermochromicGroup::opticalDataTemperature() const { + return m_opticalDataTemperature; + } + + bool ThermochromicGroup::operator==(const ThermochromicGroup& other) const { + return (standardGlazing() == other.standardGlazing()) || openstudio::equal(opticalDataTemperature(), other.opticalDataTemperature()); + } + + bool ThermochromicGroup::operator!=(const ThermochromicGroup& other) const { + return (!operator==(other)); + } + + bool ThermochromicGroup::operator<(const ThermochromicGroup& other) const { + return opticalDataTemperature() < other.opticalDataTemperature(); + } + + bool ThermochromicGroup::operator>(const ThermochromicGroup& other) const { + return opticalDataTemperature() > other.opticalDataTemperature(); + } + + bool ThermochromicGroup::operator<=(const ThermochromicGroup& other) const { + return opticalDataTemperature() <= other.opticalDataTemperature(); + } + + bool ThermochromicGroup::operator>=(const ThermochromicGroup& other) const { + return opticalDataTemperature() >= other.opticalDataTemperature(); + } + + std::ostream& operator<<(std::ostream& out, const openstudio::model::ThermochromicGroup& ThermochromicGroup) { + out << "(StandardGlazing = '" << ThermochromicGroup.standardGlazing().nameString() << "', Optional Data Temperature = '" + << ThermochromicGroup.opticalDataTemperature() << "')"; + return out; + } + namespace detail { ThermochromicGlazing_Impl::ThermochromicGlazing_Impl(const IdfObject& idfObject, Model_Impl* model, bool keepHandle) @@ -35,211 +79,240 @@ namespace model { return ThermochromicGlazing::iddObjectType(); } - double ThermochromicGlazing_Impl::thickness() const { - GlazingVector glazings = mf_glazings(); - double result = 0.0; // running average - unsigned n = 0; - bool warned = false; - for (const Glazing& glazing : glazings) { - double glazingThickness = glazing.thickness(); - if (n == 0) { - result = glazingThickness; - } else { - // keep running average - if (!warned && !equal(glazingThickness, result)) { - LOG(Warn, "Thermochromic group '" << name().get() << "' contains glazings of different " << "thicknesses."); - warned = true; - } - result = (result * static_cast(n) + glazingThickness) / static_cast(n + 1); - } - ++n; - } + const std::vector& ThermochromicGlazing_Impl::outputVariableNames() const { + // TODO: Technically, this is not on this object, but on the Glazed Surface that uses the Construction that references this ThermochromicGlazing + // Add it to SubSurface? + static const std::vector result{"Surface Window Thermochromic Layer Temperature", + "Surface Window Thermochromic Layer Property Specification Temperature"}; return result; } - double ThermochromicGlazing_Impl::thermalConductivity() const { - LOG_AND_THROW("Thermal conductivity not yet supported for ThermochromicGlazings."); + unsigned int ThermochromicGlazing_Impl::numberofThermochromicGroups() const { + return numExtensibleGroups(); } - double ThermochromicGlazing_Impl::thermalConductance() const { - LOG_AND_THROW("Thermal conductance not yet supported for ThermochromicGlazings."); - } + std::vector ThermochromicGlazing_Impl::thermochromicGroups() const { + std::vector result; - double ThermochromicGlazing_Impl::thermalResistivity() const { - LOG_AND_THROW("Thermal resistivity not yet supported for ThermochromicGlazings."); - } + for (unsigned i = 0; i < numberofThermochromicGroups(); ++i) { - double ThermochromicGlazing_Impl::thermalResistance() const { - LOG_AND_THROW("Thermal resistance not yet supported for ThermochromicGlazings."); - } - - double ThermochromicGlazing_Impl::thermalTransmittance() const { - LOG_AND_THROW("Thermal transmittance not yet supported for ThermochromicGlazings."); - } - - double ThermochromicGlazing_Impl::thermalAbsorptance() const { - LOG_AND_THROW("Thermal absorptance not yet supported for ThermochromicGlazings."); - } + boost::optional group_ = getThermochromicGroup(i); - double ThermochromicGlazing_Impl::thermalReflectance() const { - LOG_AND_THROW("Thermal reflectance not yet supported for ThermochromicGlazings."); - } + // getThermochromicGroup 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 ThermochromicGroup group at index " << i); + } + } - double ThermochromicGlazing_Impl::solarTransmittance() const { - LOG_AND_THROW("Solar transmittance not yet supported for ThermochromicGlazings."); + return result; } - double ThermochromicGlazing_Impl::solarAbsorptance() const { - LOG_AND_THROW("Solar absorptance not yet supported for ThermochromicGlazings."); + boost::optional ThermochromicGlazing_Impl::thermochromicGroupIndex(const ThermochromicGroup& thermochromicGroup) const { + const std::vector groups = thermochromicGroups(); + auto it = std::find(groups.cbegin(), groups.cend(), thermochromicGroup); + if (it != groups.end()) { + return static_cast(std::distance(groups.cbegin(), it)); + } + return boost::none; } - double ThermochromicGlazing_Impl::solarReflectance() const { - LOG_AND_THROW("Solar reflectance not yet supported for ThermochromicGlazings."); - } + boost::optional ThermochromicGlazing_Impl::getThermochromicGroup(unsigned groupIndex) const { + if (groupIndex >= numberofThermochromicGroups()) { + LOG(Error, "Asked to get ThermochromicGroup with index " << groupIndex << ", but " << briefDescription() << " has just " + << numberofThermochromicGroups() << " ThermochromicGroups."); + return boost::none; + } + auto group = getExtensibleGroup(groupIndex).cast(); - OptionalDouble ThermochromicGlazing_Impl::getVisibleTransmittance() const { - LOG_AND_THROW("Visible transmittance not yet supported for ThermochromicGlazings."); - } + boost::optional standardGlazing_ = + group.getModelObjectTarget(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName); - double ThermochromicGlazing_Impl::visibleAbsorptance() const { - LOG_AND_THROW("Visible absorptance not yet supported for ThermochromicGlazings."); - } + boost::optional opticalDataTemperature_ = + group.getDouble(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature); - double ThermochromicGlazing_Impl::visibleReflectance() const { - LOG_AND_THROW("Visible reflectance not yet supported for ThermochromicGlazings."); - } + if (!standardGlazing_) { + LOG(Error, "Could not retrieve StandardGlazing (WindowMaterialGlazingName) for extensible group " << group.groupIndex() << "."); + return boost::none; + } + if (!opticalDataTemperature_) { + LOG(Error, "Could not retrieve OpticalDataTemperature for extensible group " << group.groupIndex() << "."); + return boost::none; + } - const std::vector& ThermochromicGlazing_Impl::outputVariableNames() const { - static const std::vector result; - return result; + return {ThermochromicGroup(*standardGlazing_, *opticalDataTemperature_)}; } - double ThermochromicGlazing_Impl::opticalDataTemperature() const { - // TODO - // boost::optional value = getDouble(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature,false); // no default - //OS_ASSERT(value); - //return value.get(); - return 0; - } + bool ThermochromicGlazing_Impl::addThermochromicGroup(const ThermochromicGroup& thermochromicGroup) { - bool ThermochromicGlazing_Impl::setThickness(double value) { - GlazingVector glazings = mf_glazings(); - DoubleVector rollbackValues; - for (unsigned i = 0, n = glazings.size(); i < n; ++i) { - rollbackValues.push_back(glazings[i].thickness()); - bool ok = glazings[i].setThickness(value); - if (!ok) { - // rollback previous values - for (int j = i - 1; j >= 0; --j) { - glazings[j].setThickness(rollbackValues[j]); - } - return false; - } + // Check if ThermochromicGroup already exists + boost::optional existingIndex_ = thermochromicGroupIndex(thermochromicGroup); + if (existingIndex_) { + boost::optional group_ = getThermochromicGroup(*existingIndex_); + OS_ASSERT(group_); + LOG(Warn, "For " << briefDescription() << ", ThermochromicGroup already exists, will be modified in place from " << group_.get() << " to " + << thermochromicGroup << "."); } - return true; - } - - bool ThermochromicGlazing_Impl::setThermalConductivity(double /*value*/) { - return false; - } - bool ThermochromicGlazing_Impl::setThermalConductance(double /*value*/) { - return false; - } - - bool ThermochromicGlazing_Impl::setThermalResistivity(double /*value*/) { - return false; + // If existing, get it, otherwise Push an extensible group. ModelExtensibleGroup cannot be default-constructed, so use a ternary operator + // Given that we will potentially push many extensible groups + // we will suffer from going through the internal checks in WorkspaceObject, especially in setPointer, + // and performance will be roughly O(n^2) when adding groups. + // So to improve the performance we pushExtensible group without validity check, and call setPointer without validity check (`isValid`) + std::vector temp; + ModelExtensibleGroup eg = (existingIndex_ ? getExtensibleGroup(existingIndex_.get()).cast() + : pushExtensibleGroup(temp, false).cast()); + + bool result = eg.setPointer(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName, + thermochromicGroup.standardGlazing().handle()); + result |= eg.setDouble(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature, + thermochromicGroup.opticalDataTemperature()); + if (!result) { + // Something went wrong + // So erase the new extensible group + getObject().eraseExtensibleGroup(eg.groupIndex()); + result = false; + } + return result; } - bool ThermochromicGlazing_Impl::setThermalResistance(double /*value*/) { - return false; + bool ThermochromicGlazing_Impl::addThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature) { + return addThermochromicGroup(ThermochromicGroup{standardGlazing, opticalDataTemperature}); } - bool ThermochromicGlazing_Impl::setThermalTransmittance(double /*value*/) { - return false; - } + bool ThermochromicGlazing_Impl::addThermochromicGroups(const std::vector& thermochromicGroups) { + bool result = true; - bool ThermochromicGlazing_Impl::setThermalAbsorptance(double /*value*/) { - return false; - } + for (const auto& thermochromicGroup : thermochromicGroups) { + bool thisResult = addThermochromicGroup(thermochromicGroup); + if (!thisResult) { + LOG(Error, "Could not add ThermochromicGroup " << thermochromicGroup << " to " << briefDescription() << ". Continuing with others."); + // OS_ASSERT(false); + result = false; + } + } - bool ThermochromicGlazing_Impl::setThermalReflectance(double /*value*/) { - return false; + return result; } - bool ThermochromicGlazing_Impl::setSolarTransmittance(double /*value*/) { - return false; - } + bool ThermochromicGlazing_Impl::removeThermochromicGroup(unsigned groupIndex) { + bool result = false; - bool ThermochromicGlazing_Impl::setSolarAbsorptance(double /*value*/) { - return false; + unsigned int num = numberofThermochromicGroups(); + if (groupIndex < num) { + getObject().eraseExtensibleGroup(groupIndex); + result = true; + } + return result; } - bool ThermochromicGlazing_Impl::setSolarReflectance(double /*value*/) { - return false; + void ThermochromicGlazing_Impl::removeAllThermochromicGroups() { + getObject().clearExtensibleGroups(); } - bool ThermochromicGlazing_Impl::setVisibleTransmittance(double /*value*/) { - return false; + boost::optional ThermochromicGlazing_Impl::getVisibleTransmittance() const { + LOG_AND_THROW("Visible transmittance not yet supported for ThermochromicGlazings."); } - bool ThermochromicGlazing_Impl::setVisibleAbsorptance(double /*value*/) { - return false; + boost::optional ThermochromicGlazing_Impl::interiorVisibleAbsorptance() const { + LOG_AND_THROW("Interior Visible Absorptance not yet supported for ThermochromicGlazings."); } - bool ThermochromicGlazing_Impl::setVisibleReflectance(double /*value*/) { - return false; + boost::optional ThermochromicGlazing_Impl::exteriorVisibleAbsorptance() const { + LOG_AND_THROW("Exterior Visible Absorptance not yet supported for ThermochromicGlazings."); } - std::vector ThermochromicGlazing_Impl::mf_glazings() const { - GlazingVector result; - for (const IdfExtensibleGroup& idfGroup : extensibleGroups()) { - auto group = idfGroup.cast(); - OptionalWorkspaceObject owo = group.getTarget(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName); - if (owo) { - OptionalGlazing og = owo->optionalCast(); - OS_ASSERT(og); - result.push_back(*og); + double ThermochromicGlazing_Impl::thickness() const { + double result = 0.0; // running average + unsigned n = 0; + bool warned = false; + for (const auto& group : thermochromicGroups()) { + double glazingThickness = group.standardGlazing().thickness(); + if (n == 0) { + result = glazingThickness; + } else { + // keep running average + if (!warned && !equal(glazingThickness, result)) { + LOG(Warn, "Thermochromic group '" << nameString() << "' contains glazings of different thicknesses."); + warned = true; + } + result = (result * static_cast(n) + glazingThickness) / static_cast(n + 1); } + ++n; } return result; } - bool ThermochromicGlazing_Impl::setOpticalDataTemperature(double value) { - bool result = setDouble(OS_WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature, value); - OS_ASSERT(result); - return result; + bool ThermochromicGlazing_Impl::setThickness(double value) { + auto groups = thermochromicGroups(); + std::vector rollbackValues; + for (unsigned i = 0, n = groups.size(); i < n; ++i) { + auto standardGlazing = groups[i].standardGlazing(); + rollbackValues.push_back(standardGlazing.thickness()); + bool ok = standardGlazing.setThickness(value); + if (!ok) { + // rollback previous values + for (int j = i - 1; j >= 0; --j) { + standardGlazing.setThickness(rollbackValues[j]); + } + return false; + } + } + return true; } } // namespace detail - ThermochromicGlazing::ThermochromicGlazing(const Model& model, double opticalDataTemperature) - : Glazing(ThermochromicGlazing::iddObjectType(), model) { + ThermochromicGlazing::ThermochromicGlazing(const Model& model) : Glazing(ThermochromicGlazing::iddObjectType(), model) { OS_ASSERT(getImpl()); - - // TODO: Appropriately handle the following required object-list fields. - bool ok = true; - // ok = setHandle(); - OS_ASSERT(ok); - - setOpticalDataTemperature(opticalDataTemperature); } IddObjectType ThermochromicGlazing::iddObjectType() { return {IddObjectType::OS_WindowMaterial_GlazingGroup_Thermochromic}; } - /// @cond - ThermochromicGlazing::ThermochromicGlazing(std::shared_ptr impl) : Glazing(std::move(impl)) {} - /// @endcond + std::vector ThermochromicGlazing::thermochromicGroups() const { + return getImpl()->thermochromicGroups(); + } - double ThermochromicGlazing::opticalDataTemperature() const { - return getImpl()->opticalDataTemperature(); + unsigned int ThermochromicGlazing::numberofThermochromicGroups() const { + return getImpl()->numberofThermochromicGroups(); } - bool ThermochromicGlazing::setOpticalDataTemperature(double value) { - return getImpl()->setOpticalDataTemperature(value); + boost::optional ThermochromicGlazing::thermochromicGroupIndex(const ThermochromicGroup& thermochromicGroup) const { + return getImpl()->thermochromicGroupIndex(thermochromicGroup); } + boost::optional ThermochromicGlazing::getThermochromicGroup(unsigned groupIndex) const { + return getImpl()->getThermochromicGroup(groupIndex); + } + + bool ThermochromicGlazing::addThermochromicGroup(const ThermochromicGroup& thermochromicGroup) { + return getImpl()->addThermochromicGroup(thermochromicGroup); + } + + bool ThermochromicGlazing::addThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature) { + return getImpl()->addThermochromicGroup(standardGlazing, opticalDataTemperature); + } + + bool ThermochromicGlazing::addThermochromicGroups(const std::vector& thermochromicGroups) { + return getImpl()->addThermochromicGroups(thermochromicGroups); + } + + bool ThermochromicGlazing::removeThermochromicGroup(unsigned groupIndex) { + return getImpl()->removeThermochromicGroup(groupIndex); + } + + void ThermochromicGlazing::removeAllThermochromicGroups() { + getImpl()->removeAllThermochromicGroups(); + } + + /// @cond + ThermochromicGlazing::ThermochromicGlazing(std::shared_ptr impl) : Glazing(std::move(impl)) {} + /// @endcond + } // namespace model } // namespace openstudio diff --git a/src/model/ThermochromicGlazing.hpp b/src/model/ThermochromicGlazing.hpp index c0797a399a..7841c0407a 100644 --- a/src/model/ThermochromicGlazing.hpp +++ b/src/model/ThermochromicGlazing.hpp @@ -8,6 +8,7 @@ #include "ModelAPI.hpp" #include "Glazing.hpp" +#include "StandardGlazing.hpp" namespace openstudio { namespace model { @@ -18,6 +19,37 @@ namespace model { } // namespace detail + /** This class implements a Thermochromic Group */ + class MODEL_API ThermochromicGroup + { + public: + /* Only accepts ModelObjects that are of type Surface, Subsurface or InternalMass, will throw otherwise */ + ThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature); + + StandardGlazing standardGlazing() const; + double opticalDataTemperature() const; + + // Equality is defined as having the same StandardGlazing **OR** has having equal opticalDataTemperature with epsilon tolerance + // Meaning we enforce both unitcity of StandardGlazing and opticalDataTemperature + bool operator==(const ThermochromicGroup& other) const; + bool operator!=(const ThermochromicGroup& other) const; + + // Sort order is defined by the opticalDataTemperature only, used in FT to ensure that the groups are sorted by temperature + bool operator<(const ThermochromicGroup& other) const; + bool operator>(const ThermochromicGroup& other) const; + bool operator<=(const ThermochromicGroup& other) const; + bool operator>=(const ThermochromicGroup& other) const; + + private: + // From + StandardGlazing m_standardGlazing; + double m_opticalDataTemperature; + REGISTER_LOGGER("openstudio.model.ThermochromicGroup"); + }; + + // Overload operator<< + MODEL_API std::ostream& operator<<(std::ostream& out, const openstudio::model::ThermochromicGroup& thermochromicGroup); + /** ThermochromicGlazing is a Glazing that wraps the OpenStudio IDD object 'OS:WindowMaterial:GlazingGroup:Thermochromic'. */ class MODEL_API ThermochromicGlazing : public Glazing { @@ -25,7 +57,7 @@ namespace model { /** @name Constructors and Destructors */ //@{ - explicit ThermochromicGlazing(const Model& model, double opticalDataTemperature = 80.0); + explicit ThermochromicGlazing(const Model& model); virtual ~ThermochromicGlazing() override = default; // Default the copy and move operators because the virtual dtor is explicit @@ -41,22 +73,32 @@ namespace model { /** @name Getters */ //@{ - double opticalDataTemperature() const; - - // TODO: Handle this object's extensible fields. - //@} /** @name Setters */ //@{ - bool setOpticalDataTemperature(double value); - - // TODO: Handle this object's extensible fields. - //@} /** @name Other */ //@{ + // Handle this object's extensible fields. + + std::vector thermochromicGroups() const; + + unsigned int numberofThermochromicGroups() const; + + boost::optional thermochromicGroupIndex(const ThermochromicGroup& thermochromicGroup) const; + boost::optional getThermochromicGroup(unsigned groupIndex) const; + + bool addThermochromicGroup(const ThermochromicGroup& thermochromicGroup); + + // Convenience fucntion that will create a ThermochromicGroup + bool addThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature); + + bool addThermochromicGroups(const std::vector& thermochromicGroups); + bool removeThermochromicGroup(unsigned groupIndex); + void removeAllThermochromicGroups(); + //@} protected: /// @cond diff --git a/src/model/ThermochromicGlazing_Impl.hpp b/src/model/ThermochromicGlazing_Impl.hpp index 5801eaa643..b9f9a6e73d 100644 --- a/src/model/ThermochromicGlazing_Impl.hpp +++ b/src/model/ThermochromicGlazing_Impl.hpp @@ -13,12 +13,14 @@ namespace openstudio { namespace model { + class StandardGlazing; + class ThermochromicGroup; + namespace detail { /** ThermochromicGlazing_Impl is a Glazing_Impl that is the implementation class for ThermochromicGlazing.*/ class MODEL_API ThermochromicGlazing_Impl : public Glazing_Impl { - public: /** @name Constructors and Destructors */ //@{ @@ -40,88 +42,50 @@ namespace model { virtual IddObjectType iddObjectType() const override; - //@} - /** @name Getters */ - //@{ - + // Will warn if all the referenced StandardGlazings don't have the same thickness virtual double thickness() const override; - virtual double thermalConductivity() const; - - virtual double thermalConductance() const; - - virtual double thermalResistivity() const; - - virtual double thermalResistance() const; - - virtual double thermalTransmittance() const; - - virtual double thermalAbsorptance() const; - - virtual double thermalReflectance() const; - - virtual double solarTransmittance() const; - - virtual double solarAbsorptance() const; - - virtual double solarReflectance() const; + // Will set the thickness of all referenced StandardGlazings + virtual bool setThickness(double value) override; virtual boost::optional getVisibleTransmittance() const override; - virtual double visibleAbsorptance() const; - - virtual double visibleReflectance() const; + virtual boost::optional interiorVisibleAbsorptance() const override; - double opticalDataTemperature() const; + virtual boost::optional exteriorVisibleAbsorptance() const override; - // TODO: Handle this object's extensible fields. + //@} + /** @name Getters */ + //@{ //@} /** @name Setters */ //@{ - virtual bool setThickness(double value) override; - - virtual bool setThermalConductivity(double value); - - virtual bool setThermalConductance(double value); - - virtual bool setThermalResistivity(double value); - - virtual bool setThermalResistance(double value); - - virtual bool setThermalTransmittance(double value); - - virtual bool setThermalAbsorptance(double value); - - virtual bool setThermalReflectance(double value); - - virtual bool setSolarTransmittance(double value); - - virtual bool setSolarAbsorptance(double value); + //@} + /** @name Other */ + //@{ - virtual bool setSolarReflectance(double value); + std::vector thermochromicGroups() const; - virtual bool setVisibleTransmittance(double value); + unsigned int numberofThermochromicGroups() const; - virtual bool setVisibleAbsorptance(double value); + boost::optional thermochromicGroupIndex(const ThermochromicGroup& thermochromicGroup) const; + boost::optional getThermochromicGroup(unsigned groupIndex) const; - virtual bool setVisibleReflectance(double value); + bool addThermochromicGroup(const ThermochromicGroup& thermochromicGroup); - bool setOpticalDataTemperature(double value); + // Convenience fucntion that will create a ThermochromicGroup + bool addThermochromicGroup(const StandardGlazing& standardGlazing, double opticalDataTemperature); - // TODO: Handle this object's extensible fields. - - //@} - /** @name Other */ - //@{ + bool addThermochromicGroups(const std::vector& thermochromicGroups); + bool removeThermochromicGroup(unsigned groupIndex); + void removeAllThermochromicGroups(); //@} protected: private: REGISTER_LOGGER("openstudio.model.ThermochromicGlazing"); - - std::vector mf_glazings() const; }; } // namespace detail From 146f24f9e97101d48a1c97a6d46640839dca33c2 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 15:39:59 +0200 Subject: [PATCH 2/6] #1697 - Add model tests --- src/model/CMakeLists.txt | 1 + src/model/test/ThermochromicGlazing_GTest.cpp | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 src/model/test/ThermochromicGlazing_GTest.cpp diff --git a/src/model/CMakeLists.txt b/src/model/CMakeLists.txt index 0cd8abff91..3544838c6b 100644 --- a/src/model/CMakeLists.txt +++ b/src/model/CMakeLists.txt @@ -2355,6 +2355,7 @@ set(${target_name}_test_src test/ThermalZone_GTest.cpp test/ThermalStorageIceDetailed_GTest.cpp test/ThermalStorageChilledWaterStratified_GTest.cpp + test/ThermochromicGlazing_GTest.cpp test/TemperingValve_GTest.cpp test/ThreeJSForwardTranslator_GTest.cpp test/ThreeJSReverseTranslator_GTest.cpp diff --git a/src/model/test/ThermochromicGlazing_GTest.cpp b/src/model/test/ThermochromicGlazing_GTest.cpp new file mode 100644 index 0000000000..5be74a7474 --- /dev/null +++ b/src/model/test/ThermochromicGlazing_GTest.cpp @@ -0,0 +1,105 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "ModelFixture.hpp" + +#include "../ThermochromicGlazing.hpp" +#include "../ThermochromicGlazing_Impl.hpp" +#include "../StandardGlazing.hpp" +#include "../StandardGlazing_Impl.hpp" + +using namespace openstudio; +using namespace openstudio::model; + +TEST_F(ModelFixture, ThermochromicGlazing_GettersSetters) { + Model m; + ThermochromicGlazing thermochromicGlazing(m); + + thermochromicGlazing.setName("My ThermochromicGlazing"); +} + +TEST_F(ModelFixture, ThermochromicGlazing_ThermochromicGroups) { + + Model m; + + StandardGlazing standardGlazing(m); + + EXPECT_NO_THROW(ThermochromicGroup(standardGlazing, -10.0)); + + ThermochromicGlazing thermochromicGlazing(m); + + EXPECT_EQ(0, thermochromicGlazing.numberofThermochromicGroups()); + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(standardGlazing, 0.0)); + EXPECT_EQ(1, thermochromicGlazing.numberofThermochromicGroups()); + + StandardGlazing standardGlazing2(m); + auto group2 = ThermochromicGroup(standardGlazing2, 10.0); + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(group2)); + EXPECT_EQ(2, thermochromicGlazing.numberofThermochromicGroups()); + + boost::optional group_ = thermochromicGlazing.getThermochromicGroup(1U); + ASSERT_TRUE(group_); + EXPECT_EQ(10.0, group_->opticalDataTemperature()); + EXPECT_EQ(standardGlazing2, group_->standardGlazing()); + + // Test that you cannot add the same group twice (based on StandardGlazing only), but that instead it'll overwrite the values + auto group2bis = ThermochromicGroup(standardGlazing2, 20.0); + // Test that we can locate an existing Group correctly (comparing on StandardGlazing only, ignoring opticalDataTemperature) + boost::optional _existingIndex = thermochromicGlazing.thermochromicGroupIndex(group2bis); + ASSERT_TRUE(_existingIndex); + EXPECT_EQ(1U, _existingIndex.get()); + // Now call add, which should jut override it + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(group2bis)); + // Should still have the same number + EXPECT_EQ(2, thermochromicGlazing.numberofThermochromicGroups()); + // Retrieve it again, making sure it changed only the Optical Data Temperature + boost::optional group2_ = thermochromicGlazing.getThermochromicGroup(1U); + ASSERT_TRUE(group2_); + EXPECT_EQ(20.0, group2_->opticalDataTemperature()); + EXPECT_EQ(standardGlazing2, group2_->standardGlazing()); + + // Test that you cannot get a group by an index that's too high + EXPECT_FALSE(thermochromicGlazing.getThermochromicGroup(thermochromicGlazing.numberofThermochromicGroups())); + + // Test that you cannot find a group if it doesn't exist + StandardGlazing standardGlazing3(m); + auto group3 = ThermochromicGroup(standardGlazing3, 30.0); + EXPECT_FALSE(thermochromicGlazing.thermochromicGroupIndex(group3)); + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(group3)); + EXPECT_TRUE(thermochromicGlazing.thermochromicGroupIndex(group3)); + EXPECT_EQ(3, thermochromicGlazing.numberofThermochromicGroups()); + + // Remove a group + EXPECT_FALSE(thermochromicGlazing.removeThermochromicGroup(1000)); + EXPECT_EQ(3, thermochromicGlazing.numberofThermochromicGroups()); + + EXPECT_TRUE(thermochromicGlazing.removeThermochromicGroup(1)); + EXPECT_EQ(2, thermochromicGlazing.numberofThermochromicGroups()); + + // check that remaining blocks moved correctly + std::vector groups = thermochromicGlazing.thermochromicGroups(); + EXPECT_EQ(2U, groups.size()); + EXPECT_EQ(0.0, groups[0].opticalDataTemperature()); + EXPECT_EQ(standardGlazing, groups[0].standardGlazing()); + + EXPECT_EQ(30.0, groups[1].opticalDataTemperature()); + EXPECT_EQ(standardGlazing3, groups[1].standardGlazing()); + + // more remove checking + thermochromicGlazing.removeAllThermochromicGroups(); + EXPECT_EQ(0, thermochromicGlazing.numberofThermochromicGroups()); + EXPECT_FALSE(thermochromicGlazing.removeThermochromicGroup(0)); + EXPECT_EQ(0, thermochromicGlazing.numberofThermochromicGroups()); + + // Via a vector + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroups({group2bis, group3})); + groups = thermochromicGlazing.thermochromicGroups(); + EXPECT_EQ(2U, groups.size()); + EXPECT_EQ(group2bis.standardGlazing(), groups[0].standardGlazing()); + EXPECT_EQ(group2bis.opticalDataTemperature(), groups[0].opticalDataTemperature()); + + EXPECT_EQ(group3.standardGlazing(), groups[1].standardGlazing()); + EXPECT_EQ(group3.opticalDataTemperature(), groups[1].opticalDataTemperature()); +} From aba6785bb248bda9fcd669eda0b343ecd55e6e30 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 15:42:59 +0200 Subject: [PATCH 3/6] #1697 - FT for ThermochromicGlazing --- src/energyplus/CMakeLists.txt | 2 + src/energyplus/ForwardTranslator.cpp | 3 +- src/energyplus/ForwardTranslator.hpp | 3 + .../ForwardTranslateThermochromicGlazing.cpp | 62 ++++++++ .../Test/ThermochromicGlazing_GTest.cpp | 143 ++++++++++++++++++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/energyplus/ForwardTranslator/ForwardTranslateThermochromicGlazing.cpp create mode 100644 src/energyplus/Test/ThermochromicGlazing_GTest.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index a23df24d94..1c4a7dd15b 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -420,6 +420,7 @@ set(${target_name}_src ForwardTranslator/ForwardTranslateThermalStorageIceDetailed.cpp ForwardTranslator/ForwardTranslateThermalStorageChilledWaterStratified.cpp ForwardTranslator/ForwardTranslateThermalZone.cpp + ForwardTranslator/ForwardTranslateThermochromicGlazing.cpp ForwardTranslator/ForwardTranslateThermostatSetpointDualSetpoint.cpp ForwardTranslator/ForwardTranslateTimestep.cpp ForwardTranslator/ForwardTranslateUnitarySystemPerformanceMultispeed.cpp @@ -845,6 +846,7 @@ set(${target_name}_test_src Test/TableLookup_GTest.cpp Test/ThermalStorageChilledWaterStratified_GTest.cpp Test/ThermalZone_GTest.cpp + Test/ThermochromicGlazing_GTest.cpp Test/ThermostatSetpointDualSetpoint_GTest.cpp Test/UnitarySystemPerformanceMultispeed_GTest.cpp Test/WindowPropertyFrameAndDivider_GTest.cpp diff --git a/src/energyplus/ForwardTranslator.cpp b/src/energyplus/ForwardTranslator.cpp index 4ef7c86364..98575da2a6 100644 --- a/src/energyplus/ForwardTranslator.cpp +++ b/src/energyplus/ForwardTranslator.cpp @@ -3131,7 +3131,8 @@ namespace energyplus { break; } case openstudio::IddObjectType::OS_WindowMaterial_GlazingGroup_Thermochromic: { - LOG(Warn, "OS_WindowMaterial_GlazingGroup_Thermochromic is not currently translated"); + auto glazing = modelObject.cast(); + retVal = translateThermochromicGlazing(glazing); break; } case openstudio::IddObjectType::OS_WindowProperty_FrameAndDivider: { diff --git a/src/energyplus/ForwardTranslator.hpp b/src/energyplus/ForwardTranslator.hpp index 2f6f6ec83a..25a4de3f5f 100644 --- a/src/energyplus/ForwardTranslator.hpp +++ b/src/energyplus/ForwardTranslator.hpp @@ -457,6 +457,7 @@ namespace model { class ThermalZone; class ThermalStorageIceDetailed; class ThermalStorageChilledWaterStratified; + class ThermochromicGlazing; class ThermostatSetpointDualSetpoint; class Timestep; class UnitarySystemPerformanceMultispeed; @@ -1522,6 +1523,8 @@ namespace energyplus { // which is when fields on Sizing:Zone related to it are all defaulted (Implemeted in ForwardTranslateSizingZone) boost::optional zoneDSZADName(const model::ThermalZone& zone); + boost::optional translateThermochromicGlazing(model::ThermochromicGlazing& modelObject); + boost::optional translateThermostatSetpointDualSetpoint(model::ThermostatSetpointDualSetpoint& tsds); boost::optional translateTimestep(model::Timestep& modelObject); diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateThermochromicGlazing.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateThermochromicGlazing.cpp new file mode 100644 index 0000000000..87fa4e14a1 --- /dev/null +++ b/src/energyplus/ForwardTranslator/ForwardTranslateThermochromicGlazing.cpp @@ -0,0 +1,62 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ForwardTranslator.hpp" +#include "../../model/Model.hpp" + +#include "../../model/ThermochromicGlazing.hpp" + +#include "../../utilities/math/FloatCompare.hpp" +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/Workspace.hpp" + +#include +#include + +#include // For sort + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ForwardTranslator::translateThermochromicGlazing(model::ThermochromicGlazing& modelObject) { + + auto groups = modelObject.thermochromicGroups(); + if (groups.empty()) { + LOG(Warn, "ThermochromicGlazing '" << modelObject.nameString() << "' has no ThermochromicGroups. Cannot translate."); + return boost::none; + } + // Instantiate an IdfObject of the class to store the values + IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::WindowMaterial_GlazingGroup_Thermochromic, modelObject); + + // Sort by optical data temperature (ThermochromicGroup already has the operator< for this, but I'm making it clearer by using a lambda) + std::sort(groups.begin(), groups.end(), + [](const ThermochromicGroup& a, const ThermochromicGroup& b) { return a.opticalDataTemperature() < b.opticalDataTemperature(); }); + + auto first_thickness = groups.front().standardGlazing().thickness(); + + for (const auto& group : groups) { + + IdfExtensibleGroup eg = idfObject.pushExtensibleGroup(); + eg.setDouble(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature, group.opticalDataTemperature()); + auto standardGlazing = group.standardGlazing(); + if (boost::optional wo_ = translateAndMapModelObject(standardGlazing)) { + eg.setString(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName, wo_->nameString()); + } + + // Check if the thickness is consistent across all groups + if (!openstudio::equal(group.standardGlazing().thickness(), first_thickness)) { + LOG(Warn, "ThermochromicGlazing '" << modelObject.nameString() << "' has inconsistent thicknesses across ThermochromicGroups. " + << "Expected: " << first_thickness << ", found: " << group.standardGlazing().thickness()); + } + } + + return idfObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio diff --git a/src/energyplus/Test/ThermochromicGlazing_GTest.cpp b/src/energyplus/Test/ThermochromicGlazing_GTest.cpp new file mode 100644 index 0000000000..3ee6ae54c7 --- /dev/null +++ b/src/energyplus/Test/ThermochromicGlazing_GTest.cpp @@ -0,0 +1,143 @@ +/*********************************************************************************************************************** +* 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/ThermochromicGlazing.hpp" +#include "../../model/ThermochromicGlazing_Impl.hpp" + +#include "../../model/Model.hpp" +#include "../../model/Construction.hpp" +#include "../../model/Space.hpp" +#include "../../model/StandardGlazing.hpp" +#include "../../model/StandardGlazing_Impl.hpp" +#include "../../model/Surface.hpp" +#include "../../model/SubSurface.hpp" +#include "../../model/ThermalZone.hpp" + +#include "../../utilities/geometry/Point3d.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 +#include + +using namespace openstudio::energyplus; +using namespace openstudio::model; +using namespace openstudio; + +TEST_F(EnergyPlusFixture, ForwardTranslator_WindowMaterialGlazingGroupThermochromic) { + + ForwardTranslator ft; + + Model m; + + ThermalZone tz(m); + Space space(m); + EXPECT_TRUE(space.setThermalZone(tz)); + + const std::vector sPoints{ + {0, 2, 0}, + {0, 0, 0}, + {2, 0, 0}, + {2, 2, 0}, + }; + Surface surface(sPoints, m); + surface.setName("Surface"); + surface.setSpace(space); + + const std::vector ssPoints = { + {0, 1, 0}, + {0, 0, 0}, + {1, 0, 0}, + {1, 1, 0}, + }; + + SubSurface subSurface(ssPoints, m); + subSurface.setName("SubSurface"); + subSurface.setSurface(surface); + + Construction construction(m); + subSurface.setConstruction(construction); + + ThermochromicGlazing thermochromicGlazing(m); + thermochromicGlazing.setName("My WindowMaterialGlazingGroupThermochromic"); + + // Not assigned to a Construction, not translated + { + // Assigned to a Surface + Workspace w = ft.translateModel(m); + + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::FenestrationSurface_Detailed).size()); + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Construction).size()); + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::WindowMaterial_GlazingGroup_Thermochromic).size()); + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::WindowMaterial_Glazing).size()); + } + + construction.insertLayer(0, thermochromicGlazing); + + // Assigned to a Construction, but no extensible groups, not translated + { + Workspace w = ft.translateModel(m); + + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::FenestrationSurface_Detailed).size()); + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Construction).size()); + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::WindowMaterial_GlazingGroup_Thermochromic).size()); + EXPECT_EQ(0, w.getObjectsByType(IddObjectType::WindowMaterial_Glazing).size()); + } + + // Create 5 groups + for (int i = 1; i <= 5; ++i) { + StandardGlazing standardGlazing(m); + standardGlazing.setName("StandardGlazing" + std::to_string(i)); + + double optionalDataTemperature = 10.00 * (i - 1); + + if (i % 2 == 0) { + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(standardGlazing, optionalDataTemperature)); + } else { + ThermochromicGroup group(standardGlazing, optionalDataTemperature); + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(group)); + } + EXPECT_EQ(i, thermochromicGlazing.numberofThermochromicGroups()); + } + EXPECT_EQ(5, thermochromicGlazing.numberofThermochromicGroups()); + + std::vector groups = thermochromicGlazing.thermochromicGroups(); + + { + Workspace w = ft.translateModel(m); + + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::FenestrationSurface_Detailed).size()); + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::Construction).size()); + EXPECT_EQ(1, w.getObjectsByType(IddObjectType::WindowMaterial_GlazingGroup_Thermochromic).size()); + EXPECT_EQ(5, w.getObjectsByType(IddObjectType::WindowMaterial_Glazing).size()); + + std::vector wos = w.getObjectsByType(IddObjectType::WindowMaterial_GlazingGroup_Thermochromic); + ASSERT_EQ(1, wos.size()); + const auto& wo = wos.front(); + EXPECT_EQ(thermochromicGlazing.nameString(), wo.nameString()); + + ASSERT_EQ(5, wo.numExtensibleGroups()); + for (const auto& idf_eg : wo.extensibleGroups()) { + const auto& group = groups[idf_eg.groupIndex()]; + + EXPECT_EQ(group.opticalDataTemperature(), + idf_eg.getDouble(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature).get()); + EXPECT_EQ(group.standardGlazing().nameString(), + idf_eg.getString(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName).get()); + } + } +} From e3b46bb543d55b0e8f69a976997e0063072978b6 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 15:44:20 +0200 Subject: [PATCH 4/6] #1697 - RT for ThermochromicGlazing --- src/energyplus/CMakeLists.txt | 1 + src/energyplus/ReverseTranslator.cpp | 4 ++ src/energyplus/ReverseTranslator.hpp | 2 + ...indowMaterialGlazingGroupThermochromic.cpp | 66 +++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 src/energyplus/ReverseTranslator/ReverseTranslateWindowMaterialGlazingGroupThermochromic.cpp diff --git a/src/energyplus/CMakeLists.txt b/src/energyplus/CMakeLists.txt index 1c4a7dd15b..1a2ce3cd5d 100644 --- a/src/energyplus/CMakeLists.txt +++ b/src/energyplus/CMakeLists.txt @@ -633,6 +633,7 @@ set(${target_name}_src ReverseTranslator/ReverseTranslateVersion.cpp ReverseTranslator/ReverseTranslateWindowMaterialGas.cpp ReverseTranslator/ReverseTranslateWindowMaterialGlazing.cpp + ReverseTranslator/ReverseTranslateWindowMaterialGlazingGroupThermochromic.cpp ReverseTranslator/ReverseTranslateWindowMaterialSimpleGlazingSystem.cpp ReverseTranslator/ReverseTranslateWindowPropertyFrameAndDivider.cpp ReverseTranslator/ReverseTranslateWindowShadingControl.cpp diff --git a/src/energyplus/ReverseTranslator.cpp b/src/energyplus/ReverseTranslator.cpp index 6d9217425d..d066a659f6 100644 --- a/src/energyplus/ReverseTranslator.cpp +++ b/src/energyplus/ReverseTranslator.cpp @@ -987,6 +987,10 @@ namespace energyplus { modelObject = translateWindowMaterialGlazing(workspaceObject); break; } + case openstudio::IddObjectType::WindowMaterial_GlazingGroup_Thermochromic: { + modelObject = translateWindowMaterialGlazingGroupThermochromic(workspaceObject); + break; + } case openstudio::IddObjectType::WindowMaterial_SimpleGlazingSystem: { modelObject = translateWindowMaterialSimpleGlazingSystem(workspaceObject); break; diff --git a/src/energyplus/ReverseTranslator.hpp b/src/energyplus/ReverseTranslator.hpp index ed68331a74..30900afd44 100644 --- a/src/energyplus/ReverseTranslator.hpp +++ b/src/energyplus/ReverseTranslator.hpp @@ -377,6 +377,8 @@ namespace energyplus { boost::optional translateWindowMaterialGlazing(const WorkspaceObject& workspaceObject); + boost::optional translateWindowMaterialGlazingGroupThermochromic(const WorkspaceObject& workspaceObject); + boost::optional translateWindowMaterialSimpleGlazingSystem(const WorkspaceObject& workspaceObject); boost::optional translateWindowPropertyFrameAndDivider(const WorkspaceObject& workspaceObject); diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateWindowMaterialGlazingGroupThermochromic.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateWindowMaterialGlazingGroupThermochromic.cpp new file mode 100644 index 0000000000..0440a3f952 --- /dev/null +++ b/src/energyplus/ReverseTranslator/ReverseTranslateWindowMaterialGlazingGroupThermochromic.cpp @@ -0,0 +1,66 @@ +/*********************************************************************************************************************** +* OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC. +* See also https://openstudio.net/license +***********************************************************************************************************************/ + +#include "../ReverseTranslator.hpp" + +#include "../../model/ThermochromicGlazing.hpp" +#include "../../model/StandardGlazing.hpp" +#include "../../model/StandardGlazing_Impl.hpp" + +#include "../../utilities/idf/IdfExtensibleGroup.hpp" +#include "../../utilities/idf/WorkspaceExtensibleGroup.hpp" + +#include +#include + +using namespace openstudio::model; + +namespace openstudio { + +namespace energyplus { + + boost::optional ReverseTranslator::translateWindowMaterialGlazingGroupThermochromic(const WorkspaceObject& workspaceObject) { + + openstudio::model::ThermochromicGlazing modelObject(m_model); + + // Name + if (boost::optional name_ = workspaceObject.name()) { + modelObject.setName(name_.get()); + } + + // Extensible groups + for (const IdfExtensibleGroup& eg : workspaceObject.extensibleGroups()) { + auto weg = eg.cast(); + + double opticalDataTemperature = 0.0; + if (auto opticalDataTemperature_ = weg.getDouble(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::OpticalDataTemperature)) { + opticalDataTemperature = *opticalDataTemperature_; + } else { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) has an opticalDataTemperature, but it's a required field. Assuming zero"); + } + + if (boost::optional wo_ = + weg.getTarget(WindowMaterial_GlazingGroup_ThermochromicExtensibleFields::WindowMaterialGlazingName)) { + if (boost::optional mo_ = translateAndMapWorkspaceObject(*wo_)) { + if (boost::optional standardGlazing_ = mo_->optionalCast()) { + modelObject.addThermochromicGroup(*standardGlazing_, opticalDataTemperature); + } else { + LOG(Warn, "Extensible group " << eg.groupIndex() + << "(0-indexed) has a wrong type for WindowMaterialGlazingName, expected StandardGlazing, got " + << mo_->briefDescription()); + } + } else { + LOG(Warn, "Could not translate WindowMaterialGlazingName for Extensible group " << eg.groupIndex()); + } + } else { + LOG(Warn, "Extensible group " << eg.groupIndex() << "(0-indexed) is missed the WindowMaterialGlazingName field, skipping group"); + } + } + + return modelObject; + } // End of translate function + +} // end namespace energyplus +} // end namespace openstudio From a6fd61060b202726f413340ba510b2524f4df06c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 15:54:18 +0200 Subject: [PATCH 5/6] Add a test for the thickness helper. --- src/model/test/ThermochromicGlazing_GTest.cpp | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/model/test/ThermochromicGlazing_GTest.cpp b/src/model/test/ThermochromicGlazing_GTest.cpp index 5be74a7474..6af9da273b 100644 --- a/src/model/test/ThermochromicGlazing_GTest.cpp +++ b/src/model/test/ThermochromicGlazing_GTest.cpp @@ -10,6 +10,8 @@ #include "../StandardGlazing.hpp" #include "../StandardGlazing_Impl.hpp" +#include "../../utilities/core/StringStreamLogSink.hpp" + using namespace openstudio; using namespace openstudio::model; @@ -103,3 +105,36 @@ TEST_F(ModelFixture, ThermochromicGlazing_ThermochromicGroups) { EXPECT_EQ(group3.standardGlazing(), groups[1].standardGlazing()); EXPECT_EQ(group3.opticalDataTemperature(), groups[1].opticalDataTemperature()); } + +TEST_F(ModelFixture, ThermochromicGlazing_Thickness) { + Model m; + ThermochromicGlazing thermochromicGlazing(m); + thermochromicGlazing.setName("My ThermochromicGlazing"); + + // Create 5 groups, all with different thicknesses: 0.01, 0.02, 0.03, 0.04, 0.05, average = 0.03 + for (int i = 1; i <= 5; ++i) { + StandardGlazing standardGlazing(m); + standardGlazing.setName("StandardGlazing" + std::to_string(i)); + + double optionalDataTemperature = 10.00 * (i - 1); + EXPECT_TRUE(thermochromicGlazing.addThermochromicGroup(standardGlazing, optionalDataTemperature)); + standardGlazing.setThickness(0.01 * i); + } + + StringStreamLogSink sink; + sink.setLogLevel(Warn); + EXPECT_DOUBLE_EQ(0.03, thermochromicGlazing.thickness()); + EXPECT_EQ(1, sink.logMessages().size()); + EXPECT_EQ(Warn, sink.logMessages().front().logLevel()); + EXPECT_EQ("openstudio.model.ThermochromicGlazing", sink.logMessages().front().logChannel()); + EXPECT_EQ("Thermochromic group 'My ThermochromicGlazing' contains glazings of different thicknesses.", sink.logMessages().front().logMessage()); + + sink.resetStringStream(); + EXPECT_TRUE(thermochromicGlazing.setThickness(0.04)); + for (const auto& group : thermochromicGlazing.thermochromicGroups()) { + EXPECT_DOUBLE_EQ(0.04, group.standardGlazing().thickness()); + } + EXPECT_EQ(0, sink.logMessages().size()); + EXPECT_DOUBLE_EQ(0.04, thermochromicGlazing.thickness()); + EXPECT_EQ(0, sink.logMessages().size()); +} From 1191f7e597186be51492dfc6122bb3d84ad3cf99 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 10 Jun 2025 18:09:04 +0200 Subject: [PATCH 6/6] MaterialPropertyGlazingSpectralData wasn't RT''ing the name of the object. --- .../ReverseTranslateMaterialPropertyGlazingSpectralData.cpp | 5 +++++ src/model/ThermochromicGlazing.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateMaterialPropertyGlazingSpectralData.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateMaterialPropertyGlazingSpectralData.cpp index c302ddaf39..c6211d3a59 100644 --- a/src/energyplus/ReverseTranslator/ReverseTranslateMaterialPropertyGlazingSpectralData.cpp +++ b/src/energyplus/ReverseTranslator/ReverseTranslateMaterialPropertyGlazingSpectralData.cpp @@ -25,6 +25,11 @@ namespace energyplus { openstudio::model::MaterialPropertyGlazingSpectralData glazingSpectralData(m_model); + // Name + if (boost::optional name_ = workspaceObject.name()) { + glazingSpectralData.setName(name_.get()); + } + // get extensible groups for spectral data fields for (const IdfExtensibleGroup& idfGroup : workspaceObject.extensibleGroups()) { auto workspaceGroup = idfGroup.cast(); diff --git a/src/model/ThermochromicGlazing.cpp b/src/model/ThermochromicGlazing.cpp index 1e4801bbff..ca0939b9d1 100644 --- a/src/model/ThermochromicGlazing.cpp +++ b/src/model/ThermochromicGlazing.cpp @@ -55,8 +55,8 @@ namespace model { } std::ostream& operator<<(std::ostream& out, const openstudio::model::ThermochromicGroup& ThermochromicGroup) { - out << "(StandardGlazing = '" << ThermochromicGroup.standardGlazing().nameString() << "', Optional Data Temperature = '" - << ThermochromicGroup.opticalDataTemperature() << "')"; + out << "(StandardGlazing = '" << ThermochromicGroup.standardGlazing().nameString() + << "', Optional Data Temperature = " << ThermochromicGroup.opticalDataTemperature() << ")"; return out; }