Repository: NREL/OpenStudio (language: python)

Issue:
setWindowToWallRatio failed silently without warnings
Issue overview
--------------
<!--- Provide a general summary of the issue -->
The setWindowToWallRatio function failed when specifying a WWR of 0.98 (98%) with **0.0** sill offset and **true** for heightOffsetFromFloor.

## Current Behavior

```ruby
m = Model.new
pts = OpenStudio::Point3dVector.new;  pts << OpenStudio::Point3d.new(0, 0, 0); pts << OpenStudio::Point3d.new(0, 1, 0); pts << OpenStudio::Point3d.new(1, 1, 0); pts << OpenStudio::Point3d.new(1, 0, 0);
s = Space::fromFloorPrint(pts, 3, m).get()
surface = m.getSurfaces.select{|s| s.surfaceType.upcase == 'WALL'}.first
new_window = surface.setWindowToWallRatio(0.98, 0.0, true).get

=> RuntimeError: Optional not initialized
```

## Expected Behavior
<!--- Tell us what should happen -->
Expect: 1. window to wall ratio successfully set to 98% or above. or
2. Failed with informative messages.

## 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. Create medium office prototype with OpenStudio Standard. Use '90.1-2019' as template. 
2. Load model
3. Loop through surfaces: model.getSurfaces.each do |ss|
4. get original construction of the subsurface
5. remove the subsurfaces in the surface
6. create new window: ss.setWindowToWallRatio(0.98, 0.0, true).get

## Possible Solution
<!--- Optional, but if you can, suggest a fix/reason for the bug, -->

## Details

### Environment
OpenStudio V3.2

Some additional details about your environment for this issue (if relevant):
 - Platform (Operating system, version): Windows 10
 - Version of OpenStudio (if using an intermediate build, include SHA): 3.2.1

### Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
Try to implement the logic in the [ASHRAE 90.1 2019 addendum l to 2019](https://www.ashrae.org/file%20library/technical%20resources/standards%20and%20guidelines/standards%20addenda/90_1_2019_l_20201030.pdf)

setWindowToWallRatio failed silently without warnings
Issue overview
--------------
<!--- Provide a general summary of the issue -->
The setWindowToWallRatio function failed when specifying a WWR of 0.98 (98%) with **0.0** sill offset and **true** for heightOffsetFromFloor.

## Current Behavior

```ruby
m = Model.new
pts = OpenStudio::Point3dVector.new;  pts << OpenStudio::Point3d.new(0, 0, 0); pts << OpenStudio::Point3d.new(0, 1, 0); pts << OpenStudio::Point3d.new(1, 1, 0); pts << OpenStudio::Point3d.new(1, 0, 0);
s = Space::fromFloorPrint(pts, 3, m).get()
surface = m.getSurfaces.select{|s| s.surfaceType.upcase == 'WALL'}.first
new_window = surface.setWindowToWallRatio(0.98, 0.0, true).get

=> RuntimeError: Optional not initialized
```

## Expected Behavior
<!--- Tell us what should happen -->
Expect: 1. window to wall ratio successfully set to 98% or above. or
2. Failed with informative messages.

## 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. Create medium office prototype with OpenStudio Standard. Use '90.1-2019' as template. 
2. Load model
3. Loop through surfaces: model.getSurfaces.each do |ss|
4. get original construction of the subsurface
5. remove the subsurfaces in the surface
6. create new window: ss.setWindowToWallRatio(0.98, 0.0, true).get

## Possible Solution
<!--- Optional, but if you can, suggest a fix/reason for the bug, -->

## Details

### Environment
OpenStudio V3.2

Some additional details about your environment for this issue (if relevant):
 - Platform (Operating system, version): Windows 10
 - Version of OpenStudio (if using an intermediate build, include SHA): 3.2.1

### Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
Try to implement the logic in the [ASHRAE 90.1 2019 addendum l to 2019](https://www.ashrae.org/file%20library/technical%20resources/standards%20and%20guidelines/standards%20addenda/90_1_2019_l_20201030.pdf)



Discussion / hints:
Could use a warning that the `requestedTotalWindowArea > maxWindowArea` allowed in `applyViewAndDaylightingGlassRatios`.

https://github.com/NREL/OpenStudio/blob/f7b7f88e743e15a9e934e8109af8e61784e8b2cb/src/utilities/geometry/Geometry.cpp#L687-L695
Could use a warning that the `requestedTotalWindowArea > maxWindowArea` allowed in `applyViewAndDaylightingGlassRatios`.

https://github.com/NREL/OpenStudio/blob/f7b7f88e743e15a9e934e8109af8e61784e8b2cb/src/utilities/geometry/Geometry.cpp#L687-L695

Fix patch:
diff --git a/src/utilities/geometry/Geometry.cpp b/src/utilities/geometry/Geometry.cpp
index d15029a1cff..1a3ded1aa42 100644
--- a/src/utilities/geometry/Geometry.cpp
+++ b/src/utilities/geometry/Geometry.cpp
@@ -674,10 +674,15 @@ bool applyViewAndDaylightingGlassRatios(double viewGlassToWallRatio, double dayl
   double wallArea = wallWidth * wallHeight;
 
   if (wallWidth < 2 * minGlassToEdgeDistance) {
+    LOG_FREE(Warn, "utilities.geometry.applyViewAndDaylightingGlassRatios",
+             "Wall Width (" << wallWidth << "m) is too small to preserve 0.0254m (1 inch) on both sides.");
     return false;
   }
 
   if (wallHeight < 2 * minGlassToEdgeDistance + minViewToDaylightDistance) {
+    LOG_FREE(Warn, "utilities.geometry.applyViewAndDaylightingGlassRatios",
+             "Wall Height (" << wallHeight << "m) is too small to preserve 0.0254m (1 inch) on both sides plus the minViewToDaylightDistance ("
+                             << minViewToDaylightDistance << "m).");
     return false;
   }
 
@@ -693,6 +698,12 @@ bool applyViewAndDaylightingGlassRatios(double viewGlassToWallRatio, double dayl
   double requestedTotalWindowArea = totalWWR * wallArea;
 
   if (requestedTotalWindowArea > maxWindowArea) {
+    LOG_FREE(
+      Warn, "utilities.geometry.applyViewAndDaylightingGlassRatios",
+      "Requested WWR ("
+        << totalWWR << ") is greater than the Max WWR (" << maxWindowArea / wallArea
+        << "). This limit is meant to preserve mandatory space between window and wall: 0.0254m (1 inch) on all sides + minViewToDaylightDistance ("
+        << minViewToDaylightDistance << "m).");
     return false;
   }
 


Test patch:
diff --git a/src/model/test/Surface_GTest.cpp b/src/model/test/Surface_GTest.cpp
index 12b17adb243..bcf7ee96efb 100644
--- a/src/model/test/Surface_GTest.cpp
+++ b/src/model/test/Surface_GTest.cpp
@@ -92,9 +92,9 @@
 
 #include "../../utilities/math/FloatCompare.hpp"
 
-#include "../../utilities/core/Finder.hpp"
-
 #include "../../utilities/core/Assert.hpp"
+#include "../../utilities/core/Finder.hpp"
+#include "../../utilities/core/StringStreamLogSink.hpp"
 
 #include <boost/algorithm/string/case_conv.hpp>
 #include <boost/math/constants/constants.hpp>
@@ -1156,6 +1156,51 @@ TEST_F(ModelFixture, Surface_SouthWall_WWR) {
   */
 }
 
+TEST_F(ModelFixture, Surface_4601_WWR_tooBig) {
+
+  // Test for #4601
+  constexpr double xmin = 0.0;
+  constexpr double xmax = 1.0;
+  constexpr double zmin = 0.0;
+  constexpr double zmax = 1.0;
+  std::vector<Point3d> vertices{
+    {xmin, 0, zmax},
+    {xmin, 0, zmin},
+    {xmax, 0, zmin},
+    {xmax, 0, zmax},
+  };
+
+  constexpr double oneInch = 0.0254;
+
+  constexpr double area = (xmax - xmin) * (zmax - zmin);
+  EXPECT_DOUBLE_EQ(1.0, area);
+  constexpr double maxWindowArea = (xmax - xmin - 2 * oneInch) * (zmax - zmin - 2 * oneInch);
+  EXPECT_DOUBLE_EQ(0.90098064, maxWindowArea);
+
+  constexpr double maxWWR = maxWindowArea / area;
+  EXPECT_DOUBLE_EQ(0.90098064, maxWWR);
+
+  // window extends below desired sill height
+  Model model;
+  Surface wall(vertices, model);
+  EXPECT_EQ("Wall", wall.surfaceType());
+  EXPECT_EQ(0.0, wall.windowToWallRatio());
+  constexpr double offset = 0.0;
+  constexpr double wwr = 0.98;
+
+  StringStreamLogSink sink;
+  sink.setLogLevel(Warn);
+
+  boost::optional<SubSurface> window = wall.setWindowToWallRatio(wwr, offset, true);
+  EXPECT_FALSE(window);
+  EXPECT_EQ(1, sink.logMessages().size());
+  EXPECT_EQ(Warn, sink.logMessages().front().logLevel());
+  EXPECT_EQ("utilities.geometry.applyViewAndDaylightingGlassRatios", sink.logMessages().front().logChannel());
+  EXPECT_EQ("Requested WWR (0.98) is greater than the Max WWR (0.900981). This limit is meant to preserve mandatory space between window and wall: "
+            "0.0254m (1 inch) on all sides + minViewToDaylightDistance (0m).",
+            sink.logMessages().front().logMessage());
+}
+
 TEST_F(ModelFixture, Surface_EastWall_WWR) {
   std::vector<Point3d> vertices;
   vertices.push_back(Point3d(0, 0, 3));
@@ -3759,4 +3804,4 @@ TEST_F(ModelFixture, Issue_4374) {
     // Each surface should have no more than one sub surface
     EXPECT_TRUE(surface.subSurfaces().size() <= 1);
   }
-}
\ No newline at end of file
+}