""" The swarm_util module provides a host of utilities function.
This module stores a vararity of different functions and constants,
which is deemed general usefull when working on swarm data,
specifically when such data is loaded from
the viresclient python client.
There is a mix of general constants, such as the permuability of vacum,
and different types of functions.
The functions can generally be categorised into twi types:
Functions for working on arrays of scalars or vectors,
and functions to work with viresclient.
These categories have their own sections, with a short descriptions on each.
Array Utilities
---------------
These functions are used to work on numpy arrays of scalars or vectors.
They are mostly geared toward dealign with time-like series.
pack_3d
Take 3 equaly long arrays of scalars and make an array of 3d vectors.
as_3d
Like pack_3d, but uses dublicates of the same array of scalars.
Usefull for aggregating scalar-vector operations over an array.
map_3d
A map function over arrays of 3d vectors,
where the function maped takes a 3d vector to a 3d vector.
NEC_to_VSC
Build a transformation from the NEC frame to the VSC frame.
The return is a function, which will transfrom
its input from the NEC frame to the VSC frame.
delta
Calculate the changes of a time-like series.
means
Calculate the intermediate value (by linear interpolation)
for a time-like series.
spherical_delta
Calculate the changes in spherical coordinates as NEC frame vectors.
curl
Calculate a single component of curl for an array of vectors.
inclination
Calculate the inclination for an awway of vectors.
Viresclient
-----------
These are the functions related to fetching and loading data
from the viresclient python client.
request_data
Builds and send a request for a CDF file to vires.
Most usefull for defaulting all the relevant parameters.
read_cdf
A function to read a cdf file based on a dictionary pairing
of attributes you want to read from the cdf file,
and what you want to refere to them as afterwards.
"""
__version__ = '0.1.3'
__author__ = 'Ask Neve Gamby'
import datetime as date
import getpass
import numpy as np
import cdflib
from viresclient import SwarmRequest, ClientConfig
MU_0 = 4.0 * np.pi * 10**(-7)
TO_RADIANS = np.pi / 180
[docs]def pack_3d(xs, ys, zs):
""" Pack 3 array-likes into an array of 3d vectors.
Parameters
----------
xs : array-like
The first (xs) components of the final vector.
Must be equivalent to an array of scalars,
generators are not accepted (though ranges are).
Must have a reasoable response to len(xs),
which must be the same as len(ys) and len(zs).
ys : array-like
The second (ys) components of the final vector.
Must be equivalent to an array of scalars,
generators are not accepted.
Must have a reasoable response to len(ys),
which must be the same as len(xs) and len(zs).
zs : array-like
The third (zs) components of the final vector.
Must be equivalent to an array of scalars,
generators are not accepted.
Must have a reasoable response to len(zs),
which must be the same as len(ys) and len(xs).
Returns
-------
ndarray
An array of vectors each with 3 dimensions,
and the same length as xs.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> pack_3d(range(5),np.arange(5)*2-3,[np.cos(xs*np.pi) for xs in range(5)])
array([[ 0., -3., 1.],
[ 1., -1., -1.],
[ 2., 1., 1.],
[ 3., 3., -1.],
[ 4., 5., 1.]])
"""
result = np.zeros((len(xs), 3))
result[:, 0] = xs
result[:, 1] = ys
result[:, 2] = zs
return result
[docs]def as_3d(scalars):
""" As pack_3d, but with the same scalars in on all components.
Parameters
----------
scalars : array-like
An array-like of scalars.
Returns
-------
ndarray
An array of 3 dimensional vectors with the same length as xs.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> vec = np.arange(15).reshape(5,3)
>>> as_3d(range(5)) * vec
array([[ 0., 0., 0.],
[ 3., 4., 5.],
[12., 14., 16.],
[27., 30., 33.],
[48., 52., 56.]])
"""
return pack_3d(scalars, scalars, scalars)
[docs]def map_3d(function,vectors):
""" map_3d maps a function for 1d data to each dimension in 3d data.
Parameters
----------
function : ndarray -> ndarray
A function that takes a scalar array
and maps it to another scalar array.
vectors : ndarray
An array of 3 dimensional vectors.
Returns
-------
ndarray
An array of 3 dimensional vectors.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> vec = np.arange(15).reshape(5,3)
>>> map_3d(lambda xs: [x > 2 and x%4 != 0 for x in xs],vec)
array([[0., 0., 0.],
[1., 0., 1.],
[1., 1., 0.],
[1., 1., 1.],
[0., 1., 1.]])
"""
return pack_3d(*[function(vectors[:,i]) for i in range(3)])
[docs]def NEC_to_VSC(velocities):
""" Construct a function to transform from the NEC frame to the VSC frame.
This function generates another function,
which is a transform of vectors from the NEC frame to the VSC frame.
This specific transformation may be different for each data point.
Parameters
----------
velocities : ndarray
The velocities describe thw forward direction in the VSC frame,
and they can be different for each data point.
Returns
-------
function
A function that takes a list of vectors
of the same length as velocities and
transform them from the NEC frame to the VSC frame.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> v = np.arange(15).reshape(5,3)
>>> trans = NEC_to_VSC(v)
>>> trans(v) # doctest: +ELLIPSIS
array([[ 0.7..., 0.7..., 2. ...],
[ 3.5..., 3.5..., 5. ...],
[ 6.5..., 6.5..., 8. ...],
[ 9.5..., 9.5..., 11. ...],
[12.5..., 12.5..., 14. ...]])
>>> trans((v[::-1]-2.5) * (v + 3)) # doctest: +ELLIPSIS
array([[ 49.8..., 9.5..., 57.5...],
[ 46.0..., 46.4..., 68. ...],
[ 34.8..., 42.4..., 60.5...],
[ 7.0..., 19.1..., 35. ...],
[-38.4..., -22.4..., -8.5...]])
"""
angles = -np.arctan2(velocities[:, 0] - velocities[:, 1],
velocities[:, 0] + velocities[:, 1])
sines = np.sin(angles)
cosines = np.cos(angles)
def transform(vectors):
""" Transforms from the NEC frame to the VSC frame
This function takes an array of vectors,
which is in the NEC frame and returns them transformed
into the VSC frame.
The transform will may be different for each vector,
and is fixed when this function is constructed.
Parameters
----------
vectors : ndarray
An array of 3d points or vectors in the NEC frame.
Returns
-------
ndarray
An array of the vectors in the VSC frame.
Note
----
See NEC_to_VSC for examples of use.
"""
return pack_3d(cosines*vectors[:, 0] + sines*vectors[:, 1],
- sines*vectors[:, 0] + cosines*vectors[:, 1],
vectors[:, 2]) # Does the 3rd dimension flip?
return transform
[docs]def delta(vectors):
""" Computes the finite difference on an arralike.
Parameters
----------
vectors : ndarray
An array of vectors or scalars.
Returns
-------
ndarray
An array of vectors or scalars of shape (n-1,...),
when vectors input have shape (n,...).
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> delta(np.arange(5))
array([1, 1, 1, 1])
>>> delta(np.array([4, 3, 7, 4, 2, 92]))
array([-1, 4, -3, -2, 90])
>>> delta(np.array([[1, 0, 0], [0, 1, 3], [7, -13, 4]]))
array([[ -1, 1, 3],
[ 7, -14, 1]])
"""
return vectors[1:] - vectors[:-1]
[docs]def means(vectors):
""" Computes the means between this and the next point.
Parameters
----------
vectors : ndarray
An array of vectors or scalars.
Returns
-------
ndarray
An array of vectors or scalars of shape (n-1,...),
when vectors input have shape (n,...).
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> means(np.array(list(range(5))))
array([0.5, 1.5, 2.5, 3.5])
>>> means(np.array([4, 3, 7, 4, 2, 92]))
array([ 3.5, 5. , 5.5, 3. , 47. ])
>>> means(np.array([[1, 0, 0], [0, 1, 3], [7, -13, 4]]))
array([[ 0.5, 0.5, 1.5],
[ 3.5, -6. , 3.5]])
"""
return (vectors[1:] + vectors[:-1]) * 0.5
[docs]def spherical_delta(positions):
""" Computes the change of sherical positions as a NEC vector.
Parameters
----------
positions : ndarray
An array of positions (3d vectors) described in spherical coordinates.
Returns
-------
ndarray : ndarray
An array of vectors in the NEC frame.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> vecs = np.arange(15).reshape(5,3)
>>> spherical_delta(vecs)
array([[ 0.18317585, 0.18311308, -3. ],
[ 0.34018372, 0.33913504, -3. ],
[ 0.49719158, 0.49293804, -3. ],
[ 0.65419945, 0.64324482, -3. ]])
"""
theta, Lambda, r = (positions[:, i] for i in range(3))
r_means = means(r)
return pack_3d(r_means * np.sin(TO_RADIANS * delta(theta)),
r_means * np.sin(TO_RADIANS * delta(Lambda))
* np.cos(TO_RADIANS * means(theta)),
-delta(r))
[docs]def curl(delta_x, delta_field, target_index=2):
""" Compute a single component of curl for an array of vectors.
This function is used to calculate a finite difference apporximation
of curl. Its starting point is similar to a finite difference
approximation of a partial derivative, where you use both the
recoded change in the function and what you differentiate with respect to.
In practice this calculate an array of in independent curl operations,
as given by a single step.
Parameters
----------
delta_x : ndarray
An array of the finite difference in the considered step.
delta_field : ndarray
An array of the finite difference of the field under
the relevant considered step.
target_index : int, optional
The index of the dimension of the curl that should be given as output.
Returns
-------
ndarray
An array of the curl-like quantity,
but only target_index dimension of it.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> vecs = delta(np.arange(15).reshape(5,3))
>>> curl(vecs,vecs)
array([0., 0., 0., 0.])
>>> curl(pack_3d(np.ones((4,)),-1.5 * np.ones((4,)),np.zeros((4,))),vecs)
array([5., 5., 5., 5.])
"""
index_x = (target_index+1) % 3
index_y = (target_index+2) % 3
return (delta_field[:, index_y] / delta_x[:, index_x]
- delta_field[:, index_x] / delta_x[:, index_y])
[docs]def inclination(vectors):
""" Calculates the inclination of vectors.
Calculate the inclination for each individual vector in vectors.
This means this is equivalent to the tilt of the vectors toward
the third dimention.
Parameters
----------
vectors : ndarray
An array of 3d vectors.
Returns
-------
ndarray
An array of scalars, where each is the inclination
of their respective vector in vectors.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> inclination(np.arange(15).reshape(5,3))
array([1.10714872, 0.78539816, 0.71469295, 0.68539502, 0.66942998])
>>> np.sin(_)
array([0.89442719, 0.70710678, 0.65538554, 0.63297887, 0.62053909])
"""
length_first_second_dims = (vectors[:, 0]**2 + vectors[:, 1]**2)**0.5
return np.arctan2(vectors[:, 2], length_first_second_dims)
[docs]def request_data(
start=date.datetime(2016, 1, 1),
end=date.datetime(2016, 1, 2),
target_file='tempdata.cdf',
filters=[{'parameter': 'Latitude',
'minimum': -90.,
'maximum': 90.}],
url='https://vires.services/ows',
collection='SW_OPER_MAGA_LR_1B',
product_options={'auxiliaries': ['QDLat', 'QDLon']},
sampling_step='PT1S',
models=['MCO_SHA_2C', 'MLI_SHA_2C',
'MMA_SHA_2C-Primary', 'MMA_SHA_2C-Secondary'],
measurements=['F', 'B_NEC'],
to_file=True,
**credentials):
""" Request data from a vires server.
This function sets up a request to a vires server.
It is a wrapper on viresclient functionality,
where default arguments have been provided.
The data will be saved to as a cdf file.
Parameters
----------
start : datatime, optional
The inclusive starting time of the requested data.
Defaults to 1st of Janurary 2016
end : datetime, optional
The exclusive end time of the requested data.
Defaults to 2nd of Janurary 2016
target_file : str, optional
Filepath for the output cdf file. Must include name and extension.
Defaults to 'tempdata.cdf'
filters : list(dict), optional
A list of dictionaries, where each dictionary is the options
for a filter to be applied to the data.
target_url : str, optional
The full url for the server to request the data at.
Defaults to 'https://staging.viresdisc.vires.services/openows'
collection : str, optional
See viresclient set_collection. Defaults to 'SW_OPER_MAGA_LR_1B'.
product_options : dict, optional
Extra options to viresclient set_products.
Defaults to {'auxiliaries': ['QDLat', 'QDLon']}
sampling_step : str, optional
Describes the sampling frequency,
since vires may otherwise downsample the data.
The default is 1Hz by 'PT1S'.
models : list(str), optional
The models calculated at the data points.
See viresclient set_products models for details.
Defaults to [
'MCO_SHA_2C', 'MLI_SHA_2C',
'MMA_SHA_2C-Primary', 'MMA_SHA_2C-Secondary']
measurements : list(str), optional
Measured quantities to be included for each data point.
Defaults to ['F', 'B_NEC']
to_file : boolean, optional
If set to true, the the result will be saved to a file as cdf, otherwise the datastructure will be returned as is.
Defaults to True.
**credentials : **str, optional
extra parameters to pass to SwarmRequest. Intented for passing credentails (though other options are also possible). It is backwards compatible with calls using parameter names for credentials. The specific credentials are as below:
token : str, optional
Token for authenticating with the target_url.
You need either a token or a username/password combination.
The default options for the website will be used if all are set to None.
username : str, optional
username for authenticating with the target_url.
You need either a token or a username/password combination.
The default options for the website will be used if all are set to None.
password : str, optional
username for authenticating with the target_url.
You need either a token or a username/password combination.
The default options for the website will be used if all are set to None.
Examples
--------
>>> from pyfac.utils import * # doctest: +SKIP
>>> request_data() # doctest: +SKIP
>>> request_data(date.datetime(2017, 7, 5),
... date.datetime(2017, 7, 6))# doctest: +SKIP
Note
----
These examples are skiped by doctest due to security reasons,
and unintentional side-effects.
"""
request = SwarmRequest(url=url, **credentials)
request.set_collection(collection)
request.set_products(measurements=measurements, models=models,
sampling_step=sampling_step, **product_options)
for filter in filters:
request.set_range_filter(**filter)
data = request.get_between(start_time=start, end_time=end)
if to_file:
data.to_file(target_file)
else:
return data
[docs]def read_cdf(file, **name_pairings):
""" Read data from a cdf file.
Read a cdf file and construct a dictionary based on
name_parings. This may open and read a file as a side-effect.
Parameters
----------
file : str or cdfread
The file to read the data from.
Accepts both a string filepath to the file and an opened cdfread file.
**name_pairings
Pairs of names used to link a name in the output with its
corrisponding name in the cdf file.
Returns
-------
dict
The keys are the keywords used in name_parings,
while the values are the variables loaded from
the cdf file with the name of the corrisponding argument.
Examples
--------
We can directly ask for variables:
>>> from pyfac.utils import * # doctest: +SKIP
>>> read_cdf('tempdata.cdf') # doctest: +SKIP
{}
>>> read_cdf('tempdata.cdf', time='Timestamp') # doctest: +SKIP,+ELLIPSIS
{'time': array([...])}
We can also make use of predefined options:
>>> options = {'time': 'Timestamp', 'B_base': 'B_NEC'}
>>> read_cdf('tempdata.cdf', **options) # doctest: +SKIP,+ELLIPSIS
{'time': array([...]), 'B_base': array([[...]])}
Note
----
These examples are skiped by doctest due to dependency
on the existance of a 'tempdata.cdf' file.
"""
if not isinstance(file, cdflib.cdfread.CDF):
file = cdflib.CDF(file)
return {name: file.varget(cdf_name)
for name, cdf_name in name_pairings.items()}
if __name__ == '__main__':
# Test docstrings
import doctest
doctest.testmod()