Source code for galfitools.galout.split_components
#!/usr/bin/env python3
"""Split a GALFIT file into one file per non-sky component."""
from __future__ import annotations
import argparse
import re
from pathlib import Path
COMPONENT_RE = re.compile(
r"^\s*#\s*Component\s+number\s*:\s*(\d+)\s*$",
re.IGNORECASE,
)
TYPE_RE = re.compile(
r"^\s*0\)\s*(\S+)",
re.IGNORECASE,
)
FINAL_SEPARATOR_RE = re.compile(r"^\s*=+\s*$")
[docs]
def split_galfit_file(
galfit_file: Path,
output_dir: Path | None = None,
) -> list[Path]:
"""Split a GALFIT file into several files, one per non-sky component.
Parameters
----------
galfit_file : pathlib.Path
Input GALFIT file.
output_dir : pathlib.Path or None, optional
Directory where the output files are written. If `None`, the files are
written in the same directory as `galfit_file`.
Returns
-------
list of pathlib.Path
Paths of the generated files.
Notes
-----
The script keeps the original GALFIT header, defined as all lines before
the first ``# Component number:`` block. The sky component is ignored.
"""
galfit_file = galfit_file.expanduser().resolve()
if output_dir is None:
output_dir = galfit_file.parent
else:
output_dir = output_dir.expanduser().resolve()
output_dir.mkdir(parents=True, exist_ok=True)
lines = galfit_file.read_text().splitlines(keepends=True)
component_starts: list[int] = []
component_numbers: list[int] = []
for i, line in enumerate(lines):
match = COMPONENT_RE.match(line)
if match:
component_starts.append(i)
component_numbers.append(int(match.group(1)))
if not component_starts:
raise ValueError(f"No GALFIT component blocks were found in {galfit_file}")
header = lines[: component_starts[0]]
generated_files: list[Path] = []
base_name = galfit_file.stem
extension = galfit_file.suffix
for idx, start in enumerate(component_starts):
end = (
component_starts[idx + 1] if idx + 1 < len(component_starts) else len(lines)
)
block = lines[start:end]
# Remove empty lines and the final GALFIT separator from the last block.
while block and (
block[-1].strip() == "" or FINAL_SEPARATOR_RE.match(block[-1])
):
block = block[:-1]
component_type = get_component_type(block)
if component_type.lower() == "sky":
continue
component_number = component_numbers[idx]
output_name = f"{base_name}-{component_type}-nc{component_number}{extension}"
output_path = output_dir / output_name
output_lines = []
output_lines.extend(header)
output_lines.extend(block)
if output_lines and not output_lines[-1].endswith("\n"):
output_lines[-1] += "\n"
output_lines.append("\n")
output_lines.append("=" * 80 + "\n")
output_path.write_text("".join(output_lines))
generated_files.append(output_path)
return generated_files
[docs]
def get_component_type(component_block: list[str]) -> str:
"""Return the GALFIT function name for a component block."""
for line in component_block:
match = TYPE_RE.match(line)
if match:
return match.group(1).strip()
raise ValueError("A component block does not contain a valid '0)' type line")
[docs]
def mainSplitComp() -> None:
parser = argparse.ArgumentParser(
description="Split a GALFIT file into one file per non-sky component."
)
parser.add_argument(
"galfit_file",
help="Input GALFIT file, for example galfit.02",
)
parser.add_argument(
"-o",
"--output-dir",
default=None,
help="Output directory. Default: same directory as the input file.",
)
args = parser.parse_args()
output_files = split_galfit_file(
Path(args.galfit_file),
Path(args.output_dir) if args.output_dir is not None else None,
)
for output_file in output_files:
print(output_file)
if __name__ == "__main__":
mainSplitComp()