<commit_before>"""Module containing the code pertaining to cards"""
import random, copy
from collections import namedtuple

class Card:
    """Contains card instance data, type class and comparison functionality"""

    class Type:
        """An enumeration over card types."""
        normal = 0
        repulsor = 1
        tractor = 2

    def __init__(self, cardtype, value, name, longname):
        """Instance variables are to be considered constant."""
        self._cardtype = cardtype
        self._value = value
        self._name = name
        self._longname = longname

    def getType(self):
        return self._cardtype

    def getValue(self):
        return self._value

    def getName(self):
        return self._name

    def getLongName(self):
        return self._longname

    def __str__(self):
        """ Return string describing card"""
        return self.getName()

    def __repr__(self):
        """Return unambiguos card represenation. Whatever that is."""
        return self.__str__()

class Deck:
    """Singleton handling the creation of a deck, shuffling and laying out the
    card field"""

    def __init__(self):
        """Creates a full deck of cards, sorted, as a list"""

        # field stack tuple
        self.makeFST = namedtuple('FieldStackTuple', 'index card')

        # Master deck configuration list
        DECKCONF = [
            ("Argon", "Ar", 1, Card.Type.normal),
            ("Boron", "B", 2, Card.Type.normal),
            ("Carbon", "C", 3, Card.Type.normal),
            ("Dysprosium", "Dy", 5, Card.Type.normal),
            ("Einsteinium", "Es", 2, Card.Type.normal),
            ("Flourine", "F", 6, Card.Type.normal),
            ("Gallium", "Ga", 5, Card.Type.normal),
            ("Hydrogen", "H", 4, Card.Type.normal),
            ("Iridium", "Ir", 6, Card.Type.normal),
            ("Jodium", "Jo", 2, Card.Type.tractor),
            ("Krypton", "Kr", 2, Card.Type.repulsor),
            ("Lithium", "Li", 4, Card.Type.normal),
            ("Magnesium", "Mg", 10, Card.Type.normal),
            ("Neon", "Ne", 6, Card.Type.repulsor),
            ("Oxygen", "O", 7, Card.Type.normal),
            ("Plutonium", "Pu", 5, Card.Type.normal),
            ("Sydnium", "Qt", 3, Card.Type.tractor),
            ("Radium", "Ra", 9, Card.Type.normal),
            ("Silicon", "Si", 9, Card.Type.normal),
            ("Thorium", "Th", 2, Card.Type.normal),
            ("Uranium", "U", 5, Card.Type.repulsor),
            ("Vanadium", "V", 7, Card.Type.normal),
            ("Tungsten", "W", 8, Card.Type.normal),
            ("Xenon", "Xe", 3, Card.Type.repulsor),
            ("Yttrium", "Y", 8, Card.Type.normal),
            ("Zirconium", "Zr", 7, Card.Type.normal)
        ]

        # Business end of function; puts actual cards in deck
        self._deck = [None]*len(DECKCONF)
        for i in range(len(DECKCONF)):
            conf = DECKCONF[i]
            self._deck[i] = Card(conf[3], conf[2], conf[1], conf[0])

        # field is empty for now
        self._field = []

    def createCardField(self,playerAmount):
        """make a resource field with size of 3*playerAmount
        Returns cards in sets of 2; the second is the hidden one.

        (Does not remove the cards from self.deck, only returns the resulting field.
        This way we don't need to keep rebuilding the cards in the deck)
        """
        # draw the right amount of (unique) cards
        # 2x, because of the double cards
        cardDraws = random.sample(self._deck,2*3*playerAmount)

        # put them in sets of 2
        self._field = [None]*(3*playerAmount)
        for i in range(0,3*playerAmount):
            # each field entry contains two cards: assume the second to be the
            # hidden one
            self._field[i] = ( cardDraws[i*2] , cardDraws[i*2+1] )
        return self._field

    def percieveCardField(self):
        """Players should only see the top cards in the resource field"""
        perception = []
        i = 0
        for f in self._field:
            # return for each card set (that has not been taken yet) the index
            # and the shown card
            if f is not None:
                perception.append( self.makeFST( i, f[0]) )
            i += 1
        return perception

    def getField(self):
        return self._field

    def takeFromField(self, cardIndex):
        """Removes selected cardSet of the resource field"""
        cardSet = self._field[cardIndex]
        # make item on field None to show it has been taken. None instead of
        # removing item, because the field needs to be drawn on screen
        self._field[cardIndex] = None
        return cardSet

    def printCardList(cardList):
        for c in cardList:
            print(c)

    def printCardField(cardField):
        for c in cardField:
            if c is not None:
                print( "[ shown: "+str(c[0])+" , hidden: " +str(c[1])+" ]")
            else:
                print("[ Empty spot ]")

    def sortByResolution(cardList):
        """Sorts a given list by resolution order (alphabetic).
        Returns sorted list."""
        return sorted(cardList, key=lambda c: c.getName())
<commit_msg>Re-added function that was lost<commit_after>"""Module containing the code pertaining to cards"""
import random, copy
from collections import namedtuple

class Card:
    """Contains card instance data, type class and comparison functionality"""

    class Type:
        """An enumeration over card types."""
        normal = 0
        repulsor = 1
        tractor = 2

    def __init__(self, cardtype, value, name, longname):
        """Instance variables are to be considered constant."""
        self._cardtype = cardtype
        self._value = value
        self._name = name
        self._longname = longname

    def getType(self):
        return self._cardtype

    def getValue(self):
        return self._value

    def getName(self):
        return self._name

    def getLongName(self):
        return self._longname

    def __str__(self):
        """ Return string describing card"""
        return self.getName()

    def __repr__(self):
        """Return unambiguos card represenation. Whatever that is."""
        return self.__str__()

class Deck:
    """Singleton handling the creation of a deck, shuffling and laying out the
    card field"""

    def __init__(self):
        """Creates a full deck of cards, sorted, as a list"""

        # field stack tuple
        self.makeFST = namedtuple('FieldStackTuple', 'index card')

        # Master deck configuration list
        DECKCONF = [
            ("Argon", "Ar", 1, Card.Type.normal),
            ("Boron", "B", 2, Card.Type.normal),
            ("Carbon", "C", 3, Card.Type.normal),
            ("Dysprosium", "Dy", 5, Card.Type.normal),
            ("Einsteinium", "Es", 2, Card.Type.normal),
            ("Flourine", "F", 6, Card.Type.normal),
            ("Gallium", "Ga", 5, Card.Type.normal),
            ("Hydrogen", "H", 4, Card.Type.normal),
            ("Iridium", "Ir", 6, Card.Type.normal),
            ("Jodium", "Jo", 2, Card.Type.tractor),
            ("Krypton", "Kr", 2, Card.Type.repulsor),
            ("Lithium", "Li", 4, Card.Type.normal),
            ("Magnesium", "Mg", 10, Card.Type.normal),
            ("Neon", "Ne", 6, Card.Type.repulsor),
            ("Oxygen", "O", 7, Card.Type.normal),
            ("Plutonium", "Pu", 5, Card.Type.normal),
            ("Sydnium", "Qt", 3, Card.Type.tractor),
            ("Radium", "Ra", 9, Card.Type.normal),
            ("Silicon", "Si", 9, Card.Type.normal),
            ("Thorium", "Th", 2, Card.Type.normal),
            ("Uranium", "U", 5, Card.Type.repulsor),
            ("Vanadium", "V", 7, Card.Type.normal),
            ("Tungsten", "W", 8, Card.Type.normal),
            ("Xenon", "Xe", 3, Card.Type.repulsor),
            ("Yttrium", "Y", 8, Card.Type.normal),
            ("Zirconium", "Zr", 7, Card.Type.normal)
        ]

        # Business end of function; puts actual cards in deck
        self._deck = [None]*len(DECKCONF)
        for i in range(len(DECKCONF)):
            conf = DECKCONF[i]
            self._deck[i] = Card(conf[3], conf[2], conf[1], conf[0])

        # field is empty for now
        self._field = []

    def createCardField(self,playerAmount):
        """make a resource field with size of 3*playerAmount
        Returns cards in sets of 2; the second is the hidden one.

        (Does not remove the cards from self.deck, only returns the resulting field.
        This way we don't need to keep rebuilding the cards in the deck)
        """
        # draw the right amount of (unique) cards
        # 2x, because of the double cards
        cardDraws = random.sample(self._deck,2*3*playerAmount)

        # put them in sets of 2
        self._field = [None]*(3*playerAmount)
        for i in range(0,3*playerAmount):
            # each field entry contains two cards: assume the second to be the
            # hidden one
            self._field[i] = ( cardDraws[i*2] , cardDraws[i*2+1] )
        return self._field

    def percieveCardField(self):
        """Players should only see the top cards in the resource field"""
        perception = []
        i = 0
        for f in self._field:
            # return for each card set (that has not been taken yet) the index
            # and the shown card
            if f is not None:
                perception.append( self.makeFST( i, f[0]) )
            i += 1
        return perception

    def getField(self):
        return self._field

    def takeFromField(self, cardIndex):
        """Removes selected cardSet of the resource field"""
        cardSet = self._field[cardIndex]
        # make item on field None to show it has been taken. None instead of
        # removing item, because the field needs to be drawn on screen
        self._field[cardIndex] = None
        return cardSet

    def printCardList(cardList):
        for c in cardList:
            print(c)

    def printCardField(cardField):
        for c in cardField:
            if c is not None:
                print( "[ shown: "+str(c[0])+" , hidden: " +str(c[1])+" ]")
            else:
                print("[ Empty spot ]")

    def sortByResolution(cardList):
        """Sorts a given list by resolution order (alphabetic).
        Returns sorted list."""
        return sorted(cardList, key=lambda c: c.getName())

    def updateCopyFields(self, clone):
        """Updates the field data of a clone so that it matches the
        authoritative copy."""
        clone._field = copy.copy(self._field)
<|endoftext|>


<commit_before>#   This Python module is part of the PyRate software package.
#
#   Copyright 2020 Geoscience Australia
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
"""
This Python module does post-processing steps to assemble the
stack rate and time series outputs and save as geotiff files
"""
from os.path import join, isfile
import pickle
import numpy as np
from osgeo import gdal
import subprocess
from pathlib import Path

from pyrate.core import shared, stack, ifgconstants as ifc, mpiops, config as cf
from pyrate.constants import REF_COLOR_MAP_PATH
from pyrate.core.logger import pyratelogger as log

gdal.SetCacheMax(64)

def main(params):
    """
    PyRate merge main function. Assembles product tiles in to
    single geotiff files
    """
    # setup paths
    rows, cols = params["rows"], params["cols"]
    mpiops.run_once(_merge_stack, rows, cols, params)
    mpiops.run_once(create_png_from_tif, params[cf.OUT_DIR])

    if params[cf.TIME_SERIES_CAL]:
        _merge_timeseries(rows, cols, params)
        #mpiops.run_once(_delete_tsincr_files, params)

def _merge_stack(rows, cols, params):
    """
    Merge stacking outputs
    """
    shape, tiles, ifgs_dict = _merge_setup(rows, cols, params)

    # read and assemble tile outputs
    rate = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_rate')
    error = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_error')
    samples = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_samples')

    # mask pixels according to threshold
    if params[cf.LR_MAXSIG] > 0:
        rate, error = stack.mask_rate(rate, error, params[cf.LR_MAXSIG])
    else:
        log.info('Skipping stack product masking (maxsig = 0)')

    # save geotiff and numpy array files
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], rate, out_type='stack_rate')
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], error, out_type='stack_error')
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], samples, out_type='stack_samples')

def _merge_timeseries(rows, cols, params):
    """
    Merge time series output
    """
    shape, tiles, ifgs_dict = _merge_setup(rows, cols, params)

    # load the first tsincr file to determine the number of time series tifs
    tsincr_file = join(params[cf.TMPDIR], 'tsincr_0.npy')
    tsincr = np.load(file=tsincr_file)
    # pylint: disable=no-member
    no_ts_tifs = tsincr.shape[2]

    # create 2 x no_ts_tifs as we are splitting tsincr and tscuml to all processes.
    process_tifs = mpiops.array_split(range(2 * no_ts_tifs))

    # depending on nvelpar, this will not fit in memory
    # e.g. nvelpar=100, nrows=10000, ncols=10000, 32bit floats need 40GB memory
    # 32 * 100 * 10000 * 10000 / 8 bytes = 4e10 bytes = 40 GB
    # the double for loop helps us overcome the memory limit
    log.info('Process {} writing {} timeseries tifs of '
             'total {}'.format(mpiops.rank, len(process_tifs), no_ts_tifs * 2))
    for i in process_tifs:
        if i < no_ts_tifs:
            tscum_g = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='tscuml', index=i)
            _save_merged_files(ifgs_dict, params[cf.OUT_DIR], tscum_g, out_type='tscuml', index=i)
        else:
            i %= no_ts_tifs
            tsincr_g = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='tsincr', index=i)
            _save_merged_files(ifgs_dict, params[cf.OUT_DIR], tsincr_g, out_type='tsincr', index=i)
    mpiops.comm.barrier()
    log.debug('Process {} finished writing {} timeseries tifs of '
             'total {}'.format(mpiops.rank, len(process_tifs), no_ts_tifs * 2))

def create_png_from_tif(output_folder_path):
    """
    Function to create a preview PNG format image from a geotiff
    """
    log.info('Creating quicklook image.')
    # open raster and choose band to find min, max
    raster_path = join(output_folder_path, "stack_rate.tif")

    if not isfile(raster_path):
        raise Exception("stack_rate.tif file not found at: "+raster_path)
    gtif = gdal.Open(raster_path)
    srcband = gtif.GetRasterBand(1)

    west, north, east, south = "", "", "", ""
    for line in gdal.Info(gtif).split('\n'):
        if "Upper Left" in line:
            west, north = line.split(")")[0].split("(")[1].split(",")
        if "Lower Right" in line:
            east, south = line.split(")")[0].split("(")[1].split(",")

    kml_file_path = join(output_folder_path, "stack_rate.kml")
    kml_file_content = """<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.1">
  <Document>
    <name>stack_rate.kml</name>
    <GroundOverlay>
      <name>stack_rate.png</name>
      <Icon>
        <href>stack_rate.png</href>
      </Icon>
      <LatLonBox>
        <north> """+north+""" </north>
        <south> """+south+""" </south>
        <east>  """+east+""" </east>
        <west>  """+west+""" </west>
      </LatLonBox>
    </GroundOverlay>
  </Document>
</kml>"""

    with open(kml_file_path, "w") as f:
        f.write(kml_file_content)

    # Get raster statistics
    minimum, maximum, mean, stddev = srcband.GetStatistics(True, True)
    maximum = max(abs(minimum), abs(maximum))
    minimum = -1 * maximum
    step = (maximum - minimum) / 256.0

    del gtif  # manually close raster

    # read color map from utilities and write it to the output folder

    with open(REF_COLOR_MAP_PATH, "r") as f:
        color_map_list = []
        for line in f.readlines():
            color_map_list.append(line.strip().split(" "))

    no_of_data_value = len(np.arange(minimum, maximum, step))
    for i, no in enumerate(np.arange(minimum, maximum, step)):
        color_map_list[i+1][0] = str(no)

    color_map_path = join(output_folder_path, "colourmap.txt")
    with open(color_map_path, "w") as f:
        for i in range(no_of_data_value):
            f.write(' '.join(color_map_list[i]) + "\n")

    input_tif_path = join(output_folder_path, "stack_rate.tif")
    output_png_path = join(output_folder_path, "stack_rate.png")
    subprocess.check_call(["gdaldem", "color-relief", "-of", "PNG", input_tif_path, "-alpha",
                           color_map_path, output_png_path, "-nearest_color_entry"])
    log.debug('Finished creating quicklook image.')

def assemble_tiles(s, dir, tiles, out_type, index=None):
    """
    Function to reassemble tiles from numpy files in to a merged array

    :param tuple s: shape for merged array.
    :param str dir: path to directory containing numpy tile files.
    :param str out_type: product type string, used to construct numpy tile file name.
    :param int index: array third dimension index to extract from 3D time series array tiles.

    :return: merged_array: array assembled from all tiles.
    :rtype: ndarray
    """
    log.info('Re-assembling tiles for {}'.format(out_type))
    # pre-allocate dest array
    merged_array = np.empty(shape=s, dtype=np.float32)

    # loop over each tile, load and slot in to correct spot
    for t in tiles:
        tile_file = Path(join(dir, out_type + '_'+str(t.index)+'.npy'))
        tile = np.load(file=tile_file)
        if index is None: #2D array
            merged_array[t.top_left_y:t.bottom_right_y, t.top_left_x:t.bottom_right_x] = tile
        else: #3D array
            merged_array[t.top_left_y:t.bottom_right_y, t.top_left_x:t.bottom_right_x] = tile[:, :, index]

    log.debug('Finished assembling tiles for {}'.format(out_type))
    return merged_array

def _save_merged_files(ifgs_dict, outdir, array, out_type, index=None, savenpy=None):
    """
    Convenience function to save PyRate geotiff and numpy array files
    """
    log.info('Saving PyRate outputs {}'.format(out_type))
    gt, md, wkt = ifgs_dict['gt'], ifgs_dict['md'], ifgs_dict['wkt']
    epochlist = ifgs_dict['epochlist']

    if out_type in ('tsincr', 'tscuml'):
        epoch = epochlist.dates[index + 1]
        dest = join(outdir, out_type + "_" + str(epoch) + ".tif")
        # sequence position; first time slice is #0
        md['SEQUENCE_POSITION'] = index+1
        md[ifc.EPOCH_DATE] = epoch
    else:
        dest = join(outdir, out_type + ".tif")
        md[ifc.EPOCH_DATE] = epochlist.dates

    if out_type == 'stack_rate':
        md[ifc.DATA_TYPE] = ifc.STACKRATE
    elif out_type == 'stack_error':
        md[ifc.DATA_TYPE] = ifc.STACKERROR
    elif out_type == 'stack_samples':
        md[ifc.DATA_TYPE] = ifc.STACKSAMP
    elif out_type == 'tsincr':
        md[ifc.DATA_TYPE] = ifc.INCR
    else: #tscuml
        md[ifc.DATA_TYPE] = ifc.CUML

    shared.write_output_geotiff(md, gt, wkt, array, dest, np.nan)
    if savenpy:
        npy_rate_file = join(outdir, out_type + '.npy')
        np.save(file=npy_rate_file, arr=array)

    log.debug('Finished saving {}'.format(out_type))

def _merge_setup(rows, cols, params):
    """
    Convenience function for Merge set up steps
    """
    # setup paths
    xlks, _, crop = cf.transform_params(params)
    base_unw_paths = []

    for p in Path(params[cf.OUT_DIR]).rglob("*rlks_*cr.tif"):
        if "dem" not in str(p):
            base_unw_paths.append(str(p))

    if "tif" in base_unw_paths[0].split(".")[1]:
        dest_tifs = base_unw_paths # cf.get_dest_paths(base_unw_paths, crop, params, xlks)
        for i, dest_tif in enumerate(dest_tifs):
            dest_tifs[i] = dest_tif.replace("_tif", "")
    else:
        dest_tifs = base_unw_paths # cf.get_dest_paths(base_unw_paths, crop, params, xlks)

    # load previously saved preread_ifgs dict
    preread_ifgs_file = join(params[cf.TMPDIR], 'preread_ifgs.pk')
    ifgs_dict = pickle.load(open(preread_ifgs_file, 'rb'))
    ifgs = [v for v in ifgs_dict.values() if isinstance(v, shared.PrereadIfg)]
    shape = ifgs[0].shape
    tiles = shared.get_tiles(dest_tifs[0], rows, cols)
    return shape, tiles, ifgs_dict

def _delete_tsincr_files(params):
    """
    Convenience function to delete tsincr files
    """
    out_dir = Path(params[cf.OUT_DIR])
    for file_path in out_dir.iterdir():
        if "tsincr" in str(file_path):
            file_path.unlink()
<commit_msg>revise the log message verbosity<commit_after>#   This Python module is part of the PyRate software package.
#
#   Copyright 2020 Geoscience Australia
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
"""
This Python module does post-processing steps to assemble the
stack rate and time series outputs and save as geotiff files
"""
from os.path import join, isfile
import pickle
import numpy as np
from osgeo import gdal
import subprocess
from pathlib import Path

from pyrate.core import shared, stack, ifgconstants as ifc, mpiops, config as cf
from pyrate.constants import REF_COLOR_MAP_PATH
from pyrate.core.logger import pyratelogger as log

gdal.SetCacheMax(64)

def main(params):
    """
    PyRate merge main function. Assembles product tiles in to
    single geotiff files
    """
    # setup paths
    rows, cols = params["rows"], params["cols"]
    mpiops.run_once(_merge_stack, rows, cols, params)
    mpiops.run_once(create_png_from_tif, params[cf.OUT_DIR])

    if params[cf.TIME_SERIES_CAL]:
        _merge_timeseries(rows, cols, params)
        #mpiops.run_once(_delete_tsincr_files, params)

def _merge_stack(rows, cols, params):
    """
    Merge stacking outputs
    """
    shape, tiles, ifgs_dict = _merge_setup(rows, cols, params)

    log.info('Merging and writing Stack Rate product geotiffs')

    # read and assemble tile outputs
    rate = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_rate')
    error = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_error')
    samples = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='stack_samples')

    # mask pixels according to threshold
    if params[cf.LR_MAXSIG] > 0:
        rate, error = stack.mask_rate(rate, error, params[cf.LR_MAXSIG])
    else:
        log.info('Skipping stack product masking (maxsig = 0)')

    # save geotiff and numpy array files
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], rate, out_type='stack_rate')
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], error, out_type='stack_error')
    _save_merged_files(ifgs_dict, params[cf.OUT_DIR], samples, out_type='stack_samples')

def _merge_timeseries(rows, cols, params):
    """
    Merge time series output
    """
    shape, tiles, ifgs_dict = _merge_setup(rows, cols, params)

    # load the first tsincr file to determine the number of time series tifs
    tsincr_file = join(params[cf.TMPDIR], 'tsincr_0.npy')
    tsincr = np.load(file=tsincr_file)
    # pylint: disable=no-member
    no_ts_tifs = tsincr.shape[2]

    # create 2 x no_ts_tifs as we are splitting tsincr and tscuml to all processes.
    process_tifs = mpiops.array_split(range(2 * no_ts_tifs))

    # depending on nvelpar, this will not fit in memory
    # e.g. nvelpar=100, nrows=10000, ncols=10000, 32bit floats need 40GB memory
    # 32 * 100 * 10000 * 10000 / 8 bytes = 4e10 bytes = 40 GB
    # the double for loop helps us overcome the memory limit
    log.info('Process {} writing {} timeseries tifs of '
             'total {}'.format(mpiops.rank, len(process_tifs), no_ts_tifs * 2))
    for i in process_tifs:
        if i < no_ts_tifs:
            tscum_g = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='tscuml', index=i)
            _save_merged_files(ifgs_dict, params[cf.OUT_DIR], tscum_g, out_type='tscuml', index=i)
        else:
            i %= no_ts_tifs
            tsincr_g = assemble_tiles(shape, params[cf.TMPDIR], tiles, out_type='tsincr', index=i)
            _save_merged_files(ifgs_dict, params[cf.OUT_DIR], tsincr_g, out_type='tsincr', index=i)
    mpiops.comm.barrier()
    log.debug('Process {} finished writing {} timeseries tifs of '
             'total {}'.format(mpiops.rank, len(process_tifs), no_ts_tifs * 2))

def create_png_from_tif(output_folder_path):
    """
    Function to create a preview PNG format image from a geotiff
    """
    log.info('Creating quicklook image.')
    # open raster and choose band to find min, max
    raster_path = join(output_folder_path, "stack_rate.tif")

    if not isfile(raster_path):
        raise Exception("stack_rate.tif file not found at: "+raster_path)
    gtif = gdal.Open(raster_path)
    srcband = gtif.GetRasterBand(1)

    west, north, east, south = "", "", "", ""
    for line in gdal.Info(gtif).split('\n'):
        if "Upper Left" in line:
            west, north = line.split(")")[0].split("(")[1].split(",")
        if "Lower Right" in line:
            east, south = line.split(")")[0].split("(")[1].split(",")

    kml_file_path = join(output_folder_path, "stack_rate.kml")
    kml_file_content = """<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.1">
  <Document>
    <name>stack_rate.kml</name>
    <GroundOverlay>
      <name>stack_rate.png</name>
      <Icon>
        <href>stack_rate.png</href>
      </Icon>
      <LatLonBox>
        <north> """+north+""" </north>
        <south> """+south+""" </south>
        <east>  """+east+""" </east>
        <west>  """+west+""" </west>
      </LatLonBox>
    </GroundOverlay>
  </Document>
</kml>"""

    with open(kml_file_path, "w") as f:
        f.write(kml_file_content)

    # Get raster statistics
    minimum, maximum, mean, stddev = srcband.GetStatistics(True, True)
    maximum = max(abs(minimum), abs(maximum))
    minimum = -1 * maximum
    step = (maximum - minimum) / 256.0

    del gtif  # manually close raster

    # read color map from utilities and write it to the output folder

    with open(REF_COLOR_MAP_PATH, "r") as f:
        color_map_list = []
        for line in f.readlines():
            color_map_list.append(line.strip().split(" "))

    no_of_data_value = len(np.arange(minimum, maximum, step))
    for i, no in enumerate(np.arange(minimum, maximum, step)):
        color_map_list[i+1][0] = str(no)

    color_map_path = join(output_folder_path, "colourmap.txt")
    with open(color_map_path, "w") as f:
        for i in range(no_of_data_value):
            f.write(' '.join(color_map_list[i]) + "\n")

    input_tif_path = join(output_folder_path, "stack_rate.tif")
    output_png_path = join(output_folder_path, "stack_rate.png")
    subprocess.check_call(["gdaldem", "color-relief", "-of", "PNG", input_tif_path, "-alpha",
                           color_map_path, output_png_path, "-nearest_color_entry"])
    log.debug('Finished creating quicklook image.')

def assemble_tiles(s, dir, tiles, out_type, index=None):
    """
    Function to reassemble tiles from numpy files in to a merged array

    :param tuple s: shape for merged array.
    :param str dir: path to directory containing numpy tile files.
    :param str out_type: product type string, used to construct numpy tile file name.
    :param int index: array third dimension index to extract from 3D time series array tiles.

    :return: merged_array: array assembled from all tiles.
    :rtype: ndarray
    """
    log.debug('Re-assembling tiles for {}'.format(out_type))
    # pre-allocate dest array
    merged_array = np.empty(shape=s, dtype=np.float32)

    # loop over each tile, load and slot in to correct spot
    for t in tiles:
        tile_file = Path(join(dir, out_type + '_'+str(t.index)+'.npy'))
        tile = np.load(file=tile_file)
        if index is None: #2D array
            merged_array[t.top_left_y:t.bottom_right_y, t.top_left_x:t.bottom_right_x] = tile
        else: #3D array
            merged_array[t.top_left_y:t.bottom_right_y, t.top_left_x:t.bottom_right_x] = tile[:, :, index]

    log.debug('Finished assembling tiles for {}'.format(out_type))
    return merged_array

def _save_merged_files(ifgs_dict, outdir, array, out_type, index=None, savenpy=None):
    """
    Convenience function to save PyRate geotiff and numpy array files
    """
    log.debug('Saving PyRate outputs {}'.format(out_type))
    gt, md, wkt = ifgs_dict['gt'], ifgs_dict['md'], ifgs_dict['wkt']
    epochlist = ifgs_dict['epochlist']

    if out_type in ('tsincr', 'tscuml'):
        epoch = epochlist.dates[index + 1]
        dest = join(outdir, out_type + "_" + str(epoch) + ".tif")
        # sequence position; first time slice is #0
        md['SEQUENCE_POSITION'] = index+1
        md[ifc.EPOCH_DATE] = epoch
    else:
        dest = join(outdir, out_type + ".tif")
        md[ifc.EPOCH_DATE] = epochlist.dates

    if out_type == 'stack_rate':
        md[ifc.DATA_TYPE] = ifc.STACKRATE
    elif out_type == 'stack_error':
        md[ifc.DATA_TYPE] = ifc.STACKERROR
    elif out_type == 'stack_samples':
        md[ifc.DATA_TYPE] = ifc.STACKSAMP
    elif out_type == 'tsincr':
        md[ifc.DATA_TYPE] = ifc.INCR
    else: #tscuml
        md[ifc.DATA_TYPE] = ifc.CUML

    shared.write_output_geotiff(md, gt, wkt, array, dest, np.nan)
    if savenpy:
        npy_rate_file = join(outdir, out_type + '.npy')
        np.save(file=npy_rate_file, arr=array)

    log.debug('Finished saving {}'.format(out_type))

def _merge_setup(rows, cols, params):
    """
    Convenience function for Merge set up steps
    """
    # setup paths
    xlks, _, crop = cf.transform_params(params)
    base_unw_paths = []

    for p in Path(params[cf.OUT_DIR]).rglob("*rlks_*cr.tif"):
        if "dem" not in str(p):
            base_unw_paths.append(str(p))

    if "tif" in base_unw_paths[0].split(".")[1]:
        dest_tifs = base_unw_paths # cf.get_dest_paths(base_unw_paths, crop, params, xlks)
        for i, dest_tif in enumerate(dest_tifs):
            dest_tifs[i] = dest_tif.replace("_tif", "")
    else:
        dest_tifs = base_unw_paths # cf.get_dest_paths(base_unw_paths, crop, params, xlks)

    # load previously saved preread_ifgs dict
    preread_ifgs_file = join(params[cf.TMPDIR], 'preread_ifgs.pk')
    ifgs_dict = pickle.load(open(preread_ifgs_file, 'rb'))
    ifgs = [v for v in ifgs_dict.values() if isinstance(v, shared.PrereadIfg)]
    shape = ifgs[0].shape
    tiles = shared.get_tiles(dest_tifs[0], rows, cols)
    return shape, tiles, ifgs_dict

def _delete_tsincr_files(params):
    """
    Convenience function to delete tsincr files
    """
    out_dir = Path(params[cf.OUT_DIR])
    for file_path in out_dir.iterdir():
        if "tsincr" in str(file_path):
            file_path.unlink()
<|endoftext|>


<commit_before>"""Collection of simple, handy functions."""

import os
import argparse
import pyretinex.config as cfg
from pyretinex import __package__, __version__

def display_welcome_message(package=__package__, version=__version__):
    """Print program name and version."""
    welcome_str = '{} {}'.format(package, version)
    welcome_decor = '=' * len(welcome_str)
    print('{}\n{}\n{}'.format(welcome_decor, welcome_str, welcome_decor))

def user_interface():
    """Commandline interface."""
    parser = argparse.ArgumentParser()

    parser.add_argument(
        'filename',  metavar='path', nargs='+',
        help="Path to image. When a single path is provided applies \
        multi-scale retinex (MSR). Otherwise switches to multi-scale retinex \
        with barycenter preservation (MSRBP)."
        )
    parser.add_argument(
        '--out_dir', type=str, metavar='path', required=False,
        help="Absolute path of output directory. If not provided, processed \
        images will be saved in the input image path."
        )
    parser.add_argument(
        '--scales', nargs='+', type=int, required=False,
        metavar=' '.join(str(e) for e in cfg.scales), default=cfg.scales,
        help="Standard deviations for Gaussian kernels. More or less than 3 \
        values can be given. Disscussion of how to determine/optimize the \
        scale values can be found in Jobson, Rahman, Woodell (1997)"
        )
    parser.add_argument(
        "--intensity_balance", action='store_true',
        help="Experimental, work in progress."
        )
    parser.add_argument(
        "--color_balance", action='store_true',
        help="Experimental, work in progress."
        )
    parser.add_argument(
        "--no_retinex", action='store_true',
        help="Do not perform retinex image enhancement. Useful for diagnosis."
        )

    args = parser.parse_args()
    cfg.filename = args.filename
    if os.path.isdir(args.out_dir):
        cfg.out_dir = args.out_dir
    else:
        raise ValueError('Output directory does not exist.')

    cfg.scales = args.scales
    cfg.intensity_balance = args.intensity_balance
    cfg.color_balance = args.color_balance
    cfg.no_retinex = args.no_retinex
<commit_msg>Fix and improve output dir option<commit_after>"""Collection of simple, handy functions."""

import os
import argparse
import pyretinex.config as cfg
from pyretinex import __package__, __version__

def display_welcome_message(package=__package__, version=__version__):
    """Print program name and version."""
    welcome_str = '{} {}'.format(package, version)
    welcome_decor = '=' * len(welcome_str)
    print('{}\n{}\n{}'.format(welcome_decor, welcome_str, welcome_decor))

def user_interface():
    """Commandline interface."""
    parser = argparse.ArgumentParser()

    parser.add_argument(
        'filename',  metavar='path', nargs='+',
        help="Path to image. When a single path is provided applies \
        multi-scale retinex (MSR). Otherwise switches to multi-scale retinex \
        with barycenter preservation (MSRBP)."
        )
    parser.add_argument(
        '--out_dir', type=str, metavar='path', required=False,
        default=cfg.out_dir,
        help="Absolute path of output directory. If not provided, processed \
        images will be saved in the input image path."
        )
    parser.add_argument(
        '--scales', nargs='+', type=int, required=False,
        metavar=' '.join(str(e) for e in cfg.scales), default=cfg.scales,
        help="Standard deviations for Gaussian kernels. More or less than 3 \
        values can be given. Disscussion of how to determine/optimize the \
        scale values can be found in Jobson, Rahman, Woodell (1997)"
        )
    parser.add_argument(
        "--intensity_balance", action='store_true',
        help="Experimental, work in progress."
        )
    parser.add_argument(
        "--color_balance", action='store_true',
        help="Experimental, work in progress."
        )
    parser.add_argument(
        "--no_retinex", action='store_true',
        help="Do not perform retinex image enhancement. Useful for diagnosis."
        )

    args = parser.parse_args()
    cfg.filename = args.filename
    cfg.out_dir = args.out_dir
    cfg.scales = args.scales
    cfg.intensity_balance = args.intensity_balance
    cfg.color_balance = args.color_balance
    cfg.no_retinex = args.no_retinex

    if cfg.out_dir:
        if os.path.isdir(cfg.out_dir):
            pass
        else:
            os.mkdir(cfg.out_dir)
<|endoftext|>


<commit_before>import time
from datetime import datetime

from . import base
from .attachments import Attachment
from groupy import utils
from groupy import pagers

class Messages(base.Manager):
    """A message manager for a particular group.

    :param session: the request session
    :type session: :class:`~groupy.session.Session`
    :param str group_id: the group_id of a group
    """

    def __init__(self, session, group_id):
        path = 'groups/{}/messages'.format(group_id)
        super().__init__(session, path=path)

    def _raw_list(self, **params):
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def list(self, before_id=None, since_id=None, after_id=None, limit=20):
        """Return a page of group messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``, or ``after_id``.

        :param str before_id: message ID for paging backwards
        :param str after_id: message ID for paging forwards
        :param str since_id: message ID for most recent messages since
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return pagers.MessageList(self, self._raw_list, before_id=before_id,
                                  after_id=after_id, since_id=since_id,
                                  limit=limit)

    def list_before(self, message_id, limit=None):
        """Return a page of group messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=message_id, limit=None)

    def list_since(self, message_id, limit=None):
        """Return a page of group messages created since a message.

        This is used to fetch the most recent messages after another. There
        may exist messages between the one given and the ones returned. Use
        :func:`list_after` to retrieve newer messages without skipping any.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(since_id=message_id, limit=limit)

    def list_after(self, message_id, limit=None):
        """Return a page of group messages created after a message.

        This is used to page forwards through messages.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(after_id=message_id, limit=limit)

    def list_all(self, limit=None):
        """Return all group messages.

        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list().autopage()

    def list_all_before(self, message_id, limit=None):
        """Return all group messages created before a message.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_before(message_id, limit=limit).autopage()

    def list_all_after(self, message_id, limit=None):
        """Return all group messages created after a message.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_after(message_id, limit=limit).autopage()

    def create(self, text=None, attachments=None, source_guid=None):
        """Create a new message in the group.

        :param str text: the text of the message
        :param attachments: a list of attachments
        :type attachments: :class:`list`
        :param str source_guid: a unique identifier for the message
        :return: the created message
        :rtype: :class:`~groupy.api.messages.Message`
        """
        message = {
            'source_guid': source_guid or str(time.time()),
        }

        if text is not None:
            message['text'] = text

        if attachments is not None:
            message['attachments'] = [a.to_json() for a in attachments]

        payload = {'message': message}
        response = self.session.post(self.url, json=payload)
        message = response.data['message']
        return Message(self, **message)

class DirectMessages(base.Manager):
    """Manager for direct messages with a particular user.

    :param session: request session
    :param str other_user_id: user_id of another user
    """

    def __init__(self, session, other_user_id):
        super().__init__(session, 'direct_messages')
        self.other_user_id = other_user_id

    def _raw_list(self, **params):
        params['other_user_id'] = self.other_user_id
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['direct_messages']
        return [DirectMessage(self, **message) for message in messages]

    def list(self, before_id=None, since_id=None, **kwargs):
        """Return a page of direct messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``.

        :param str before_id: message ID for paging backwards
        :param str since_id: message ID for most recent messages since
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return pagers.MessageList(self, self._raw_list, before_id=before_id,
                                  since_id=since_id, **kwargs)

    def list_before(self, message_id, **kwargs):
        """Return a page of direct messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=message_id, **kwargs)

    def list_since(self, message_id, **kwargs):
        """Return a page of direct messages created since a message.

        This is used to fetch the most recent messages after another. There
        may exist messages between the one given and the ones returned.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(since_id=message_id, **kwargs)

    def list_all(self, before_id=None, since_id=None, **kwargs):
        """Return all direct messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``.

        :param str before_id: message ID for paging backwards
        :param str since_id: message ID for most recent messages since
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=before_id, since_id=since_id, **kwargs).autopage()

    def list_all_before(self, message_id, **kwargs):
        """Return all direct messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_before(message_id, **kwargs).autopage()

    def create(self, text=None, attachments=None, source_guid=None):
        """Send a new direct message to the user.

        Only provide the source_guid if you want to control it.

        :param str text: the message content
        :param attachments: message attachments
        :param str source_guid: a client-side unique ID for the message
        :return: the message sent
        :rtype: :class:`~groupy.api.messages.DirectMessage`
        """
        message = {
            'source_guid': source_guid or str(time.time()),
            'recipient_id': self.other_user_id,
        }

        if text is not None:
            message['text'] = text

        if attachments is not None:
            message['attachments'] = [a.to_json() for a in attachments]

        payload = {'direct_message': message}
        response = self.session.post(self.url, json=payload)
        message = response.data['direct_message']
        return DirectMessage(self, **message)

class GenericMessage(base.ManagedResource):
    """A message.

    :param manager: a message manager
    :param str conversation_id: the ID for the conversation
    :param kwargs data: the data of the message
    """

    #: number of characters seen in the repr output
    preview_length = 42

    def __init__(self, manager, conversation_id, **data):
        super().__init__(manager, **data)
        self.conversation_id = conversation_id
        self.created_at = datetime.fromtimestamp(self.created_at)
        attachments = self.data.get('attachments') or []
        self.attachments = Attachment.from_bulk_data(attachments)
        self._likes = Likes(self.manager.session, self.conversation_id,
                            message_id=self.id)

    def __repr__(self):
        klass = self.__class__.__name__
        text = self.text
        if text and len(text) > self.preview_length:
            text = text[:self.preview_length - 3] + '...'
        return ('<{}(name={!r}, text={!r}, attachments={})>'
                .format(klass, self.name, text, len(self.attachments)))

    def like(self):
        """Like the message."""
        return self._likes.like()

    def unlike(self):
        """Unlike the message."""
        return self._likes.unlike()

class Message(GenericMessage):
    """A group message."""

    def __init__(self, manager, **data):
        conversation_id = data['group_id']
        super().__init__(manager, conversation_id, **data)

class DirectMessage(GenericMessage):
    """A direct message between two users."""

    # manager could be from a chat or from a group... is that a problem?
    def __init__(self, manager, **data):
        data['conversation_id'] = self.__class__.get_conversation_id(data)
        super().__init__(manager, **data)

    @staticmethod
    def get_conversation_id(data):
        # some endpoints return direct messages with a conversation id. if not,
        # we have to construct it
        conversation_id = data.get('conversation_id')
        if not conversation_id:
            participant_ids = data['recipient_id'], data['sender_id']
            conversation_id = '+'.join(sorted(participant_ids))
        return conversation_id

class Leaderboard(base.Manager):
    """Manager for messages on the leaderboard."""

    def __init__(self, session, group_id):
        path = 'groups/{}/likes'.format(group_id)
        super().__init__(session, path=path)

    def _get_messages(self, path=None, **params):
        url = utils.urljoin(self.url, path)
        response = self.session.get(url, params=params)
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def list(self, period):
        """List most liked messages for a given period.

        :param str period: either "day", "week", or "month"
        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period=period)

    def list_day(self):
        """List most liked messages for the last day.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='day')

    def list_week(self):
        """List most liked messages for the last week.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='week')

    def list_month(self):
        """List most liked messages for the last month.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='month')

    def list_mine(self):
        """List messages you liked.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(path='mine')

    def list_for_me(self):
        """List your top liked messages.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(path='for_me')

class Likes(base.Manager):
    """Manager for likes/unlikes of a particular message.

    The message can be from either a group or a chat.

    :param session: request session
    :param str conversation_id: unique ID for a the conversation from which
                                the message originates
    :param str message_id: unique ID of the message to like/unlike
    """

    def __init__(self, session, conversation_id, message_id):
        path = 'messages/{}/{}'.format(conversation_id, message_id)
        super().__init__(session, path=path)

    def like(self):
        """Like the message."""
        url = utils.urljoin(self.url, 'like')
        response = self.session.post(url)
        return response.ok

    def unlike(self):
        """Unlike the message."""
        url = utils.urljoin(self.url, 'unlike')
        response = self.session.post(url)
        return response.ok

class Gallery(base.Manager):
    """Manager for messages in the gallery.

    This endpoint is undocumented!

    :param session: request session
    :param str group_id: group_id for a particular group
    """

    def __init__(self, session, group_id):
        path = 'conversations/{}/gallery'.format(group_id)
        super().__init__(session, path=path)

    def _raw_list(self, **params):
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def _convert_to_rfc3339(self, when=None):
        if when is None:
            return None
        return utils.get_rfc3339(when)

    def list(self, before=None, since=None, after=None, limit=100):
        before = self._convert_to_rfc3339(before)
        since = self._convert_to_rfc3339(since)
        after = self._convert_to_rfc3339(after)
        return pagers.GalleryList(self, self._raw_list, before=before,
                                  since=since, after=after, limit=limit)

    def list_before(self, when, limit=100):
        return self.list(before=when, limit=limit)

    def list_since(self, when, limit=100):
        return self.list(since=when, limit=limit)

    def list_after(self, when, limit=100):
        return self.list(after=when, limit=limit)

    def list_all(self, **params):
        return self.list(**params).autopage()

    def list_all_before(self, when, limit=100):
        return self.list_before(when=when, limit=limit).autopage()

    def list_all_after(self, when, limit=100):
        return self.list_after(when=when, limit=limit).autopage()

<commit_msg>add verbose failure for bad created_at<commit_after>import time
from datetime import datetime

from . import base
from .attachments import Attachment
from groupy import utils
from groupy import pagers

class Messages(base.Manager):
    """A message manager for a particular group.

    :param session: the request session
    :type session: :class:`~groupy.session.Session`
    :param str group_id: the group_id of a group
    """

    def __init__(self, session, group_id):
        path = 'groups/{}/messages'.format(group_id)
        super().__init__(session, path=path)

    def _raw_list(self, **params):
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def list(self, before_id=None, since_id=None, after_id=None, limit=20):
        """Return a page of group messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``, or ``after_id``.

        :param str before_id: message ID for paging backwards
        :param str after_id: message ID for paging forwards
        :param str since_id: message ID for most recent messages since
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return pagers.MessageList(self, self._raw_list, before_id=before_id,
                                  after_id=after_id, since_id=since_id,
                                  limit=limit)

    def list_before(self, message_id, limit=None):
        """Return a page of group messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=message_id, limit=None)

    def list_since(self, message_id, limit=None):
        """Return a page of group messages created since a message.

        This is used to fetch the most recent messages after another. There
        may exist messages between the one given and the ones returned. Use
        :func:`list_after` to retrieve newer messages without skipping any.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(since_id=message_id, limit=limit)

    def list_after(self, message_id, limit=None):
        """Return a page of group messages created after a message.

        This is used to page forwards through messages.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(after_id=message_id, limit=limit)

    def list_all(self, limit=None):
        """Return all group messages.

        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list().autopage()

    def list_all_before(self, message_id, limit=None):
        """Return all group messages created before a message.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_before(message_id, limit=limit).autopage()

    def list_all_after(self, message_id, limit=None):
        """Return all group messages created after a message.

        :param str message_id: the ID of a message
        :param int limit: maximum number of messages per page
        :return: group messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_after(message_id, limit=limit).autopage()

    def create(self, text=None, attachments=None, source_guid=None):
        """Create a new message in the group.

        :param str text: the text of the message
        :param attachments: a list of attachments
        :type attachments: :class:`list`
        :param str source_guid: a unique identifier for the message
        :return: the created message
        :rtype: :class:`~groupy.api.messages.Message`
        """
        message = {
            'source_guid': source_guid or str(time.time()),
        }

        if text is not None:
            message['text'] = text

        if attachments is not None:
            message['attachments'] = [a.to_json() for a in attachments]

        payload = {'message': message}
        response = self.session.post(self.url, json=payload)
        message = response.data['message']
        return Message(self, **message)

class DirectMessages(base.Manager):
    """Manager for direct messages with a particular user.

    :param session: request session
    :param str other_user_id: user_id of another user
    """

    def __init__(self, session, other_user_id):
        super().__init__(session, 'direct_messages')
        self.other_user_id = other_user_id

    def _raw_list(self, **params):
        params['other_user_id'] = self.other_user_id
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['direct_messages']
        return [DirectMessage(self, **message) for message in messages]

    def list(self, before_id=None, since_id=None, **kwargs):
        """Return a page of direct messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``.

        :param str before_id: message ID for paging backwards
        :param str since_id: message ID for most recent messages since
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return pagers.MessageList(self, self._raw_list, before_id=before_id,
                                  since_id=since_id, **kwargs)

    def list_before(self, message_id, **kwargs):
        """Return a page of direct messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=message_id, **kwargs)

    def list_since(self, message_id, **kwargs):
        """Return a page of direct messages created since a message.

        This is used to fetch the most recent messages after another. There
        may exist messages between the one given and the ones returned.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(since_id=message_id, **kwargs)

    def list_all(self, before_id=None, since_id=None, **kwargs):
        """Return all direct messages.

        The messages come in reversed order (newest first). Note you can only
        provide _one_ of ``before_id``, ``since_id``.

        :param str before_id: message ID for paging backwards
        :param str since_id: message ID for most recent messages since
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list(before_id=before_id, since_id=since_id, **kwargs).autopage()

    def list_all_before(self, message_id, **kwargs):
        """Return all direct messages created before a message.

        This can be used to page backwards through messages.

        :param str message_id: the ID of a message
        :return: direct messages
        :rtype: :class:`~groupy.pagers.MessageList`
        """
        return self.list_before(message_id, **kwargs).autopage()

    def create(self, text=None, attachments=None, source_guid=None):
        """Send a new direct message to the user.

        Only provide the source_guid if you want to control it.

        :param str text: the message content
        :param attachments: message attachments
        :param str source_guid: a client-side unique ID for the message
        :return: the message sent
        :rtype: :class:`~groupy.api.messages.DirectMessage`
        """
        message = {
            'source_guid': source_guid or str(time.time()),
            'recipient_id': self.other_user_id,
        }

        if text is not None:
            message['text'] = text

        if attachments is not None:
            message['attachments'] = [a.to_json() for a in attachments]

        payload = {'direct_message': message}
        response = self.session.post(self.url, json=payload)
        message = response.data['direct_message']
        return DirectMessage(self, **message)

class GenericMessage(base.ManagedResource):
    """A message.

    :param manager: a message manager
    :param str conversation_id: the ID for the conversation
    :param kwargs data: the data of the message
    """

    #: number of characters seen in the repr output
    preview_length = 42

    def __init__(self, manager, conversation_id, **data):
        super().__init__(manager, **data)
        self.conversation_id = conversation_id
        try:

            self.created_at = datetime.fromtimestamp(self.created_at)
        except:
            print('failure creating date for {}'.format(self.created_at))
            print(data)
            raise
        attachments = self.data.get('attachments') or []
        self.attachments = Attachment.from_bulk_data(attachments)
        self._likes = Likes(self.manager.session, self.conversation_id,
                            message_id=self.id)

    def __repr__(self):
        klass = self.__class__.__name__
        text = self.text
        if text and len(text) > self.preview_length:
            text = text[:self.preview_length - 3] + '...'
        return ('<{}(name={!r}, text={!r}, attachments={})>'
                .format(klass, self.name, text, len(self.attachments)))

    def like(self):
        """Like the message."""
        return self._likes.like()

    def unlike(self):
        """Unlike the message."""
        return self._likes.unlike()

class Message(GenericMessage):
    """A group message."""

    def __init__(self, manager, **data):
        conversation_id = data['group_id']
        super().__init__(manager, conversation_id, **data)

class DirectMessage(GenericMessage):
    """A direct message between two users."""

    # manager could be from a chat or from a group... is that a problem?
    def __init__(self, manager, **data):
        data['conversation_id'] = self.__class__.get_conversation_id(data)
        super().__init__(manager, **data)

    @staticmethod
    def get_conversation_id(data):
        # some endpoints return direct messages with a conversation id. if not,
        # we have to construct it
        conversation_id = data.get('conversation_id')
        if not conversation_id:
            participant_ids = data['recipient_id'], data['sender_id']
            conversation_id = '+'.join(sorted(participant_ids))
        return conversation_id

class Leaderboard(base.Manager):
    """Manager for messages on the leaderboard."""

    def __init__(self, session, group_id):
        path = 'groups/{}/likes'.format(group_id)
        super().__init__(session, path=path)

    def _get_messages(self, path=None, **params):
        url = utils.urljoin(self.url, path)
        response = self.session.get(url, params=params)
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def list(self, period):
        """List most liked messages for a given period.

        :param str period: either "day", "week", or "month"
        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period=period)

    def list_day(self):
        """List most liked messages for the last day.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='day')

    def list_week(self):
        """List most liked messages for the last week.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='week')

    def list_month(self):
        """List most liked messages for the last month.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(period='month')

    def list_mine(self):
        """List messages you liked.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(path='mine')

    def list_for_me(self):
        """List your top liked messages.

        :return: the messages
        :rtype: :class:`list`
        """
        return self._get_messages(path='for_me')

class Likes(base.Manager):
    """Manager for likes/unlikes of a particular message.

    The message can be from either a group or a chat.

    :param session: request session
    :param str conversation_id: unique ID for a the conversation from which
                                the message originates
    :param str message_id: unique ID of the message to like/unlike
    """

    def __init__(self, session, conversation_id, message_id):
        path = 'messages/{}/{}'.format(conversation_id, message_id)
        super().__init__(session, path=path)

    def like(self):
        """Like the message."""
        url = utils.urljoin(self.url, 'like')
        response = self.session.post(url)
        return response.ok

    def unlike(self):
        """Unlike the message."""
        url = utils.urljoin(self.url, 'unlike')
        response = self.session.post(url)
        return response.ok

class Gallery(base.Manager):
    """Manager for messages in the gallery.

    This endpoint is undocumented!

    :param session: request session
    :param str group_id: group_id for a particular group
    """

    def __init__(self, session, group_id):
        path = 'conversations/{}/gallery'.format(group_id)
        super().__init__(session, path=path)

    def _raw_list(self, **params):
        response = self.session.get(self.url, params=params)
        if response.status_code == 304:
            return []
        messages = response.data['messages']
        return [Message(self, **message) for message in messages]

    def _convert_to_rfc3339(self, when=None):
        if when is None:
            return None
        return utils.get_rfc3339(when)

    def list(self, before=None, since=None, after=None, limit=100):
        before = self._convert_to_rfc3339(before)
        since = self._convert_to_rfc3339(since)
        after = self._convert_to_rfc3339(after)
        return pagers.GalleryList(self, self._raw_list, before=before,
                                  since=since, after=after, limit=limit)

    def list_before(self, when, limit=100):
        return self.list(before=when, limit=limit)

    def list_since(self, when, limit=100):
        return self.list(since=when, limit=limit)

    def list_after(self, when, limit=100):
        return self.list(after=when, limit=limit)

    def list_all(self, **params):
        return self.list(**params).autopage()

    def list_all_before(self, when, limit=100):
        return self.list_before(when=when, limit=limit).autopage()

    def list_all_after(self, when, limit=100):
        return self.list_after(when=when, limit=limit).autopage()

<|endoftext|>


<commit_before>#!/usr/bin/env python
'''This module handles constraint generation for IRSBs.'''

import z3
import pyvex
import s_irstmt
import s_helpers
import s_exit

import logging
l = logging.getLogger("s_irsb")
#l.setLevel(logging.DEBUG)

class SymbolicIRSBError(Exception):
	pass

sirsb_count = 0

class SymbolicIRSB:
	# Symbolically parses a basic block.
	#
	#	irsb - the pyvex IRSB to parse
	#	initial_state - the symbolic state at the beginning of the block
	#	id - the ID of the basic block
	#	ethereal - whether the basic block is a made-up one (ie, for an emulated ret)
	def __init__(self, irsb, initial_state, id=None, ethereal=False):
		global sirsb_count

		if irsb.size() == 0:
			raise SymbolicIRSBError("Empty IRSB passed to SymbolicIRSB.")

		self.irsb = irsb

		# set the ID and copy the initial state
		self.first_imark = [i for i in self.irsb.statements() if type(i)==pyvex.IRStmt.IMark][0]
		state = initial_state
		if not ethereal:
			state.block_path.append(self.first_imark.addr)

		if id is None:
			state.id = "%x" % self.first_imark.addr
		else:
			state.id = id
		self.initial_state = initial_state.copy_after()

		#l.debug("Blockstack is now: %s" % " ".join(["%x" % x for x in self.initial_state.block_path ]))

		#
		# Now translate!
		#

		# first, prepare symbolic variables for the statements
		state.temps = { }
		for n, t in enumerate(self.irsb.tyenv.types()):
			state.temps[n] = z3.BitVec('%s_%d_t%d' % (state.id, sirsb_count, n), s_helpers.get_size(t))
		sirsb_count += 1

		# now get the constraints
		self.first_imark = [i for i in self.irsb.statements() if type(i)==pyvex.IRStmt.IMark][0]
		state, self.last_imark, self.s_statements = s_irstmt.handle_statements(state, self.first_imark, self.irsb.statements())

		# final state
		l.debug("%d constraints at end of SymbolicIRSB %s"%(len(state.old_constraints),state.id))
		self.final_state = state

	# return the exits from the IRSB
	def exits(self):
		exits = [ ]
		if len(self.irsb.statements()) == 0:
			l.debug("Returning no exits for empty IRSB")
			return [ ]

		l.debug("Generating exits of IRSB at 0x%x." % self.last_imark.addr)

		for e in [ s for s in self.s_statements if type(s.stmt) == pyvex.IRStmt.Exit ]:
			exits.append(s_exit.SymbolicExit(sexit = e, stmt_index = self.s_statements.index(e)))
			if e.stmt.jumpkind == "Ijk_Call":
				raise Exception("Good job, you caught this exception! This was placed here by Yan to find out if this case is possible. Please tell Yan that it is and then remove this line. Apologies for the inconvenience!")
				exits.append(s_exit.SymbolicExit(sexit_postcall = e, stmt_index = self.s_statements.index(e)))

		# and add the default one
		exits.append(s_exit.SymbolicExit(sirsb_exit = self))
		if self.irsb.jumpkind == "Ijk_Call":
			exits.append(s_exit.SymbolicExit(sirsb_postcall = self))

		l.debug("Generated %d exits for 0x%x" % (len(exits), self.last_imark.addr))
		return exits
<commit_msg>some debugging<commit_after>#!/usr/bin/env python
'''This module handles constraint generation for IRSBs.'''

import z3
import pyvex
import s_irstmt
import s_helpers
import s_exit

import logging
l = logging.getLogger("s_irsb")
#l.setLevel(logging.DEBUG)

class SymbolicIRSBError(Exception):
	pass

sirsb_count = 0

class SymbolicIRSB:
	# Symbolically parses a basic block.
	#
	#	irsb - the pyvex IRSB to parse
	#	initial_state - the symbolic state at the beginning of the block
	#	id - the ID of the basic block
	#	ethereal - whether the basic block is a made-up one (ie, for an emulated ret)
	def __init__(self, irsb, initial_state, id=None, ethereal=False):
		global sirsb_count

		if irsb.size() == 0:
			raise SymbolicIRSBError("Empty IRSB passed to SymbolicIRSB.")

		self.irsb = irsb
		l.debug("Entering block %s with %d constraints." % (id, len(initial_state.constraints_after())))

		# set the ID and copy the initial state
		self.first_imark = [i for i in self.irsb.statements() if type(i)==pyvex.IRStmt.IMark][0]
		state = initial_state
		if not ethereal:
			state.block_path.append(self.first_imark.addr)

		if id is None:
			state.id = "%x" % self.first_imark.addr
		else:
			state.id = id
		self.initial_state = initial_state.copy_after()

		#l.debug("Blockstack is now: %s" % " ".join(["%x" % x for x in self.initial_state.block_path ]))

		#
		# Now translate!
		#

		# first, prepare symbolic variables for the statements
		state.temps = { }
		for n, t in enumerate(self.irsb.tyenv.types()):
			state.temps[n] = z3.BitVec('%s_%d_t%d' % (state.id, sirsb_count, n), s_helpers.get_size(t))
		sirsb_count += 1

		# now get the constraints
		self.first_imark = [i for i in self.irsb.statements() if type(i)==pyvex.IRStmt.IMark][0]
		state, self.last_imark, self.s_statements = s_irstmt.handle_statements(state, self.first_imark, self.irsb.statements())

		# final state
		l.debug("%d constraints at end of SymbolicIRSB %s"%(len(state.old_constraints),state.id))
		self.final_state = state

	# return the exits from the IRSB
	def exits(self):
		exits = [ ]
		if len(self.irsb.statements()) == 0:
			l.debug("Returning no exits for empty IRSB")
			return [ ]

		l.debug("Generating exits of IRSB at 0x%x." % self.last_imark.addr)

		for e in [ s for s in self.s_statements if type(s.stmt) == pyvex.IRStmt.Exit ]:
			exits.append(s_exit.SymbolicExit(sexit = e, stmt_index = self.s_statements.index(e)))
			if e.stmt.jumpkind == "Ijk_Call":
				raise Exception("Good job, you caught this exception! This was placed here by Yan to find out if this case is possible. Please tell Yan that it is and then remove this line. Apologies for the inconvenience!")

		# and add the default one
		exits.append(s_exit.SymbolicExit(sirsb_exit = self))
		if self.irsb.jumpkind == "Ijk_Call":
			exits.append(s_exit.SymbolicExit(sirsb_postcall = self))

		l.debug("Generated %d exits for 0x%x" % (len(exits), self.last_imark.addr))
		return exits
<|endoftext|>


<commit_before>import argparse

from flask import Flask
from src.pin import OutputPin

parser = argparse.ArgumentParser(description='Controls an AC System')
parser.add_argument("-p", "--port", type=int, help="the port to run server on", default=5003)
parser.add_argument("-a", "--AC_pin", type=int, help="the pin to turn on to activate the AC system", default=4)
parser.add_argument("-h", "--heater_pin", type=int, help="the pin to turn on to activate the heater", default=5)
parser.add_argument("-f", "--fan_pin", type=int, help="the pin to turn on to activate the fan", default=6)

args = parser.parse_args()

AC_pin = OutputPin(args.AC_pin)
heater_pin = OutputPin(args.heater_pin)
fan_pin = OutputPin(args.fan_pin)

app = Flask(__name__)

@app.route("/ac/<on_off>", methods=["POST", "GET"])
def ac(on_off: str):
    on = on_off == 'on'
    AC_pin.set(on)
    return on_off

@app.route("/heater/<on_off>", methods=["POST", "GET"])
def heater(on_off: str):
    on = on_off == 'on'
    heater_pin.set(on)
    return on_off

@app.route("/fan/<on_off>", methods=["POST", "GET"])
def fan(on_off: str):
    on = on_off == 'on'
    fan_pin.set(on)
    return on_off

if __name__ == "__main__":
    app.run(debug=True, port=args.port)
<commit_msg>Revert to Python 2<commit_after>import argparse

from flask import Flask
from src.pin import OutputPin

parser = argparse.ArgumentParser(description='Controls an AC System')
parser.add_argument("-p", "--port", type=int, help="the port to run server on", default=5003)
parser.add_argument("-a", "--AC_pin", type=int, help="the pin to turn on to activate the AC system", default=4)
parser.add_argument("-h", "--heater_pin", type=int, help="the pin to turn on to activate the heater", default=5)
parser.add_argument("-f", "--fan_pin", type=int, help="the pin to turn on to activate the fan", default=6)

args = parser.parse_args()

AC_pin = OutputPin(args.AC_pin)
heater_pin = OutputPin(args.heater_pin)
fan_pin = OutputPin(args.fan_pin)

app = Flask(__name__)

@app.route("/ac/<on_off>", methods=["POST", "GET"])
def ac(on_off):
    on = on_off == 'on'
    AC_pin.set(on)
    return on_off

@app.route("/heater/<on_off>", methods=["POST", "GET"])
def heater(on_off):
    on = on_off == 'on'
    heater_pin.set(on)
    return on_off

@app.route("/fan/<on_off>", methods=["POST", "GET"])
def fan(on_off):
    on = on_off == 'on'
    fan_pin.set(on)
    return on_off

if __name__ == "__main__":
    app.run(debug=True, port=args.port)
<|endoftext|>


<commit_before>import numpy as np

from hyperion.util.functions import B_nu, random_id
from hyperion.util.functions import FreezableClass
from hyperion.util.integrate import integrate_loglog

import atpy

class Source(FreezableClass):

    def __init__(self, luminosity=None, spectrum=None, temperature=None,
                 name=None, peeloff=True):

        self.luminosity = luminosity
        self.spectrum = None
        self.temperature = None

        if spectrum:
            self.set_spectrum(spectrum)
        elif temperature:
            self.set_temperature(temperature)

        if name:
            self.name = name
        else:
            self.name = random_id(length=8)

        self.peeloff = peeloff

        self._freeze()

    def check_all_set(self):
        if self.luminosity is None:
            raise Exception("luminosity is not set")

    def set_spectrum(self, spectrum):
        self.spectrum = spectrum

    def set_temperature(self, temperature):
        self.temperature = temperature

    def set_luminosity(self, luminosity):
        self.luminosity = luminosity

    def get_spectrum(self):
        self.check_all_set()
        if self.spectrum is not None:
            if isinstance(self.spectrum, atpy.Table):
                nu, fnu = self.spectrum.nu, self.spectrum.fnu
            elif type(self.spectrum) in [tuple, list]:
                nu, fnu = self.spectrum
            else:
                raise Exception("Spectrum should be tuple or ATpy table")
        elif self.temperature:
            nu = np.logspace(np.log10(3.e9), np.log10(3.e16))
            fnu = B_nu(nu, self.temperature)
        else:
            raise Exception("Not implemented")
        norm = integrate_loglog(nu, fnu)
        return nu, fnu / norm * self.luminosity

    def write_spectrum(self, handle):
        if self.spectrum:
            handle.attrs['spectrum'] = 'spectrum'
            if isinstance(self.spectrum, atpy.Table):
                self.spectrum.table_name = 'Spectrum'
                self.spectrum.write(handle, type='hdf5')
            else:
                table = atpy.Table(name='Spectrum')
                table.add_column('nu', self.spectrum[0])
                table.add_column('fnu', self.spectrum[1])
                table.write(handle, type='hdf5')
        elif self.temperature:
            handle.attrs['spectrum'] = 'temperature'
            handle.attrs['temperature'] = self.temperature
        else:
            handle.attrs['spectrum'] = 'lte'

    def has_lte_spectrum(self):
        return self.spectrum is None and self.temperature is None

class SpotSource(Source):

    def __init__(self, luminosity=None, longitude=None, latitude=None,
                 radius=None, spectrum=None, temperature=None, name=None, peeloff=True):

        self.longitude = longitude
        self.latitude = latitude
        self.radius = radius
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.longitude is None:
            raise Exception("longitude is not set")
        if self.latitude is None:
            raise Exception("latitude is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.has_lte_spectrum():
            raise Exception("Spot source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'spot'
        g.attrs['longitude'] = self.longitude
        g.attrs['latitude'] = self.latitude
        g.attrs['radius'] = self.radius
        Source.write(self, g)

class PointSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), spectrum=None,
                 temperature=None, name=None, peeloff=True):
        self.position = position
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.has_lte_spectrum():
            raise Exception("Point source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'point'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        Source.write(self, g)

class SphericalSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 limb_darkening=False, spectrum=None, temperature=None,
                 name=None, peeloff=True):
        self.position = position
        self.radius = radius
        self.limb = limb_darkening
        self.spots = []
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.limb is None:
            raise Exception("limb is not set")
        if self.has_lte_spectrum():
            raise Exception("Spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'sphere'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        if self.limb:
            g.attrs['limb'] = 'yes'
        else:
            g.attrs['limb'] = 'no'
        Source.write(self, g)

        for i, spot in enumerate(self.spots):
            spot.write(g, 'Spot %i' % i)

    def add_spot(self, *args, **kwargs):
        self.spots.append(SpotSource(*args, **kwargs))

class ExternalSphericalSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 spectrum=None, temperature=None, name=None, peeloff=True):
        self.position = position
        self.radius = radius
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("r is not set")
        if self.has_lte_spectrum():
            raise Exception("External spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'extern_sph'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        Source.write(self, g)

class ExternalBoxSource(Source):

    def __init__(self, luminosity=None, bounds=None,
                 spectrum=None, temperature=None, name=None, peeloff=True):
        self.bounds = bounds
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.bounds is None:
            raise Exception("bounds are not set")
        if self.has_lte_spectrum():
            raise Exception("External spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'extern_box'
        g.attrs['xmin'] = self.bounds[0][0]
        g.attrs['xmax'] = self.bounds[0][1]
        g.attrs['ymin'] = self.bounds[1][0]
        g.attrs['ymax'] = self.bounds[1][1]
        g.attrs['zmin'] = self.bounds[2][0]
        g.attrs['zmax'] = self.bounds[2][1]
        Source.write(self, g)

class MapSource(Source):

    def __init__(self, luminosity=None, map=None, spectrum=None,
                 temperature=None, name=None, peeloff=True):
        self.map = map
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.map is None:
            raise Exception("map is not set")
        if np.all(self.map == 0.):
            raise Exception("Luminosity map is zero everywhere")
        Source.check_all_set(self)

    def write(self, handle, name, grid, compression=True, map_dtype=float):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'map'
        grid.write_physical_array(g, self.map, "Luminosity map", dust=False,
                                  compression=compression,
                                  physics_dtype=map_dtype)
        Source.write(self, g)

class PlaneParallelSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 direction=(0., 0.), spectrum=None,
                 temperature=None, name=None, peeloff=False):
        if peeloff:
            raise Exception("Cannot peeloff plane parallel source")
        self.position = position
        self.radius = range
        self.direction = direction
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.direction is None:
            raise Exception("direction is not set")
        if self.has_lte_spectrum():
            raise Exception("Point source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'plane_parallel'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        g.attrs['theta'] = self.direction[0]
        g.attrs['phi'] = self.direction[1]
        Source.write(self, g)
<commit_msg>Finish committing changes to sources to specify whether to peeloff<commit_after>import numpy as np

from hyperion.util.functions import B_nu, random_id
from hyperion.util.functions import FreezableClass
from hyperion.util.integrate import integrate_loglog

import atpy

class Source(FreezableClass):

    def __init__(self, luminosity=None, spectrum=None, temperature=None,
                 name=None, peeloff=True):

        self.luminosity = luminosity
        self.spectrum = None
        self.temperature = None

        if spectrum:
            self.set_spectrum(spectrum)
        elif temperature:
            self.set_temperature(temperature)

        if name:
            self.name = name
        else:
            self.name = random_id(length=8)

        self.peeloff = peeloff

        self._freeze()

    def check_all_set(self):
        if self.luminosity is None:
            raise Exception("luminosity is not set")

    def set_spectrum(self, spectrum):
        self.spectrum = spectrum

    def set_temperature(self, temperature):
        self.temperature = temperature

    def set_luminosity(self, luminosity):
        self.luminosity = luminosity

    def get_spectrum(self):
        self.check_all_set()
        if self.spectrum is not None:
            if isinstance(self.spectrum, atpy.Table):
                nu, fnu = self.spectrum.nu, self.spectrum.fnu
            elif type(self.spectrum) in [tuple, list]:
                nu, fnu = self.spectrum
            else:
                raise Exception("Spectrum should be tuple or ATpy table")
        elif self.temperature:
            nu = np.logspace(np.log10(3.e9), np.log10(3.e16))
            fnu = B_nu(nu, self.temperature)
        else:
            raise Exception("Not implemented")
        norm = integrate_loglog(nu, fnu)
        return nu, fnu / norm * self.luminosity

    def write(self, handle):
        handle.attrs['luminosity'] = self.luminosity
        handle.attrs['peeloff'] = 'yes' if self.peeloff else 'no'
        if self.spectrum:
            handle.attrs['spectrum'] = 'spectrum'
            if isinstance(self.spectrum, atpy.Table):
                self.spectrum.table_name = 'Spectrum'
                self.spectrum.write(handle, type='hdf5')
            else:
                table = atpy.Table(name='Spectrum')
                table.add_column('nu', self.spectrum[0])
                table.add_column('fnu', self.spectrum[1])
                table.write(handle, type='hdf5')
        elif self.temperature:
            handle.attrs['spectrum'] = 'temperature'
            handle.attrs['temperature'] = self.temperature
        else:
            handle.attrs['spectrum'] = 'lte'

    def has_lte_spectrum(self):
        return self.spectrum is None and self.temperature is None

class SpotSource(Source):

    def __init__(self, luminosity=None, longitude=None, latitude=None,
                 radius=None, spectrum=None, temperature=None, name=None, peeloff=True):

        self.longitude = longitude
        self.latitude = latitude
        self.radius = radius
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.longitude is None:
            raise Exception("longitude is not set")
        if self.latitude is None:
            raise Exception("latitude is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.has_lte_spectrum():
            raise Exception("Spot source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'spot'
        g.attrs['longitude'] = self.longitude
        g.attrs['latitude'] = self.latitude
        g.attrs['radius'] = self.radius
        Source.write(self, g)

class PointSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), spectrum=None,
                 temperature=None, name=None, peeloff=True):
        self.position = position
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.has_lte_spectrum():
            raise Exception("Point source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'point'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        Source.write(self, g)

class SphericalSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 limb_darkening=False, spectrum=None, temperature=None,
                 name=None, peeloff=True):
        self.position = position
        self.radius = radius
        self.limb = limb_darkening
        self.spots = []
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.limb is None:
            raise Exception("limb is not set")
        if self.has_lte_spectrum():
            raise Exception("Spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'sphere'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        if self.limb:
            g.attrs['limb'] = 'yes'
        else:
            g.attrs['limb'] = 'no'
        Source.write(self, g)

        for i, spot in enumerate(self.spots):
            spot.write(g, 'Spot %i' % i)

    def add_spot(self, *args, **kwargs):
        self.spots.append(SpotSource(*args, **kwargs))

class ExternalSphericalSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 spectrum=None, temperature=None, name=None, peeloff=True):
        self.position = position
        self.radius = radius
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("r is not set")
        if self.has_lte_spectrum():
            raise Exception("External spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'extern_sph'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        Source.write(self, g)

class ExternalBoxSource(Source):

    def __init__(self, luminosity=None, bounds=None,
                 spectrum=None, temperature=None, name=None, peeloff=True):
        self.bounds = bounds
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.bounds is None:
            raise Exception("bounds are not set")
        if self.has_lte_spectrum():
            raise Exception("External spherical source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'extern_box'
        g.attrs['xmin'] = self.bounds[0][0]
        g.attrs['xmax'] = self.bounds[0][1]
        g.attrs['ymin'] = self.bounds[1][0]
        g.attrs['ymax'] = self.bounds[1][1]
        g.attrs['zmin'] = self.bounds[2][0]
        g.attrs['zmax'] = self.bounds[2][1]
        Source.write(self, g)

class MapSource(Source):

    def __init__(self, luminosity=None, map=None, spectrum=None,
                 temperature=None, name=None, peeloff=True):
        self.map = map
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.map is None:
            raise Exception("map is not set")
        if np.all(self.map == 0.):
            raise Exception("Luminosity map is zero everywhere")
        Source.check_all_set(self)

    def write(self, handle, name, grid, compression=True, map_dtype=float):

        self.check_all_set()

        g = handle.create_group(name)
        g.attrs['type'] = 'map'
        grid.write_physical_array(g, self.map, "Luminosity map", dust=False,
                                  compression=compression,
                                  physics_dtype=map_dtype)
        Source.write(self, g)

class PlaneParallelSource(Source):

    def __init__(self, luminosity=None, position=(0., 0., 0.), radius=None,
                 direction=(0., 0.), spectrum=None,
                 temperature=None, name=None, peeloff=False):
        if peeloff:
            raise Exception("Cannot peeloff plane parallel source")
        self.position = position
        self.radius = range
        self.direction = direction
        Source.__init__(self, luminosity=luminosity, spectrum=spectrum,
                        temperature=temperature, name=name, peeloff=peeloff)

    def check_all_set(self):
        if self.position is None:
            raise Exception("position is not set")
        if self.radius is None:
            raise Exception("radius is not set")
        if self.direction is None:
            raise Exception("direction is not set")
        if self.has_lte_spectrum():
            raise Exception("Point source cannot have LTE spectrum")
        Source.check_all_set(self)

    def write(self, handle, name):
        self.check_all_set()
        g = handle.create_group(name)
        g.attrs['type'] = 'plane_parallel'
        g.attrs['x'] = self.position[0]
        g.attrs['y'] = self.position[1]
        g.attrs['z'] = self.position[2]
        g.attrs['r'] = self.radius
        g.attrs['theta'] = self.direction[0]
        g.attrs['phi'] = self.direction[1]
        Source.write(self, g)
<|endoftext|>