Simulating observations with MUSTANG-2

MUSTANG-2 is a bolometric array on the Green Bank Telescope. In this notebook we simulate an observation of the Whirlpool Galaxy (M51).

[1]:
import maria
from maria.io import fetch

input_map = maria.map.load(fetch("maps/crab_nebula.fits"), nu=93e9)

input_map.plot()
print(input_map)
Downloading https://github.com/thomaswmorris/maria-data/raw/master/maps/crab_nebula.fits: 100%|████████████████| 2.00M/2.00M [00:00<00:00, 30.4MB/s]
ProjectedMap:
  shape(nu, y, x): (1, 500, 500)
  stokes: naive
  nu: [93.] GHz
  t: naive
  z: naive
  quantity: rayleigh_jeans_temperature
  units: K_RJ
    min: 0.000e+00
    max: 5.876e-02
  center:
    ra:  05ʰ34ᵐ31.80ˢ
    dec: 22°01’3.00”
  size(y, x): (8.83’, 8.83’)
  resolution(y, x): (1.06”, 1.06”)
  beam(maj, min, rot): (0 rad, 0 rad, 0 rad)
  memory: 4 MB
../_images/tutorials_mustang-galaxy_1_2.png
[2]:
from maria import Planner

planner = Planner(target=input_map, site="green_bank", constraints={"el": (60, 90)})
plans = planner.generate_plans(total_duration=900, sample_rate=100)

plans[0].plot()
print(plans)
PlanList(2 plans, 900 s):
                           start_time duration    target(ra,dec)     center(az,el)
chunk
0      2025-08-25 10:42:14.473 +00:00    600 s  (83.63°, 22.02°)   (116.2°, 60.9°)
1      2025-08-25 10:52:51.973 +00:00    300 s  (83.63°, 22.01°)  (118.8°, 62.31°)
../_images/tutorials_mustang-galaxy_2_1.png
[3]:
instrument = maria.get_instrument("MUSTANG-2")
print(instrument)
instrument.plot()
Instrument(1 array)
├ arrays:
│            n   FOV baseline      bands polarized
│  array1  217  4.2’      0 m  [m2/f093]     False
│
└ bands:
         name     center      width    η      NEP          NET_RJ          NET_CMB    FWHM
   0  m2/f093  86.21 GHz  20.98 GHz  0.1  15 aW√s  0.5711 mK_RJ√s  0.6905 mK_CMB√s  9.133”
../_images/tutorials_mustang-galaxy_3_1.png
[4]:
sim = maria.Simulation(
    instrument,
    plans=plans,
    site="green_bank",
    map=input_map,
    atmosphere="2d",
)

print(sim)
Downloading https://github.com/thomaswmorris/maria-data/raw/master/atmosphere/spectra/am/v2/green_bank.h5: 100%|████████████████| 11.1M/11.1M [00:00<00:00, 97.9MB/s]
Downloading https://github.com/thomaswmorris/maria-data/raw/master/atmosphere/weather/era5/green_bank.h5: 100%|████████████████| 12.0M/12.0M [00:00<00:00, 30.1MB/s]
Simulation
├ Instrument(1 array)
│ ├ arrays:
│ │            n   FOV baseline      bands polarized
│ │  array1  217  4.2’      0 m  [m2/f093]     False
│ │
│ └ bands:
│          name     center      width    η      NEP          NET_RJ          NET_CMB    FWHM
│    0  m2/f093  86.21 GHz  20.98 GHz  0.1  15 aW√s  0.5711 mK_RJ√s  0.6905 mK_CMB√s  9.133”
├ Site:
│   region: green_bank
│   timezone: America/New_York
│   location:
│     longitude: 79°50’23.28” W
│     latitude:  38°25’59.16” N
│     altitude: 825 m
│   seasonal: True
│   diurnal: True
├ PlanList(2 plans, 900 s):
│                            start_time duration    target(ra,dec)     center(az,el)
│ chunk
│ 0      2025-08-25 10:42:14.473 +00:00    600 s  (83.63°, 22.02°)   (116.2°, 60.9°)
│ 1      2025-08-25 10:52:51.973 +00:00    300 s  (83.63°, 22.01°)  (118.8°, 62.31°)
├ '2d'
└ ProjectedMap:
    shape(stokes, nu, y, x): (1, 1, 500, 500)
    stokes: I
    nu: [93.] GHz
    t: naive
    z: naive
    quantity: rayleigh_jeans_temperature
    units: K_RJ
      min: 0.000e+00
      max: 5.876e-02
    center:
      ra:  05ʰ34ᵐ31.80ˢ
      dec: 22°01’3.00”
    size(y, x): (8.83’, 8.83’)
    resolution(y, x): (1.06”, 1.06”)
    beam(maj, min, rot): (0 rad, 0 rad, 0 rad)
    memory: 4 MB
[5]:
tods = sim.run()
tods[0].plot()
2025-08-24 19:01:48.243 INFO: Simulating observation 1 of 2
Constructing atmosphere: 100%|████████████████| 10/10 [00:00<00:00, 15.42it/s]
Generating turbulence: 100%|████████████████| 10/10 [00:00<00:00, 46.51it/s]
Sampling turbulence: 100%|████████████████| 10/10 [00:03<00:00,  3.10it/s]
Computing atmospheric emission: 100%|████████████████| 1/1 [00:00<00:00,  1.22it/s, band=m2/f093]
Sampling map: 100%|████████████████| 1/1 [00:05<00:00,  5.72s/it, band=m2/f093, channel=(0 Hz, inf Hz), stokes=I]
Generating noise: 100%|████████████████| 1/1 [00:00<00:00,  1.53it/s, band=m2/f093]
2025-08-24 19:02:08.800 INFO: Simulated observation 1 of 2 in 20.55 s
2025-08-24 19:02:08.801 INFO: Simulating observation 2 of 2
Constructing atmosphere: 100%|████████████████| 10/10 [00:00<00:00, 18.93it/s]
Generating turbulence: 100%|████████████████| 10/10 [00:00<00:00, 55.67it/s]
Sampling turbulence: 100%|████████████████| 10/10 [00:03<00:00,  3.15it/s]
Computing atmospheric emission: 100%|████████████████| 1/1 [00:00<00:00,  1.53it/s, band=m2/f093]
Sampling map: 100%|████████████████| 1/1 [00:03<00:00,  3.48s/it, band=m2/f093, channel=(0 Hz, inf Hz), stokes=I]
Generating noise: 100%|████████████████| 1/1 [00:00<00:00,  2.62it/s, band=m2/f093]
2025-08-24 19:02:21.967 INFO: Simulated observation 2 of 2 in 13.16 s
../_images/tutorials_mustang-galaxy_5_1.png
[6]:
from maria.mappers import BinMapper

mapper = BinMapper(
    center=input_map.center,
    frame="ra/dec",
    width=10 / 60,
    height=10 / 60,
    resolution=0.05 / 60,
    tod_preprocessing={
        "window": {"name": "hamming"},
        "remove_modes": {"modes_to_remove": [0]},
        "remove_spline": {"knot_spacing": 30, "remove_el_gradient": True},
    },
    map_postprocessing={
        "gaussian_filter": {"sigma": 1},
        "median_filter": {"size": 1},
    },
    units="uK_RJ",
)

mapper.add_tods(tods)
output_map = mapper.run()
Mapping band m2/f093: 100%|██████████| 2/2 [00:05<00:00,  2.80s/it, stokes=I, tod=2/2]
2025-08-24 19:02:31.988 INFO: Ran mapper for band m2/f093 in 5.606 s.
[7]:
output_map.plot()
output_map.to_fits("/tmp/simulated_mustang_map.fits")
../_images/tutorials_mustang-galaxy_7_0.png