Repository: NREL/OpenStudio
Pull request #5372: Fix #5371 - add FuelTypes to PlantComponentUserDefined
Author: login: jmarrec
Labels: name: component - Standards
name: component - Model
name: Pull Request - Ready for CI
description: This pull request if finalized and is ready for continuous integration verification prior to merge.
Created: 2025-03-18T16:42:59Z | Merged: 2025-03-31T19:42:33Z

Pull request overview
---------------------

 - Fixes #5371

<!--- DESCRIBE PURPOSE OF THIS PULL REQUEST -->

<!--- Please read [OpenStudio Pull Requests](https://github.com/NREL/OpenStudio/wiki/OpenStudio-Pull-Requests) to better understand the OpenStudio Pull Request protocol. -->

### Pull Request Author

<!--- Add to this list or remove from it as applicable.  This is a simple templated set of guidelines. -->

 - [ ] Model API Changes / Additions
 - [ ] Any new or modified fields have been implemented in the EnergyPlus ForwardTranslator (and ReverseTranslator as appropriate)
 - [ ] Model API methods are tested (in `src/model/test`)
 - [ ] EnergyPlus ForwardTranslator Tests (in `src/energyplus/Test`)
 - [ ] If a new object or method, added a test in NREL/OpenStudio-resources: Add Link
 - [ ] If needed, added VersionTranslation rules for the objects (`src/osversion/VersionTranslator.cpp`)
 - [ ] Verified that C# bindings built fine on Windows, partial classes used as needed, etc.
 - [ ] All new and existing tests passes
 - [ ] If methods have been deprecated, update rest of code to use the new methods

**Labels:**

 - [ ] If change to an IDD file, add the label `IDDChange`
 - [ ] If breaking existing API, add the label `APIChange`
 - [ ] If deemed ready, add label `Pull Request - Ready for CI` so that CI builds your PR

### Review Checklist

This will not be exhaustively relevant to every PR.
 - [ ] Perform a Code Review on GitHub
 - [ ] Code Style, strip trailing whitespace, etc.
 - [ ] All related changes have been implemented: model changes, model tests, FT changes, FT tests, VersionTranslation, OS App
 - [ ] Labeling is ok
 - [ ] If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
 - [ ] If feature, test running new feature, try creative ways to break it
 - [ ] CI status: all green or justified


Linked issue #5371: PlantComponentUserDefined missing fuelTypes
Issue overview
--------------

Found today while working on 179d.
A proposed model that is created using this method: 

`create_central_air_source_heat_pump(model`

https://github.com/NREL/openstudio-standards/blob/429974633cf77f8386e838479b6dd69116a40805/lib/openstudio-standards/prototypes/common/objects/Prototype.CentralAirSourceHeatPump.rb#L14

## Current Behavior

Baseline determination ends up with a system that uses a gas boiler when the proposed model has only electricity 

## Expected Behavior

https://github.com/NREL/OpenStudio/blob/98207d21649408d964fbc2660110a4cbdb35eb7d/src/model/PlantComponentUserDefined.cpp#L501-L515

## Steps to Reproduce
<!-- Provide an unambiguous set of steps to reproduce this bug. -->
<!--- Provide a link to a model that has the bug (a minimum complete and verifiable example (MCVE) is preferred) and/or code to reproduce -->
1.
2.
3.
4.

## Possible Solution
<!--- Optional, but if you can, suggest a fix/reason for the bug, -->

## Details

### Environment

Some additional details about your environment for this issue (if relevant):
 - Platform (Operating system, version): all
 - Version of OpenStudio (if using an intermediate build, include SHA): 3.10.0 alpha

### Context

179d

Linked issue comments: user: login: jmarrec
created_at: 2025-03-19T10:01:02Z
body: @mdahlhausen @MatthewSteen @eringold I'd like to bring this to your attention since this affects openstudio-standards, as well the PR that fixes it:

* https://github.com/NREL/OpenStudio/pull/5372
author_association: COLLABORATOR

Diff:
diff --git a/src/model/EnergyManagementSystemMeteredOutputVariable.cpp b/src/model/EnergyManagementSystemMeteredOutputVariable.cpp
index ef1f555e645..2dfc50a0606 100644
--- a/src/model/EnergyManagementSystemMeteredOutputVariable.cpp
+++ b/src/model/EnergyManagementSystemMeteredOutputVariable.cpp
@@ -31,6 +31,7 @@
 #include <utilities/idd/OS_EnergyManagementSystem_MeteredOutputVariable_FieldEnums.hxx>
 
 #include "../utilities/core/Assert.hpp"
+#include "../utilities/data/DataEnums.hpp"
 
 namespace openstudio {
 namespace model {
@@ -308,6 +309,97 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", EndUseType already has `((Cogeneration)(Generators))` and
+      // there can't be more than 2 aliases, so handle it manually
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
+
+      // const std::vector<EndUseType> otherTypes{
+      //   EndUseType::InteriorLights, EndUseType::ExteriorLights, EndUseType::InteriorEquipment, EndUseType::ExteriorEquipment,
+      //   EndUseType::Fans,           EndUseType::Pumps,          EndUseType::HeatRejection,     EndUseType::Humidifier,
+      //   EndUseType::HeatRecovery,   EndUseType::WaterSystems,
+      // };
+      LOG(Info, "For " << briefDescription() << ", unable to determine componentType from End Use Category = '" << end_use_cat_str
+                       << "', assuming ComponentType::None");
+      return ComponentType::None;
+    }
+
+    boost::optional<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::fuelTypeFromResourceType() const {
+      // resourceTypeValues: contains all of the FuelType plus these
+      // ["WaterUse", "OnSiteWaterProduced", "MainsWaterSupply", "RainWaterCollected", "WellWaterDrawn", "CondensateWaterCollected",
+      //  "ElectricityProducedOnSite", "SolarWaterHeating", "SolarAirHeating"]
+      auto const res_type_str = this->resourceType();
+      FuelType ft;
+      try {
+        ft = FuelType(res_type_str);
+      } catch (...) {
+        if (openstudio::istringEqual(res_type_str, "SolarWaterHeating") || openstudio::istringEqual(res_type_str, "SolarAirHeating")
+            || openstudio::istringEqual(res_type_str, "ElectricityProducedOnSite")) {
+          ft = FuelType::Solar;
+        } else {
+          LOG(Info, "For " << briefDescription() << ", unable to determine fuelType From Resource Type = '" << res_type_str << "', assuming None");
+          return boost::none;
+        }
+      }
+      return ft;
+    }
+
+    std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::coolingFuelTypes() const {
+      std::vector<FuelType> result;
+      if (componentType() == ComponentType::Cooling) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(*ft_);
+        }
+      }
+      return result;
+    }
+
+    std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::heatingFuelTypes() const {
+      std::vector<FuelType> result;
+      if (componentType() == ComponentType::Heating) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(*ft_);
+        }
+      }
+      return result;
+    }
+
+    std::vector<AppGFuelType> EnergyManagementSystemMeteredOutputVariable_Impl::appGHeatingFuelTypes() const {
+      std::vector<AppGFuelType> result;
+      if (componentType() == ComponentType::Heating) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(convertFuelTypeToAppG(*ft_));
+        }
+      }
+      return result;
+    }
+
   }  // namespace detail
 
   EnergyManagementSystemMeteredOutputVariable::EnergyManagementSystemMeteredOutputVariable(const Model& model, const std::string eMSVariableName)
@@ -616,5 +708,21 @@ namespace model {
     : ModelObject(std::move(impl)) {}
   /// @endcond
 
+  ComponentType EnergyManagementSystemMeteredOutputVariable::componentType() const {
+    return getImpl<detail::EnergyManagementSystemMeteredOutputVariable_Impl>()->componentType();
+  }
+
+  std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable::coolingFuelTypes() const {
+    return getImpl<detail::EnergyManagementSystemMeteredOutputVariable_Impl>()->coolingFuelTypes();
+  }
+
+  std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable::heatingFuelTypes() const {
+    return getImpl<detail::EnergyManagementSystemMeteredOutputVariable_Impl>()->heatingFuelTypes();
+  }
+
+  std::vector<AppGFuelType> EnergyManagementSystemMeteredOutputVariable::appGHeatingFuelTypes() const {
+    return getImpl<detail::EnergyManagementSystemMeteredOutputVariable_Impl>()->appGHeatingFuelTypes();
+  }
+
 }  // namespace model
 }  // namespace openstudio
diff --git a/src/model/EnergyManagementSystemMeteredOutputVariable.hpp b/src/model/EnergyManagementSystemMeteredOutputVariable.hpp
index b46470a06fa..3d8e037e5ee 100644
--- a/src/model/EnergyManagementSystemMeteredOutputVariable.hpp
+++ b/src/model/EnergyManagementSystemMeteredOutputVariable.hpp
@@ -20,6 +20,10 @@
 
 namespace openstudio {
 
+class AppGFuelType;
+class ComponentType;
+class FuelType;
+
 namespace energyplus {
   class ReverseTranslator;
 }
@@ -147,6 +151,13 @@ namespace model {
     /** @name Other */
     //@{
 
+    // This is not an HVACComponent but it will help in the determination of the PlantUserDefinedComponent, so implementation is here because it's
+    // more logical
+    ComponentType componentType() const;
+    std::vector<FuelType> coolingFuelTypes() const;
+    std::vector<FuelType> heatingFuelTypes() const;
+    std::vector<AppGFuelType> appGHeatingFuelTypes() const;
+
     //@}
    protected:
     /// @cond
diff --git a/src/model/EnergyManagementSystemMeteredOutputVariable_Impl.hpp b/src/model/EnergyManagementSystemMeteredOutputVariable_Impl.hpp
index c834ca327e4..da334677523 100644
--- a/src/model/EnergyManagementSystemMeteredOutputVariable_Impl.hpp
+++ b/src/model/EnergyManagementSystemMeteredOutputVariable_Impl.hpp
@@ -12,6 +12,11 @@
 #include "EnergyManagementSystemSubroutine_Impl.hpp"
 
 namespace openstudio {
+
+class AppGFuelType;
+class ComponentType;
+class FuelType;
+
 namespace model {
 
   namespace detail {
@@ -114,6 +119,14 @@ namespace model {
       /** @name Other */
       //@{
 
+      // This is not an HVACComponent but it will help in the determination of the PlantUserDefinedComponent, so implementation is here because it's
+      // more logical
+      ComponentType componentType() const;
+      boost::optional<FuelType> fuelTypeFromResourceType() const;
+      std::vector<FuelType> coolingFuelTypes() const;
+      std::vector<FuelType> heatingFuelTypes() const;
+      std::vector<AppGFuelType> appGHeatingFuelTypes() const;
+
       //@}
      protected:
      private:
diff --git a/src/model/EnergyManagementSystemProgram.cpp b/src/model/EnergyManagementSystemProgram.cpp
index 83c692bfd0b..97a712f55ff 100644
--- a/src/model/EnergyManagementSystemProgram.cpp
+++ b/src/model/EnergyManagementSystemProgram.cpp
@@ -6,6 +6,9 @@
 #include "EnergyManagementSystemProgram.hpp"
 #include "EnergyManagementSystemProgram_Impl.hpp"
 
+#include "EnergyManagementSystemMeteredOutputVariable.hpp"
+#include "EnergyManagementSystemMeteredOutputVariable_Impl.hpp"
+
 #include <utilities/idd/IddEnums.hxx>
 #include <utilities/idd/OS_EnergyManagementSystem_Program_FieldEnums.hxx>
 
@@ -339,6 +342,12 @@ namespace model {
       return result;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable>
+      EnergyManagementSystemProgram_Impl::energyManagementSystemMeteredOutputVariables() const {
+      return getObject<ModelObject>().getModelObjectSources<EnergyManagementSystemMeteredOutputVariable>(
+        EnergyManagementSystemMeteredOutputVariable::iddObjectType());
+    }
+
   }  // namespace detail
 
   EnergyManagementSystemProgram::EnergyManagementSystemProgram(const Model& model)
@@ -382,6 +391,10 @@ namespace model {
     return getImpl<detail::EnergyManagementSystemProgram_Impl>()->invalidReferencedObjects();
   }
 
+  std::vector<EnergyManagementSystemMeteredOutputVariable> EnergyManagementSystemProgram::energyManagementSystemMeteredOutputVariables() const {
+    return getImpl<detail::EnergyManagementSystemProgram_Impl>()->energyManagementSystemMeteredOutputVariables();
+  }
+
   /// @cond
   EnergyManagementSystemProgram::EnergyManagementSystemProgram(std::shared_ptr<detail::EnergyManagementSystemProgram_Impl> impl)
     : ModelObject(std::move(impl)) {}
diff --git a/src/model/EnergyManagementSystemProgram.hpp b/src/model/EnergyManagementSystemProgram.hpp
index ab802e2acec..85ef049b440 100644
--- a/src/model/EnergyManagementSystemProgram.hpp
+++ b/src/model/EnergyManagementSystemProgram.hpp
@@ -12,6 +12,8 @@
 namespace openstudio {
 namespace model {
 
+  class EnergyManagementSystemMeteredOutputVariable;
+
   namespace detail {
 
     class EnergyManagementSystemProgram_Impl;
@@ -65,6 +67,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Convenience method to return all EnergyManagementSystemMeteredOutputVariable that refer to this Program
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
     //@}
    protected:
     /// @cond
diff --git a/src/model/EnergyManagementSystemProgram_Impl.hpp b/src/model/EnergyManagementSystemProgram_Impl.hpp
index 0bda13fe503..01eb5881f31 100644
--- a/src/model/EnergyManagementSystemProgram_Impl.hpp
+++ b/src/model/EnergyManagementSystemProgram_Impl.hpp
@@ -12,6 +12,8 @@
 namespace openstudio {
 namespace model {
 
+  class EnergyManagementSystemMeteredOutputVariable;
+
   namespace detail {
 
     /** EnergyManagementSystemProgram_Impl is a ModelObject_Impl that is the implementation class for EnergyManagementSystemProgram.*/
@@ -65,6 +67,8 @@ namespace model {
       /** @name Other */
       //@{
 
+      std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
       //@}
      protected:
      private:
diff --git a/src/model/EnergyManagementSystemSubroutine.cpp b/src/model/EnergyManagementSystemSubroutine.cpp
index 24b7d281d2d..cba5ae09aec 100644
--- a/src/model/EnergyManagementSystemSubroutine.cpp
+++ b/src/model/EnergyManagementSystemSubroutine.cpp
@@ -6,6 +6,9 @@
 #include "EnergyManagementSystemSubroutine.hpp"
 #include "EnergyManagementSystemSubroutine_Impl.hpp"
 
+#include "EnergyManagementSystemMeteredOutputVariable.hpp"
+#include "EnergyManagementSystemMeteredOutputVariable_Impl.hpp"
+
 #include <utilities/idd/IddEnums.hxx>
 #include <utilities/idd/OS_EnergyManagementSystem_Subroutine_FieldEnums.hxx>
 
@@ -336,6 +339,12 @@ namespace model {
       }
       return result;
     }
+
+    std::vector<EnergyManagementSystemMeteredOutputVariable>
+      EnergyManagementSystemSubroutine_Impl::energyManagementSystemMeteredOutputVariables() const {
+      return getObject<ModelObject>().getModelObjectSources<EnergyManagementSystemMeteredOutputVariable>(
+        EnergyManagementSystemMeteredOutputVariable::iddObjectType());
+    }
   }  // namespace detail
 
   EnergyManagementSystemSubroutine::EnergyManagementSystemSubroutine(const Model& model)
@@ -379,6 +388,10 @@ namespace model {
     return getImpl<detail::EnergyManagementSystemSubroutine_Impl>()->invalidReferencedObjects();
   }
 
+  std::vector<EnergyManagementSystemMeteredOutputVariable> EnergyManagementSystemSubroutine::energyManagementSystemMeteredOutputVariables() const {
+    return getImpl<detail::EnergyManagementSystemSubroutine_Impl>()->energyManagementSystemMeteredOutputVariables();
+  }
+
   /// @cond
   EnergyManagementSystemSubroutine::EnergyManagementSystemSubroutine(std::shared_ptr<detail::EnergyManagementSystemSubroutine_Impl> impl)
     : ModelObject(std::move(impl)) {}
diff --git a/src/model/EnergyManagementSystemSubroutine.hpp b/src/model/EnergyManagementSystemSubroutine.hpp
index 3c918c79405..02b51a7478f 100644
--- a/src/model/EnergyManagementSystemSubroutine.hpp
+++ b/src/model/EnergyManagementSystemSubroutine.hpp
@@ -12,6 +12,8 @@
 namespace openstudio {
 namespace model {
 
+  class EnergyManagementSystemMeteredOutputVariable;
+
   namespace detail {
 
     class EnergyManagementSystemSubroutine_Impl;
@@ -65,6 +67,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Convenience method to return all EnergyManagementSystemMeteredOutputVariable that refer to this Subroutine
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
     //@}
    protected:
     /// @cond
diff --git a/src/model/EnergyManagementSystemSubroutine_Impl.hpp b/src/model/EnergyManagementSystemSubroutine_Impl.hpp
index fce2863360d..eb24779901a 100644
--- a/src/model/EnergyManagementSystemSubroutine_Impl.hpp
+++ b/src/model/EnergyManagementSystemSubroutine_Impl.hpp
@@ -12,6 +12,8 @@
 namespace openstudio {
 namespace model {
 
+  class EnergyManagementSystemMeteredOutputVariable;
+
   namespace detail {
 
     /** EnergyManagementSystemSubroutine_Impl is a ModelObject_Impl that is the implementation class for EnergyManagementSystemSubroutine.*/
@@ -65,6 +67,8 @@ namespace model {
       /** @name Other */
       //@{
 
+      std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
       //@}
      protected:
      private:
diff --git a/src/model/PlantComponentUserDefined.cpp b/src/model/PlantComponentUserDefined.cpp
index d33ea765c58..28bd16c9ea9 100644
--- a/src/model/PlantComponentUserDefined.cpp
+++ b/src/model/PlantComponentUserDefined.cpp
@@ -15,6 +15,8 @@
 #include "EnergyManagementSystemProgram_Impl.hpp"
 #include "EnergyManagementSystemActuator.hpp"
 #include "EnergyManagementSystemActuator_Impl.hpp"
+#include "EnergyManagementSystemMeteredOutputVariable.hpp"
+#include "EnergyManagementSystemMeteredOutputVariable_Impl.hpp"
 #include "Connection.hpp"
 #include "Connection_Impl.hpp"
 #include "Node.hpp"
@@ -24,6 +26,7 @@
 
 #include "../utilities/core/Assert.hpp"
 #include "../utilities/data/DataEnums.hpp"
+#include "../utilities/core/ContainersMove.hpp"
 
 #include <utilities/idd/IddFactory.hxx>
 #include <utilities/idd/IddEnums.hxx>
@@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
+
     ComponentType PlantComponentUserDefined_Impl::componentType() const {
-      return ComponentType::Both;
+      bool has_heating = false;
+      bool has_cooling = false;
+
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        auto compType = ems_metered_var.componentType();
+        if (compType == ComponentType::Cooling) {
+          has_cooling = true;
+        } else if (compType == ComponentType::Heating) {
+          has_heating = true;
+        } else if (compType == ComponentType::Both) {
+          has_cooling = true;
+          has_heating = true;
+        }
+      }
+
+      if (has_cooling && has_heating) {
+        return ComponentType::Both;
+      } else if (has_cooling) {
+        return ComponentType::Cooling;
+      } else if (has_heating) {
+        return ComponentType::Heating;
+      }
+      return ComponentType::None;
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::coolingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.coolingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::heatingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.heatingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
     std::vector<AppGFuelType> PlantComponentUserDefined_Impl::appGHeatingFuelTypes() const {
-      return {AppGFuelType::Other};
+      std::set<AppGFuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.appGHeatingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
   }  // namespace detail
@@ -791,6 +845,10 @@ namespace model {
     return getImpl<detail::PlantComponentUserDefined_Impl>()->createSimProgram(otActuator, mfrActuator, model);
   }
 
+  std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined::energyManagementSystemMeteredOutputVariables() const {
+    return getImpl<detail::PlantComponentUserDefined_Impl>()->energyManagementSystemMeteredOutputVariables();
+  }
+
   /// @cond
   PlantComponentUserDefined::PlantComponentUserDefined(std::shared_ptr<detail::PlantComponentUserDefined_Impl> impl) : StraightComponent(impl) {}
   /// @endcond
diff --git a/src/model/PlantComponentUserDefined.hpp b/src/model/PlantComponentUserDefined.hpp
index 0c366fec309..df85749a8f0 100644
--- a/src/model/PlantComponentUserDefined.hpp
+++ b/src/model/PlantComponentUserDefined.hpp
@@ -21,6 +21,7 @@ namespace model {
   class EnergyManagementSystemProgramCallingManager;
   class EnergyManagementSystemProgram;
   class EnergyManagementSystemActuator;
+  class EnergyManagementSystemMeteredOutputVariable;
   class Connection;
   class Node;
   class ThermalZone;
@@ -156,6 +157,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Will look into the MainModelProgram and the PlantSimulationProgram 's linked EMS Metered Output Variables
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
     //@}
    protected:
     /// @cond
diff --git a/src/model/PlantComponentUserDefined_Impl.hpp b/src/model/PlantComponentUserDefined_Impl.hpp
index bcb94cf10fd..b58d2370505 100644
--- a/src/model/PlantComponentUserDefined_Impl.hpp
+++ b/src/model/PlantComponentUserDefined_Impl.hpp
@@ -22,6 +22,7 @@ namespace model {
   class EnergyManagementSystemProgramCallingManager;
   class EnergyManagementSystemProgram;
   class EnergyManagementSystemActuator;
+  class EnergyManagementSystemMeteredOutputVariable;
   class Connection;
   class Node;
   class ThermalZone;
@@ -171,6 +172,9 @@ namespace model {
       /** @name Other */
       //@{
 
+      // Will look into the MainModelProgram and the PlantSimulationProgram 's linked EMS Metered Output Variables
+      std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
+
       //@}
      protected:
      private:
diff --git a/src/model/test/EnergyManagementSystemMeteredOutputVariable_GTest.cpp b/src/model/test/EnergyManagementSystemMeteredOutputVariable_GTest.cpp
index eb7db20b2e1..c14b838ed6a 100644
--- a/src/model/test/EnergyManagementSystemMeteredOutputVariable_GTest.cpp
+++ b/src/model/test/EnergyManagementSystemMeteredOutputVariable_GTest.cpp
@@ -79,3 +79,59 @@ TEST_F(ModelFixture, EMSMeteredOutputVariable) {
   ASSERT_TRUE(outvar_sen.emsSensor());
   EXPECT_EQ(outvar_sen.emsVariableObject().get(), outvar_sen.emsSensor().get());
 }
+
+TEST_F(ModelFixture, EMSMeteredOutputVariable_fuelTypes) {
+
+  Model model;
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(model, "MeteredVar");
+
+  EXPECT_TRUE(meteredoutvar.setResourceType("NaturalGas"));
+
+  std::vector<std::pair<std::string, ComponentType>> expectedComponentTypes{
+    {"Heating", ComponentType::Heating},
+    {"HeatingCoils", ComponentType::Heating},
+    {"Boilers", ComponentType::Heating},
+    {"Baseboard", ComponentType::Heating},
+    {"HeatRecoveryForHeating", ComponentType::Heating},
+    {"OnSiteGeneration", ComponentType::Heating},
+    {"Cooling", ComponentType::Cooling},
+    {"Refrigeration", ComponentType::Cooling},
+    {"HeatRecoveryForCooling", ComponentType::Cooling},
+    {"HeatRecoveryForHeating", ComponentType::Heating},
+    {"CoolingCoils", ComponentType::Cooling},
+    {"Chillers", ComponentType::Cooling},
+    {"InteriorLights", ComponentType::None},
+    {"InteriorEquipment", ComponentType::None},
+    {"ExteriorEquipment", ComponentType::None},
+    {"Fans", ComponentType::None},
+    {"Pumps", ComponentType::None},
+    {"HeatRejection", ComponentType::None},
+    {"Humidifier", ComponentType::None},
+    {"HeatRecovery", ComponentType::None},
+    {"WaterSystems", ComponentType::None},
+  };
+  const auto validCats = EnergyManagementSystemMeteredOutputVariable::endUseCategoryValues();
+
+  // Start by asserting that it doesn't throw for no reason
+  {
+    for (const auto& [cat, expectedComponentType] : expectedComponentTypes) {
+      SCOPED_TRACE(cat.data());
+      EXPECT_TRUE(meteredoutvar.setEndUseCategory(cat));
+      EXPECT_NO_THROW(meteredoutvar.componentType());
+      EXPECT_EQ(expectedComponentType, meteredoutvar.componentType()) << "Failed for " << cat;
+      if (expectedComponentType == ComponentType::Heating) {
+        EXPECT_TRUE(meteredoutvar.coolingFuelTypes().empty());
+        testFuelTypeEquality({FuelType::Gas}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({AppGFuelType::Fuel}, meteredoutvar.appGHeatingFuelTypes());
+      } else if (expectedComponentType == ComponentType::Cooling) {
+        testFuelTypeEquality({FuelType::Gas}, meteredoutvar.coolingFuelTypes());
+        testFuelTypeEquality({}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({}, meteredoutvar.appGHeatingFuelTypes());
+      } else {
+        testFuelTypeEquality({}, meteredoutvar.coolingFuelTypes());
+        testFuelTypeEquality({}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({}, meteredoutvar.appGHeatingFuelTypes());
+      }
+    }
+  }
+}
diff --git a/src/model/test/EnergyManagementSystemProgram_GTest.cpp b/src/model/test/EnergyManagementSystemProgram_GTest.cpp
index e78cf8208ff..abafe044dad 100644
--- a/src/model/test/EnergyManagementSystemProgram_GTest.cpp
+++ b/src/model/test/EnergyManagementSystemProgram_GTest.cpp
@@ -15,6 +15,7 @@
 #include "../EnergyManagementSystemActuator.hpp"
 #include "../EnergyManagementSystemSensor.hpp"
 #include "../EnergyManagementSystemProgram.hpp"
+#include "../EnergyManagementSystemMeteredOutputVariable.hpp"
 #include "../OutputVariable.hpp"
 #include "../OutputVariable_Impl.hpp"
 #include "../Model_Impl.hpp"
@@ -317,3 +318,17 @@ TEST_F(ModelFixture, EMSProgramUid_EMSProgramUid) {
   EXPECT_EQ(true,
             (fan_program_1.referencedObjects()[2].nameString() == fanName) || (fan_program_1.referencedObjects()[2].nameString() == "OATdb_Sensor"));
 }
+
+TEST_F(ModelFixture, EMSProgram_emsMeteredOutputVariables) {
+  Model model;
+
+  //add program
+  EnergyManagementSystemProgram program(model);
+  EXPECT_TRUE(program.energyManagementSystemMeteredOutputVariables().empty());
+
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(model, "Elec_Consumption");
+  EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(program));
+  EXPECT_EQ(program.nameString(), meteredoutvar.emsProgramOrSubroutineName());
+  ASSERT_EQ(1, program.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(meteredoutvar, program.energyManagementSystemMeteredOutputVariables().at(0));
+}
diff --git a/src/model/test/EnergyManagementSystemSubroutine_GTest.cpp b/src/model/test/EnergyManagementSystemSubroutine_GTest.cpp
index 18c0a34a23d..509091f8abb 100644
--- a/src/model/test/EnergyManagementSystemSubroutine_GTest.cpp
+++ b/src/model/test/EnergyManagementSystemSubroutine_GTest.cpp
@@ -16,6 +16,7 @@
 #include "../EnergyManagementSystemProgram.hpp"
 #include "../EnergyManagementSystemSensor.hpp"
 #include "../EnergyManagementSystemSubroutine.hpp"
+#include "../EnergyManagementSystemMeteredOutputVariable.hpp"
 #include "../OutputVariable.hpp"
 #include "../OutputVariable_Impl.hpp"
 #include "../Model_Impl.hpp"
@@ -195,3 +196,17 @@ TEST_F(ModelFixture, EMSSubroutine_EMSSubroutine) {
   body = fan_program_3.body();
   EXPECT_EQ("", body.get());
 }
+
+TEST_F(ModelFixture, EMSSubroutine_emsMeteredOutputVariables) {
+  Model model;
+
+  //add program
+  EnergyManagementSystemSubroutine program(model);
+  EXPECT_TRUE(program.energyManagementSystemMeteredOutputVariables().empty());
+
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(model, "Elec_Consumption");
+  EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(program));
+  EXPECT_EQ(program.nameString(), meteredoutvar.emsProgramOrSubroutineName());
+  ASSERT_EQ(1, program.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(meteredoutvar, program.energyManagementSystemMeteredOutputVariables().at(0));
+}
diff --git a/src/model/test/PlantComponentUserDefined_GTest.cpp b/src/model/test/PlantComponentUserDefined_GTest.cpp
index de2c137c994..72eb0eececc 100644
--- a/src/model/test/PlantComponentUserDefined_GTest.cpp
+++ b/src/model/test/PlantComponentUserDefined_GTest.cpp
@@ -14,11 +14,9 @@
 #include "../Node_Impl.hpp"
 #include "../AirLoopHVACZoneSplitter.hpp"
 #include "../EnergyManagementSystemProgramCallingManager.hpp"
+#include "../EnergyManagementSystemProgram.hpp"
 #include "../EnergyManagementSystemActuator.hpp"
-
-#include "../../utilities/core/Path.hpp"
-#include "../../utilities/idf/IdfFile.hpp"
-#include "../../utilities/idf/IdfObject.hpp"
+#include "../EnergyManagementSystemMeteredOutputVariable.hpp"
 
 //using namespace openstudio::model;
 using namespace openstudio::energyplus;
@@ -211,3 +209,119 @@ TEST_F(ModelFixture, PlantComponentUserDefined_constructor) {
   EXPECT_TRUE(b1.designVolumeFlowRateActuator().get().actuatedComponent());
   EXPECT_EQ(b1.handle(), b1.designVolumeFlowRateActuator().get().actuatedComponent().get().handle());
 }
+
+TEST_F(ModelFixture, PlantComponentUserDefined_EMSProgram_emsMeteredOutputVariables) {
+
+  Model m;
+  PlantComponentUserDefined plant_comp(m);
+  plant_comp.setPlantLoadingMode("MeetsLoadWithNominalCapacityHiOutLimit");
+  plant_comp.setPlantLoopFlowRequestMode("NeedsFlowIfLoopIsOn");
+
+  //PlantComponentUserDefined has CompType::None until it has EMS MeteredOutputVariables
+  EXPECT_EQ(ComponentType(ComponentType::None), plant_comp.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({}, plant_comp.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({}, plant_comp.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({}, plant_comp.appGHeatingFuelTypes()));
+
+  EXPECT_EQ(0, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+
+  // I create an EMSMeteredOutputVar of type "Heating" and "Electricity" into the plantSimulationProgram,
+  // and check that the PlantComponentUserDefined has the right fuel types
+  auto sim_pgrm = plant_comp.plantSimulationProgram().get();
+  sim_pgrm.setName("Sim_Pgrm");
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0");
+  {
+    EnergyManagementSystemMeteredOutputVariable meteredoutvar(m, "Plant Heating Comp Electricity Consumption");
+    EXPECT_TRUE(meteredoutvar.setEMSVariableName("Elec_Htg_Cons"));
+    EXPECT_TRUE(meteredoutvar.setUpdateFrequency("SystemTimestep"));
+    EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(sim_pgrm));
+    EXPECT_TRUE(meteredoutvar.setResourceType("Electricity"));
+    EXPECT_TRUE(meteredoutvar.setGroupType("HVAC"));
+    EXPECT_TRUE(meteredoutvar.setEndUseCategory("Heating"));
+    EXPECT_TRUE(meteredoutvar.setEndUseSubcategory(""));
+    EXPECT_TRUE(meteredoutvar.setUnits("J"));
+    EXPECT_EQ(ComponentType(ComponentType::Heating), meteredoutvar.componentType());
+    EXPECT_TRUE(testFuelTypeEquality({}, meteredoutvar.coolingFuelTypes()));
+    EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, meteredoutvar.heatingFuelTypes()));
+    EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, meteredoutvar.appGHeatingFuelTypes()));
+  }
+
+  EXPECT_EQ(1, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant_comp.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({}, plant_comp.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, plant_comp.appGHeatingFuelTypes()));
+
+  // Plant inherits it correctly AFTER I assign the PlantComponentUserDefined on the supply side
+  PlantLoop plant(m);
+  EXPECT_EQ(ComponentType(ComponentType::None), plant.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({}, plant.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({}, plant.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({}, plant.appGHeatingFuelTypes()));
+
+  EXPECT_TRUE(plant.addSupplyBranchForComponent(plant_comp));
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({}, plant.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, plant.appGHeatingFuelTypes()));
+
+  // Test with a second variable, this type "Chillers" with "Natural Gas"
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0\nSET Elec_Clg_Cons = 0");
+  {
+    EnergyManagementSystemMeteredOutputVariable meteredoutvar2(m, "Plant Cooling Comp Electricity Consumption");
+    EXPECT_TRUE(meteredoutvar2.setEMSVariableName("Elec_Htg_Cons"));
+    EXPECT_TRUE(meteredoutvar2.setUpdateFrequency("SystemTimestep"));
+    EXPECT_TRUE(meteredoutvar2.setEMSProgramOrSubroutineName(sim_pgrm));
+    EXPECT_TRUE(meteredoutvar2.setResourceType("NaturalGas"));
+    EXPECT_TRUE(meteredoutvar2.setGroupType("HVAC"));
+    EXPECT_TRUE(meteredoutvar2.setEndUseCategory("Chillers"));
+    EXPECT_TRUE(meteredoutvar2.setEndUseSubcategory(""));
+    EXPECT_TRUE(meteredoutvar2.setUnits("J"));
+    EXPECT_EQ(ComponentType(ComponentType::Cooling), meteredoutvar2.componentType());
+    EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, meteredoutvar2.coolingFuelTypes()));
+    EXPECT_TRUE(testFuelTypeEquality({}, meteredoutvar2.heatingFuelTypes()));
+    EXPECT_TRUE(testAppGFuelTypeEquality({}, meteredoutvar2.appGHeatingFuelTypes()));
+  }
+
+  EXPECT_EQ(2, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant_comp.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant_comp.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, plant_comp.appGHeatingFuelTypes()));
+
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, plant.appGHeatingFuelTypes()));
+
+  // Test with a third varaible, this time in the mainModelProgram, "OnSiteGeneration" (Cogeneration) with Diesel
+  EnergyManagementSystemProgram main_pgrm(m);
+  main_pgrm.setBody("SET Cogen_Cons = 0");
+  EXPECT_TRUE(plant_comp.setMainModelProgram(main_pgrm));
+  {
+    EnergyManagementSystemMeteredOutputVariable meteredoutvar3(m, "Plant Cogen Comp Diesel Consumption");
+    EXPECT_TRUE(meteredoutvar3.setEMSVariableName("Cogen_Cons"));
+    EXPECT_TRUE(meteredoutvar3.setUpdateFrequency("SystemTimestep"));
+    EXPECT_TRUE(meteredoutvar3.setEMSProgramOrSubroutineName(main_pgrm));
+    EXPECT_TRUE(meteredoutvar3.setResourceType("Diesel"));
+    EXPECT_TRUE(meteredoutvar3.setGroupType("HVAC"));
+    EXPECT_TRUE(meteredoutvar3.setEndUseCategory("OnSiteGeneration"));
+    EXPECT_TRUE(meteredoutvar3.setEndUseSubcategory(""));
+    EXPECT_TRUE(meteredoutvar3.setUnits("J"));
+    EXPECT_EQ(ComponentType(ComponentType::Heating), meteredoutvar3.componentType());
+    EXPECT_TRUE(testFuelTypeEquality({}, meteredoutvar3.coolingFuelTypes()));
+    EXPECT_TRUE(testFuelTypeEquality({FuelType::Diesel}, meteredoutvar3.heatingFuelTypes()));
+    EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Fuel}, meteredoutvar3.appGHeatingFuelTypes()));
+  }
+
+  EXPECT_EQ(3, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant_comp.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant_comp.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity, FuelType::Diesel}, plant_comp.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric, AppGFuelType::Fuel}, plant_comp.appGHeatingFuelTypes()));
+
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity, FuelType::Diesel}, plant.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric, AppGFuelType::Fuel}, plant.appGHeatingFuelTypes()));
+}
diff --git a/src/utilities/data/DataEnums.hpp b/src/utilities/data/DataEnums.hpp
index cbe9d7e9921..68e87d5d486 100644
--- a/src/utilities/data/DataEnums.hpp
+++ b/src/utilities/data/DataEnums.hpp
@@ -192,6 +192,7 @@ OPENSTUDIO_ENUM(EndUseType,
   ((Baseboard))
   ((HeatRecoveryForCooling))
   ((HeatRecoveryForHeating))
+  ((Chillers))
 );
  *  \endcode */
 OPENSTUDIO_ENUM(EndUseType,
@@ -215,6 +216,7 @@ OPENSTUDIO_ENUM(EndUseType,
   ((Baseboard))
   ((HeatRecoveryForCooling))
   ((HeatRecoveryForHeating))
+  ((Chillers))
 );
 
 /** \class BuildingSector


Reviews:
user: login: jmarrec
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-19T09:45:40Z
user: login: joseph-robertson
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-31T19:37:49Z

Review comments:
diff_hunk: @@ -65,6 +67,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Convenience method to return all EnergyManagementSystemMeteredOutputVariable that refer to this Subroutine
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
path: src/model/EnergyManagementSystemSubroutine.hpp
user: login: jmarrec
body: Convenience
created_at: 2025-03-19T09:29:42Z
author_association: COLLABORATOR
diff_hunk: @@ -336,6 +339,12 @@ namespace model {
       }
       return result;
     }
+
+    std::vector<EnergyManagementSystemMeteredOutputVariable>
+      EnergyManagementSystemSubroutine_Impl::energyManagementSystemMeteredOutputVariables() const {
+      return getObject<ModelObject>().getModelObjectSources<EnergyManagementSystemMeteredOutputVariable>(
+        EnergyManagementSystemMeteredOutputVariable::iddObjectType());
+    }
path: src/model/EnergyManagementSystemSubroutine.cpp
user: login: jmarrec
body: Simple
created_at: 2025-03-19T09:29:59Z
author_association: COLLABORATOR
diff_hunk: @@ -65,6 +67,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Convenience method to return all EnergyManagementSystemMeteredOutputVariable that refer to this Program
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
path: src/model/EnergyManagementSystemProgram.hpp
user: login: jmarrec
body: Convenience for Program too
created_at: 2025-03-19T09:30:12Z
author_association: COLLABORATOR
diff_hunk: @@ -147,6 +151,13 @@ namespace model {
     /** @name Other */
     //@{
 
+    // This is not an HVACComponent but it will help in the determination of the PlantUserDefinedComponent, so implementation is here because it's
+    // more logical
+    ComponentType componentType() const;
+    std::vector<FuelType> coolingFuelTypes() const;
+    std::vector<FuelType> heatingFuelTypes() const;
+    std::vector<AppGFuelType> appGHeatingFuelTypes() const;
path: src/model/EnergyManagementSystemMeteredOutputVariable.hpp
user: login: jmarrec
body: New fuel type methods for EMS:MeteredOutputVariable
created_at: 2025-03-19T09:30:41Z
author_association: COLLABORATOR
diff_hunk: @@ -114,6 +119,14 @@ namespace model {
       /** @name Other */
       //@{
 
+      // This is not an HVACComponent but it will help in the determination of the PlantUserDefinedComponent, so implementation is here because it's
+      // more logical
+      ComponentType componentType() const;
+      boost::optional<FuelType> fuelTypeFromResourceType() const;
path: src/model/EnergyManagementSystemMeteredOutputVariable_Impl.hpp
user: login: jmarrec
body: On the Impl side you also have this
created_at: 2025-03-19T09:30:54Z
author_association: COLLABORATOR
diff_hunk: @@ -192,6 +192,7 @@ OPENSTUDIO_ENUM(EndUseType,
   ((Baseboard))
   ((HeatRecoveryForCooling))
   ((HeatRecoveryForHeating))
+  ((Chillers))
path: src/utilities/data/DataEnums.hpp
user: login: jmarrec
body: This wasn't in EndUseType, but it is a valid EndUseType for sure, and is reported as a meter too.

I can't add OnSiteGeneration though, more on this in a bit
created_at: 2025-03-19T09:31:43Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: Check the endUseCategory and try to convert it to an EndUseType. I messed up my comment above, but the reason I can't add "OnSiteGeneration" to the EndUseType OPENSTUDIO_ENUM is that it already has an alias

```
  ((Cogeneration)(Generators))
```

created_at: 2025-03-19T09:33:55Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: ```suggestion
      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", EndUseType already has `((Cogeneration)(Generators))` and
      // there can't be more than 2 aliases
```
created_at: 2025-03-19T09:37:30Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: Mapping these as heating
created_at: 2025-03-19T09:38:14Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: These as cooling
created_at: 2025-03-19T09:38:25Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
+
+      // const std::vector<EndUseType> otherTypes{
+      //   EndUseType::InteriorLights, EndUseType::ExteriorLights, EndUseType::InteriorEquipment, EndUseType::ExteriorEquipment,
+      //   EndUseType::Fans,           EndUseType::Pumps,          EndUseType::HeatRejection,     EndUseType::Humidifier,
+      //   EndUseType::HeatRecovery,   EndUseType::WaterSystems,
+      // };
+      LOG(Info, "For " << briefDescription() << ", unable to determine componentType from End Use Category = '" << end_use_cat_str
+                       << "', assuming ComponentType::None");
+      return ComponentType::None;
+    }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: Rest of them: None
created_at: 2025-03-19T09:38:39Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
+
+      // const std::vector<EndUseType> otherTypes{
+      //   EndUseType::InteriorLights, EndUseType::ExteriorLights, EndUseType::InteriorEquipment, EndUseType::ExteriorEquipment,
+      //   EndUseType::Fans,           EndUseType::Pumps,          EndUseType::HeatRejection,     EndUseType::Humidifier,
+      //   EndUseType::HeatRecovery,   EndUseType::WaterSystems,
+      // };
+      LOG(Info, "For " << briefDescription() << ", unable to determine componentType from End Use Category = '" << end_use_cat_str
+                       << "', assuming ComponentType::None");
+      return ComponentType::None;
+    }
+
+    boost::optional<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::fuelTypeFromResourceType() const {
+      // resourceTypeValues: contains all of the FuelType plus these
+      // ["WaterUse", "OnSiteWaterProduced", "MainsWaterSupply", "RainWaterCollected", "WellWaterDrawn", "CondensateWaterCollected",
+      //  "ElectricityProducedOnSite", "SolarWaterHeating", "SolarAirHeating"]
+      auto const res_type_str = this->resourceType();
+      FuelType ft;
+      try {
+        ft = FuelType(res_type_str);
+      } catch (...) {
+        if (openstudio::istringEqual(res_type_str, "SolarWaterHeating") || openstudio::istringEqual(res_type_str, "SolarAirHeating")
+            || openstudio::istringEqual(res_type_str, "ElectricityProducedOnSite")) {
+          ft = FuelType::Solar;
+        } else {
+          LOG(Info, "For " << briefDescription() << ", unable to determine fuelType From Resource Type = '" << res_type_str << "', assuming None");
+          return boost::none;
+        }
+      }
+      return ft;
+    }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: here is the FuelType determination, handling some extra values.
created_at: 2025-03-19T09:39:13Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
+
+      // const std::vector<EndUseType> otherTypes{
+      //   EndUseType::InteriorLights, EndUseType::ExteriorLights, EndUseType::InteriorEquipment, EndUseType::ExteriorEquipment,
+      //   EndUseType::Fans,           EndUseType::Pumps,          EndUseType::HeatRejection,     EndUseType::Humidifier,
+      //   EndUseType::HeatRecovery,   EndUseType::WaterSystems,
+      // };
+      LOG(Info, "For " << briefDescription() << ", unable to determine componentType from End Use Category = '" << end_use_cat_str
+                       << "', assuming ComponentType::None");
+      return ComponentType::None;
+    }
+
+    boost::optional<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::fuelTypeFromResourceType() const {
+      // resourceTypeValues: contains all of the FuelType plus these
+      // ["WaterUse", "OnSiteWaterProduced", "MainsWaterSupply", "RainWaterCollected", "WellWaterDrawn", "CondensateWaterCollected",
+      //  "ElectricityProducedOnSite", "SolarWaterHeating", "SolarAirHeating"]
+      auto const res_type_str = this->resourceType();
+      FuelType ft;
+      try {
+        ft = FuelType(res_type_str);
+      } catch (...) {
+        if (openstudio::istringEqual(res_type_str, "SolarWaterHeating") || openstudio::istringEqual(res_type_str, "SolarAirHeating")
+            || openstudio::istringEqual(res_type_str, "ElectricityProducedOnSite")) {
+          ft = FuelType::Solar;
+        } else {
+          LOG(Info, "For " << briefDescription() << ", unable to determine fuelType From Resource Type = '" << res_type_str << "', assuming None");
+          return boost::none;
+        }
+      }
+      return ft;
+    }
+
+    std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::coolingFuelTypes() const {
+      std::vector<FuelType> result;
+      if (componentType() == ComponentType::Cooling) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(*ft_);
+        }
+      }
+      return result;
+    }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: If ComponentType is deeemed to be cooling and only then, then grab the FuelType.
created_at: 2025-03-19T09:39:48Z
author_association: COLLABORATOR
diff_hunk: @@ -308,6 +309,96 @@ namespace model {
       return value;
     }
 
+    ComponentType EnergyManagementSystemMeteredOutputVariable_Impl::componentType() const {
+      // endUseCategoryValues: contains all EndUseType but also "OnSiteGeneration", and I can't
+      auto const end_use_cat_str = this->endUseCategory();
+      EndUseType end_use_type;
+      try {
+        end_use_type = EndUseType(end_use_cat_str);
+      } catch (...) {
+
+        if (openstudio::istringEqual(end_use_cat_str, "OnSiteGeneration")) {
+          end_use_type = EndUseType::Cogeneration;
+        } else {
+          return ComponentType::None;
+        }
+      }
+
+      const std::vector<EndUseType> heatingTypes{
+        EndUseType::Heating,   EndUseType::HeatingCoils,           EndUseType::Boilers,
+        EndUseType::Baseboard, EndUseType::HeatRecoveryForHeating, EndUseType::Cogeneration,
+      };
+      if (std::find(heatingTypes.cbegin(), heatingTypes.cend(), end_use_type) != heatingTypes.end()) {
+        return ComponentType::Heating;
+      }
+
+      const std::vector<EndUseType> coolingTypes{
+        EndUseType::Cooling, EndUseType::Refrigeration, EndUseType::HeatRecoveryForCooling, EndUseType::Chillers, EndUseType::CoolingCoils,
+      };
+      if (std::find(coolingTypes.cbegin(), coolingTypes.cend(), end_use_type) != coolingTypes.end()) {
+        return ComponentType::Cooling;
+      }
+
+      // const std::vector<EndUseType> otherTypes{
+      //   EndUseType::InteriorLights, EndUseType::ExteriorLights, EndUseType::InteriorEquipment, EndUseType::ExteriorEquipment,
+      //   EndUseType::Fans,           EndUseType::Pumps,          EndUseType::HeatRejection,     EndUseType::Humidifier,
+      //   EndUseType::HeatRecovery,   EndUseType::WaterSystems,
+      // };
+      LOG(Info, "For " << briefDescription() << ", unable to determine componentType from End Use Category = '" << end_use_cat_str
+                       << "', assuming ComponentType::None");
+      return ComponentType::None;
+    }
+
+    boost::optional<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::fuelTypeFromResourceType() const {
+      // resourceTypeValues: contains all of the FuelType plus these
+      // ["WaterUse", "OnSiteWaterProduced", "MainsWaterSupply", "RainWaterCollected", "WellWaterDrawn", "CondensateWaterCollected",
+      //  "ElectricityProducedOnSite", "SolarWaterHeating", "SolarAirHeating"]
+      auto const res_type_str = this->resourceType();
+      FuelType ft;
+      try {
+        ft = FuelType(res_type_str);
+      } catch (...) {
+        if (openstudio::istringEqual(res_type_str, "SolarWaterHeating") || openstudio::istringEqual(res_type_str, "SolarAirHeating")
+            || openstudio::istringEqual(res_type_str, "ElectricityProducedOnSite")) {
+          ft = FuelType::Solar;
+        } else {
+          LOG(Info, "For " << briefDescription() << ", unable to determine fuelType From Resource Type = '" << res_type_str << "', assuming None");
+          return boost::none;
+        }
+      }
+      return ft;
+    }
+
+    std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::coolingFuelTypes() const {
+      std::vector<FuelType> result;
+      if (componentType() == ComponentType::Cooling) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(*ft_);
+        }
+      }
+      return result;
+    }
+
+    std::vector<FuelType> EnergyManagementSystemMeteredOutputVariable_Impl::heatingFuelTypes() const {
+      std::vector<FuelType> result;
+      if (componentType() == ComponentType::Heating) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(*ft_);
+        }
+      }
+      return result;
+    }
+
+    std::vector<AppGFuelType> EnergyManagementSystemMeteredOutputVariable_Impl::appGHeatingFuelTypes() const {
+      std::vector<AppGFuelType> result;
+      if (componentType() == ComponentType::Heating) {
+        if (auto ft_ = fuelTypeFromResourceType()) {
+          result.emplace_back(convertFuelTypeToAppG(*ft_));
+        }
+      }
+      return result;
+    }
path: src/model/EnergyManagementSystemMeteredOutputVariable.cpp
user: login: jmarrec
body: Same on the heating side
created_at: 2025-03-19T09:40:01Z
author_association: COLLABORATOR
diff_hunk: @@ -79,3 +79,59 @@ TEST_F(ModelFixture, EMSMeteredOutputVariable) {
   ASSERT_TRUE(outvar_sen.emsSensor());
   EXPECT_EQ(outvar_sen.emsVariableObject().get(), outvar_sen.emsSensor().get());
 }
+
+TEST_F(ModelFixture, EMSMeteredOutputVariable_fuelTypes) {
+
+  Model model;
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(model, "MeteredVar");
+
+  EXPECT_TRUE(meteredoutvar.setResourceType("NaturalGas"));
+
+  std::vector<std::pair<std::string, ComponentType>> expectedComponentTypes{
+    {"Heating", ComponentType::Heating},
+    {"HeatingCoils", ComponentType::Heating},
+    {"Boilers", ComponentType::Heating},
+    {"Baseboard", ComponentType::Heating},
+    {"HeatRecoveryForHeating", ComponentType::Heating},
+    {"OnSiteGeneration", ComponentType::Heating},
+    {"Cooling", ComponentType::Cooling},
+    {"Refrigeration", ComponentType::Cooling},
+    {"HeatRecoveryForCooling", ComponentType::Cooling},
+    {"HeatRecoveryForHeating", ComponentType::Heating},
+    {"CoolingCoils", ComponentType::Cooling},
+    {"Chillers", ComponentType::Cooling},
+    {"InteriorLights", ComponentType::None},
+    {"InteriorEquipment", ComponentType::None},
+    {"ExteriorEquipment", ComponentType::None},
+    {"Fans", ComponentType::None},
+    {"Pumps", ComponentType::None},
+    {"HeatRejection", ComponentType::None},
+    {"Humidifier", ComponentType::None},
+    {"HeatRecovery", ComponentType::None},
+    {"WaterSystems", ComponentType::None},
+  };
+  const auto validCats = EnergyManagementSystemMeteredOutputVariable::endUseCategoryValues();
+
+  // Start by asserting that it doesn't throw for no reason
+  {
+    for (const auto& [cat, expectedComponentType] : expectedComponentTypes) {
+      SCOPED_TRACE(cat.data());
+      EXPECT_TRUE(meteredoutvar.setEndUseCategory(cat));
+      EXPECT_NO_THROW(meteredoutvar.componentType());
+      EXPECT_EQ(expectedComponentType, meteredoutvar.componentType()) << "Failed for " << cat;
+      if (expectedComponentType == ComponentType::Heating) {
+        EXPECT_TRUE(meteredoutvar.coolingFuelTypes().empty());
+        testFuelTypeEquality({FuelType::Gas}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({AppGFuelType::Fuel}, meteredoutvar.appGHeatingFuelTypes());
+      } else if (expectedComponentType == ComponentType::Cooling) {
+        testFuelTypeEquality({FuelType::Gas}, meteredoutvar.coolingFuelTypes());
+        testFuelTypeEquality({}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({}, meteredoutvar.appGHeatingFuelTypes());
+      } else {
+        testFuelTypeEquality({}, meteredoutvar.coolingFuelTypes());
+        testFuelTypeEquality({}, meteredoutvar.heatingFuelTypes());
+        testAppGFuelTypeEquality({}, meteredoutvar.appGHeatingFuelTypes());
+      }
+    }
+  }
+}
path: src/model/test/EnergyManagementSystemMeteredOutputVariable_GTest.cpp
user: login: jmarrec
body: The test shows the mapping.
created_at: 2025-03-19T09:40:26Z
author_association: COLLABORATOR
diff_hunk: @@ -156,6 +157,9 @@ namespace model {
     /** @name Other */
     //@{
 
+    // Will look into the MainModelProgram and the PlantSimulationProgram 's linked EMS Metered Output Variables
+    std::vector<EnergyManagementSystemMeteredOutputVariable> energyManagementSystemMeteredOutputVariables() const;
path: src/model/PlantComponentUserDefined.hpp
user: login: jmarrec
body: New convenience for PlantComponentUserDefined too.
created_at: 2025-03-19T09:40:54Z
author_association: COLLABORATOR
diff_hunk: @@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
path: src/model/PlantComponentUserDefined.cpp
user: login: jmarrec
body: I look in the mainModelProgram and the plantSimulationProgram, **NOT** in the plantInitializationProgram.
created_at: 2025-03-19T09:41:33Z
author_association: COLLABORATOR
diff_hunk: @@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
+
     ComponentType PlantComponentUserDefined_Impl::componentType() const {
-      return ComponentType::Both;
+      bool has_heating = false;
+      bool has_cooling = false;
+
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        auto compType = ems_metered_var.componentType();
+        if (compType == ComponentType::Cooling) {
+          has_cooling = true;
+        } else if (compType == ComponentType::Heating) {
+          has_heating = true;
+        } else if (compType == ComponentType::Both) {
+          has_cooling = true;
+          has_heating = true;
+        }
+      }
+
+      if (has_cooling && has_heating) {
+        return ComponentType::Both;
+      } else if (has_cooling) {
+        return ComponentType::Cooling;
+      } else if (has_heating) {
+        return ComponentType::Heating;
+      }
+      return ComponentType::None;
path: src/model/PlantComponentUserDefined.cpp
user: login: jmarrec
body: Alright, so now to determine `PlantComponentUserDefined` componentType, I "just" have to look into the attached EMS MeteredOutputVariables.
created_at: 2025-03-19T09:42:24Z
author_association: COLLABORATOR
diff_hunk: @@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
+
     ComponentType PlantComponentUserDefined_Impl::componentType() const {
-      return ComponentType::Both;
+      bool has_heating = false;
+      bool has_cooling = false;
+
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        auto compType = ems_metered_var.componentType();
+        if (compType == ComponentType::Cooling) {
+          has_cooling = true;
+        } else if (compType == ComponentType::Heating) {
+          has_heating = true;
+        } else if (compType == ComponentType::Both) {
+          has_cooling = true;
+          has_heating = true;
+        }
+      }
+
+      if (has_cooling && has_heating) {
+        return ComponentType::Both;
+      } else if (has_cooling) {
+        return ComponentType::Cooling;
+      } else if (has_heating) {
+        return ComponentType::Heating;
+      }
+      return ComponentType::None;
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::coolingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.coolingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
path: src/model/PlantComponentUserDefined.cpp
user: login: jmarrec
body: Same for coolingFuelTypes
created_at: 2025-03-19T09:42:42Z
author_association: COLLABORATOR
diff_hunk: @@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
+
     ComponentType PlantComponentUserDefined_Impl::componentType() const {
-      return ComponentType::Both;
+      bool has_heating = false;
+      bool has_cooling = false;
+
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        auto compType = ems_metered_var.componentType();
+        if (compType == ComponentType::Cooling) {
+          has_cooling = true;
+        } else if (compType == ComponentType::Heating) {
+          has_heating = true;
+        } else if (compType == ComponentType::Both) {
+          has_cooling = true;
+          has_heating = true;
+        }
+      }
+
+      if (has_cooling && has_heating) {
+        return ComponentType::Both;
+      } else if (has_cooling) {
+        return ComponentType::Cooling;
+      } else if (has_heating) {
+        return ComponentType::Heating;
+      }
+      return ComponentType::None;
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::coolingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.coolingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::heatingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.heatingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
path: src/model/PlantComponentUserDefined.cpp
user: login: jmarrec
body: heatingFuelTypes
created_at: 2025-03-19T09:42:51Z
author_association: COLLABORATOR
diff_hunk: @@ -498,20 +501,71 @@ namespace model {
       return simProgram;
     }
 
+    std::vector<EnergyManagementSystemMeteredOutputVariable> PlantComponentUserDefined_Impl::energyManagementSystemMeteredOutputVariables() const {
+      std::vector<EnergyManagementSystemMeteredOutputVariable> result;
+      if (auto prgm_ = mainModelProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      if (auto prgm_ = plantSimulationProgram()) {
+        openstudio::detail::concat_helper(result, prgm_->energyManagementSystemMeteredOutputVariables());
+      }
+      return result;
+    }
+
     ComponentType PlantComponentUserDefined_Impl::componentType() const {
-      return ComponentType::Both;
+      bool has_heating = false;
+      bool has_cooling = false;
+
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        auto compType = ems_metered_var.componentType();
+        if (compType == ComponentType::Cooling) {
+          has_cooling = true;
+        } else if (compType == ComponentType::Heating) {
+          has_heating = true;
+        } else if (compType == ComponentType::Both) {
+          has_cooling = true;
+          has_heating = true;
+        }
+      }
+
+      if (has_cooling && has_heating) {
+        return ComponentType::Both;
+      } else if (has_cooling) {
+        return ComponentType::Cooling;
+      } else if (has_heating) {
+        return ComponentType::Heating;
+      }
+      return ComponentType::None;
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::coolingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.coolingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
     std::vector<FuelType> PlantComponentUserDefined_Impl::heatingFuelTypes() const {
-      return {FuelType::OtherFuel_1};
+      std::set<FuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.heatingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
     }
 
     std::vector<AppGFuelType> PlantComponentUserDefined_Impl::appGHeatingFuelTypes() const {
-      return {AppGFuelType::Other};
+      std::set<AppGFuelType> result;
+      for (const auto& ems_metered_var : energyManagementSystemMeteredOutputVariables()) {
+        for (auto& ft : ems_metered_var.appGHeatingFuelTypes()) {
+          result.insert(ft);
+        }
+      }
+      return {result.begin(), result.end()};
path: src/model/PlantComponentUserDefined.cpp
user: login: jmarrec
body: appGHeatingFuelTypes
created_at: 2025-03-19T09:42:56Z
author_association: COLLABORATOR
diff_hunk: @@ -211,3 +212,68 @@ TEST_F(ModelFixture, PlantComponentUserDefined_constructor) {
   EXPECT_TRUE(b1.designVolumeFlowRateActuator().get().actuatedComponent());
   EXPECT_EQ(b1.handle(), b1.designVolumeFlowRateActuator().get().actuatedComponent().get().handle());
 }
+
+TEST_F(ModelFixture, PlantComponentUserDefined_EMSProgram_emsMeteredOutputVariables) {
+
+  Model m;
+  PlantComponentUserDefined plant_comp(m);
+  plant_comp.setPlantLoadingMode("MeetsLoadWithNominalCapacityHiOutLimit");
+  plant_comp.setPlantLoopFlowRequestMode("NeedsFlowIfLoopIsOn");
+
+  EXPECT_EQ(ComponentType(ComponentType::None), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant_comp.appGHeatingFuelTypes());
path: src/model/test/PlantComponentUserDefined_GTest.cpp
user: login: jmarrec
body: PlantComponentUserDefined has CompType::None until it has EMS MeteredOutputVariables
created_at: 2025-03-19T09:43:44Z
author_association: COLLABORATOR
diff_hunk: @@ -211,3 +212,68 @@ TEST_F(ModelFixture, PlantComponentUserDefined_constructor) {
   EXPECT_TRUE(b1.designVolumeFlowRateActuator().get().actuatedComponent());
   EXPECT_EQ(b1.handle(), b1.designVolumeFlowRateActuator().get().actuatedComponent().get().handle());
 }
+
+TEST_F(ModelFixture, PlantComponentUserDefined_EMSProgram_emsMeteredOutputVariables) {
+
+  Model m;
+  PlantComponentUserDefined plant_comp(m);
+  plant_comp.setPlantLoadingMode("MeetsLoadWithNominalCapacityHiOutLimit");
+  plant_comp.setPlantLoopFlowRequestMode("NeedsFlowIfLoopIsOn");
+
+  EXPECT_EQ(ComponentType(ComponentType::None), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant_comp.appGHeatingFuelTypes());
+
+  EXPECT_EQ(0, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  auto sim_pgrm = plant_comp.plantSimulationProgram().get();
+  sim_pgrm.setName("Sim_Pgrm");
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0");
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(m, "Plant Heating Comp Electricity Consumption");
+  EXPECT_TRUE(meteredoutvar.setEMSVariableName("Elec_Htg_Cons"));
+  EXPECT_TRUE(meteredoutvar.setUpdateFrequency("SystemTimestep"));
+  EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(sim_pgrm));
+  EXPECT_TRUE(meteredoutvar.setResourceType("Electricity"));
+  EXPECT_TRUE(meteredoutvar.setGroupType("HVAC"));
+  EXPECT_TRUE(meteredoutvar.setEndUseCategory("Heating"));
+  EXPECT_TRUE(meteredoutvar.setEndUseSubcategory(""));
+  EXPECT_TRUE(meteredoutvar.setUnits("J"));
+  EXPECT_EQ(1, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({AppGFuelType::Electric}, meteredoutvar.appGHeatingFuelTypes());
path: src/model/test/PlantComponentUserDefined_GTest.cpp
user: login: jmarrec
body: I create an EMSMeteredOutputVar of type "Heating" and "Electricity" into the plantSimulationProgram, and check that the PlantComponentUserDefined has the right fuel types
created_at: 2025-03-19T09:44:27Z
author_association: COLLABORATOR
diff_hunk: @@ -211,3 +212,68 @@ TEST_F(ModelFixture, PlantComponentUserDefined_constructor) {
   EXPECT_TRUE(b1.designVolumeFlowRateActuator().get().actuatedComponent());
   EXPECT_EQ(b1.handle(), b1.designVolumeFlowRateActuator().get().actuatedComponent().get().handle());
 }
+
+TEST_F(ModelFixture, PlantComponentUserDefined_EMSProgram_emsMeteredOutputVariables) {
+
+  Model m;
+  PlantComponentUserDefined plant_comp(m);
+  plant_comp.setPlantLoadingMode("MeetsLoadWithNominalCapacityHiOutLimit");
+  plant_comp.setPlantLoopFlowRequestMode("NeedsFlowIfLoopIsOn");
+
+  EXPECT_EQ(ComponentType(ComponentType::None), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant_comp.appGHeatingFuelTypes());
+
+  EXPECT_EQ(0, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  auto sim_pgrm = plant_comp.plantSimulationProgram().get();
+  sim_pgrm.setName("Sim_Pgrm");
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0");
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(m, "Plant Heating Comp Electricity Consumption");
+  EXPECT_TRUE(meteredoutvar.setEMSVariableName("Elec_Htg_Cons"));
+  EXPECT_TRUE(meteredoutvar.setUpdateFrequency("SystemTimestep"));
+  EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(sim_pgrm));
+  EXPECT_TRUE(meteredoutvar.setResourceType("Electricity"));
+  EXPECT_TRUE(meteredoutvar.setGroupType("HVAC"));
+  EXPECT_TRUE(meteredoutvar.setEndUseCategory("Heating"));
+  EXPECT_TRUE(meteredoutvar.setEndUseSubcategory(""));
+  EXPECT_TRUE(meteredoutvar.setUnits("J"));
+  EXPECT_EQ(1, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({AppGFuelType::Electric}, meteredoutvar.appGHeatingFuelTypes());
+
+  PlantLoop plant(m);
+  EXPECT_EQ(ComponentType(ComponentType::None), plant.componentType());
+  testFuelTypeEquality({}, plant.coolingFuelTypes());
+  testFuelTypeEquality({}, plant.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant.appGHeatingFuelTypes());
+
+  EXPECT_TRUE(plant.addSupplyBranchForComponent(plant_comp));
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant.componentType());
+  testFuelTypeEquality({}, plant.coolingFuelTypes());
+  testFuelTypeEquality({FuelType::Electricity}, plant.heatingFuelTypes());
+  testAppGFuelTypeEquality({AppGFuelType::Electric}, plant.appGHeatingFuelTypes());
path: src/model/test/PlantComponentUserDefined_GTest.cpp
user: login: jmarrec
body: Plant inherits it correctly AFTER I assign the PlantComponentUserDefined on the supply side
created_at: 2025-03-19T09:45:14Z
author_association: COLLABORATOR
diff_hunk: @@ -211,3 +212,68 @@ TEST_F(ModelFixture, PlantComponentUserDefined_constructor) {
   EXPECT_TRUE(b1.designVolumeFlowRateActuator().get().actuatedComponent());
   EXPECT_EQ(b1.handle(), b1.designVolumeFlowRateActuator().get().actuatedComponent().get().handle());
 }
+
+TEST_F(ModelFixture, PlantComponentUserDefined_EMSProgram_emsMeteredOutputVariables) {
+
+  Model m;
+  PlantComponentUserDefined plant_comp(m);
+  plant_comp.setPlantLoadingMode("MeetsLoadWithNominalCapacityHiOutLimit");
+  plant_comp.setPlantLoopFlowRequestMode("NeedsFlowIfLoopIsOn");
+
+  EXPECT_EQ(ComponentType(ComponentType::None), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant_comp.appGHeatingFuelTypes());
+
+  EXPECT_EQ(0, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  auto sim_pgrm = plant_comp.plantSimulationProgram().get();
+  sim_pgrm.setName("Sim_Pgrm");
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0");
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar(m, "Plant Heating Comp Electricity Consumption");
+  EXPECT_TRUE(meteredoutvar.setEMSVariableName("Elec_Htg_Cons"));
+  EXPECT_TRUE(meteredoutvar.setUpdateFrequency("SystemTimestep"));
+  EXPECT_TRUE(meteredoutvar.setEMSProgramOrSubroutineName(sim_pgrm));
+  EXPECT_TRUE(meteredoutvar.setResourceType("Electricity"));
+  EXPECT_TRUE(meteredoutvar.setGroupType("HVAC"));
+  EXPECT_TRUE(meteredoutvar.setEndUseCategory("Heating"));
+  EXPECT_TRUE(meteredoutvar.setEndUseSubcategory(""));
+  EXPECT_TRUE(meteredoutvar.setUnits("J"));
+  EXPECT_EQ(1, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant_comp.componentType());
+  testFuelTypeEquality({}, plant_comp.coolingFuelTypes());
+  testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes());
+  testAppGFuelTypeEquality({AppGFuelType::Electric}, meteredoutvar.appGHeatingFuelTypes());
+
+  PlantLoop plant(m);
+  EXPECT_EQ(ComponentType(ComponentType::None), plant.componentType());
+  testFuelTypeEquality({}, plant.coolingFuelTypes());
+  testFuelTypeEquality({}, plant.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, plant.appGHeatingFuelTypes());
+
+  EXPECT_TRUE(plant.addSupplyBranchForComponent(plant_comp));
+  EXPECT_EQ(ComponentType(ComponentType::Heating), plant.componentType());
+  testFuelTypeEquality({}, plant.coolingFuelTypes());
+  testFuelTypeEquality({FuelType::Electricity}, plant.heatingFuelTypes());
+  testAppGFuelTypeEquality({AppGFuelType::Electric}, plant.appGHeatingFuelTypes());
+
+  sim_pgrm.setBody("SET Elec_Htg_Cons = 0\nSET Elec_Clg_Cons = 0");
+  EnergyManagementSystemMeteredOutputVariable meteredoutvar2(m, "Plant Cooling Comp Electricity Consumption");
+  EXPECT_TRUE(meteredoutvar2.setEMSVariableName("Elec_Htg_Cons"));
+  EXPECT_TRUE(meteredoutvar2.setUpdateFrequency("SystemTimestep"));
+  EXPECT_TRUE(meteredoutvar2.setEMSProgramOrSubroutineName(sim_pgrm));
+  EXPECT_TRUE(meteredoutvar2.setResourceType("NaturalGas"));
+  EXPECT_TRUE(meteredoutvar2.setGroupType("HVAC"));
+  EXPECT_TRUE(meteredoutvar2.setEndUseCategory("Chillers"));
+  EXPECT_TRUE(meteredoutvar2.setEndUseSubcategory(""));
+  EXPECT_TRUE(meteredoutvar2.setUnits("J"));
+  EXPECT_EQ(2, plant_comp.energyManagementSystemMeteredOutputVariables().size());
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant_comp.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant_comp.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant_comp.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({}, meteredoutvar2.appGHeatingFuelTypes()));
+
+  EXPECT_EQ(ComponentType(ComponentType::Both), plant.componentType());
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Gas}, plant.coolingFuelTypes()));
+  EXPECT_TRUE(testFuelTypeEquality({FuelType::Electricity}, plant.heatingFuelTypes()));
+  EXPECT_TRUE(testAppGFuelTypeEquality({AppGFuelType::Electric}, plant.appGHeatingFuelTypes()));
path: src/model/test/PlantComponentUserDefined_GTest.cpp
user: login: jmarrec
body: Test with a second variable, this type "Chillers" with "Natural Gas"
created_at: 2025-03-19T09:45:35Z
author_association: COLLABORATOR

PR comments:
user: login: ci-commercialbuildings
created_at: 2025-03-19T10:56:21Z
body: CI Results for b2461a87967cd7fd28f3d60d0079c92dc7c22b42:

* Ubuntu 24.04:
    * [OpenStudio-3.10.0-alpha+958020ef33-Ubuntu-24.04-x86_64.deb](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha%2B958020ef33-Ubuntu-24.04-x86_64.deb)
    * [OpenStudio-3.10.0-alpha+958020ef33-Ubuntu-24.04-x86_64.tar.gz](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha%2B958020ef33-Ubuntu-24.04-x86_64.tar.gz)

* Ubuntu 22.04:
    * [OpenStudio-3.10.0-alpha+958020ef33-Ubuntu-22.04-x86_64.deb](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha%2B958020ef33-Ubuntu-22.04-x86_64.deb)
    * [OpenStudio-3.10.0-alpha+958020ef33-Ubuntu-22.04-x86_64.tar.gz](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha%2B958020ef33-Ubuntu-22.04-x86_64.tar.gz)
    * [perf_test/results.html](https://openstudio-ci-builds.s3-us-west-2.amazonaws.com/PR-5372/b2461a87967cd7fd28f3d60d0079c92dc7c22b42/perf_test/results.html)

* Darwin:
    * [OpenStudio-3.10.0-alpha+b2461a8796-Darwin-x86_64.dmg](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha+b2461a8796-Darwin-x86_64.dmg)
    * [OpenStudio-3.10.0-alpha+b2461a8796-Darwin-x86_64.tar.gz](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha+b2461a8796-Darwin-x86_64.tar.gz)

* Windows:
    * [OpenStudio-3.10.0-alpha+b2461a8796-Windows.exe](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha+b2461a8796-Windows.exe)
    * [OpenStudio-3.10.0-alpha+b2461a8796-Windows.tar.gz](http://openstudio-ci-builds.s3-website-us-west-2.amazonaws.com/PR-5372/OpenStudio-3.10.0-alpha+b2461a8796-Windows.tar.gz)

* Code Coverage:
author_association: COLLABORATOR