Repository: NREL/OpenStudio (language: python)

Issue:
SimulationControl choices not translating to E+
Looks like the choices from SimulationControl aren't being translated to E+ correctly (ref: [UnmetHours](https://unmethours.com/question/3054/disable-zoneplantsystem-sizing/)).

![openstudio_issue1472-1b](https://cloud.githubusercontent.com/assets/8397034/6237638/c01c1100-b6b1-11e4-836d-2dd69e6a9621.png)
![openstudio_issue1472-2b](https://cloud.githubusercontent.com/assets/8397034/6237647/d1826458-b6b1-11e4-9f50-08fc99de6122.png)

![openstudio_issue1p](https://cloud.githubusercontent.com/assets/8397034/6237174/d1a41728-b6ad-11e4-89e2-171a72827a31.png)
![openstudio_issue2p](https://cloud.githubusercontent.com/assets/8397034/6237177/daae25b6-b6ad-11e4-876d-4f753031592f.png)

SimulationControl choices not translating to E+
Looks like the choices from SimulationControl aren't being translated to E+ correctly (ref: [UnmetHours](https://unmethours.com/question/3054/disable-zoneplantsystem-sizing/)).

![openstudio_issue1472-1b](https://cloud.githubusercontent.com/assets/8397034/6237638/c01c1100-b6b1-11e4-836d-2dd69e6a9621.png)
![openstudio_issue1472-2b](https://cloud.githubusercontent.com/assets/8397034/6237647/d1826458-b6b1-11e4-9f50-08fc99de6122.png)

![openstudio_issue1p](https://cloud.githubusercontent.com/assets/8397034/6237174/d1a41728-b6ad-11e4-89e2-171a72827a31.png)
![openstudio_issue2p](https://cloud.githubusercontent.com/assets/8397034/6237177/daae25b6-b6ad-11e4-876d-4f753031592f.png)



Discussion / hints:
@MatthewSteen Well there are some overrides in the translator to make sure simulations don't crash.  This is a great example of OpenStudio trying to thread a needle between usability and full EnergyPlus power.  Did we miss the mark?

https://github.com/NREL/OpenStudio/blob/develop/openstudiocore/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp#L59

@MatthewSteen maybe I should be more explicit. If you said no to zone sizing (and there are zones and design days) your simulation would fail anyway.  It has been a while since I have been in these dark corners, but I'm pretty sure it has to do with how we define ventilation.  Maybe other things too. 

OK.  I reviewed the Unmet Hours post.  I see LSurf points has a valid scenario where zone sizing could be off and OpenStudio is not allowing it.  Seems like a valid complaint.  Maybe that override logic should just go away.  OS default zone sizing on, allow it to be turned off manually and respect that choice potential E+ failure or not.

https://github.com/NREL/OpenStudio/blob/c018cb6316372cb77b33a4f62f22452908320bc9/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp#L63-L87

@kbenne how about:

```diff
    if (modelObject.doZoneSizingCalculation()) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
-   } else if ((numSizingPeriods > 0) && (modelObject.model().getConcreteModelObjects<ThermalZone>().size() > 0)) {
+   } else if (modelObject.isDoZoneSizingCalculationDefaulted() &&
                 (numSizingPeriods > 0) && (modelObject.model().getConcreteModelObjects<ThermalZone>().size() > 0)) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
    } else {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "No");
    }
```
Or maybe even better:

```c++
    #include <algorithm>

    auto hasZoneEquipment = [](const Model& m) {
      auto zones = m.getConcreteModelObjects<ThermalZone>();
      return std::any_of(zones.cbegin(), zones.cend(), [](const auto& z){ return !z.equipment().empty(); });
    };

    if (modelObject.doZoneSizingCalculation()) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
    } else if ((numSizingPeriods > 0) && hasZoneEquipment(modelObject.model())) {
      if (modelObject.isDoZoneSizingCalculationDefaulted()) {
        LOG(Info, "You have zonal equipment and design days, and SimulationControl::DoZoneSizingCalculation is defaulted: turning on Do Zone Sizing Calculation");
        simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
      } else {
        LOG(Info, "You have zonal equipment and design days, it's possible you should enable SimulationControl::DoZoneSizingCalculation");
      }
    } else {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "No");
    }
```
@MatthewSteen Well there are some overrides in the translator to make sure simulations don't crash.  This is a great example of OpenStudio trying to thread a needle between usability and full EnergyPlus power.  Did we miss the mark?

https://github.com/NREL/OpenStudio/blob/develop/openstudiocore/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp#L59

@MatthewSteen maybe I should be more explicit. If you said no to zone sizing (and there are zones and design days) your simulation would fail anyway.  It has been a while since I have been in these dark corners, but I'm pretty sure it has to do with how we define ventilation.  Maybe other things too. 

OK.  I reviewed the Unmet Hours post.  I see LSurf points has a valid scenario where zone sizing could be off and OpenStudio is not allowing it.  Seems like a valid complaint.  Maybe that override logic should just go away.  OS default zone sizing on, allow it to be turned off manually and respect that choice potential E+ failure or not.

https://github.com/NREL/OpenStudio/blob/c018cb6316372cb77b33a4f62f22452908320bc9/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp#L63-L87

@kbenne how about:

```diff
    if (modelObject.doZoneSizingCalculation()) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
-   } else if ((numSizingPeriods > 0) && (modelObject.model().getConcreteModelObjects<ThermalZone>().size() > 0)) {
+   } else if (modelObject.isDoZoneSizingCalculationDefaulted() &&
                 (numSizingPeriods > 0) && (modelObject.model().getConcreteModelObjects<ThermalZone>().size() > 0)) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
    } else {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "No");
    }
```
Or maybe even better:

```c++
    #include <algorithm>

    auto hasZoneEquipment = [](const Model& m) {
      auto zones = m.getConcreteModelObjects<ThermalZone>();
      return std::any_of(zones.cbegin(), zones.cend(), [](const auto& z){ return !z.equipment().empty(); });
    };

    if (modelObject.doZoneSizingCalculation()) {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
    } else if ((numSizingPeriods > 0) && hasZoneEquipment(modelObject.model())) {
      if (modelObject.isDoZoneSizingCalculationDefaulted()) {
        LOG(Info, "You have zonal equipment and design days, and SimulationControl::DoZoneSizingCalculation is defaulted: turning on Do Zone Sizing Calculation");
        simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
      } else {
        LOG(Info, "You have zonal equipment and design days, it's possible you should enable SimulationControl::DoZoneSizingCalculation");
      }
    } else {
      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "No");
    }
```

Fix patch:
diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp
index fec5f6dc0e9..d28ce1d7efb 100644
--- a/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp
+++ b/src/energyplus/ForwardTranslator/ForwardTranslateSimulationControl.cpp
@@ -4,17 +4,15 @@
 ***********************************************************************************************************************/
 
 #include "../ForwardTranslator.hpp"
-#include "../../model/AirLoopHVAC.hpp"
-#include "../../model/AirLoopHVAC_Impl.hpp"
+
+#include "../../model/SimulationControl.hpp"
+
 #include "../../model/PlantLoop.hpp"
 #include "../../model/PlantLoop_Impl.hpp"
-#include "../../model/SimulationControl.hpp"
 #include "../../model/SizingPeriod.hpp"
 #include "../../model/SizingPeriod_Impl.hpp"
 #include "../../model/SizingPlant.hpp"
-#include "../../model/SizingPlant_Impl.hpp"
-#include "../../model/ThermalZone.hpp"
-#include "../../model/ThermalZone_Impl.hpp"
+
 #include <utilities/idd/SimulationControl_FieldEnums.hxx>
 #include <utilities/idd/IddEnums.hxx>
 
@@ -27,72 +25,30 @@ namespace openstudio {
 namespace energyplus {
 
   boost::optional<IdfObject> ForwardTranslator::translateSimulationControl(SimulationControl& modelObject) {
-    IdfObject simCon(openstudio::IddObjectType::SimulationControl);
 
-    m_idfObjects.push_back(simCon);
+    IdfObject simCon = createAndRegisterIdfObject(openstudio::IddObjectType::SimulationControl, modelObject);
 
-    OptionalString s = modelObject.name();
-    if (s) {
-      simCon.setName(*s);
-    }
+    auto boolToString = [](bool b) { return b ? "Yes" : "No"; };
 
-    unsigned numSizingPeriods = modelObject.model().getModelObjects<SizingPeriod>().size();
+    // NOTE: the logic for these three where it's defaulted is inside the model object:
+    // it checks if you have Design Days + (Zonal equipment / AirLoopHVAC / PlantLoop)
+    simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, boolToString(modelObject.doZoneSizingCalculation()));
+    simCon.setString(openstudio::SimulationControlFields::DoSystemSizingCalculation, boolToString(modelObject.doSystemSizingCalculation()));
+    simCon.setString(openstudio::SimulationControlFields::DoPlantSizingCalculation, boolToString(modelObject.doPlantSizingCalculation()));
 
-    if (modelObject.doZoneSizingCalculation()) {
-      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
-    } else if ((numSizingPeriods > 0) && (!modelObject.model().getConcreteModelObjects<ThermalZone>().empty())) {
-      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "Yes");
-    } else {
-      simCon.setString(openstudio::SimulationControlFields::DoZoneSizingCalculation, "No");
-    }
-
-    if (modelObject.doSystemSizingCalculation()) {
-      simCon.setString(openstudio::SimulationControlFields::DoSystemSizingCalculation, "Yes");
-    } else if ((numSizingPeriods > 0) && (!modelObject.model().getConcreteModelObjects<AirLoopHVAC>().empty())) {
-      simCon.setString(openstudio::SimulationControlFields::DoSystemSizingCalculation, "Yes");
-    } else {
-      simCon.setString(openstudio::SimulationControlFields::DoSystemSizingCalculation, "No");
-    }
-
-    if (modelObject.doPlantSizingCalculation()) {
-      simCon.setString(openstudio::SimulationControlFields::DoPlantSizingCalculation, "Yes");
-    } else if ((numSizingPeriods > 0) && (!modelObject.model().getConcreteModelObjects<PlantLoop>().empty())) {
-      simCon.setString(openstudio::SimulationControlFields::DoPlantSizingCalculation, "Yes");
-    } else {
-      simCon.setString(openstudio::SimulationControlFields::DoPlantSizingCalculation, "No");
-    }
-
-    if (modelObject.runSimulationforSizingPeriods()) {
-      simCon.setString(openstudio::SimulationControlFields::RunSimulationforSizingPeriods, "Yes");
-    } else {
-      simCon.setString(openstudio::SimulationControlFields::RunSimulationforSizingPeriods, "No");
-    }
+    simCon.setString(openstudio::SimulationControlFields::RunSimulationforSizingPeriods, boolToString(modelObject.runSimulationforSizingPeriods()));
 
     // DLM: might want to check for weather file object?
-    if (modelObject.runSimulationforWeatherFileRunPeriods()) {
-      simCon.setString(openstudio::SimulationControlFields::RunSimulationforWeatherFileRunPeriods, "Yes");
-    } else {
-      simCon.setString(openstudio::SimulationControlFields::RunSimulationforWeatherFileRunPeriods, "No");
-    }
-
-    if (modelObject.doHVACSizingSimulationforSizingPeriods()) {
+    simCon.setString(openstudio::SimulationControlFields::RunSimulationforWeatherFileRunPeriods,
+                     boolToString(modelObject.runSimulationforWeatherFileRunPeriods()));
+
+    // The logic when defaulted is inside the model object
+    if (!modelObject.isDoHVACSizingSimulationforSizingPeriodsDefaulted()) {
+      simCon.setString(openstudio::SimulationControlFields::DoHVACSizingSimulationforSizingPeriods,
+                       boolToString(modelObject.doHVACSizingSimulationforSizingPeriods()));
+    } else if (modelObject.doHVACSizingSimulationforSizingPeriods()) {
+      // We only write it if it's not the IDD default of "No", so we don't want to pollute the IDF with advanced fields that people aren't used to.
       simCon.setString(openstudio::SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, "Yes");
-    } else if (numSizingPeriods > 0) {
-      // Sizing:Plant I/O: "The use of 'Coincident' sizing option requires that the object
-      // be set to YES for the input field called Do HVAC Sizing Simulation for Sizing Periods"
-      std::vector<PlantLoop> plantLoops = modelObject.model().getConcreteModelObjects<PlantLoop>();
-      if (std::any_of(plantLoops.begin(), plantLoops.end(),
-                      [](const PlantLoop& p) { return openstudio::istringEqual("Coincident", p.sizingPlant().sizingOption()); })) {
-        simCon.setString(openstudio::SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, "Yes");
-      } else {
-        if (!modelObject.isDoZoneSizingCalculationDefaulted()) {
-          simCon.setString(openstudio::SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, "No");
-        }
-      }
-    } else {
-      if (!modelObject.isDoZoneSizingCalculationDefaulted()) {
-        simCon.setString(openstudio::SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, "No");
-      }
     }
 
     if (!modelObject.isMaximumNumberofHVACSizingSimulationPassesDefaulted()) {
@@ -102,7 +58,7 @@ namespace energyplus {
 
     // other fields mapped to Building
 
-    return boost::optional<IdfObject>(simCon);
+    return simCon;
   }
 
 }  // namespace energyplus
diff --git a/src/model/SimulationControl.cpp b/src/model/SimulationControl.cpp
index dfe9752da1a..01c4739cd7f 100644
--- a/src/model/SimulationControl.cpp
+++ b/src/model/SimulationControl.cpp
@@ -7,22 +7,30 @@
 #include "SimulationControl_Impl.hpp"
 
 #include "Model.hpp"
+
+#include "AirLoopHVAC.hpp"
+#include "AirLoopHVAC_Impl.hpp"
 #include "ConvergenceLimits.hpp"
 #include "ConvergenceLimits_Impl.hpp"
 #include "HeatBalanceAlgorithm.hpp"
 #include "HeatBalanceAlgorithm_Impl.hpp"
 #include "InsideSurfaceConvectionAlgorithm.hpp"
 #include "InsideSurfaceConvectionAlgorithm_Impl.hpp"
+#include "PlantLoop.hpp"
+#include "PlantLoop_Impl.hpp"
 #include "OutsideSurfaceConvectionAlgorithm.hpp"
 #include "OutsideSurfaceConvectionAlgorithm_Impl.hpp"
 #include "RunPeriod.hpp"
 #include "RunPeriod_Impl.hpp"
 #include "ShadowCalculation.hpp"
 #include "ShadowCalculation_Impl.hpp"
-#include "SizingPeriod.hpp"
-#include "SizingPeriod_Impl.hpp"
 #include "SizingParameters.hpp"
 #include "SizingParameters_Impl.hpp"
+#include "SizingPeriod.hpp"
+#include "SizingPeriod_Impl.hpp"
+#include "SizingPlant.hpp"
+#include "ThermalZone.hpp"
+#include "ThermalZone_Impl.hpp"
 #include "Timestep.hpp"
 #include "Timestep_Impl.hpp"
 #include "WeatherFile.hpp"
@@ -46,6 +54,8 @@
 #include "../utilities/core/Compare.hpp"
 #include "../utilities/units/Unit.hpp"
 
+#include <algorithm>
+
 namespace openstudio {
 namespace model {
 
@@ -169,9 +179,36 @@ namespace model {
     }
 
     bool SimulationControl_Impl::doZoneSizingCalculation() const {
-      boost::optional<std::string> value = getString(OS_SimulationControlFields::DoZoneSizingCalculation, true);
-      OS_ASSERT(value);
-      return openstudio::istringEqual(value.get(), "Yes");
+
+      auto shouldItBeOn = [this]() -> bool {
+        const auto m = model();
+        const auto hasSizingPeriods = !m.getModelObjects<SizingPeriod>().empty();
+        if (!hasSizingPeriods) {
+          return false;
+        }
+
+        const auto zones = m.getConcreteModelObjects<ThermalZone>();
+        const auto hasZoneEquipment = std::any_of(zones.cbegin(), zones.cend(), [](const auto& z) { return !z.equipment().empty(); });
+
+        return hasZoneEquipment;
+      };
+
+      if (!isDoZoneSizingCalculationDefaulted()) {
+        boost::optional<std::string> value = getString(OS_SimulationControlFields::DoZoneSizingCalculation, true);
+        OS_ASSERT(value);
+        const auto result = openstudio::istringEqual(value.get(), "Yes");
+        if (!result && shouldItBeOn()) {
+          LOG(Warn, "You have zonal equipment and design days, it's possible you should enable SimulationControl::DoZoneSizingCalculation");
+        }
+        return result;
+      }
+
+      const auto result = shouldItBeOn();
+      if (result) {
+        LOG(Debug, "You have zonal equipment and design days, and SimulationControl::DoZoneSizingCalculation is defaulted: turning on.");
+      }
+
+      return result;
     }
 
     bool SimulationControl_Impl::isDoZoneSizingCalculationDefaulted() const {
@@ -179,9 +216,28 @@ namespace model {
     }
 
     bool SimulationControl_Impl::doSystemSizingCalculation() const {
-      boost::optional<std::string> value = getString(OS_SimulationControlFields::DoSystemSizingCalculation, true);
-      OS_ASSERT(value);
-      return openstudio::istringEqual(value.get(), "Yes");
+
+      auto shouldItBeOn = [this]() -> bool {
+        const auto m = model();
+        return !m.getModelObjects<SizingPeriod>().empty() && !m.getConcreteModelObjects<AirLoopHVAC>().empty();
+      };
+
+      if (!isDoSystemSizingCalculationDefaulted()) {
+        boost::optional<std::string> value = getString(OS_SimulationControlFields::DoSystemSizingCalculation, true);
+        OS_ASSERT(value);
+        const auto result = openstudio::istringEqual(value.get(), "Yes");
+        if (!result && shouldItBeOn()) {
+          LOG(Warn, "You have AirLoopHVAC(s) and design day(s), it's possible you should enable SimulationControl::DoSystemSizingCalculation");
+        }
+        return result;
+      }
+
+      const auto result = shouldItBeOn();
+      if (result) {
+        LOG(Debug, "You have AirLoopHVAC(s) and design day(s), and SimulationControl::DoSystemSizingCalculation is defaulted: assuming on.");
+      }
+
+      return result;
     }
 
     bool SimulationControl_Impl::isDoSystemSizingCalculationDefaulted() const {
@@ -189,9 +245,28 @@ namespace model {
     }
 
     bool SimulationControl_Impl::doPlantSizingCalculation() const {
-      boost::optional<std::string> value = getString(OS_SimulationControlFields::DoPlantSizingCalculation, true);
-      OS_ASSERT(value);
-      return openstudio::istringEqual(value.get(), "Yes");
+
+      auto shouldItBeOn = [this]() -> bool {
+        const auto m = model();
+        return !m.getModelObjects<SizingPeriod>().empty() && !m.getConcreteModelObjects<PlantLoop>().empty();
+      };
+
+      if (!isDoPlantSizingCalculationDefaulted()) {
+        boost::optional<std::string> value = getString(OS_SimulationControlFields::DoPlantSizingCalculation, true);
+        OS_ASSERT(value);
+        const auto result = openstudio::istringEqual(value.get(), "Yes");
+        if (!result && shouldItBeOn()) {
+          LOG(Warn, "You have PlantLoop(s) and design days, it's possible you should enable SimulationControl::DoPlantSizingCalculation");
+        }
+        return result;
+      }
+
+      const auto result = shouldItBeOn();
+      if (result) {
+        LOG(Debug, "You have PlantLoop(s) and design days, and SimulationControl::DoPlantSizingCalculation is defaulted: assuming on.");
+      }
+
+      return result;
     }
 
     bool SimulationControl_Impl::isDoPlantSizingCalculationDefaulted() const {
@@ -543,9 +618,42 @@ namespace model {
     }
 
     bool SimulationControl_Impl::doHVACSizingSimulationforSizingPeriods() const {
-      boost::optional<std::string> value = getString(OS_SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, true);
-      OS_ASSERT(value);
-      return openstudio::istringEqual(value.get(), "Yes");
+
+      auto shouldItBeOn = [this]() -> bool {
+        const auto m = model();
+        const auto hasSizingPeriods = !m.getModelObjects<SizingPeriod>().empty();
+        if (!hasSizingPeriods) {
+          return false;
+        }
+
+        // Sizing:Plant I/O: "The use of 'Coincident' sizing option requires that the object
+        // be set to YES for the input field called Do HVAC Sizing Simulation for Sizing Periods"
+        const auto plantLoops = m.getConcreteModelObjects<PlantLoop>();
+        const auto hasPlantsWithCoincidentSizingOption = std::any_of(plantLoops.begin(), plantLoops.end(), [](const PlantLoop& p) {
+          return openstudio::istringEqual("Coincident", p.sizingPlant().sizingOption());
+        });
+
+        return hasPlantsWithCoincidentSizingOption;
+      };
+
+      if (!isDoHVACSizingSimulationforSizingPeriodsDefaulted()) {
+        boost::optional<std::string> value = getString(OS_SimulationControlFields::DoHVACSizingSimulationforSizingPeriods, true);
+        OS_ASSERT(value);
+        const auto result = openstudio::istringEqual(value.get(), "Yes");
+        if (!result && shouldItBeOn()) {
+          LOG(Warn, "You have PlantLoop(s) with 'Coincident' Sizing Option and design day(s), it's possible you should enable "
+                    "SimulationControl::DoHVACSizingSimulationforSizingPeriods");
+        }
+        return result;
+      }
+
+      const auto result = shouldItBeOn();
+      if (result) {
+        LOG(Debug, "You have PlantLoop(s) with 'Coincident' Sizing Option and design day(s), and "
+                   "SimulationControl::DoHVACSizingSimulationforSizingPeriods is defaulted: assuming on.");
+      }
+
+      return result;
     }
 
     bool SimulationControl_Impl::isDoHVACSizingSimulationforSizingPeriodsDefaulted() const {
diff --git a/src/model/SimulationControl.hpp b/src/model/SimulationControl.hpp
index 076b9accf22..3abf0f29ea7 100644
--- a/src/model/SimulationControl.hpp
+++ b/src/model/SimulationControl.hpp
@@ -60,14 +60,17 @@ namespace model {
     /** @name Getters */
     //@{
 
+    // When defaulted, this will return true if you have DesignDays in your model and any zone with a Zonal HVAC equipment, false otherwise
     bool doZoneSizingCalculation() const;
 
     bool isDoZoneSizingCalculationDefaulted() const;
 
+    // When defaulted, this will return true only if you have DesignDays and at least one AirLoopHVAC in your model
     bool doSystemSizingCalculation() const;
 
     bool isDoSystemSizingCalculationDefaulted() const;
 
+    // When defaulted, this will return true only if you have DesignDays and at least one PlantLoop in your model
     bool doPlantSizingCalculation() const;
 
     bool isDoPlantSizingCalculationDefaulted() const;
@@ -100,6 +103,7 @@ namespace model {
 
     bool isMinimumNumberofWarmupDaysDefaulted() const;
 
+    // When defaulted, this will return true only if you have DesignDays and at least one PlantLoop with a Sizing Plant having a 'Coincident' Sizing Option
     bool doHVACSizingSimulationforSizingPeriods() const;
     bool isDoHVACSizingSimulationforSizingPeriodsDefaulted() const;
 


Test patch:
diff --git a/src/model/test/SimulationControl_GTest.cpp b/src/model/test/SimulationControl_GTest.cpp
index 8a6573446f9..70e81ae696f 100644
--- a/src/model/test/SimulationControl_GTest.cpp
+++ b/src/model/test/SimulationControl_GTest.cpp
@@ -9,7 +9,14 @@
 
 #include "../SimulationControl.hpp"
 #include "../SimulationControl_Impl.hpp"
+
+#include "../AirLoopHVAC.hpp"
+#include "../DesignDay.hpp"
 #include "../LifeCycleCost.hpp"
+#include "../PlantLoop.hpp"
+#include "../SizingPlant.hpp"
+#include "../ThermalZone.hpp"
+#include "../ZoneHVACBaseboardConvectiveElectric.hpp"
 
 using namespace openstudio;
 using namespace openstudio::model;
@@ -196,3 +203,65 @@ TEST_F(ModelFixture, SimulationControl_LifeCycleCost) {
   // adding this cost (and many others) would be really weird, expect a throw
   EXPECT_THROW(LifeCycleCost cost(simulationControl), openstudio::Exception);
 }
+
+TEST_F(ModelFixture, SimulationControl_SmartDefaults) {
+
+  Model m;
+
+  auto sc = m.getUniqueModelObject<SimulationControl>();
+
+  auto testDefaults = [&sc](bool doZone, bool doSystem, bool doPlant, bool doHVACForSizing) {
+    EXPECT_EQ(doZone, sc.doZoneSizingCalculation());
+    EXPECT_EQ(doSystem, sc.doSystemSizingCalculation());
+    EXPECT_EQ(doPlant, sc.doPlantSizingCalculation());
+    EXPECT_EQ(doHVACForSizing, sc.doHVACSizingSimulationforSizingPeriods());
+  };
+
+  sc.resetDoHVACSizingSimulationforSizingPeriods();
+  sc.resetDoZoneSizingCalculation();
+  sc.resetDoSystemSizingCalculation();
+  sc.resetDoPlantSizingCalculation();
+
+  // No Design Day, no Zonal / AirLoopHVAC / PlantLoop
+  testDefaults(false, false, false, false);
+
+  DesignDay dd(m);
+  testDefaults(false, false, false, false);
+
+  EXPECT_FALSE(sc.doZoneSizingCalculation());
+  EXPECT_FALSE(sc.doSystemSizingCalculation());
+  EXPECT_FALSE(sc.doPlantSizingCalculation());
+  EXPECT_FALSE(sc.doHVACSizingSimulationforSizingPeriods());
+
+  ThermalZone z(m);
+  testDefaults(false, false, false, false);
+
+  EXPECT_FALSE(sc.doZoneSizingCalculation());
+  EXPECT_FALSE(sc.doSystemSizingCalculation());
+  EXPECT_FALSE(sc.doPlantSizingCalculation());
+  EXPECT_FALSE(sc.doHVACSizingSimulationforSizingPeriods());
+
+  ZoneHVACBaseboardConvectiveElectric b1(m);
+  testDefaults(false, false, false, false);
+
+  // We have design day + a zone with some equipment => precondition for doZoneSizingCalculation is met
+  EXPECT_TRUE(b1.addToThermalZone(z));
+  testDefaults(true, false, false, false);
+
+  // Design Day + AirLoopHVAC => doSystemSizingCalculation
+  AirLoopHVAC a(m);
+  testDefaults(true, true, false, false);
+
+  // Design Day + PlantLoop => doPlantSizingCalculation
+  PlantLoop p(m);
+  EXPECT_TRUE(p.sizingPlant().setSizingOption("NonCoincident"));
+  testDefaults(true, true, true, false);
+
+  // Design Day + Coicident => doHVACSizingSimulationforSizingPeriods
+  EXPECT_TRUE(p.sizingPlant().setSizingOption("Coincident"));
+  testDefaults(true, true, true, true);
+
+  // Remove design day
+  dd.remove();
+  testDefaults(false, false, false, false);
+}