Source code for punchbowl.level3.low_noise

from datetime import UTC, datetime

import numpy as np
from astropy.nddata import StdDevUncertainty
from dateutil.parser import parse as parse_datetime
from ndcube import NDCube

from punchbowl.data.meta import NormalizedMetadata
from punchbowl.data.punch_io import check_outlier
from punchbowl.prefect import punch_task


[docs] @punch_task def create_low_noise_task(cubes: list[NDCube]) -> NDCube: """Create a low noise image from a set of inputs.""" cubes = [cube for cube in cubes if not check_outlier(cube)] ref_cube_index = len(cubes)//2 - 1 data_stack = np.stack([cube.data for cube in cubes], axis=0) uncertainty_stack = np.array([cube.uncertainty.array for cube in cubes]) uncertainty_stack[uncertainty_stack <= 0] = np.inf uncertainty_stack[~np.isfinite(uncertainty_stack)] = 1E32 uncertainty_stack[np.isnan(data_stack)] = np.inf uncertainty_stack[data_stack == 0] = np.inf weight_stack = 1/np.square(uncertainty_stack) weight_stack[np.isnan(uncertainty_stack)] = np.nan new_data = np.nansum(data_stack * weight_stack, axis=0) / np.nansum(weight_stack, axis=0) final_uncertainty = np.sqrt(np.nansum(uncertainty_stack, axis=0)) / np.sqrt(new_data) new_code = cubes[0].meta.product_code[0] + "A" + cubes[0].meta.product_code[2] new_meta = NormalizedMetadata.load_template(new_code, "3") for k in cubes[0].meta.fits_keys: if k not in ("COMMENT", "HISTORY", "") and k in new_meta: new_meta[k] = cubes[ref_cube_index].meta[k].value times_obs = np.array([cube.meta.datetime.timestamp() for cube in cubes]) times_beg = np.array([parse_datetime(cube.meta["DATE-BEG"].value).replace(tzinfo=UTC).timestamp() for cube in cubes]) times_end = np.array([parse_datetime(cube.meta["DATE-END"].value).replace(tzinfo=UTC).timestamp() for cube in cubes]) new_meta["TYPECODE"] = new_code[0:2] # TODO - distinction here? and mean versus midpoint. new_meta["DATE-OBS"] = datetime.fromtimestamp(np.mean(times_obs), tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] new_meta["DATE-AVG"] = datetime.fromtimestamp(np.mean(times_obs), tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] new_meta["DATE-BEG"] = datetime.fromtimestamp(np.min(times_beg), tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] new_meta["DATE-END"] = datetime.fromtimestamp(np.max(times_end), tz=UTC).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] return NDCube(data=new_data, uncertainty=StdDevUncertainty(final_uncertainty), wcs=cubes[ref_cube_index].wcs, meta=new_meta)