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
PWR
series:PWR_Q1
,PWR_Q2
,PWR_U1
, andPWR_U2
; - Four series are called
DEM
series: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:49
represents 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 valueN
is 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 otherStripTod
objects 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 = 1
The rank of PWR_Q1
in the samples
field of a StripTod
variable.
Stripeline.PWR_Q2_RANK
— ConstantPWR_Q2_RANK = 2
The rank of PWR_Q2
in the samples
field of a StripTod
variable.
Stripeline.PWR_U1_RANK
— ConstantPWR_U1_RANK = 3
The rank of PWR_U1
in the samples
field of a StripTod
variable.
Stripeline.PWR_U2_RANK
— ConstantPWR_U2_RANK = 4
The rank of PWR_U2
in the samples
field of a StripTod
variable.
Stripeline.DEM_Q1_RANK
— ConstantDEM_Q1_RANK = 5
The rank of DEM_Q1
in the samples
field of a StripTod
variable.
Stripeline.DEM_Q2_RANK
— ConstantDEM_Q2_RANK = 6
The rank of DEM_Q2
in the samples
field of a StripTod
variable.
Stripeline.DEM_U1_RANK
— ConstantDEM_U1_RANK = 7
The rank of DEM_U1
in the samples
field of a StripTod
variable.
Stripeline.DEM_U2_RANK
— ConstantDEM_U2_RANK = 8
The rank of DEM_U2
in the samples
field of a StripTod
variable.
Stripeline.pwrq1
— Functionpwrq1(tod::Stripeline.StripTod, pol::T) where T <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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 valueN
is 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 otherStripTod
objects 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 = 1
The rank of STOKES_I
in the samples
field of a StripTod
variable.
Stripeline.STOKES_Q_RANK
— ConstantSTOKES_Q_RANK = 2
The rank of STOKES_Q
in the samples
field of a StripTod
variable.
Stripeline.STOKES_U_RANK
— ConstantSTOKES_U_RANK = 3
The rank of STOKES_U
in the samples
field of a StripTod
variable.
Stripeline.stokesi
— Functionstokesi(tod::Stripeline.StripTod, pol::T) where T <: Integer
Return 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 <: Integer
Return 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 <: Integer
Return 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]