irfpy.util.polarframe

Polar frame related module.

This module provides 3-D grid system together with the data allocated on the grid.

Similar functionality is found in irfpy.util.gridsphere module, which is for 2-D mapping on the sphere.

In addition, frame conversion between RTP and XYZ is provided.

Code author: Yoshifumi Futaana

class irfpy.util.polarframe.RegularGridContainer(data, rgrid, tgrid, pgrid)[source]

Bases: object

Container that preserves 3-D polar frame.

Values at regularly gridded spherical coordinates (r, \(\theta\), \(\phi\)).

All should be “ascending order”.

Initialize the container.

Parameters:
  • data – Data at the grid. It must be a numpy array with (nr, nt, np) or (nr, nt, np, data_dimension) array. data is just copied by reference (not deep-copied).

  • rgrid – Grid in the r direction. Should be ascending.

  • tgrid – Grid in the theta direction. Should be ascending. The angle is measured from the north pole. and thus the range is (0, 2|pi|).

  • pgrid – Grid in the phi direction. Should be ascending. Range is user specific; so that you can consider (-\(\pi\), \(\pi\)) or (0, 2 \(\pi\)) or whatever else. It could be more than 1 rotation in principle, namely (0, 4 \(\pi\)), but not well tested for such case.

>>> rgrid = [0, 1, 2, 3, 4, 5]
>>> tgrid = np.deg2rad([40, 60, 80, 100, 120])
>>> pgrid = np.deg2rad([-180, -90, 0, 90])
>>> data = np.arange(6 * 5 * 4).reshape([6, 5, 4])
>>> print(data.shape)
(6, 5, 4)
>>> reg_grid_val = RegularGridContainer(data, rgrid, tgrid, pgrid)
get(rs, ts, ps, degrees=False)[source]

Return the value at the positions. Values are the data of the belonging grids

Parameters:
  • rs – Array of r

  • ts – Array of theta (radians)

  • ps – Array of phi (radians)

  • degrees – Use degrees for ts and ps.

Returns:

The value at the position of (rs, ts, ps).

All the shape rs, ts, ps should be the same. No broadcasting supported (currently).

>>> rs = [0.5, 1.5, 2.5, 3.5, 4.5]
>>> ts = np.deg2rad([30, 90, 150])
>>> ps = np.deg2rad([45, 135, 225, 315])
>>> value = np.arange(5 * 3 * 4).reshape([5, 3, 4])
>>> rgc = RegularGridContainer(value, rs, ts, ps)
>>> print(rgc.get(0.5, 30, 45, degrees=True))
0
>>> print(rgc.get(0.5, 30, 135, degrees=True))
1
>>> print(rgc.get(0, 30, 45, degrees=True))
--

Arrays can be input

>>> print(rgc.get([0, 0.5, 1.5, 6], [30, 30, 30, 30], [45, 45, 45, 45], degrees=True))
[-- 0 12 --]
classmethod zeros(rgrid, tgrid, pgrid)[source]

Create a RegularGridContainer with data of zero.

Parameters:
  • rgrid – Array of r-grid. See __init__()

  • tgrid – Array of t-grid

  • pgrid – Array of p-grid

Returns:

Zero-data filled RegularGridContainer

Equivallent to RegularGridContainer(np.ma.zeros( [len(rgrid), len(tgrid), len(pgrid)]), rgrid, tgrid, pgrid)

>>> rgrid = [1, 3, 5]
>>> tgrid = np.deg2rad([30, 60, 90, 120, 150])
>>> pgrid = np.deg2rad([10, 100, 190, 280])
>>> zero_grid = RegularGridContainer.zeros(rgrid, tgrid, pgrid)
>>> print(zero_grid.data.max())
0.0
>>> print(zero_grid.data.min())
0.0
class irfpy.util.polarframe.RegularGridVolumeContainer(data, rgrid, tgrid, pgrid, rgridsize='linear', tgridsize='linear', pgridsize='linear')[source]

Bases: RegularGridContainer

Container of data in 3-D polar frame with volume information.

The class supports, in addition to the data container of RegularGridContainer, the volume of each bin. Each bin size can explicitly given by the parameters, or can be automatically calculated.

Note

Operators and other functions have NOT yet supported.

Todo

  • Implement zeros function.

  • Implement __add__ and __sub__ functions.

>>> rgrid = [10, 100, 1000, 10000]
>>> tgrid = np.deg2rad([-30, -15, 0, 15, 30])
>>> pgrid = np.deg2rad([300, 350, 400, 450, 500, 550])
>>> RegularGridVolumeContainer.zeros(rgrid, tgrid, pgrid) 
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NotImplementedError: ...
Parameters:
  • data – Data, passed to RegularGridContainer

  • rgrid – Grid in r direction. Ascending.

  • tgrid – Grid in \(\theta\) direction. Ascending. Radians.

  • pgrid – Grid in \(\phi\) direction. Ascending. Radians. (If periodic condition will make the array non-ascending, the array should be sorted by using, for example, irfpy.util.nputils.sort_periodic().)

  • rgridsize – Size for each bin. Numpy array with the same dimension as rgrid. Automatic guess is available with ‘linear’ or ‘log’ options, where irfpy.util.nptools.guess_upper() and irfpy.util.nptools.guess_lower() is used.

  • tgridsize – Identical to rgridsize, but for theta direction.

  • pgridsize – Identical to rgridsize, but for phi direction.

>>> rgrid = [10, 100, 1000, 10000]
>>> tgrid = np.deg2rad([-30, -15, 0, 15, 30])
>>> pgrid = np.deg2rad([300, 350, 400, 450, 500, 550])
>>> data = np.arange(120).reshape([4, 5, 6])
>>> regGrid = RegularGridVolumeContainer(data, rgrid, tgrid, pgrid, rgridsize='log')
>>> print(regGrid.rgridsize)    
[2.84604989e+01   2.84604989e+02   2.84604989e+03   2.84604989e+04]
>>> print(regGrid.tgridsize)    
[0.26179939  0.26179939  0.26179939  0.26179939  0.26179939]
>>> print(regGrid.pgridsize)    
[0.87266463  0.87266463  0.87266463  0.87266463  0.87266463  0.87266463]
volume()[source]

Return a matrix of volume for each grid.

Returns:

The volume in (nr, nt, nph) shaped array.

The volume of the each bin is

\[V = r^2 \sin\theta \Delta r\Delta\theta\Delta\phi\]
>>> egrid_c, egrid_b, egrid_d = nptools.loggrids(1, 2, 80)
>>> tgrid_c, tgrid_b, tgrid_d = nptools.lingrids(0, np.pi, 50)
>>> pgrid_c, pgrid_b, pgrid_d = nptools.lingrids(-np.pi, np.pi, 90)
>>> val = np.ones([80, 50, 90])
>>> vol = RegularGridVolumeContainer(val, egrid_c, tgrid_c, pgrid_c, rgridsize='log')
>>> dvol = vol.volume()
>>> print('{:.3e}'.format((vol.data * dvol).sum()))   # This is theoretically the sphere of radius of r1 - that of r0, i.e. 4.1846e6
4.184e+06
integrate()[source]

A simple integrator.

Returns:

The integrated value.

Integration is done as

\[\int\int\int y(r, \theta, \phi) r^2 \sin\theta dr d\theta d\phi\]
>>> egrid_c, egrid_b, egrid_d = nptools.loggrids(1, 2, 80)
>>> tgrid_c, tgrid_b, tgrid_d = nptools.lingrids(0, np.pi, 50)
>>> pgrid_c, pgrid_b, pgrid_d = nptools.lingrids(-np.pi, np.pi, 90)
>>> val = np.ones([80, 50, 90])
>>> vol = RegularGridVolumeContainer(val, egrid_c, tgrid_c, pgrid_c, rgridsize='log')
>>> print('{:.3e}'.format(vol.integrate()))   # This is theoretically the sphere of radius of r1 - that of r0, i.e. 4.1846e6
4.184e+06
irfpy.util.polarframe.rtp2xyz(r, t, p, longitude_deg, latitude_deg)[source]

RTP frame to xyz frame

RTP (radius, \(\theta\), \(\phi\)) frame is frequently used to represent a local vector measurement, such as velocity vector or magnetic field vector on a sphere (e.g. Earth). (XYZ is the global frame. This defines longitude and latitude.)

Unit vectors for R, T, and P is defined as below:

  • R: The radial direction from the center.

  • \(\theta\) : The south-ward component, tangential to the sphere surface.

  • \(\phi\) : The east-ward component, tangential to the sphere surface.

The conversion is

\[\begin{split}x = r \cos\lambda\cos\alpha + \theta\sin\lambda\cos\alpha - \phi\sin\alpha \\ y = r \cos\lambda\sin\alpha + \theta\sin\lambda\sin\alpha + \phi\cos\alpha \\ z = r \sin\alpha - \theta\cos\alpha\end{split}\]

Here \(\alpha\) is the longitude, and \(\lambda\) is the latitude.

Parameters:
  • r – R, scalar or numpy array.

  • t – T, scalar or numpy array.

  • p – P, scalar or numpy array. Shapes of r, t, p should be the same.

  • longitude_deg – Longitude in degrees. scalar or numpy array. Same shape as r, or scalar.

  • latitude_deg – Latitude in degrees. scalar or numpy array. Same shape as r, or scalar. Same shape as longitude_deg.

Returns:

[x, y, z] as numpy float64 or numpy array.

For the spacecraft position at (1, 0, 0), then the rtp=(1, 0, 0) will be xyz=(1, 0, 0). rtp=(0, 1, 0) will be xyz=(0, 0, -1). rtp=(0, 0, 1) will be xyz=(0, 1, 0)

>>> print(rtp2xyz(1, 0, 0, 0, 0))
(1.0, 0.0, 0.0)
>>> print(rtp2xyz(0, 1, 0, 0, 0))
(0.0, 0.0, -1.0)
>>> print(rtp2xyz(0, 0, 1, 0, 0))
(0.0, 1.0, 0.0)

For the spacecraft position at (0, 1, 0)=(lon=90, lat=0), then the rtp=(1, 0, 0) will be xyz=(0, 1, 0). rtp=(0, 1, 0) will be (0, 0, -1). rtp=(0, 0, 1) will be xyz=(-1, 0, 0)

>>> print(rtp2xyz(1, 0, 0, 90, 0))  # tiny residual.  
(0.0, 1.0, 0.0)
>>> print(rtp2xyz(0, 1, 0, 90, 0))
(0.0, 0.0, -1.0)
>>> print(rtp2xyz(0, 0, 1, 90, 0))  # tiny residual.  
(-1.0, 0.0, 0.0)

For the spacecraft position at lon=0, lat=45, then the rtp=(0, 0, 1) is xyz=(0, 1, 0)

>>> print(rtp2xyz(0, 0, 1, 0, 45))
(0.0, 1.0, 0.0)

rtp=(1, 0, 0) is (0.707, 0, 0.707) and rtp=(0, 1, 0) is (0.707, 0, -0.707)

>>> print(rtp2xyz(1, 0, 0, 0, 45))  
(0.7071067811865476, 0.0, 0.7071067811865475)
>>> print(rtp2xyz(0, 1, 0, 0, 45))  
(0.7071067811865475, 0.0, -0.7071067811865476)

Multidiemnsional version.

>>> r = [1, 0]
>>> t = [0, 1]
>>> p = [0, 0]
>>> lon = [0, 90]
>>> lat = [0, 0]
>>> x, y, z = rtp2xyz(r, t, p, lon, lat)
>>> print(x)   
[1.  0.]
>>> print(y)   
[0.  0.]
>>> print(z)
[ 0. -1.]
irfpy.util.polarframe.xyz2rtp(x, y, z, longitude_deg, latitude_deg)[source]

XYZ frame to RTP frame.

See rtp2xyz() for details on RTP frame.

Parameters:
  • x – X, scalar

  • y – Y, scalar

  • z – Z, scalar

  • longitude_deg – Longitude of the “observer”

  • latitude_deg – Latitude of the “observer”

Returns:

(r, t, p) each as numpy.float or numpy.array

>>> print(xyz2rtp(0, 0, -1, 90, 0))
(0.0, 1.0, 0.0)
>>> print(xyz2rtp(0, 1, 0, 0, 45))
(0.0, 0.0, 1.0)
>>> print(rtp2xyz(1, 0, 0, 0, 0))
(1.0, 0.0, 0.0)

Multi-dimensional support.

>>> x = [0, 0]
>>> y = [0, 1]
>>> z = [-1, 0]
>>> lon = [90, 0]
>>> lat = [0, 45]
>>> r, t, p = xyz2rtp(x, y, z, lon, lat)
>>> print(r)   
[0.  0.]
>>> print(t)   
[1.  0.]
>>> print(p)   
[0.  1.]