Source code for simtel.segmentation

"""sim_telarray segmentation serializer and historical-file adapter."""

from pathlib import Path

from simtools.data_model.mirror_segmentation import (
    kind_required_fields,
    make_quantity,
    quantity_value,
    validate_segments,
)


def _parse_ring(fields, line, kind, count):
    if len(fields) not in (3, 4, 5):
        raise ValueError(f"Invalid ring segmentation line: {line}")
    return {
        "kind": kind,
        "count": count,
        "r_min": make_quantity(float(fields[0]), "cm"),
        "r_max": make_quantity(float(fields[1]), "cm"),
        "dphi": make_quantity(float(fields[2]), "deg"),
        "phi0": make_quantity(float(fields[3]) if len(fields) > 3 else 0.0, "deg"),
        "gap": make_quantity(float(fields[4]) if len(fields) > 4 else 0.0, "cm"),
    }


def _parse_shape(fields, line, kind, count):
    if len(fields) not in (3, 4):
        raise ValueError(f"Invalid shape segmentation line: {line}")
    return {
        "kind": kind,
        "count": count,
        "x": make_quantity(float(fields[0]), "cm"),
        "y": make_quantity(float(fields[1]), "cm"),
        "diameter": make_quantity(float(fields[2]), "cm"),
        "rotation": make_quantity(float(fields[3]) if len(fields) == 4 else 0.0, "deg"),
    }


def _parse_polygon(fields, line, kind, count):
    if len(fields) < 7 or len(fields[1:]) % 2:
        raise ValueError(f"Invalid polygon segmentation line: {line}")
    return {
        "kind": kind,
        "count": count,
        "rotation": make_quantity(float(fields[0]), "deg"),
        "vertices": [
            {"x": make_quantity(float(x), "cm"), "y": make_quantity(float(y), "cm")}
            for x, y in zip(fields[1::2], fields[2::2], strict=False)
        ],
    }


def _parse_line(line, parameter_name, schema_version):
    fields = line.replace(",", " ").split()
    kind = fields.pop(0).lower()
    count = int(fields.pop(0))
    required = kind_required_fields(parameter_name, schema_version).get(kind)
    if required is None:
        raise ValueError(f"Unknown mirror segmentation kind: {kind}")
    if "r_min" in required:
        return _parse_ring(fields, line, kind, count)
    if "vertices" in required:
        return _parse_polygon(fields, line, kind, count)
    return _parse_shape(fields, line, kind, count)


[docs] def write_mirror_segmentation(records, output_path, parameter_name, schema_version): """Serialize validated segmentation records in sim_telarray syntax.""" validate_segments(records, parameter_name, schema_version) output_path = Path(output_path) if ".." in output_path.parts: raise ValueError(f"Unsafe mirror segmentation output path: {output_path}") output_path.parent.mkdir(parents=True, exist_ok=True) lines = [] for record in records: required = kind_required_fields(parameter_name, schema_version)[record["kind"]] if "r_min" in required: lines.append( f"RING {record['count']} {quantity_value(record, 'r_min', 'cm')} " f"{quantity_value(record, 'r_max', 'cm')} " f"{quantity_value(record, 'dphi', 'deg')} " f"{quantity_value(record, 'phi0', 'deg', 0)} " f"{quantity_value(record, 'gap', 'cm', 0)}" ) elif "vertices" not in required: lines.append( f"{record['kind'].upper()} 1 {quantity_value(record, 'x', 'cm')} " f"{quantity_value(record, 'y', 'cm')} {quantity_value(record, 'diameter', 'cm')} " f"{quantity_value(record, 'rotation', 'deg', 0)}" ) else: vertices = " ".join( f"{quantity_value(vertex, 'x', 'cm')} {quantity_value(vertex, 'y', 'cm')}" for vertex in record["vertices"] ) lines.append(f"POLYGON 1 {quantity_value(record, 'rotation', 'deg', 0)} {vertices}") output_path.write_text("\n".join(lines) + "\n", encoding="utf-8") return output_path.name