Time-ordered data (TOD)
Stripeline contains functions that create the timeline of scientific data as produced by each polarimeter and distribute it across several parallel processes, potentially running through MPI.
A timeline of scientific data is generically called a TOD, and the polarimeters implemented in Strip produce a complex flow of data, as they output eight series:
- Four series are called
PWRseries:PWR_Q1,PWR_Q2,PWR_U1, andPWR_U2; - Four series are called
DEMseries:DEM_Q1,DEM_Q2,DEM_U1, andDEM_U2.
Each of these series is sampled at 50 Hz and contains scientific data (in the preliminary unit tests done in 2017, the sampling frequency was 25 Hz; keep this in mind if you happen to replicate some returns of those ancient tests!); the raw instrument represents them as integer numbers, but Stripeline usually assumes they have already been calibrated to Kelvin and therefore uses floating-point numbers.
A TOD is represented by the structure StripTod, which contains the following fields:
polarimeters: a generic sequence of values, which represent each polarimeter stored in the TOD. Very common choices for this field are a range (for example,1:49represents all the Q-band polarimeters) or a list of strings, e.g.,["I0", "I3", "V2"].time_range: a range representing the time of each sample in the TOD. Very common choices for this are floating-point ranges like0.0:(1 / 50):100.0(100 s sampled at 50 Hz) and ranges of astronomical times, created using the AstroTime package.samples: this is the matrix containing the actual scientific samples. It has a shape of $N \times 8 \times m$, where $N$ is the number of samples along the time axis and must be equal tolength(time_range), and $m$ is the number of polarimeters, which must be equal tolength(polarimeters). The middle dimension is 8 because there are eight timelines produced by each polarimeter (see above).
Allocating the TOD is a matter of calling the function allocate_tod:
using Stripeline
# One minute of data sampled at 50 Hz, 10 polarimeters
tod = allocate_tod(StripTod, Float32, 0.0:(1.0 / 50.0):60.0, 1:10)TOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)
In a MPI environment, you can pass the keywords mpi_rank and mpi_size to allocate_tod and the function will only allocate the subset of the overall time range that is assigned to the running MPI process.
Once a StripTod variable is allocated, accessing the samples in the TOD can be done through the field samples, which is a matrix:
println("The shape of the TOD is ", size(tod.samples))The shape of the TOD is (3001, 8, 10)
Accessing the right column can be cumbersome, as there are eight of them (PWR_Q1, PWR_Q2, …), so Stripeline provides the constants PWR_Q1_RANK, PWR_Q2_RANK, … for this:
# Print the minimum sample acquired by PWR_Q1 for polarimeter number #4
println(minimum(tod.samples[:, PWR_Q1_RANK, 4]))0.0
The result is zero because allocate_tod sets all the samples to zero by default. An alternative approach is to use one of the functions pwrq1, pwrq2, pwru1, pwru2, demq1, demq2, demu1, demu2, which return a view of the original array and can thus be used both for reading and writing:
# Set one element of PWR_Q1 for polarimeter number #4
pwrq1(tod, 4)[142] = 1.36
println(
"The maximum element of polarimeter #4 is now ",
maximum(pwrq1(tod, 4)),
)The maximum element of polarimeter #4 is now 1.36
Stripeline.StripTod — TypeA matrix of time-ordered PWR/DEM data for a subset of polarimeters
The fields of this structure are the following:
time_range: a range representing the time of each sample in the TOD.samples: a N×8×m matrix, where N is the number of samples in the timeline, and m is the number of polarimeters. The middle dimension spans the three Stokes parametersI,Q, andU(in this order). The valueNis equal tolength(time_range)rng: a pseudo-random number generator to be used for this TOD. It is guaranteed that the generator is uncorrelated with any other generator used for otherStripTodobjects created by the same call toallocate_tod.
Stripeline.allocate_tod — Functionallocate_tod(todtype, t, time_range, polarimeters; mpi_rank=0, mpi_size=1, zero_tod=true)Allocate memory for a time-ordered data matrix appropriate for the set of polarimeters in the variable polarimeters (an array or range, only its length is used and it is copied as-is in the returned StripTod value). The type of each sample is set to t. The variable time_range must be a range of floating-point values or epochs (as defined by the AstroTime package) which represent the full set of samples acquired during an observation.
The optional parameters mpi_rank and mpi_size can be used in MPI scripts to allocate only the subset of samples that must be processed by the current MPI process.
If zero_tod is true, each sample in the TOD is set to zero; otherwise, its value is undefined. Using zero_tod = false is slightly faster, but it should be used only if you are going to set by hand every sample in the TOD once allocate_tod returns.
Return a StripTod value.
Example
julia> tod = allocate_tod(StripTod, Float32, 0.0:0.01:100.0, 1:5)
TOD(5 polarimeters, 10001 rows/polarimeter, time range from 0.0 to 100.0)Stripeline.PWR_Q1_RANK — ConstantPWR_Q1_RANK = 1The rank of PWR_Q1 in the samples field of a StripTod variable.
Stripeline.PWR_Q2_RANK — ConstantPWR_Q2_RANK = 2The rank of PWR_Q2 in the samples field of a StripTod variable.
Stripeline.PWR_U1_RANK — ConstantPWR_U1_RANK = 3The rank of PWR_U1 in the samples field of a StripTod variable.
Stripeline.PWR_U2_RANK — ConstantPWR_U2_RANK = 4The rank of PWR_U2 in the samples field of a StripTod variable.
Stripeline.DEM_Q1_RANK — ConstantDEM_Q1_RANK = 5The rank of DEM_Q1 in the samples field of a StripTod variable.
Stripeline.DEM_Q2_RANK — ConstantDEM_Q2_RANK = 6The rank of DEM_Q2 in the samples field of a StripTod variable.
Stripeline.DEM_U1_RANK — ConstantDEM_U1_RANK = 7The rank of DEM_U1 in the samples field of a StripTod variable.
Stripeline.DEM_U2_RANK — ConstantDEM_U2_RANK = 8The rank of DEM_U2 in the samples field of a StripTod variable.
Stripeline.pwrq1 — Functionpwrq1(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel PWR_Q1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, PWR_Q1_RANK, pol]Stripeline.pwrq2 — Functionpwrq2(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel PWR_Q2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, PWR_Q2_RANK, pol]Stripeline.pwru1 — Functionpwru1(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel PWR_U1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, PWR_U1_RANK, pol]Stripeline.pwru2 — Functionpwru2(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel PWR_U2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, PWR_U2_RANK, pol]Stripeline.demq1 — Functiondemq1(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel DEM_Q1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, DEM_Q1_RANK, pol]Stripeline.demq2 — Functiondemq2(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel DEM_Q2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, DEM_Q2_RANK, pol]Stripeline.demu1 — Functiondemu1(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel DEM_U1 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, DEM_U1_RANK, pol]Stripeline.demu2 — Functiondemu2(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel DEM_U2 in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, DEM_U2_RANK, pol]I/Q/U TODs
The scientific information kept in a StripTod variable is encoded in eight timelines (PWRQ1,PWRQ2,PWRU1,PWRU2,DEMQ1,DEMQ2,DEMU1,DEMU2`), but most of the information in these timelines is redundant and is essentially correlated/uncorrelated noise.
The eight timelines can be combined to provide a direct estimate of the four Stokes parameters $I$, $Q$, and $U$ in the reference frame of the detector, using these formulae:
\[\begin{aligned} I &= \frac{\text{PWRQ1} + \text{PWRQ2} + \text{PWRU1} + \text{PWRU2}}4,\\ Q &= \frac{\text{PWRQ1} - \text{PWRQ2}}2,\\ U &= \frac{\text{PWRU1} - \text{PWRU2}}2. \end{aligned}\]
The three $I$/$Q$/$U$ timelines can be stored in a StokesTod variable, which behaves exactly like a StripTod but only contains three timeseries instead of eight. To allocate it, you can use allocate_tod again, passing a StokesTod parameter:
stokestod = allocate_tod(StokesTod, Float32, 0.0:(1.0 / 50.0):60.0, 1:10)StokesTOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)
Or you can compute it starting from a StokesTod variable:
stokestod = stokes(tod)StokesTOD(10 polarimeters, 3001 rows/polarimeter, time range from 0.0 to 60.0)
The latter call works out-of-the-box in a MPI environment, of course. You can access the $I$/$Q$/$U$ timelines either using the explicit indexes STOKES_I_RANK, STOKES_Q_RANK, STOKES_U_RANK, or the three functions stokesi, stokesq, stokesu, similarly to what was described above for StripTod.
Stripeline.StokesTod — TypeA matrix of time-ordered PWR/DEM data for a subset of polarimeters
The fields of this structure are the following:
time_range: a range representing the time of each sample in the TOD.samples: a N×8×m matrix, where N is the number of samples in the timeline, and m is the number of polarimeters. The middle dimension spans the three Stokes parametersI,Q, andU(in this order). The valueNis equal tolength(time_range)rng: a pseudo-random number generator to be used for this TOD. It is guaranteed that the generator is uncorrelated with any other generator used for otherStripTodobjects created by the same call toallocate_tod.
Stripeline.stokes — Functionstokes(tod::StripTod{T, S, R})Return a StokesTod variable containing the estimated $I$/$Q$/$U$ Stokes parameters associated with the parameter tod, which is a time-ordered data variable containing the eight outputs PWR_Q1, PWR_Q2, PWR_U1, PWR_U2, DEM_Q1, DEM_Q2, DEM_U1, DEM_U2 of a set of Strip polarimeters.
Stripeline.STOKES_I_RANK — ConstantSTOKES_I_RANK = 1The rank of STOKES_I in the samples field of a StripTod variable.
Stripeline.STOKES_Q_RANK — ConstantSTOKES_Q_RANK = 2The rank of STOKES_Q in the samples field of a StripTod variable.
Stripeline.STOKES_U_RANK — ConstantSTOKES_U_RANK = 3The rank of STOKES_U in the samples field of a StripTod variable.
Stripeline.stokesi — Functionstokesi(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel STOKES_I in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, STOKES_I_RANK, pol]Stripeline.stokesq — Functionstokesq(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel STOKES_Q in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, STOKES_Q_RANK, pol]Stripeline.stokesu — Functionstokesu(tod::Stripeline.StripTod, pol::T) where T <: IntegerReturn a view of the timeline for channel STOKES_U in the time-ordered data for the polarimeter indexed by pol in tod. It is a shortcut for
tod.samples[:, STOKES_U_RANK, pol]