Source code for pyradtran.models.output

"""Output configuration model.

Maps to uvspec keywords: output_user, output_quantity, output_process,
output_format, quiet, verbose, zout, output_file.

Reference: libRadtran src_py/output_options.py
"""

from __future__ import annotations

from pydantic import Field, model_validator

from pyradtran.models.base import UvspecOption

VALID_OUTPUT_FORMATS = frozenset({"ascii", "flexstor", "netcdf", "sat_picture"})
VALID_HEATING_RATE_MODES = frozenset({"none", "local", "layer_fd", "layer_cd", "ipa3d"})
VALID_OUTPUT_PROCESSES = frozenset({
    "integrate", "sum", "rgbraw", "rgb_raw", "rgb",
    "per_nm", "per_cm-1", "per_band",
})


[docs] class OutputConfig(UvspecOption): """Output format and content configuration. Attributes: quantities: Output column quantities (e.g. ["lambda", "edir", "edn", "eup"]). quantity: Output quantity type (transmittance, reflectivity, brightness). process: Output processing (e.g. 'integrate', 'per_nm', 'sum'). format: Output file format -- "netcdf" (default), "ascii", "flexstor". quiet: Suppress uvspec stdout messages. Default: True. verbose: Enable verbose uvspec output. zout: Output altitudes in km above ground level. Supports float values and special strings like "toa" and "boa". output_file: Path for output file (overrides auto-generated name). heating_rate: Heating rate calculation mode (e.g. 'local', 'layer_fd'). write_optical_properties: Write optical properties to output file. """ quantities: list[str] = Field(default_factory=list) quantity: str | None = None process: str | None = None format: str = Field(default="netcdf") quiet: bool = True verbose: bool = False zout: list[float | str] = Field(default_factory=list) output_file: str | None = None heating_rate: str | None = None write_optical_properties: bool = False
[docs] @model_validator(mode="after") def validate_output(self) -> OutputConfig: if self.format not in VALID_OUTPUT_FORMATS: raise ValueError( f"Unknown output format '{self.format}'. Valid: {sorted(VALID_OUTPUT_FORMATS)}" ) if self.quiet and self.verbose: raise ValueError("Cannot set both quiet=True and verbose=True") if ( self.heating_rate is not None and self.heating_rate not in VALID_HEATING_RATE_MODES ): raise ValueError( f"Unknown heating_rate " f"'{self.heating_rate}'. Valid: {sorted(VALID_HEATING_RATE_MODES)}" ) if self.process is not None and self.process not in VALID_OUTPUT_PROCESSES: raise ValueError( f"Unknown output_process '{self.process}'. Valid: {sorted(VALID_OUTPUT_PROCESSES)}" ) return self
[docs] def to_uvspec_lines(self) -> list[str]: lines: list[str] = [] if self.quantities: lines.append(f"output_user {' '.join(self.quantities)}") if self.quantity is not None: lines.append(f"output_quantity {self.quantity}") if self.process is not None: lines.append(f"output_process {self.process}") lines.append(f"output_format {self.format}") if self.quiet: lines.append("quiet") elif self.verbose: lines.append("verbose") if self.heating_rate is not None: lines.append(f"heating_rate {self.heating_rate}") if self.zout: zout_str = " ".join(str(z) for z in self.zout) lines.append(f"zout {zout_str}") if self.output_file is not None: lines.append(f"output_file {self.output_file}") if self.write_optical_properties: lines.append("write_optical_properties") return lines
[docs] def to_uvspec_items(self) -> list[tuple[int, str]]: phase = 9 return [(phase, line) for line in self.to_uvspec_lines()]