<reponame>gkmngrgn/gork<filename>gork/palette.py
import scipy.spatial as sp
from gork.structs import Color

COLORS = [
    Color("#ffffff", name="Black"),
    Color("#f7f7f7", name="Maroon"),
    Color("#efefef", name="Green"),
    Color("#e7e7e7", name="Olive"),
    Color("#dfdfdf", name="Navy"),
    Color("#d7d7d7", name="Purple"),
    Color("#cfcfcf", name="Teal"),
    Color("#c7c7c7", name="Silver"),
    Color("#bfbfbf", name="Grey"),
    Color("#b7b7b7", name="Red"),
    Color("#afafaf", name="Lime"),
    Color("#a7a7a7", name="Yellow"),
    Color("#9f9f9f", name="Blue"),
    Color("#979797", name="Fuchsia"),
    Color("#8f8f8f", name="Aqua"),
    Color("#878787", name="White"),
    Color("#7f7f7f", name="Grey0"),
    Color("#777777", name="NavyBlue"),
    Color("#6f6f6f", name="DarkBlue"),
    Color("#676767", name="Blue3"),
    Color("#5f5f5f", name="Blue3"),
    Color("#575757", name="Blue1"),
    Color("#4f4f4f", name="DarkGreen"),
    Color("#474747", name="DeepSkyBlue4"),
    Color("#3f3f3f", name="DeepSkyBlue4"),
    Color("#373737", name="DeepSkyBlue4"),
    Color("#2f2f2f", name="DodgerBlue3"),
    Color("#272727", name="DodgerBlue2"),
    Color("#1f1f1f", name="Green4"),
    Color("#171717", name="SpringGreen4"),
    Color("#0f0f0f", name="Turquoise4"),
    Color("#070707", name="DeepSkyBlue3"),
    Color("#000000", name="DeepSkyBlue3"),
    Color("#bfa78f", name="DodgerBlue1"),
    Color("#b7a088", name="Green3"),
    Color("#af9880", name="SpringGreen3"),
    Color("#a79078", name="DarkCyan"),
    Color("#9f8971", name="LightSeaGreen"),
    Color("#968169", name="DeepSkyBlue2"),
    Color("#8e7961", name="DeepSkyBlue1"),
    Color("#86725a", name="Green3"),
    Color("#7e6a52", name="SpringGreen3"),
    Color("#75624a", name="SpringGreen2"),
    Color("#6d5a42", name="Cyan3"),
    Color("#65533b", name="DarkTurquoise"),
    Color("#5d4b33", name="Turquoise2"),
    Color("#54432b", name="Green1"),
    Color("#4c3c24", name="SpringGreen2"),
    Color("#43331b", name="SpringGreen1"),
    Color("#bf7b4b", name="MediumSpringGreen"),
    Color("#b37347", name="Cyan2"),
    Color("#ab6f43", name="Cyan1"),
    Color("#a36b3f", name="DarkRed"),
    Color("#9b633b", name="DeepPink4"),
    Color("#8f5f37", name="Purple4"),
    Color("#875733", name="Purple4"),
    Color("#7f532f", name="Purple3"),
    Color("#774f2b", name="BlueViolet"),
    Color("#6b4727", name="Orange4"),
    Color("#5f4323", name="Grey37"),
    Color("#533f1f", name="MediumPurple4"),
    Color("#4b371b", name="SlateBlue3"),
    Color("#3f2f17", name="SlateBlue3"),
    Color("#332b13", name="RoyalBlue1"),
    Color("#2b230f", name="Chartreuse4"),
    Color("#ffebdf", name="DarkSeaGreen4"),
    Color("#ffe3d3", name="PaleTurquoise4"),
    Color("#ffdbc7", name="SteelBlue"),
    Color("#ffd3bb", name="SteelBlue3"),
    Color("#ffcfb3", name="CornflowerBlue"),
    Color("#ffc7a7", name="Chartreuse3"),
    Color("#ffbf9b", name="DarkSeaGreen4"),
    Color("#ffbb93", name="CadetBlue"),
    Color("#ffb383", name="CadetBlue"),
    Color("#f7ab7b", name="SkyBlue3"),
    Color("#efa373", name="SteelBlue1"),
    Color("#e79b6b", name="Chartreuse3"),
    Color("#df9363", name="PaleGreen3"),
    Color("#d78b5b", name="SeaGreen3"),
    Color("#cf8353", name="Aquamarine3"),
    Color("#cb7f4f", name="MediumTurquoise"),
    Color("#ffeedc", name="SteelBlue1"),
    Color("#ffdcb9", name="Chartreuse2"),
    Color("#ffcb97", name="SeaGreen2"),
    Color("#ffb975", name="SeaGreen1"),
    Color("#ffa855", name="SeaGreen1"),
    Color("#ff9736", name="Aquamarine1"),
    Color("#ff8619", name="DarkSlateGray2"),
    Color("#ff7500", name="DarkRed"),
    Color("#f36d00", name="DeepPink4"),
    Color("#e56500", name="DarkMagenta"),
    Color("#d85d00", name="DarkMagenta"),
    Color("#cb5500", name="DarkViolet"),
    Color("#be4d00", name="Purple"),
    Color("#b14500", name="Orange4"),
    Color("#a43d00", name="LightPink4"),
    Color("#973600", name="Plum4"),
    Color("#ffffef", name="MediumPurple3"),
    Color("#ffffcf", name="MediumPurple3"),
    Color("#ffffaf", name="SlateBlue1"),
    Color("#ffff8f", name="Yellow4"),
    Color("#ffff6f", name="Wheat4"),
    Color("#ffff4f", name="Grey53"),
    Color("#ffff2f", name="LightSlateGrey"),
    Color("#ffff0f", name="MediumPurple"),
    Color("#ffff00", name="LightSlateBlue"),
    Color("#cfcf00", name="Yellow4"),
    Color("#afaf00", name="DarkOliveGreen3"),
    Color("#8f8f00", name="DarkSeaGreen"),
    Color("#6f6f00", name="LightSkyBlue3"),
    Color("#4f4f00", name="LightSkyBlue3"),
    Color("#2f2f00", name="SkyBlue2"),
    Color("#0f0f00", name="Chartreuse2"),
    Color("#ffff73", name="DarkOliveGreen3"),
    Color("#ebdb57", name="PaleGreen3"),
    Color("#d7bb43", name="DarkSeaGreen3"),
    Color("#c39b2f", name="DarkSlateGray3"),
    Color("#af7b1f", name="SkyBlue1"),
    Color("#9b5b13", name="Chartreuse1"),
    Color("#874307", name="LightGreen"),
    Color("#732b00", name="LightGreen"),
    Color("#ffdfdf", name="PaleGreen1"),
    Color("#ffbfbf", name="Aquamarine1"),
    Color("#ff9f9f", name="DarkSlateGray1"),
    Color("#ff7f7f", name="Red3"),
    Color("#ff5f5f", name="DeepPink4"),
    Color("#ff3f3f", name="MediumVioletRed"),
    Color("#ff1f1f", name="Magenta3"),
    Color("#ff0000", name="DarkViolet"),
    Color("#ef0000", name="Purple"),
    Color("#df0000", name="DarkOrange3"),
    Color("#cf0000", name="IndianRed"),
    Color("#bf0000", name="HotPink3"),
    Color("#af0000", name="MediumOrchid3"),
    Color("#9f0000", name="MediumOrchid"),
    Color("#8f0000", name="MediumPurple2"),
    Color("#7f0000", name="DarkGoldenrod"),
    Color("#6f0000", name="LightSalmon3"),
    Color("#5f0000", name="RosyBrown"),
    Color("#4f0000", name="Grey63"),
    Color("#3f0000", name="MediumPurple2"),
    Color("#2f0000", name="MediumPurple1"),
    Color("#1f0000", name="Gold3"),
    Color("#0f0000", name="DarkKhaki"),
    Color("#ffb7b7", name="NavajoWhite3"),
    Color("#f3a3a3", name="Grey69"),
    Color("#e78f8f", name="LightSteelBlue3"),
    Color("#db7b7b", name="LightSteelBlue"),
    Color("#cb6b6b", name="Yellow3"),
    Color("#bf5b5b", name="DarkOliveGreen3"),
    Color("#b34f4f", name="DarkSeaGreen3"),
    Color("#a73f3f", name="DarkSeaGreen2"),
    Color("#8e2e00", name="LightCyan3"),
    Color("#862700", name="LightSkyBlue1"),
    Color("#7e2000", name="GreenYellow"),
    Color("#751900", name="DarkOliveGreen2"),
    Color("#6d1200", name="PaleGreen1"),
    Color("#650b00", name="DarkSeaGreen2"),
    Color("#5d0500", name="DarkSeaGreen1"),
    Color("#550000", name="PaleTurquoise1"),
    Color("#77ff4f", name="Red3"),
    Color("#70f04b", name="DeepPink3"),
    Color("#69e046", name="DeepPink3"),
    Color("#61d041", name="Magenta3"),
    Color("#5ac03c", name="Magenta3"),
    Color("#52b037", name="Magenta2"),
    Color("#4ba032", name="DarkOrange3"),
    Color("#43902d", name="IndianRed"),
    Color("#3c8028", name="HotPink3"),
    Color("#357023", name="HotPink2"),
    Color("#2d601e", name="Orchid"),
    Color("#265019", name="MediumOrchid1"),
    Color("#1e4014", name="Orange3"),
    Color("#17300f", name="LightSalmon3"),
    Color("#0f200a", name="LightPink3"),
    Color("#070f04", name="Pink3"),
    Color("#deffa8", name="Plum3"),
    Color("#c7e494", name="Violet"),
    Color("#adc880", name="Gold3"),
    Color("#95ad6b", name="LightGoldenrod3"),
    Color("#7c9258", name="Tan"),
    Color("#647744", name="MistyRose3"),
    Color("#4a5a30", name="Thistle3"),
    Color("#323f1d", name="Plum2"),
    Color("#00ff00", name="Yellow3"),
    Color("#00df00", name="Khaki3"),
    Color("#00bf00", name="LightGoldenrod2"),
    Color("#009f00", name="LightYellow3"),
    Color("#007f00", name="Grey84"),
    Color("#005f00", name="LightSteelBlue1"),
    Color("#003f00", name="Yellow2"),
    Color("#001f00", name="DarkOliveGreen1"),
    Color("#ff6fff", name="DarkOliveGreen1"),
    Color("#ff00ff", name="DarkSeaGreen1"),
    Color("#df00df", name="Honeydew2"),
    Color("#bf00bf", name="LightCyan1"),
    Color("#9f009f", name="Red1"),
    Color("#7f007f", name="DeepPink2"),
    Color("#5f005f", name="DeepPink1"),
    Color("#3f003f", name="DeepPink1"),
    Color("#e9e9f3", name="Magenta2"),
    Color("#c4c4e1", name="Magenta1"),
    Color("#9d9dce", name="OrangeRed1"),
    Color("#7777bb", name="IndianRed1"),
    Color("#5454a7", name="IndianRed1"),
    Color("#414183", name="HotPink"),
    Color("#2e2e5c", name="HotPink"),
    Color("#1b1b34", name="MediumOrchid1"),
    Color("#d5f1ff", name="DarkOrange"),
    Color("#bfebff", name="Salmon1"),
    Color("#aae3ff", name="LightCoral"),
    Color("#95ddff", name="PaleVioletRed1"),
    Color("#80d6ff", name="Orchid2"),
    Color("#6acfff", name="Orchid1"),
    Color("#55c8ff", name="Orange1"),
    Color("#3fbfff", name="SandyBrown"),
    Color("#379ddf", name="LightSalmon1"),
    Color("#2f8fbf", name="LightPink1"),
    Color("#27779f", name="Pink1"),
    Color("#1f5f7f", name="Plum1"),
    Color("#00bfbf", name="Gold1"),
    Color("#007f7f", name="LightGoldenrod2"),
    Color("#005f5f", name="LightGoldenrod2"),
    Color("#003f3f", name="NavajoWhite1"),
    Color("#e7e7ff", name="MistyRose1"),
    Color("#c6c6ff", name="Thistle1"),
    Color("#adadff", name="Yellow1"),
    Color("#8c8cff", name="LightGoldenrod1"),
    Color("#7373ff", name="Khaki1"),
    Color("#5252ff", name="Wheat1"),
    Color("#3131ff", name="Cornsilk1"),
    Color("#1818ff", name="Grey100"),
    Color("#0000ff", name="Grey3"),
    Color("#0000e7", name="Grey7"),
    Color("#0000ce", name="Grey11"),
    Color("#0000b5", name="Grey15"),
    Color("#00009c", name="Grey19"),
    Color("#000084", name="Grey23"),
    Color("#00006b", name="Grey27"),
    Color("#000052", name="Grey30"),
    Color("#00004f", name="Grey35"),
    Color("#00003f", name="Grey39"),
    Color("#000037", name="Grey42"),
    Color("#000027", name="Grey46"),
    Color("#00001f", name="Grey50"),
    Color("#00000f", name="Grey54"),
    Color("#000007", name="Grey58"),
    Color("#00ffff", name="Grey62"),
    Color("#cf7fcf", name="Grey66"),
    Color("#b76fb7", name="Grey70"),
    Color("#9f5f9f", name="Grey74"),
    Color("#874f87", name="Grey78"),
    Color("#6f3f6f", name="Grey82"),
    Color("#572f57", name="Grey85"),
    Color("#3f1f3f", name="Grey89"),
    Color("#270f27", name="Grey93"),
]
COLOR_COUNT = len(COLORS)
PALETTE = [c.as_rgb.as_tuple for c in COLORS]
COLOR_TREE = sp.KDTree(PALETTE)
DEFAULT_WIDTH = 78  # FIXME: remove this line


<reponame>dpazel/music_rep<filename>transformation/shift/t_shift_example.py
"""
File: T_shift_example.py
Purpose: examples of t_shift applied to a melody.
"""
from fractions import Fraction

from tonalmodel.interval import Interval as TonalInterval
from tonalmodel.modality import ModalityType
from transformation.shift.t_shift import TShift
from tonalmodel.tonality import Tonality

def duration_ltr(duration):
    if duration.duration == Fraction(1, 8):
        return 'i'
    elif duration.duration == Fraction(1, 4):
        return 'q'
    elif duration.duration == Fraction(1, 2):
        return 'h'
    elif duration.duration == Fraction(1):
        return 'w'
    return '>'

def print_line(line):
    notes = line.get_all_notes()
    prior_octave = None
    prior_duration = None
    note_annotations = list()
    for note in notes:
        annotation = ''
        d = duration_ltr(note.duration)
        if d != prior_duration:
            annotation += d
            prior_duration = d
        annotation += str(note.diatonic_pitch.diatonic_tone.diatonic_symbol)
        o = note.diatonic_pitch.octave
        if o != prior_octave:
            annotation += ":" + str(o)
            prior_octave = o
        note_annotations.append(annotation)
    s = ' '.join(annotation for annotation in note_annotations)
    print(s)

def print_hct(hct):
    hc_annotations = list()
    for hc in hct.hc_list():
        s = '<{0}: {1}>({2})'.format(hc.tonality, hc.chord, hc.duration)
        hc_annotations.append(s)
    hcs = ' '.join(hc_str for hc_str in hc_annotations)
    print(hcs)

def simple_shift_example():
    print('----- Simple Shift Example -----')
    source_expression = '{<C-Major: I> iC:4 C qD E <:IV> iF G hA <:V> ig b qf g <:VI> ie e qd ic d <:I> h@c}'

    t_shift = TShift.create(source_expression, TonalInterval.parse('M:3'))
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift up M:3')
    target_line, target_hct = t_shift.apply()

    print_line(target_line)
    print_hct(target_hct)
    print()

    t_shift = TShift.create(source_expression, TonalInterval.parse('-m:2'))

    target_line, target_hct = t_shift.apply()

    print('Shift down m:2')
    print_line(target_line)
    print_hct(target_hct)
    print()

def shift_change_modality():
    print('----- Shift Change Modality Example -----')
    source_expression = '{<C-Major: I> iC:4 C qD E <:IV> iF G hA <:V> ig b qf g <:VI> ie e qd ic d <:i> h@c}'

    t_shift = TShift.create(source_expression, TonalInterval.parse('P:4'))
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift up P:4, modality MelodicMinor')
    target_line, target_hct = t_shift.apply(range_modality_type=ModalityType.MelodicMinor)

    print_line(target_line)
    print_hct(target_hct)
    print()

    print('Shift up P:4, modality NaturalMinor')
    target_line, target_hct = t_shift.apply(range_modality_type=ModalityType.NaturalMinor)

    print_line(target_line)
    print_hct(target_hct)
    print()

def shift_change_modal_index():
    print('----- Shift Change Modal Index Example -----')
    source_expression = '{<C-Major: I> iC:4 C qD E <:IV> iF G hA <:V> ig b qf g <:VI> ie e qd ic d <:i> h@c}'

    t_shift = TShift.create(source_expression)
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift to modal index 4 (Mixolydian)')
    target_line, target_hct = t_shift.apply(modal_index=4)

    print_line(target_line)
    print_hct(target_hct)
    print()

    print('Shift to C as modal index 3 (lydian) on a melodic minor scale (G)')
    target_line, target_hct = t_shift.apply(range_modality_type=ModalityType.MelodicMinor, modal_index=3)
    print_line(target_line)
    print_hct(target_hct)
    print()

def shift_change_modal_index_modality_and_shift():
    print('----- Shift Change Modal Index, modality, shift Example -----')
    source_expression = '{<C-Major: I> iC:4 C qD E <:IV> iF G hA <:V> ig b qf g <:VI> ie e qd ic d <:i> h@c}'

    t_shift = TShift.create(source_expression)
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift to modal index 1 (dorian)')
    target_line, target_hct = t_shift.apply(root_shift_interval=TonalInterval.parse('M:2'), modal_index=1)

    print_line(target_line)
    print_hct(target_hct)
    print()

    t_shift = TShift(target_line, target_hct)

    print('Shift P:4 to modal index 2 (phrygian) of MelodicMinor')
    target_line, target_hct = t_shift.apply(root_shift_interval=TonalInterval.parse('P:4'),
                                            range_modality_type=ModalityType.MelodicMinor, modal_index=2)

    print_line(target_line)
    print_hct(target_hct)
    print()

def example():
    print('----- Book example of shifted tonality on modal not 0 -----')
    source_expression = '{<D-Major(1): I> iE:4 F# G A qE B}'

    t_shift = TShift.create(source_expression,
                            TonalInterval.parse('P:4'))
    target_line, target_hct = t_shift.apply(range_modality_type=ModalityType.MelodicMinor, modal_index=2)
    print(t_shift.source_hct)
    print(target_line)
    print(target_hct)

def example1():
    print('----- Book example of shifted secondary tonality  -----')
    source_expression = '{<C-Major: V/iii> iD#:4 F# G A qD#:5 B:4}'

    t_shift = TShift.create(source_expression, TonalInterval.parse('P:5'))
    target_line, target_hct = t_shift.apply(range_modality_type=ModalityType.MelodicMinor)
    print(t_shift.source_hct)
    print(target_line)
    print(target_hct)

def example2():
    print('----- Debug meaning of modal index change and hct -----')

    # example tonality with modal index
    # Create a harmonic minor tonality of some basis root which as Mixolydian has F as the root.
    #       The answer is Bb-HarmonicMinor F(4)

    source_expression = '{<C-Major: I> iC:4}'

    t_shift = TShift.create(source_expression)
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift to modal index 4 (Mixolydian)')
    # This makes C the Mixolydian of F-Major
    target_line, target_hct = t_shift.apply(modal_index=4)
    print_line(target_line)
    print_hct(target_hct)
    print()

    # if you wanted G Mixolydian based on C
    print('Shift as if moving tomodal index 4 (Mixolydian) in C')
    target_line, target_hct = t_shift.apply(
           root_shift_interval=TonalInterval.parse('P:5'), modal_index=4)

    print_line(target_line)
    print_hct(target_hct)
    print()

#simple_shift_example()
#shift_change_modality()
shift_change_modal_index()
#shift_change_modal_index_modality_and_shift()
#example2()
#example1()


<filename>ChrisFuncs/ChrisFuncs.py<gh_stars>0
# Import smorgasbord
import pdb
import pdb
"""from IPython import get_ipython
get_ipython().run_line_magic('pdb','on')"""
import sys
import os
#sys.path.append( os.path.split( os.path.realpath(__file__) )[:-1][0] )
#sys.path.append( os.path.split( os.path.split( os.path.realpath(__file__) )[:-1][0] )[:-1][0] )
#sys.path.insert(0, '../')
import numpy as np
import scipy.stats
import scipy.ndimage
import scipy.ndimage.measurements
import scipy.spatial
#import matplotlib
#matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.cm
import matplotlib.patches
import astropy
astropy.log.setLevel('ERROR')
import astropy.io.fits
import astropy.wcs
import astropy.convolution
import astropy.coordinates
import astropy.units
import astroquery.irsa_dust
import shutil
import wget
import glob
import time
import re
import copy

# A python 2/3 compatability hack for stirng type handling
try:
  basestring
except NameError:
  basestring = str

# Function to sum all elements in an ellipse centred on the middle of a given array
# Args: Array, semi-major axis (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse
# Returns: Numpy array containing the sum of the pixel values in the ellipse, total number of pixels counted, and an array containing the pixel values
def EllipseSum(array, rad, axial_ratio, angle, i_centre, j_centre):
    from . import Photom
    return Photom.EllipseSum(array, rad, axial_ratio, angle, i_centre, j_centre)

# Function to sum all elements in an annulus centred upon the middle of the given array
# Args: Array, semi-major axis of inside edge of annulus (pix), width of annulus (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse
# Returns: Numpy array containing the sum of the pixel values in the annulus, the total number of pixels counted, and an array containing the pixel values
def AnnulusSum(array, rad_inner, width, axial_ratio, angle, i_centre, j_centre):
    from . import Photom
    return Photom.AnnulusSum(array, rad_inner, width, axial_ratio, angle, i_centre, j_centre)

# Function to make annular photometry faster by pre-preparing arrays of transposed coords that are to be repeatedly used
# Args: Array, semi-major axis of inside edge of annulus (pix), width of annulus (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse
# Returns: List containing i & j transposed coords
def AnnulusQuickPrepare(array, angle, i_centre, j_centre):
    from . import Photom
    return Photom.AnnulusQuickPrepare(array, angle, i_centre, j_centre)

# Function to sum all elements in an annulus centred upon the middle of the given array, usingpre-prepared transposed coord arrays
# Args: Array, semi-major axis of inside edge of annulus (pix), width of annulus (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse, i & j transposed coord arrays
# Returns: Numpy array containing the sum of the pixel values in the annulus, the total number of pixels counted, and an array containing the pixel values
def AnnulusQuickSum(array, rad_inner, width, axial_ratio, angle, i_centre, j_centre, i_trans, j_trans):
    from . import Photom
    return Photom.AnnulusQuickSum(array, rad_inner, width, axial_ratio, angle, i_centre, j_centre, i_trans, j_trans)

# Function to sum all elements in an annulus centred upon the middle of the given array, using pre-prepared transposed coord arrays
# Args: Array, semi-major axis of ellipse (pix), position angle (deg), i & j coords of centre of ellipse, i & j transposed coord arrays
# Returns: Numpy array containing the sum of the pixel values in the ellipse, the total number of pixels counted, and an array containing the pixel values
def EllipseQuickSum(array, rad, axial_ratio, angle, i_centre, j_centre, i_trans, j_trans):
    from . import Photom
    return Photom.EllipseQuickSum(array, rad, axial_ratio, angle, i_centre, j_centre, i_trans, j_trans)

# Function to return a mask identifying all pixels within an ellipse of given parameters
# Args: Array, semi-major axis (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse
# Returns: Mask array of same dimensions as input array where pixels that lie within ellipse have value 1
def EllipseMask(array, rad, axial_ratio, angle, i_centre, j_centre):
    from . import Photom
    return Photom.EllipseMask(array, rad, axial_ratio, angle, i_centre, j_centre)

# Function to sum all pixel elements inside a given circle... the old-fashioned way
# Args: Array to be used, i & j coordinates of centre of circle, radius of circle
# Returns: Sum of elements within circle, number of pixels within circle
def CircleSum(fits, i_centre, j_centre, r):
    from . import Photom
    return Photom.CircleSum(fits, i_centre, j_centre, r)

# Function to sum all pixel elements inside a given circle... the old-fashioned way
# Args: Array to be used, i & j coordinates of centre of circle, radius of circle
# Returns: Sum of elements within circle, number of pixels within circle
def CircleAnnulusSum(fits, i_centre, j_centre, r, width):
    from . import Photom
    return Photom.CircleAnnulusSum(fits, i_centre, j_centre, r, width)

# Function to sum all elements in an ellipse centred on the middle of an array that has been resized to allow better pixel sampling
# Args: Array, semi-major axis (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse, upscaling factor
# Returns: Numpy array containing the sum of the pixel values in the ellipse, the total number of pixels counted, and an array containing the pixel values
def EllipseSumUpscale(cutout, rad, axial_ratio, angle, i_centre, j_centre, upscale=1):
    from . import Photom
    return Photom.EllipseSumUpscale(cutout, rad, axial_ratio, angle, i_centre, j_centre, upscale=1)

# Function to sum all elements in an annulus centred upon the middle of an array that has been resized to allow better pixel sampling
# Args: Array, semi-major axis of inside edge of annulus (pix), width of annulus (pix), axial ratio, position angle (deg), i & j coords of centre of ellipse, upscaling factor
# Returns: Numpy array containing the sum of the pixel values in the annulus, the total number of pixels counted, and an array containing the pixel values
def AnnulusSumUpscale(cutout, rad_inner, width, axial_ratio, angle, i_centre, j_centre, upscale=1):
    from . import Photom
    return Photom.AnnulusSumUpscale(cutout, rad_inner, width, axial_ratio, angle, i_centre, j_centre, upscale=1)

# Function to iteratively calculate SPIRE aperture noise of photometry cutout using randomly-placed (annular-background-subtracted) circular aperture
# Args: Map, radius of aperture (pix), area of aperture (pix), boolean of whether or not to sky-subtract the noise apertures, relative radius of inner edge of annulus, relative width of annulus, angle of source ellipse, axial ratio of source ellipse
# Returns: Aperture standard deviation, and list of mean background values, list of aperture sum values
def CircularApertureStandardDeviationFinder(fits, area, ann=True, ann_inner=1.5, ann_width=1.0, angle=0.0, axial_ratio=1.0, apertures=100):
    from . import Photom
    return Photom.CircularApertureStandardDeviationFinder(fits, area, ann=True, ann_inner=1.5, ann_width=1.0, angle=0.0, axial_ratio=1.0, apertures=100)

# Function to find all contiguous pixels that lie above a given flux limit
# Args: Array, radius of guess region (pix), i & j coords of centre of guess region, cutoff value for pixel selection
# Returns: Array of ones and zeros indicating contiguous region
def ContiguousPixels(cutout, rad_initial, i_centre, j_centre, cutoff):
    from . import Photom
    return Photom.ContiguousPixels(cutout, rad_initial, i_centre, j_centre, cutoff)

# Function that combines all of the ellipse-fitting steps (finds convex hull, fits ellipse to this, then finds properties of ellipse)
# Args: x & y coordinates to which the ellipse is to be fitted
# Returns: Array of x & y coordinates of ellipse centre, array of ellipse's major & minor axes, ellipse's position angle
def EllipseFit(x,y):
    from . import Photom
    return Photom.EllipseFit(x,y)

# Function to calculate the coordinates of the centre of an ellipse produced by EllipseFit
# Args: Ellipse produced by EllipseFit
# Returns: Array of x & y coordinates of ellipse centre
def EllipseCentre(a):
    from . import Photom
    return Photom.EllipseCentre(a)

# Function to calculate the lengths of the axes of an ellipse produced by EllipseFit
# Args: Ellipse produced by EllipseFit
# Returns: Array of ellipse's major & minor axes
def EllipseAxes(a):
    from . import Photom
    return Photom.EllipseAxes(a)

# Function to calculat the position angle of the centre of an ellipse produced by EllipseFit
# Args: Ellipse produced by EllipseFit
# Returns: Ellipse's position angle
def EllipseAngle(a):
    from . import Photom
    return Photom.EllipseAngle(a)

# Function to create a cutout of a fits file - NOW JUST A WRAPPER OF AN ASTROPY FUNCTION
# Args: Input fits, cutout central ra (deg), cutout central dec (deg), cutout radius (arcsec), pixel width (arcsec), fits image extension, boolean of whether to reproject, boolean stating if an output variable is desired, output fits pathname if required
# Returns: HDU of new file
def FitsCutout(pathname, ra, dec, rad_arcsec, pix_width_arcsec=None, exten=0, reproj=False, variable=False, outfile=False, parallel=True, fast=True):
    from . import Fits
    return Fits.FitsCutout(pathname, ra, dec, rad_arcsec, pix_width_arcsec=pix_width_arcsec, exten=exten, reproj=reproj, variable=variable, outfile=outfile, parallel=parallel, fast=fast)

# Function to embed a fits file in a larger array of NaNs (for APLpy or the like)
# Args: Input fits pathname, margin to place around array, fits extension of interest, boolean stating if margin is in arcseconds, no pixelsboolean stating if an output variable is desired, output fits pathname
# Returns: HDU of new file
def FitsEmbed(pathname, margin, exten=0, variable=False, outfile=False):
    from . import Fits
    return Fits.FitsEmbed(pathname, margin, exten=exten, variable=variable, outfile=outfile)

# Define function to generate a generic FITS header for a given projection
# Args: Central right ascension (deg), central declination (deg), image width (deg), pixel size (arcsec)
# Returns: FITS header
def FitsHeader(ra, dec, map_width_deg, pix_width_arcsec):
    from . import Fits
    return Fits.FitsHeader(ra, dec, map_width_deg, pix_width_arcsec)

# Function to perform a sigma clip upon a set of values
# Args: Array of values, convergence tolerance, state if median instead of mean should be used for clip centrepoint, clipping threshold, boolean for whether sigma of zero can be accepted
# Returns: List containing the clipped standard deviation, the average, and the values themselves
def SigmaClip(values, tolerance=0.001, median=False, sigma_thresh=3.0, no_zeros=False):

    # Remove NaNs from input values
    values = np.array(values)
    values = values[ np.where(np.isnan(values)==False) ]
    values_original = np.copy(values)

    # Continue loop until result converges
    diff = 10E10
    while diff>tolerance:

        # Assess current input iteration
        if median == False:
            average = np.mean(values)
        elif median == True:
            average = np.median(values)
        sigma_old = np.std(values)

        # Mask those pixels that lie more than 3 stdev away from mean
        check = np.zeros([len(values)])
        check[ np.where( values>(average+(sigma_thresh*sigma_old)) ) ] = 1
        check[ np.where( values<(average-(sigma_thresh*sigma_old)) ) ] = 1
        values = values[ np.where(check<1) ]

        # Re-measure sigma and test for convergence
        sigma_new = np.std(values)
        diff = abs(sigma_old-sigma_new) / sigma_old

    # Perform final mask
    check = np.zeros([len(values)])
    check[ np.where( values>(average+(sigma_thresh*sigma_old)) ) ] = 1
    check[ np.where( values<(average-(sigma_thresh*sigma_old)) ) ] = 1
    values = values[ np.where(check<1) ]

    # If required, check if calculated sigma is zero
    if no_zeros==True:
        if sigma_new==0.0:
            sigma_new = np.std(values_original)
            if median==False:
                average = np.mean(values)
            elif median==True:
                average = np.median(values)

    # Return results
    return [sigma_new, average, values]

# Keflavich function to downsample an array
# Args: Array to downsample, downsampling factor, and estiamtor
# Returns: Downsampled array
def Downsample(myarr, factor, estimator=np.nanmean):
    ys,xs = myarr.shape
    crarr = myarr[:ys-(ys % int(factor)),:xs-(xs % int(factor))]
    dsarr = estimator( np.concatenate([[crarr[i::factor,j::factor]
        for i in range(factor)]
        for j in range(factor)]), axis=0)
    return dsarr

# A function to fit and remove a background polynomial to an image, masking a central ellipse
# Args: Array to process, i-coord of masked central ellipse, j-coord of masked central ellipse, semimajor axis of masked central ellipse, axial ratio of masked central ellipse, position angle of masked central ellipse, order of polynomial, sigma threshold at which bright pixels cut off, downsampling factor to use, boolean of whether to only apply polynomial if it makes significant difference to image
# Returns: Poynomial-filtered array, array of the polynomial filter
def PolySub(image_in, mask_centre_i, mask_centre_j, mask_semimaj_pix, mask_axial_ratio, mask_angle, poly_order=5, cutoff_sigma=2.0, downsample=1.0, change_check=False):

    # If requested, downsample image to improve processing time
    downsample_factor = np.round(np.int(downsample))
    if downsample_factor>=2:
        image_ds = Downsample(image_in, downsample_factor)
    else:
        image_ds = image_in

    # Downsample related values accordingly
    mask_semimaj_pix = mask_semimaj_pix / downsample_factor
    mask_centre_i = int(round(float((0.5*mask_centre_i)-1.0)))
    mask_centre_j = int(round(float((0.5*mask_centre_j)-1.0)))

    # Find cutoff for excluding bright pixels by sigma-clipping map
    clip_value = SigmaClip(image_ds, tolerance=0.01, sigma_thresh=2.0, median=True)
    noise_value = clip_value[0]
    field_value = clip_value[1]
    cutoff = field_value + ( cutoff_sigma * noise_value )

    # Mask all image pixels in masking region around source
    image_masked = image_ds.copy()
    ellipse_mask = EllipseMask(image_ds, mask_semimaj_pix, mask_axial_ratio, mask_angle, mask_centre_i, mask_centre_j)
    image_masked[ np.where( ellipse_mask==1 ) ] = np.nan

    # Mask all image pixels identified as being high SNR
    image_masked[ np.where( image_masked>cutoff ) ] = np.nan

    # Use astropy to fit 2-dimensional polynomial to the image
    image_masked[ np.where( np.isnan(image_masked)==True ) ] = field_value
    poly_model = astropy.modeling.models.Polynomial2D(degree=poly_order)
    i_coords, j_coords = np.mgrid[:image_masked.shape[0], :image_masked.shape[1]]
    fitter = astropy.modeling.fitting.LevMarLSQFitter()
    i_coords = i_coords.flatten()
    j_coords = j_coords.flatten()
    image_flattened = image_masked.flatten()
    good = np.where(np.isnan(image_flattened)==False)
    i_coords = i_coords[good]
    j_coords = j_coords[good]
    image_flattened = image_flattened[good]
    fit = fitter(poly_model, i_coords, j_coords, image_flattened)

    # Create final polynomial filter (undoing downsampling using lorenzoriano GitHub script)
    i_coords, j_coords = np.mgrid[:image_ds.shape[0], :image_ds.shape[1]]
    poly_fit = fit(i_coords, j_coords)
    poly_full = scipy.ndimage.interpolation.zoom(poly_fit, [ float(image_in.shape[0])/float(poly_fit.shape[0]), float(image_in.shape[1])/float(poly_fit.shape[1]) ], mode='nearest')

    # Establish background variation before application of filter
    sigma_thresh = 2.0
    clip_in = SigmaClip(image_in, tolerance=0.005, median=True, sigma_thresh=sigma_thresh)
    bg_in = image_in[ np.where( image_in<clip_in[1] ) ]
    spread_in = np.mean( np.abs( bg_in - clip_in[1] ) )

    # How much reduction in background variation there was due to application of the filter
    image_sub = image_in - poly_full
    clip_sub = SigmaClip(image_sub, tolerance=0.005, median=True, sigma_thresh=sigma_thresh)
    bg_sub = image_sub[ np.where( image_sub<clip_sub[1] ) ]
    spread_sub = np.mean( np.abs( bg_sub - clip_sub[1] ) )
    spread_diff = spread_in / spread_sub

    # If the filter made significant difference, apply to image and return it; otherwise, just return the unaltered map
    if change_check:
        if spread_diff>1.1:
            image_out = image_sub
            poly_out = poly_full
        else:
            image_out = image_ds
            poly_out = np.zeros(image_in.shape)
    else:
        image_out = image_sub
        poly_out = poly_full
    return image_out, poly_out

# Function that provides Galactic extinction correction, via IRSA dust extinction service (which uses the Schlafly & Finkbeiner 2011 prescription)
# Args: RA of target coord (deg), dec of target coord (deg), name of band of interest, (boolean of whether function should be verbose, and meaningless verbose output prefix string)
# Returns: Extinction correction factor (ie, multiply uncorrected flux by this value to yield corrected flux)
def ExtCorrrct(ra, dec, band_name, verbose=True, verbose_prefix=''):

    # Make sure there's a space at the end of the verbose prefix
    if verbose_prefix!='':
        if verbose_prefix[-1:]!=' ':
            verbose_prefix += ' '

    # Offset RA or Dec if either is exactly 0, as this can confuse IRSA
    if np.abs(ra)<0.01:
        ra = 0.01
    if np.abs(dec)<0.01:
        dec = 0.01

    # List bands for which IRSA provids corrections
    excorr_possible = ['GALEX_FUV','GALEX_NUV','SDSS_u','SDSS_g','SDSS_r','SDSS_i','SDSS_z','CTIO_U','CTIO_B','CTIO_V','CTIO_R','CTIO_I','DSS_B','DSS_R','DSS_I','2MASS_J','2MASS_H','2MASS_Ks','UKIRT_Y','UKIRT_J','UKIRT_H','UKIRT_K','Spitzer_3.6','Spitzer_4.5','Spitzer_5.8','Spitzer_8.0','WISE_3.4','WISE_4.6']

    # Check if corrections are available for this band
    photom_band_parsed = BandParse(band_name)
    if photom_band_parsed==None:
        if verbose: print(verbose_prefix+'Unable to parse band name; not conducting Galactic extinction correction for this band.')
        excorr = 1.0
        return excorr
    if photom_band_parsed not in excorr_possible:
        if verbose: print(verbose_prefix+'Galactic extinction correction not available for this band.')
        excorr = 1.0
        return excorr

    # Else if extinction correction is possible, prepare query IRSA dust extinction service
    if verbose: print(verbose_prefix+'Retreiving extinction corrections from IRSA Galactic Dust Reddening & Extinction Service.')
    query_count = 0
    query_success = False
    query_limit = 10

    # Keep trying to access extinction corrections, until it works
    while not query_success:
        if query_count>=query_limit:
            break

        # Carry out query
        try:
            sys.stdout = open(os.devnull, "w")
            irsa_query = astroquery.irsa_dust.IrsaDust.get_extinction_table( str(ra)+', '+str(dec) )
            sys.stdout = sys.__stdout__
            query_success = True
            break

        # Handle exceptions
        except Exception as exception:
            sys.stdout = sys.__stdout__
            if query_count==0:
                if hasattr(exception, 'message'):
                    print(verbose_prefix+'IRSA Galactic Dust Reddening & Extinction Service query failed with error: \"'+repr(exception.message)+'\" - reattempting.')
                else:
                    print(verbose_prefix+'IRSA Galactic Dust Reddening & Extinction Service query failed: reattempting (exception not caught).')
            query_count += 1
            time.sleep(10.0)
    if not query_success:
        print(verbose_prefix+'Unable to access IRSA Galactic Dust Reddening & Extinction Service after '+str(query_limit)+' attemps.')
        raise ValueError('Unable to access IRSA Galactic Dust Reddening & Extinction Service after '+str(query_limit)+' attemps.')

    # Loop over entries in the IRSA table, looking for the current band
    irsa_band_exists = False
    for irsa_band_raw in irsa_query['Filter_name'].tolist():
        irsa_band_parsed = BandParse(irsa_band_raw)
        if irsa_band_parsed==None:
            continue

        # If band found in IRSA table, apply quoted Schlafly & Finkbeiner extinction correction
        if irsa_band_parsed==photom_band_parsed:
            irsa_band_index = np.where( irsa_query['Filter_name']==irsa_band_raw )[0][0]
            irsa_band_excorr_mag = irsa_query['A_SandF'][irsa_band_index]
            irsa_band_excorr = 10.0**( irsa_band_excorr_mag / 2.51 )
            irsa_band_exists = True
            break

    # If band is GALEX, determine appropriate extinction correction using reddening coefficients derived from Cardelli (1989) extinction law (cf Gil de Paz 2007, arXiv:1009.4705, arXiv:1108.2837)
    if (irsa_band_exists==False) and (photom_band_parsed in ['GALEX_FUV','GALEX_NUV']):

        # Get the A(V) / E(B-V) extinction-to-excess ratio in V-band
        irsa_v_index = np.where( irsa_query['Filter_name']=='CTIO V' )[0][0]
        irsa_av_ebv_ratio = irsa_query["A_over_E_B_V_SandF"][irsa_v_index]

        # Get the A(V) attenuation in V-band
        irsa_av = irsa_query["A_SandF"][irsa_v_index]

        # Determine the factor
        if photom_band_parsed=='GALEX_FUV':
            reddening_coeff = 7.9
        elif photom_band_parsed=='GALEX_NUV':
            reddening_coeff = 8.0

        # Calculate and apply the extincton correction
        irsa_band_excorr_mag = reddening_coeff * ( irsa_av / irsa_av_ebv_ratio )
        irsa_band_excorr = 10.0**( irsa_band_excorr_mag / 2.51 )

    # If band is Y-band, use point 36.557% of way between z-band and J-band corrections
    if (irsa_band_exists==False) and (photom_band_parsed=='UKIRT_Y'):
        irsa_z_index = np.where( irsa_query['Filter_name']=='SDSS z' )[0][0]
        irsa_J_index = np.where( irsa_query['Filter_name']=='UKIRT J')[0][0]
        irsa_band_excorr_mag = irsa_query['A_SandF'][irsa_J_index] + ( (1.0-0.36557) * (irsa_query['A_SandF'][irsa_z_index] - irsa_query['A_SandF'][irsa_J_index]) )
        irsa_band_excorr = 10.0**( irsa_band_excorr_mag / 2.51 )
        irsa_band_exists = True

    # Report result and return extinction correction
    import FromGitHub.randlet
    if verbose: print(verbose_prefix+'Galactic extinction correction factor is '+str(FromGitHub.randlet.ToPrecision(irsa_band_excorr,4))+' (ie, '+str(FromGitHub.randlet.ToPrecision(irsa_band_excorr_mag,4))+' magnitudes).')
    return irsa_band_excorr

# A function to determine what particular band a given band name refers to
# Args: The band name to be parsed
# Returns: The parsed band name
def BandParse(band_name_target):

    # Define dictionary containing lists of possible alternate names for each band
    band_names_dict = {'GALEX_FUV':['GALEX_FUV','FUV','FUV-band','GALEX_f','f','f-band'],
                       'GALEX_NUV':['GALEX_NUV','NUV','NUV-band','GALEX_n','n','n-band'],
                       'SDSS_u':['SDSS_u','u','u-band','SDSS_u-band'],
                       'SDSS_g':['SDSS_g','g','g-band','SDSS_g-band'],
                       'SDSS_r':['SDSS_r','r','r-band','SDSS_r-band'],
                       'SDSS_i':['SDSS_i','i','i-band','SDSS_i-band'],
                       'SDSS_z':['SDSS_z','z','z-band','SDSS_z-band','VISTA_Z','VISTA_Z-band'],
                       'CTIO_U':['CTIO_U','CTIO_U-band'],
                       'CTIO_B':['CTIO_B','CTIO_B-band','B','B-band'],
                       'CTIO_V':['CTIO_V','CTIO_V-band','V','V-band'],
                       'CTIO_R':['CTIO_R','CTIO_R-band'],
                       'CTIO_I':['CTIO_I','CTIO_I-band'],
                       'DSS_B':['DSS_B','DSS1_B','DSSI_B','DSS2_B','DSSII_B','DSS_B-band','DSS1_B-band','DSSI_B-band','DSS2_B-band','DSSII_B-band','DSS_G','DSS1_G','DSSI_G','DSS2_G','DSSII_G','DSS_G-band','DSS1_G-band','DSSI_G-band','DSS2_G-band','DSSII_G-band'],
                       'DSS_R':['DSS_R','DSS1_R','DSSI_R','DSS2_R','DSSII_R','DSS_R-band','DSS1_R-band','DSSI_R-band','DSS2_R-band','DSSII_R-band'],
                       'DSS_I':['DSS_I','DSS1_I','DSSI_I','DSS2_I','DSSII_I','DSS_I-band','DSS1_I-band','DSSI_I-band','DSS2_I-band','DSSII_I-band'],
                       '2MASS_J':['2MASS_J','J','J-band','2MASS_J-band'],
                       '2MASS_H':['2MASS_H','H','H-band','2MASS_H-band'],
                       '2MASS_Ks':['2MASS_Ks','Ks','Ks-band','2MASS_Ks-band','2MASS_K','2MASS_K-band','VISTA_Ks','VISTA_Ks-band','VISTA_K','VISTA_K-band','VIRCAM_Ks','VIRCAM_Ks-band','VIRCAM_K','VIRCAM_K-band'],
                       'UKIRT_Y':['UKIRT_Y','UKIRT_Y-band','UKIDSS_Y','UKIDSS_Y-band','WFCAM_Y','WFCAM_Y-band','VISTA_Y','VISTA_Y-band','VIRCAM_Y','VIRCAM_Y-band'],
                       'UKIRT_J':['UKIRT_J','UKIRT_J-band','UKIDSS_J','UKIDSS_J-band','WFCAM_J','WFCAM_J-band','VISTA_J','VISTA_J-band','VIRCAM_J','VIRCAM_J-band'],
                       'UKIRT_H':['UKIRT_H','UKIRT_H-band','UKIDSS_H','UKIDSS_H-band','WFCAM_H','WFCAM_H-band','VISTA_H','VISTA_H-band','VIRCAM_H','VIRCAM_H-band'],
                       'UKIRT_K':['UKIRT_K','UKIRT_K-band','K','K-band','UKIDSS_K','UKIDSS_K-band','WFCAM_K','WFCAM_K-band'],
                       'Spitzer_3.6':['Spitzer_3.6','Spitzer_3.6um','Spitzer_3.6mu','Spitzer_IRAC_3.6','Spitzer_IRAC_3.6um','Spitzer_IRAC_3.6mu','Spitzer_IRAC1','Spitzer_I1','IRAC_3.6','IRAC_3.6um','IRAC_3.6mu','IRAC1','I1','Spitzer_IRAC1-band','IRAC1-band','I1-band','3.6','3.6um','3.6mu'],
                       'Spitzer_4.5':['Spitzer_4.5','Spitzer_4.5um','Spitzer_4.5mu','Spitzer_IRAC_4.5','Spitzer_IRAC_4.5um','Spitzer_IRAC_4.5mu','Spitzer_IRAC2','Spitzer_I2','IRAC_4.5','IRAC_4.5um','IRAC_4.5mu','IRAC2','I2','Spitzer_IRAC2-band','IRAC2-band','I2-band','4.5','4.5um','4.5mu'],
                       'Spitzer_5.8':['Spitzer_5.8','Spitzer_5.8um','Spitzer_5.8mu','Spitzer_IRAC_5.8','Spitzer_IRAC_5.8um','Spitzer_IRAC_5.8mu','Spitzer_IRAC3','Spitzer_I3','IRAC_5.8','IRAC_5.8um','IRAC_5.8mu','IRAC3','I3','Spitzer_IRAC3-band','IRAC3-band','I3-band','5.8','5.8um','5.8mu'],
                       'Spitzer_8.0':['Spitzer_8.0','Spitzer_8.0um','Spitzer_8.0mu','Spitzer_IRAC_8.0','Spitzer_IRAC_8.0um','Spitzer_IRAC_8.0mu','Spitzer_IRAC4','Spitzer_I4','IRAC_8.0','IRAC_8.0um','IRAC_8.0mu','IRAC4','I4','Spitzer_IRAC4-band','IRAC4-band','I4-band','8.0','8.0um','8.0mu','Spitzer_8','Spitzer_8m','Spitzer_8mu','Spitzer_IRAC_8','Spitzer_IRAC_8um','Spitzer_IRAC_8mu','IRAC_8','IRAC_8um','IRAC_8mu','8','8um','8mu'],
                       'WISE_3.4':['WISE_3.4','WISE_3.4um','WISE_3.4mu','WISE1','WISE1-band','W1','W1-band','WISE_W1','WISE_W1-band'],
                       'WISE_4.6':['WISE_4.6','WISE_4.6um','WISE_4.6mu','WISE2','WISE2-band','W2','W2-band','WISE_W2','WISE_W2-band'],
                       'PACS_70':['PACS_70','Herschel_70','Herschel-PACS_70'],
                       'PACS_100':['PACS_100','Herschel_100','Hercshel-PACS_100'],
                       'PACS_160':['PACS_160','Herschel_160','Herschel-PACS_160'],
                       'SPIRE_250':['SPIRE_250','Hercshel_250','Hercshel-SPIRE_250'],
                       'SPIRE_350':['SPIRE_350','Hercshel_350','Hercshel-SPIRE_350'],
                       'SPIRE_500':['SPIRE_500','Hercshel_500','Hercshel-SPIRE_500'],
                       'Planck_350':['Planck_857','Planck-HFI_350','Planck-HFI_857','HFI_350','HFI_857'],
                       'Planck_550':['Planck_545','Planck-HFI_550','Planck-HFI_545','HFI_550','HFI_545'],
                       'Planck_850':['Planck_353','Planck-HFI_850','Planck-HFI_353','HFI_850','HFI_353'],
                       'Planck_1380':['Planck_217','Planck-HFI_1380','Planck-HFI_217','HFI_1380','HFI_217'],
                       'Planck_2100':['Planck_143','Planck-HFI_2100','Planck-HFI_143','HFI_2100','HFI_143'],
                       'Planck_3000':['Planck_100','Planck-HFI_3000','Planck-HFI_100','HFI_3000','HFI_100'],
                       'Planck_4260':['Planck_70','Planck-LFI_4260','Planck_LFI_70','LFI_4260','LFI_70'],
                       'Planck_6810':['Planck_44','Planck-LFI_6810','Planck_LFI_44','LFI_6810','LFI_44'],
                       'Planck_10600':['Planck_30','Planck-LFI_10600','Planck_LFI_30','LFI_10600','LFI_30'],}

    # Loop over alternate band name dictionary entries
    band_altnames_matches = []
    for band_name_key in band_names_dict.keys():
        for band_altname in band_names_dict[band_name_key]:

            # Make band names all-lowercase and alphanumeric-only, for ease of comparison
            band_name_target_comp = re.sub(r'\W+', '', band_name_target).replace('_','').lower()
            band_altname_comp = re.sub(r'\W+', '', band_altname).replace('_','').lower()

            # Strip away wavelength and frequency units suffixes, for ease of comparison
            unit_suffixes = ['um','micron','mm','GHz','MHz']
            for unit in unit_suffixes:
                band_name_target_comp = band_name_target_comp.replace(unit,'')
                band_altname_comp = band_altname_comp.replace(unit,'')

            # If target and alternate band names match, record
            if band_name_target_comp==band_altname_comp:
                band_altnames_matches.append(band_name_key)

    # If no matches found, or more than one match found, report null output
    if len(band_altnames_matches)==0:
        return None
    elif len(band_altnames_matches)>1:
        raise Exception('Band name has multiple possible matches! Can you be more specific?')

    # Else if a good match is found, return it
    elif len(band_altnames_matches)==1:
        return band_altnames_matches[0]

# Function to read in file containing transmission functions for instruments, and convert to a dictionary
# Kwargs: Path of a file containing transmisison information for all the bands (or, optionally, leave blank to look for Transmissions.dat in same directory as script).
    # The first row for a given band takes form 'band,[BAND NAME]'.
    # The following row for a given band is optional, and gives information about that band's reference spectrum, taking the form 'ref,[SPECTRUM_DESCRIPTION]'; the spectrum description can be either nu_X, where X is replaced by a number giving the index of some frequency-dependent power law spectrum; of BB_T, where T is replaced by a number giving the temperature of a blackbody spectrum.
    # All subsequent rows for a given band then take the form '[SOME WAVELENGTH IN MICRONS],[TRANSMISSION FRACTION]'.
    # This format can be repeated to fit transmissions data for any number of bands in one file.
# Returns: Dictionary of filter transmissions
def TransmissionDict(path=None):

    # If no path given, assume Tramsissions.dat file is in same directory as script
    if path == None:
        path = os.path.join(os.path.dirname(os.path.realpath(__file__)),'Transmissions.dat')

    # Read in transmission curves file, and loop over lines
    trans_dict = {'refs':{}}
    with open(path) as curve_file:
        curve_list = curve_file.readlines()
    for i in range(0,len(curve_list)):
        curve_line = curve_list[i]

        # Check if this line indicates start of a new band; if so, start recording new band
        if curve_line[:4] == 'band':
            band = curve_line[5:].replace('\n','')
            trans_dict[band] = []

        # Check if this line contains reference spectrun information; if so, place in the 'refs' dictionary entry
        elif curve_line[:3] == 'ref':
            trans_dict['refs'][band] = curve_line[4:].replace('\n','')

        # Check if this line contains regular transmission information; if so, record
        else:
            trans_dict[band].append(curve_line.replace('\n','').split(','))

    # Loop over filters in filter dict, setting them to be arrays
    for curve in trans_dict.keys():
        if curve != 'refs':
            trans_dict[curve] = np.array(trans_dict[curve]).astype(float)

    # Return finished dictionary
    return trans_dict

# Function to colour correct a flux density, for a given source SED, reference SED, and response curve
# Args: Wavelength of source flux density (in metres), the source spectrum (a Nx2 array, of wavelengths in metres, and fluxes in whatever),
    # the band filter (either a string giving name of a filter in Transmissions.dat, or a Nx2 array of wavelenghts in metres, and transmission fractions),
# Kwargs: Reference spectrum (a Nx2 array of wavelengths in metres, and fluxes; although this can be left to None if this band is in Transmissions.dat),
    # a dictionary containing transmission curves (optional, in case a custom dictionary is desired; must be in same format as yielded by TransmissionDict)
    # and a boolean for fail-unsafe mode (where a correction factor of 1 will be returned if band name not recognised)
# Returns: Colour correction factor (yes, FACTOR)
def ColourCorrect(wavelength, source_spec, band_filter, ref_spec=None, trans_dict=None, fail_unsafe=False):

    # Define physical constants
    c = 3E8
    h = 6.64E-34
    k = 1.38E-23

    # Check if a filter curve has been provided, or if a band name has been given for a common band in Transmissions.dat
    if isinstance(band_filter, np.ndarray):
        band_name = False
    elif isinstance(band_filter, str):
        band_name = copy.copy(band_filter)

        # If a dictionary of curves has already been provided, use it; else read in Transmissions.dat
        if trans_dict == None:
            trans_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),'Transmissions.dat')
            if os.path.exists(trans_path):
                trans_dict = TransmissionDict(trans_path)
            else:
                raise Exception('Dictionary of band transmissions not given, and no Transmissions.dat file found in same directory as script')

        # Check that requested filter is actually in dictionary; if it is, grab it, and convert wavelengths from microns to metres
        if band_filter not in trans_dict.keys():
            if fail_unsafe:
                return 1.0
            else:
                raise Exception('Reqested filter not in database of common filters; please provide as an array instead')
        else:
            band_filter = trans_dict[band_filter].copy()
            band_filter[:,0] /= 1E6

    # Check if reference spectrum present in transmission dictionary; if it is, construct spectrum array
    if isinstance(ref_spec, np.ndarray):
        pass
    elif (isinstance(band_name, str)) and (ref_spec == None) and (band_name in trans_dict.keys()) and (band_name in trans_dict['refs'].keys()):
        nu = (c / band_filter[:,0])

        # If reference is a power law, turn into corresponding array of values at same wavelength intervals as filter curve
        if trans_dict['refs'][band_name][:2] == 'nu':
            index = float(trans_dict['refs'][band_name][3:])
            ref_spec = np.zeros(band_filter.shape)
            ref_spec[:,0] = band_filter[:,0]
            ref_spec[:,1] = nu**index
            #ref_spec[:,1] /= np.max(ref_spec[:,1])

        # If reference spectrum is a blackbody, turn into a corresponding array of values at same wavelength intervals as filter curve
        if trans_dict['refs'][band_name][:2] == 'BB':
            temp = float(trans_dict['refs'][band_name][3:])
            ref_spec = np.zeros(band_filter.shape)
            ref_spec[:,0] = band_filter[:,0]
            planck_prefactor = np.divide((2.0 * h * nu**3.0), c**2.0)
            planck_exponent = (h * nu) / (k * temp)
            ref_spec[:,1] = planck_prefactor * (np.e**planck_exponent - 1)**-1.0
            #ref_spec[:,1] /= np.max(ref_spec[:,1])

    # If reference spectrum not in Transmission.dat, nor provided by user, raise exception
    else:
        raise Exception('Reference spectrum not given, nor found in dictionary of band transmissions; please provide reference spectrum')

    # Normalise source and reference SEDs to have observed flux at (interpolated) nominal wavelength
    source_spec[:,1] /= np.interp(wavelength, source_spec[:,0], source_spec[:,1])
    ref_spec[:,1] /= np.interp(wavelength, ref_spec[:,0], ref_spec[:,1])

    # Filter SEDs by response curve (appropriately resampled in wavelength intervals), to record observed flux
    source_obs = source_spec[:,1] * np.interp(source_spec[:,0], band_filter[:,0], band_filter[:,1])
    ref_obs = ref_spec[:,1] * np.interp(ref_spec[:,0], band_filter[:,0], band_filter[:,1])

    # Integrate observed filtered SEDs (in intervals of freqency, as Jy are in terms of Hz)
    source_int = np.trapz(source_obs, x=(c/source_spec[:,0]))
    ref_int = np.trapz(ref_obs, x=(c/ref_spec[:,0]))

    # Calculate and return colour correction factor from integrals
    colour_corr_factor = ref_int / source_int
    return colour_corr_factor

# Function to find uncertainty in an array, in terms of distance from given value, out to a certain percentile limit
# Args: Array of numbers to find uncertainty of, percentile range to find uncertainty out to, boolean of whether to return up-and-down bound values
# Returns: Percentile uncertainty
def PercentileError(data, value, percentile=68.27, bounds=False):
    data = Nanless(data)
    percentile = np.float(percentile)
    if bounds==False:
        error = ( np.sort( np.abs( data - value ) ) )[ np.int( (percentile/100.0) * data.shape[0] ) ]
        return error
    elif bounds==True:
        data_up = data[ np.where( data>=value ) ]
        data_down = data[ np.where( data<value ) ]
        error_up = np.percentile( np.sort(np.abs(data_up-value)), percentile )
        error_down = np.percentile( np.sort(np.abs(data_down-value)), percentile )
        return (error_down, error_up)

# Function to find the uncertainty on a median (or mean) of a dataset by bootstrapping
# Args: Data in question; (int of number of boostrap resmaples to use; boolean of whether to use mean instead of median; boolean False or quantile to use if using percentile instead of standard deviation)
# Returns: Computed uncertainty on average
def AvgUncBootstrap(data, n_samples=100, mean=False, percentile=False):

    # Make sure input data is a flat numpy array
    data = np.array(data)
    data = data.flatten()

    # Take average
    if not mean:
        avg = np.nanmedian(data)
    else:
        avg = np.nanmean(data)

    # Generate boostrap resamples, and take averages of them
    samples = np.array([np.random.choice(data,size=len(data),replace=True) for i in range(n_samples)])
    if not mean:
        samples_avgs = np.nanmedian(samples, axis=1)
    else:
        samples_avgs = np.nanmean(samples, axis=1)

    # Find uncertainty on sample averages
    if percentile == False:
        avg_unc = np.std(samples_avgs)
    else:
        avg_unc = np.nanpercentile(np.abs(samples_avgs-avg), percentile)

    # Return result
    return avg_unc

# Function to find the uncertainty on a median (or mean) of a dataset containing a selection of subsets by bootstrapping
# Args: 2D array of data in question [n_subsets X n_datapoints]; (int of number of boostrap resmaples to use; boolean of whether to use mean instead of median; boolean False or quantile to use if using percentile instead of standard deviation)
# Returns: Computed uncertainty on average
def SubsetAvgUncBootstrap(data, n_samples=100, mean=False, percentile=False):

    # Take average
    if not mean:
        avg = np.nanmedian(data)
    else:
        avg = np.nanmean(data)

    # Generate boostrap resamples, by drawing a random sample of the subsets
    samples = np.nan * np.zeros([data.shape[0], data.shape[1], n_samples])
    for b in range(n_samples):
        indxs = np.random.choice(np.arange(data.shape[0]), size=data.shape[0], replace=True)
        samples[:,:,b] = data[indxs,:]

    # Take averages of resampled data
    if not mean:
        samples_avgs = np.nanmedian(samples, axis=(0,1))
    else:
        samples_avgs = np.nanmean(samples, axis=2)

    # Find uncertainty on sample averages
    if percentile == False:
        avg_unc = np.std(samples_avgs)
    else:
        avg_unc = np.nanpercentile(np.abs(samples_avgs-avg), percentile)

    # Return result
    return avg_unc

# Function to trim an array to a given size
# Args: Array to be trimmed, i & j coordinates of centre of trimmed region, width of trimmed region
# Returns: Trimmed array
def Trim(data, i_centre, j_centre, width):
    box_rad  = int(round(float(width)/2.0))
    i_cutout_min = max([0, i_centre-box_rad])
    i_cutout_max = min([(data.shape)[0], i_centre+box_rad])
    j_cutout_min = max([0, j_centre-box_rad])
    j_cutout_max = min([(data.shape)[1], j_centre+box_rad])
    trimmed = data[ int(round(i_cutout_min)):int(round(i_cutout_max)), int(round(j_cutout_min)):int(round(j_cutout_max)) ]
    i_centre = (int(round(float(trimmed.shape[0])/2.0))+1) + ( (i_centre-box_rad) - (int(round(i_cutout_min))) )
    j_centre = (int(round(float(trimmed.shape[1])/2.0))+1) + ( (j_centre-box_rad) - (int(round(j_cutout_min))) )
    return trimmed, [i_centre, j_centre]

# Wrapper around Scipy grid interpolation function, to inpute NaN pixels in 2D data
# Args: Image to have NaN pixels imputed over
# Returns: Image with NaN pixels imputed
def ImputeImage(img_in):
    bad_coords = np.where(np.isnan(img_in))
    good_coords = np.where(np.isnan(img_in) == False)
    good_values = img_in[good_coords]
    bad_values_interp = scipy.interpolate.griddata(good_coords, good_values, bad_coords, method='cubic')
    img_out = img_in.copy()
    img_out[bad_coords] = bad_values_interp
    return img_out

# Function that uses Driver & Robotham (2010) foruma to give percentage cosmic variance
# Args: Survey volume (in Mpc^3, assuming H0=70 km s^-1 Mpc^-1), number of survey fields, survey field aspect ratio
# Returns: Percentage cosmic variance
def CosmicVariance(v, n, x):
    v, n, x = float(v), float(n), float(x)
    first_term = 1.00 - ( 0.03 * np.sqrt(x-1.0) )
    second_term = ( 219.7 - (52.4*np.log10(v)) + (3.21*(np.log10(v))**2.0) ) / n**0.5
    cv = first_term * second_term
    return cv

# Function to convert the bin-edge output of np.histogram to be bin-centred (and this of same dimensions as bin totals)
# Args: Array of bin edges
# Returns: Array of bin centres
def HistBinMerge(bin_edges):
    bin_edges = np.array(bin_edges)
    bin_centres = np.zeros([bin_edges.shape[0]-1])
    for i in range(0, bin_edges.shape[0]-1):
        bin_centres[i] = np.mean([bin_edges[i],bin_edges[i+1]])
    return bin_centres

# Function to perform gaussian fit to data
# Args: Array of data to be fit
# Returns: Mean of fit, standard deviation of fit
def GaussFit(data, n_bins=50):
    def QuickGauss(x, *p):
        A, mu, sigma = p
        return A * np.exp( (-((x-mu)**2.0)) / (2.0*sigma**2.0) )
    hist_tots, hist_bins = np.histogram(data, bins=n_bins)
    hist_bins = HistBinMerge(hist_bins)
    hist_guesses = [np.max(hist_tots), np.mean(data), np.std(data)]
    hist_fit = scipy.optimize.curve_fit(QuickGauss, hist_bins, hist_tots, p0=hist_guesses)
    return abs(hist_fit[0][1]), abs(hist_fit[0][2])

# Function to convert an observed brightness into a luminosity
# Args: Flux (Jy), distance (pc) frequency or boolean for nuSnu luminosity (Hz or False), boolean to switch flux input to AB magnitudes
# Output; Luminosity in bolometric solar luminosities
def FluxToLum(flux, dist, freq=False, mags=False):
    if mags==True:
        flux = ABMagsToJy(flux)
    watts_per_hz = 10.0**-26.0 * flux * 4.0 * np.pi * ( dist * 3.26 * 9.5E15 )**2.0
    if freq==False:
        watts = watts_per_hz
    elif (freq!=False) and (freq>0):
        watts = watts_per_hz * freq
    Lsol = watts / 3.846E26
    return Lsol

# Function to convert SDSS-III "nanomaggies" (nMgy) into pogson magnitudes
# Args: Value to be converted (nanomaggies)
# Returns: Pogson magnitudes (mags; duh)
def nMaggiesToMags(nMaggies):
    mag = 22.5 - ( 2.5*np.log10(nMaggies) )
    return mag

# Function to convert GAMA data units into AB pogson magnitudes
# Args: Value to be converted (data units)
# Returns: AB pogson magnitudes (mags; duh)
def GAMACountsToMags(GAMA):
    mag = 30.0 - ( 2.5*np.log10(GAMA) )
    return mag

# Function to convert from AB pogson magnitudes into GAMA data units
# Args: Value to be converted (mags)
# Returns: AB pogson magnitudes (data units)
def GAMAMagsToCounts(mag):
    GAMA = 10.0**( (30.0-mag) / 2.5 )
    return GAMA

# Function to convert from AB pogson magnitudes into flux in janskys
# Args: Value to be converted (mags)
# Returns: Source flux density (Jy)
def ABMagsToJy(mag):
    Jy = 1E-6 * 10.0**((23.9-mag)/2.5)
    return Jy

# Function to convert from flux in janskys to AB pogson magnitudes
# Args: Value to be converted (mags)
# Returns: Source flux density (Jy)
def JyToABMags(Jy):
    mag = 23.9 - ( 2.5 * np.log10( Jy * 10**6.0 ) )
    return mag

# Function to convert an uncertainty in AB pogson magnitudes to an uncertainty in janskys
# Args: Uncertainty to be converted (mags), and its associated measurement (mags)
# Returns: Uncertainty in flux density (Jy)
def ErrABMagsToJy(err, mag):
    Jy_down = ABMagsToJy(mag) - ABMagsToJy(mag + err)
    Jy_up = ABMagsToJy(mag - err) - ABMagsToJy(mag)
    Jy = ( Jy_down + Jy_up ) / 2.0
    return Jy

# Function to convery absolute AB pogson magnitudes into solar luminosities
# Args: Absolute AB pogson magnitude (Mags)
# Returns: Luminosity (Lsol):
def ABAbsToLsol(Mag):
    Lsol = 10.0**( (4.58 - Mag ) / 2.51 )
    return Lsol

# Function to convert an RMS deviation in relative linear flux to magnitudes
# Args: Relative RMS deviation in flux
# Returns: RMS deviation in mangitude
def RMSFluxToMags(S_rms):
    M_rms = abs( 2.5 * np.log10(1.0-S_rms) )
    return M_rms

# Function to convert an RMS deviation in magnitude to relative linear flux
# Args: RMS deviation in magnitude
# Returns: RMS deviation in relative flux
def RMSMagsToFlux(m_rms):
    S_rms = 1.0 - abs( 10.0**(m_rms/-2.5) )
    return S_rms

# New function to convert an uncertainty to log space
# Args: Value, uncertainty
# Returns: Logarithmic uncertainty
def LogError(value, error):
    value, error = np.array(value), np.array(error)
    frac = 1.0 + (error/value)
    error_up = value * frac
    error_down = value / frac
    log_error_up = np.abs( np.log10(error_up) - np.log10(value) )
    log_error_down = np.abs( np.log10(value) - np.log10(error_down) )
    return 0.5*(log_error_up+log_error_down)

# Function to convert a logarithmic uncertainty to linear space
# Args: Logarithmic value, logarithmic uncertainty, boolean of whether average unlogged errors or return them asymetrically
# Returns: Linear uncertainty
def UnlogError(log_value, log_error, bounds=False):
    if bounds==False:
        value = 10**log_value
        log_up = log_value + log_error
        log_down = log_value - log_error
        lin_up = 10**log_up
        lin_down = 10**log_down
        rel_up = lin_up / value
        rel_down = lin_down / value
        frac = (0.5 * (rel_up + (rel_down**-1))) - 1.0
        return frac * value
    elif bounds==True:
        error_up = 10.0**(log_value + log_error) - 10.0**log_value
        error_down = 10.0**(log_value - log_error) - 10.0**log_value
        return [error_up, error_down]

# Function to generate appropriate dimensions plot divisions of a figure in along a given direction
# Args: Index of plot element in question, total number of plot elements, dimension of figure, x or y axis,
# Returns: Starting position of plot, dimension of plot
def GridPos(n_plot, n_tot, img_dim, axis='y', nonstandard=False, gaps=False):
    if nonstandard>0:
        base = nonstandard
    elif nonstandard==False:
        if axis=='y':
            base = 6.0
        elif axis=='x':
            base = 8.0
    n_plot, n_tot, img_dim = float(n_plot), float(n_tot), float(img_dim)
    margin_start = 0.125 * (base / img_dim)
    margin_end = (1.0 - 0.95) * (base / img_dim)
    fig_start = margin_start
    fig_end = 1.0 - margin_end
    fig_dim = fig_end - fig_start
    plot_dim = fig_dim / n_tot
    plot_start = fig_start + (n_plot * plot_dim)
    if gaps>0:
        plot_start += (0.5 * gaps) * plot_dim
        plot_dim *= 1.0 - (0.5 * gaps)
    return plot_start, plot_dim

# Function to find the Sheather-Jones bandwidth estimator (<NAME>, 1991), adapted from: https://github.com/Neojume/pythonABC
# Args: Array of values of which bandwidth will be found
# Returns: Sheather-Jones bandwidth of array
def SheatherJonesBW(x, weights=None):

    # Define Equation 12 from Sheather & Jones (1991)
    def sj12(x, h):
        phi6 = lambda x: (x ** 6 - 15 * x ** 4 + 45 * x ** 2 - 15) * scipy.stats.norm.pdf(x, loc=0.0, scale=1.0)
        phi4 = lambda x: (x ** 4 - 6 * x ** 2 + 3) * scipy.stats.norm.pdf(x, loc=0.0, scale=1.0)
        n = len(x)
        one = np.ones((1, n))
        lam = np.percentile(x, 75) - np.percentile(x, 25)
        a = 0.92 * lam * n ** (-1 / 7.0)
        b = 0.912 * lam * n ** (-1 / 9.0)
        W = np.tile(x, (n, 1))
        W = W - W.T
        W1 = phi6(W / b)
        tdb = np.dot(np.dot(one, W1), one.T)
        tdb = -tdb / (n * (n - 1) * b ** 7)
        W1 = phi4(W / a)
        sda = np.dot(np.dot(one, W1), one.T)
        sda = sda / (n * (n - 1) * a ** 5)
        alpha2 = 1.357 * (abs(sda / tdb)) ** (1 / 7.0) * h ** (5 / 7.0)
        W1 = phi4(W / alpha2)
        sdalpha2 = np.dot(np.dot(one, W1), one.T)
        sdalpha2 = sdalpha2 / (n * (n - 1) * alpha2 ** 5)
        return (scipy.stats.norm.pdf(0, loc=0, scale=np.sqrt(2)) /
                (n * abs(sdalpha2[0, 0]))) ** 0.2 - h

    # Bandwidth estimator for normal case (From paragraph 2.4.2 of Bowman & Azzalini, 1997)
    def hnorm(x, weights=None):
        wmean = lambda x,w: sum(x * w) / float(sum(w))
        wvar = lambda x,w: sum(w * (x - wmean(x, w)) ** 2) / float(sum(w) - 1)
        x = np.asarray(x)
        if weights is None:
            weights = np.ones(len(x))
        n = float(sum(weights))
        if len(x.shape) == 1:
            sd = np.sqrt(wvar(x, weights))
            return sd * (4 / (3 * n)) ** (1 / 5.0)
        if len(x.shape) == 2:
            ndim = x.shape[1]
            sd = np.sqrt(np.apply_along_axis(wvar, 1, x, weights))
            return (4.0 / ((ndim + 2.0) * n) ** (1.0 / (ndim + 4.0))) * sd

    # Actual calculator of bandwidth
    h0 = hnorm(x)
    v0 = sj12(x, h0)
    if v0 > 0:
        hstep = 1.1
    else:
        hstep = 0.9
    h1 = h0 * hstep
    v1 = sj12(x, h1)
    while v1 * v0 > 0:
        h0 = h1
        v0 = v1
        h1 = h0 * hstep
        v1 = sj12(x, h1)
    return h0 + (h1 - h0) * abs(v0) / (abs(v0) + abs(v1))

# Function to remove all NaN entries from an array
# Args: Array to be cleansed
# Returns: Purified array
def Nanless(bad):
    bad = np.array(bad)
    good = bad[np.where(np.isnan(bad)==False)]
    return good

# Function to aappend an arbitrarily long list of arrays into 1 array:
# Args: List of numpy arrays
# Returns: One big appended (1D) numpy array
def PanAppend(arr_list):
    n_arr = len(arr_list)
    arr_app = np.array([])
    for i in range(0,n_arr):
        arr_app = np.append( arr_app, np.array( arr_list[i] ) )
    return arr_app

# Function to wget a file from a given URL to a given directory
# Args: String of target url, string of output filepath, boolean for clobbering, boolean for auto-retrying, boolean for verbosity
# Returns: None
def wgetURL(url, filename, overwrite=True, auto_retry=False):
    if os.path.exists(filename):
        if overwrite:
            os.remove(filename)
        else:
            raise Exception('Output file already exists; if you\'re happy to overwrite it, re-run wgetURL with overwrite=True.')
    success = False
    while success==False:
        try:
            try:
                wget.download(url, out=filename)
            except:
                os.system('wget \"'+url+'\" -O '+filename)
            print('Successful acquisition of '+url)
            success = True
        except:
            print('Failure! Retrying acquistion of '+url)
            time.sleep(0.1)
            success = False
            if not auto_retry:
                raise Exception('Unsuccessful wget attempt.')

# Function to delete a directory "as best it can"; fully if possible, else by recursievely removing all delete-able files
# Args: Path of directory to delete
# Returns: None
def RemoveCrawl(directory):
    try:
        shutil.rmtree(directory)
    except:
        for glob_path in glob.glob(os.path.join(directory,'**'), recursive=True):
            if os.path.isfile(glob_path):
                try:
                    os.remove(glob_path)
                except:
                    pass

# Function to estimate time until a task completes
# Args: List of time taken by each iteration in units of seconds since Unix epoch, total number of iterations
# Returns: Python time string of estimated time/date of completion
def TimeEst(time_list, total, plot=False, raw=False):

    # Convert times into log space, fit trend, project, and un-log
    time_list_log = np.log10(np.array(time_list))
    iter_list = np.arange( 0.0, float(len(time_list)) )
    time_fit_log = np.polyfit(iter_list, time_list_log, 1)

    # Find offset between most recent time value, and fit at that point
    time_latest_actual = time_list[-1:][0]
    time_latest_predicted_log = ( time_fit_log[0] * (float(np.array(time_list).shape[0])-1.0) ) + time_fit_log[1]
    time_latest_offset_log = np.log10(time_latest_actual) - time_latest_predicted_log
    time_fit_log[1] += time_latest_offset_log

    # Predict time of completion
    time_end_log = ( time_fit_log[0] * total ) + time_fit_log[1]
    time_end = 10.0**time_end_log

    # If requested, create plot
    if plot:
        fig = plt.figure(figsize=(8,6))
        ax_dims = [0.125, 0.125, 0.825, 0.825]
        ax = fig.add_axes(ax_dims)

        # Create plotting arrays
        time_list_hours = ( np.array(time_list) - time_list[0] ) / 3600.0
        time_end_hours = ( time_end - time_list[0] ) / 3600.0
        line_x = np.linspace(0, total, 10000)
        line_y_log = ( time_fit_log[0] * line_x ) + time_fit_log[1]
        line_y = ( 10.0**( line_y_log ) - time_list[0] ) / 3600.0

        # Plot points, and line of best fit
        ax.scatter(iter_list, time_list_hours, c='#4D78C9', marker='o', s=25, linewidths=0)
        ax.scatter(total, time_end_hours, c='#C03030', marker='H', s=100, linewidths=0)
        ax.plot(line_x, line_y, ls='--', lw=1.0, c='#C03030')

        # Format axis limts and labels
        ax.set_xlabel(r'Iteration', fontsize=15)
        ax.set_ylabel(r'Time Since Start (hrs)', fontsize=15)
        ax.set_xlim(0.0,1.1*line_x.max())
        ax.set_ylim(0.0,1.1*line_y.max())
        for xlabel in ax.get_xticklabels():
            xlabel.set_fontproperties(matplotlib.font_manager.FontProperties(size=15))
        for ylabel in ax.get_yticklabels():
            ylabel.set_fontproperties(matplotlib.font_manager.FontProperties(size=15))
        ax.grid(linestyle='dotted')
        ax.xaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
        ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())

    # Produce human-readable output, if required
    if not raw:
        time_end = time.strftime('%H:%M:%S %a %d %b %Y', time.localtime(time_end))

    # Return estimate (and plot, if requested)
    if plot:
        return time_end, fig
    elif not plot:
        return time_end

# Function that uses small files in a temporary folder to track progress of parallel functions
# Input; Progress directory to use, total number of iterations to be completed
# Outut: How many iterations have completed, and estimated completion time
def ProgressDir(prog_dir, iter_total, raw=False):

    # If directory doens't seem to exist, wait a bit to see if some parallel process makes it first
    prog_dir_exists = False
    for i in range(0,5):
        if os.path.exists(prog_dir):
            prog_dir_exists = True
            break
        else:
            time.sleep(1.0 + (5.0 * np.random.random()))

    # If directory definately doesn't already exist, create it, add time file, and finish up
    if not prog_dir_exists:
        try:
            os.mkdir(prog_dir)
            prog_file = open( os.path.join(prog_dir, str(time.time())), 'w')
            prog_file.close()
            return 1, 'pending'
        except:
            prog_dir_exists = True

    # Else if progress directroy does indeed exist, carry on
    if prog_dir_exists:

        # Create file in directory, with filename recording the current time (assuming no identically-named file exists)
        while True:
            prog_filename = os.path.join(prog_dir, str(time.time()))
            if not os.path.exists(prog_filename):
                prog_file = open(prog_filename, 'w')
                prog_file.close()
                break

        # List of all files in directory, and convert to list of completion times, and record completed iterations
        prog_list = np.array([ float(prog_time) for prog_time in os.listdir(prog_dir) ])
        prog_list.sort()
        prog_list = prog_list.tolist()
        iter_complete = len(prog_list)

        # Estimate time until completion
        time_est = TimeEst(prog_list, iter_total, raw=raw)

        # If this was the final iteration, clean up
        if iter_complete == iter_total:
            time.sleep(10)
            shutil.rmtree(prog_dir)

        # Return results
        return iter_complete, time_est

# A context manager to suppress console output, even from external code (like IDL)
# Input: Optional choice of where to redirect output to
# Output: None
def StdOutRedirect(to=os.devnull):
    raise Exception('Just use with contextlib.redirect_stdout(None) instead')

"""
# IDIUT'S GUIDE TO ELLIPTICAL APERTURES
I assume you also know the location of the ellipse's center. Call that (x0,y0).
Let t be the counterclockwise angle the major axis makes with respect to the
x-axis. Let a and b be the semi-major and semi-minor axes, respectively. If
P = (x,y) is an arbitrary point then do this:

X = (x-x0)*cos(t)+(y-y0)*sin(t); % Translate and rotate coords.
Y = -(x-x0)*sin(t)+(y-y0)*cos(t); % to align with ellipse

If

X^2/a^2+Y^2/b^2

is less than 1, the point P lies inside the ellipse. If it equals 1, it is right on
the ellipse. If it is greater than 1, P is outside.
"""

"""
target = wcs_in.wcs_world2pix(np.array([[178.54283, 0.1388639]]), 0)
cutout_inviolate = ChrisFuncs.PurgeStarPieter(cutout_inviolate, 298.49901264, 304.47718041, 0, 5)
ChrisFuncs.FitsCutout('E:\\Work\\H-ATLAS\\HAPLESS_Cutouts\\HAPLESS_1_NUV.fits', 178.55113899, 0.13662582, 1000, exten=0, variable=False, outfile='E:\\Work\\NUV.fits')
"""


<filename>examples/multidaq_sync.py
"""
Synchronised multiple daqs
==========================

This examples demonstrates the use of multiple daq devices in a synchronised
fashion. To synchronise the two daqs, we have to oversample the slower one.
Daqs need not have sampling rates that are integer multiples of one another,
yet, it makes sense to set their read_sizes such that it takes "roughly" the
same time to perform a read operation from the various devices. A DumbDaq is
used as a master daq to control the main program update rate (i.e. master
clock). We might want to set the properties of the master daq to be equal to
that of the faster of the real daqs. This is however not strict; we could
also set the read time of the  master daq to be even faster (but not slower).
In that case, we would end up also oversampling the faster daq. An alternative
would be to use the faster daq as the master daq. When each daq is updated
we store its values in a cache. When the master clock ticks, the cache from
each daq is queried. Note that by "fast" and "slow", we do not refer to the
sampling rate of the daq, but rather the time needed to complete a `read()`
operation, which is dependent on both the rate and the read_size.

The example demonstrates how to store the raw data from the multiple devices
and also how to combine (through concatenation) and store their processed data.
The various devices may provide series of different lengths but interpolation
is not required as the final step of the processing pipeline includes a
feature computation that is performed along the time axis.

If ``rate_m``, ``read_size_m`` and ``counter_m`` are the sampling rate, read
size and the counter limit, respectively, of the master daq, and ``rate`` and
``read_size`` are the rate and read size of a real daq, then the output raw
data array for this device is expected to have shape:
(n_channels, rate * int(counter_m * read_size_m / rate_m)).

The concatenated processed data array will have shape:
(n_total_channels, counter_m), where ``n_total_channels`` is the sum of the
number of channels of the various devices.
"""

import numpy as np
from scipy.signal import butter

from axopy.experiment import Experiment
from axopy.task import Task
from axopy.timing import Counter
from axopy.gui.canvas import Canvas, Text
from axopy.features import mean_absolute_value
from axopy.pipeline import Pipeline, Windower, Callable, Filter

class CountTask(Task):
    def __init__(self, rate, readsize):
        super().__init__()
        self.rate = rate
        self.readsize = readsize
        self.pipeline = {
            'daq_1': self.make_pipeline(rate=self.rate['daq_1'],
                                        readsize=self.readsize['daq_1'],
                                        winsize=250,
                                        lowpass=100,
                                        highpass=500),
            'daq_2': self.make_pipeline(rate=self.rate['daq_2'],
                                        readsize=self.readsize['daq_2'],
                                        winsize=15,
                                        lowpass=5,
                                        highpass=15)
        }
        # This is where the data from the multiple streams will be stored
        # after they have been processed.
        self.cache = {'daq_1': None, 'daq_2': None}

    def make_pipeline(self, rate, readsize, winsize, lowpass, highpass):
        b, a = butter(4, (lowpass/rate/2., highpass/rate/2.), 'bandpass')
        pipeline = Pipeline([
            Windower(winsize),
            # overlap = winsize - read_rate
            Filter(b, a=a, overlap=winsize-readsize),
            Callable(mean_absolute_value,
                     func_kwargs={'axis': 1, 'keepdims': True})
        ])

        return pipeline

    def prepare_design(self, design):
        block = design.add_block()
        block.add_trial()

    def prepare_graphics(self, container):
        self.text0 = Text('Master: 0')
        self.text0.pos = -0.2, 0.5

        self.text1 = Text('Daq 1: 0')
        self.text1.pos = -0.2, 0

        self.text2 = Text('Daq 2: 0')
        self.text2.pos = -0.2, -0.5

        canvas = Canvas()
        canvas.add_item(self.text0)
        canvas.add_item(self.text1)
        canvas.add_item(self.text2)
        container.set_widget(canvas)

    def prepare_daq(self, daqstream):
        # The master counter will determine trial time, i.e.
        # trial_time = counter_limit * read_daq / rate_daq
        self.counter0 = Counter(101, reset_on_timeout=False)
        self.counter1 = Counter(1000)
        self.counter2 = Counter(1000)

        self.counter0.timeout.connect(self.finish_trial)

        self.daqstream = daqstream

    def prepare_storage(self, storage):
        self.writer = storage.create_task('async_daqs')

    def run_trial(self, trial):
        trial.add_array('dev_1_raw', stack_axis=1)
        trial.add_array('dev_2_raw', stack_axis=1)
        trial.add_array('dev_12_processed', stack_axis=1)

        self.daqstream['daq_0'].start()
        self.daqstream['daq_1'].start()
        self.daqstream['daq_2'].start()

        self.daqstream['daq_0'].updated.connect(self.update_daq0)
        self.daqstream['daq_1'].updated.connect(self.update_daq1)
        self.daqstream['daq_2'].updated.connect(self.update_daq2)

    def update_daq0(self):
        # Daq 0 is the "master" Daq, i.e. an update happens when daq 0 is
        # updated. The check is used to ensure that updates start only after
        # the two streams have started providing data.
        if not any(elem is None for elem in self.cache.values()):
            daq1data = self.cache['daq_1'].copy()
            daq2data = self.cache['daq_2'].copy()
            data_c = np.concatenate((daq1data, daq2data), axis=0)

            self.trial.arrays['dev_12_processed'].stack(data_c)

            self.counter0.increment()
            self.text0.qitem.setText("Master: " + str(self.counter0.count))

    def update_daq1(self, data):
        self.counter1.increment()
        self.text1.qitem.setText("Daq 1: " + str(self.counter1.count))

        data_proc = self.pipeline['daq_1'].process(data)
        self.cache['daq_1'] = data_proc
        self.trial.arrays['dev_1_raw'].stack(data)

    def update_daq2(self, data):
        self.counter2.increment()
        self.text2.qitem.setText("Daq 2: " + str(self.counter2.count))

        data_proc = self.pipeline['daq_2'].process(data)
        self.cache['daq_2'] = data_proc
        self.trial.arrays['dev_2_raw'].stack(data)

    def finish_trial(self):
        self.writer.write(self.trial)

        self.daqstream['daq_0'].updated.disconnect(self.update_daq0)
        self.daqstream['daq_1'].updated.disconnect(self.update_daq1)
        self.daqstream['daq_2'].updated.disconnect(self.update_daq2)

        # use wait=False so these don't hang up the final graphics update
        self.daqstream['daq_0'].stop(wait=False)
        self.daqstream['daq_1'].stop(wait=False)
        self.daqstream['daq_2'].stop(wait=False)

        self.finish()

if __name__ == '__main__':
    from axopy.daq import NoiseGenerator, DumbDaq
    rate = {'daq_0': 1000, 'daq_1': 2000, 'daq_2': 42}
    readsize = {'daq_0': 100, 'daq_1': 200, 'daq_2': 10}

    daq0 = DumbDaq(rate=rate['daq_0'], read_size=readsize['daq_0'])
    daq1 = NoiseGenerator(num_channels=4, rate=rate['daq_1'],
                          read_size=readsize['daq_1'])
    daq2 = NoiseGenerator(num_channels=2, rate=rate['daq_2'],
                          read_size=readsize['daq_2'])

    exp = Experiment(daq={'daq_0': daq0, 'daq_1': daq1, 'daq_2': daq2},
                     subject='test', allow_overwrite=True)
    exp.run(CountTask(rate, readsize))


<reponame>ChriPiv/stinespring-algo-paper
# (C) Copyright IBM 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

import autograd
import autograd.numpy as np
from scipy.optimize import minimize
from qiskit import *
from qiskit.quantum_info import *
from qiskit.aqua.components.variational_forms import *
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.utils import insert_noise

sys.path.append("..")
from json_tools import *
from channels import *
from variational_approximation import error_mean, get_approx_circuit, get_varform_circuit
from diamond_norm import *
import autograd.numpy as np

n_qubits = 3
full_connectivity = False

U = random_unitary(2**n_qubits, seed=1234).data
noise_model = NoiseModel.from_dict(json_from_file("2020_04_08.json"))
noise_model.add_quantum_error(noise_model._local_quantum_errors['cx']['2,3'], 'cx', [0,2])
noise_model.add_quantum_error(noise_model._local_quantum_errors['cx']['3,2'], 'cx', [2,0])

def dilation_channel(data, is_unitary=True, ideal=False):
    exp = channel_expand(n_qubits-1,1)
    if is_unitary:
        qc = QuantumCircuit(n_qubits)
        qc.unitary(data, list(range(n_qubits)))
    else:
        qc = data
    if not ideal:
        if not full_connectivity:
            qc = qiskit.compiler.transpile(qc, basis_gates=noise_model.basis_gates,
                                               coupling_map=[[0,1],[1,2]])
        qc = insert_noise(qc, noise_model, transpile=True)
    qc = SuperOp(qc)
    tr = channel_trace(n_qubits-1,1)
    channel = exp.compose(qc.compose(tr))
    return Choi(channel).data

ch_ideal = dilation_channel(U, ideal=True)
ch_ref = dilation_channel(U)
assert Choi(ch_ideal).is_tp()
assert Choi(ch_ideal).is_cp()
assert Choi(ch_ref).is_tp()
assert Choi(ch_ref).is_cp()
print("Ref:", dnorm(ch_ideal - ch_ref))

if full_connectivity:
    depth_list = [1,2,3,4,5,6,7,8,9,10,15]
else:
    depth_list = [1,3,4,5,6,7,8,9,10,15,20,30,40]
for depth in depth_list:
    U_approx,params = get_approx_circuit(U, n_qubits, depth, full_connectivity)
    qc = get_varform_circuit(params, n_qubits, depth, full_connectivity)
    ch = dilation_channel(qc, is_unitary=False)
    print(depth, error_mean(U, U_approx, 2), dnorm(ch - ch_ideal))


<reponame>machendos/graphics-lab<gh_stars>0
import cv2
import skimage as s
import scipy.ndimage as sc
import numpy as np
from matplotlib import pyplot as plt

# prewitt mask
moon = cv2.imread('../images/moon.jpg', 0)
img = s.img_as_int(moon)
prewittVert = np.array(
  [[-1,0,1],[-1,0,1],[-1,0,1]], dtype='float64'
)
prewittVertOut = s.img_as_ubyte(sc.convolve(img, prewittVert, mode='constant', cval=0.0))
plt.imshow(prewittVertOut, cmap='gray')
plt.show()


import cPickle
import numpy as n
import astropy.cosmology as co
import astropy.units as uu
aa =co.Planck13
import time
from astropy.io import fits
import os
from os.path import join
import matplotlib.pyplot as p
from scipy.interpolate import interp1d
mockDir = join("..","MD_1Gpc","density_field")

inFiles = n.array(["dmdens_cic_104_DFhist.dat", "dmdens_cic_101_DFhist.dat", "dmdens_cic_097_DFhist.dat", "dmdens_cic_087_DFhist.dat"])
# ZS = 0.7 0.8 1.0 1.48

def getNN0_sim(inSim,NR=10):
	f=open(join(mockDir, inSim))
	bins, HDF0 = cPickle.load(f)
	f.close()
	#bins = n.hstack((0,n.logspace(-3, 4, 1000)))
	xb = (bins[1:]+bins[:-1])/2.
	dx = bins[1:] - bins[:-1]
	X, Y = n.meshgrid(xb,xb)
	N0 = HDF0 /dx / (1000.-2*1000./2048)**3.
	HDF0R = n.array([HDF0[ii::NR] for ii in range(NR)]).sum(axis=0)
	binsR = bins[::NR]
	N0R = HDF0R / ((binsR[1:] - binsR[:-1]) * 250.**3.)
	return N0, bins, N0R, binsR

N0z07s, binsz07s, N0z07, binsz07 = getNN0_sim(inFiles[0])
xb = (binsz07[1:]+binsz07[:-1])/2.

f=open(join(mockDir,'Planck-ng512-L250.0.HDF0.pkl'),'r')
bins, HDF0, N0 = cPickle.load(f)
f.close()

NR = 10
HDF0R = n.array([HDF0[ii::NR] for ii in range(NR)]).sum(axis=0)
binsR = bins[::NR]
N0R = HDF0R / ((binsR[1:] - binsR[:-1]) * 250.**3.)
N0R_sig = n.array([N0[ii::NR] for ii in range(NR)]).std(axis=0)

muscleDelta = interp1d( N0R, (binsR[:-1]+binsR[1:])/2.)
mdplDelta = interp1d( N0z07, xb)
ok=(N0R>0)&(N0R<=100)#n.max(N0z07))
trueDelta = mdplDelta(N0R[ok])
index=n.argsort(N0R[ok])
deltaMuscle = (binsR[:-1]+binsR[1:])/2.
n.savetxt(join(mockDir,"delta-conversion-muscle-mdpl.txt"),n.transpose([deltaMuscle[ok][index],trueDelta[index]]),header="deltaMuscle deltaMDPL")

p.figure(0)
p.title('QSO')
p.plot(xb, N0z07,'kx', rasterized=True, label='z=0.7 all')
p.plot(xb, N0,'bx', rasterized=True, label='z=0.7 muscle')
p.plot((binsR[:-1]+binsR[1:])/2., N0R,'rx', rasterized=True, label='z=0.7 muscle resampled')
p.plot(trueDelta[index], N0R[ok][index], 'm--', lw=2, rasterized=True, label='z=0.7 muscle corr')
p.xlabel(r'$\delta_0$')
p.ylabel(r'N')
p.xscale('log')
p.yscale('log')
p.ylim((1e-10, 1e2))
p.xlim((0.1, 1e4))
gl = p.legend(loc=3)
gl.set_frame_on(False)
p.grid()
p.savefig(join(mockDir,"plots","muscle-delta-HDF0.png"))
p.show()


<reponame>doanmanhduy0210/cosface
# Code referenced from https://gist.github.com/gyglim/1f8dfb1b5c82627ae3efcfbbadb9f514
# tensorboard --logdir='./logs' --port=6006
# tensorboard --logdir=old:out/2019-07-20___18-13-10/tensorboard,new:out/2019-07-21___14-45-41/tensorboard --port=6006

import os
import sys
import argparse

import tensorflow as tf
import numpy as np
import scipy.misc
try:
    from StringIO import StringIO  # Python 2.7
except ImportError:
    from io import BytesIO         # Python 3.x

class Logger(object):

    def __init__(self, log_dir):
        """Create a summary writer logging to log_dir."""
        self.writer = tf.summary.FileWriter(log_dir)

    def scalar_summary(self, tag, value, step):
        """Log a scalar variable."""
        summary = tf.Summary(value=[tf.Summary.Value(tag=tag, simple_value=value)])
        self.writer.add_summary(summary, step)

    def image_summary(self, tag, images, step):
        """Log a list of images."""

        img_summaries = []
        for i, img in enumerate(images):
            # Write the image to a string
            try:
                s = StringIO()
            except:
                s = BytesIO()
            scipy.misc.toimage(img).save(s, format="png")

            # Create an Image object
            img_sum = tf.Summary.Image(encoded_image_string=s.getvalue(),
                                       height=img.shape[0],
                                       width=img.shape[1])
            # Create a Summary value
            img_summaries.append(tf.Summary.Value(tag='%s/%d' % (tag, i), image=img_sum))

        # Create and write Summary
        summary = tf.Summary(value=img_summaries)
        self.writer.add_summary(summary, step)

    def histo_summary(self, tag, values, step, bins=1000):
        """Log a histogram of the tensor of values."""

        # Create a histogram using numpy
        counts, bin_edges = np.histogram(values, bins=bins)

        # Fill the fields of the histogram proto
        hist = tf.HistogramProto()
        hist.min = float(np.min(values))
        hist.max = float(np.max(values))
        hist.num = int(np.prod(values.shape))
        hist.sum = float(np.sum(values))
        hist.sum_squares = float(np.sum(values**2))

        # Drop the start of the first bin
        bin_edges = bin_edges[1:]

        # Add bin edges and counts
        for edge in bin_edges:
            hist.bucket_limit.append(edge)
        for c in counts:
            hist.bucket.append(c)

        # Create and write Summary
        summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)])
        self.writer.add_summary(summary, step)
        self.writer.flush()

def main(ARGS):
    out_dir = os.path.expanduser(ARGS.out_dir)
    assert os.path.isdir(out_dir), "Path {} does not exist ".format(ARGS.out_dir)

    print("\nStarting Tensorboard")

    date_folders = []
    files = os.listdir(out_dir)
    if len(files):
        for f in files:
            date_folders.append(f)

    assert date_folders, "No tensorboard folders"

    command_string = "tensorboard --logdir="
    date_folders.sort()
    for i,folder in enumerate(date_folders):
        tensorboard_path = os.path.join(os.path.join(out_dir, folder), 'tensorboard')
        # print("Full Tensorboard Path: {}".format(tensorboard_path))
        command_string += str(i+1) + ":" + tensorboard_path
        if i != len(date_folders)-1:
            command_string += ","

    # Port
    command_string += " --port={}".format(ARGS.tensorboard_port)

    print("FINAL Command String: {}".format(command_string))
    print("\n")
    os.system(command_string)

def parse_arguments(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument('--out_dir', type=str,  help='Directory where models and event logs are stored.', default='./out')
    parser.add_argument('--tensorboard_port', type=int, help='Tensorboard port for command', default=6006)
    return parser.parse_args(argv)

if __name__ == '__main__':
    main(parse_arguments(sys.argv[1:]))