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

CIDEVSTC-291 - Coverage Projection #155

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions coverage_model/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ def append_parameter(self, parameter_context):
if hasattr(pcontext, '_pval_callback'):
pcontext._pval_callback = self.get_parameter_values
pcontext._pctxt_callback = self.get_parameter_context
pcontext._pdir = self.persistence_dir

self._range_dictionary.add_context(pcontext)
s = self._persistence_layer.init_parameter(pcontext, self._bricking_scheme)
Expand Down Expand Up @@ -2186,6 +2187,8 @@ def _doload(self):
if hasattr(pc, '_pval_callback'):
pc._pval_callback = self.get_parameter_values
pc._pctxt_callback = self.get_parameter_context
pc._pdir = self.persistence_dir

self._range_dictionary.add_context(pc)
if pc.param_type._value_class == 'SparseConstantValue':
s = SparsePersistedStorage(md, mm, self._persistence_layer.brick_dispatcher, dtype=pc.param_type.storage_encoding, fill_value=pc.param_type.fill_value, mode=self.mode, inline_data_writes=inline_data_writes, auto_flush=auto_flush_values)
Expand Down
39 changes: 39 additions & 0 deletions coverage_model/parameter_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from numbers import Number
from collections import OrderedDict
from coverage_model.basic_types import AbstractBase
import os


class ParameterFunctionException(Exception):
Expand Down Expand Up @@ -269,3 +270,41 @@ def __eq__(self, other):
ret = self.expression == other.expression

return ret

class ExternalFunction(AbstractFunction):
def __init__(self, name, external_guid, external_name):
self.external_name = external_name
param_map = {external_name : external_guid}
AbstractFunction.__init__(self, name, [], param_map)

def load_coverage(self, pdir):
from coverage_model.coverage import AbstractCoverage
root_path, guid = os.path.split(pdir)
external_guid = self.param_map[self.external_name]
path = os.path.join(root_path, external_guid)
cov = AbstractCoverage.load(path, mode='r')
return cov

def evaluate(self, pval_callback, pdir, slice_, fill_value=-9999):
return self.linear_map(pval_callback, pdir, slice_)

def linear_map(self, pval_callback, pdir, slice_):
cov = self.load_coverage(pdir)
# TODO: Might not want to hard-code time
x = pval_callback('time', slice_)
x_i = cov.get_parameter_values('time')
y_i = cov.get_parameter_values(self.external_name)

# Where in x_i does x fit in?
upper = np.searchsorted(x_i, x)
# Clip values not in [1, N-1]
upper = upper.clip(1, len(x_i)-1).astype(int)
lower = upper - 1

# Linear interpolation
w = (x - x_i[lower]) / (x_i[upper] - x_i[lower])
y = y_i[lower] * (1-w) + y_i[upper] * w
return y



4 changes: 3 additions & 1 deletion coverage_model/parameter_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ def __init__(self, function, value_encoding=None, **kwargs):

self._template_attrs['_pval_callback'] = None
self._template_attrs['_pctxt_callback'] = None
self._template_attrs['_pdir'] = None

self._gen_template_attrs()

Expand All @@ -607,13 +608,14 @@ def get_function_map(self, parent_arg_name=None):

def _todict(self, exclude=None):
# Must exclude _cov_range_value from persistence
return super(ParameterFunctionType, self)._todict(exclude=['_pval_callback', '_pctxt_callback', '_fmap', '_iparams', '_dparams'])
return super(ParameterFunctionType, self)._todict(exclude=['_pdir', '_pval_callback', '_pctxt_callback', '_fmap', '_iparams', '_dparams'])

@classmethod
def _fromdict(cls, cmdict, arg_masks=None):
ret = super(ParameterFunctionType, cls)._fromdict(cmdict, arg_masks=arg_masks)
# Add the _pval_callback attribute, initialized to None
ret._pval_callback = None
ret._pdir = None
return ret

def __eq__(self, other):
Expand Down
8 changes: 6 additions & 2 deletions coverage_model/parameter_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from ooi.logging import log
from coverage_model.basic_types import AbstractBase, InMemoryStorage, VariabilityEnum, Span
from coverage_model.numexpr_utils import is_well_formed_where, nest_wheres
from coverage_model.parameter_functions import ParameterFunctionException
from coverage_model.parameter_functions import ParameterFunctionException, ExternalFunction
from coverage_model import utils
import numpy as np
import numexpr as ne
Expand Down Expand Up @@ -267,6 +267,7 @@ def __init__(self, parameter_type, domain_set, storage=None, **kwargs):

# Grab a local pointer to the coverage's _cov_range_value object
self._pval_callback = self.parameter_type._pval_callback
self._pdir = self.parameter_type._pdir
self._memoized_values = None

@property
Expand All @@ -292,7 +293,10 @@ def __getitem__(self, slice_):
slice_ = utils.fix_slice(slice_, self.shape)

try:
r = self.content.evaluate(self._pval_callback, slice_, self.parameter_type.fill_value)
if isinstance(self.parameter_type.function, ExternalFunction):
r = self.content.evaluate(self._pval_callback, self._pdir, slice_, self.parameter_type.fill_value)
else:
r = self.content.evaluate(self._pval_callback, slice_, self.parameter_type.fill_value)
ve = self.parameter_type.value_encoding
if hasattr(self.parameter_type, 'inner_encoding'):
ve = self.parameter_type.inner_encoding
Expand Down
22 changes: 22 additions & 0 deletions coverage_model/test/test_complex_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from copy import deepcopy
from coverage_model.hdf_utils import HDFLockingFile
from coverage_test_base import CoverageIntTestBase, get_props
from coverage_model.parameter_functions import ExternalFunction
import time


Expand Down Expand Up @@ -168,6 +169,27 @@ def test_aggregates(self):

vcov = ViewCoverage(self.working_dir, create_guid(), 'view coverage', reference_coverage_location = ccov.persistence_dir)

@attr('INT', group='cov')
def test_external_refs(self):

# Create a three param coverage
offset = NumexprFunction('offset', arg_list=['x'], expression='x + 1')
offset.param_map = {'x':'value_set'}
ctx = ParameterContext('offset', param_type=ParameterFunctionType(offset, value_encoding='<f4'))

cova_pth = _make_cov(self.working_dir, ['value_set', ctx], data_dict={'time':np.arange(10), 'value_set':np.arange(20,30)})
cova = SimplexCoverage.load(cova_pth, mode='r')


# Create another coverage that references the above
pfunc = ExternalFunction('example', cova.persistence_guid, 'offset')
ctx = ParameterContext('example', param_type=ParameterFunctionType(pfunc, value_encoding='<f4'))
covb_pth = _make_cov(self.working_dir, [ctx], data_dict={'time':np.arange(0.5, 10.5, 1)})
cov = SimplexCoverage.load(covb_pth, mode='r')
# Assert that the values are correctly interpolated
np.testing.assert_array_equal(cov.get_parameter_values('example'), np.arange(21.5, 31.5, 1))




@attr('INT',group='cov')
Expand Down