# Field Basics

`using CMBLensing`

## Base Fields

The basic building blocks of CMBLensing.jl are CMB "fields", like temperature, Q or U polarization, or the lensing potential $\phi$. These types are all encompassed by the abstract type `Field`

, with some concrete examples including `FlatMap`

for a flat-sky map projection, or `FlatQUMap`

for Q/U polarization, etc...

`Flat`

fields are just thin wrappers around Julia arrays, e.g.

`Ix = rand(2,2)`

```
2×2 Array{Float64,2}:
0.0942826 0.301606
0.09312 0.822432
```

`f = FlatMap(Ix)`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.09428258967352532
0.0931199729195833
0.3016063922394068
0.822431685907995
```

When displayed, you can see the pixels in the 2x2 map have been splayed out into a length-4 array. This is intentional, as even though the maps themselves are two-dimensional, it is extremely useful conceptually to think of fields as vectors (which they are, in fact, as they form an abstract vector space). This tie to vector spaces is deeply rooted in CMBLensing, to the extent that `Field`

objects are a subtype of Julia's own `AbstractVector`

type,

`f isa AbstractVector`

`true`

The data itself, however, is still stored as the original 2x2 matrix, and can be accessed as follows,

`f.Ix`

```
2×2 Array{Float64,2}:
0.0942826 0.301606
0.09312 0.822432
```

But since `Fields`

are vectors, they can be tranposed,

`f'`

```
1×4 LinearAlgebra.Adjoint{Float64,FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}}:
0.0942826 0.09312 0.301606 0.822432
```

inner products can be computed,

`f' * f`

`0.7849208298972281`

and they can be added with each other as well as multiplied by scalars,

`2*f+f`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.28284776902057596
0.2793599187587499
0.9048191767182203
2.4672950577239847
```

## Diagonal operators

Vector spaces have linear operators which act on the vectors. Linear operators correpsond to matrices, thus for a map with $N$ total pixels, a general linear operator would be an $N$-by-$N$ matrix, which for even modest map sizes becomes far too large to actually store. Thus, an important class of linear operators are ones which are diagonal, since these can actually be stored. CMBLensing uses Julia's builtin `Diagonal`

to represent these. `Diagonal(f)`

takes a vector `f`

and puts it on the diagonal of the matrix:

`Diagonal(f)`

```
4×4 Diagonal{Float64,FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}}:
0.0942826 ⋅ ⋅ ⋅
⋅ 0.09312 ⋅ ⋅
⋅ ⋅ 0.301606 ⋅
⋅ ⋅ ⋅ 0.822432
```

Multiplying this operator by the original map is then a matrix-vector product:

`Diagonal(f) * f`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.008889206715546343
0.008671329356543927
0.09096641583967088
0.6763938779854669
```

Note that this is also equal to the the pointwise multiplication of `f`

with itself:

`f .* f`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.008889206715546343
0.008671329356543927
0.09096641583967088
0.6763938779854669
```

## Field Tuples

You can put `Fields`

together into tuples. For example,

```
a = FlatMap(rand(2,2))
b = FlatMap(rand(2,2));
```

`FieldTuple(a,b)`

```
8-element Field2Tuple{CMBLensing.BasisTuple{Tuple{Map,Map}}, Float64}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
0.9753308871001249
0.02873851357884316
0.42348224991890393
0.9998332928263747
```

The components can also have names:

`ft = FieldTuple(a=a, b=b)`

```
8-element Field2Tuple{(:a, :b), CMBLensing.BasisTuple{Tuple{Map,Map}}, Float64}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
0.9753308871001249
0.02873851357884316
0.42348224991890393
0.9998332928263747
```

which can be accessed later:

`ft.a`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
```

`FieldTuples`

have all of the same behavior of individual fields. Indeed, spin fields like QU or IQU are simply special `FieldTuples`

:

```
fqu = FlatQUMap(a,b)
fqu isa FieldTuple
```

`true`

## Field Vectors

*in progress*

## Basis Conversion

All fields are tagged as to which basis they are stored in. You can convert them to other bases by calling the basis type on them:

`f`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.09428258967352532
0.0931199729195833
0.3016063922394068
0.822431685907995
```

`f′ = Fourier(f)`

```
4-element FlatFourier{2×2 map, 1′ pixels, fourier∂, Array{Complex{Float64},2}}:
1.3114406407405104 + 0.0im
-0.5196626769146462 + 0.0im
-0.9366355155542931 + 0.0im
0.5219879104225302 + 0.0im
```

Basis conversion is usually done automatically for you. E.g. here `f′`

is automatically converted to a `FlatMap`

before addition:

`f + f′`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.18856517934705064
0.1862399458391666
0.6032127844788135
1.64486337181599
```

A key feature of `Diagonal`

operators is they convert the field they are acting on to the right basis before multiplication:

`Diagonal(f) * f′`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.008889206715546343
0.008671329356543927
0.09096641583967088
0.6763938779854669
```

A `FlatMap`

times a `FlatFourier`

doesn't have a natural linear algebra meaning so its an error:

`f * f′`

```
MethodError: no method matching *(::FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}, ::FlatFourier{2×2 map, 1′ pixels, fourier∂, Array{Complex{Float64},2}})
Closest candidates are:
*(::Any, ::Any, !Matched::Any, !Matched::Any...) at operators.jl:538
*(!Matched::ChainRulesCore.One, ::Any) at /home/cosmo/.julia/packages/ChainRulesCore/CcY7R/src/differential_arithmetic.jl:98
*(!Matched::FuncOp, ::Field) at /home/cosmo/CMBLensing/src/specialops.jl:124
...
Stacktrace:
[1] top-level scope at In[21]:1
[2] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
```

## Properties and indices

`FlatMap`

and `FlatFourier`

can be indexed directly like arrays. If given 1D indices, this is the index into the vector representation:

`f`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.09428258967352532
0.0931199729195833
0.3016063922394068
0.822431685907995
```

`f[1], f[2], f[3], f[4]`

`(0.09428258967352532, 0.0931199729195833, 0.3016063922394068, 0.822431685907995)`

`f[5]`

```
BoundsError: attempt to access 2×2 Array{Float64,2} at index [5]
Stacktrace:
[1] getindex at ./array.jl:809 [inlined]
[2] getindex(::FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}, ::Int64) at /home/cosmo/CMBLensing/src/flat_s0.jl:53
[3] top-level scope at In[24]:1
[4] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
```

Or with a 2D index, this indexes directly into the 2D map:

`f[1,1], f[2,1], f[1,2], f[2,2]`

`(0.09428258967352532, 0.0931199729195833, 0.3016063922394068, 0.822431685907995)`

*Note:* there is no overhead to indexing `f`

in this way as compared to working directly on the underlying array.

For other fields which are built on `FieldTuples`

, 1D indexing will instead index the tuple indices:

`ft`

```
8-element Field2Tuple{(:a, :b), CMBLensing.BasisTuple{Tuple{Map,Map}}, Float64}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
0.9753308871001249
0.02873851357884316
0.42348224991890393
0.9998332928263747
```

`ft[1]`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
```

`ft[2]`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.9753308871001249
0.02873851357884316
0.42348224991890393
0.9998332928263747
```

`ft[3]`

```
BoundsError: attempt to access NamedTuple{(:a, :b),Tuple{FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}},FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}}}
at index [3]
Stacktrace:
[1] getindex at ./namedtuple.jl:112 [inlined]
[2] getindex(::Field2Tuple{(:a, :b), CMBLensing.BasisTuple{Tuple{Map,Map}}, Float64}, ::Int64) at /home/cosmo/CMBLensing/src/field_tuples.jl:47
[3] top-level scope at In[29]:1
[4] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
```

To get the underlying data arrays, use the object's properties:

`f.Ix`

```
2×2 Array{Float64,2}:
0.0942826 0.301606
0.09312 0.822432
```

You can always find out what properties are available by typing `f.<Tab>`

. For example, if you typed `ft`

then hit `<Tab>`

you'd get:

`ft |> propertynames`

`(:fs, :a, :b)`

For a `FieldTuple`

like the `FlatQUMap`

object, `fqu`

, you can get each individual Q or U field:

`fqu.Q`

```
4-element FlatMap{2×2 map, 1′ pixels, fourier∂, Array{Float64,2}}:
0.686662940424382
0.9214957622316127
0.22704946075311416
0.5141967036522339
```

Or `fqu.Qx`

which is shorthand for `fqu.Q.Ix`

:

`fqu.Q.Ix === fqu.Qx`

`true`

If you convert `f`

to Fourier space, it would have the `Il`

property to get the Fourier coefficients of the $I$ component:

`Fourier(f).Il`

```
2×2 Array{Complex{Float64},2}:
1.31144+0.0im -0.936636+0.0im
-0.519663+0.0im 0.521988+0.0im
```

For convenience, you can index fields with brackets `[]`

and any necessary conversions will be done automatically:

`f[:Il]`

```
2×2 Array{Complex{Float64},2}:
1.31144+0.0im -0.936636+0.0im
-0.519663+0.0im 0.521988+0.0im
```

This works between any bases. For example. `fqu`

is originally `QUMap`

but we can convert to `EBFourier`

and get the `El`

coefficients:

`fqu[:El]`

```
2×2 Array{Complex{Float64},2}:
-2.3494-0.0im -0.866913+0.0im
-0.52198+0.0im -1.52294+0.0im
```

The general rule to keep in mind for these two ways of accessing the underlying data is:

**Properties**(i.e.`f.Ix`

) are type-stable and get you the underlying data arrays, even recursively from special`FieldTuples`

like`FlatQUMap`

, etc... If these arrays are modified, they affect the original field.**Indices**(i.e.`f[:Ix]`

) are not type-stable, and may or may not be one of the underlying data arrays (because a basis conversion may have been performed). They should be used for getting (not setting) data, and in non-performance-critical code.