Skip to content

Commit 802486a

Browse files
committed
Merge remote-tracking branch 'upstream/main' into pydata_theme
* upstream/main: tweak whatsnew (SciTools#4682) Bump peter-evans/create-pull-request from 4.0.0 to 4.0.1 (SciTools#4675) Reorder Constraints sections of user guide, add time bounds example (SciTools#4656) Updated environment lockfiles (SciTools#4678) Updated environment lockfiles (SciTools#4677) Automatic reversal of DimCoord bounds (SciTools#4466) 'Deep' benchmarks to catch scaling with field count (SciTools#4654) Updated environment lockfiles (SciTools#4655)
2 parents 9a3ab26 + 3a8eee4 commit 802486a

14 files changed

Lines changed: 404 additions & 321 deletions

File tree

.github/workflows/refresh-lockfiles.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
7373
- name: Create Pull Request
7474
id: cpr
75-
uses: peter-evans/create-pull-request@d6d5519d05f5814158ef015b8448f2f74648c421
75+
uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f
7676
with:
7777
commit-message: Updated environment lockfiles
7878
committer: "Lockfile bot <[email protected]>"

benchmarks/benchmarks/load/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424

2525

2626
class LoadAndRealise:
27+
# For data generation
28+
timeout = 600.0
2729
params = [
28-
[(2, 2, 2), (1280, 960, 5)],
30+
[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)],
2931
[False, True],
3032
["FF", "PP", "NetCDF"],
3133
]
@@ -68,7 +70,7 @@ def time_realise(self, _, __, ___, ____) -> None:
6870

6971
class STASHConstraint:
7072
# xyz sizes mimic LoadAndRealise to maximise file re-use.
71-
params = [[(2, 2, 2), (1280, 960, 5)], ["FF", "PP"]]
73+
params = [[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], ["FF", "PP"]]
7274
param_names = ["xyz", "file_format"]
7375

7476
def setup_cache(self) -> dict:
@@ -155,7 +157,7 @@ class StructuredFF:
155157
avoiding the cost of merging.
156158
"""
157159

158-
params = [[(2, 2, 2), (1280, 960, 5)], [False, True]]
160+
params = [[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], [False, True]]
159161
param_names = ["xyz", "structured_loading"]
160162

161163
def setup_cache(self) -> dict:

docs/src/userguide/cube_maths.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Let's load some air temperature which runs from 1860 to 2100::
3838
air_temp = iris.load_cube(filename, 'air_temperature')
3939

4040
We can now get the first and last time slices using indexing
41-
(see :ref:`subsetting_a_cube` for a reminder)::
41+
(see :ref:`cube_indexing` for a reminder)::
4242

4343
t_first = air_temp[0, :, :]
4444
t_last = air_temp[-1, :, :]

docs/src/userguide/loading_iris_cubes.rst

Lines changed: 2 additions & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -206,241 +206,8 @@ a specific ``model_level_number``::
206206
level_10 = iris.Constraint(model_level_number=10)
207207
cubes = iris.load(filename, level_10)
208208

209-
Constraints can be combined using ``&`` to represent a more restrictive
210-
constraint to ``load``::
211-
212-
filename = iris.sample_data_path('uk_hires.pp')
213-
forecast_6 = iris.Constraint(forecast_period=6)
214-
level_10 = iris.Constraint(model_level_number=10)
215-
cubes = iris.load(filename, forecast_6 & level_10)
216-
217-
.. note::
218-
219-
Whilst ``&`` is supported, the ``|`` that might reasonably be expected is
220-
not. Explanation as to why is in the :class:`iris.Constraint` reference
221-
documentation.
222-
223-
For an example of constraining to multiple ranges of the same coordinate to
224-
generate one cube, see the :class:`iris.Constraint` reference documentation.
225-
226-
To generate multiple cubes, each constrained to a different range of the
227-
same coordinate, use :py:func:`iris.load_cubes`.
228-
229-
As well as being able to combine constraints using ``&``,
230-
the :class:`iris.Constraint` class can accept multiple arguments,
231-
and a list of values can be given to constrain a coordinate to one of
232-
a collection of values::
233-
234-
filename = iris.sample_data_path('uk_hires.pp')
235-
level_10_or_16_fp_6 = iris.Constraint(model_level_number=[10, 16], forecast_period=6)
236-
cubes = iris.load(filename, level_10_or_16_fp_6)
237-
238-
A common requirement is to limit the value of a coordinate to a specific range,
239-
this can be achieved by passing the constraint a function::
240-
241-
def bottom_16_levels(cell):
242-
# return True or False as to whether the cell in question should be kept
243-
return cell <= 16
244-
245-
filename = iris.sample_data_path('uk_hires.pp')
246-
level_lt_16 = iris.Constraint(model_level_number=bottom_16_levels)
247-
cubes = iris.load(filename, level_lt_16)
248-
249-
.. note::
250-
251-
As with many of the examples later in this documentation, the
252-
simple function above can be conveniently written as a lambda function
253-
on a single line::
254-
255-
bottom_16_levels = lambda cell: cell <= 16
256-
257-
258-
Note also the :ref:`warning on equality constraints with floating point coordinates <floating-point-warning>`.
259-
260-
261-
Cube attributes can also be part of the constraint criteria. Supposing a
262-
cube attribute of ``STASH`` existed, as is the case when loading ``PP`` files,
263-
then specific STASH codes can be filtered::
264-
265-
filename = iris.sample_data_path('uk_hires.pp')
266-
level_10_with_stash = iris.AttributeConstraint(STASH='m01s00i004') & iris.Constraint(model_level_number=10)
267-
cubes = iris.load(filename, level_10_with_stash)
268-
269-
.. seealso::
270-
271-
For advanced usage there are further examples in the
272-
:class:`iris.Constraint` reference documentation.
273-
274-
275-
Constraining a Circular Coordinate Across its Boundary
276-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
277-
278-
Occasionally you may need to constrain your cube with a region that crosses the
279-
boundary of a circular coordinate (this is often the meridian or the dateline /
280-
antimeridian). An example use-case of this is to extract the entire Pacific Ocean
281-
from a cube whose longitudes are bounded by the dateline.
282-
283-
This functionality cannot be provided reliably using constraints. Instead you should use the
284-
functionality provided by :meth:`cube.intersection <iris.cube.Cube.intersection>`
285-
to extract this region.
286-
287-
288-
.. _using-time-constraints:
289-
290-
Constraining on Time
291-
^^^^^^^^^^^^^^^^^^^^
292-
Iris follows NetCDF-CF rules in representing time coordinate values as normalised,
293-
purely numeric, values which are normalised by the calendar specified in the coordinate's
294-
units (e.g. "days since 1970-01-01").
295-
However, when constraining by time we usually want to test calendar-related
296-
aspects such as hours of the day or months of the year, so Iris
297-
provides special features to facilitate this:
298-
299-
Firstly, when Iris evaluates Constraint expressions, it will convert time-coordinate
300-
values (points and bounds) from numbers into :class:`~datetime.datetime`-like objects
301-
for ease of calendar-based testing.
302-
303-
>>> filename = iris.sample_data_path('uk_hires.pp')
304-
>>> cube_all = iris.load_cube(filename, 'air_potential_temperature')
305-
>>> print('All times :\n' + str(cube_all.coord('time')))
306-
All times :
307-
DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar)
308-
points: [2009-11-19 10:00:00, 2009-11-19 11:00:00, 2009-11-19 12:00:00]
309-
shape: (3,)
310-
dtype: float64
311-
standard_name: 'time'
312-
>>> # Define a function which accepts a datetime as its argument (this is simplified in later examples).
313-
>>> hour_11 = iris.Constraint(time=lambda cell: cell.point.hour == 11)
314-
>>> cube_11 = cube_all.extract(hour_11)
315-
>>> print('Selected times :\n' + str(cube_11.coord('time')))
316-
Selected times :
317-
DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar)
318-
points: [2009-11-19 11:00:00]
319-
shape: (1,)
320-
dtype: float64
321-
standard_name: 'time'
322-
323-
Secondly, the :class:`iris.time` module provides flexible time comparison
324-
facilities. An :class:`iris.time.PartialDateTime` object can be compared to
325-
objects such as :class:`datetime.datetime` instances, and this comparison will
326-
then test only those 'aspects' which the PartialDateTime instance defines:
327-
328-
>>> import datetime
329-
>>> from iris.time import PartialDateTime
330-
>>> dt = datetime.datetime(2011, 3, 7)
331-
>>> print(dt > PartialDateTime(year=2010, month=6))
332-
True
333-
>>> print(dt > PartialDateTime(month=6))
334-
False
335-
>>>
336-
337-
These two facilities can be combined to provide straightforward calendar-based
338-
time selections when loading or extracting data.
339-
340-
The previous constraint example can now be written as:
341-
342-
>>> the_11th_hour = iris.Constraint(time=iris.time.PartialDateTime(hour=11))
343-
>>> print(iris.load_cube(
344-
... iris.sample_data_path('uk_hires.pp'),
345-
... 'air_potential_temperature' & the_11th_hour).coord('time'))
346-
DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar)
347-
points: [2009-11-19 11:00:00]
348-
shape: (1,)
349-
dtype: float64
350-
standard_name: 'time'
351-
352-
It is common that a cube will need to be constrained between two given dates.
353-
In the following example we construct a time sequence representing the first
354-
day of every week for many years:
355-
356-
.. testsetup:: timeseries_range
357-
358-
import datetime
359-
import numpy as np
360-
from iris.time import PartialDateTime
361-
long_ts = iris.cube.Cube(np.arange(150), long_name='data', units='1')
362-
_mondays = iris.coords.DimCoord(7 * np.arange(150), standard_name='time', units='days since 2007-04-09')
363-
long_ts.add_dim_coord(_mondays, 0)
364-
365-
366-
.. doctest:: timeseries_range
367-
:options: +NORMALIZE_WHITESPACE, +ELLIPSIS
368-
369-
>>> print(long_ts.coord('time'))
370-
DimCoord : time / (days since 2007-04-09, gregorian calendar)
371-
points: [
372-
2007-04-09 00:00:00, 2007-04-16 00:00:00, ...,
373-
2010-02-08 00:00:00, 2010-02-15 00:00:00]
374-
shape: (150,)
375-
dtype: int64
376-
standard_name: 'time'
377-
378-
Given two dates in datetime format, we can select all points between them.
379-
380-
.. doctest:: timeseries_range
381-
:options: +NORMALIZE_WHITESPACE, +ELLIPSIS
382-
383-
>>> d1 = datetime.datetime.strptime('20070715T0000Z', '%Y%m%dT%H%MZ')
384-
>>> d2 = datetime.datetime.strptime('20070825T0000Z', '%Y%m%dT%H%MZ')
385-
>>> st_swithuns_daterange_07 = iris.Constraint(
386-
... time=lambda cell: d1 <= cell.point < d2)
387-
>>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07)
388-
>>> print(within_st_swithuns_07.coord('time'))
389-
DimCoord : time / (days since 2007-04-09, gregorian calendar)
390-
points: [
391-
2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00,
392-
2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00]
393-
shape: (6,)
394-
dtype: int64
395-
standard_name: 'time'
396-
397-
Alternatively, we may rewrite this using :class:`iris.time.PartialDateTime`
398-
objects.
399-
400-
.. doctest:: timeseries_range
401-
:options: +NORMALIZE_WHITESPACE, +ELLIPSIS
402-
403-
>>> pdt1 = PartialDateTime(year=2007, month=7, day=15)
404-
>>> pdt2 = PartialDateTime(year=2007, month=8, day=25)
405-
>>> st_swithuns_daterange_07 = iris.Constraint(
406-
... time=lambda cell: pdt1 <= cell.point < pdt2)
407-
>>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07)
408-
>>> print(within_st_swithuns_07.coord('time'))
409-
DimCoord : time / (days since 2007-04-09, gregorian calendar)
410-
points: [
411-
2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00,
412-
2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00]
413-
shape: (6,)
414-
dtype: int64
415-
standard_name: 'time'
416-
417-
A more complex example might require selecting points over an annually repeating
418-
date range. We can select points within a certain part of the year, in this case
419-
between the 15th of July through to the 25th of August. By making use of
420-
PartialDateTime this becomes simple:
421-
422-
.. doctest:: timeseries_range
423-
424-
>>> st_swithuns_daterange = iris.Constraint(
425-
... time=lambda cell: PartialDateTime(month=7, day=15) <= cell < PartialDateTime(month=8, day=25))
426-
>>> within_st_swithuns = long_ts.extract(st_swithuns_daterange)
427-
...
428-
>>> # Note: using summary(max_values) to show more of the points
429-
>>> print(within_st_swithuns.coord('time').summary(max_values=100))
430-
DimCoord : time / (days since 2007-04-09, gregorian calendar)
431-
points: [
432-
2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00,
433-
2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00,
434-
2008-07-21 00:00:00, 2008-07-28 00:00:00, 2008-08-04 00:00:00,
435-
2008-08-11 00:00:00, 2008-08-18 00:00:00, 2009-07-20 00:00:00,
436-
2009-07-27 00:00:00, 2009-08-03 00:00:00, 2009-08-10 00:00:00,
437-
2009-08-17 00:00:00, 2009-08-24 00:00:00]
438-
shape: (17,)
439-
dtype: int64
440-
standard_name: 'time'
441-
442-
Notice how the dates printed are between the range specified in the ``st_swithuns_daterange``
443-
and that they span multiple years.
209+
Further details on using :class:`iris.Constraint` are
210+
discussed later in :ref:`cube_extraction`.
444211

445212
.. _strict-loading:
446213

0 commit comments

Comments
 (0)