irfpy.util.polarframe
¶
Polar frame related module.
This module provides 3-D grid system together with the data allocated on the grid.
RegularGridContainer
can store values on a regularly gridded sphere coordinate system.RegularGridVolumeContainer
extends theRegularGridContainer
with volume support.
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()
andirfpy.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.]