From 6ff552d82d34738da05f4e0ef6c3f232c998dca8 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Mon, 23 Dec 2024 12:25:59 -0600 Subject: [PATCH 01/10] o First draft area correction --- uxarray/grid/area.py | 109 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index b785f87f8..de31300d5 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -7,7 +7,13 @@ @njit(cache=True) def calculate_face_area( - x, y, z, quadrature_rule="gaussian", order=4, coords_type="spherical" + x, + y, + z, + quadrature_rule="gaussian", + order=4, + coords_type="spherical", + correct_area=False, ): """Calculate area of a face on sphere. @@ -56,6 +62,22 @@ def calculate_face_area( # num triangles is two less than the total number of nodes num_triangles = num_nodes - 2 + if coords_type == "spherical": + # Preallocate arrays for Cartesian coordinates + n_points = len(x) + x_cartesian = np.empty(n_points) + y_cartesian = np.empty(n_points) + z_cartesian = np.empty(n_points) + + # Convert all points to Cartesian coordinates using an explicit loop + for i in range(n_points): + lon_rad = np.deg2rad(x[i]) + lat_rad = np.deg2rad(y[i]) + cartesian = _lonlat_rad_to_xyz(lon_rad, lat_rad) + x_cartesian[i], y_cartesian[i], z_cartesian[i] = cartesian + + x, y, z = x_cartesian, y_cartesian, z_cartesian + # Using tempestremap GridElements: https://github.com/ClimateGlobalChange/tempestremap/blob/master/src/GridElements.cpp # loop through all sub-triangles of face for j in range(0, num_triangles): @@ -63,16 +85,6 @@ def calculate_face_area( node2 = np.array([x[j + 1], y[j + 1], z[j + 1]], dtype=x.dtype) node3 = np.array([x[j + 2], y[j + 2], z[j + 2]], dtype=x.dtype) - if coords_type == "spherical": - node1 = _lonlat_rad_to_xyz(np.deg2rad(x[0]), np.deg2rad(y[0])) - node1 = np.asarray(node1) - - node2 = _lonlat_rad_to_xyz(np.deg2rad(x[j + 1]), np.deg2rad(y[j + 1])) - node2 = np.asarray(node2) - - node3 = _lonlat_rad_to_xyz(np.deg2rad(x[j + 2]), np.deg2rad(y[j + 2])) - node3 = np.asarray(node3) - for p in range(len(dW)): if quadrature_rule == "gaussian": for q in range(len(dW)): @@ -92,6 +104,28 @@ def calculate_face_area( area += dW[p] * jacobian jacobian += jacobian + # check if the any edge is on the line of constant latitude + # which means we need to check edges for same z-coordinates and call area correction routine + correction = 0.0 + if correct_area: + for i in range(num_nodes): + node1 = np.array([x[i], y[i], z[i]], dtype=x.dtype) + node2 = np.array( + [ + x[(i + 1) % num_nodes], + y[(i + 1) % num_nodes], + z[(i + 1) % num_nodes], + ], + dtype=x.dtype, + ) + if np.isclose( + node1[2], node2[2] + ): # Check if z-coordinates are approximately equal + correction += area_correction(node1, node2) + + # TODO: Fix sign of the calculated correction + area += correction + return area, jacobian @@ -141,6 +175,10 @@ def get_all_face_area_from_coords( ------- area of all faces : ndarray """ + # this casting helps to prevent the type mismatch + x = np.asarray(x, dtype=np.float64) + y = np.asarray(y, dtype=np.float64) + z = np.asarray(z, dtype=np.float64) n_face, n_max_face_nodes = face_nodes.shape @@ -170,6 +208,55 @@ def get_all_face_area_from_coords( return area, jacobian +@njit(cache=True) +def area_correction(node1, node2): + """ + Calculate the area correction A using the given formula. + + Parameters: + - node1: first node of the edge (normalized). + - node2: second node of the edge (normalized). + - z: z-coordinate (shared by both points and part of the formula, normalized). + + Returns: + - A: correction term of the area, when one of the edges is a line of constant latitude + """ + x1 = node1[0] + y1 = node1[1] + x2 = node2[0] + y2 = node2[1] + z = node1[2] + + # Calculate terms + term1 = x1 * y2 - x2 * y1 + den1 = x1**2 + y1**2 + x1 * x2 + y1 * y2 + den2 = x1 * x2 + y1 * y2 + + # Helper function to handle arctan quadrants + def arctan_quad(y, x): + if x > 0: + return np.arctan(y / x) + elif x < 0 and y >= 0: + return np.arctan(y / x) + np.pi + elif x < 0 and y < 0: + return np.arctan(y / x) - np.pi + elif x == 0 and y > 0: + return np.pi / 2 + elif x == 0 and y < 0: + return -np.pi / 2 + else: + return 0 # x == 0 and y == 0 case + + # Compute angles using arctan + angle1 = arctan_quad(z * term1, den1) + angle2 = arctan_quad(term1, den2) + + # Compute A + A = abs(2 * angle1 - z * angle2) + print(x1, y1, x2, y2, z, "correction:", A) + return A + + @njit(cache=True) def calculate_spherical_triangle_jacobian(node1, node2, node3, dA, dB): """Calculate Jacobian of a spherical triangle. This is a helper function From ff9dc40478d53b43c43176040544d17ce54bd75e Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Tue, 7 Jan 2025 17:58:38 -0600 Subject: [PATCH 02/10] o Add test and notebook to showcase area correction --- docs/user-guide/area_calc.ipynb | 1856 ++++++++++++++++++++++++++++++- test/constants.py | 1 + test/test_grid.py | 11 +- uxarray/grid/area.py | 67 +- uxarray/grid/grid.py | 11 +- 5 files changed, 1906 insertions(+), 40 deletions(-) diff --git a/docs/user-guide/area_calc.ipynb b/docs/user-guide/area_calc.ipynb index 49e4d1ace..ba1ba6c07 100644 --- a/docs/user-guide/area_calc.ipynb +++ b/docs/user-guide/area_calc.ipynb @@ -22,12 +22,13 @@ "2. Options for `Grid.calculate_total_face_area` Function\n", "3. Getting Area of Individual Faces\n", "4. Calculate Area of a Single Triangle in Cartesian Coordinates\n", - "5. Calculate Area from Multiple Faces in Spherical Coordinates" + "5. Calculate Area from Multiple Faces in Spherical Coordinates\n", + "6. Demonstrate Area Correction at Line of Constant Lattitude" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": { "collapsed": false, "jupyter": { @@ -49,14 +50,434 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: UGRID\n",
+       "Grid Dimensions:\n",
+       "  * n_node: 5402\n",
+       "  * n_face: 5400\n",
+       "  * n_max_face_nodes: 4\n",
+       "  * n_nodes_per_face: (5400,)\n",
+       "Grid Coordinates (Spherical):\n",
+       "  * node_lon: (5402,)\n",
+       "  * node_lat: (5402,)\n",
+       "Grid Coordinates (Cartesian):\n",
+       "Grid Connectivity Variables:\n",
+       "  * face_node_connectivity: (5400, 4)\n",
+       "Grid Descriptor Variables:\n",
+       "  * n_nodes_per_face: (5400,)\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: UGRID\n", + "Grid Dimensions:\n", + " * n_node: 5402\n", + " * n_face: 5400\n", + " * n_max_face_nodes: 4\n", + " * n_nodes_per_face: (5400,)\n", + "Grid Coordinates (Spherical):\n", + " * node_lon: (5402,)\n", + " * node_lat: (5402,)\n", + "Grid Coordinates (Cartesian):\n", + "Grid Connectivity Variables:\n", + " * face_node_connectivity: (5400, 4)\n", + "Grid Descriptor Variables:\n", + " * n_nodes_per_face: (5400,)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "base_path = \"../../test/meshfiles/\"\n", "grid_path = base_path + \"/ugrid/outCSne30/outCSne30.ug\"\n", @@ -75,14 +496,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(12.566370614678554)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "t4_area = ugrid.calculate_total_face_area()\n", "t4_area" @@ -106,9 +538,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(12.571403993719983)" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "t1_area = ugrid.calculate_total_face_area(quadrature_rule=\"triangular\", order=1)\n", "t1_area" @@ -139,14 +582,411 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'face_areas' (n_face: 5400)> Size: 43kB\n",
+       "array([0.00211174, 0.00211221, 0.00210723, ..., 0.00210723, 0.00211221,\n",
+       "       0.00211174])\n",
+       "Dimensions without coordinates: n_face\n",
+       "Attributes:\n",
+       "    cf_role:    face_areas\n",
+       "    long_name:  Area of each face.
" + ], + "text/plain": [ + " Size: 43kB\n", + "array([0.00211174, 0.00211221, 0.00210723, ..., 0.00210723, 0.00211221,\n", + " 0.00211174])\n", + "Dimensions without coordinates: n_face\n", + "Attributes:\n", + " cf_role: face_areas\n", + " long_name: Area of each face." + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ugrid.face_areas" ] @@ -160,9 +1000,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(12.566370614359112)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "all_face_areas, all_face_jacobians = ugrid.compute_face_areas(\n", " quadrature_rule=\"gaussian\", order=4\n", @@ -180,9 +1031,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(np.float64(0.005033379360810386),\n", + " np.float64(3.1938185429680743e-10),\n", + " np.float64(6.039613253960852e-14))" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "actual_area = 4 * np.pi\n", "diff_t4_area = np.abs(t4_area - actual_area)\n", @@ -212,14 +1076,429 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: Face Vertices\n",
+       "Grid Dimensions:\n",
+       "  * n_node: 3\n",
+       "  * n_face: 1\n",
+       "  * n_max_face_nodes: 3\n",
+       "  * n_nodes_per_face: (1,)\n",
+       "Grid Coordinates (Spherical):\n",
+       "Grid Coordinates (Cartesian):\n",
+       "  * node_x: (3,)\n",
+       "  * node_y: (3,)\n",
+       "  * node_z: (3,)\n",
+       "Grid Connectivity Variables:\n",
+       "  * face_node_connectivity: (1, 3)\n",
+       "Grid Descriptor Variables:\n",
+       "  * n_nodes_per_face: (1,)\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: Face Vertices\n", + "Grid Dimensions:\n", + " * n_node: 3\n", + " * n_face: 1\n", + " * n_max_face_nodes: 3\n", + " * n_nodes_per_face: (1,)\n", + "Grid Coordinates (Spherical):\n", + "Grid Coordinates (Cartesian):\n", + " * node_x: (3,)\n", + " * node_y: (3,)\n", + " * node_z: (3,)\n", + "Grid Connectivity Variables:\n", + " * face_node_connectivity: (1, 3)\n", + "Grid Descriptor Variables:\n", + " * n_nodes_per_face: (1,)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "verts = [\n", " [\n", @@ -237,14 +1516,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(1.0719419938548218)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "vgrid.calculate_total_face_area()" ] @@ -258,14 +1548,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(1.0475702709086991)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Calculate the area of the triangle\n", "area_gaussian = vgrid.calculate_total_face_area(quadrature_rule=\"gaussian\", order=5)\n", @@ -288,7 +1589,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": { "collapsed": false, "jupyter": { @@ -342,14 +1643,430 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<uxarray.Grid>\n",
+       "Original Grid Type: Face Vertices\n",
+       "Grid Dimensions:\n",
+       "  * n_node: 14\n",
+       "  * n_face: 3\n",
+       "  * n_max_face_nodes: 6\n",
+       "  * n_nodes_per_face: (3,)\n",
+       "Grid Coordinates (Spherical):\n",
+       "  * node_lon: (14,)\n",
+       "  * node_lat: (14,)\n",
+       "Grid Coordinates (Cartesian):\n",
+       "Grid Connectivity Variables:\n",
+       "  * face_node_connectivity: (3, 6)\n",
+       "Grid Descriptor Variables:\n",
+       "  * n_nodes_per_face: (3,)\n",
+       "
" + ], + "text/plain": [ + "\n", + "Original Grid Type: Face Vertices\n", + "Grid Dimensions:\n", + " * n_node: 14\n", + " * n_face: 3\n", + " * n_max_face_nodes: 6\n", + " * n_nodes_per_face: (3,)\n", + "Grid Coordinates (Spherical):\n", + " * node_lon: (14,)\n", + " * node_lat: (14,)\n", + "Grid Coordinates (Cartesian):\n", + "Grid Connectivity Variables:\n", + " * face_node_connectivity: (3, 6)\n", + "Grid Descriptor Variables:\n", + " * n_nodes_per_face: (3,)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "verts_grid = ux.open_grid(faces_verts_ndarray, latlon=True)\n", "\n", @@ -358,23 +2075,108 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.14323746, 0.25118746, 0.12141312])" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "area, jacobian = verts_grid.compute_face_areas()\n", "area" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Area Correction\n", + "\n", + "For the same big triangle as used above calculate area correction term at the line of constant latitude." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.04757027])" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "area, jacobian = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5)\n", + "area" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The above code shows area calculation withou any correction term. The correction term is calculated below." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Check if edge passes through pole: False\n", + "0.577350269522959 -0.5773502685229592 0.5773502695229592 0.577350268522959 -0.5773502695229591 correction: 0.1402978685558901\n", + "For Node 1 [ 0.57735027 -0.57735027 -0.57735027] \n", + " and Node 2 [ 0.57735027 0.57735027 -0.57735027] \n", + "CORRECTION 0.1402978685558901\n", + "Check if edge passes through pole: False\n", + "0.5773502695229592 0.577350268522959 -0.5773502695229594 0.5773502685229589 -0.5773502695229591 correction: 0.14029786955589052\n", + "For Node 1 [ 0.57735027 0.57735027 -0.57735027] \n", + " and Node 2 [-0.57735027 0.57735027 -0.57735027] \n", + "CORRECTION 0.14029786955589052\n", + "Check if edge passes through pole: True\n", + "AREA Before Correction 1.0475702709086991\n", + "AREA After Correction 1.3281660100204802\n" + ] + } + ], + "source": [ + "# Continuing with our example from above\n", + "area = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5, correct_area=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Two edges of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above " + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "uxarray_env3.12", "language": "python", "name": "python3" }, @@ -388,7 +2190,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.7" } }, "nbformat": 4, diff --git a/test/constants.py b/test/constants.py index 4a54e1b1a..eeb23ecf3 100644 --- a/test/constants.py +++ b/test/constants.py @@ -7,6 +7,7 @@ NNODES_outRLL1deg = 64442 DATAVARS_outCSne30 = 4 TRI_AREA = 1.047 +CORRECTED_TRI_AREA = 1.3281 # 4*Pi is 12.56 MESH30_AREA = 12.566 PSI_INTG = 12.566 diff --git a/test/test_grid.py b/test/test_grid.py index 4c8d98c76..8300b7590 100644 --- a/test/test_grid.py +++ b/test/test_grid.py @@ -266,15 +266,16 @@ def test_face_areas_calculate_total_face_area_triangle(): # validate the grid assert grid_verts.validate() - # calculate area - area_gaussian = grid_verts.calculate_total_face_area( - quadrature_rule="gaussian", order=5) - nt.assert_almost_equal(area_gaussian, constants.TRI_AREA, decimal=3) - + # calculate area without correction area_triangular = grid_verts.calculate_total_face_area( quadrature_rule="triangular", order=4) nt.assert_almost_equal(area_triangular, constants.TRI_AREA, decimal=1) + # calculate area + area_gaussian = grid_verts.calculate_total_face_area( + quadrature_rule="gaussian", order=5, correct_area=True) + nt.assert_almost_equal(area_gaussian, constants.CORRECTED_TRI_AREA, decimal=3) + def test_face_areas_calculate_total_face_area_file(): """Create a uxarray grid from vertices and saves an exodus file.""" area = ux.open_grid(gridfile_CSne30).calculate_total_face_area() diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index de31300d5..72bed817b 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -118,17 +118,71 @@ def calculate_face_area( ], dtype=x.dtype, ) - if np.isclose( - node1[2], node2[2] - ): # Check if z-coordinates are approximately equal - correction += area_correction(node1, node2) + # Check if z-coordinates are approximately equal + if np.isclose(node1[2], node2[2]): + # Check if the edge passes through a pole + passes_through_pole = edge_passes_through_pole(node1, node2) + print("Check if edge passes through pole: ", passes_through_pole) + + if passes_through_pole: + # Skip the edge if it passes through a pole + continue + else: + # Calculate the correction term + correction = area_correction(node1, node2) + print( + "For Node 1 ", + node1, + "\n and Node 2", + node2, + "\nCORRECTION", + correction, + ) + correction += correction + + if correction != 0.0: + print("AREA Before Correction", area) - # TODO: Fix sign of the calculated correction + # TODO: Fix sign of the calculated correction? area += correction + if correction != 0.0: + print("AREA After Correction", area) + return area, jacobian +@njit(cache=True) +def edge_passes_through_pole(node1, node2): + """ + Check if the edge passes through a pole. + + Parameters: + - node1: first node of the edge (normalized). + - node2: second node of the edge (normalized). + + Returns: + - bool: True if the edge passes through a pole, False otherwise. + """ + # Calculate the normal vector to the plane defined by the origin, node1, and node2 + n = np.cross(node1, node2) + + # Check for numerical stability issues with the normal vector + if np.allclose(n, 0): + # Handle cases where the cross product is near zero, such as when nodes are nearly identical or opposite + return False # Or handle it according to your specific needs + + # Normalize the normal vector + n = n / np.linalg.norm(n) + + # North and South Pole vectors (make them float64) + p_north = np.array([0.0, 0.0, 1.0]) # Changed to float64 + p_south = np.array([0.0, 0.0, -1.0]) # Changed to float64 + + # Check if the normal vector is orthogonal to either pole + return np.isclose(np.dot(n, p_north), 0) or np.isclose(np.dot(n, p_south), 0) + + @njit(cache=True) def get_all_face_area_from_coords( x, @@ -140,6 +194,7 @@ def get_all_face_area_from_coords( quadrature_rule="triangular", order=4, coords_type="spherical", + correct_area=False, ): """Given coords, connectivity and other area calculation params, this routine loop over all faces and return an numpy array with areas of each @@ -199,7 +254,7 @@ def get_all_face_area_from_coords( # After getting all the nodes of a face assembled call the cal. face area routine face_area, face_jacobian = calculate_face_area( - face_x, face_y, face_z, quadrature_rule, order, coords_type + face_x, face_y, face_z, quadrature_rule, order, coords_type, correct_area ) # store current face area area[face_idx] = face_area diff --git a/uxarray/grid/grid.py b/uxarray/grid/grid.py index e1bac7d24..b4baf988d 100644 --- a/uxarray/grid/grid.py +++ b/uxarray/grid/grid.py @@ -1726,7 +1726,10 @@ def encode_as(self, grid_type: str) -> xr.Dataset: return out_ds def calculate_total_face_area( - self, quadrature_rule: Optional[str] = "triangular", order: Optional[int] = 4 + self, + quadrature_rule: Optional[str] = "triangular", + order: Optional[int] = 4, + correct_area: Optional[bool] = False, ) -> float: """Function to calculate the total surface area of all the faces in a mesh. @@ -1744,7 +1747,9 @@ def calculate_total_face_area( """ # call function to get area of all the faces as a np array - face_areas, face_jacobian = self.compute_face_areas(quadrature_rule, order) + face_areas, face_jacobian = self.compute_face_areas( + quadrature_rule, order, correct_area=correct_area + ) return np.sum(face_areas) @@ -1753,6 +1758,7 @@ def compute_face_areas( quadrature_rule: Optional[str] = "triangular", order: Optional[int] = 4, latlon: Optional[bool] = True, + correct_area: Optional[bool] = False, ): """Face areas calculation function for grid class, calculates area of all faces in the grid. @@ -1820,6 +1826,7 @@ def compute_face_areas( quadrature_rule, order, coords_type, + correct_area, ) min_jacobian = np.min(self._face_jacobian) From f29164811e985dac115c66858fc59b1ae4ef337d Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Tue, 7 Jan 2025 23:26:55 -0600 Subject: [PATCH 03/10] o Add more to notebook, mathematical formulation of correction and plot of original triangle --- docs/user-guide/area_calc.ipynb | 173 +++++++++++++++++++++++--------- 1 file changed, 128 insertions(+), 45 deletions(-) diff --git a/docs/user-guide/area_calc.ipynb b/docs/user-guide/area_calc.ipynb index ba1ba6c07..55e4c7060 100644 --- a/docs/user-guide/area_calc.ipynb +++ b/docs/user-guide/area_calc.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 24, "metadata": { "collapsed": false, "jupyter": { @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 25, "metadata": { "collapsed": false, "jupyter": { @@ -446,14 +446,14 @@ " * face_node_connectivity: (5400, 4)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (5400,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      4 4 4 4 4 4 4 4 ... 4 4 4 4 4 4 4 4
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([4, 4, 4, ..., 4, 4, 4])
  • " ], "text/plain": [ "\n", @@ -473,7 +473,7 @@ " * n_nodes_per_face: (5400,)" ] }, - "execution_count": 19, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -496,7 +496,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 26, "metadata": { "collapsed": false, "jupyter": { @@ -510,7 +510,7 @@ "np.float64(12.566370614678554)" ] }, - "execution_count": 20, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -538,7 +538,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -547,7 +547,7 @@ "np.float64(12.571403993719983)" ] }, - "execution_count": 21, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -582,7 +582,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 28, "metadata": { "collapsed": false, "jupyter": { @@ -969,8 +969,8 @@ "Dimensions without coordinates: n_face\n", "Attributes:\n", " cf_role: face_areas\n", - " long_name: Area of each face." + " long_name: Area of each face." ], "text/plain": [ " Size: 43kB\n", @@ -982,7 +982,7 @@ " long_name: Area of each face." ] }, - "execution_count": 22, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -1000,7 +1000,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -1009,7 +1009,7 @@ "np.float64(12.566370614359112)" ] }, - "execution_count": 23, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +1031,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -1042,7 +1042,7 @@ " np.float64(6.039613253960852e-14))" ] }, - "execution_count": 24, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -1076,7 +1076,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 31, "metadata": { "collapsed": false, "jupyter": { @@ -1473,7 +1473,7 @@ " * face_node_connectivity: (1, 3)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (1,)\n", - "" + "" ], "text/plain": [ "\n", @@ -1494,7 +1494,7 @@ " * n_nodes_per_face: (1,)" ] }, - "execution_count": 25, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -1516,7 +1516,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 32, "metadata": { "collapsed": false, "jupyter": { @@ -1530,7 +1530,7 @@ "np.float64(1.0719419938548218)" ] }, - "execution_count": 26, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1548,7 +1548,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 33, "metadata": { "collapsed": false, "jupyter": { @@ -1562,7 +1562,7 @@ "np.float64(1.0475702709086991)" ] }, - "execution_count": 27, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1589,7 +1589,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 34, "metadata": { "collapsed": false, "jupyter": { @@ -1643,7 +1643,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 35, "metadata": { "collapsed": false, "jupyter": { @@ -2039,10 +2039,10 @@ " * face_node_connectivity: (3, 6)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (3,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      6 6 6
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([6, 6, 6])
  • " ], "text/plain": [ "\n", @@ -2062,7 +2062,7 @@ " * n_nodes_per_face: (3,)" ] }, - "execution_count": 29, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -2075,7 +2075,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 36, "metadata": { "collapsed": false, "jupyter": { @@ -2089,7 +2089,7 @@ "array([0.14323746, 0.25118746, 0.12141312])" ] }, - "execution_count": 30, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -2105,12 +2105,94 @@ "source": [ "## 6. Area Correction\n", "\n", - "For the same big triangle as used above calculate area correction term at the line of constant latitude." + "### Correction Formula\n", + "\n", + "The correction, $A$, is calculated using the following formula:\n", + "\n", + "\\begin{align}\n", + "A = 2 \\arctan \\left[ \\frac{z (x_1 y_2 - x_2 y_1)}{x_1^2 + y_1^2 + x_1 x_2 + y_1 y_2} \\right] - z \\arctan \\left[ \\frac{x_1 y_2 - x_2 y_1}{x_1 x_2 + y_1 y_2} \\right].\n", + "\\end{align}\n", + "\n", + "**Where:**\n", + "\n", + "- $(x_1, y_1, z)$ are the Cartesian coordinates of the first node.\n", + "- $(x_2, y_2, z)$ are the Cartesian coordinates of the second node (note that the $z$ coordinate is the same for both nodes).\n", + "\n", + "**Assumptions:**\n", + "\n", + "- This formula assumes that the input coordinates $(x_1, y_1)$ and $(x_2, y_2)$ are normalized (i.e., they lie on the unit sphere).\n", + " \n", + "\n", + "#### For the same big triangle as used in Section 4, we calculate area correction term when an edge is at the line of constant latitude. The code below plots that triangle and marks the edges with different colors, highlights the edge at the line of constant latitude and also marks the coordinates of the vertices." ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdAAAAG3CAYAAAAErATiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd1gUVxfG36X3oiAoCioWxAIqFuy9R2Os0dhjSazRGE2M3agx1diNEnuJsYGiiBQlKCJ2EUFEighIld523+8Pwnyu9A46v+fZR5m5c+bM7N05c+89RUKSEBERERERESkRClWtgIiIiIiISE1ENKAiIiIiIiKlQDSgIiIiIiIipUA0oCIiIiIiIqVANKAiIiIiIiKlQDSgIiIiIiIipUA0oCIiIiIiIqVANKAiIiIiIiKlQDSgIiIiIiIipUA0oCIiIiIiIqVANKAi7yXh4eH47LPPULt2bairq6N169bw8fER9pPEqlWrULduXairq6Nfv3549uyZnIybN2/C2toaDRs2xP79+4XtnTt3xpw5c+Ta7t69GxKJBAcOHJDbPnXqVHTv3r1YOu/YsQMNGzaEmpoaOnXqBG9vb2Gfv78/unbtivr162PDhg3FvQ1yVOQ9AQCJRJLv58SJE6XSV0Sk2kMRkfeMuLg4mpmZcerUqbx16xaDgoLo5OTEwMBAoc3mzZupq6vLc+fO8cGDBxw+fDgbNWrEtLQ0oU2LFi34zz//0NPTk+bm5gwNDSVJLl++nM2bN5c759ixY9mgQQNOmTJFbruZmRlXrVpVpM4nTpygiooK7ezs6Ovry5kzZ1JPT49RUVEkyX79+nHnzp308fGhjY0NPT09q9U9IUkA/OuvvxgRESH3eft4EZH3CdGAirx3LFu2jN26dStwv0wmo7GxMX/66SdhW0JCAlVVVXn8+HFhm6mpKYOCgpicnEwbGxv6+vqSJJ2cnAiAERERQlsjIyPu2LGDZmZmwragoCACoJubW5E6d+zYkXPnzhX+lkqlrFevHjdt2kSSbN++PW/dusXMzEwOHz6cFy9eLFLm21T0PSFzDOjZs2dLpJeISE1GnMIVee+wt7eHjY0NxowZgzp16qBt27b4888/hf0vXrxAZGQk+vXrJ2zT1dVFp06dcPPmTWHbqlWr0KJFC+jq6qJz586wtLQEAHTt2hXKyspwc3MDADx58gRpaWmYMWMGYmNj8eLFCwCAm5sb1NTUYGtrW6i+mZmZuHPnjpw+CgoK6Nevn6DPunXr0K9fP2hoaEBBQQEDBw6sVvdERORDRDSgIu8dQUFB2LVrF5o2bQonJyd88cUXWLBgAQ4ePAgAiIyMBAAYGRnJHWdkZCTsAyAYxOjoaGzbtk3YrqmpiY4dO8Ld3R0A4O7ujm7dukFVVRVdunSR225rawtVVdVC9Y2JiYFUKi1UnyFDhiA6OhqvXr3C2bNnoaioWK3uSS6ffvoptLS05D6hoaEl0lVEpKagVNUKiIiUNzKZDDY2Nti4cSMAoG3btnj8+DF2796NKVOmlEiWpqYmNDU182zv1asXTp06BSDHUPbq1QsA0LNnT7i7u2PatGlwd3fHzJkzy3Yxb6GqqgpDQ8NSHVsZ9wQAfvvtN7lRLADUq1evVDqLiFR3xBGoyHtH3bp180wttmjRQhgJGRsbAwCioqLk2kRFRQn7iqJ3794ICAhAeHg43N3d0bNnTwD/N6DPnz9HWFgY+vTpU6QsAwMDKCoqlkmfoqiMe5Irp0mTJnIfJSXxPV3k/UQ0oCLvHV27doW/v7/ctoCAAJiZmQEAGjVqBGNjY7i4uAj7ExMTcevWrSLXK3Pp0qULVFRUsHPnTqSnp6N9+/YAgA4dOiA6Ohp2dnbCVG9RqKiooH379nL6yGQyuLi4FFufoqiMeyIi8sFR1V5MIiLljbe3N5WUlPjDDz/w2bNnPHr0KDU0NHjkyBGhzebNm6mnp8fz58/z4cOHHDFiRJ6QjaLo0aMHtbW1OWjQILntvXv3pra2NgcMGFBsWSdOnKCqqioPHDjAJ0+ecNasWdTT02NkZGSxZRRGZdwTFBDGkpycXC7XICJS3RANqMh7iYODA1u1akVVVVVaWFhw7969cvtlMhlXrlxJIyMjqqqqsm/fvvT39y/ROVavXk0A3Lx5s9z2NWvWEIAQglJctm3bRlNTU6qoqLBjx4708vIq0fFFUdH3BEC+n5LeBxGRmoKEJKts+CsiIiIiIlJDEddARURERERESoFoQEVEREREREqBaEBFRERERERKgWhARURERERESoEY4SwiUomQRGpqKpKSkpCYmJjvv2//Py0tDZqamtDW1oa2tjZ0dHTy/Tf3/2pqapBIJFV9mSIiHwSiF66ISBmQyWQICwuDn58fnj59iqCgICQmJuLNmzfCJ9coJicnIzU1FTKZDEBOAgVdXd18DeHbBjE1NTVf4/r2v1lZWQAARUXFAo2rtrY29PT0YG5ujhYtWsDCwgJ169YVDa6ISCkRDaiISDHIyMjAs2fPBEP56NEjPH78GEFBQcjMzIS6ujpIIi0trVTyp02bBjs7uzLpV9Ao9u1tcXFxCAwMhJ+fH4KDg6GtrQ0LCwvBoOb+a25uLqbgExEpAtGAioi8RUJCgmAknzx5gkePHsHX1xfh4eFQVFSEqqoqZDIZ1NTUYGZmhpYtW6JJkyaws7ODsrIywsPDMXnyZEycOBENGzZE3bp1oaioiLVr12L37t2Ijo7O97w9evTAtWvXKvVaU1NT5V4Kcv/19/eHTCZDkyZN0KJFCznj2rx5c2hpaVWqniIi1RXRgIp8kMhkMjx69AjXr1/H48eP8fDhQzx9+hQJCQlQV1dHrVq18Pr1a2hra6N169YYMmQIOnXqhCZNmqBu3bpQUMjrf5eRkYGQkBA0bNgQKioqcvsOHjyIxYsXIy4uDgCgpKQEBwcHtGzZEiYmJvnKK2+SkpIQHR0NU1PTQkeXUqkUwcHBglF9/PgxHj16hKCgICQkJKBBgwaCQW3ZsiV69eqFpk2bilPBIh8cogEV+WAICwvD1atX4eTkhCtXriApKQmqqqrQ1tZGs2bN0KdPH5ibm0NHRwf6+vqwtraGtrZ2kXJTUlLw7NkzvHz5Evr6+rCysoKWlhauXbuGBQsWICsrC35+frCyssLZs2fRqFGjYukrlUrx7NkzNG3atMT1P/MjLi4ODx8+REpKCho2bAhzc3OoqakVeVxKSgoCAwMRHR2N8PBwJCQkIDY2FpGRkTh79izi4+NhZGSEgQMHYsCAAejbt2+py66JiNQkRAMq8t7y5s0buLu7w9nZGVevXkVgYCAsLS3x4sULWFlZoXfv3rCysoKioiLMzMxQt25d1K5dO8/osSBSUlLg7++P8PBw1KtXD02bNoWOjg4AIDw8HJ07d8bLly+hra2NKVOmYPny5TAxMSm2/llZWXB0dMSQIUOgrKxcqnvwLiSFddDo6GiYmZmhadOmxTKkAJCeno6YmBhER0cjMjISb968wZ07d+Dm5obIyEjUrl0bjx8/Rps2bdCvXz/0798f3bt3h7q6ernoLyJSnRANqMh7Q1ZWFry8vHD16lU4OzvD29sbTZo0Qd++fdG1a1eYmJjg1atXUFZWxqNHj/D8+XNs2LABDRs2LNF5MjMz4e/vj+DgYJiYmKBZs2bCumBQUBDWrl2LQ4cOAQD69OkDe3v7AgtQF3U95W1A3yYhIQH+/v6Ijo5G06ZNS+w4RBJr167Fo0eP0L59e1hYWCA0NFR4ifD09ISzszNev36Nrl27on///ujXrx/atm1bLiNqEZGqRjSgIjUWkvDz84OzszOcnZ1x7do1qKurCyOfTp06QSKRIDg4GKmpqbh9+zZ8fHzQrFkzLF++HKampiU+X3BwMPz8/KCvr4+WLVtCR0cH4eHhWL58OTIzM/H3338L7RcsWICtW7eW+voq2oDmEhsbi8ePHyM9PR2tW7cuUWgLSdy9exf79u3D8ePH0aFDB9ja2sLKygoKCgqwtLRERkYGPDw8cPXqVbi6ukJBQQG9e/fGoEGD0L9//2JPaYuIVDdEAypSo4iOjoaTkxMuXryIK1euIDExEY0bN8bMmTPRv39/NG3aFBEREXjx4gWSk5MREhKC06dPIygoCHPnzsXs2bNRq1atEp/3zZs3uH//PjIzM9GmTRsYGRnBzs4OM2bMENpoaGhg+PDh6NChAxYsWFDmMJDKMqBAjiEMCwuDr68v9PT00KZNmxKPmv39/fHLL7/gzz//hIaGBjp16oSPPvoIjRo1gr6+PszMzHDt2jVMnToVAKCpqYm0tDTUqVMHQ4YMwZAhQ9C/f39hBCsiUt0RDahItSc7OxtOTk6ws7PDhQsXoKysjJSUFACArq4uYmJiEBsbi5CQEERGRiIrKwu7d+/GzZs30aZNG4waNQpz5swp1TSqTCZDQEAAAgMDYW5ujmbNmmHv3r2QSCQgiS+//BIAMG7cONjZ2UFDQ6PcrruyDChJ/Pvvv0hLS0NGRgbevHkDLS0tmJiYoH379iX2EHZ1dUXfvn2Fv/X09NCtWzcMGzYMderUQUZGBv7880+4uroKbRQUFKCmpobs7GyMHz8eM2bMQPfu3UXPXpFqjWhARaotz549g52dHQ4dOgSJRII+ffrg8OHDwv6EhAREREQgJCQE2dnZCAoKwp49e+Dr6wsAmDRpkrAWWRqSkpJw584dkETbtm2xcuVKbN++Xa6NsrIyLCwscP/+/XIPRalIA5qcnIzr168jKCgIT58+xY4dO4R9ysrKaNGiBebNm4dXr15h69atOHPmDHr27Flsg5aZmYkLFy7g7NmzuHTpEmJjYwEADRs2RL9+/TBw4EDo6urixYsXWLx4sfBCBORkU1JUVES9evUwc+ZMTJkypUTOVyIilUYFF+wWESkRycnJPHDgANu3b09FRUWqqqpy/fr17NGjBwGwbdu2fPz4MW/fvk17e3veuHGD8+bNo4KCAgEInzVr1jAzM7NUOshkMoaEhNDBwYGPHz9mdnY2g4OD5eTnfsaNG8egoKByvgs5ZGZm8ty5c6W+jvzIysri8uXLqaysTABUUVEhAM6fP5/BwcEMCwujVCplVFQUL126xMWLF/PAgQNs27Yt1dTU2KpVKy5atIinT5/mzZs3GRQUxLS0tCLPGxMTw8OHD3P16tXs3bs3b9y4wbCwMF6/fp1nz57l7Nmz2aBBgzz3V01NjYqKihw8eDBPnz7NjIyMcrsXIiJlRRyBilQ5JOHl5YU9e/bg2LFjUFJSypMSr0OHDvj2229Rq1YtJCcnw9TUFKqqqvDy8sKUKVPk2p49exYff/xxqXTJysqCt7c34uLikJaWBk9PT3h4eODBgweQyWQwMjJC9+7d0aVLF/Tv3x+tWrUq7WUXS5fyHIFGRUVhwoQJcHd3x/fff4+JEyeiadOmAFDgyJIk3NzcEB8fj2fPnsHf3x9Xr17Fy5cvhTaKiopo27Ytunfvju7du6Nbt25CHKibmxtOnTqF8ePHo1GjRnJJIx49eoQlS5agZcuWkEql6NatG54+fYqzZ8/i/v37eXRRV1eHqqoqpk6dihkzZlTovRcRKQ6iARWpMqKionDo0CHs2LED4eHhwvaFCxfil19+gZ6eHr755ht07twZ2dnZIIlGjRrhwYMH2L17N1xcXAAATZo0wciRIzFz5kzBIBQHqVQKZ2dnnDt3Du7u7lBSUsKMGTOQkZGBLVu2ID4+Ho0bNxaMQrdu3dC8efNKW5crTwPq6OiIyZMnQ1FRESdOnEDv3r1LdHxSUhK8vb2hoaGBdu3a4c2bN4iKikJkZCSeP38uvGiEhIQAACwsLNC3b1+5qWEA0NLSwrBhw7Bjxw50794dz58/R1ZWFmQyGXR0dDBo0CAMGTIE8fHxiI2NxcaNG4Xk+7moqKhAKpWibdu2+PzzzzF+/Hjo6uqW6f6IiJQG0YCKVCrZ2dm4dOkSdu3aBScnJygrK0MikSA9PR0jR47EpEmT8Mknn0BNTQ2nTp2CoqIi1NTU0LRpU7i5ueGbb75BZGQkunbtitmzZ2PYsGHQ19cvkQ4xMTGws7PD7t278eLFCzRp0gRjxoxBu3btkJ6eDm1tbZiamsLU1BS1a9euoDtRNOVlQIODg9GqVSv06NEDBw8eLHWWoKysLNy9exdJSUno1KmTXJamhIQEqKioIDY2Fh4eHvDw8MDFixcRFhZWqMzp06djxowZOHr0KGxsbDB16lQ8fPgQ2dnZQtpDY2Nj7Nu3D+3atYNUKpXzfNbW1kZ2djZGjRqFGTNmoEePHpWSFlFEBBANqEgl4e/vjz///BN2dnaCw0hmZqaw/8qVK4iNjcW0adMwePBgTJw4EYaGhmjevDl8fX2xceNGuLi4YOTIkVi3bl2xp+9IIjk5Gffu3YOLiwtcXFxw69YtKCoqYty4cfjyyy9hYmKC+/fvo2XLlnliEoOCgnD8+HGEh4eDJL7//ntoa2vjypUriIuLQ7t27XD79m04OTkhNDQU9+/fx5MnT2BhYVHme1YeBpQkBgwYgICAADx69KhEISIZGRnIzMyUM5T8L/Y2ODgYHTt2hIGBARwcHDB8+HChzYABA2BlZQULCwtERUXhu+++g4qKCmrVqoXIyMg858nN5PQuMpkMr169gr+/P6RSKZo1awZTU1MoKCjAw8MDPXr0ENo2aNAAaWlp0NHRwbRp0zB16lTUr1+/2NcqIlIqqmDdVeQDwtvbm71796aioiKVlJSoqqqarzOOsrIyR4wYwePHj/P06dOMjIykTCbj4cOHCYDt27fnqVOnKJVKCz3fixcvuGPHDs6dO5e9evVi7dq1hXPUqlWLo0aN4s6dOxkdHU2SfPbsGS9cuMCIiIg8smbMmCEca2xszDp16lBbW1twwMl1XFJSUmKPHj3YvXt3wSGnPCgPJ6LffvuNAOjk5FTsYx49esTOnTtTWVmZCgoKtLW15YYNG5iSkiK0efHiBR0cHBgWFsbXr1+zd+/e+X6vuZ+2bdtSS0uLrVq1ErYtX76cL168KFIfmUzGsLAwXr16lU5OTgwODqZUKqVMJuPff/8tyHvy5An/+ecfDhkyhMrKyvz000/59OnT0tw2EZFiIRpQkQrh7t27HDp0qODlmfsZM2YMdXR0hL8VFBTYt29fnjx5kpcuXRIMZ1ZWFrds2UIVFRVOnTqVMpms0PPduHGDgwcPpkQioZKSEi0tLTl69GiuW7eOx44d4507d+SMr0wmo6+vLx0dHRkfH59HXmhoqJzen3zyCV+/fs2vv/6a27ZtY1BQEN+8eUMPDw++efOGJHnnzh0qKiqyQYMGzM7OLvM9LKsBtbe3p4KCAr/++usSHffRRx+xXr163LZtG/fs2cNBgwYRAEeOHCnXLjIykg4ODoIRlMlkvHv3rvA9fPPNN9y+fTsB0MbGRriXs2fP5vPnz4v8Tt8l15BeuXKFLi4ufPXqlSDjXVnBwcGcNWsWVVVVOXnyZAYGBpboXCIixUE0oCLlysOHD/nJJ59QWVmZioqKwkPTx8eHXl5etLW1paqqKk+dOsUFCxbw7NmzdHJyYlhYmPAQDAwMpLW1NRUUFLho0aJCQxdSUlK4cuVKKigo0MrKivv372dycnKhOspkMt6/f59OTk5MTEzMt423t7ecAb106VKeNnFxcbx8+TLXrFnDQYMGUVFRkbVq1eLmzZtLcMcKprQGVCqVcufOndTQ0OAnn3xS5Kj9XebOnUsAdHNzY3R0NDt27EgAPHr0aJ62MTExvHjxIgMCAuTOv379ekokkgJHpLdu3SqRTu9e3/Pnz+no6Mjr168zNja2wLbPnz/ntGnTqKqqyhkzZjA4OLjU5xUReRfRgIqUC0+ePOG4ceOopqbG2bNnyz0sJ02axDVr1lBFRYVNmzbltWvXeOPGDV68eJGBgYFyD/inT5/S0NCQTZs25e3btws8X2RkJH/55RcaGxtTSUmJa9asYVZWVpF6ymQy3rt3j87OznJTkvmhpaUlXEO9evU4cOBAzpkzhxMmTGCzZs3kpoYHDx7Mbdu2FWm8S0JJDahUKqW7u7swlTxr1qwir/Fd3N3daWxszMaNGzM4OJjt2rVjnTp1eOfOnQKPSUhIoKOjY57pUhcXF06ZMkWuL9jb29PLy6tEOhVEZmYm/fz86ODgQB8fH6amphbYNiAggJ999hnV1NQ4Z84choWFlYsOIh82ogGtZF6+fMmJEyeyVq1aQmD624ZCJpNx5cqVNDY2ppqaGvv27Sv3dk/mTFdaWVnRzMyM+/btk9sHgKqqqnnetEeMGMEpU6aU+/W8+2Dy8/Nj+/bt84w4lJSUuHLlSt69e5f29vZ8+PBhnpGlVCpl165d2bRpU2GN8m2ysrJ46NAhDhgwgAoKClRWVubkyZP5/PnzYun6tvEs7GGby6tXr3j27Flu376dixcv5ogRI9imTRt27tyZc+fO5aFDhxgQEFDiqcjiUlwDevfuXS5evJgmJiYEwObNm9PNza1E55JKpVy4cCEBsHv37oyMjOSpU6cIgN7e3kUeX5ARzSU8PJzOzs4Vcq9SU1Pp4+NDBwcHPn36tNDp87df9ObPn89Xr16Vuz7k+/c7F8kf0YBWInFxcTQzM+PUqVN569YtBgUF0cnJSW59ZvPmzdTV1eW5c+f44MEDDh8+nI0aNZLL9tKiRQv+888/9PT0pLm5OUNDQ4V9+C97y+TJk+XOXd4/rOfPn3Pq1KlUVlYWnGr279/PjRs35jGeU6ZMoY+PDx0dHenp6SmsGb7Ljz/+SAB0d3fPs08qlXLSpEkEwB49enDPnj2MiYkptr6507bFNZ7VgaIMaHZ2NpcvX04ArFOnDufNm0dPT88ST9mSpI+PDwFw7dq1wvF//fUXAfD48ePFkpFrRJ89e1bi85cHMTExdHNz45UrVxgZGVlo29ylBnV1dS5evJhRUVHlpsf79DsXKRzRgFYiy5YtY7du3QrcL5PJaGxszJ9++knYlpCQQFVVVbmHmKmpKYOCgpicnEwbGxv6+voK+wDw66+/poKCAh89eiRsL68fVkhICD///HOqqKhQU1Mzz7rWwYMHBa/L0NBQRkdH899//+Xly5cZHh5e4Ahkx44dBMDvvvsuz77AwEAOGjSIEomk2A/zd/H19eXly5dLPKVZlRRlQM+cOUMAXL9+fbGmrwvD19eXAOjh4SFsS09P52effUYA3LlzZ7HkxMfH88KFC1W21iiTyRgUFMQLFy7Q29u7yJelu3fv8qOPPqKmpiaXLVuW78xHSXkffucixUM0oJVIixYtuGjRIo4ePZqGhoa0trbm3r17hf3Pnz8nAN67d0/uuB49enDBggXC3/v27aOqqioVFRU5b948ubYAePbsWQ4fPpxDhw4Vtpf1h/Xy5Ut+8cUXVFVVZa1atSiRSNizZ0/BeD5+/Fho++DBA6anp/PJkyd0cHDgw4cPC52GjI2NJQCqq6vz8OHDTEpKIpnzAF+7di1VVVVpampKe3v7Uun+7NkzOjo6FugwVF0pyoDm9pfTp0+X+VxxcXFUUlJiv3795EY6MpmM06ZNY+3atYu9vhsdHU0HBweGh4eXWa/SkpaWRh8fH164cKFYHr/e3t4cNGgQtbW1+f333zMuLq7U567Jv3ORkiEa0EpEVVWVqqqq/Pbbb3n37l3u2bOHampqPHDgAEnS09OTAPKsy4wZM4Zjx46V25acnJzvjzz3h+Xr60tFRUVev36dZOl/WBEREVy4cCHV1dWFZN8DBgzg1q1baWRkRAC0trbmrl27hIdUXFwcXVxc6Obmlm+IyLu8fPlSLtxl+/btdHZ2poWFBZWUlLhs2bJSO+eEhYXxwoULxdKjulGcNVBLS0tOmzatXM7n6OhIExMTampqcuHChcIoMigoiMrKytTX1+eECRP45MkTkjnG9enTp9y6dSvHjh3L/v37s3Pnzhw1ahRPnz5NBweHEk2zVwRRUVG8cuUKr1+/LryYFYanpyf79u1LHR0drlmzhgkJCSU+Z038nYuUjrJV/BUpETKZDDY2Nti4cSMAoG3btnj8+DF2796dJyF6UWhqahZa39LS0hKTJ0/G8uXL4enpWWJdMzMzsWXLFmzatAnm5uZQVlaGmpoaDh06hBs3bmD58uVQVVWFuro6/P39sXjxYkyaNAlBQUEIDg5GkyZN0LhxYygoKCArK6vQc9WpUwdJSUkgCSMjIyxduhQA0KlTJ5w8eRItWrQAgCLlvEtcXBzu3buHdu3aQVNTs8THVzW5+ham94gRI3D06FFkZGQUmMIuN5dsUSnu+vXrh/v372Pbtm3Yt28fjh07hrCwMNSvXx8+Pj44e/YsTpw4gc6dO8PS0hLPnz9HcnKycHy9evVgZWWFZ8+e4bPPPsPMmTMhkUjQpUsXaGlplfTyywV9fX1069YNAQEBcHNzQ7NmzdCoUaMC8xl36NABly5dwvXr17F27Vps3boVGzduxKxZs4qdIrAm/c5FyoZoQCuRunXrwtLSUm5bixYtcPr0aQA5OT+BnCTrdevWFdpERUXB2tq6xOdbu3YtmjVrhnPnzpXoOB8fH8yYMQPx8fHo27evXO5RABg0aBAGDRqU57irV68K/w8ICEBAQECJdT548KDc3y9evMCLFy9KLOdtfHx8ynR8VePs7Fzgvk6dOqFTp064fPlyuZ2vXbt22LlzJwDIybW2ti5xP8zOzsb169fLTbey8vTpUzx9+rRYbRcvXow7d+5gw4YNOHnyJP788080adKkyONqyu9cpOyIBrQS6dq1K/z9/eW2BQQEwMzMDADQqFEjGBsbw8XFRfghJSYm4tatW/jiiy9KfL4GDRpg3rx5+O6772Bubl5k+7S0NKxevRrbtm2DpqYm4uLiMGDAALRp0wbJycno1KlTnmN69OiBLVu2ICQkBObm5jA3N6/yZN5ZWVm4ceMG6tSpI4xeqxsZGRn49ddfkZ6eDqlUivT0dLRs2RLTpk0T2mRlZcHZ2Rn9+/cvNBfu4MGDkZiYiL1798LS0hISiQQymQw7d+7E2rVr0bRpUygoKMDf3x9nz55Ft27dCpQVHByMCRMmwNfXFzNnzsTPP/+cb7ukpCRYWloiOzsb3333HWxtbaGuro7AwEBkZ2fDzMwMNjY2IIm7d+8iKysLHTt2rPK+kZ2dDX9/f7x8+RKWlpaoX79+odV1hg4diiVLlmDlypWwsrLC2rVrsWjRIigpFfzorO6/c5FypKrnkD8kvL29qaSkxB9++IHPnj3j0aNHqaGhwSNHjghtNm/eTD09PZ4/f54PHz7kiBEj8ri3Fwb+WxvJJTY2lrq6ulRTUyt0bcTd3Z1NmjRhs2bNqKqqSmtra8HrTyqVCuuTurq6HDt2LBUVFfnJJ5/Q3d2dzs7OZXK6KE9kMhlv3rzJGzduVFh8ZlmJj49n9+7dqaamRnNzc2prawsJJ96muHGgN2/eZK1atQiAlpaW7Natm5AucfHixUxLS2N6ejr79+9PfX19+vv7Fyjr9u3bBHIKkhflwXr58mXhvCYmJgW2z8rKoqurK+/fv1+ovMokMjKSly5d4q1bt4pdpNvDw4PNmzdnhw4d+PDhwwLbVeffuUj5IhrQSsbBwYGtWrWiqqoqLSws5LzzyP8HWBsZGVFVVZV9+/Yt9IH3Lu/+sEgKsZn5/bDevHnDOXPmUFNTk506dSIAfvbZZ3nCPQ4cOMDz58/zxo0b1NTU5OzZs3nhwgXev3+/zCEU5Ymvry+vXr1apgTsFc2oUaOor69PT09PIRQF/8X16ejocPfu3SRLlokoIyODFy5c4OTJkzl+/Hhu2rRJSJfn4uLCzp07s3bt2jQ3Ny8wUQWZY+w0NTULTUcok8m4adMm1qpVi127duXnn39OAILe+ZGSkkJHR8dqlUovPT2dXl5evHz5crGdndLS0vjtt99SXV2dq1atYnp6er7tqtvvXKRiEA3oB8yFCxdYv359duzYkY0aNaKGhgbt7OwKHLk5OTmxdu3aXLVqFS9cuFClYQr5ERERQQcHhwITNVQH7t69SwD866+/SJJXrlxhq1atuGLFCi5atIgAaGdnR7JsyeRlMhlnzZoll7gfAC9fvsw6deqwZcuWBWbh6devH4cNG1ag7EePHgkvWg0bNqSamhrV1dVpY2NTqE6vX7+mg4NDtfKIlslkfP78OR0cHOjv71/sWYu7d+/S2tqalpaWvHnzZgVrKVJdEQ3oB0h0dDQnTpxIXV1dDho0iAoKCrSxsaGfn1++7VNTUzlu3DgaGxtz7969dHFxKdecr+VBcnIyL168WO1znG7evJkAePfuXbntYWFhbNGiBY2NjYV41bIY0Fu3bhEAP//8cx49epRbt24lAHbt2pUeHh6sV68emzRpIne//Pz8GBAQwL59+xZqQN3d3QmA169fZ3JyMletWsUxY8Zwy5YtRerl7+/PK1euFHvatLKIj4/nlStX6OnpWeCo8l0yMzO5ceNGamho8Kuvvqp2vwmRikc0oB8QMpmMx48fp4GBATt37sx69epRTU2NGzZsKPQhfezYMbZv355nz57lgwcPyqVUV3mSm0T9wYMHVa1KkSQnJ7NevXps06aNEE+ZlpbGRo0aEQB79uzJ169fkyybAb137x4B8Nq1ayRzSq3ljkLHjx/PRo0aUVlZmW3atKFUKuWDBw/kRqrLly8vUHZ2djYtLCzYq1evEveF3DXqW7duVbs16szMTHp7e/Py5cslWtP38/Nj165d2ahRI169erUCNRSpbogG9APh5cuXHDp0KGvXrs0WLVoQAIcOHVpkInaZTMYNGzbw5MmT1XZ09/jxY7q5uVU7w55LdHQ0b9y4wX///ZfZ2dmCkVJUVGRCQgKzs7O5bNkyjh8/nrVq1eKwYcMok8nKZECzs7Opq6vL2bNnkyStra1pYGAgVGrp2LGjkMO4X79+bNOmDfX19enq6srTp08XWiKMJK9evUqJRMK1a9eWWLf09HRevnyZQUFBJT62opHJZHz27BkdHBxKtF4rlUq5bds2amlp8fPPP69W09QiFYdoQN9zZDIZ9+zZQ3V1deGBaWlpycuXLxd5bGZmJr28vLh//342a9asTDUcK4qoqCg6ODhUyzR9SUlJXL16NTU0NASj2alTJw4bNkz4+8SJE3LJ1x0cHAiAv/76K9PT08tUUPuPP/4gAB44cICTJk2impoar169ysaNGxMANTU1aWlpyb59+3L48OF5Kn5IpVLevHmTy5cv59y5c7ljxw6GhYXR2dmZrVu3JgA2a9asVLrlrodW1/Xq169f09HRkffv3y9Rcv7g4GAOHDiQdevW5blz5ypQQ5HqgGhA32MCAwPZo0cPqqqqCg/sAwcOFMtrNjk5mS4uLvT09OTvv/9OALx48WIlaF18MjIyeOnSpWrl2UnmrBn/8ssvNDAwoKqqKr/55hs+fPiQzs7ONDIyooaGBlevXk09PT0CoEQiEdK8keT8+fMJgPXr1y+TASXJAQMGsHPnzoyJiREMeadOnTh//nxaWFiwWbNmHDFiBA8dOsSEhATKZDL6+PhwyZIlrF+/PgHQ0NCQlpaWBEAFBQU541+W8CVfX1+6urpW25mDlJQUurm50cPDo9jromTOS+uBAweor6/PsWPHFlkZRqTmIhrQ95S//vqLampqVFJSIgAOHjy42I4bMTExdHR05MOHD3nt2jUC4MSJE0tVJqsi8fb2ppeXV7VaS3v69Cnr1atHRUVFzpw5kyEhIcK+0NBQAuBXX30lbIuJieHnn39OiUQirInmrum2b9+e586dK5HDjVQqpVQq5alTp/jzzz+zXbt2QsJ5f39/wZko9zNw4EB27tyZAKinpyf839DQkF9++SXd3d0FAxcWFsbvvvuOX331VbkksJdKpXRzc5OrMlLdyMrK4q1bt+js7FziWY6IiAiOHj2atWrVKtaMj0jNQzSg7xlZWVlcsGAB1dTUhIfkDz/8UGwjExoaSgcHB2F96uuvvyYAXrp0qSLVLjFhYWF0dHQsduB5ZXHjxo0CK6Rs2rSJAAQDp66uTkNDQ6EQ9rtrzI6Ojjx37lyRU4GxsbHcu3cvBw0aJEzT478i5gDYpUsXuTW5Bw8e8NKlS4KzEpnzvX/xxRfs3bs3z507V2mxvW/evKGDg0ORa65ViUwmo6+vLy9evFiquqGHDh2ipqYmt2zZUq1e9kTKjoQkS5fDSKS6ERsbi9GjR8PLywvp6ekAgEuXLuWbt/ZdSMLf3x/Pnz9Hhw4dUKdOHdy/fx8DBgyAiYkJvL29C00nV5mkp6fD1dUV1tbWqFevXlWrI0d2djZ0dXXxzTffYPXq1cL21NRUDB48GP7+/oiMjESPHj2QnJyM0aNHIyEhAQ0aNMD8+fPlZGVmZuLSpUuYPXs2bG1tYWFhgZkzZ6Jx48ZCG6lUCmtrazx58gQ9e/bE0KFDoampiWbNmqF3797w9PREhw4doKqqWmn3oKQEBgYiODgYvXv3hqKiYlWrUyChoaF4+PAhWrduLaTlKy4+Pj4YOXIkevTogX379kFdXb2CtBSpVKrYgIuUEw8ePGCDBg2EsmCGhoYMDAws1rFSqZT37t3j5cuXBacOT09P6unpsX379lVekupdvL296e3tXdVq5EEmk3HhwoVyWWJy1xT79OlDTU1Nenh4MCsri927d6e2tjafPn1aoLxcL9ylS5dywIABlEgkXLNmjVybY8eOEQBv3LhRkZdWochkMl67dk2upmx15fXr17xw4QKfPn1a4tFkREQEu3btynbt2slN7YvUXKo2s7NIuXDq1CnY2tri5cuXyMzMxPjx4/HixYtiJZbOzs6Gt7c34uLi0KNHDzx58gSDBg1Ct27d0KpVK7i4uKB27dqVcBXFIyIiAtHR0WjdunVVq5KHdevWYevWrejduzdIYtGiRWjYsCFsbGzw8OFDXLx4EW3btoWpqSk8PDygoqKC2NjYIuVu2LABlpaWIClX5UMqlWL9+vUYPHgwbG1tK/LSKhSJRAJra2u8ePECCQkJVa1OoRgaGqJ79+4IDg7Gw4cPhVJxxcHY2Biurq6wsbGBjY0NPDw8KlBTkUqhqi24SOmRSqVcsWIF1dXVhXWvw4cPF/v4jIwMXrt2jZcvX+bPP/9Mc3NzQU7//v2rXbaYzMxMXrp0qdq+vS9dupT16tWjRCIREqzPnTtXLjevVCplw4YN5Rx5hg8fnq+83BFoWloaTUxMWK9ePWZkZPD58+fcv38/e/bsSQD08vKqzMusMPz8/Ojm5lbtnNXyIzU1lS4uLvTy8iqVF/GuXbuooaHBXbt2VYB2IpWFuAZaQ3nz5g0+++wzPH36FIGBgQBySiY1bdq0WMenpaXB2dkZT548wapVq5CdnY23u8KLFy/QsGHDilC91Dx8+BBJSUno0qVLoSWoqpqsrCxER0fD2Ng43/JdTk5OcuvS48ePx9atW1GnTh2kpaUhICAAT58+RXp6OvT09PDs2TNs3LgR6enp0NHRQVRUFCQSCTp06IC1a9cWa427JiCVSuHu7o6GDRvWiLJcmZmZ8PLygpKSEjp27FhoibP88PDwwKhRozBy5Ehs27YNKioqFaSpSIVRxQZcpBT4+/vTwsKCAwcOZFxcXIFvwI8ePeLff//No0eP0sPDg5mZmUxKSqKbmxsPHDjARYsWCUH1uZ+ff/65WsblxcfH08HBgUlJSVWtSqlJT0/nt99+K9zrpUuX8sSJE2zYsCElEgnNzMyE0SsAqqur89y5c9TQ0OBnn31GNTU1fvLJJ9UuIXt5krvGWFQptepCVlYW//33X16/fr1U8bohISFs164du3btKsaL1kDEEWgN49KlS/j0008xa9YsbNq0KV+vxejoaCxatAjHjh2T266urg49PT2sW7cOz549g7OzM+7duyfsf/LkSbUsQE0S169fr9YFsovDihUrsHHjxjzbe/TogbFjxyIoKAiGhobo0aMHLCwsoKamBmdnZwQHB+P48eO4detWtZwZKG/u3LkDmUyGDh06VLUqxUIqlcLHxwdpaWmwtbUtscdzamoqZs6cievXr+Ps2bOwsbGpIE1Fyp0qNuAixUQmk3Hz5s3U1NTk0aNH822TkpLC7du308DAgLq6ukJ5LAD8448/uHXrVp45c4ZHjhyhsbExFRUV5UafJ0+erOSrKh7BwcF0cnKqliPjkvDy5UvOnDmTn3/+Of39/ZmUlMTAwEBhzW///v0EwAEDBvCnn35i//79ee7cOWpra3Pw4MF56j++r6SlpfHChQsF1iytjkilUt6+fZsuLi4lylqUi0wm408//URNTc0S+TGIVC2iAa0BpKSkcPz48WzQoAHv3LmTZ79UKhXynr79OXjwoNz/HR0duW3bNkokEvbp04evXr0S6lPiv+Tm1S03aa7jUHWrPVoeJCQk8ObNm7xy5QqDg4P577//CiFIioqKHDZsGM+dO1eq4P2aTkBAAF1dXWtU4oGyGlGSvHz5MvX19bl48eJqVaheJH9KtuotUumEhITg448/hra2Nnx8fFCnTh25/S9fvkSzZs2QlpaW59hPP/0U4eHhSE1NhY6ODuzt7XHw4EFs3LgRS5cuhaKiIoyNjYX2HTp0gI6OToVfU0kICAiAtrY26tatW9WqlBseHh6YOHEiwsLC5LbnTv0tX74cc+fOhYKCAhwdHaGvr18ValYpjRs3RkhICIKDg9GoUaOqVqdYKCgooF27drh79y48PT3RtWvXEk/nDhw4EN7e3hgxYgQePXqEEydOoFat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", + "text/plain": [ + "
    " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline \n", + "# Convert the points to latitude and longitude\n", + "points = [(point[0] * 180 / 3.14159, point[1] * 180 / 3.14159) for vert in verts for point in vert]\n", + "\n", + "# Node names\n", + "node_names = ['Node 1', 'Node 2', 'Node 3']\n", + "\n", + "# Edge names and colors\n", + "edge_names = ['Edge 1', 'Edge 2', 'Edge 3']\n", + "edge_colors = ['red', 'green', 'blue']\n", + "\n", + "# Create a new figure\n", + "fig = plt.figure(figsize=(10, 5))\n", + "\n", + "# Create a GeoAxes in the Orthographic projection\n", + "ax = fig.add_subplot(1, 1, 1, projection=ccrs.Orthographic(central_longitude=0))\n", + "\n", + "# Add coastlines and gridlines for reference\n", + "ax.add_feature(cfeature.COASTLINE)\n", + "ax.gridlines(draw_labels=True)\n", + "\n", + "# Plot the points, edges, and labels\n", + "for i in range(len(points)):\n", + " lon1, lat1 = points[i]\n", + " lon2, lat2 = points[(i + 1) % len(points)]\n", + " \n", + " # Plot the edge\n", + " ax.plot([lon1, lon2], [lat1, lat2], color=edge_colors[i], transform=ccrs.Geodetic(), label=edge_names[i])\n", + " \n", + " # Add edge label (adjust the offset as needed)\n", + " ax.text((lon1 + lon2) / 2, (lat1 + lat2) / 2, edge_names[i], transform=ccrs.Geodetic(), color=edge_colors[i])\n", + " \n", + " # Add node label with Cartesian coordinates\n", + " cartesian_coords = verts[0][i] # Get Cartesian coordinates from verts\n", + " label = f'{node_names[i]}\\n[{cartesian_coords[0]:.2f}, {cartesian_coords[1]:.2f}, {cartesian_coords[2]:.2f}]'\n", + " ax.text(lon1, lat1, label, transform=ccrs.Geodetic(), fontsize=8, color='black')\n", + "\n", + "# Show the full globe\n", + "ax.set_global()\n", + "\n", + "# Add a legend for the edges\n", + "plt.legend()\n", + "\n", + "# Display the plot\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [ { @@ -2119,12 +2201,13 @@ "array([1.04757027])" ] }, - "execution_count": 31, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# Area of the triangle with correction\n", "area, jacobian = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5)\n", "area" ] @@ -2133,12 +2216,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The above code shows area calculation withou any correction term. The correction term is calculated below." + "### The above code shows area calculation without any correction term. The correction term is calculated below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Two edges (Edge 2 - blue and Edge 3 - green) of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above. Edge 3 - red is also along the line of constant latitude, but also passes through the poles, so it is not considered for the correction term. Simply passing an argument correct_area = True in the function call will calculate the area with the correction for line of constant latitude." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -2165,13 +2255,6 @@ "# Continuing with our example from above\n", "area = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5, correct_area=True)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Two edges of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above " - ] } ], "metadata": { From aa7b4898b714d6befced7ff309863afe2536d815 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Tue, 7 Jan 2025 23:40:30 -0600 Subject: [PATCH 04/10] o fix notebook --- docs/user-guide/area_calc.ipynb | 162 +++++++++++++++----------------- 1 file changed, 75 insertions(+), 87 deletions(-) diff --git a/docs/user-guide/area_calc.ipynb b/docs/user-guide/area_calc.ipynb index 55e4c7060..50e70c38d 100644 --- a/docs/user-guide/area_calc.ipynb +++ b/docs/user-guide/area_calc.ipynb @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 15, "metadata": { "collapsed": false, "jupyter": { @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 16, "metadata": { "collapsed": false, "jupyter": { @@ -446,14 +446,14 @@ " * face_node_connectivity: (5400, 4)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (5400,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      4 4 4 4 4 4 4 4 ... 4 4 4 4 4 4 4 4
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([4, 4, 4, ..., 4, 4, 4])
  • " ], "text/plain": [ "\n", @@ -473,7 +473,7 @@ " * n_nodes_per_face: (5400,)" ] }, - "execution_count": 25, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -496,7 +496,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 17, "metadata": { "collapsed": false, "jupyter": { @@ -510,7 +510,7 @@ "np.float64(12.566370614678554)" ] }, - "execution_count": 26, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -538,7 +538,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -547,7 +547,7 @@ "np.float64(12.571403993719983)" ] }, - "execution_count": 27, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -582,7 +582,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 19, "metadata": { "collapsed": false, "jupyter": { @@ -969,8 +969,8 @@ "Dimensions without coordinates: n_face\n", "Attributes:\n", " cf_role: face_areas\n", - " long_name: Area of each face." + " long_name: Area of each face." ], "text/plain": [ " Size: 43kB\n", @@ -982,7 +982,7 @@ " long_name: Area of each face." ] }, - "execution_count": 28, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -1000,7 +1000,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -1009,7 +1009,7 @@ "np.float64(12.566370614359112)" ] }, - "execution_count": 29, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +1031,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -1042,7 +1042,7 @@ " np.float64(6.039613253960852e-14))" ] }, - "execution_count": 30, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -1076,7 +1076,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 22, "metadata": { "collapsed": false, "jupyter": { @@ -1473,7 +1473,7 @@ " * face_node_connectivity: (1, 3)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (1,)\n", - "" + "" ], "text/plain": [ "\n", @@ -1494,7 +1494,7 @@ " * n_nodes_per_face: (1,)" ] }, - "execution_count": 31, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -1516,7 +1516,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 23, "metadata": { "collapsed": false, "jupyter": { @@ -1530,7 +1530,7 @@ "np.float64(1.0719419938548218)" ] }, - "execution_count": 32, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -1548,7 +1548,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 24, "metadata": { "collapsed": false, "jupyter": { @@ -1562,7 +1562,7 @@ "np.float64(1.0475702709086991)" ] }, - "execution_count": 33, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -1589,7 +1589,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 25, "metadata": { "collapsed": false, "jupyter": { @@ -1643,7 +1643,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 26, "metadata": { "collapsed": false, "jupyter": { @@ -2039,10 +2039,10 @@ " * face_node_connectivity: (3, 6)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (3,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      6 6 6
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([6, 6, 6])
  • " ], "text/plain": [ "\n", @@ -2062,7 +2062,7 @@ " * n_nodes_per_face: (3,)" ] }, - "execution_count": 35, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -2075,7 +2075,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 27, "metadata": { "collapsed": false, "jupyter": { @@ -2089,7 +2089,7 @@ "array([0.14323746, 0.25118746, 0.12141312])" ] }, - "execution_count": 36, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -2122,13 +2122,12 @@ "\n", "- This formula assumes that the input coordinates $(x_1, y_1)$ and $(x_2, y_2)$ are normalized (i.e., they lie on the unit sphere).\n", " \n", - "\n", "#### For the same big triangle as used in Section 4, we calculate area correction term when an edge is at the line of constant latitude. The code below plots that triangle and marks the edges with different colors, highlights the edge at the line of constant latitude and also marks the coordinates of the vertices." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -2144,15 +2143,23 @@ ], "source": [ "%matplotlib inline \n", + "import matplotlib.pyplot as plt\n", + "import cartopy.crs as ccrs\n", + "import cartopy.feature as cfeature\n", + "\n", "# Convert the points to latitude and longitude\n", - "points = [(point[0] * 180 / 3.14159, point[1] * 180 / 3.14159) for vert in verts for point in vert]\n", + "points = [\n", + " (point[0] * 180 / 3.14159, point[1] * 180 / 3.14159)\n", + " for vert in verts\n", + " for point in vert\n", + "]\n", "\n", "# Node names\n", - "node_names = ['Node 1', 'Node 2', 'Node 3']\n", + "node_names = [\"Node 1\", \"Node 2\", \"Node 3\"]\n", "\n", "# Edge names and colors\n", - "edge_names = ['Edge 1', 'Edge 2', 'Edge 3']\n", - "edge_colors = ['red', 'green', 'blue']\n", + "edge_names = [\"Edge 1\", \"Edge 2\", \"Edge 3\"]\n", + "edge_colors = [\"red\", \"green\", \"blue\"]\n", "\n", "# Create a new figure\n", "fig = plt.figure(figsize=(10, 5))\n", @@ -2168,17 +2175,29 @@ "for i in range(len(points)):\n", " lon1, lat1 = points[i]\n", " lon2, lat2 = points[(i + 1) % len(points)]\n", - " \n", + "\n", " # Plot the edge\n", - " ax.plot([lon1, lon2], [lat1, lat2], color=edge_colors[i], transform=ccrs.Geodetic(), label=edge_names[i])\n", - " \n", + " ax.plot(\n", + " [lon1, lon2],\n", + " [lat1, lat2],\n", + " color=edge_colors[i],\n", + " transform=ccrs.Geodetic(),\n", + " label=edge_names[i],\n", + " )\n", + "\n", " # Add edge label (adjust the offset as needed)\n", - " ax.text((lon1 + lon2) / 2, (lat1 + lat2) / 2, edge_names[i], transform=ccrs.Geodetic(), color=edge_colors[i])\n", - " \n", + " ax.text(\n", + " (lon1 + lon2) / 2,\n", + " (lat1 + lat2) / 2,\n", + " edge_names[i],\n", + " transform=ccrs.Geodetic(),\n", + " color=edge_colors[i],\n", + " )\n", + "\n", " # Add node label with Cartesian coordinates\n", " cartesian_coords = verts[0][i] # Get Cartesian coordinates from verts\n", - " label = f'{node_names[i]}\\n[{cartesian_coords[0]:.2f}, {cartesian_coords[1]:.2f}, {cartesian_coords[2]:.2f}]'\n", - " ax.text(lon1, lat1, label, transform=ccrs.Geodetic(), fontsize=8, color='black')\n", + " label = f\"{node_names[i]}\\n[{cartesian_coords[0]:.2f}, {cartesian_coords[1]:.2f}, {cartesian_coords[2]:.2f}]\"\n", + " ax.text(lon1, lat1, label, transform=ccrs.Geodetic(), fontsize=8, color=\"black\")\n", "\n", "# Show the full globe\n", "ax.set_global()\n", @@ -2194,18 +2213,7 @@ "cell_type": "code", "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.04757027])" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Area of the triangle with correction\n", "area, jacobian = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5)\n", @@ -2216,45 +2224,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The above code shows area calculation without any correction term. The correction term is calculated below." + "### The above code shows area calculation without any correction term. Next code block calculates the corrected area of the triangle. Two edges (Edge 2 - blue and Edge 3 - green) of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above. Edge 3 - red is also along the line of constant latitude, but also passes through the poles, so it is not considered for the correction term. Simply passing an argument correct_area = True in the function call will calculate the area with the correction for line of constant latitude." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "### Two edges (Edge 2 - blue and Edge 3 - green) of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above. Edge 3 - red is also along the line of constant latitude, but also passes through the poles, so it is not considered for the correction term. Simply passing an argument correct_area = True in the function call will calculate the area with the correction for line of constant latitude." + "# Continuing with our example from above and now we will correct the area\n", + "area = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5, correct_area=True)" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Check if edge passes through pole: False\n", - "0.577350269522959 -0.5773502685229592 0.5773502695229592 0.577350268522959 -0.5773502695229591 correction: 0.1402978685558901\n", - "For Node 1 [ 0.57735027 -0.57735027 -0.57735027] \n", - " and Node 2 [ 0.57735027 0.57735027 -0.57735027] \n", - "CORRECTION 0.1402978685558901\n", - "Check if edge passes through pole: False\n", - "0.5773502695229592 0.577350268522959 -0.5773502695229594 0.5773502685229589 -0.5773502695229591 correction: 0.14029786955589052\n", - "For Node 1 [ 0.57735027 0.57735027 -0.57735027] \n", - " and Node 2 [-0.57735027 0.57735027 -0.57735027] \n", - "CORRECTION 0.14029786955589052\n", - "Check if edge passes through pole: True\n", - "AREA Before Correction 1.0475702709086991\n", - "AREA After Correction 1.3281660100204802\n" - ] - } - ], - "source": [ - "# Continuing with our example from above\n", - "area = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5, correct_area=True)" - ] + "outputs": [], + "source": [] } ], "metadata": { From cadb8c87fc16678b874810d9b84515db0f865353 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Wed, 8 Jan 2025 11:08:33 -0600 Subject: [PATCH 05/10] Update uxarray/grid/area.py Co-authored-by: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> --- uxarray/grid/area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index 72bed817b..f74645c4d 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -170,7 +170,7 @@ def edge_passes_through_pole(node1, node2): # Check for numerical stability issues with the normal vector if np.allclose(n, 0): # Handle cases where the cross product is near zero, such as when nodes are nearly identical or opposite - return False # Or handle it according to your specific needs + return False # Normalize the normal vector n = n / np.linalg.norm(n) From bd3ebfc5f656bb86d57e11e624b0f760092d2806 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Wed, 8 Jan 2025 11:08:50 -0600 Subject: [PATCH 06/10] Update uxarray/grid/area.py Co-authored-by: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> --- uxarray/grid/area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index f74645c4d..d930ce617 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -175,7 +175,7 @@ def edge_passes_through_pole(node1, node2): # Normalize the normal vector n = n / np.linalg.norm(n) - # North and South Pole vectors (make them float64) + # North and South Pole vectors p_north = np.array([0.0, 0.0, 1.0]) # Changed to float64 p_south = np.array([0.0, 0.0, -1.0]) # Changed to float64 From aae25d82c590ddf8a09102834fb9c4a2cebdc1f1 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Wed, 8 Jan 2025 11:09:01 -0600 Subject: [PATCH 07/10] Update uxarray/grid/area.py Co-authored-by: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> --- uxarray/grid/area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index d930ce617..680b72bc6 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -176,7 +176,7 @@ def edge_passes_through_pole(node1, node2): n = n / np.linalg.norm(n) # North and South Pole vectors - p_north = np.array([0.0, 0.0, 1.0]) # Changed to float64 + p_north = np.array([0.0, 0.0, 1.0]) p_south = np.array([0.0, 0.0, -1.0]) # Changed to float64 # Check if the normal vector is orthogonal to either pole From 7e746ba7467d928d1329db008f8794520aed9508 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Wed, 8 Jan 2025 11:09:09 -0600 Subject: [PATCH 08/10] Update uxarray/grid/area.py Co-authored-by: Philip Chmielowiec <67855069+philipc2@users.noreply.github.com> --- uxarray/grid/area.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index 680b72bc6..f9f16a800 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -177,7 +177,7 @@ def edge_passes_through_pole(node1, node2): # North and South Pole vectors p_north = np.array([0.0, 0.0, 1.0]) - p_south = np.array([0.0, 0.0, -1.0]) # Changed to float64 + p_south = np.array([0.0, 0.0, -1.0]) # Check if the normal vector is orthogonal to either pole return np.isclose(np.dot(n, p_north), 0) or np.isclose(np.dot(n, p_south), 0) From 9f2a507f52a9373e07f8f82b8661a36b526a58e8 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Thu, 9 Jan 2025 12:11:19 -0600 Subject: [PATCH 09/10] o Cleanup notebook --- docs/user-guide/area_calc.ipynb | 289 +++++++++++++++++++++++++------- 1 file changed, 231 insertions(+), 58 deletions(-) diff --git a/docs/user-guide/area_calc.ipynb b/docs/user-guide/area_calc.ipynb index 50e70c38d..a90fa76b8 100644 --- a/docs/user-guide/area_calc.ipynb +++ b/docs/user-guide/area_calc.ipynb @@ -28,14 +28,138 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n const py_version = '3.5.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n const reloading = false;\n const Bokeh = root.Bokeh;\n\n // Set a timeout for this load but only if we are not already initializing\n if (typeof (root._bokeh_timeout) === \"undefined\" || (force || !root._bokeh_is_initializing)) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n // Don't load bokeh if it is still initializing\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n } else if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n // There is nothing to load\n run_callbacks();\n return null;\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error(e) {\n const src_el = e.srcElement\n console.error(\"failed to load \" + (src_el.href || src_el.src));\n }\n\n const skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {}, 'shim': {}});\n root._bokeh_is_loading = css_urls.length + 0;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n const existing_stylesheets = []\n const links = document.getElementsByTagName('link')\n for (let i = 0; i < links.length; i++) {\n const link = links[i]\n if (link.href != null) {\n existing_stylesheets.push(link.href)\n }\n }\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const escaped = encodeURI(url)\n if (existing_stylesheets.indexOf(escaped) !== -1) {\n on_load()\n continue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } var existing_scripts = []\n const scripts = document.getElementsByTagName('script')\n for (let i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n existing_scripts.push(script.src)\n }\n }\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (let i = 0; i < js_modules.length; i++) {\n const url = js_modules[i];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) !== -1 || existing_scripts.indexOf(escaped) !== -1) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n const url = js_exports[name];\n const escaped = encodeURI(url)\n if (skip.indexOf(escaped) >= 0 || root[name] != null) {\n if (!window.requirejs) {\n on_load();\n }\n continue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.holoviz.org/panel/1.5.4/dist/bundled/reactiveesm/es-module-shims@^1.10.0/dist/es-module-shims.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.5.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.5.2.min.js\", \"https://cdn.holoviz.org/panel/1.5.4/dist/panel.min.js\"];\n const js_modules = [];\n const js_exports = {};\n const css_urls = [];\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (let i = 0; i < inline_js.length; i++) {\n try {\n inline_js[i].call(root, root.Bokeh);\n } catch(e) {\n if (!reloading) {\n throw e;\n }\n }\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n var NewBokeh = root.Bokeh;\n if (Bokeh.versions === undefined) {\n Bokeh.versions = new Map();\n }\n if (NewBokeh.version !== Bokeh.version) {\n Bokeh.versions.set(NewBokeh.version, NewBokeh)\n }\n root.Bokeh = Bokeh;\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n // If the timeout and bokeh was not successfully loaded we reset\n // everything and try loading again\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n root._bokeh_is_loading = 0\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n const bokeh_loaded = root.Bokeh != null && (root.Bokeh.version === py_version || (root.Bokeh.versions !== undefined && root.Bokeh.versions.has(py_version)));\n if (!reloading && !bokeh_loaded) {\n if (root.Bokeh) {\n root.Bokeh = undefined;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n", + "application/vnd.holoviews_load.v0+json": "" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
    \n", + "
    \n", + "
    \n", + "" + ] + }, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "47d60927-0d34-47fc-aee0-f53d51723d8d" + } + }, + "output_type": "display_data" + } + ], "source": [ "import uxarray as ux\n", "import numpy as np" @@ -50,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { @@ -446,14 +570,14 @@ " * face_node_connectivity: (5400, 4)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (5400,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      4 4 4 4 4 4 4 4 ... 4 4 4 4 4 4 4 4
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([4, 4, 4, ..., 4, 4, 4])
  • " ], "text/plain": [ "\n", @@ -473,7 +597,7 @@ " * n_nodes_per_face: (5400,)" ] }, - "execution_count": 16, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -496,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 3, "metadata": { "collapsed": false, "jupyter": { @@ -510,7 +634,7 @@ "np.float64(12.566370614678554)" ] }, - "execution_count": 17, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -538,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -547,7 +671,7 @@ "np.float64(12.571403993719983)" ] }, - "execution_count": 18, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -582,7 +706,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 5, "metadata": { "collapsed": false, "jupyter": { @@ -969,8 +1093,8 @@ "Dimensions without coordinates: n_face\n", "Attributes:\n", " cf_role: face_areas\n", - " long_name: Area of each face." + " long_name: Area of each face." ], "text/plain": [ " Size: 43kB\n", @@ -982,7 +1106,7 @@ " long_name: Area of each face." ] }, - "execution_count": 19, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -1000,7 +1124,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -1009,7 +1133,7 @@ "np.float64(12.566370614359112)" ] }, - "execution_count": 20, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +1155,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -1042,7 +1166,7 @@ " np.float64(6.039613253960852e-14))" ] }, - "execution_count": 21, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -1076,7 +1200,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 8, "metadata": { "collapsed": false, "jupyter": { @@ -1473,7 +1597,7 @@ " * face_node_connectivity: (1, 3)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (1,)\n", - "" + "" ], "text/plain": [ "\n", @@ -1494,7 +1618,7 @@ " * n_nodes_per_face: (1,)" ] }, - "execution_count": 22, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -1516,7 +1640,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 9, "metadata": { "collapsed": false, "jupyter": { @@ -1530,7 +1654,7 @@ "np.float64(1.0719419938548218)" ] }, - "execution_count": 23, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -1548,7 +1672,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 10, "metadata": { "collapsed": false, "jupyter": { @@ -1562,7 +1686,7 @@ "np.float64(1.0475702709086991)" ] }, - "execution_count": 24, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -1589,7 +1713,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 11, "metadata": { "collapsed": false, "jupyter": { @@ -1643,7 +1767,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 12, "metadata": { "collapsed": false, "jupyter": { @@ -2039,10 +2163,10 @@ " * face_node_connectivity: (3, 6)\n", "Grid Descriptor Variables:\n", " * n_nodes_per_face: (3,)\n", - "
    • n_nodes_per_face
      (n_face)
      int64
      6 6 6
      cf_role :
      n_nodes_per_face
      long name :
      Number of nodes per face
      array([6, 6, 6])
  • " ], "text/plain": [ "\n", @@ -2062,7 +2186,7 @@ " * n_nodes_per_face: (3,)" ] }, - "execution_count": 26, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -2075,7 +2199,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 13, "metadata": { "collapsed": false, "jupyter": { @@ -2089,7 +2213,7 @@ "array([0.14323746, 0.25118746, 0.12141312])" ] }, - "execution_count": 27, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -2105,29 +2229,41 @@ "source": [ "## 6. Area Correction\n", "\n", - "### Correction Formula\n", - "\n", - "The correction, $A$, is calculated using the following formula:\n", + "The correction, \\(A\\), is calculated using the following formula:\n", "\n", + "\\[\n", "\\begin{align}\n", - "A = 2 \\arctan \\left[ \\frac{z (x_1 y_2 - x_2 y_1)}{x_1^2 + y_1^2 + x_1 x_2 + y_1 y_2} \\right] - z \\arctan \\left[ \\frac{x_1 y_2 - x_2 y_1}{x_1 x_2 + y_1 y_2} \\right].\n", + "A &= 2 \\arctan \\left( \\frac{z (x_1 y_2 - x_2 y_1)}{x_1^2 + y_1^2 + x_1 x_2 + y_1 y_2} \\right) \\nonumber \\\\\n", + "&\\quad - z \\arctan \\left( \\frac{x_1 y_2 - x_2 y_1}{x_1 x_2 + y_1 y_2} \\right).\n", "\\end{align}\n", + "\\]\n", + "\n", + "\n", + "### Where:\n", + "
      \n", + "\n", + "
    • (x1, y1, z) are the Cartesian coordinates of the first node.
    • \n", + "\n", + "
    • (x2, y2, z) are the Cartesian coordinates of the second node (note that the z coordinate is the same for both nodes).
    • \n", "\n", - "**Where:**\n", + "
    \n", "\n", - "- $(x_1, y_1, z)$ are the Cartesian coordinates of the first node.\n", - "- $(x_2, y_2, z)$ are the Cartesian coordinates of the second node (note that the $z$ coordinate is the same for both nodes).\n", + "### Assumptions:\n", + "- This formula assumes that the input coordinates \\((x_1, y_1)\\) and \\((x_2, y_2)\\) are normalized (i.e., they lie on the unit sphere).\n", "\n", - "**Assumptions:**\n", "\n", - "- This formula assumes that the input coordinates $(x_1, y_1)$ and $(x_2, y_2)$ are normalized (i.e., they lie on the unit sphere).\n", - " \n", - "#### For the same big triangle as used in Section 4, we calculate area correction term when an edge is at the line of constant latitude. The code below plots that triangle and marks the edges with different colors, highlights the edge at the line of constant latitude and also marks the coordinates of the vertices." + "For the same large triangle used in **Section 4**, we calculate the area correction term when an edge lies along the line of constant latitude.\n", + "\n", + "### The following code:\n", + "- Plots the triangle.\n", + "- Marks the edges with different colors.\n", + "- Highlights the edge along the line of constant latitude.\n", + "- Marks the coordinates of the vertices." ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -2209,11 +2345,29 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The default for correcttion is False, so we set it to True to calculate the correction term." + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.04757027])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Area of the triangle with correction\n", "area, jacobian = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5)\n", @@ -2224,25 +2378,44 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### The above code shows area calculation without any correction term. Next code block calculates the corrected area of the triangle. Two edges (Edge 2 - blue and Edge 3 - green) of the triangle are along the line of constant latitude. 1.047 (no correction) + 2*0.140 (correction term) gives us the right area of the triangle as shown above. Edge 3 - red is also along the line of constant latitude, but also passes through the poles, so it is not considered for the correction term. Simply passing an argument correct_area = True in the function call will calculate the area with the correction for line of constant latitude." + "- **Edge 3** (blue) and **Edge 2** (green) of the triangle lie along lines of constant latitude.\n", + "- Without correction, the area is calculated as `1.047`. With the correction, the area becomes `1.047 + 2 * 0.140`, which gives the correct area of the triangle.\n", + "\n", + "\n", + "- **Edge 3** (red) also lies along a line of constant latitude but passes through the poles. Therefore, it is not considered for the correction term.\n", + "\n", + "To compute the area with the correction term applied, pass the argument ``correct_area=True`` to the function call. This enables automatic adjustment for edges along lines of constant latitude." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Check if edge passes through pole: False\n", + "0.577350269522959 -0.5773502685229592 0.5773502695229592 0.577350268522959 -0.5773502695229591 correction: 0.1402978685558901\n", + "For Node 1 [ 0.57735027 -0.57735027 -0.57735027] \n", + " and Node 2 [ 0.57735027 0.57735027 -0.57735027] \n", + "CORRECTION 0.1402978685558901\n", + "Check if edge passes through pole: False\n", + "0.5773502695229592 0.577350268522959 -0.5773502695229594 0.5773502685229589 -0.5773502695229591 correction: 0.14029786955589052\n", + "For Node 1 [ 0.57735027 0.57735027 -0.57735027] \n", + " and Node 2 [-0.57735027 0.57735027 -0.57735027] \n", + "CORRECTION 0.14029786955589052\n", + "Check if edge passes through pole: True\n", + "AREA Before Correction 1.0475702709086991\n", + "AREA After Correction 1.3281660100204802\n" + ] + } + ], "source": [ "# Continuing with our example from above and now we will correct the area\n", "area = vgrid.compute_face_areas(quadrature_rule=\"gaussian\", order=5, correct_area=True)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 3928a5e4eb13d44580f9d420b30a6abb50fcc995 Mon Sep 17 00:00:00 2001 From: Rajeev Jain Date: Thu, 9 Jan 2025 12:21:10 -0600 Subject: [PATCH 10/10] o Fix spaces --- uxarray/grid/area.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uxarray/grid/area.py b/uxarray/grid/area.py index f9f16a800..ccbb18c36 100644 --- a/uxarray/grid/area.py +++ b/uxarray/grid/area.py @@ -170,14 +170,14 @@ def edge_passes_through_pole(node1, node2): # Check for numerical stability issues with the normal vector if np.allclose(n, 0): # Handle cases where the cross product is near zero, such as when nodes are nearly identical or opposite - return False + return False # Normalize the normal vector n = n / np.linalg.norm(n) # North and South Pole vectors - p_north = np.array([0.0, 0.0, 1.0]) - p_south = np.array([0.0, 0.0, -1.0]) + p_north = np.array([0.0, 0.0, 1.0]) + p_south = np.array([0.0, 0.0, -1.0]) # Check if the normal vector is orthogonal to either pole return np.isclose(np.dot(n, p_north), 0) or np.isclose(np.dot(n, p_south), 0)