Skip to content

Commit c62e342

Browse files
committed
feat: add function to create 3D particle data structures containing Cluster CODIF psd data for use with particle routines.
1 parent 460324f commit c62e342

1 file changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import logging
2+
import re
3+
import numpy as np
4+
from pyspedas.tplot_tools import get_data
5+
6+
logging.captureWarnings(True)
7+
logging.basicConfig(format='%(asctime)s: %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO)
8+
9+
def cluster_get_codif_dist(tname, probe=None):
10+
"""
11+
Returns 3D particle data structures containing Cluster CODIF
12+
data for use with pySPEDAS particle routines.
13+
14+
Input
15+
----------
16+
tname: str
17+
tplot variable name containing the CODIF distribution data
18+
Example: 'ions_3d__C1_CP_CIS_CODIF_HS_H1_PSD'
19+
20+
Parameters
21+
----------
22+
probe: str
23+
Spacecraft probe #
24+
25+
Returns
26+
----------
27+
3D particle data structure(s) containing Cluster CODIF distribution functions
28+
"""
29+
data = get_data(tname)
30+
31+
if data is None:
32+
logging.error('Problem extracting the CODIF distribution data.')
33+
return
34+
35+
if any(data[i].ndim == 2 for i in (2, 3, 4)):
36+
raise ValueError(
37+
"data[2], data[3], and data[4] must all be 1-D arrays"
38+
)
39+
40+
# check which species by looking at second last part of input string
41+
# this could be a function
42+
parts = tname.split("_")
43+
if re.fullmatch(r"[A-Z][a-z]?\d", parts[-2]):
44+
species = parts[-2].lower()
45+
else:
46+
raise ValueError(f"No valid species found in: {tname}")
47+
48+
# possible masses in amu
49+
if species == 'h1':
50+
mass = 1.04535e-2
51+
charge = 1.0
52+
elif species == 'he1':
53+
mass = 4.18138e-2
54+
charge = 1.0
55+
elif species == 'o1':
56+
mass = 0.167255
57+
charge = 1.0
58+
else:
59+
logging.error('Invalid species: ' + species)
60+
#return
61+
62+
out = {'project_name': 'Cluster',
63+
'spacecraft': probe,
64+
'species': species,
65+
'data_name': 'CODIF ' + species,
66+
'charge': charge,
67+
'units_name': 'df_km',
68+
'mass': mass}
69+
70+
# --- Reformat data
71+
# Shuffle the output to be [time, energy, phi, theta]
72+
# (data[1] is currently [time, theta, phi, energy])
73+
out_data = data[1].transpose([0, 3, 2, 1]) #* 1.0E-18 * 1.0E-12 # convert from s^3 km^-6 to s^3 cm^-6
74+
out_bins = np.zeros(out_data.shape) + 1
75+
out_dphi = np.zeros(out_data.shape) + 22.5 # 16 pixels in 360 degree window
76+
out_dtheta = np.zeros(out_data.shape) + 22.5 # 8 pixels in 180 degree window
77+
out_denergy = np.zeros(out_data.shape)
78+
79+
# theta
80+
# elevations are constant across time
81+
theta_reform = np.asarray(data[2]) # shape: (theta,)
82+
theta_reform = theta_reform[None, None, :] # shape: (1, 1, theta)
83+
theta_rebinned = np.broadcast_to(
84+
theta_reform,
85+
(len(data[4]), len(data[3]), theta_reform.shape[2])
86+
) # shape: (energy, phi, theta)
87+
out_theta = np.broadcast_to(
88+
theta_rebinned,
89+
(len(data[0]),) + theta_rebinned.shape
90+
) # shape: (time, energy, phi, theta)
91+
92+
# energy
93+
energy_len = len(data[4])
94+
energy_reform = np.asarray(data[4])[:, None, None] # shape: (energy, 1, 1)
95+
energy_table = np.broadcast_to(
96+
energy_reform,
97+
(len(data[4]), len(data[3]), len(data[2]))
98+
) # shape: (energy, phi, theta)
99+
out_energy = np.broadcast_to(
100+
energy_table,
101+
(len(data[0]),) + energy_table.shape
102+
) # shape: (time, energy, phi, theta)
103+
104+
# phi
105+
phi_reform = np.asarray(data[3])[None, :, None] # shape: (1, phi, 1)
106+
phi_rebinned = np.broadcast_to(
107+
phi_reform,
108+
(len(data[4]), len(data[3]), len(data[2]))
109+
) # shape: (energy, phi, theta)
110+
out_phi = np.broadcast_to(
111+
phi_rebinned,
112+
(len(data[0]),) + phi_rebinned.shape
113+
) # shape: (time, energy, phi, theta)
114+
115+
out_list = []
116+
for time_idx, time in enumerate(data[0]):
117+
out_table = {**out}
118+
out_table['data'] = out_data[time_idx, :]
119+
out_table['bins'] = out_bins[time_idx, :]
120+
out_table['theta'] = out_theta[time_idx, :]
121+
out_table['phi'] = out_phi[time_idx, :]
122+
out_table['energy'] = out_energy[time_idx, :]
123+
out_table['dtheta'] = out_dtheta[time_idx, :]
124+
out_table['dphi'] = out_dphi[time_idx, :]
125+
out_table['denergy'] = out_denergy[time_idx, :]
126+
out_table['n_energy'] = energy_len
127+
out_table['n_theta'] = len(data[3])
128+
out_table['n_phi'] = len(data[2])
129+
out_table['start_time'] = time # note: assumes the data weren't centered
130+
out_table['end_time'] = time + 4 # one spin = 4 seconds
131+
out_list.append(out_table)
132+
133+
return out_list

0 commit comments

Comments
 (0)