Repository: santoshphilip/eppy

Issue:
surface.true_azimuth does not take into account the zone.Direction_of_Relative_North
This issue has been opened to further address the issue of `surface.true_azimuth` discussed in #282 and #283, where `zone.Direction_of_Relative_North` has yet to be included in the calculation.

Discussion:

Patch:
diff --git a/eppy/bunch_subclass.py b/eppy/bunch_subclass.py
index ba78e47..f44c648 100644
--- a/eppy/bunch_subclass.py
+++ b/eppy/bunch_subclass.py
@@ -1,5 +1,6 @@
 # Copyright (c) 2012 Santosh Philip
 # Copyright (c) 2016 Jamie Bull
+# Copyright (c) 2020 Cheng Cui
 # =======================================================================
 #  Distributed under the MIT License.
 #  (See accompanying file LICENSE or copy at
diff --git a/eppy/function_helpers.py b/eppy/function_helpers.py
index 140e569..25b8c4d 100644
--- a/eppy/function_helpers.py
+++ b/eppy/function_helpers.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2012 Santosh Philip
+# Copyright (c) 2020 Cheng Cui
 # =======================================================================
 #  Distributed under the MIT License.
 #  (See accompanying file LICENSE or copy at
@@ -58,12 +59,18 @@ def azimuth(ddtt):
 
 
 def true_azimuth(ddtt):
-    """azimuth of the surface"""
+    """true azimuth of the surface"""
     idf = ddtt.theidf
-    building_north_axis = idf.idfobjects["building".upper()][0].North_Axis
-    coords = getcoords(ddtt)
-    surface_azimuth = g_surface.azimuth(coords)
-    return g_surface.true_azimuth(building_north_axis, surface_azimuth)
+    zone_name = ddtt.Zone_Name
+
+    building_north_axis = idf.idfobjects["building"][0].North_Axis
+    zone_direction_of_relative_north = idf.getobject(
+        "zone", zone_name
+    ).Direction_of_Relative_North
+    surface_azimuth = azimuth(ddtt)
+    return g_surface.true_azimuth(
+        building_north_axis, zone_direction_of_relative_north, surface_azimuth
+    )
 
 
 def tilt(ddtt):
diff --git a/eppy/geometry/surface.py b/eppy/geometry/surface.py
index 8d2bfb9..1fb822d 100644
--- a/eppy/geometry/surface.py
+++ b/eppy/geometry/surface.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2012 Tuan Tran
+# Copyright (c) 2020 Cheng Cui
 
 # This file is part of eppy.
 # =======================================================================
@@ -131,10 +132,19 @@ def azimuth(poly):
         return angle2vecs(vec_azi, vec_n)
 
 
-def true_azimuth(building_north_axis, surface_azimuth):
+def true_azimuth(
+    building_north_axis, zone_direction_of_relative_north, surface_azimuth
+):
     """True azimuth of a polygon poly"""
     building_north_axis = 0 if building_north_axis == "" else building_north_axis
-    return (building_north_axis + surface_azimuth) % 360
+    zone_direction_of_relative_north = (
+        0
+        if zone_direction_of_relative_north == ""
+        else zone_direction_of_relative_north
+    )
+    return (
+        building_north_axis + zone_direction_of_relative_north + surface_azimuth
+    ) % 360
 
 
 def tilt(poly):


Test patch:
diff --git a/eppy/tests/geometry_tests/test_surface.py b/eppy/tests/geometry_tests/test_surface.py
index 6d5b32c..a1aeba4 100644
--- a/eppy/tests/geometry_tests/test_surface.py
+++ b/eppy/tests/geometry_tests/test_surface.py
@@ -1,4 +1,5 @@
 # Copyright (c) 2012 Tuan Tran
+# Copyright (c) 2020 Cheng Cui
 # =======================================================================
 #  Distributed under the MIT License.
 #  (See accompanying file LICENSE or copy at
@@ -109,13 +110,21 @@ def test_azimuth():
 def test_true_azimuth():
     """test the true azimuth of a polygon poly"""
     data = (
-        ("", 180, 180),
-        # building_north_axis, surface_azimuth, answer,
-        (20, 0, 20),
-        (240, 180, 60),
+        (45, 30, 0, 75),
+        # building_north_axis, zone_direction_of_relative_north, surface_azimuth, answer,
+        ("", 0, 180, 180),
+        (20, "", 20, 40),
+        (240, 90, 180, 150),
     )
-    for building_north_axis, surface_azimuth, answer in data:
-        result = surface.true_azimuth(building_north_axis, surface_azimuth)
+    for (
+        building_north_axis,
+        zone_direction_of_relative_north,
+        surface_azimuth,
+        answer,
+    ) in data:
+        result = surface.true_azimuth(
+            building_north_axis, zone_direction_of_relative_north, surface_azimuth
+        )
         assert almostequal(answer, result, places=3) == True
 
 
diff --git a/eppy/tests/test_function_helpers.py b/eppy/tests/test_function_helpers.py
index e69de29..3d503c8 100644
--- a/eppy/tests/test_function_helpers.py
+++ b/eppy/tests/test_function_helpers.py
@@ -0,0 +1,162 @@
+# Copyright (c) 2020 Cheng Cui
+# =======================================================================
+#  Distributed under the MIT License.
+#  (See accompanying file LICENSE or copy at
+#  http://opensource.org/licenses/MIT)
+# =======================================================================
+
+"""py.test for function_helpers"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+from six import StringIO
+
+import eppy.function_helpers as fh
+from eppy.iddcurrent import iddcurrent
+from eppy.modeleditor import IDF
+from eppy.pytest_helpers import almostequal
+
+iddtxt = iddcurrent.iddtxt
+iddfhandle = StringIO(iddcurrent.iddtxt)
+if IDF.getiddname() == None:
+    IDF.setiddname(iddfhandle)
+
+idftxt = """
+    Version,8.0;
+
+    Building,
+        Simple One Zone,         !- Name
+        ;                        !- North Axis {deg} 
+
+    Zone,
+        ZONE ONE,                !- Name
+        ,                        !- Direction of Relative North {deg}
+        0, 0, 0;                            !- X,Y,Z  {m}
+
+    GlobalGeometryRules,
+        UpperLeftCorner,         !- Starting Vertex Position
+        CounterClockWise,        !- Vertex Entry Direction
+        World;                   !- Coordinate System
+
+    BuildingSurface:Detailed,
+        Zn001:Wall001,           !- Name
+        Wall,                    !- Surface Type
+        R13WALL,                 !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Outdoors,                !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        SunExposed,              !- Sun Exposure
+        WindExposed,             !- Wind Exposure
+        0.5000000,               !- View Factor to Ground
+        4,                       !- Number of Vertices
+        0, 0, 4.572000,                     !- X,Y,Z  1 {m}
+        0, 0, 0,                            !- X,Y,Z  2 {m}
+        15.24000, 0, 0,                     !- X,Y,Z  3 {m}
+        15.24000, 0, 4.572000;              !- X,Y,Z  4 {m}
+
+    BuildingSurface:Detailed,
+        Zn001:Wall002,           !- Name
+        Wall,                    !- Surface Type
+        R13WALL,                 !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Outdoors,                !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        SunExposed,              !- Sun Exposure
+        WindExposed,             !- Wind Exposure
+        0.5000000,               !- View Factor to Ground
+        4,                       !- Number of Vertices
+        15.24000, 0, 4.572000,              !- X,Y,Z  1 {m}
+        15.24000, 0, 0,                     !- X,Y,Z  2 {m}
+        15.24000, 15.24000, 0,              !- X,Y,Z  3 {m}
+        15.24000, 15.24000, 4.572000;       !- X,Y,Z  4 {m}
+
+    BuildingSurface:Detailed,
+        Zn001:Wall003,           !- Name
+        Wall,                    !- Surface Type
+        R13WALL,                 !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Outdoors,                !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        SunExposed,              !- Sun Exposure
+        WindExposed,             !- Wind Exposure
+        0.5000000,               !- View Factor to Ground
+        4,                       !- Number of Vertices
+        15.24000, 15.24000, 4.572000,       !- X,Y,Z  1 {m}
+        15.24000, 15.24000, 0,              !- X,Y,Z  2 {m}
+        0, 15.24000, 0,                     !- X,Y,Z  3 {m}
+        0, 15.24000, 4.572000;              !- X,Y,Z  4 {m}
+
+    BuildingSurface:Detailed,
+        Zn001:Wall004,           !- Name
+        Wall,                    !- Surface Type
+        R13WALL,                 !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Outdoors,                !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        SunExposed,              !- Sun Exposure
+        WindExposed,             !- Wind Exposure
+        0.5000000,               !- View Factor to Ground
+        4,                       !- Number of Vertices
+        0, 15.24000, 4.572000,              !- X,Y,Z  1 {m}
+        0, 15.24000, 0,                     !- X,Y,Z  2 {m}
+        0, 0, 0,                            !- X,Y,Z  3 {m}
+        0, 0, 4.572000;                     !- X,Y,Z  4 {m}
+
+    BuildingSurface:Detailed,
+        Zn001:Flr001,            !- Name
+        Floor,                   !- Surface Type
+        FLOOR,                   !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Adiabatic,               !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        NoSun,                   !- Sun Exposure
+        NoWind,                  !- Wind Exposure
+        1.000000,                !- View Factor to Ground
+        4,                       !- Number of Vertices
+        15.24000, 0.000000, 0.0,            !- X,Y,Z  1 {m}
+        0.000000, 0.000000, 0.0,            !- X,Y,Z  2 {m}
+        0.000000, 15.24000, 0.0,            !- X,Y,Z  3 {m}
+        15.24000, 15.24000, 0.0;            !- X,Y,Z  4 {m}
+
+    BuildingSurface:Detailed,
+        Zn001:Roof001,           !- Name
+        Roof,                    !- Surface Type
+        ROOF31,                  !- Construction Name
+        ZONE ONE,                !- Zone Name
+        Outdoors,                !- Outside Boundary Condition
+        ,                        !- Outside Boundary Condition Object
+        SunExposed,              !- Sun Exposure
+        WindExposed,             !- Wind Exposure
+        0,                       !- View Factor to Ground
+        4,                       !- Number of Vertices
+        0.000000, 15.24000, 4.572,          !- X,Y,Z  1 {m}
+        0.000000, 0.000000, 4.572,          !- X,Y,Z  2 {m}
+        15.24000, 0.000000, 4.572,          !- X,Y,Z  3 {m}
+        15.24000, 15.24000, 4.572;          !- X,Y,Z  4 {m}
+"""
+
+
+def test_true_azimuth():
+    """py.test for true_azimuth"""
+    data = (
+        (45, 30, 255),
+        # building_north_axis, zone_direction_of_relative_north, answer,
+        ("", 0, 180),
+        (20, "", 200),
+        (240, 90, 150),
+    )
+
+    fhandle = StringIO(idftxt)
+    idf = IDF(fhandle)
+    building = idf.idfobjects["Building"][0]
+    zone = idf.idfobjects["Zone"][0]
+    surface = idf.idfobjects["BuildingSurface:Detailed"][0]
+
+    for building_north_axis, zone_direction_of_relative_north, answer in data:
+        building.North_Axis = building_north_axis
+        zone.Direction_of_Relative_North = zone_direction_of_relative_north
+        result = fh.true_azimuth(surface)
+        assert almostequal(answer, result, places=3) == True