From 1bb7e85c6b2d67a83f1cc0d10c17601ded02d089 Mon Sep 17 00:00:00 2001 From: tgalvin Date: Fri, 9 Aug 2024 11:43:31 +0800 Subject: [PATCH] type fixes/spelling --- flint/bandpass.py | 10 ++-- flint/imager/wsclean.py | 29 +++++------ flint/masking.py | 30 ++++++------ flint/ms.py | 103 ++++++---------------------------------- flint/summary.py | 32 ++++++------- 5 files changed, 66 insertions(+), 138 deletions(-) diff --git a/flint/bandpass.py b/flint/bandpass.py index 5d6c9acb..d0e3b5b5 100644 --- a/flint/bandpass.py +++ b/flint/bandpass.py @@ -10,7 +10,7 @@ from flint.calibrate.aocalibrate import AOSolutions, calibrate_apply_ms from flint.flagging import flag_ms_aoflagger from flint.logging import logger -from flint.ms import MS, describe_ms, preprocess_askap_ms +from flint.ms import MS, describe_ms, preprocess_askap_ms, get_field_id_for_field from flint.naming import create_ms_name from flint.sky_model import KNOWN_1934_FILES, get_1934_model @@ -145,10 +145,10 @@ def extract_correct_bandpass_pointing( return ms.with_options(beam=ms_summary.beam) good_field_name = f"{source_name_prefix}_beam{ms_summary.beam}" - field_id = ms.get_field_id_for_field(field_name=good_field_name) + field_id = get_field_id_for_field(ms=ms, field_name=good_field_name) out_name = create_ms_name(ms_path=ms.path, field=f"{source_name_prefix}") - + out_path = Path("./") # Handle writing out to elected output directory. # TODO: Move this to a helper utility. if ms_out_dir: @@ -185,7 +185,7 @@ def calibrate_bandpass( set will be created container just the appropriate field to calibrate. Args: - ms_path (Path): Path the the measurement set containing bandpass obervations of B1934-638 + ms_path (Path): Path the the measurement set containing bandpass observations of B1934-638 data_column (str): The column that will be calibrated. mode (str): The calibration approach to use. Currently only `calibrate` is supported. calibrate_container (Path): The path to the singularity container that holds the appropriate software. @@ -196,7 +196,7 @@ def calibrate_bandpass( Returns: MS: The calibrated measurement set with nominated column """ - logger.info(f"Will calibrate {str(ms_path)}, colum {data_column}") + logger.info(f"Will calibrate {str(ms_path)}, column {data_column}") # TODO: Check to make sure only 1934-638 model_path: Path = get_1934_model(mode=mode) diff --git a/flint/imager/wsclean.py b/flint/imager/wsclean.py index ab442231..2fef8dda 100644 --- a/flint/imager/wsclean.py +++ b/flint/imager/wsclean.py @@ -25,7 +25,7 @@ class ImageSet(NamedTuple): - """A structure to represent the images and auxillary products produced by + """A structure to represent the images and auxiliary products produced by wsclean""" prefix: str @@ -48,7 +48,7 @@ class WSCleanOptions(NamedTuple): Basic support for environment variables is available. Should a value start with `$` it is assumed to be a environment variable, it is will be looked up. - Some basic attempts to deterimine if it is a path is made. + Some basic attempts to determine if it is a path is made. Should the `temp_dir` options be specified then all images will be created in this location, and then moved over to the same parent directory @@ -78,7 +78,7 @@ class WSCleanOptions(NamedTuple): nmiter: int = 15 """Maximum number of major cycles to perform""" niter: int = 750000 - """Maximum numer of minor cycles""" + """Maximum number of minor cycles""" multiscale: bool = True """Enable multiscale deconvolution""" multiscale_scale_bias: float = 0.75 @@ -95,7 +95,7 @@ class WSCleanOptions(NamedTuple): ) """Scales used for multi-scale deconvolution""" fit_spectral_pol: int = 2 - """Number of spectral terms to include during sub-band subtractin""" + """Number of spectral terms to include during sub-band subtraction""" weight: str = "briggs -0.5" """Robustness of the weighting used""" data_column: str = "CORRECTED_DATA" @@ -280,7 +280,7 @@ def delete_wsclean_outputs( Args: prefix (str): The prefix of the files to remove. This would correspond to the -name of wsclean. output_type (str, optional): What type of wsclean output to try to remove. Defaults to 'image'. - ignore_mfs (bool, optional): If True, do not remove MFS outputs (attempt to, atleast). Defaults to True. + ignore_mfs (bool, optional): If True, do not remove MFS outputs (attempt to, at least). Defaults to True. Returns: Collection[Path]: The paths that were removed (or at least attempted to be removed)/ @@ -330,12 +330,13 @@ def create_wsclean_name_argument(wsclean_options: WSCleanOptions, ms: MS) -> Pat temp_dir = wsclean_options_dict.get("temp_dir", None) if temp_dir: # Resolve if environment variable - name_dir = ( + name_dir: Union[Path, str, None] = ( get_environment_variable(variable=temp_dir) if isinstance(temp_dir, str) and temp_dir[0] == "$" else Path(temp_dir) ) assert name_dir is not None, f"{name_dir=} is None, which is bad" + name_dir = Path(name_dir) name_argument_path = Path(name_dir) / name_prefix_str logger.info(f"Constructed -name {name_argument_path}") @@ -359,7 +360,7 @@ def _resolve_wsclean_key_value_to_cli_str(key: str, value: Any) -> ResolvedCLIRe the appropriate form to pass to a CLI call into wsclean. Args: - key (str): The wsclean argument name to consider. Underscores will be converted to hypens, as expected by wsclean + key (str): The wsclean argument name to consider. Underscores will be converted to hyphens, as expected by wsclean value (Any): The value of the argument that should be converted to the appropriately formatted string Returns: @@ -424,14 +425,14 @@ def create_wsclean_cmd( #. the `-name` argument will be generated and supplied to the CLI string and will default to the parent directory and name of the supplied measurement set #. If `wsclean_options.temp_dir` is specified this directory is used in place of the measurement sets parent directory - If `container` is supplied to immediatedly execute this command then the + If `container` is supplied to immediately execute this command then the output wsclean image products will be moved from the `temp-dir` to the same directory as the measurement set. Args: ms (MS): The measurement set to be imaged wsclean_options (WSCleanOptions): WSClean options to image with - container (Optional[Path], optional): If a path to a container is provided the command is executed immediatedly. Defaults to None. + container (Optional[Path], optional): If a path to a container is provided the command is executed immediately. Defaults to None. Raises: ValueError: Raised when a option has not been successfully processed @@ -562,7 +563,7 @@ def combine_subbands_to_cube( def run_wsclean_imager( wsclean_cmd: WSCleanCommand, container: Path, - bind_dirs: Optional[Tuple[Path]] = None, + bind_dirs: Optional[Tuple[Path, ...]] = None, move_hold_directories: Optional[Tuple[Path, Optional[Path]]] = None, make_cube_from_subbands: bool = True, image_prefix_str: Optional[str] = None, @@ -578,8 +579,8 @@ def run_wsclean_imager( Args: wsclean_cmd (WSCleanCommand): The command to run, and other properties (cleanup.) container (Path): Path to the container with wsclean available in it - bind_dirs (Optional[Tuple[Path]], optional): Additional directories to include when binding to the wsclean container. Defaults to None. - move_hold_directories (Optional[Tuple[Path,Optional[Path]]], optional): The `move_directory` and `hold_directory` passed to the temporary context manger. If None no `hold_then_move_into` manager is used. Defaults to None. + bind_dirs (Optional[Tuple[Path, ...]], optional): Additional directories to include when binding to the wsclean container. Defaults to None. + move_hold_directories (Optional[Tuple[Path,Optional[Path]]], optional): The `move_directory` and `hold_directory` passed to the temporary context manager. If None no `hold_then_move_into` manager is used. Defaults to None. make_cube_from_subbands (bool, optional): Form a single FITS cube from the set of sub-band images wsclean produces. Defaults to False. image_prefix_str (Optional[str], optional): The name used to search for wsclean outputs. If None, it is guessed from the name and location of the MS. Defaults to None. @@ -665,7 +666,7 @@ def wsclean_imager( Args: ms (Union[Path,MS]): Path to the measurement set that will be imaged wsclean_container (Path): Path to the container with wsclean installed - update_wsclean_options (Optional[Dict[str, Any]], optional): Additional options to update the generated WscleanOptions with. Keys should be attributes of WscleanOptions. Defaults ot None. + update_wsclean_options (Optional[Dict[str, Any]], optional): Additional options to update the generated WscleanOptions with. Keys should be attributes of WscleanOptions. Defaults to None. Returns: WSCleanCommand: _description_ @@ -694,7 +695,7 @@ def get_parser() -> ArgumentParser: subparser = parser.add_subparsers(dest="mode") wsclean_parser = subparser.add_parser( - "image", help="Attempt to run a wsclean commmand. " + "image", help="Attempt to run a wsclean command. " ) wsclean_parser.add_argument( "ms", type=Path, help="Path to a measurement set to image" diff --git a/flint/masking.py b/flint/masking.py index 68fc6d04..b2398eb6 100644 --- a/flint/masking.py +++ b/flint/masking.py @@ -5,7 +5,6 @@ from __future__ import annotations from argparse import ArgumentParser -from collections import Iterable from pathlib import Path from typing import Collection, NamedTuple, Optional, Union @@ -45,17 +44,17 @@ class MaskingOptions(NamedTuple): flood_fill_use_mbc_box_size: int = 75 """The size of the mbc box size should mbc be used""" suppress_artefacts: bool = True - """Whether to attempt artefacts based on the presence of sigificant negatives""" + """Whether to attempt artefacts based on the presence of significant negatives""" suppress_artefacts_negative_seed_clip: float = 5 - """The significance level of a negative island for the sidelobe suppresion to be activated. This should be a positive number (the signal map is internally inverted)""" + """The significance level of a negative island for the sidelobe suppression to be activated. This should be a positive number (the signal map is internally inverted)""" suppress_artefacts_guard_negative_dilation: float = 40 - """The minimum positive signifance pixels should have to be guarded when attempting to suppress artefacts around bright sources""" + """The minimum positive significance pixels should have to be guarded when attempting to suppress artefacts around bright sources""" suppress_artefacts_large_island_threshold: float = 1.0 """Threshold in units of beams for an island of negative pixels to be considered large""" grow_low_snr_island: bool = False """Whether to attempt to grow a mask to capture islands of low SNR (e.g. diffuse emission)""" grow_low_snr_island_clip: float = 1.75 - """The minimum signifance levels of pixels to be to seed low SNR islands for consideration""" + """The minimum significance levels of pixels to be to seed low SNR islands for consideration""" grow_low_snr_island_size: int = 768 """The number of pixels an island has to be for it to be accepted""" minimum_boxcar: bool = True @@ -103,8 +102,9 @@ def consider_beam_mask_round( return mask_rounds is not None and ( (isinstance(mask_rounds, str) and mask_rounds.lower() == "all") or (isinstance(mask_rounds, int) and current_round >= mask_rounds) - or (isinstance(mask_rounds, Iterable) and current_round in mask_rounds) # type: ignore - ) + or (isinstance(mask_rounds, (list, tuple))) + and current_round in mask_rounds + ) # type: ignore def extract_beam_mask_from_mosaic( @@ -126,7 +126,7 @@ def extract_beam_mask_from_mosaic( Returns: FITSMaskNames: _description_ """ - # TODO: Ideally we can accept an arbitary WCS, or read the wsclean docs to + # TODO: Ideally we can accept an arbitrary WCS, or read the wsclean docs to # try to construct it ourselves. The last thing that this pirate wants is # to run the imager in a dry-run type mode n cleaning type mode purely for # the WCS. @@ -198,7 +198,7 @@ def grow_low_snr_mask( a mask through to the imagery of choice the islands may be larger than the features at their native resolution, unless some other more sophisticated filtering is performed. - This function attempts to grow masks to capture islands of contigous pixels above a low + This function attempts to grow masks to capture islands of contiguous pixels above a low SNR cut that would otherwise go uncleaned. Args: @@ -255,13 +255,13 @@ def minimum_boxcar_artefact_mask( increase_factor: float = 2.0, ) -> np.ndarray: """Attempt to remove islands from a potential clean mask by - examining surronding pixels. A boxcar is applied to find the + examining surrounding pixels. A boxcar is applied to find the minimum signal to noise in a small localised region. For each island the maximum signal is considered. If the absolute minimum signal increased by a factor in an island region is larger to the maximum signal then that island - is ommited. + is omitted. Args: @@ -334,12 +334,12 @@ def suppress_artefact_mask( - negative islands are around bright sources with deconvolution/calibration errors - if there are brightish negative islands there is also positive brightish arteefact islands nearby - For this reason the guard mask should be sufficently high to protect the main source but nuke the fask positive islands + For this reason the guard mask should be sufficiently high to protect the main source but nuke the fask positive islands Args: signal (np.ndarray): The signal mask, - negative_seed_clip (float): The minimum signficance level to seed. This is a positive number (as it is applied to the inverted signal). + negative_seed_clip (float): The minimum significance level to seed. This is a positive number (as it is applied to the inverted signal). guard_negative_dilation (float): Regions of positive emission above this are protected. This is positive. pixels_per_beam (Optional[float], optional): The number of pixels per beam. If not None, seed islands larger than this many pixels are removed. Defaults to None. large_island_threshold (float, optional): The number of beams required for a large island of negative pixels to be dropped as an artefact seed. Only used if `pixels_per_beam` is set. Defaults to 1.0. @@ -433,12 +433,12 @@ def reverse_negative_flood_fill( """Attempt to: * seed masks around bright regions of an image and grow them to lower significance thresholds - * remove regions of negative and positive islands that surrond bright sources. + * remove regions of negative and positive islands that surround bright sources. An initial set of islands (and masks) are constructed by first using the `positive_seed_clip` to create an initial SNR based mask. These islands then are binary dilated to grow the islands - to adjacent pixels at a lower signifcance level (see `scipy.ndimage.binary_dilation`). + to adjacent pixels at a lower significance level (see `scipy.ndimage.binary_dilation`). Next an attempt is made to remove artefacts around bright sources, where there are likely to be positive and negative artefacts diff --git a/flint/ms.py b/flint/ms.py index 76dcf957..94e5298a 100644 --- a/flint/ms.py +++ b/flint/ms.py @@ -32,80 +32,6 @@ from flint.options import MS from flint.utils import copy_directory, rsync_copy_directory -# class MS(NamedTuple): -# """Helper to keep track of measurement set information - -# This is the class that should be used when describing a measurement -# set that will be operated on. -# """ - -# path: Path -# """Path to the measurement set that is being represented""" -# column: Optional[str] = None -# """Column that should be operated against""" -# beam: Optional[int] = None -# """The beam ID of the MS within an ASKAP field""" -# spw: Optional[int] = None -# """Intended to be used with ASKAP high-frequency resolution modes, where the MS is divided into SPWs""" -# field: Optional[str] = None -# """The field name of the data""" -# model_column: Optional[str] = None -# """The column name of the most recently MODEL data""" - -# def get_field_id_for_field(self, field_name: str) -> Union[int, None]: -# """Return the FIELD_ID for an elected field in a measurement set. See -# `flink.ms.get_field_id_for_field` for full details. -# """ -# # TODO: I think this should be removed. The young pirate in me was -# # going to go in a different direction -# return get_field_id_for_field(ms=self, field_name=field_name) - -# @property -# def ms(self) -> MS: -# return self - -# @classmethod -# def cast(cls, ms: Union[MS, Path]) -> MS: -# """Create/return a MS instance given either a Path or MS. - -# If the input is neither a MS instance or Path, the object will -# be checked to see if it has a `.ms` attribute. If it does then -# this will be used. - -# Args: -# ms (Union[MS, Path]): The input type to consider - -# Raises: -# MSError: Raised when the input ms can not be cast to an MS instance - -# Returns: -# MS: A normalised MS -# """ -# if isinstance(ms, MS): -# # Nothing to do -# pass -# elif isinstance(ms, Path): -# ms = MS(path=ms) -# elif "ms" in dir(ms) and isinstance(ms.ms, MS): -# ms = ms.ms -# else: -# raise MSError(f"Unable to convert {ms=} of {type(ms)} to MS object. ") - -# return ms - -# def with_options(self, **kwargs) -> MS: -# """Create a new MS instance with keywords updated - -# Returns: -# MS: New MS instance with updated attributes -# """ -# # TODO: Update the signature to have the actual attributes to -# # help keep mypy and other linters happy -# as_dict = self._asdict() -# as_dict.update(kwargs) - -# return MS(**as_dict) - class MSSummary(NamedTuple): """Small structure to contain overview of a MS""" @@ -114,7 +40,7 @@ class MSSummary(NamedTuple): """Number of unflagged records""" flagged: int """Number of flagged records""" - flag_spectrum: np.ndarray[float] + flag_spectrum: np.ndarray """Flagged spectral channels""" fields: List[str] """Collection of unique field names from the FIELDS table""" @@ -147,7 +73,7 @@ def critical_ms_interaction( nonsense. This mechanism is intended to make it clear that the measurement set is in a dangerous part of code. - Failure to return the MS to its orignal name (or rename the copy) highlights + Failure to return the MS to its original name (or rename the copy) highlights this failed stage. Args: @@ -178,7 +104,7 @@ def critical_ms_interaction( yield output_ms except Exception as e: logger.error( - f"An error occured when interacting with {input_ms} during a critical stage. " + f"An error occurred when interacting with {input_ms} during a critical stage. " ) raise e @@ -246,7 +172,7 @@ def get_beam_from_ms(ms: Union[MS, Path]) -> int: def get_freqs_from_ms(ms: Union[MS, Path]) -> np.ndarray: - """Return the frequencies observed from an ASKAP Meaurement set. + """Return the frequencies observed from an ASKAP Measurement set. Some basic checks are performed to ensure they conform to some expectations. @@ -337,7 +263,7 @@ def get_pol_axis_from_ms( Args: ms (Union[MS, Path]): The path to the measurement set that will be inspected - feed_idx (Optional[int], optional): Specify the entery in the FEED + feed_idx (Optional[int], optional): Specify the entry in the FEED table of `ms` to return. This might be required when a subset of a measurement set has been extracted from an observation with a varying orientation. @@ -360,7 +286,7 @@ def get_pol_axis_from_ms( ms_feed = tf.getcol(col) * u.rad # PAF is at 45deg to feeds # 45 - feed_angle = pol_angle - pol_axes = -(ms_feed - 45.0 * u.deg) + pol_axes = -(ms_feed - (45.0 * u.deg)) # type: ignore if feed_idx is None: assert (ms_feed[:, 0] == ms_feed[0, 0]).all() & ( @@ -372,6 +298,7 @@ def get_pol_axis_from_ms( logger.debug(f"Extracting the third-axis orientation for {feed_idx=}") pol_ang = pol_axes[feed_idx, 0].to(u.deg) + assert pol_ang is not None, f"{pol_ang=}, which should not happen" return pol_ang @@ -393,7 +320,7 @@ def describe_ms(ms: Union[MS, Path], verbose: bool = False) -> MSSummary: with table(str(ms.path), readonly=True, ack=False) as tab: colnames = tab.colnames() - flags: np.ndarray[bool] = tab.getcol("FLAG") + flags: np.ndarray = tab.getcol("FLAG") flagged = np.sum(flags == True) # Noqa: E712 unflagged = np.sum(flags == False) # Noqa: E712 total = np.prod(flags.shape) @@ -448,7 +375,7 @@ def split_by_field( """ ms = MS.cast(ms) - # TODO: Split describe_ms up so can get just fiels + # TODO: Split describe_ms up so can get just fields ms_summary = describe_ms(ms, verbose=False) logger.info("Collecting field names and corresponding FIELD_IDs") @@ -466,7 +393,7 @@ def split_by_field( ms_out_dir.mkdir(parents=True) except Exception as e: logger.warning(e) - pass # Incase above fails due to race condition + pass # In case above fails due to race condition logger.info(f"Opening {ms.path}. ") with table(str(ms.path), ack=False) as tab: # noqa: F841 @@ -623,7 +550,7 @@ def preprocess_askap_ms( skip_rotation: bool = False, fix_stokes_factor: bool = False, ) -> MS: - """The ASKAP MS stores its data in a way that is not immediatedly accessible + """The ASKAP MS stores its data in a way that is not immediately accessible to other astronomical software, like wsclean or casa. For each measurement set the centre of the field is stored, and beam offsets are stored in a separate table. @@ -687,7 +614,7 @@ def preprocess_askap_ms( logger.info(f"Returning {ms=}.") return ms - logger.info("Applying roation matrix to correlations. ") + logger.info("Applying rotation matrix to correlations. ") logger.info( f"Rotating visibilities for {ms.path} with data_column={instrument_column} amd corrected_data_column={data_column}" ) @@ -753,7 +680,7 @@ def copy_and_preprocess_casda_askap_ms( logger.info("Correcting directions. ") fix_ms_dir(ms=str(ms.path)) - logger.info("Applying roation matrix to correlations. ") + logger.info("Applying rotation matrix to correlations. ") logger.info( f"Rotating visibilities for {ms.path} with data_column={instrument_column} amd corrected_data_column={data_column}" ) @@ -782,7 +709,7 @@ def rename_ms_and_columns_for_selfcal( Args: ms (MS): The subject measurement set to rename - target (Union[str, Path]): The targett path the measurement set will be renamed to. This shoudl not already exist. + target (Union[str, Path]): The targett path the measurement set will be renamed to. This should not already exist. corrected_data (str, optional): The name of the column with the latest calibrated data. This becomes the `data` column. Defaults to "CORRECTED_DATA". data (str, optional): The name of the column that will be subsequently processed. If it exists it will be removed. Defaults to "DATA". @@ -807,7 +734,7 @@ def rename_ms_and_columns_for_selfcal( logger.info(f"Renaming {ms.path} to {target=}") ms.path.rename(target=target) - # Just some sanity incase None is passed through + # Just some sanity in case None is passed through if not (corrected_data or data): return ms.with_options(path=target) diff --git a/flint/summary.py b/flint/summary.py index d50c3777..0c4ec1c6 100644 --- a/flint/summary.py +++ b/flint/summary.py @@ -5,7 +5,7 @@ ) from pathlib import Path -from typing import Collection, NamedTuple, Optional, Tuple, Union +from typing import NamedTuple, Optional, Tuple, Union, List import astropy.units as u from astropy.coordinates import ( @@ -51,7 +51,7 @@ class FieldSummary(NamedTuple): There are known issues around serialising astropy units within a dask/prefect environment, for example: https://github.com/astropy/astropy/issues/11317, - This could become important if an instance of this object is exchaned + This could become important if an instance of this object is exchanged between many prefect or dask like delayed tasks. Ye be warned. """ @@ -83,7 +83,7 @@ class FieldSummary(NamedTuple): """Computed elevations of the field""" median_rms: Optional[float] = None """The meanian RMS computed from an RMS image""" - beam_summaries: Optional[Collection[BeamSummary]] = None + beam_summaries: Optional[List[BeamSummary]] = None """Summary information from each beam. Contains MSSummary, ImageSet and other information.""" linmos_image: Optional[Path] = None """The path to the linmos image of all beams""" @@ -118,9 +118,7 @@ def _get_pol_axis_as_rad(ms: Union[MS, Path]) -> float: # TODO: Need to establise a MSLike type -def add_ms_summaries( - field_summary: FieldSummary, mss: Collection[MS] -) -> Tuple[MSSummary]: +def add_ms_summaries(field_summary: FieldSummary, mss: List[MS]) -> FieldSummary: """Obtain a MSSummary instance to add to a FieldSummary Quantities derived from the field centre (hour angles, elevations) are @@ -131,7 +129,7 @@ def add_ms_summaries( Args: field_summary (FieldSummary): Existing field summary object to update - mss (Collection[MS]): Set of measurement sets to describe + mss (List[MS]): Set of measurement sets to describe Returns: Tuple[MSSummary]: Results from the inspected set of measurement sets @@ -151,8 +149,8 @@ def add_ms_summaries( telescope = field_summary.location ms_times = field_summary.ms_times centre_altaz = centre.transform_to(AltAz(obstime=ms_times, location=telescope)) - hour_angles = centre_altaz.az.to(u.hourangle) - elevations = centre_altaz.alt.to(u.deg) + hour_angles = centre_altaz.az.to(u.hourangle) # type: ignore + elevations = centre_altaz.alt.to(u.deg) # type: ignore # The INSTRUMENT_RECEPTOR_ANGLE comes from fixms and is # inserted to preserve the original orientation. @@ -227,7 +225,7 @@ def add_linmos_fits_image( def update_field_summary( field_summary: FieldSummary, aegean_outputs: Optional[AegeanOutputs] = None, - mss: Optional[Collection[MS]] = None, + mss: Optional[List[MS]] = None, linmos_command: Optional[LinmosCommand] = None, **kwargs, ) -> FieldSummary: @@ -266,7 +264,7 @@ def update_field_summary( def create_field_summary( - mss: Collection[Union[MS, Path]], + mss: List[Union[MS, Path]], cal_sbid_path: Optional[Path] = None, holography_path: Optional[Path] = None, aegean_outputs: Optional[AegeanOutputs] = None, @@ -293,10 +291,10 @@ def create_field_summary( # TODO: A check here to ensure all MSs are in a consistent format # and are from the same field - ms = mss[0] + ms = MS.cast(ms=mss[0]) ms_components = processed_ms_format(in_name=ms.path) - + assert ms_components is not None sbid = str(ms_components.sbid) field = ms_components.field @@ -307,7 +305,7 @@ def create_field_summary( str(get_sbid_from_path(path=cal_sbid_path)) if cal_sbid_path else None ) except ValueError: - cal_sbid = -9999 + cal_sbid = "-9999" logger.info(f"Extracting SBID from {cal_sbid_path=} failed. Using {cal_sbid=}") ms_times = get_times_from_ms(ms=ms) @@ -319,7 +317,7 @@ def create_field_summary( field_summary = FieldSummary( sbid=sbid, field_name=field, - cal_sbid=cal_sbid, + cal_sbid=f"{cal_sbid}", # could be None, and that's OK location=location, integration_time=integration, holography_path=holography_path, @@ -328,7 +326,9 @@ def create_field_summary( **kwargs, ) - field_summary = add_ms_summaries(field_summary=field_summary, mss=mss) + field_summary = add_ms_summaries( + field_summary=field_summary, mss=[MS.cast(ms=ms) for ms in mss] + ) if aegean_outputs: field_summary = add_rms_information(