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

Gb/bc #96

Merged
merged 29 commits into from
Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
efcf50e
added bias module with a place for bias transformation functions and …
grantbuster Sep 20, 2022
115e9c2
feature specific bias correction
grantbuster Sep 20, 2022
c184917
removed warnings about excessive padding - not a bad thing
grantbuster Sep 20, 2022
1d823e3
added a site-by-site linear bias correction calculation method
grantbuster Sep 21, 2022
8b7f25b
bug fix and logging
grantbuster Sep 21, 2022
56148db
bias calc mods and new functions
grantbuster Sep 22, 2022
50c3692
added bias calc cli
grantbuster Sep 22, 2022
2f7d4f7
added bias calc to main cli
grantbuster Sep 22, 2022
c132b81
make bias out dir
grantbuster Sep 22, 2022
915edd5
bug fixes and minor refactor to run on eagle
grantbuster Sep 22, 2022
7b28943
added local linear bias correct to forward pass bc options
grantbuster Sep 22, 2022
0cee671
added option to smooth spatial bias correction factors outside of the…
grantbuster Sep 23, 2022
aa0a040
better enumerated progress logging for fwp
grantbuster Sep 23, 2022
240a0d6
added bias correction option to QA
grantbuster Sep 23, 2022
9ac0905
minor refactor to bias correct u and v instead of windspeed and direc…
grantbuster Sep 23, 2022
d2fb1e2
fixed up the u/v QA with bias correction
grantbuster Sep 27, 2022
01fbeda
added meta data to bc h5 output attrs
grantbuster Sep 27, 2022
b370b9f
more bc convenience functions
grantbuster Sep 28, 2022
24c40b6
added monthly bias correction
grantbuster Sep 28, 2022
0deab95
added montly bias correction data transformation method and integrate…
grantbuster Sep 29, 2022
1f28ccc
fixed collection logic for undefined mask meta variable when file is …
grantbuster Oct 2, 2022
5d93283
added bias correction calc tests
grantbuster Oct 3, 2022
0638d7b
added bias transform calcs
grantbuster Oct 3, 2022
6cc6ced
added fwp+bc integration test
grantbuster Oct 3, 2022
ca24793
added qa+bc integration test
grantbuster Oct 3, 2022
b75b3fc
added version record to bias calc output files and incremented versio…
grantbuster Oct 4, 2022
b0a2c49
simplify qa test and pylint issue
grantbuster Oct 4, 2022
7b9c88f
fixed test on h5 meta attrs dtype and docstrings
grantbuster Oct 4, 2022
2ea15e3
serial data handling for QA+BC bug
grantbuster Oct 4, 2022
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
Prev Previous commit
Next Next commit
added montly bias correction data transformation method and integrate…
…d into fwp and qa
  • Loading branch information
grantbuster committed Oct 4, 2022
commit 0deab957df6de89828ca47cd1f5130d8d3c89c1e
4 changes: 3 additions & 1 deletion sup3r/bias/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
"""Bias calculation and correction modules."""
from .bias_transforms import global_linear_bc, local_linear_bc
from .bias_transforms import (global_linear_bc, local_linear_bc,
monthly_local_linear_bc)
from .bias_calc import LinearCorrection, MonthlyLinearCorrection
99 changes: 97 additions & 2 deletions sup3r/bias/bias_transforms.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# -*- coding: utf-8 -*-
"""Bias correction transformation functions."""
import numpy as np
import logging
from warnings import warn
from rex import Resource


logger = logging.getLogger(__name__)


def global_linear_bc(input, scalar, adder, out_range=None):
"""Bias correct data using a simple global *scalar +adder method.

Parameters
----------
input : np.ndarray
Any data to be bias corrected
Sup3r input data to be bias corrected, assumed to be 3D with shape
(spatial, spatial, temporal) for a single feature.
scalar : float
Scalar (multiplicative) value to apply to input data.
adder : float
Expand All @@ -37,7 +43,8 @@ def local_linear_bc(input, feature_name, bias_fp, lr_padded_slice,
Parameters
----------
input : np.ndarray
Any data to be bias corrected
Sup3r input data to be bias corrected, assumed to be 3D with shape
(spatial, spatial, temporal) for a single feature.
feature_name : str
Name of feature that is being corrected. Datasets with names
"{feature_name}_scalar" and "{feature_name}_adder" will be retrieved
Expand Down Expand Up @@ -69,6 +76,11 @@ def local_linear_bc(input, feature_name, bias_fp, lr_padded_slice,
scalar = res[scalar]
adder = res[adder]

# 3D bias correction factors have seasonal/monthly correction in last axis
if len(scalar.shape) == 3 and len(adder.shape) == 3:
scalar = scalar.mean(axis=-1)
adder = adder.mean(axis=-1)

if lr_padded_slice is not None:
spatial_slice = (lr_padded_slice[0], lr_padded_slice[1])
scalar = scalar[spatial_slice]
Expand All @@ -86,3 +98,86 @@ def local_linear_bc(input, feature_name, bias_fp, lr_padded_slice,
out = np.minimum(out, np.max(out_range))

return out


def monthly_local_linear_bc(input, feature_name, bias_fp, lr_padded_slice,
time_index, temporal_avg=True, out_range=None):
"""Bias correct data using a simple global *scalar +adder method.

Parameters
----------
input : np.ndarray
Sup3r input data to be bias corrected, assumed to be 3D with shape
(spatial, spatial, temporal) for a single feature.
feature_name : str
Name of feature that is being corrected. Datasets with names
"{feature_name}_scalar" and "{feature_name}_adder" will be retrieved
from bias_fp.
bias_fp : str
Filepath to bias correction file from the bias calc module. Must have
datasets "{feature_name}_scalar" and "{feature_name}_adder" that are
the full low-resolution shape of the forward pass input that will be
sliced using lr_padded_slice for the current chunk.
lr_padded_slice : tuple | None
Tuple of length four that slices (spatial_1, spatial_2, temporal,
features) where each tuple entry is a slice object for that axes.
Note that if this method is called as part of a sup3r forward pass, the
lr_padded_slice will be included automatically in the kwargs for the
active chunk. If this is None, no slicing will be done and the full
bias correction source shape will be used.
time_index : pd.DatetimeIndex
DatetimeIndex object associated with the input data temporal axis
(assumed 3rd axis e.g. axis=2). Note that if this method is called as
part of a sup3r resolution forward pass, the time_index will be
included automatically for the current chunk.
temporal_avg : bool
Take the average scalars and adders for the chunk's time index, this
will smooth the transition of scalars/adders from month to month if
processing small chunks. If processing the full annual time index, set
this to False.
out_range : None | tuple
Option to set floor/ceiling values on the output data.

Returns
-------
out : np.ndarray
out = input * scalar + adder
"""

scalar = f'{feature_name}_scalar'
adder = f'{feature_name}_adder'
with Resource(bias_fp) as res:
scalar = res[scalar]
adder = res[adder]

assert len(scalar.shape) == 3, 'Monthly bias correct needs 3D scalars'
assert len(adder.shape) == 3, 'Monthly bias correct needs 3D adders'

if lr_padded_slice is not None:
spatial_slice = (lr_padded_slice[0], lr_padded_slice[1])
scalar = scalar[spatial_slice]
adder = adder[spatial_slice]

imonths = time_index.month.values - 1
scalar = scalar[..., imonths]
adder = adder[..., imonths]

if temporal_avg:
scalar = scalar.mean(axis=-1)
adder = adder.mean(axis=-1)
scalar = np.expand_dims(scalar, axis=-1)
adder = np.expand_dims(adder, axis=-1)
scalar = np.repeat(scalar, input.shape[-1], axis=-1)
adder = np.repeat(adder, input.shape[-1], axis=-1)
if len(time_index.month.unique()) > 2:
msg = ('Bias correction method "monthly_local_linear_bc" was used '
'with temporal averaging over a time index with >2 months.')
warn(msg)
logger.warning(msg)

out = input * scalar + adder
if out_range is not None:
out = np.maximum(out, np.min(out_range))
out = np.minimum(out, np.max(out_range))

return out
15 changes: 9 additions & 6 deletions sup3r/pipeline/forward_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from rex.utilities.execution import SpawnProcessPool

import sup3r.models
import sup3r.bias
import sup3r.bias.bias_transforms
from sup3r.preprocessing.data_handling import InputMixIn
from sup3r.preprocessing.exogenous_data_handling import ExogenousDataHandler
from sup3r.postprocessing.file_handling import (OutputHandlerH5,
Expand Down Expand Up @@ -694,10 +694,11 @@ def __init__(self, file_paths, model_kwargs, fwp_chunk_shape,
pass
bias_correct_method : str | None
Optional bias correction function name that can be imported from
the sup3r.bias module. This will transform the source data
according to some predefined bias correction transformation along
with the bias_correct_kwargs. As the first argument, this method
must receive a generic numpy array of data to be bias corrected
the sup3r.bias.bias_transforms module. This will transform the
source data according to some predefined bias correction
transformation along with the bias_correct_kwargs. As the first
argument, this method must receive a generic numpy array of data to
be bias corrected
bias_correct_kwargs : dict | None
Optional namespace of kwargs to provide to bias_correct_method.
If this is provided, it must be a dictionary where each key is a
Expand Down Expand Up @@ -1394,13 +1395,15 @@ def bias_correct_source_data(self, data):
method = self.strategy.bias_correct_method
kwargs = self.strategy.bias_correct_kwargs
if method is not None:
method = getattr(sup3r.bias, method)
method = getattr(sup3r.bias.bias_transforms, method)
logger.info('Running bias correction with: {}'.format(method))
for feature, feature_kwargs in kwargs.items():
idf = self.data_handler.features.index(feature)

if 'lr_padded_slice' in signature(method).parameters:
feature_kwargs['lr_padded_slice'] = self.lr_padded_slice
if 'time_index' in signature(method).parameters:
feature_kwargs['time_index'] = self.data_handler.time_index

logger.debug('Bias correcting feature "{}" at axis index {} '
'using function: {} with kwargs: {}'
Expand Down
17 changes: 10 additions & 7 deletions sup3r/qa/qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
import numpy as np
import xarray as xr
import logging
from inspect import signature
from rex import Resource
from rex.utilities.fun_utils import get_fun_call_str
import sup3r.bias
import sup3r.bias.bias_transforms
from sup3r.preprocessing.feature_handling import Feature
from sup3r.postprocessing.file_handling import RexOutputs, H5_ATTRS
from sup3r.preprocessing.feature_handling import Feature
from sup3r.utilities import ModuleName
from sup3r.utilities.utilities import (get_input_handler_class,
get_source_type,
Expand Down Expand Up @@ -102,10 +102,11 @@ def __init__(self, source_file_paths, out_file_path, s_enhance, t_enhance,
(only .h5 is supported)
bias_correct_method : str | None
Optional bias correction function name that can be imported from
the sup3r.bias module. This will transform the source data
according to some predefined bias correction transformation along
with the bias_correct_kwargs. As the first argument, this method
must receive a generic numpy array of data to be bias corrected
the sup3r.bias.bias_transforms module. This will transform the
source data according to some predefined bias correction
transformation along with the bias_correct_kwargs. As the first
argument, this method must receive a generic numpy array of data to
be bias corrected
bias_correct_kwargs : dict | None
Optional namespace of kwargs to provide to bias_correct_method.
If this is provided, it must be a dictionary where each key is a
Expand Down Expand Up @@ -320,9 +321,11 @@ def bias_correct_source_data(self, data, feature):
method = self.bias_correct_method
kwargs = self.bias_correct_kwargs
if method is not None:
method = getattr(sup3r.bias, method)
method = getattr(sup3r.bias.bias_transforms, method)
logger.info('Running bias correction with: {}'.format(method))
feature_kwargs = kwargs[feature]
if 'time_index' in signature(method).parameters:
feature_kwargs['time_index'] = self.time_index
logger.debug('Bias correcting feature "{}" using function: {} '
'with kwargs: {}'
.format(feature, method, feature_kwargs))
Expand Down