@@ -956,84 +956,12 @@ def cart_to_azimut_TR(u, v, mode="from"):
956956 np .sqrt (u ** 2 + v ** 2 ))
957957
958958
959- def sfc_area_deg (lon1 , lon2 , lat1 , lat2 , R = 3390000. ):
960- """
961- Return the surface between two sets of latitudes/longitudes::
962-
963- S = int[R^2 dlon cos(lat) dlat] _____lat2
964- \ \
965- \____\lat1
966- lon1 lon2
967- :param lon1: longitude from set 1 [°]
968- :type lon1: float
969- :param lon2: longitude from set 2 [°]
970- :type lon2: float
971- :param lat1: latitude from set 1 [°]
972- :type lat1: float
973- :param lat2: longitude from set 2 [°]
974- :type lat2: float
975- :param R: planetary radius [m]
976- :type R: int
977-
978- .. note::
979- qLon and Lat define the corners of the area not the grid cell center.
980- """
981-
982- lat1 *= np .pi / 180
983- lat2 *= np .pi / 180
984- lon1 *= np .pi / 180
985- lon2 *= np .pi / 180
986- return ((R ** 2 )
987- * np .abs (lon1 - lon2 )
988- * np .abs (np .sin (lat1 ) - np .sin (lat2 )))
989-
990-
991- def area_meridional_cells_deg (lat_c , dlon , dlat , normalize = False , R = 3390000. ):
992- """
993- Return area of invidual cells for a meridional band of thickness
994- ``dlon`` where ``S = int[R^2 dlon cos(lat) dlat]`` with
995- ``sin(a)-sin(b) = 2 cos((a+b)/2)sin((a+b)/2)``
996- so ``S = 2 R^2 dlon 2cos(lat)sin(dlat/2)``::
997-
998- _________lat + dlat/2
999- \ lat \ ^
1000- \lon + \ | dlat
1001- \________\lat - dlat/2 v
1002- lon - dlon/2 lon + dlon/2
1003- <------>
1004- dlon
1005-
1006- :param lat_c: latitude of cell center [°]
1007- :type lat_c: float
1008- :param dlon: cell angular width [°]
1009- :type dlon: float
1010- :param dlat: cell angular height [°]
1011- :type dlat: float
1012- :param R: planetary radius [m]
1013- :type R: float
1014- :param normalize: if True, the sum of the output elements = 1
1015- :type normalize: bool
1016- :return: ``S`` areas of the cells, same size as ``lat_c`` in [m2]
1017- or normalized by the total area
1018- """
1019-
1020- # Initialize
1021- area_tot = 1.
1022- # Compute total area in a longitude band extending from
1023- # ``lat[0] - dlat/2`` to ``lat_c[-1] + dlat/2``
1024- if normalize :
1025- area_tot = sfc_area_deg (- dlon / 2 , dlon / 2 , (lat_c [0 ] - dlat / 2 ),
1026- (lat_c [- 1 ] + dlat / 2 ), R )
1027- # Now convert to radians
1028- lat_c = lat_c * np .pi / 180
1029- dlon *= np .pi / 180
1030- dlat *= np .pi / 180
1031- return (2. * R ** 2 * dlon * np .cos (lat_c ) * np .sin (dlat / 2. ) / area_tot )
1032-
1033-
1034959def area_weights_deg (var_shape , lat_c , axis = - 2 ):
1035960 """
1036- Return weights for averaging the variable.
961+ Returns weights scaled so that np.mean(var*W) gives an area-weighted
962+ average. This works because grid cells near the poles have smaller
963+ areas than those at the equator, so they should contribute less to
964+ a global average.
1037965
1038966 Expected dimensions are:
1039967
@@ -1048,12 +976,11 @@ def area_weights_deg(var_shape, lat_c, axis = -2):
1048976 may be truncated on either end (e.g., ``lat = [-20 ..., 0... 50]``)
1049977 but must be continuous.
1050978
1051- :param var_shape: variable shape
979+ :param var_shape: the shape/dimensions of your data array
1052980 :type var_shape: tuple
1053- :param lat_c: latitude of cell centers [°]
981+ :param lat_c: latitude cell centers in degrees [°]
1054982 :type lat_c: float
1055- :param axis: position of the latitude axis for 2D and higher
1056- dimensional arrays. The default is the SECOND TO LAST dimension
983+ :param axis: which dimension contains latitude, default: 2nd-to-last
1057984 :type axis: int
1058985 :return: ``W`` weights for the variable ready for standard
1059986 averaging as ``np.mean(var*W)`` [condensed form] or
@@ -1083,33 +1010,75 @@ def area_weights_deg(var_shape, lat_c, axis = -2):
10831010 ``np.average(var, weights=W, axis = X)`` to average over a
10841011 specific axis.
10851012 """
1086-
1013+ # Check if either the lat array or var shape is essentially
1014+ # scalar (single value)
1015+ # np.atleast_1d() ensures the input is treated as at least a 1D
1016+ # array for checking its length
10871017 if (len (np .atleast_1d (lat_c )) == 1 or
10881018 len (np .atleast_1d (var_shape )) == 1 ):
1089- # If variable or lat is a scalar, do nothing
1019+ # If either is scalar, returns an array of 1s matching the var
1020+ # shape (no weighting needed since there's nothing to weight across)
10901021 return np .ones (var_shape )
10911022 else :
1092- # Then lat has at least 2 elements
1023+ # Calculates lat spacing by taking the difference between the
1024+ # first two latitude points. Assumes uniform grid spacing
10931025 dlat = lat_c [1 ]- lat_c [0 ]
1094- # Calculate cell areas. Since it is normalized, we can use
1095- # ``dlon = 1`` and ``R = 1`` without changing the result. Note
1096- # that ``sum(A) = (A1 + A2 + ... An) = 1``
1097- A = area_meridional_cells_deg (lat_c , 1 , dlat , normalize = True , R = 1 )
1026+
1027+ # Calculate cell areas
1028+ # Use dlon = 1 (arbitrary lon spacing and planet
1029+ # radius because normalization makes the absolute values
1030+ # irrelevant), then normalize
1031+ R = 1.
1032+ dlon = 1.
1033+
1034+ # Compute total surface area for normalization
1035+ lon1_deg = - dlon / 2
1036+ lon2_deg = dlon / 2
1037+ lat1_deg = lat_c [0 ] - dlat / 2
1038+ lat2_deg = lat_c [- 1 ] + dlat / 2
1039+
1040+ # Convert to radians for area calculation
1041+ lat1_rad = lat1_deg * np .pi / 180
1042+ lat2_rad = lat2_deg * np .pi / 180
1043+ lon1_rad = lon1_deg * np .pi / 180
1044+ lon2_rad = lon2_deg * np .pi / 180
1045+
1046+ area_tot = ((R ** 2 )
1047+ * np .abs (lon1_rad - lon2_rad )
1048+ * np .abs (np .sin (lat1_rad ) - np .sin (lat2_rad )))
1049+
1050+ # Convert to radians
1051+ lat_c_rad = lat_c * np .pi / 180
1052+ dlon_rad = dlon * np .pi / 180
1053+ dlat_rad = dlat * np .pi / 180
1054+
1055+ # Calculate normalized areas. Areas sum to 1
1056+ A = (2. * R ** 2 * dlon_rad * np .cos (lat_c_rad ) *
1057+ np .sin (dlat_rad / 2. ) / area_tot )
10981058
1059+ # Check if var is 1D (just lat values)
10991060 if len (var_shape ) == 1 :
1100- # Variable is a 1D array of size = [lat]. Easiest case
1101- # since ``(w1 + w2 + ...wn ) = sum(A) = 1`` and
1102- # ``N = len(lat)``
1061+ # For 1D case: multiply normalized areas by the number of
1062+ # lat points. Creates weights where sum(W ) = len(lat_c),
1063+ # allowing np.mean(var*W) to give the area-weighted average
11031064 W = A * len (lat_c )
11041065 else :
1105- # Generate the appropriate shape for the area ``A``,
1106- # e.g., [time, lev, lat, lon] > [1, 1, lat, 1]
1107- # In this case,`` N = time * lev * lat * lon`` and
1108- # ``(w1 + w2 + ...wn) = time * lev * lon * sum(A)``,
1109- # therefore ``N / (w1 + w2 + ...wn) = lat``
1066+ # For multidimensional data: create a list of 1s with
1067+ # length matching the number of dims in the var
1068+ # (e.g., [1, 1, 1, 1] for 4D data).
11101069 reshape_shape = [1 for i in range (0 , len (var_shape ))]
1070+ # Set the lat dim to the actual number of lat points
1071+ # (e.g., [1, 1, lat, 1] for 4D data where lat = third dim)
11111072 reshape_shape [axis ] = len (lat_c )
1073+ # Reshape the 1D area array to match the var's
1074+ # dimensionality (broadcasting-ready shape), then multiply
1075+ # by the number of lat points. Allows the weights to
1076+ # broadcast correctly across all other dims.
11121077 W = A .reshape (reshape_shape )* len (lat_c )
1078+
1079+ # Expand the weights to full var shape by multiplying with an
1080+ # array of 1s. Creates the final weight array that can be
1081+ # directly multiplied with other data for area-weighted avg.
11131082 return W * np .ones (var_shape )
11141083
11151084
@@ -1738,7 +1707,8 @@ def get_trend_2D(VAR, LON, LAT, type_trend="wmean"):
17381707 # dimensions
17391708
17401709 # Flatten array (``[10, 36, lat, lon]`` -> ``[360, lat, lon]``)
1741- nflatten = int (np .prod (var_shape [:- 2 ]))
1710+ prod_result = np .prod (var_shape [:- 2 ])
1711+ nflatten = int (prod_result .item ()) if hasattr (prod_result , 'item' ) else int (prod_result )
17421712 reshape_flat = np .append (nflatten , var_shape [- 2 :])
17431713 VAR = VAR .reshape (reshape_flat )
17441714
0 commit comments