Repository: NREL/OpenStudio (language: python)

Issue:
`setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setter for `PipeIndoor` do not work
Issue overview
--------------
The `PipeIndoor`'s `setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setters don't appear to work. I'm getting the following error when trying to use them: `[openstudio.model.ScheduleTypeRegistry] <2> No 'Ambient Temperature Schedule' Schedule is registered for class PipeIndoor.`.

## Steps to Reproduce
```
model = OpenStudio::Model::Model.new
pipe = OpenStudio::Model::PipeIndoor.new(model)
pipe.setAmbientTemperatureSchedule(model.alwaysOnDiscreteSchedule)
```

### Environment
 - Platform: Windows 11
 - Version of OpenStudio: 3.6

### Context
I'm using the `setPointer` workaround but it would be nice if if the setters could work.
`setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setter for `PipeIndoor` do not work
Issue overview
--------------
The `PipeIndoor`'s `setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setters don't appear to work. I'm getting the following error when trying to use them: `[openstudio.model.ScheduleTypeRegistry] <2> No 'Ambient Temperature Schedule' Schedule is registered for class PipeIndoor.`.

## Steps to Reproduce
```
model = OpenStudio::Model::Model.new
pipe = OpenStudio::Model::PipeIndoor.new(model)
pipe.setAmbientTemperatureSchedule(model.alwaysOnDiscreteSchedule)
```

### Environment
 - Platform: Windows 11
 - Version of OpenStudio: 3.6

### Context
I'm using the `setPointer` workaround but it would be nice if if the setters could work.
`setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setter for `PipeIndoor` do not work
Issue overview
--------------
The `PipeIndoor`'s `setAmbientTemperatureSchedule` and `setAmbientAirVelocitySchedule` setters don't appear to work. I'm getting the following error when trying to use them: `[openstudio.model.ScheduleTypeRegistry] <2> No 'Ambient Temperature Schedule' Schedule is registered for class PipeIndoor.`.

## Steps to Reproduce
```
model = OpenStudio::Model::Model.new
pipe = OpenStudio::Model::PipeIndoor.new(model)
pipe.setAmbientTemperatureSchedule(model.alwaysOnDiscreteSchedule)
```

### Environment
 - Platform: Windows 11
 - Version of OpenStudio: 3.6

### Context
I'm using the `setPointer` workaround but it would be nice if if the setters could work.


Discussion / hints:

Fix patch:
diff --git a/src/energyplus/ForwardTranslator/ForwardTranslatePipeIndoor.cpp b/src/energyplus/ForwardTranslator/ForwardTranslatePipeIndoor.cpp
index da2a2381da7..5669db1d38d 100644
--- a/src/energyplus/ForwardTranslator/ForwardTranslatePipeIndoor.cpp
+++ b/src/energyplus/ForwardTranslator/ForwardTranslatePipeIndoor.cpp
@@ -47,38 +47,33 @@ namespace openstudio {
 namespace energyplus {
 
   boost::optional<IdfObject> ForwardTranslator::translatePipeIndoor(PipeIndoor& modelObject) {
-    IdfObject idfObject(openstudio::IddObjectType::Pipe_Indoor);
 
-    m_idfObjects.push_back(idfObject);
+    IdfObject idfObject = createRegisterAndNameIdfObject(openstudio::IddObjectType::Pipe_Indoor, modelObject);
 
-    if (auto value = modelObject.name()) {
-      idfObject.setName(value.get());
+    if (auto node_ = modelObject.inletModelObject()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::FluidInletNodeName, node_->nameString());
     }
 
-    if (auto node = modelObject.inletModelObject()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::FluidInletNodeName, node->name().get());
+    if (auto node_ = modelObject.outletModelObject()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::FluidOutletNodeName, node_->nameString());
     }
 
-    if (auto node = modelObject.outletModelObject()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::FluidOutletNodeName, node->name().get());
-    }
-
-    if (auto construction = modelObject.construction()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::ConstructionName, construction->name().get());
+    if (auto construction_ = modelObject.construction()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::ConstructionName, construction_->nameString());
     }
 
     idfObject.setString(openstudio::Pipe_IndoorFields::EnvironmentType, modelObject.environmentType());
 
-    if (auto zone = modelObject.ambientTemperatureZone()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientTemperatureZoneName, zone->name().get());
+    if (auto zone_ = modelObject.ambientTemperatureZone()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientTemperatureZoneName, zone_->nameString());
     }
 
-    if (auto schedule = modelObject.ambientTemperatureSchedule()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientTemperatureScheduleName, schedule->name().get());
+    if (auto schedule_ = modelObject.ambientTemperatureSchedule()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientTemperatureScheduleName, schedule_->nameString());
     }
 
-    if (auto schedule = modelObject.ambientAirVelocitySchedule()) {
-      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientAirVelocityScheduleName, schedule->name().get());
+    if (auto schedule_ = modelObject.ambientAirVelocitySchedule()) {
+      idfObject.setString(openstudio::Pipe_IndoorFields::AmbientAirVelocityScheduleName, schedule_->nameString());
     }
 
     idfObject.setDouble(openstudio::Pipe_IndoorFields::PipeInsideDiameter, modelObject.pipeInsideDiameter());
diff --git a/src/model/PipeIndoor.cpp b/src/model/PipeIndoor.cpp
index 191b51280b2..08d5bdf9110 100644
--- a/src/model/PipeIndoor.cpp
+++ b/src/model/PipeIndoor.cpp
@@ -87,14 +87,14 @@ namespace model {
 
     std::vector<ScheduleTypeKey> PipeIndoor_Impl::getScheduleTypeKeys(const Schedule& schedule) const {
       std::vector<ScheduleTypeKey> result;
-      UnsignedVector fieldIndices = getSourceIndices(schedule.handle());
-      UnsignedVector::const_iterator b(fieldIndices.begin());
-      UnsignedVector::const_iterator e(fieldIndices.end());
+      const UnsignedVector fieldIndices = getSourceIndices(schedule.handle());
+      const auto b = fieldIndices.cbegin();
+      const auto e = fieldIndices.cend();
       if (std::find(b, e, OS_Pipe_IndoorFields::AmbientTemperatureSchedule) != e) {
-        result.push_back(ScheduleTypeKey("PipeIndoor", "Ambient Temperature Schedule"));
+        result.emplace_back("PipeIndoor", "Ambient Temperature Schedule");
       }
       if (std::find(b, e, OS_Pipe_IndoorFields::AmbientAirVelocitySchedule) != e) {
-        result.push_back(ScheduleTypeKey("PipeIndoor", "Ambient Air Velocity Schedule"));
+        result.emplace_back("PipeIndoor", "Ambient Air Velocity Schedule");
       }
       return result;
     }
diff --git a/src/model/ScheduleTypeRegistry.cpp b/src/model/ScheduleTypeRegistry.cpp
index f88d1568d5f..450dca8f2e7 100644
--- a/src/model/ScheduleTypeRegistry.cpp
+++ b/src/model/ScheduleTypeRegistry.cpp
@@ -340,8 +340,8 @@ namespace model {
       {"People", "Ankle Level Air Velocity", "ankleLevelAirVelocitySchedule", true, "Velocity", 0.0, OptionalDouble()},
       {"PhotovoltaicPerformanceSimple", "Efficiency", "efficiencySchedule", true, "", 0.0, 1.0},
       {"PlantComponentTemperatureSource", "Source Temperature", "sourceTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()},
-      {"PipeIndoor", "Ambient Temperature", "ambientTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()},
-      {"PipeIndoor", "Ambient Air Velocity", "ambientAirVelocitySchedule", true, "Velocity", 0.0, OptionalDouble()},
+      {"PipeIndoor", "Ambient Temperature Schedule", "ambientTemperatureSchedule", true, "Temperature", OptionalDouble(), OptionalDouble()},
+      {"PipeIndoor", "Ambient Air Velocity Schedule", "ambientAirVelocitySchedule", true, "Velocity", 0.0, OptionalDouble()},
       {"PlantLoop", "Plant Equipment Operation Heating Load Schedule", "plantEquipmentOperationHeatingLoadSchedule", false, "Availability", 0.0, 1.0},
       {"PlantLoop", "Plant Equipment Operation Cooling Load Schedule", "plantEquipmentOperationCoolingLoadSchedule", false, "Availability", 0.0, 1.0},
       {"PlantLoop", "Primary Plant Equipment Operation Scheme Schedule", "primaryPlantEquipmentOperationSchemeSchedule", false, "Availability", 0.0,


Test patch:
diff --git a/src/model/test/PipeIndoor_GTest.cpp b/src/model/test/PipeIndoor_GTest.cpp
index f87ef281e31..957c2281848 100644
--- a/src/model/test/PipeIndoor_GTest.cpp
+++ b/src/model/test/PipeIndoor_GTest.cpp
@@ -27,25 +27,130 @@
 *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ***********************************************************************************************************************/
 
-#include <gtest/gtest.h>
-
 #include "ModelFixture.hpp"
 
 #include "../PipeIndoor.hpp"
 #include "../PipeIndoor_Impl.hpp"
 
+#include "../AirLoopHVACZoneSplitter.hpp"
+#include "../Construction.hpp"
+#include "../Node.hpp"
+#include "../Node_Impl.hpp"
+#include "../PlantLoop.hpp"
+#include "../StandardOpaqueMaterial.hpp"
+#include "../Schedule.hpp"
+#include "../ScheduleConstant.hpp"
+#include "../ThermalZone.hpp"
+
 using namespace openstudio;
 using namespace openstudio::model;
 
-TEST_F(ModelFixture, PipeIndoor_PipeIndoor) {
-  ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+TEST_F(ModelFixture, PipeIndoor_GettersSetters) {
+  Model m;
+
+  PipeIndoor pipeIndoor(m);
+  pipeIndoor.setName("My PipeIndoor");
+
+  // Construction: Optional Object
+  StandardOpaqueMaterial mat(m, "Smooth", 3.00E-03, 45.31, 7833.0, 500.0);
+  EXPECT_TRUE(mat.setThermalAbsorptance(0.9));
+  EXPECT_TRUE(mat.setSolarAbsorptance(0.5));
+  EXPECT_TRUE(mat.setVisibleAbsorptance(0.5));
+  Construction c(m);
+  EXPECT_TRUE(c.insertLayer(0, mat));
+  EXPECT_TRUE(pipeIndoor.setConstruction(c));
+  ASSERT_TRUE(pipeIndoor.construction());
+  EXPECT_EQ(c, pipeIndoor.construction().get());
+
+  // Environment Type: Required String
+  EXPECT_TRUE(pipeIndoor.setEnvironmentType("Zone"));
+  EXPECT_EQ("Zone", pipeIndoor.environmentType());
+  // Bad Value
+  EXPECT_FALSE(pipeIndoor.setEnvironmentType("BADENUM"));
+  EXPECT_EQ("Zone", pipeIndoor.environmentType());
+
+  // Ambient Temperature Zone: Optional Object
+  ThermalZone ambientTemperatureZone(m);
+  EXPECT_TRUE(pipeIndoor.setAmbientTemperatureZone(ambientTemperatureZone));
+  ASSERT_TRUE(pipeIndoor.ambientTemperatureZone());
+  EXPECT_EQ(ambientTemperatureZone, pipeIndoor.ambientTemperatureZone().get());
+
+  // Ambient Temperature Schedule: Optional Object
+  ScheduleConstant ambientTemperatureSchedule(m);
+  EXPECT_TRUE(pipeIndoor.setAmbientTemperatureSchedule(ambientTemperatureSchedule));
+  ASSERT_TRUE(pipeIndoor.ambientTemperatureSchedule());
+  EXPECT_EQ(ambientTemperatureSchedule, pipeIndoor.ambientTemperatureSchedule().get());
+
+  // Ambient Air Velocity Schedule: Optional Object
+  ScheduleConstant ambientAirVelocitySchedule(m);
+  EXPECT_TRUE(pipeIndoor.setAmbientAirVelocitySchedule(ambientAirVelocitySchedule));
+  ASSERT_TRUE(pipeIndoor.ambientAirVelocitySchedule());
+  EXPECT_EQ(ambientAirVelocitySchedule, pipeIndoor.ambientAirVelocitySchedule().get());
+
+  // Pipe Inside Diameter: Required Double
+  EXPECT_TRUE(pipeIndoor.setPipeInsideDiameter(1.0));
+  EXPECT_EQ(1.0, pipeIndoor.pipeInsideDiameter());
+  // Bad Value
+  EXPECT_FALSE(pipeIndoor.setPipeInsideDiameter(-10.0));
+  EXPECT_EQ(1.0, pipeIndoor.pipeInsideDiameter());
+
+  // Pipe Length: Required Double
+  EXPECT_TRUE(pipeIndoor.setPipeLength(50.0));
+  EXPECT_EQ(50.0, pipeIndoor.pipeLength());
+  // Bad Value
+  EXPECT_FALSE(pipeIndoor.setPipeLength(-10.0));
+  EXPECT_EQ(50.0, pipeIndoor.pipeLength());
+}
+
+TEST_F(ModelFixture, PipeIndoor_addToNode) {
+  Model m;
+  PipeIndoor pipeIndoor(m);
+
+  AirLoopHVAC airLoop(m);
+
+  Node supplyOutletNode = airLoop.supplyOutletNode();
+
+  EXPECT_FALSE(pipeIndoor.addToNode(supplyOutletNode));
+  EXPECT_EQ((unsigned)2, airLoop.supplyComponents().size());
+
+  Node inletNode = airLoop.zoneSplitter().lastOutletModelObject()->cast<Node>();
+
+  EXPECT_FALSE(pipeIndoor.addToNode(inletNode));
+  EXPECT_EQ((unsigned)5, airLoop.demandComponents().size());
+
+  PlantLoop plantLoop(m);
+  supplyOutletNode = plantLoop.supplyOutletNode();
+  EXPECT_TRUE(pipeIndoor.addToNode(supplyOutletNode));
+  EXPECT_EQ((unsigned)7, plantLoop.supplyComponents().size());
+
+  Node demandOutletNode = plantLoop.demandOutletNode();
+  EXPECT_TRUE(pipeIndoor.addToNode(demandOutletNode));
+  EXPECT_EQ((unsigned)7, plantLoop.demandComponents().size());
+
+  PipeIndoor pipeIndoor2(m);
+
+  EXPECT_TRUE(pipeIndoor2.addToNode(demandOutletNode));
+  EXPECT_EQ((unsigned)9, plantLoop.demandComponents().size());
+
+  PlantLoop plantLoop2(m);
+  demandOutletNode = plantLoop2.demandOutletNode();
+  EXPECT_TRUE(pipeIndoor.addToNode(demandOutletNode));
+  EXPECT_EQ((unsigned)7, plantLoop2.demandComponents().size());
+
+  auto pipeIndoorClone = pipeIndoor.clone(m).cast<PipeIndoor>();
+  supplyOutletNode = plantLoop.supplyOutletNode();
+
+  EXPECT_TRUE(pipeIndoorClone.addToNode(supplyOutletNode));
+  EXPECT_EQ((unsigned)7, plantLoop.supplyComponents().size());
+}
+
+TEST_F(ModelFixture, PipeIndoor_HeatCoolFuelTypes) {
+  Model m;
 
-  ASSERT_EXIT(
-    {
-      Model m;
-      PipeIndoor testObject(m);
+  PipeIndoor pipeIndoor(m);
 
-      exit(0);
-    },
-    ::testing::ExitedWithCode(0), "");
+  EXPECT_EQ(ComponentType(ComponentType::None), pipeIndoor.componentType());
+  testFuelTypeEquality({}, pipeIndoor.coolingFuelTypes());
+  testFuelTypeEquality({}, pipeIndoor.heatingFuelTypes());
+  testAppGFuelTypeEquality({}, pipeIndoor.appGHeatingFuelTypes());
 }