Skip to content

Commit d794fa1

Browse files
committed
New radial profiles interface
The previous interface was exposing a pandas dataframe, which itself has an API that's unecessarily complex for our goals and not flexible as subclassing dataframes properly is difficult. In particular, this led to the awkward rprof.get_rprof interface to get RPROF_EXTRA variables. This commit fixes these problems by implementing a rprof interface as member of Step instances which abstracts rprof.get_rprof as well as misc.get_rbounds. In particular: - step.rprofs replaces step.rprof (for consistency with tracers and fields attributes which have a plural name); - items of rprofs are named tuple with values, rad, and meta fields; - EXTRA variables are seamlessly accessible through the same interface; - rprof.get_rprof is removed as it is no longer necessary; - step.rprofs.bounds replaces misc.get_rbounds; - step.rprofs.centers offers easy access to cell center location; - step.rprofs.walls offers access to walls location and replaces the redges EXTRA variable (which is dropped).
1 parent 3c47dcf commit d794fa1

File tree

11 files changed

+189
-189
lines changed

11 files changed

+189
-189
lines changed

docs/sources/stagyydata.rst

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,23 @@ are given a default value according to the par file ``~/.config/stagpy/par``.
8686
Radial profiles
8787
---------------
8888

89-
Radial profile data are contained in the :attr:`~stagpy._step.Step.rprof`
90-
attribute of a :class:`~stagpy._step.Step` instance. This attribute is a
91-
:class:`pandas.DataFrame`. Its :attr:`columns` are the names of available
92-
variables (such as e.g. ``'Tmean'`` and ``'ftop'``). Its :attr:`index` is the
93-
cell number (from ``0`` to ``nz-1``). The list of available variables can be
94-
obtained by running ``% stagpy var``.
89+
Radial profile data are accessible trough the :attr:`~stagpy._step.Step.rprofs`
90+
attribute of a :class:`~stagpy._step.Step` instance. This attribute implements
91+
getitem to access radial profiles. Keys are the names of available
92+
variables (such as e.g. ``'Tmean'`` and ``'ftop'``). Items are named tuples
93+
with three fields:
94+
95+
- :data:`values`: the profile itself;
96+
- :data:`rad`: the radial position at which the profile is evaluated;
97+
- :data:`meta`: metadata of the profile, also a named tuple with:
98+
99+
- :data:`description`: explanation of what the profile is;
100+
- :data:`kind`: the category of profile;
101+
- :data:`dim`: the dimension of the profile (if applicable) in SI units.
102+
103+
The list of available variables can be obtained by running ``% stagpy var``.
95104

96-
For example, ``sdat.steps[1000].rprof['Tmean']`` is the temperature profile of
105+
For example, ``sdat.steps[1000].rprofs['Tmean']`` is the temperature profile of
97106
the 1000th timestep.
98107

99108
Time series

docs/sources/tuto.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Instantiating and using this class is rather simple::
8787
sdat = StagyyData('path/to/run/')
8888

8989
# absolute vertical velocity profile of last snapshot
90-
last_v_prof = sdat.snaps[-1].rprof['vzabs']
90+
last_v_prof = sdat.snaps[-1].rprof['vzabs'].values
9191

9292
# temperature field of the 10000th time step
9393
# (will be None if no snapshot is available at this timestep)

stagpy/_step.py

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
"""
88

99
from collections.abc import Mapping
10+
from collections import namedtuple
1011
from itertools import chain
1112
import re
1213

1314
import numpy as np
1415

15-
from . import error, phyvars, stagyyparsers
16+
from . import error, misc, phyvars, stagyyparsers
1617

1718

1819
UNDETERMINED = object()
@@ -370,6 +371,103 @@ def __iter__(self):
370371
raise TypeError('tracers collection is not iterable')
371372

372373

374+
Rprof = namedtuple('Rprof', ['values', 'rad', 'meta'])
375+
376+
377+
class _Rprofs:
378+
"""Radial profiles data structure.
379+
380+
The :attr:`Step.rprofs` attribute is an instance of this class.
381+
382+
:class:`_Rprofs` implements the getitem mechanism. Keys are profile names
383+
defined in :data:`stagpy.phyvars.RPROF[_EXTRA]`. An item is a named tuple
384+
('values', 'rad', 'meta'), respectively the profile itself, the radial
385+
position at which it is evaluated, and meta is a
386+
:class:`stagpy.phyvars.Varr` instance with relevant metadata. Note that
387+
profiles are automatically scaled if conf.scaling.dimensional is True.
388+
389+
Attributes:
390+
step (:class:`Step`): the step object owning the :class:`_Rprofs`
391+
instance
392+
"""
393+
394+
def __init__(self, step):
395+
self.step = step
396+
self._data = UNDETERMINED
397+
self._centers = UNDETERMINED
398+
self._walls = UNDETERMINED
399+
self._bounds = UNDETERMINED
400+
401+
@property
402+
def _rprofs(self):
403+
if self._data is UNDETERMINED:
404+
step = self.step
405+
self._data = step.sdat._rprof_and_times[0].get(step.istep)
406+
if self._data is None:
407+
raise error.MissingDataError('No rprof data in step {} of {}'
408+
.format(step.istep, step.sdat))
409+
return self._data
410+
411+
def __getitem__(self, name):
412+
step = self.step
413+
if name in self._rprofs.columns:
414+
rprof = self._rprofs[name].values
415+
rad = self.centers
416+
if name in phyvars.RPROF:
417+
meta = phyvars.RPROF[name]
418+
else:
419+
meta = phyvars.Varr(name, None, '1')
420+
elif name in phyvars.RPROF_EXTRA:
421+
meta = phyvars.RPROF_EXTRA[name]
422+
rprof, rad = meta.description(step)
423+
meta = phyvars.Varr(misc.baredoc(meta.description),
424+
meta.kind, meta.dim)
425+
else:
426+
raise error.UnknownRprofVarError(name)
427+
rprof, _ = step.sdat.scale(rprof, meta.dim)
428+
rad, _ = step.sdat.scale(rad, 'm')
429+
430+
return Rprof(rprof, rad, meta)
431+
432+
@property
433+
def centers(self):
434+
"""Radial position of cell centers."""
435+
if self._centers is UNDETERMINED:
436+
self._centers = self._rprofs['r'].values + self.bounds[0]
437+
return self._centers
438+
439+
@property
440+
def walls(self):
441+
"""Radial position of cell walls."""
442+
if self._walls is UNDETERMINED:
443+
rbot, rtop = self.bounds
444+
centers = self.centers
445+
# assume walls are mid-way between T-nodes
446+
# could be T-nodes at center between walls
447+
self._walls = (centers[:-1] + centers[1:]) / 2
448+
self._walls = np.insert(self._walls, 0, rbot)
449+
self._walls = np.append(self._walls, rtop)
450+
return self._walls
451+
452+
@property
453+
def bounds(self):
454+
"""Radial or vertical position of boundaries.
455+
456+
Radial/vertical positions of boundaries of the domain.
457+
"""
458+
if self._bounds is UNDETERMINED:
459+
step = self.step
460+
if step.geom is not None:
461+
rcmb = step.geom.rcmb
462+
else:
463+
rcmb = step.sdat.par['geometry']['r_cmb']
464+
if step.sdat.par['geometry']['shape'].lower() == 'cartesian':
465+
rcmb = 0
466+
rcmb = max(rcmb, 0)
467+
self._bounds = rcmb, rcmb + 1
468+
return self._bounds
469+
470+
373471
class Step:
374472
"""Time step data structure.
375473
@@ -416,6 +514,7 @@ def __init__(self, istep, sdat):
416514
self.sfields = _Fields(self, phyvars.SFIELD, [],
417515
phyvars.SFIELD_FILES, phyvars.SFIELD_FILES_H5)
418516
self.tracers = _Tracers(self)
517+
self.rprofs = _Rprofs(self)
419518
self._isnap = UNDETERMINED
420519

421520
@property
@@ -438,16 +537,6 @@ def timeinfo(self):
438537
return None
439538
return self.sdat.tseries.loc[self.istep]
440539

441-
@property
442-
def rprof(self):
443-
"""Radial profiles data of the time step.
444-
445-
This is a :class:`pandas.DataFrame` with iz as index and variable names
446-
as columns. Set to None if no radial profiles data is available for
447-
this time step.
448-
"""
449-
return self.sdat._rprof_and_times[0].get(self.istep)
450-
451540
@property
452541
def isnap(self):
453542
"""Snapshot index corresponding to time step.

stagpy/misc.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,6 @@ def set_of_vars(lovs):
121121
return set(var for pvars in lovs for svars in pvars for var in svars)
122122

123123

124-
def get_rbounds(step):
125-
"""Radial or vertical position of boundaries.
126-
127-
Args:
128-
step (:class:`~stagpy.stagyydata._Step`): a step of a StagyyData
129-
instance.
130-
Returns:
131-
tuple of floats: radial or vertical positions of boundaries of the
132-
domain.
133-
"""
134-
if step.geom is not None:
135-
rcmb = step.geom.rcmb
136-
else:
137-
rcmb = step.sdat.par['geometry']['r_cmb']
138-
if step.sdat.par['geometry']['shape'].lower() == 'cartesian':
139-
rcmb = 0
140-
rcmb = max(rcmb, 0)
141-
return rcmb, rcmb + 1
142-
143-
144124
class InchoateFiles:
145125
"""Context manager handling files whose names are not known yet.
146126

stagpy/phyvars.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@
192192
))
193193

194194
RPROF_EXTRA = OrderedDict((
195-
('redges', Varr(processing.r_edges, 'Radius', 'm')),
196195
('dr', Varr(processing.delta_r, 'dr', 'm')),
197196
('diff', Varr(processing.diff_prof, 'Heat flux', 'W/m2')),
198197
('diffs', Varr(processing.diffs_prof, 'Heat flux', 'W/m2')),

stagpy/plates.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,11 @@ def main_plates(sdat):
593593
"""Plot several plates information."""
594594
# calculating averaged horizontal surface velocity
595595
# needed for redimensionalisation
596-
rlast = sdat.snaps[-1].rprof
596+
rlast = sdat.snaps[-1].rprofs
597597
nprof = 0
598-
uprof_averaged = rlast.loc[:, 'vhrms'] * 0
599-
for step in sdat.walk.filter(rprof=True):
600-
uprof_averaged += step.rprof['vhrms']
598+
uprof_averaged = np.zeros_like(rlast['vhrms'].values)
599+
for step in sdat.walk.filter(rprofs=True):
600+
uprof_averaged += step.rprofs['vhrms'].values
601601
nprof += 1
602602
uprof_averaged /= nprof
603603
radius = rlast['r'].values

0 commit comments

Comments
 (0)