Skip to content

config

Module for handling all configuration of scripts.

Config File Fields

General pipeline settings

cfg.root_dir

Root directory for output products.

cfg.tel

Telescope identifier used when constructing the output directory tree.

cfg.pointing_type

Pointing type used to distinguish beam-analysis products. ie. pointing_model, raw, etc.

cfg.append

Optional suffix appended to the output directory name.

cfg.test_append

Optional suffix used when naming directories and jobdb. Mostly used for testing and one-offs.

cfg.copy_fits_test

If True when when test_append is not "" then make a copy of the beam fits. This will overwrite an existing file.

cfg.single_det

Whether the analysis is operating on single-detector data. When True,_single_det is appended to the output directory name.

cfg.ctx_path

Path to the sotodlib context.

cfg.preprocess_cfg

Path to the preprocessing configuration used to load and preprocess the data before fitting or mapmaking.

cfg.source_list

List of source names to process. Note that this has the following aliases:

  • map_source_list: used in make_source_map.
  • fit_source_list: used in fit_source_map.

This distiction is because there are sources we want to map that we do not want to fit in the standard pipeline (ie. TauA).

cfg.start_time

Lower bound on the observation timestamp used when selecting jobs. If args.lookback is passed then this becomes the current time minus the lookback.

cfg.stop_time

Upper bound on the observation timestamp used when selecting jobs. If args.lookback is passed then this becomes the current time.

cfg.fwhm_tol

Fractional tolerance between the measured radial FWHM and the nominal band FWHM. A fit is rejected when: abs(1 - data_fwhm / nominal_fwhm) > fwhm_tol This is aliased to fwhm_tol_map for map fitting and fwhm_tol_pointing for pointing fits..

cfg.nominal_fwhm

Mapping from observing band to nominal beam FWHM. These values are used for the initial Gaussian fit, FWHM quality cuts, noise estimation, and stacking diagnostics. These should be in arcmins and should be a dict where each key is a bandname (ie. "f090").

cfg.min_samps

Minimum number of source-flagged samples required for a pointing fit to proceed. It is also used when deciding which detectors have enough source-flagged samples to remain in the fit.

cfg.min_dets

Minimum number of detectors required after cuts.

Mapmaking

cfg.extent

Angular extent of the map region used for beam fitting and stacking. Also used when generating fitted-model and residual diagnostic plots. Should be in arcseconds.

cfg.res

Target pixel resolution used when constructing the common tangent-plane WCS for beam maps and high-resolution profile calculations. Should be in radians.

cfg.mask_size

Angular size of the mask used during mapmaking and beam-model fitting. When cfg.apply_fscale is enabled, the fitting stage scales this value according to the observing frequency before converting it to radians. This is aliased by map_mask_size.

cfg.apply_fscale

Whether beam-mask is adjusted according to observing frequency. When enabled, the mask is scaled by 90 / frequency_GHz.

cfg.aperature

Aperture size used by the Bessel beam model. The fitting stage converts this value to a Quantity in meters before passing it to the Bessel fitting routine.

cfg.buf

Buffer used when estimating the beam center on the original map. In units of pixels.

cfg.buf_cropped

Buffer used when estimating the beam center after the map has been cropped, again in pixels.

cfg.smooth_kern

Angular smoothing scale used when estimating the beam center.

cfg.snr_extent

Angular extent around the estimated beam center excluded when estimating map noise for the initial SNR calculation.

cfg.extent_highres

Angular extent of the high-resolution map used for calculating the final profile and covariance.

cfg.pixsize_highres

Pixel size for the high-resolution final profile.

cfg.search_mask

Mask definition used to search for the source in an initial map.

cfg.del_map

If True delete maps that don't pass cuts in mapmaking.

cfg.cgiters_single

Number of CG iters used when making a single obs ML map.

cfg.cgiters_full

Number of CG iters used when making a full ML map.

cfg.mlpass

Number of passes to run the ML mapmaker for.

cfg.comps

Which comps to mapmake. Should be "T" or "TQU".

cfg.force_zero_cent

Whether map-fitting workflows force the beam center to zero instead of fitting for a recenter.

cfg.n_modes

Number of modes to remove when mapmaking.

cfg.relcal_range

Allowed relative calibration range.

cfg.min_det_secs

Minimum number of detector seconds in the source mask needed to mapmake.

Pointing fits

cfg.forced_ws

Wafer-slot identifiers that are forced to be processed even when they are not present in the observation's source tags. The pointing-fit script uses these values when constructing the set of wafer slots eligible for fitting.

cfg.try_all

If True then try all wafer slots. This will override forced_ws.

cfg.max_dur

Maximum allowed observation duration, in hours, when selecting pointing-fit observations from the observation database.

cfg.nominal_path

Path to the nominal focal-plane pointing model. The pointing-fit script loads this HDF5 file and uses it to obtain nominal detector positions, calculate the UFM radius, and provide nominal pointing information for source masking.

cfg.pointing_mask

Mask definition used when generating source flags with the centered source flagger. The pointing-fit script passes this configuration to sotodlib.coords.planets.compute_source_flags to identify samples containing the astronomical source.

cfg.ds

Downsampling factor applied to the TOD before filtering and fitting.

cfg.hp_fc

High-pass filter cutoff frequency used when filtering the TOD before the pointing fit. It is also passed to fit_tod_pointing as part of the filter configuration.

cfg.lp_fc

Low-pass filter cutoff frequency used when filtering the TOD before the pointing fit. It is also passed to fit_tod_pointing as part of the filter configuration.

cfg.n_med

Multiplier applied to the median detector noise when rejecting unusually noisy detectors within each frequency band.

cfg.n_std

Number of standard deviations used by source-flagging logic. It controls the threshold in the blind and SVD source flaggers.

cfg.block_size

Time/sample block size used by source-flagging logic. It controls the minimum extent and separation of flagged source regions and the buffering applied to source flags.

cfg.trim_samps

Number of samples trimmed from each edge of the downsampled TOD to avoid Fourier-filter ringing.

cfg.min_hits

Minimum number of source hits required for an individual detector fit to be considered acceptable.

cfg.high_hits

Higher hit-count threshold used when identifying a sufficiently well-sampled set of detectors for estimating the center of the array.

cfg.max_chisq

Maximum allowed reduced chi-squared for an individual pointing fit. Detectors with reduced chi-squared above this threshold are marked as bad.

cfg.min_R2

Minimum acceptable R2 value for a pointing fit. Fits below this threshold are excluded from the focal-plane diagnostic plot and treated as bad fits.

cfg.svd_modes

Number of SVD modes used by the SVD-based source flagger. When source filtering is enabled, the same value is also passed to cp.filter_for_sources.

cfg.svd_iters

Number of iterations used by the SVD-based source flagger.

cfg.iter_svd_sub

Whether the SVD-derived common mode is subtracted from the TOD after SVD source identification.

cfg.filter_for_sources

Whether the pointing-fit TOD is additionally filtered using the source flags and SVD modes before fitting.

cfg.source_flag_exp

Expression defining how source flags are combined. The default expression is (svd + blind) * cent. The pointing-fit script evaluates this expression using source flags supplied by the SVD, blind, and centered source flaggers.

cfg.fit_pars

Additional keyword arguments passed directly to fit_tod_pointing.

cfg.pad

Whether the pointing-fit result is padded with detectors that were present in the observation metadata but did not produce a fitted result. When enabled, missing detectors are added with NaN values for floating-point fit fields.

cfg.src_msk

Whether samples identified by the source-flag expression are used to restrict the TOD to the source-crossing region and remove detectors with insufficient source-flagged samples.

Beam-fit configuration

cfg.sym_gauss

Whether the Gaussian beam fit is constrained to be symmetric.

cfg.min_snr

Minimum SNR required for an individual beam map to proceed through the fitting stage.

cfg.bessel_beam

Whether to fit the Bessel-based beam model after the Gaussian fit.

cfg.min_sigma

Minimum allowed beam-model width used when validating and processing fitted Gaussian and Bessel model parameters. Set to a negetive value to use the whole map.

cfg.n_bessel

Number of Bessel terms/components used by the Bessel beam fit.

cfg.n_multipoles

Number of multipoles included in the Bessel beam model. This also controls the number of non-axisymmetric beam modes shown in fitting diagnostics.

cfg.skip_multipoles

Multipoles excluded from the Bessel beam fit.

cfg.bessel_wing_n_sigma

Controls the extent of the Bessel-model wing relative to the fitted beam. When frequency scaling is enabled, the fitting stage scales this value by the same factor used for the beam mask.

cfg.gauss_multipole

If True fit for the multipole expansion of the Gauss fit.

cfg.corr_primary

Error correlation scale of the mirror in mm.

info" "cfg.eps_primary

RMS error of the mirror in um-rms.

Stacking quality cuts

cfg.min_stack_snr

Minimum fitted beam SNR required for an observation to contribute to a stack.

cfg.max_pwv

Maximum allowed PWV/elevation-corrected atmospheric loading. Fits are retained only when: pwv / sin(elevation) <= max_pwv.

cfg.max_cut_pix_frac

Maximum allowed fraction of pixels masked or removed from a candidate beam map before it is rejected from a stack.

cfg.min_irat

Minimum acceptable inverse-variance median-to-variance ratio. Used to reject maps with poorly behaved or highly structured inverse variance.

cfg.max_cn

Threshold on the logarithm of the correlated/white noise levels used during map-quality selection.

cfg.corr_ratio_cut

Maximum allowed correlated-to-white-noise ratio, subject to the adjustment based on the absolute noise levels.

cfg.miscenter_thresh

Maximum allowed displacement, in pixels, between the estimated beam center and the expected center of the reprojected map.

Noise and diagnostic configuration

cfg.n_lmin

Lower multipole bound used when estimating map noise.

cfg.n_lmax

Upper multipole bound used when estimating map noise.

cfg.log_thresh

Logarithmic threshold used when generating beam-map diagnostic plots.

cfg.empir_cov

Whether to calculate empirical covariance information from the individual beam fits. In the fitting stage, empirical covariance is loaded when more than five contributing fits are available. It also controls whether empirical scatter based summary plots are generated.

cfg.lmax

Maximum multipole used when calculating the beam window function and Bessel profile covariance.

cfg.cov_modes

Number or configuration of covariance modes retained when calculating the Bessel profile covariance.

Split and epoch configuration

cfg.det_split_dir

Directory associated with detector splits. This field is initialized by setup_cfg but is not directly used by the pointing-fit script.

cfg.det_splits

Detector split names to process. Each script automatically adds "full" to this list when selecting stack-map jobs.

cfg.split_by

Split dimensions used to select stack jobs for fitting. These can be anything that beam_utils.get_split_vec can understand.

cfg.metasplits

Metadata split definitions passed to the beam-processing utilities when constructing split vectors.

cfg.epochs

Sequence of (start, end) time ranges over which stack jobs are constructed or selected. The fitting stage only processes jobs whose epoch range matches one of these configured ranges.

deep_merge(a, b)

Recursively merge two dictionaries.

Values from b take precedence over values from a. When a key exists in both dictionaries and both corresponding values are dictionaries, those dictionaries are merged recursively. All other values from b replace the corresponding values from a. Values are deep-copied when inserted into the result, so mutable values in the input dictionaries are not shared with the returned dictionary.

Parameters:

Name Type Description Default
a dict[str, Any]

The base dictionary.

required
b dict[str, Any]

The dictionary whose values take precedence.

required

Returns:

Type Description
dict[str, Any]

A new dictionary containing the recursively merged values. Neither input dictionary is modified.

Source code in lat_beams/utils/config.py
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
def deep_merge(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]:
    """
    Recursively merge two dictionaries.

    Values from `b` take precedence over values from `a`. When a key
    exists in both dictionaries and both corresponding values are
    dictionaries, those dictionaries are merged recursively. All other
    values from `b` replace the corresponding values from `a`.
    Values are deep-copied when inserted into the result, so mutable values
    in the input dictionaries are not shared with the returned dictionary.

    Parameters
    ----------
    a : dict[str, Any]
        The base dictionary.
    b : dict[str, Any]
        The dictionary whose values take precedence.

    Returns
    -------
    dict[str, Any]
        A new dictionary containing the recursively merged values. Neither
        input dictionary is modified.
    """
    result = deepcopy(a)
    for bk, bv in b.items():
        av = result.get(bk)
        if isinstance(av, dict) and isinstance(bv, dict):
            result[bk] = deep_merge(av, bv)
        else:
            result[bk] = deepcopy(bv)
    return result

get_args_cfg()

Parse command-line arguments and load the configuration file. Run the script with --help for details.

Returns:

Name Type Description
args Namespace

Parsed command-line arguments.

cfg dict[str, Any]

Configuration loaded from the YAML file. This is loaded recursively, see load_config for details.

Source code in lat_beams/utils/config.py
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
def get_args_cfg() -> tuple[argparse.Namespace, dict[str, Any]]:
    """
    Parse command-line arguments and load the configuration file.
    Run the script with `--help` for details.

    Returns
    -------
    args : argparse.Namespace
        Parsed command-line arguments.
    cfg : dict[str, Any]
        Configuration loaded from the YAML file.
        This is loaded recursively, see `load_config` for details.
    """
    # Only the config is necessary; the rest are just for ease of use.
    parser = argparse.ArgumentParser()
    parser.add_argument("cfg", help="Path to the config file")
    parser.add_argument(
        "--plot_only",
        "-p",
        action="store_true",
        help="Don't do any fitting or mamaking, just plot TODs or existing maps",
    )
    parser.add_argument(
        "--summary",
        "-s",
        action="store_true",
        help="Don't do any fitting or mamaking, just plot a summary of results",
    )
    # Control which obs are used
    parser.add_argument("--obs_ids", nargs="+", help="Pass a list of obs ids to run on")
    parser.add_argument(
        "--lookback",
        "-l",
        type=float,
        help="Amount of time to lookback for query, overides start time from config",
    )
    # JobDB stuff
    parser.add_argument(
        "--overwrite", "-o", action="store_true", help="Overwrite an existing fit"
    )
    parser.add_argument(
        "--retry_failed", "-r", action="store_true", help="Retry failed jobs"
    )
    parser.add_argument(
        "--job_memory",
        "-m",
        type=float,
        help="If job was run within this many hours of this script starting then don't rerun even if overwrite or retry_failed is passed",
    )
    parser.add_argument(
        "--job_memory_buffer",
        "-mb",
        default=0,
        type=float,
        help="If job was run within this many minutes of this script starting then rerun even if job_memory is passed",
    )
    # Shared useful stuff
    parser.add_argument(
        "--profile",
        action="store_true",
        help="Run a profile (only for fit_pointing and make_source_mask)",
    )
    # fit_pointing exclusive args
    parser.add_argument(
        "--forced_ws",
        "-ws",
        nargs="+",
        help="Force these wafer slots into the fit (only for fit_pointing)",
    )
    parser.add_argument(
        "--parallel_factor",
        "-f",
        default=4,
        type=int,
        help="Per-obs parallelization factor (only for fit_pointing)",
    )
    args = parser.parse_args()
    cfg = load_config({}, args.cfg)

    return args, cfg

load_config(start_cfg, cfg_path)

Load a configuration file and recursively merge its base configuration.

The configuration at cfg_path is loaded and merged with start_cfg. If the loaded configuration contains a base key then the referenced base configuration is loaded recursively and is merged in.

Values from the more specific configuration take precedence over values from its base configuration.

Note that relative "base" paths are resolved relative to the directory containing the configuration file that references them.

Parameters:

Name Type Description Default
start_cfg dict[str, Any]

Configuration values that take precedence over values loaded from cfg_path.

required
cfg_path str

Path to the YAML configuration file to load.

required

Returns:

Type Description
dict[str, Any]

The fully merged configuration.

Raises:

Type Description
FileNotFoundError

If cfg_path or a referenced base configuration does not exist.

YAMLError

If a configuration file contains invalid YAML.

Source code in lat_beams/utils/config.py
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
def load_config(start_cfg: dict[str, Any], cfg_path: str) -> dict[str, Any]:
    """
    Load a configuration file and recursively merge its base configuration.

    The configuration at `cfg_path` is loaded and merged with
    `start_cfg`. If the loaded configuration contains a `base` key then
    the referenced base configuration is loaded recursively and is merged in.

    Values from the more specific configuration take precedence over values
    from its base configuration.

    Note that relative ``"base"`` paths are resolved relative to the directory
    containing the configuration file that references them.

    Parameters
    ----------
    start_cfg : dict[str, Any]
        Configuration values that take precedence over values loaded from `cfg_path`.
    cfg_path : str
        Path to the YAML configuration file to load.

    Returns
    -------
    dict[str, Any]
        The fully merged configuration.

    Raises
    ------
    FileNotFoundError
        If `cfg_path` or a referenced base configuration does not exist.
    yaml.YAMLError
        If a configuration file contains invalid YAML.
    """
    with open(cfg_path) as file:
        new_cfg = yaml.safe_load(file)

    cfg = deep_merge(new_cfg, start_cfg)
    if "base" in new_cfg:
        base_path = new_cfg["base"]
        if not os.path.isabs(base_path):
            base_path = os.path.join(os.path.dirname(cfg_path), base_path)
        return load_config(cfg, base_path)

    return cfg

setup_cfg(args, cfg, replace=None, apply_ds=False)

Apply defaults and command-line overrides to a loaded configuration. This also lets you rename things. When loading from cfg_str you don't need to apply any processing, you can just convert directly to a dict to get the final config.

Parameters:

Name Type Description Default
args Namespace

Parsed command-line arguments.

required
cfg dict[str, Any]

Configuration dictionary to modify.

required
replace Optional[dict[str, str]]

Mapping of configuration keys to rename. Keys present in cfg are copied to their new names and removed from their old names.

None
apply_ds bool

Whether downsampling should be applied when calculating sample-dependent configuration values.

False

Returns:

Name Type Description
cfg Namespace

Configuration converted to an attribute-accessible namespace.

cfg_str str

YAML representation of the final configuration.

Source code in lat_beams/utils/config.py
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
def setup_cfg(
    args: argparse.Namespace,
    cfg: dict[str, Any],
    replace: Optional[dict[str, str]] = None,
    apply_ds: bool = False,
) -> tuple[argparse.Namespace, str]:
    """
    Apply defaults and command-line overrides to a loaded configuration.
    This also lets you rename things. When loading from `cfg_str` you
    don't need to apply any processing, you can just convert directly
    to a dict to get the final config.

    Parameters
    ----------
    args : argparse.Namespace
        Parsed command-line arguments.
    cfg : dict[str, Any]
        Configuration dictionary to modify.
    replace : Optional[dict[str, str]], default: None
        Mapping of configuration keys to rename. Keys present in ``cfg`` are
        copied to their new names and removed from their old names.
    apply_ds : bool, default: False
        Whether downsampling should be applied when calculating
        sample-dependent configuration values.

    Returns
    -------
    cfg : argparse.Namespace
        Configuration converted to an attribute-accessible namespace.
    cfg_str : str
        YAML representation of the final configuration.
    """
    # TODO: Make a default config yaml file and only do modifications here

    if replace is None:
        replace = {}

    # General pipeline settings
    cfg["root_dir"] = os.path.expanduser(cfg.get("root_dir", "~"))
    cfg["tel"] = cfg.get("tel", "lat")
    cfg["pointing_type"] = cfg.get("pointing_type", "pointing_model")
    cfg["append"] = cfg.get("append", "")
    cfg["test_append"] = cfg.get("test_append", "")
    cfg["copy_fits_test"] = cfg.get("copy_fits_test", True)
    cfg["single_det"] = cfg.get("single_det", False)
    cfg["ctx_path"] = cfg.get(
        "ctx_path",
        f"/global/cfs/cdirs/sobs/metadata/{cfg['tel']}/contexts/"
        "smurf_detcal_local.yaml",
    )
    cfg["preprocess_cfg"] = cfg.get("preprocess_cfg", None)
    cfg["map_source_list"] = cfg.get("map_source_list", ["mars", "saturn"])
    cfg["fit_source_list"] = cfg.get("fit_source_list", ["mars", "saturn"])
    cfg["start_time"] = cfg.get("start_time", 0)
    if args.lookback is not None:
        cfg["start_time"] = time.time() - 3600 * args.lookback
    cfg["stop_time"] = cfg.get("stop_time", 20000000000)
    if args.lookback is not None:
        cfg["stop_time"] = time.time()
    cfg["nominal_fwhm"] = cfg.get(
        "nominal_fwhm",
        {
            "f030": 7.4,
            "f040": 5.1,
            "f090": 2.0,
            "f150": 1.3,
            "f220": 0.95,
            "f280": 0.83,
        },
    )
    cfg["min_samps"] = cfg.get("min_samps", 1000)
    cfg["min_dets"] = cfg.get("min_dets", 30)

    # Mapmaking
    cfg["extent"] = cfg.get("extent", 600)
    cfg["res"] = cfg.get("res", (10 / 3600.0) * np.pi / 180.0)
    cfg["map_mask_size"] = cfg.get("map_mask_size", 0.1)
    cfg["apply_fscale"] = cfg.get("apply_fscale", True)
    cfg["aperature"] = cfg.get("aperature", 6)
    cfg["buf"] = cfg.get("buf", 30)
    cfg["buf_cropped"] = cfg.get("buf_cropped", 10)
    cfg["smooth_kern"] = cfg.get("smooth_kern", 60)
    cfg["snr_extent"] = cfg.get("snr_extent", 500)
    cfg["extent_highres"] = cfg.get("extent_highres", 3600)
    cfg["pixsize_highres"] = cfg.get("pixsize_highres", 1)
    cfg["search_mask"] = cfg.get(
        "search_mask",
        {"shape": "circle", "xyr": (0, 0, 0.5)},
    )
    cfg["del_map"] = cfg.get("del_map", True)
    cfg["cgiters_single"] = cfg.get("cgiters_single", 30)
    cfg["cgiters_full"] = cfg.get("cgiters_full", 400)
    cfg["mlpass"] = cfg.get("mlpass", 3)
    cfg["comps"] = cfg.get("comps", "TQU")
    cfg["force_zero_cent"] = cfg.get("force_zero_cent", False)
    cfg["n_modes"] = cfg.get("n_modes", 10)
    cfg["relcal_range"] = cfg.get("relcal_range", [0.3, 2])
    cfg["min_det_secs"] = cfg.get("min_det_secs", 600)

    # Pointing fits
    cfg["forced_ws"] = args.forced_ws if args.forced_ws is not None else []
    if cfg.get("try_all", False):
        cfg["forced_ws"] = ["ws0", "ws1", "ws2", "ws."]
    cfg["max_dur"] = cfg.get("max_dur", 2)
    cfg["nominal_path"] = os.path.expanduser(
        cfg.get(
            "nominal_path",
            f"~/data/pointing/{cfg['tel']}/nominal/focal_plane.h5",
        )
    )
    cfg["pointing_mask"] = cfg.get(
        "pointing_mask",
        {"shape": "circle", "xyr": (0, 0, 0.75)},
    )
    cfg["ds"] = cfg.get("ds", 5)
    ds = cfg["ds"] if apply_ds else 1
    cfg["hp_fc"] = cfg.get("hp_fc", 4)
    cfg["lp_fc"] = cfg.get("lp_fc", 30)
    cfg["n_med"] = cfg.get("n_med", 5)
    cfg["n_std"] = cfg.get("n_std", 10)
    cfg["block_size"] = int(cfg.get("block_size", 200) // ds)
    cfg["trim_samps"] = cfg.get("trim_samps", 200) // ds
    cfg["min_samps"] = cfg["min_samps"] / ds
    cfg["min_hits"] = cfg.get("min_hits", 1)
    cfg["high_hits"] = cfg.get("high_hits", 5)
    cfg["max_chisq"] = cfg.get("max_chisq", 2.5)
    cfg["min_R2"] = cfg.get("min_R2", 0.01)
    cfg["svd_modes"] = cfg.get("svd_modes", 10)
    cfg["svd_iters"] = cfg.get("svd_iters", 5)
    cfg["iter_svd_sub"] = cfg.get("iter_svd_sub", False)
    cfg["filter_for_sources"] = cfg.get("filter_for_sources", False)
    cfg["source_flag_exp"] = cfg.get("source_flag_exp", "(svd + blind) * cent")
    cfg["fit_pars"] = cfg.get("fit_pars", {})
    cfg["pad"] = cfg.get("pad", True)
    cfg["src_msk"] = cfg.get("src_msk", True)

    # Beam-fit configuration
    cfg["sym_gauss"] = cfg.get("sym_gauss", True)
    cfg["min_snr"] = cfg.get("min_snr", 5)
    cfg["bessel_beam"] = cfg.get("bessel_beam", True)
    cfg["min_sigma"] = cfg.get("min_sigma", 3)
    cfg["n_bessel"] = cfg.get("n_bessel", 10)
    cfg["n_multipoles"] = cfg.get("n_multipoles", 3)
    cfg["skip_multipoles"] = cfg.get("skip_multipoles", [])
    cfg["bessel_wing_n_sigma"] = cfg.get("bessel_wing_n_sigma", 5)
    cfg["gauss_multipole"] = cfg.get("gauss_multipole", True)
    cfg["corr_primary"] = cfg.get("corr_primary", 280)
    cfg["eps_primary"] = cfg.get("eps_primary", 17)

    # Stacking quality cuts
    cfg["min_stack_snr"] = cfg.get("min_stack_snr", 10)
    cfg["max_pwv"] = cfg.get("max_pwv", 2.5)
    cfg["max_cut_pix_frac"] = cfg.get("max_cut_pix_frac", 0.15)
    cfg["min_irat"] = cfg.get("min_irat", 3)
    cfg["max_cn"] = cfg.get("max_cn", -2.5)
    cfg["corr_ratio_cut"] = cfg.get("corr_ratio_cut", 20)
    cfg["miscenter_thresh"] = cfg.get("miscenter_thresh", 5)

    # Noise and diagnostic configuration
    cfg["n_lmin"] = cfg.get("n_lmin", 2000)
    cfg["n_lmax"] = cfg.get("n_lmax", 60000)
    cfg["log_thresh"] = cfg.get("log_thresh", 1e-3)
    cfg["empir_cov"] = cfg.get("empir_cov", False)
    cfg["lmax"] = cfg.get("lmax", 20000)
    cfg["cov_modes"] = cfg.get("cov_modes", 20)

    # Split and epoch configuration
    cfg["det_split_dir"] = cfg.get("det_split_dir", "")
    cfg["det_splits"] = cfg.get("det_splits", [])

    cfg["split_by"] = cfg.get(
        "split_by",
        [
            "band",
            "tube_slot+band",
            "source+band",
            "source+tube_slot+band",
        ],
    )
    cfg["metasplits"] = cfg.get("metasplits", {})
    cfg["epochs"] = cfg.get("epochs", [(0, 2e10)])

    # Rename for our scope
    for old_name, new_name in replace.items():
        if old_name not in cfg:
            continue
        cfg[new_name] = cfg[old_name]
        del cfg[old_name]

    cfg_str = yaml.dump(cfg)

    return argparse.Namespace(**cfg), cfg_str

setup_paths(root_dir, project, tel, append='')

Create and return the plot and data directories.

Parameters:

Name Type Description Default
root_dir str

Root directory under which the project directories are created.

required
project str

Project name used to construct the directory paths.

required
tel str

Telescope name used to construct the directory paths.

required
append str

Additional path component appended to the project/telescope paths.

''

Returns:

Name Type Description
plot_dir str

Path to the plot directory.

data_dir str

Path to the data directory.

Source code in lat_beams/utils/config.py
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
def setup_paths(
    root_dir: str,
    project: str,
    tel: str,
    append: str = "",
) -> tuple[str, str]:
    """
    Create and return the plot and data directories.

    Parameters
    ----------
    root_dir : str
        Root directory under which the project directories are created.
    project : str
        Project name used to construct the directory paths.
    tel : str
        Telescope name used to construct the directory paths.
    append : str, optional
        Additional path component appended to the project/telescope paths.

    Returns
    -------
    plot_dir : str
        Path to the plot directory.
    data_dir : str
        Path to the data directory.
    """
    plot_dir = os.path.join(root_dir, "plots", project, tel, append)
    data_dir = os.path.join(root_dir, "data", project, tel, append)
    os.makedirs(plot_dir, exist_ok=True)
    os.makedirs(data_dir, exist_ok=True)

    return plot_dir, data_dir