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

WIP skip over ValueError in _time_reduce #242

Merged
merged 13 commits into from
Dec 5, 2017

Conversation

micahkim23
Copy link
Collaborator

Closes #202

@micahkim23
Copy link
Collaborator Author

@spencerahill @spencerkclark Let me know what you guys think about the pr.

Copy link
Collaborator

@spencerkclark spencerkclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@micahkim23 this looks great so far! It would be great if you could add some tests for this new logic.

I would probably start by creating a variable that was not defined in time in our test object library: https://github.com/spencerahill/aospy/blob/develop/aospy/test/data/objects/examples.py. Then I would write tests to see if the time reductions were skipped when using CalcSuite and that new variable, and if an error was raised if a standalone Calc object was created using that variable that included time reductions in dtype_out_time. The tests would probably be best placed in test_automate.py and test_calc_basic.py.

aospy/calc.py Outdated
msg = ("Var {0} has no time dimension "
"for the given time reduction "
"{1}".format(var.name, reduction))
logging.info(msg)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per #202 (comment), let's raise an exception here instead of just logging a message (probably a ValueError is appropriate here).

Copy link
Collaborator Author

@micahkim23 micahkim23 Dec 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for testing, are the only possible values for dtype_out_time: None, some subset of ['av', 'std, 'ts', 'reg.av', 'reg.std', 'reg.ts', 'None'], or individual strings of: 'av', 'std, 'ts', 'reg.av', 'reg.std', 'reg.ts', 'None'?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 👍

specs = self._combine_core_aux_specs()

for spec in specs:
spec['dtype_out_time'] = _prune_invalid_time_reductions(spec)
Copy link
Collaborator Author

@micahkim23 micahkim23 Dec 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is spec['dtype_out_time'] always a tuple? I'm confused because in here https://github.com/spencerahill/aospy/blob/develop/aospy/calc.py#L153-L156, it's possible for dtype_out_time to be a string, but then it's changed back to a tuple.

Copy link
Collaborator

@spencerkclark spencerkclark Dec 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's reasonably safe to say here that it will be an iterable of some kind; the documentation states that a list is required in the calc_suite_specs dictionary:
http://aospy.readthedocs.io/en/latest/api.html?highlight=calcsuite#aospy.automate.submit_mult_calcs.

Copy link
Owner

@spencerahill spencerahill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @micahkim23 . I think we're most of the way there. A few things though that need fixing or I'm not totally understanding.

@@ -47,6 +47,13 @@ def total_precipitation(convection_rain, condensation_rain):
models=[example_model]
)

precip_largescale = Var(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this new Var object needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just needed to test a Var object with def_time=False.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Let's rename it to make that explicit; otherwise it's confusing why there are two nearly identical objects. E.g. var_not_time_defined = Var(name='var_no_time_def', def_time=False). You can leave alt_names and description unspecified.



@pytest.mark.parametrize(
('var'),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere: parentheses around a single object do nothing: ('var') == 'var'. So I would omit them, except where they are used to break expressions over multiple lines.

Conversely, if there is a trailing comma, it defines a length-1 tuple: e.g. ('var',).

spec = {
'var': var
}
for i in range(1, 8):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more explicit, replace range(1, 8) with range(1, len(time_options) + 1).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are using the string 'None', instead of the built-in None object. I think we want the latter.

('None'),
([])]
)
def test_calc_object_no_time_options(dtype_out_time):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't assert anything; it will only fail if the CalcInterface call fails. So let's add an assertion about the dtype_out_time in the resulting CalcInterface object.

CalcInterface(**test_params)


@pytest.mark.fixture
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fixture decorator shouldn't be used on a test; instead it's used to prepare the data needed for a test. See https://docs.pytest.org/en/latest/fixture.html#fixtures

@@ -51,6 +51,9 @@ Bug Fixes
writing to netCDF files to avoid bugs when using ``libnetcdf``
version 4.5.0 (:pull:`235`). By `Spencer Hill
<https://github.com/spencerahill>`_.
- Skip ``Calc`` object calculation that computes time average of
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand a bit: note that this refers to when using CalcSuite (and thus submit_mult_calcs), and that Calc now raises a ValueError when instantiated with a non-time-defined variable but with one or more time-defined reductions.

- Skip ``Calc`` object calculation that computes time average of
a variable with no time dimension. (closes :issue:`202`
via :pull:`242`). By `Micah Kim <https://github.com/micahkim23>`_.
- In ``CalcSuite`` when calling ``submit_mult_calc``, calculations
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CalcSuite doesn't call submit_mult_calc; it's the other way around.

@@ -351,3 +354,24 @@ def test_permute_aux_specs(self, calc_suite, calcsuite_specs):
assert len(actual) == len(expected)
for act in actual:
assert act in expected


@pytest.mark.parametrize(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style note: splitting across lines like this, with an open parentheses/bracket/curly bracket and then an immediate line break, is usually only needed when either

  1. The items/expression within the overall parentheses/bracket/curly bracket is long enough that this helps it fit onto fewer lines, or at least look nicer.
  2. The items aren't themselves too long to fit on one line each, but there are a lot of them, so it makes it easier to read.

In this case, you don't need to split lines at all; it would fit into a single line:

@pytest.mark.parametrize('var', [precip_largescale, condensation_rain])

Also note more meaningless parentheses; please omit.

@@ -160,6 +161,56 @@ def setUp(self):
}


test_params = {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: indentation is longer than the standard 4 spaces.

(C.f. comment above re: line-splitting: for me this particular case falls into a gray area of intermediate length, where it could work throwing everything onto as few lines as possible, but it's also long enough that what you've done also works.)

}


@pytest.mark.parametrize(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More unneeded parentheses + line breaks

assert calc.dtype_out_time == tuple([dtype_out_time])


@pytest.mark.parametrize(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More unneeded parentheses

@@ -51,6 +51,12 @@ Bug Fixes
writing to netCDF files to avoid bugs when using ``libnetcdf``
version 4.5.0 (:pull:`235`). By `Spencer Hill
<https://github.com/spencerahill>`_.
- In ``CalcSuite`` when calling ``submit_mult_calc``, calculations
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other way around: submit_mult_calc calls CalcSuite. How about "CalcSuite (and thus submit_mult_calcs) now skips calculations that involve..."

@@ -47,6 +47,13 @@ def total_precipitation(convection_rain, condensation_rain):
models=[example_model]
)

precip_largescale = Var(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Let's rename it to make that explicit; otherwise it's confusing why there are two nearly identical objects. E.g. var_not_time_defined = Var(name='var_no_time_def', def_time=False). You can leave alt_names and description unspecified.

Copy link
Collaborator

@spencerkclark spencerkclark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there @micahkim23! Just a few more small things from me.

@@ -22,6 +22,7 @@
_REGIONS_STR = 'regions'
_VARIABLES_STR = 'variables'
_TAG_ATTR_MODIFIERS = dict(all='', default='default_')
_TIME_DEFINED_REDUCTIONS = ['av', 'std', 'ts', 'reg.av', 'reg.std', 'reg.ts']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I think it might be a good idea to keep the definition of these only in calc.py and import them here. This way we won't need to remember to maintain this list in two places.

from calc import _TIME_DEFINED_REDUCTIONS



@pytest.mark.parametrize(
('dtype_out_time'),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove the parentheses here too.

}


@pytest.mark.parametrize(('dtype_out_time'), [None, 'None', []])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove the parentheses around 'dtype_out_time' here. Also, is there a reason we are testing with the value 'None'?



def test_calc_object_time_options():
time_options = ['av', 'std', 'ts', 'reg.av', 'reg.std', 'reg.ts', 'None']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per @spencerahill's earlier comment, should we use None rather than 'None' here?

Copy link
Collaborator Author

@micahkim23 micahkim23 Dec 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'None': lambda xarr: xarr,
@spencerkclark I thought 'None' was a possible time reduction.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @micahkim23! @spencerahill was this intentional? Should we change it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops! Yes, good call @micahkim23 . Sorry if any comments I made previously were confused on this point.

Since 'None' appears to just be a synonym for 'ts', I'd say we just remove it entirely. So there's only ['ts', 'av', 'std', 'reg.ts', 'reg.av', 'reg.std'].

Since custom reductions are a big priority moving forward (#208), this will get refactored soon-ish anyways.

@spencerahill
Copy link
Owner

spencerahill commented Dec 5, 2017

We tempted the AppVeyor gods too long 😜

I'd say this is good to go. Thanks @spencerkclark for the reviews and esp. @micahkim23 for the PR! And sorry again for my leading you astray on the None business. Nice to have this in.

@spencerahill spencerahill merged commit 841a756 into spencerahill:develop Dec 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants