Repository: natcap/invest (python)

Issue:
Resolve deprecation warnings in tests
When running `make test` or `make test_ui`, pytest displays a bunch of deprecation warnings and some other warnings near the end of the test output. Nosetests probably wasn't catching these because it hasn't been updated in a long time. We should go through and resolve each issue causing a warning to prevent future problems. 

Most of them are pretty straightforward e.g. replacing `warn` with `warning`.

Pull request:
Remove deprecated numpy.float and numpy.bool
# Description
Fixes #255 
> DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.

> DeprecationWarning: `np.float` is a deprecated alias for the builtin `float`. To silence this warning, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.

Just replacing these.



Patch:
diff --git a/requirements-docs.yml b/requirements-docs.yml
index 8d5fab083..4ee9246fd 100644
--- a/requirements-docs.yml
+++ b/requirements-docs.yml
@@ -20,7 +20,7 @@ dependencies:
 - setuptools>=8.0
 - Shapely>=1.7.1,<2.0.0
 - sphinx==3.3.1
-- scipy>=0.16.1,<1.5.0
+- scipy>=1.6.0
 - taskgraph[niced_processes]>=0.10.2
 - virtualenv>=12.0.1
 - wheel>=0.27.0
diff --git a/requirements.txt b/requirements.txt
index e3efcba6b..0b45a6541 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,9 +18,9 @@ pandas>=1.2.1
 numpy>=1.11.0,!=1.16.0
 Rtree>=0.8.2,!=0.9.1
 Shapely>=1.7.1,<2.0.0
-scipy>=0.16.1,<1.5.0 # pip-only
-pygeoprocessing>=2.1.1 # pip-only
-taskgraph[niced_processes]>=0.10.3 # pip-only
+scipy>=1.6.0  # pip-only
+pygeoprocessing>=2.1.1  # pip-only
+taskgraph[niced_processes]>=0.10.3  # pip-only
 psutil>=5.6.6
 chardet>=3.0.4
 openpyxl
diff --git a/src/natcap/invest/coastal_blue_carbon/coastal_blue_carbon.py b/src/natcap/invest/coastal_blue_carbon/coastal_blue_carbon.py
index 768b018f5..7ffc96a8b 100644
--- a/src/natcap/invest/coastal_blue_carbon/coastal_blue_carbon.py
+++ b/src/natcap/invest/coastal_blue_carbon/coastal_blue_carbon.py
@@ -1295,7 +1295,7 @@ def _calculate_npv(
             npv[:] = NODATA_FLOAT32_MIN
 
             matrix_sum = numpy.zeros(npv.shape, dtype=numpy.float32)
-            valid_pixels = numpy.ones(npv.shape, dtype=numpy.bool)
+            valid_pixels = numpy.ones(npv.shape, dtype=bool)
             for matrix in sequestration_matrices:
                 valid_pixels &= ~numpy.isclose(matrix, NODATA_FLOAT32_MIN)
                 matrix_sum[valid_pixels] += matrix[valid_pixels]
@@ -1570,7 +1570,7 @@ def _calculate_net_sequestration(
         # and then assume that the Emissions raster has the extra spatial
         # nuances of the landscape (like nodata holes).
         valid_accumulation_pixels = numpy.ones(accumulation_matrix.shape,
-                                               dtype=numpy.bool)
+                                               dtype=bool)
         if accumulation_nodata is not None:
             valid_accumulation_pixels &= (
                 ~numpy.isclose(accumulation_matrix, accumulation_nodata))
@@ -1695,7 +1695,7 @@ def _sum_n_rasters(
         sum_array[:] = 0.0
 
         # Assume everything is valid until proven otherwise
-        pixels_touched = numpy.zeros(sum_array.shape, dtype=numpy.bool)
+        pixels_touched = numpy.zeros(sum_array.shape, dtype=bool)
         for (_, band, nodata) in raster_tuple_list:
             if time.time() - last_log_time >= 5.0:
                 percent_complete = round(
@@ -1901,7 +1901,7 @@ def _reclassify_accumulation_transition(
         output_matrix[:] = NODATA_FLOAT32_MIN
 
         valid_pixels = numpy.ones(landuse_transition_from_matrix.shape,
-                                  dtype=numpy.bool)
+                                  dtype=bool)
         if from_nodata is not None:
             valid_pixels &= (landuse_transition_from_matrix != from_nodata)
 
@@ -1963,7 +1963,7 @@ def _reclassify_disturbance_magnitude(
         output_matrix[:] = NODATA_FLOAT32_MIN
 
         valid_pixels = numpy.ones(landuse_transition_from_matrix.shape,
-                                  dtype=numpy.bool)
+                                  dtype=bool)
         if from_nodata is not None:
             valid_pixels &= (landuse_transition_from_matrix != from_nodata)
 
diff --git a/src/natcap/invest/coastal_vulnerability.py b/src/natcap/invest/coastal_vulnerability.py
index 9f3e22e24..3a46fd166 100644
--- a/src/natcap/invest/coastal_vulnerability.py
+++ b/src/natcap/invest/coastal_vulnerability.py
@@ -2321,7 +2321,7 @@ def calculate_final_risk(output_vector_path, output_csv_path):
     for point_feature in point_layer:
         r_array = numpy.array([
             point_feature.GetField(risk_id)
-            for risk_id in risk_id_list], dtype=numpy.float)
+            for risk_id in risk_id_list], dtype=float)
         r_tot = _geometric_mean(r_array)
         point_feature.SetField(final_risk, float(r_tot))
         risk_id_list_no_hab = [
@@ -2330,7 +2330,7 @@ def calculate_final_risk(output_vector_path, output_csv_path):
         # by replacing R_hab with 5:
         r_array_nohab = numpy.array([
             point_feature.GetField(risk_id)
-            for risk_id in risk_id_list_no_hab] + [5.0], dtype=numpy.float)
+            for risk_id in risk_id_list_no_hab] + [5.0], dtype=float)
         r_tot_no_hab = _geometric_mean(r_array_nohab)
         point_feature.SetField(
             risk_no_habitat, float(r_tot_no_hab))
@@ -2379,7 +2379,7 @@ def _bin_values_to_percentiles(base_values_dict, invert_values=False):
     percentiles = [20, 40, 60, 80, 100]
     rank_array = numpy.searchsorted(
         numpy.nanpercentile(base_values, percentiles),
-        base_values).astype(numpy.float) + 1  # floats so we can hold nan
+        base_values).astype(float) + 1  # floats so we can hold nan
     # nan in base_values need to be re-assigned to nan:
     rank_array[rank_array > len(percentiles)] = numpy.nan
 
diff --git a/src/natcap/invest/forest_carbon_edge_effect.py b/src/natcap/invest/forest_carbon_edge_effect.py
index 64659727a..7dd1813dc 100644
--- a/src/natcap/invest/forest_carbon_edge_effect.py
+++ b/src/natcap/invest/forest_carbon_edge_effect.py
@@ -412,7 +412,7 @@ def combine_carbon_maps(*carbon_maps):
 
     """
     result = numpy.zeros(carbon_maps[0].shape)
-    nodata_mask = numpy.empty(carbon_maps[0].shape, dtype=numpy.bool)
+    nodata_mask = numpy.empty(carbon_maps[0].shape, dtype=bool)
     nodata_mask[:] = True
     for carbon_map in carbon_maps:
         valid_mask = carbon_map != NODATA_VALUE
@@ -833,16 +833,15 @@ def _calculate_tropical_forest_edge_carbon_map(
         col_coords, row_coords = numpy.meshgrid(col_range, row_range)
 
         # query nearest points for every point in the grid
-        # n_jobs=-1 means use all available CPUs
+        # workers=-1 means use all available CPUs
         coord_points = list(zip(
             row_coords[valid_edge_distance_mask].ravel(),
             col_coords[valid_edge_distance_mask].ravel()))
-        # note, the 'n_jobs' parameter was introduced in SciPy 0.16.0
         # for each forest point x, for each of its k nearest neighbors
         # shape of distances and indexes: (x, k)
         distances, indexes = kd_tree.query(
             coord_points, k=n_nearest_model_points,
-            distance_upper_bound=DISTANCE_UPPER_BOUND, n_jobs=-1)
+            distance_upper_bound=DISTANCE_UPPER_BOUND, workers=-1)
 
         if n_nearest_model_points == 1:
             distances = distances.reshape(distances.shape[0], 1)
diff --git a/src/natcap/invest/globio.py b/src/natcap/invest/globio.py
index 6cd71d743..1eacb4393 100644
--- a/src/natcap/invest/globio.py
+++ b/src/natcap/invest/globio.py
@@ -650,7 +650,7 @@ def _msa_calculation(
     def msa_op(msa_f, msa_lu, msa_i):
         """Calculate the MSA which is the product of the sub MSAs."""
         result = numpy.full_like(msa_f, msa_nodata, dtype=numpy.float32)
-        valid_mask = numpy.ones(msa_f.shape, dtype=numpy.bool)
+        valid_mask = numpy.ones(msa_f.shape, dtype=bool)
         for msa_array, nodata_val in zip([msa_f, msa_lu, msa_i], nodata_array):
             if nodata_val is not None:
                 valid_mask &= ~numpy.isclose(msa_array, nodata_val)
diff --git a/src/natcap/invest/pollination.py b/src/natcap/invest/pollination.py
index 8b29d2b9a..f36fb6587 100644
--- a/src/natcap/invest/pollination.py
+++ b/src/natcap/invest/pollination.py
@@ -1247,7 +1247,7 @@ class _SumRasters(object):
 
     def __call__(self, *array_list):
         """Calculate sum of array_list and account for nodata."""
-        valid_mask = numpy.zeros(array_list[0].shape, dtype=numpy.bool)
+        valid_mask = numpy.zeros(array_list[0].shape, dtype=bool)
         result = numpy.empty_like(array_list[0])
         result[:] = 0
         for array in array_list:
diff --git a/src/natcap/invest/scenario_gen_proximity.py b/src/natcap/invest/scenario_gen_proximity.py
index 9621bbbce..50be358e1 100644
--- a/src/natcap/invest/scenario_gen_proximity.py
+++ b/src/natcap/invest/scenario_gen_proximity.py
@@ -774,7 +774,7 @@ def _convert_by_score(
 
     row_array = numpy.empty((_BLOCK_SIZE,), dtype=numpy.uint32)
     col_array = numpy.empty((_BLOCK_SIZE,), dtype=numpy.uint32)
-    data_array = numpy.empty((_BLOCK_SIZE,), dtype=numpy.bool)
+    data_array = numpy.empty((_BLOCK_SIZE,), dtype=bool)
     next_index = 0
     dirty_blocks = set()
 
diff --git a/src/natcap/invest/urban_cooling_model.py b/src/natcap/invest/urban_cooling_model.py
index 60ab977db..24d595748 100644
--- a/src/natcap/invest/urban_cooling_model.py
+++ b/src/natcap/invest/urban_cooling_model.py
@@ -1261,10 +1261,10 @@ def flat_disk_kernel(max_distance, kernel_filepath):
             # matrices to float64 matrices.
             row_indices, col_indices = numpy.indices((row_block_width,
                                                       col_block_width),
-                                                     dtype=numpy.float)
+                                                     dtype=float)
 
-            row_indices += numpy.float(row_offset - max_distance)
-            col_indices += numpy.float(col_offset - max_distance)
+            row_indices += float(row_offset - max_distance)
+            col_indices += float(col_offset - max_distance)
 
             kernel_index_distances = numpy.hypot(
                 row_indices, col_indices)
diff --git a/src/natcap/invest/urban_flood_risk_mitigation.py b/src/natcap/invest/urban_flood_risk_mitigation.py
index 486ca95b5..8b837a4a0 100644
--- a/src/natcap/invest/urban_flood_risk_mitigation.py
+++ b/src/natcap/invest/urban_flood_risk_mitigation.py
@@ -679,7 +679,7 @@ def _runoff_retention_op(q_pi_array, p_value, q_pi_nodata, result_nodata):
     """
     result = numpy.empty_like(q_pi_array)
     result[:] = result_nodata
-    valid_mask = numpy.ones(q_pi_array.shape, dtype=numpy.bool)
+    valid_mask = numpy.ones(q_pi_array.shape, dtype=bool)
     if q_pi_nodata is not None:
         valid_mask[:] = ~numpy.isclose(q_pi_array, q_pi_nodata)
     result[valid_mask] = 1.0 - (q_pi_array[valid_mask] / p_value)
@@ -704,7 +704,7 @@ def _q_pi_op(p_value, s_max_array, s_max_nodata, result_nodata):
     result[:] = result_nodata
 
     zero_mask = (p_value <= lam * s_max_array)
-    non_nodata_mask = numpy.ones(s_max_array.shape, dtype=numpy.bool)
+    non_nodata_mask = numpy.ones(s_max_array.shape, dtype=bool)
     if s_max_nodata is not None:
         non_nodata_mask[:] = ~numpy.isclose(s_max_array, s_max_nodata)
 
@@ -761,7 +761,7 @@ def _lu_to_cn_op(
     """
     result = numpy.empty_like(lucode_array, dtype=numpy.float32)
     result[:] = cn_nodata
-    valid_mask = numpy.ones(lucode_array.shape, dtype=numpy.bool)
+    valid_mask = numpy.ones(lucode_array.shape, dtype=bool)
     if lucode_nodata is not None:
         valid_mask[:] &= ~numpy.isclose(lucode_array, lucode_nodata)
     if soil_type_nodata is not None:
diff --git a/src/natcap/invest/utils.py b/src/natcap/invest/utils.py
index 35985ec20..dd6ef491e 100644
--- a/src/natcap/invest/utils.py
+++ b/src/natcap/invest/utils.py
@@ -346,10 +346,10 @@ def exponential_decay_kernel_raster(expected_distance, kernel_filepath):
             # matrices to float64 matrices.
             row_indices, col_indices = numpy.indices((row_block_width,
                                                       col_block_width),
-                                                     dtype=numpy.float)
+                                                     dtype=float)
 
-            row_indices += numpy.float(row_offset - max_distance)
-            col_indices += numpy.float(col_offset - max_distance)
+            row_indices += float(row_offset - max_distance)
+            col_indices += float(col_offset - max_distance)
 
             kernel_index_distances = numpy.hypot(
                 row_indices, col_indices)


Test patch:
diff --git a/tests/test_coastal_vulnerability.py b/tests/test_coastal_vulnerability.py
index e9dfc8699..2673aef76 100644
--- a/tests/test_coastal_vulnerability.py
+++ b/tests/test_coastal_vulnerability.py
@@ -769,7 +769,7 @@ class CoastalVulnerabilityTests(unittest.TestCase):
 
     def test_geometric_mean_with_nan(self):
         """CV: test geometric mean function retuns `nan` with missing data."""
-        array = numpy.array([1, 1, 1, 1, None], dtype=numpy.float)
+        array = numpy.array([1, 1, 1, 1, None], dtype=float)
         result = coastal_vulnerability._geometric_mean(array)
         self.assertTrue(numpy.isnan(result))