Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force use of stated nodata value and float32 dtype in pollination #1636

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@

Unreleased Changes
------------------
* Pollination
* Fixed an issue with nodata handling that was causing some outputs to be
filled either with the float32 value for positive infinity, or else with
a value very close to it. https://github.com/natcap/invest/issues/1635
* While working on https://github.com/natcap/invest/issues/1635, we also
updated the stated dtype of most pollination model outputs to be float32
instead of the float64 dtype that was being assumed previously. This
will result in smaller output filesizes with minimal loss of precision.
* Workbench
* Several small updates to the model input form UI to improve usability
and visual consistency (https://github.com/natcap/invest/issues/912)
Expand Down
52 changes: 51 additions & 1 deletion scripts/convert-requirements-to-conda-yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""convert-requirements-to-conda-yml.py"""

import argparse
import os
import platform
import sys

Expand All @@ -14,6 +15,46 @@
"""


# Environment marker handling is taken straight from
# https://peps.python.org/pep-0508/#environment-markers
def _get_implementation_version():
def format_full_version(info):
version = '{0.major}.{0.minor}.{0.micro}'.format(info)
kind = info.releaselevel
if kind != 'final':
version += kind[0] + str(info.serial)
return version

if hasattr(sys, 'implementation'):
implementation_version = format_full_version(
sys.implementation.version)
else:
implementation_version = "0"
return implementation_version


# Environment marker handling is taken straight from
# https://peps.python.org/pep-0508/#environment-markers
ENV_MARKERS = {
"os_name": os.name,
"sys_platform": sys.platform,
"platform_machine": platform.machine(),
"platform_python_impl": platform.python_implementation(),
"platform_release": platform.release(),
"platform_system": platform.system(),
"platform_version": platform.version(),

# Deliberately not supporting python_version marker in order to avoid
# possible issues with the python version not yet being known when this
# script is run.
# "python_version": '.'.join(platform.python_version_tuple()[:2]),

"python_full_version": platform.python_version(),
"implementation_name": sys.implementation.name,
"implementation_version": _get_implementation_version(),
}


def build_environment_from_requirements(cli_args):
"""Build a conda environment.yml from requirements.txt files.

Expand Down Expand Up @@ -62,7 +103,16 @@ def build_environment_from_requirements(cli_args):
# requirement if we're using pip.
conda_requirements.add('pip')

pip_requirements.add(line)
# Handle environment specifiers and see if the requirement
# should exist in this environment.
# InVEST pretty much just uses this for checking operating
# systems.
if ";" in line:
package, marker = line.split(';')
if eval(marker, ENV_MARKERS):
pip_requirements.add(package)
else:
pip_requirements.add(line)

# If an scm needs to be installed for pip to clone to a
# revision, add it to the conda package list.
Expand Down
21 changes: 16 additions & 5 deletions src/natcap/invest/pollination.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,7 @@ def execute(args):
floral_resources_index_path_map[species],
convolve_ps_path],
target_path=pollinator_abundance_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA),
dependent_task_list=[
foraged_flowers_index_task_map[(species, season)],
Expand Down Expand Up @@ -935,7 +936,9 @@ def execute(args):
rasters=[
half_saturation_raster_path,
total_pollinator_abundance_index_path],
target_path=farm_pollinator_season_path),
target_path=farm_pollinator_season_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA),
dependent_task_list=[
half_saturation_task, total_pollinator_abundance_task[season]],
target_path_list=[farm_pollinator_season_path]))
Expand Down Expand Up @@ -976,20 +979,24 @@ def execute(args):
kwargs=dict(
op=pyt_op,
rasters=[managed_pollinator_path, farm_pollinator_path],
target_path=total_pollinator_yield_path),
target_path=total_pollinator_yield_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA),
dependent_task_list=[farm_pollinator_task, managed_pollinator_task],
target_path_list=[total_pollinator_yield_path])

# calculate PYW
wild_pollinator_yield_path = os.path.join(
output_dir, _WILD_POLLINATOR_YIELD_FILE_PATTERN % file_suffix)
wild_pollinator_task = task_graph.add_task(
task_name='calcualte_wild_pollinators',
task_name='calculate_wild_pollinators',
func=pygeoprocessing.raster_map,
kwargs=dict(
op=pyw_op,
rasters=[managed_pollinator_path, total_pollinator_yield_path],
target_path=wild_pollinator_yield_path),
target_path=wild_pollinator_yield_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA),
dependent_task_list=[pyt_task, managed_pollinator_task],
target_path_list=[wild_pollinator_yield_path])

Expand Down Expand Up @@ -1392,7 +1399,8 @@ def _sum_arrays(*array_list):
result = numpy.empty_like(array_list[0])
result[:] = 0
for array in array_list:
local_valid_mask = ~pygeoprocessing.array_equals_nodata(array, _INDEX_NODATA)
local_valid_mask = ~pygeoprocessing.array_equals_nodata(
array, _INDEX_NODATA)
result[local_valid_mask] += array[local_valid_mask]
valid_mask |= local_valid_mask
result[~valid_mask] = _INDEX_NODATA
Expand Down Expand Up @@ -1423,6 +1431,7 @@ def max_op(*substrate_index_arrays):
pygeoprocessing.raster_map(
op=max_op,
rasters=substrate_path_list,
target_dtype=numpy.float32,
target_path=target_habitat_nesting_index_path)


Expand All @@ -1432,6 +1441,7 @@ def _multiply_by_scalar(raster_path, scalar, target_path):
op=lambda array: array * scalar,
rasters=[raster_path],
target_path=target_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA,
)

Expand All @@ -1455,6 +1465,7 @@ def _calculate_pollinator_supply_index(
op=lambda f_r, h_n: species_abundance * f_r * h_n,
rasters=[habitat_nesting_suitability_path, floral_resources_path],
target_path=target_path,
target_dtype=numpy.float32,
target_nodata=_INDEX_NODATA
)

Expand Down
Loading