Source code for q3dfit.q3dutil

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Utility functions that are used by multiple modules in the q3dfit package.
"""
from __future__ import annotations

from typing import Optional
import numpy as np
from q3dfit.exceptions import InitializationError


[docs] def get_q3dio(inobj: str | object) -> object: ''' Load initialization or output object. Determine whether it's already an object, or needs to be loaded from file. Parameters ---------- inobj Input object. If a string, assume it's an input .npy file. If an ndarray, assume the file's been loaded but not stripped to dict{}. If an object, assume all is well. Raises ------ InitializationError Returns ------- object :py:class:`~q3dfit.q3din.q3din` or :py:class:`~q3dfit.q3dout.q3dout` object. ''' # If it's a string, assume it's an input .npy file if type(inobj) == str: q3dioarr = np.load(inobj, allow_pickle=True) # type: ignore q3dio = q3dioarr[()] # If it's an ndarray, assume the file's been loaded but not stripped # to dict{} elif isinstance(inobj, np.ndarray): q3dio = inobj[()] # If it's an object, assume all is well elif isinstance(inobj, object): q3dio = inobj else: raise InitializationError('q3di/o not in expected format') return q3dio
[docs] def get_spaxels(ncols: int, nrows: int, cols: Optional[int | list | np.ndarray]=None, rows: Optional[int | list | np.ndarray]=None) -> \ tuple[int, np.ndarray[int], np.ndarray[int]]: ''' Set up 1D arrays specifying column value and row value for each spaxel to be fitted. These are zero-offset for indexing other arrays. Parameters ---------- ncols Total number of columns in the data cube. nrows Total number of rows in the data cube. cols Optional. Column values for spaxels to be fitted. Default is None, which means all columns will be fitted. If a scalar, only that column will be fitted. If a 2-element list or array, the elements are the starting and ending columns to be fitted. Unity-offset values assumed. rows Optional. Row values for spaxels to be fitted. Default is None, which means all rows will be fitted. If a scalar, only that row will be fitted. If a 2-element list or array, the elements are the starting and ending rows to be fitted. Unity-offset values assumed. Returns ------- int Number of spaxels to be fitted. np.ndarray[int] 1D array of column values for spaxels to be fitted. np.ndarray[int] 1D array of row values for spaxels to be fitted. ''' # Set up 2-element arrays with starting and ending columns/rows # These are unity-offset to reflect pixel labels if not cols: cols = [1, ncols] ncols = ncols # case: cols is a scalar elif not isinstance(cols, (list, np.ndarray)): cols = [cols, cols] ncols = 1 elif len(cols) == 1: cols = [cols[0], cols[0]] ncols = 1 else: ncols = cols[1]-cols[0]+1 if not rows: rows = [1, nrows] nrows = nrows elif not isinstance(rows, (list, np.ndarray)): rows = [rows, rows] nrows = 1 elif len(rows) == 1: rows = [rows[0], rows[0]] nrows = 1 else: nrows = rows[1]-rows[0]+1 if len(cols)<=2 or len(rows) <= 2: colarr = np.empty((ncols, nrows), dtype=np.int32) rowarr = np.empty((ncols, nrows), dtype=np.int32) for i in range(nrows): colarr[:, i] = np.arange(cols[0]-1, cols[1], dtype=np.int32) for i in range(ncols): rowarr[i, :] = np.arange(rows[0]-1, rows[1], dtype=np.int32) # Flatten from 2D to 1D arrays to preserve indexing using only ispax # currently not needed. fitloop expects 2D lists. colarr = colarr.flatten() rowarr = rowarr.flatten() nspax = ncols * nrows if len(cols) > 2 or len(rows) > 2: colarr = cols rowarr = rows nspax = len(cols) return nspax, colarr, rowarr
''' def get_voronoi(cols, rows, vormap): construct voronoi map Parameters ---------- cols : TYPE DESCRIPTION. rows : TYPE DESCRIPTION. vormap : TYPE DESCRIPTION. Raises ------ ValueError DESCRIPTION. Returns ------- cols : TYPE DESCRIPTION. if len(cols) == 1 and len(rows) == 1: cols = vormap[cols[0]-1, rows[0]-1] return cols else: raise ValueError('Q3DF: ERROR: Can only specify 1 spaxel, \ or all spaxels, in Voronoi mode.') '''
[docs] def write_msg(message: str, file: Optional[str]=None, quiet: bool=True ): ''' Write message to log file and/or :py:func:`~sys.stdout`. Parameters ---------- message Message to be written logfile Optional. Filename for progress messages. Default is None, which means no message is written to a file. quiet Optional. Suppress progress messages to stdout. Default is True. ''' if file is not None: with open(file, 'a') as log: log.write(message) if not quiet: print(message)
[docs] class lmlabel(): """ Remove characters from a label string that are incompatible with LMFIT's parser; or reverse the operation. All keys of a Parameters() instance must be strings and valid Python symbol names. This class replaces incompatible characters with valid symbols. https://lmfit.github.io/lmfit-py/parameters.html#lmfit.parameter.Parameters Parameters ---------- label Input label string. reverse Optional. If set, reverse the operation. Default is False. Attributes ---------- label Original label string. lmlabel Label string with incompatible characters replaced by valid symbols. """ def __init__(self, label: str, reverse: bool=False): if reverse: lmlabel = label origlabel = label.replace('lb', '[').replace('rb', ']').\ replace('pt', '.') else: origlabel = label lmlabel = label.replace('[', 'lb').replace(']', 'rb').\ replace('.', 'pt') self.label = origlabel self.lmlabel = lmlabel
# class lmpar(): # # def __init__(self, lmlabel, comp, partype): # self.parname = f'{lmlabel}_c{comp}_g{partype}'