Skip to content

Source

Initialize a Source object.

Parameters:

Name Type Description Default
optBand str

Optical band identifier. Available bands include: - 'U', 'B', 'V', 'V0', 'V1', 'R', 'R2'-'R4' - 'I', 'I1'-'I10' - 'J', 'J2', 'H' - 'Kp', 'Ks', 'K', 'K0', 'K1' - 'L', 'M', 'Na', 'EOS', 'IR1310' Call Source.print_available_bands() to see their central wavelengths, bandwidths, and zero-point fluxes.

required
magnitude float

Apparent magnitude of the star.

required
coordinates list

Sky coordinates [zenith, azimuth] in [arcsec, degrees], by default [0, 0].

[0, 0]
altitude float

Altitude of the source in meters. Defaults to infinity (NGS).

inf
laser_coordinates list

Launch coordinates for a laser source [x, y] in meters.

[0, 0]
Na_profile float

Sodium layer profile [altitudes, values]. Required for LGS.

None
FWHM_spot_up float

FWHM of the LGS spot in arcsec.

None
chromatic_shift list

Shift per atmospheric layer due to chromatic dispersion, in arcsec.

None
logger Logger

Logger instance for logging.

None
Source code in SAOS/Source.py
    def __init__(self,
                 optBand:str,
                 magnitude:float,
                 coordinates:list = [0,0],
                 altitude:float = np.inf, 
                 laser_coordinates:list = [0,0],
                 Na_profile:float = None,
                 FWHM_spot_up:float = None,
                 chromatic_shift:list = None,
                 logger = None):
        """
        Initialize a Source object.

        Parameters
        ----------
        optBand : str
            Optical band identifier. Available bands include:
            - 'U', 'B', 'V', 'V0', 'V1', 'R', 'R2'-'R4'
            - 'I', 'I1'-'I10'
            - 'J', 'J2', 'H'
            - 'Kp', 'Ks', 'K', 'K0', 'K1'
            - 'L', 'M', 'Na', 'EOS', 'IR1310'
            Call Source.print_available_bands() to see their central wavelengths, 
            bandwidths, and zero-point fluxes.
        magnitude : float
            Apparent magnitude of the star.
        coordinates : list, optional
            Sky coordinates [zenith, azimuth] in [arcsec, degrees], by default [0, 0].
        altitude : float, optional
            Altitude of the source in meters. Defaults to infinity (NGS).
        laser_coordinates : list, optional
            Launch coordinates for a laser source [x, y] in meters.
        Na_profile : float, optional
            Sodium layer profile [altitudes, values]. Required for LGS.
        FWHM_spot_up : float, optional
            FWHM of the LGS spot in arcsec.
        chromatic_shift : list, optional
            Shift per atmospheric layer due to chromatic dispersion, in arcsec.
        logger : logging.Logger, optional
            Logger instance for logging.
        """
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% INITIALIZATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
        # Setup the logger to handle the queue of info, warning and errors msgs in the simulator
        if logger is None:
            self.queue_listerner = self.setup_logging()
            self.logger = logging.getLogger()
            self.external_logger_flag = False
        else:
            self.external_logger_flag = True
            self.logger = logger

        self.is_initialized = False
        tmp             = self.photometry(optBand)              # get the photometry properties
        self.optBand    = optBand                               # optical band
        self.wavelength = tmp[0]                                # wavelength in m
        self.bandwidth  = tmp[1]                                # optical bandwidth
        self.zeroPoint  = tmp[2]/368                            # zero point
        self.magnitude  = magnitude                             # magnitude
        self.phase      = []                                    # phase of the source 
        self.phase_no_pupil      = []                           # phase of the source (no pupil)
        self.fluxMap    = []                                    # 2D flux map of the source
        self.nPhoton    = self.zeroPoint*10**(-0.4*magnitude)   # number of photon per m2 per s
        self.tag        = 'source'                              # tag of the object
        self.altitude = altitude                                # altitude of the source object in m    
        self.coordinates = coordinates                          # polar coordinates [r,theta] 
        self.laser_coordinates = laser_coordinates              # Laser Launch Telescope coordinates in [m] 
        self.chromatic_shift = chromatic_shift                             # shift in arcsec to be applied to the atmospheric phase screens (one value for each layer) to simulate a chromatic effect
        if Na_profile is not None and FWHM_spot_up is not None:
            self.Na_profile = Na_profile
            self.FWHM_spot_up = FWHM_spot_up

            # consider the altitude weigthed by Na profile
            self.altitude = np.sum(Na_profile[0,:]*Na_profile[1,:])
            self.type     = 'LGS'
        else:

            self.type     = 'NGS'

        self.is_initialized = True   

PHOTOMETRY_BANDS class-attribute instance-attribute

PHOTOMETRY_BANDS = {
    "U": [3.6e-07, 7e-08, 1960000000000.0],
    "B": [4.4e-07, 1e-07, 5380000000000.0],
    "V0": [5e-07, 9e-08, 3640000000000.0],
    "V1": [5.25e-07, 9e-08, 3310000000000.0],
    "V": [5.5e-07, 9e-08, 3310000000000.0],
    "R": [6.4e-07, 1.5e-07, 4010000000000.0],
    "R2": [6.5e-07, 3e-07, 7900000000000.0],
    "R3": [6e-07, 3e-07, 8560000000000.0],
    "R4": [6.8e-07, 3e-07, 7660000000000.0],
    "I": [7.9e-07, 1.5e-07, 2690000000000.0],
    "I1": [7e-07, 3.3e-08, 670000000000.0],
    "I2": [7.5e-07, 3.3e-08, 620000000000.0],
    "I3": [8e-07, 3.3e-08, 580000000000.0],
    "I4": [7e-07, 1e-07, 2020000000000.0],
    "I5": [8.5e-07, 1e-07, 1670000000000.0],
    "I6": [1e-06, 1e-07, 1420000000000.0],
    "I7": [8.5e-07, 3e-07, 5000000000000.0],
    "I8": [7.5e-07, 1e-07, 1890000000000.0],
    "I9": [8.5e-07, 3e-07, 5000000000000.0],
    "I10": [9e-07, 3e-07, 4720000000000.0],
    "J": [1.215e-06, 2.6e-07, 1900000000000.0],
    "J2": [1.55e-06, 2.6e-07, 1490000000000.0],
    "H": [1.654e-06, 2.9e-07, 1050000000000.0],
    "Kp": [2.1245e-06, 3.51e-07, 620000000000.0],
    "Ks": [2.157e-06, 3.2e-07, 550000000000.0],
    "K": [2.179e-06, 4.1e-07, 700000000000.0],
    "K0": [2e-06, 4.1e-07, 760000000000.0],
    "K1": [2.4e-06, 4.1e-07, 640000000000.0],
    "L": [3.547e-06, 5.7e-07, 250000000000.0],
    "M": [4.769e-06, 4.5e-07, 84000000000.0],
    "Na": [5.89e-07, 0, 3300000000000.0],
    "EOS": [1.064e-06, 0, 3300000000000.0],
    "IR1310": [1.31e-06, 0, 2000000000000.0],
}

queue_listerner instance-attribute

queue_listerner = setup_logging()

logger instance-attribute

logger = getLogger()

external_logger_flag instance-attribute

external_logger_flag = False

optBand instance-attribute

optBand = optBand

wavelength instance-attribute

wavelength = tmp[0]

bandwidth instance-attribute

bandwidth = tmp[1]

zeroPoint instance-attribute

zeroPoint = tmp[2] / 368

magnitude instance-attribute

magnitude = magnitude

phase instance-attribute

phase = []

phase_no_pupil instance-attribute

phase_no_pupil = []

fluxMap instance-attribute

fluxMap = []

nPhoton instance-attribute

nPhoton = zeroPoint * 10 ** (-0.4 * magnitude)

tag instance-attribute

tag = 'source'

altitude instance-attribute

altitude = altitude

coordinates instance-attribute

coordinates = coordinates

laser_coordinates instance-attribute

laser_coordinates = laser_coordinates

chromatic_shift instance-attribute

chromatic_shift = chromatic_shift

Na_profile instance-attribute

Na_profile = Na_profile

FWHM_spot_up instance-attribute

FWHM_spot_up = FWHM_spot_up

type instance-attribute

type = 'LGS'

is_initialized instance-attribute

is_initialized = True

get_available_bands classmethod

get_available_bands()

Returns a list of available optical bands.

Returns:

Type Description
list of str

Available optical bands.

Source code in SAOS/Source.py
@classmethod
def get_available_bands(cls):
    """
    Returns a list of available optical bands.

    Returns
    -------
    list of str
        Available optical bands.
    """
    return list(cls.PHOTOMETRY_BANDS.keys())

print_available_bands classmethod

print_available_bands()

Prints the available optical bands and their properties.

Source code in SAOS/Source.py
@classmethod
def print_available_bands(cls):
    """
    Prints the available optical bands and their properties.
    """
    print(f"{'Band':<8} | {'Wavelength (m)':<15} | {'Bandwidth (m)':<15} | {'Zero Point Flux':<15}")
    print("-" * 65)
    for band, props in cls.PHOTOMETRY_BANDS.items():
        print(f"{band:<8} | {props[0]:<15.3e} | {props[1]:<15.3e} | {props[2]:<15.3e}")

photometry

photometry(arg)

Returns photometric properties of the selected band.

Parameters:

Name Type Description Default
arg str

Name of the photometric band (e.g., 'V', 'H', 'Na').

required

Returns:

Type Description
list or int

List of [wavelength, bandwidth, zero-point flux] or -1 if invalid.

Source code in SAOS/Source.py
def photometry(self,arg):
    """
    Returns photometric properties of the selected band.

    Parameters
    ----------
    arg : str
        Name of the photometric band (e.g., 'V', 'H', 'Na').

    Returns
    -------
    list or int
        List of [wavelength, bandwidth, zero-point flux] or -1 if invalid.
    """
    self.logger.debug('Source::photometry')

    if isinstance(arg,str):
        if arg in self.PHOTOMETRY_BANDS:
            return self.PHOTOMETRY_BANDS[arg]
        else:
            self.logger.error(f'Source::photometry - Wrong name for the photometry object. Available bands: {self.get_available_bands()}')
            raise ValueError(f'Wrong name for the photometry object. Available bands: {self.get_available_bands()}')
    else:
        self.logger.error('Source::photometry - The photometry object takes a string as an input.')
        raise ValueError('The photometry object takes a string as an input.')   

print_properties

print_properties()

Print the main properties of the source.

Returns:

Type Description
None
Source code in SAOS/Source.py
def print_properties(self):
    """
    Print the main properties of the source.

    Returns
    -------
    None
    """
    self.logger.info('Source::print_properties')
    self.logger.info('{: ^8s}'.format('Source') +'{: ^10s}'.format('Wavelength')+ '{: ^8s}'.format('Zenith')+ '{: ^10s}'.format('Azimuth')+ '{: ^10s}'.format('Altitude')+ '{: ^10s}'.format('Magnitude') + '{: ^10s}'.format('Flux'))
    self.logger.info('{: ^8s}'.format('') +'{: ^10s}'.format('[m]')+ '{: ^8s}'.format('[arcsec]')+ '{: ^10s}'.format('[deg]')+ '{: ^10s}'.format('[m]')+ '{: ^10s}'.format('') + '{: ^10s}'.format('[phot/m2/s]') )

    self.logger.info('-------------------------------------------------------------------')        
    self.logger.info('{: ^8s}'.format(self.type) +'{: ^10s}'.format(str(self.wavelength))+ '{: ^8s}'.format(str(self.coordinates[0]))+ '{: ^10s}'.format(str(self.coordinates[1]))+'{: ^10s}'.format(str(np.round(self.altitude,2)))+ '{: ^10s}'.format(str(self.magnitude))+'{: ^10s}'.format(str(np.round(self.nPhoton,1))) )

setup_logging

setup_logging(logging_level=logging.WARNING)
Source code in SAOS/Source.py
def setup_logging(self, logging_level=logging.WARNING):
    #
    #  Setup of logging at the main process using QueueHandler
    log_queue = Queue()
    queue_handler = logging.handlers.QueueHandler(log_queue)
    root_logger = logging.getLogger()
    root_logger.setLevel(logging_level)  # Minimum log level

    # Setup of the formatting
    formatter = logging.Formatter(
        "%(asctime)s - %(levelname)s - %(message)s"
    )

    # Output to terminal
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)

    # Qeue handler captures the messages from the different logs and serialize them
    queue_listener = logging.handlers.QueueListener(log_queue, console_handler)
    root_logger.addHandler(queue_handler)
    queue_listener.start()

    return queue_listener

__del__

__del__()
Source code in SAOS/Source.py
def __del__(self):
    if not self.external_logger_flag:
        self.queue_listerner.stop()