<commit_before># ----------------------------------------------------------------------------
# Copyright (c) 2016-2018, QIIME 2 development team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# ----------------------------------------------------------------------------

import os

import click

import q2cli

@click.group(help='Tools for working with QIIME 2 files.')
def tools():
    pass

@tools.command(name='export',
               short_help='Export data from a QIIME 2 Artifact or '
                          'Visualization.',
               help="Export data from a QIIME 2 Artifact or Visualization. "
                    "Exporting extracts the data stored in an Artifact or "
                    "Visualization and will support exporting to multiple "
                    "formats in the future. For now, the data is exported in "
                    "the format it is stored within the Artifact or "
                    "Visualization. Use 'qiime tools extract' to extract the "
                    "Artifact or Visualization's entire archive.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--output-dir', required=True,
              type=click.Path(exists=False, file_okay=False, dir_okay=True,
                              writable=True),
              help='Directory where data should be exported to')
def export_data(path, output_dir):
    import qiime2.sdk

    result = qiime2.sdk.Result.load(path)
    result.export_data(output_dir)

def show_importable_types(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return

    import qiime2.sdk

    importable_types = sorted(qiime2.sdk.PluginManager().importable_types,
                              key=repr)

    if importable_types:
        for name in importable_types:
            click.echo(name)
    else:
        click.echo('There are no importable types in the current deployment.')

    ctx.exit()

def show_importable_formats(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return

    import qiime2.sdk

    importable_formats = sorted(qiime2.sdk.PluginManager().importable_formats)

    if importable_formats:
        for format in importable_formats:
            click.echo(format)
    else:
        click.echo('There are no importable formats '
                   'in the current deployment.')

    ctx.exit()

@tools.command(name='import',
               short_help='Import data into a new QIIME 2 Artifact.',
               help="Import data to create a new QIIME 2 Artifact. See "
                    "https://docs.qiime2.org/ for usage examples and details "
                    "on the file types and associated semantic types that can "
                    "be imported.")
@q2cli.option('--type', required=True,
              help='The semantic type of the artifact that will be created '
                   'upon importing. Use --show-importable-types to see what '
                   'importable semantic types are available in the current '
                   'deployment.')
@q2cli.option('--input-path', required=True,
              type=click.Path(exists=True, file_okay=True, dir_okay=True,
                              readable=True),
              help='Path to file or directory that should be imported.')
@q2cli.option('--output-path', required=True,
              type=click.Path(exists=False, file_okay=True, dir_okay=False,
                              writable=True),
              help='Path where output artifact should be written.')
@q2cli.option('--source-format', required=False,
              help='The format of the data to be imported. If not provided, '
                   'data must be in the format expected by the semantic type '
                   'provided via --type.')
@q2cli.option('--show-importable-types', is_flag=True, is_eager=True,
              callback=show_importable_types, expose_value=False,
              help='Show the semantic types that can be supplied to --type '
                   'to import data into an artifact.')
@q2cli.option('--show-importable-formats', is_flag=True, is_eager=True,
              callback=show_importable_formats, expose_value=False,
              help='Show formats that can be supplied to --source-format to '
                   'import data into an artifact.')
def import_data(type, input_path, output_path, source_format):
    import qiime2.sdk
    import qiime2.plugin
    try:
        artifact = qiime2.sdk.Artifact.import_data(type, input_path,
                                                   view_type=source_format)
    except qiime2.plugin.ValidationError as e:
        header = 'There was a problem importing %s:' % input_path
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = 'An unexpected error has occurred:'
        q2cli.util.exit_with_error(e, header=header)
    artifact.save(output_path)

@tools.command(short_help='Take a peek at a QIIME 2 Artifact or '
                          'Visualization.',
               help="Display basic information about a QIIME 2 Artifact or "
                    "Visualization, including its UUID and type.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
def peek(path):
    import qiime2.sdk

    metadata = qiime2.sdk.Result.peek(path)

    click.secho("UUID:        ", fg="green", nl=False)
    click.secho(metadata.uuid)
    click.secho("Type:        ", fg="green", nl=False)
    click.secho(metadata.type)
    if metadata.format is not None:
        click.secho("Data format: ", fg="green", nl=False)
        click.secho(metadata.format)

@tools.command('inspect-metadata',
               short_help='Inpect columns available in metadata.',
               help='Inspect metadata files or artifacts viewable as metadata.'
                    ' Providing multiple file paths to this command will merge'
                    ' the metadata.')
@q2cli.option('--tsv/--no-tsv', default=False,
              help='Print as machine-readable TSV instead of text.')
@click.argument('paths', nargs=-1, required=True,
                type=click.Path(exists=True, file_okay=True, dir_okay=False,
                                readable=True))
@q2cli.util.pretty_failure(traceback=None)
def inspect_metadata(paths, tsv, failure):
    m = [_load_metadata(p) for p in paths]
    metadata = m[0]
    if m[1:]:
        metadata = metadata.merge(*m[1:])

    # we aren't expecting errors below this point, so set traceback to default
    failure.traceback = 'stderr'
    failure.header = "An unexpected error has occurred:"

    COLUMN_NAME = "COLUMN NAME"
    COLUMN_TYPE = "TYPE"
    max_name_len = max([len(n) for n in metadata.columns]
                       + [len(COLUMN_NAME)])
    max_type_len = max([len(p.type) for p in metadata.columns.values()]
                       + [len(COLUMN_TYPE)])

    if tsv:
        import csv
        import io

        def formatter(*row):
            # This is gross, but less gross than robust TSV writing.
            with io.StringIO() as fh:
                writer = csv.writer(fh, dialect='excel-tab', lineterminator='')
                writer.writerow(row)
                return fh.getvalue()
    else:
        formatter = ("{0:>%d}  {1:%d}" % (max_name_len, max_type_len)).format

    click.secho(formatter(COLUMN_NAME, COLUMN_TYPE), bold=True)
    if not tsv:
        click.secho(formatter("=" * max_name_len, "=" * max_type_len),
                    bold=True)

    for name, props in metadata.columns.items():
        click.echo(formatter(name, props.type))

    if not tsv:
        click.secho(formatter("=" * max_name_len, "=" * max_type_len),
                    bold=True)
        click.secho(("{0:>%d}  " % max_name_len).format("IDS:"),
                    bold=True, nl=False)
        click.echo(metadata.id_count)
        click.secho(("{0:>%d}  " % max_name_len).format("COLUMNS:"),
                    bold=True, nl=False)
        click.echo(metadata.column_count)

def _load_metadata(path):
    import qiime2
    import qiime2.sdk

    # TODO: clean up duplication between this and the metadata handlers.
    try:
        artifact = qiime2.sdk.Result.load(path)
    except Exception:
        metadata = qiime2.Metadata.load(path)
    else:
        if isinstance(artifact, qiime2.Visualization):
            raise Exception("Visualizations cannot be viewed as QIIME 2"
                            " metadata:\n%r" % path)
        elif artifact.has_metadata():
            metadata = artifact.view(qiime2.Metadata)
        else:
            raise Exception("Artifacts with type %r cannot be viewed as"
                            " QIIME 2 metadata:\n%r" % (artifact.type, path))

    return metadata

@tools.command(short_help='View a QIIME 2 Visualization.',
               help="Displays a QIIME 2 Visualization until the command "
                    "exits. To open a QIIME 2 Visualization so it can be "
                    "used after the command exits, use 'qiime extract'.")
@click.argument('visualization-path',
                type=click.Path(exists=True, file_okay=True, dir_okay=False,
                                readable=True))
@q2cli.option('--index-extension', required=False, default='html',
              help='The extension of the index file that should be opened. '
                   '[default: html]')
def view(visualization_path, index_extension):
    # Guard headless envs from having to import anything large
    import sys
    if not os.getenv("DISPLAY") and sys.platform != "darwin":
        raise click.UsageError(
            'Visualization viewing is currently not supported in headless '
            'environments. You can view Visualizations (and Artifacts) at '
            'https://view.qiime2.org, or move the Visualization to an '
            'environment with a display and view it with `qiime tools view`.')

    import zipfile
    import qiime2.sdk

    if index_extension.startswith('.'):
        index_extension = index_extension[1:]
    try:
        visualization = qiime2.sdk.Visualization.load(visualization_path)
    # TODO: currently a KeyError is raised if a zipped file that is not a
    # QIIME 2 result is passed. This should be handled better by the framework.
    except (zipfile.BadZipFile, KeyError, TypeError):
        raise click.BadParameter(
            '%s is not a QIIME 2 Visualization. Only QIIME 2 Visualizations '
            'can be viewed.' % visualization_path)

    index_paths = visualization.get_index_paths(relative=False)

    if index_extension not in index_paths:
        raise click.BadParameter(
            'No index %s file with is present in the archive. Available index '
            'extensions are: %s' % (index_extension,
                                    ', '.join(index_paths.keys())))
    else:
        index_path = index_paths[index_extension]
        launch_status = click.launch(index_path)
        if launch_status != 0:
            click.echo('Viewing visualization failed while attempting to '
                       'open %s' % index_path, err=True)
        else:
            while True:
                click.echo(
                    "Press the 'q' key, Control-C, or Control-D to quit. This "
                    "view may no longer be accessible or work correctly after "
                    "quitting.", nl=False)
                # There is currently a bug in click.getchar where translation
                # of Control-C and Control-D into KeyboardInterrupt and
                # EOFError (respectively) does not work on Python 3. The code
                # here should continue to work as expected when the bug is
                # fixed in Click.
                #
                # https://github.com/pallets/click/issues/583
                try:
                    char = click.getchar()
                    click.echo()
                    if char in {'q', '\x03', '\x04'}:
                        break
                except (KeyboardInterrupt, EOFError):
                    break

@tools.command(short_help="Extract a QIIME 2 Artifact or Visualization "
                          "archive.",
               help="Extract all contents of a QIIME 2 Artifact or "
                    "Visualization's archive, including provenance, metadata, "
                    "and actual data. Use 'qiime tools export' to export only "
                    "the data stored in an Artifact or Visualization, with "
                    "the choice of exporting to different formats.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--output-dir', required=False,
              type=click.Path(exists=True, file_okay=False, dir_okay=True,
                              writable=True),
              help='Directory where archive should be extracted to '
                   '[default: current working directory]',
              default=os.getcwd())
def extract(path, output_dir):
    import zipfile
    import qiime2.sdk

    try:
        extracted_dir = qiime2.sdk.Result.extract(path, output_dir)
    except (zipfile.BadZipFile, ValueError):
        raise click.BadParameter(
            '%s is not a valid QIIME 2 Result. Only QIIME 2 Artifacts and '
            'Visualizations can be extracted.' % path)
    else:
        click.echo('Extracted to %s' % extracted_dir)

@tools.command(short_help='Validate data in a QIIME 2 Artifact.',
               help='Validate data in a QIIME 2 Artifact. QIIME 2 '
                    'automatically performs some basic validation when '
                    'managing your data; use this command to perform explicit '
                    'and/or more thorough validation of your data (e.g. when '
                    'debugging issues with your data or analyses).\n\nNote: '
                    'validation can take some time to complete, depending on '
                    'the size and type of your data.')
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--level', required=False, type=click.Choice(['min', 'max']),
              help='Desired level of validation. "min" will perform minimal '
                   'validation, and "max" will perform maximal validation (at '
                   'the potential cost of runtime).',
              default='max', show_default=True)
def validate(path, level):
    import qiime2.sdk

    try:
        artifact = qiime2.sdk.Artifact.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 Artifact:' % path
        q2cli.util.exit_with_error(e, header=header)

    try:
        artifact.validate(level)
    except qiime2.plugin.ValidationError as e:
        header = 'Artifact %s does not appear to be valid at level=%s:' % (
                path, level)
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = ('An unexpected error has occurred while attempting to '
                  'validate artifact %s:' % path)
        q2cli.util.exit_with_error(e, header=header)
    else:
        click.secho('Artifact %s appears to be valid at level=%s.'
                    % (path, level), fg="green")

@tools.command(short_help='Print citations for a QIIME 2 result.',
               help='Print citations as a BibTex file (.bib) for a QIIME 2'
                    ' result.')
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
def citations(path):
    import qiime2.sdk
    import io
    ctx = click.get_current_context()

    try:
        result = qiime2.sdk.Result.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 result:' % path
        q2cli.util.exit_with_error(e, header=header)

    if result.citations:
        with io.StringIO() as fh:
            result.citations.save(fh)
            click.echo(fh.getvalue(), nl=False)
        ctx.exit(0)
    else:
        click.secho('No citations found.', fg='yellow', err=True)
        ctx.exit(1)
<commit_msg>DOC: Fixed typo in inspect-metadata helpstring (#188)<commit_after># ----------------------------------------------------------------------------
# Copyright (c) 2016-2018, QIIME 2 development team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
# ----------------------------------------------------------------------------

import os

import click

import q2cli

@click.group(help='Tools for working with QIIME 2 files.')
def tools():
    pass

@tools.command(name='export',
               short_help='Export data from a QIIME 2 Artifact or '
                          'Visualization.',
               help="Export data from a QIIME 2 Artifact or Visualization. "
                    "Exporting extracts the data stored in an Artifact or "
                    "Visualization and will support exporting to multiple "
                    "formats in the future. For now, the data is exported in "
                    "the format it is stored within the Artifact or "
                    "Visualization. Use 'qiime tools extract' to extract the "
                    "Artifact or Visualization's entire archive.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--output-dir', required=True,
              type=click.Path(exists=False, file_okay=False, dir_okay=True,
                              writable=True),
              help='Directory where data should be exported to')
def export_data(path, output_dir):
    import qiime2.sdk

    result = qiime2.sdk.Result.load(path)
    result.export_data(output_dir)

def show_importable_types(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return

    import qiime2.sdk

    importable_types = sorted(qiime2.sdk.PluginManager().importable_types,
                              key=repr)

    if importable_types:
        for name in importable_types:
            click.echo(name)
    else:
        click.echo('There are no importable types in the current deployment.')

    ctx.exit()

def show_importable_formats(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return

    import qiime2.sdk

    importable_formats = sorted(qiime2.sdk.PluginManager().importable_formats)

    if importable_formats:
        for format in importable_formats:
            click.echo(format)
    else:
        click.echo('There are no importable formats '
                   'in the current deployment.')

    ctx.exit()

@tools.command(name='import',
               short_help='Import data into a new QIIME 2 Artifact.',
               help="Import data to create a new QIIME 2 Artifact. See "
                    "https://docs.qiime2.org/ for usage examples and details "
                    "on the file types and associated semantic types that can "
                    "be imported.")
@q2cli.option('--type', required=True,
              help='The semantic type of the artifact that will be created '
                   'upon importing. Use --show-importable-types to see what '
                   'importable semantic types are available in the current '
                   'deployment.')
@q2cli.option('--input-path', required=True,
              type=click.Path(exists=True, file_okay=True, dir_okay=True,
                              readable=True),
              help='Path to file or directory that should be imported.')
@q2cli.option('--output-path', required=True,
              type=click.Path(exists=False, file_okay=True, dir_okay=False,
                              writable=True),
              help='Path where output artifact should be written.')
@q2cli.option('--source-format', required=False,
              help='The format of the data to be imported. If not provided, '
                   'data must be in the format expected by the semantic type '
                   'provided via --type.')
@q2cli.option('--show-importable-types', is_flag=True, is_eager=True,
              callback=show_importable_types, expose_value=False,
              help='Show the semantic types that can be supplied to --type '
                   'to import data into an artifact.')
@q2cli.option('--show-importable-formats', is_flag=True, is_eager=True,
              callback=show_importable_formats, expose_value=False,
              help='Show formats that can be supplied to --source-format to '
                   'import data into an artifact.')
def import_data(type, input_path, output_path, source_format):
    import qiime2.sdk
    import qiime2.plugin
    try:
        artifact = qiime2.sdk.Artifact.import_data(type, input_path,
                                                   view_type=source_format)
    except qiime2.plugin.ValidationError as e:
        header = 'There was a problem importing %s:' % input_path
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = 'An unexpected error has occurred:'
        q2cli.util.exit_with_error(e, header=header)
    artifact.save(output_path)

@tools.command(short_help='Take a peek at a QIIME 2 Artifact or '
                          'Visualization.',
               help="Display basic information about a QIIME 2 Artifact or "
                    "Visualization, including its UUID and type.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
def peek(path):
    import qiime2.sdk

    metadata = qiime2.sdk.Result.peek(path)

    click.secho("UUID:        ", fg="green", nl=False)
    click.secho(metadata.uuid)
    click.secho("Type:        ", fg="green", nl=False)
    click.secho(metadata.type)
    if metadata.format is not None:
        click.secho("Data format: ", fg="green", nl=False)
        click.secho(metadata.format)

@tools.command('inspect-metadata',
               short_help='Inspect columns available in metadata.',
               help='Inspect metadata files or artifacts viewable as metadata.'
                    ' Providing multiple file paths to this command will merge'
                    ' the metadata.')
@q2cli.option('--tsv/--no-tsv', default=False,
              help='Print as machine-readable TSV instead of text.')
@click.argument('paths', nargs=-1, required=True,
                type=click.Path(exists=True, file_okay=True, dir_okay=False,
                                readable=True))
@q2cli.util.pretty_failure(traceback=None)
def inspect_metadata(paths, tsv, failure):
    m = [_load_metadata(p) for p in paths]
    metadata = m[0]
    if m[1:]:
        metadata = metadata.merge(*m[1:])

    # we aren't expecting errors below this point, so set traceback to default
    failure.traceback = 'stderr'
    failure.header = "An unexpected error has occurred:"

    COLUMN_NAME = "COLUMN NAME"
    COLUMN_TYPE = "TYPE"
    max_name_len = max([len(n) for n in metadata.columns]
                       + [len(COLUMN_NAME)])
    max_type_len = max([len(p.type) for p in metadata.columns.values()]
                       + [len(COLUMN_TYPE)])

    if tsv:
        import csv
        import io

        def formatter(*row):
            # This is gross, but less gross than robust TSV writing.
            with io.StringIO() as fh:
                writer = csv.writer(fh, dialect='excel-tab', lineterminator='')
                writer.writerow(row)
                return fh.getvalue()
    else:
        formatter = ("{0:>%d}  {1:%d}" % (max_name_len, max_type_len)).format

    click.secho(formatter(COLUMN_NAME, COLUMN_TYPE), bold=True)
    if not tsv:
        click.secho(formatter("=" * max_name_len, "=" * max_type_len),
                    bold=True)

    for name, props in metadata.columns.items():
        click.echo(formatter(name, props.type))

    if not tsv:
        click.secho(formatter("=" * max_name_len, "=" * max_type_len),
                    bold=True)
        click.secho(("{0:>%d}  " % max_name_len).format("IDS:"),
                    bold=True, nl=False)
        click.echo(metadata.id_count)
        click.secho(("{0:>%d}  " % max_name_len).format("COLUMNS:"),
                    bold=True, nl=False)
        click.echo(metadata.column_count)

def _load_metadata(path):
    import qiime2
    import qiime2.sdk

    # TODO: clean up duplication between this and the metadata handlers.
    try:
        artifact = qiime2.sdk.Result.load(path)
    except Exception:
        metadata = qiime2.Metadata.load(path)
    else:
        if isinstance(artifact, qiime2.Visualization):
            raise Exception("Visualizations cannot be viewed as QIIME 2"
                            " metadata:\n%r" % path)
        elif artifact.has_metadata():
            metadata = artifact.view(qiime2.Metadata)
        else:
            raise Exception("Artifacts with type %r cannot be viewed as"
                            " QIIME 2 metadata:\n%r" % (artifact.type, path))

    return metadata

@tools.command(short_help='View a QIIME 2 Visualization.',
               help="Displays a QIIME 2 Visualization until the command "
                    "exits. To open a QIIME 2 Visualization so it can be "
                    "used after the command exits, use 'qiime extract'.")
@click.argument('visualization-path',
                type=click.Path(exists=True, file_okay=True, dir_okay=False,
                                readable=True))
@q2cli.option('--index-extension', required=False, default='html',
              help='The extension of the index file that should be opened. '
                   '[default: html]')
def view(visualization_path, index_extension):
    # Guard headless envs from having to import anything large
    import sys
    if not os.getenv("DISPLAY") and sys.platform != "darwin":
        raise click.UsageError(
            'Visualization viewing is currently not supported in headless '
            'environments. You can view Visualizations (and Artifacts) at '
            'https://view.qiime2.org, or move the Visualization to an '
            'environment with a display and view it with `qiime tools view`.')

    import zipfile
    import qiime2.sdk

    if index_extension.startswith('.'):
        index_extension = index_extension[1:]
    try:
        visualization = qiime2.sdk.Visualization.load(visualization_path)
    # TODO: currently a KeyError is raised if a zipped file that is not a
    # QIIME 2 result is passed. This should be handled better by the framework.
    except (zipfile.BadZipFile, KeyError, TypeError):
        raise click.BadParameter(
            '%s is not a QIIME 2 Visualization. Only QIIME 2 Visualizations '
            'can be viewed.' % visualization_path)

    index_paths = visualization.get_index_paths(relative=False)

    if index_extension not in index_paths:
        raise click.BadParameter(
            'No index %s file with is present in the archive. Available index '
            'extensions are: %s' % (index_extension,
                                    ', '.join(index_paths.keys())))
    else:
        index_path = index_paths[index_extension]
        launch_status = click.launch(index_path)
        if launch_status != 0:
            click.echo('Viewing visualization failed while attempting to '
                       'open %s' % index_path, err=True)
        else:
            while True:
                click.echo(
                    "Press the 'q' key, Control-C, or Control-D to quit. This "
                    "view may no longer be accessible or work correctly after "
                    "quitting.", nl=False)
                # There is currently a bug in click.getchar where translation
                # of Control-C and Control-D into KeyboardInterrupt and
                # EOFError (respectively) does not work on Python 3. The code
                # here should continue to work as expected when the bug is
                # fixed in Click.
                #
                # https://github.com/pallets/click/issues/583
                try:
                    char = click.getchar()
                    click.echo()
                    if char in {'q', '\x03', '\x04'}:
                        break
                except (KeyboardInterrupt, EOFError):
                    break

@tools.command(short_help="Extract a QIIME 2 Artifact or Visualization "
                          "archive.",
               help="Extract all contents of a QIIME 2 Artifact or "
                    "Visualization's archive, including provenance, metadata, "
                    "and actual data. Use 'qiime tools export' to export only "
                    "the data stored in an Artifact or Visualization, with "
                    "the choice of exporting to different formats.")
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--output-dir', required=False,
              type=click.Path(exists=True, file_okay=False, dir_okay=True,
                              writable=True),
              help='Directory where archive should be extracted to '
                   '[default: current working directory]',
              default=os.getcwd())
def extract(path, output_dir):
    import zipfile
    import qiime2.sdk

    try:
        extracted_dir = qiime2.sdk.Result.extract(path, output_dir)
    except (zipfile.BadZipFile, ValueError):
        raise click.BadParameter(
            '%s is not a valid QIIME 2 Result. Only QIIME 2 Artifacts and '
            'Visualizations can be extracted.' % path)
    else:
        click.echo('Extracted to %s' % extracted_dir)

@tools.command(short_help='Validate data in a QIIME 2 Artifact.',
               help='Validate data in a QIIME 2 Artifact. QIIME 2 '
                    'automatically performs some basic validation when '
                    'managing your data; use this command to perform explicit '
                    'and/or more thorough validation of your data (e.g. when '
                    'debugging issues with your data or analyses).\n\nNote: '
                    'validation can take some time to complete, depending on '
                    'the size and type of your data.')
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
@q2cli.option('--level', required=False, type=click.Choice(['min', 'max']),
              help='Desired level of validation. "min" will perform minimal '
                   'validation, and "max" will perform maximal validation (at '
                   'the potential cost of runtime).',
              default='max', show_default=True)
def validate(path, level):
    import qiime2.sdk

    try:
        artifact = qiime2.sdk.Artifact.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 Artifact:' % path
        q2cli.util.exit_with_error(e, header=header)

    try:
        artifact.validate(level)
    except qiime2.plugin.ValidationError as e:
        header = 'Artifact %s does not appear to be valid at level=%s:' % (
                path, level)
        q2cli.util.exit_with_error(e, header=header, traceback=None)
    except Exception as e:
        header = ('An unexpected error has occurred while attempting to '
                  'validate artifact %s:' % path)
        q2cli.util.exit_with_error(e, header=header)
    else:
        click.secho('Artifact %s appears to be valid at level=%s.'
                    % (path, level), fg="green")

@tools.command(short_help='Print citations for a QIIME 2 result.',
               help='Print citations as a BibTex file (.bib) for a QIIME 2'
                    ' result.')
@click.argument('path', type=click.Path(exists=True, file_okay=True,
                                        dir_okay=False, readable=True))
def citations(path):
    import qiime2.sdk
    import io
    ctx = click.get_current_context()

    try:
        result = qiime2.sdk.Result.load(path)
    except Exception as e:
        header = 'There was a problem loading %s as a QIIME 2 result:' % path
        q2cli.util.exit_with_error(e, header=header)

    if result.citations:
        with io.StringIO() as fh:
            result.citations.save(fh)
            click.echo(fh.getvalue(), nl=False)
        ctx.exit(0)
    else:
        click.secho('No citations found.', fg='yellow', err=True)
        ctx.exit(1)
<|endoftext|>


<commit_before># coding: utf-8
# Raytheon BBN Technologies 2016
# Contributiors: Graham Rowlands
#
# This file contains utility functions for quince

import re

def next_available_name(node_names, label):
	# Check node_names and see if we can find 'label ###'
	# If we can, then we'll return the next available ###+1
	match_attempts = [re.match(label+' (\d+)', nn) for nn in node_names]
	try:
		match_attempts[:] = [int(m.group(1)) for m in match_attempts if m != None]
		if len(match_attempts) == 0:
			return label+" 1"
		else:
			return label+" {:d}".format(sorted(match_attempts)[-1]+1)
	except:
		return label + '???'

def strip_numbers(label):
	match_attempt = re.match('(.*?)(\d+)$', label)
	try:
		stripped = match_attempt.group(1).strip()
	except:
		stripped = label + '???'
	return stripped<commit_msg>Safer application of regex<commit_after># coding: utf-8
# Raytheon BBN Technologies 2016
# Contributiors: Graham Rowlands
#
# This file contains utility functions for quince

import re

def next_available_name(node_names, label):
	# Check node_names and see if we can find 'label ###'
	# If we can, then we'll return the next available ###+1
	try:
		match_attempts = [re.match(label+' (\d+)', nn) for nn in node_names]
		match_attempts[:] = [int(m.group(1)) for m in match_attempts if m != None]
		if len(match_attempts) == 0:
			return label+" 1"
		else:
			return label+" {:d}".format(sorted(match_attempts)[-1]+1)
	except:
		return label + ' ?'

def strip_numbers(label):
	try:
		match_attempt = re.match('(.*?)(\d+)$', label)
		stripped = match_attempt.group(1).strip()
	except:
		stripped = label
	return stripped<|endoftext|>


<commit_before>__author__ = 'gmr'
import logging
try:
    import queue
except ImportError:
    import Queue as queue
import socket
import ssl
import threading

LOGGER = logging.getLogger(__name__)

from pamqp import frame
from pamqp import exceptions as pamqp_exceptions
from pamqp import specification
from pamqp import PYTHON3

from rabbitpy import base
from rabbitpy import exceptions

class IO(threading.Thread, base.StatefulObject):

    CONNECTION_TIMEOUT = 0.01
    CONTENT_METHODS = ['Basic.Deliver', 'Basic.GetOk', 'Basic.Return']
    READ_BUFFER_SIZE = specification.FRAME_MAX_SIZE

    def __init__(self, group=None, target=None, name=None, args=(),
                 kwargs=None):
        if kwargs is None:
            kwargs = dict()
        super(IO, self).__init__(group, target, name, args, kwargs)

        self._args = kwargs['connection_args']
        self._on_connected = kwargs['connected']
        self._write_queue = kwargs['write_queue']
        self._close_event = kwargs['close']

        self._buffer = bytes()
        self._channels = dict()
        self._remote_name = None
        self._socket = None
        self._state = None

    def add_channel(self, channel_number, write_queue):
        """Add a channel to the channel queue dict for dispatching frames
        to the channel.

        :param int channel_number: The channel to write
        :param Queue.Queue write_queue: Queue for sending frames to the channel

        """
        self._channels[channel_number] = write_queue

    def run(self):
        """Start the thread, which will connect to the socket and run the
        event loop.

        """
        self._connect()

        # Create the remote name
        address, port = self._socket.getsockname()
        peer_address, peer_port = self._socket.getpeername()
        self._remote_name = '%s:%s -> %s:%s' % (address, port,
                                                peer_address, peer_port)

        while self.open and not self._close_event.is_set():
            try:
                frame_value = self._write_queue.get(False)
                self._write_frame(*frame_value)
            except queue.Empty:
                pass

            # Read and process data
            frame_value = self._read_frame()
            if frame_value[0] is not None:
                self._channels[frame_value[0]].put(frame_value[1])

        self._close()

    def _add_frame_to_queue(self, channel_id, frame_value):
        """Add the frame to the stack by creating the key value used in
        expectations and then add it to the list.

        :param int channel_id: The channel id the frame was received on
        :param pamqp.specification.Frame frame_value: The frame to add

        """
        LOGGER.debug('Adding frame to channel %i: %s',
                     channel_id, type(frame_value))
        self._channels[channel_id].put(frame_value)

    def _close(self):
        self._set_state(self.CLOSING)
        self._socket.close()
        self._set_state(self.CLOSED)

    def _create_socket(self):
        """Create the new socket, optionally with SSL support.

        :rtype: socket.socket or ssl.SSLSocket

        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        sock.setblocking(1)
        sock.settimeout(self.CONNECTION_TIMEOUT)
        if self._args['ssl']:
            return ssl.wrap_socket(sock,
                                   self._args['ssl_key'],
                                   self._args['ssl_cert'],
                                   False,
                                   self._args['ssl_validation'],
                                   self._args['ssl_version'],
                                   self._args['ssl_cacert'])
        return sock

    def _connect_socket(self):
        """Connect the socket to the specified host and port."""
        LOGGER.debug('Connecting to %(host)s:%(port)i', self._args)
        self._socket.connect((self._args['host'], self._args['port']))

    def _connect(self):
        """Connect to the RabbitMQ Server

        :raises: ConnectionException

        """
        self._set_state(self.OPENING)
        self._socket = self._create_socket()
        self._connect_socket()
        self._set_state(self.OPEN)
        self._on_connected.set()

    def _disconnect_socket(self):
        """Close the existing socket connection"""
        self._socket.close()

    def _get_frame_from_str(self, value):
        """Get the pamqp frame from the string value.

        :param str value: The value to parse for an pamqp frame
        :return (str, int, pamqp.specification.Frame): Remainder of value,
                                                       channel id and
                                                       frame value

        """
        if not value:
            return value, None, None
        try:
            byte_count, channel_id, frame_in = frame.unmarshal(value)
        except (pamqp_exceptions.UnmarshalingException,
                specification.AMQPFrameError) as error:
            LOGGER.debug('Failed to demarshal: %r', error)
            return value, None, None
        return value[byte_count:], channel_id, frame_in

    def _read_frame(self):
        """Read in a full frame and return it, trying to read it from the
        buffer before reading it from the socket.

        :return tuple: The channel the frame came in on and the frame

        """
        channel_id, frame_value = self._read_frame_from_buffer()
        if frame_value:
            return channel_id, frame_value
        return self._read_frame_from_socket()

    def _read_frame_from_buffer(self):
        """Read from the buffer and try and get the demarshaled frame.

        :rtype (int, pamqp.specification.Frame): The channel and frame

        """
        self._buffer, chan_id, value = self._get_frame_from_str(self._buffer)
        return chan_id, value

    def _read_frame_from_socket(self):
        """Read from the socket, appending to the buffer, then try and get the
        frame from the buffer.

        :rtype (int, pamqp.specification.Frame): The channel and frame

        """
        self._buffer += self._read_socket()
        if not self._buffer:
            return None, None
        return self._read_frame_from_buffer()

    def _read_socket(self):
        """Read the negotiated maximum frame size from the socket. Default
        value for self.maximum_frame_size = pamqp.specification.FRAME_MAX_SIZE

        :rtype: str

        """
        try:
            value = self._socket.recv(self.READ_BUFFER_SIZE)
            return value
        except socket.timeout:
            return bytes() if PYTHON3 else str()
        except socket.error as error:
            raise exceptions.RemoteClosedException(-1,
                                                   'Socket Error: %s' %
                                                   error)

    def _write_frame(self, channel_id, frame_value):
        """Marshal the frame and write it to the socket.
        :param frame_value: The frame to write
        :type frame_value: pamqp.specification.Frame|pamqp.header.ProtocolHeader
        :param int channel_id: The channel id to send
        :rtype: int

        """
        frame_data = frame.marshal(frame_value, channel_id)
        bytes_sent = 0
        while bytes_sent < len(frame_data):
            bytes_sent += self._write_frame_data(frame_data)
        return bytes_sent

    def _write_frame_data(self, frame_data):
        """Write the frame data to the socket

        :param str frame_data: The frame data to write
        :return int: bytes written

        """
        if self._args['ssl']:
            return self._socket.write(frame_data)
        return self._socket.send(frame_data)

<commit_msg>Rework to use events object flow<commit_after>__author__ = 'gmr'
import logging
try:
    import queue
except ImportError:
    import Queue as queue
import socket
import ssl
import threading

LOGGER = logging.getLogger(__name__)

from pamqp import frame
from pamqp import exceptions as pamqp_exceptions
from pamqp import specification
from pamqp import PYTHON3

from rabbitpy import base
from rabbitpy import events
from rabbitpy import exceptions

class IO(threading.Thread, base.StatefulObject):

    CONNECTION_TIMEOUT = 0.01
    CONTENT_METHODS = ['Basic.Deliver', 'Basic.GetOk', 'Basic.Return']
    READ_BUFFER_SIZE = specification.FRAME_MAX_SIZE

    def __init__(self, group=None, target=None, name=None, args=(),
                 kwargs=None):
        if kwargs is None:
            kwargs = dict()
        super(IO, self).__init__(group, target, name, args, kwargs)

        self._args = kwargs['connection_args']
        self._events = kwargs['events']
        self._write_queue = kwargs['write_queue']

        self._buffer = bytes()
        self._channels = dict()
        self._remote_name = None
        self._socket = None
        self._state = None

    def add_channel(self, channel_number, write_queue):
        """Add a channel to the channel queue dict for dispatching frames
        to the channel.

        :param int channel_number: The channel to write
        :param Queue.Queue write_queue: Queue for sending frames to the channel

        """
        self._channels[channel_number] = write_queue

    def run(self):
        """Start the thread, which will connect to the socket and run the
        event loop.

        """
        self._connect()

        # Create the remote name
        address, port = self._socket.getsockname()
        peer_address, peer_port = self._socket.getpeername()
        self._remote_name = '%s:%s -> %s:%s' % (address, port,
                                                peer_address, peer_port)

        while self.open and not self._events.is_set(events.SOCKET_CLOSED):

            try:
                frame_value = self._write_queue.get(False)
                self._write_frame(*frame_value)
            except queue.Empty:
                pass

            # Read and process data
            frame_value = self._read_frame()
            if frame_value[0] is not None:
                self._channels[frame_value[0]].put(frame_value[1])

            if self._events.is_set(events.SOCKET_CLOSE):
                LOGGER.debug('Closing as requested')
                return self._close()

            # Close if Channel0 closes
            if self._events.is_set(events.CHANNEL0_CLOSED):
                LOGGER.debug('Closing due to closed channel')
                return self._close()

        self._close()

    def _add_frame_to_queue(self, channel_id, frame_value):
        """Add the frame to the stack by creating the key value used in
        expectations and then add it to the list.

        :param int channel_id: The channel id the frame was received on
        :param pamqp.specification.Frame frame_value: The frame to add

        """
        LOGGER.debug('Adding frame to channel %i: %s',
                     channel_id, type(frame_value))
        self._channels[channel_id].put(frame_value)

    def _close(self):
        self._set_state(self.CLOSING)
        self._socket.close()
        self._set_state(self.CLOSED)
        self._events.set(events.SOCKET_CLOSED)

    def _create_socket(self):
        """Create the new socket, optionally with SSL support.

        :rtype: socket.socket or ssl.SSLSocket

        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        sock.setblocking(1)
        sock.settimeout(self.CONNECTION_TIMEOUT)
        if self._args['ssl']:
            return ssl.wrap_socket(sock,
                                   self._args['ssl_key'],
                                   self._args['ssl_cert'],
                                   False,
                                   self._args['ssl_validation'],
                                   self._args['ssl_version'],
                                   self._args['ssl_cacert'])
        return sock

    def _connect_socket(self):
        """Connect the socket to the specified host and port."""
        LOGGER.debug('Connecting to %(host)s:%(port)i', self._args)
        self._socket.connect((self._args['host'], self._args['port']))

    def _connect(self):
        """Connect to the RabbitMQ Server

        :raises: ConnectionException

        """
        self._set_state(self.OPENING)
        self._socket = self._create_socket()
        self._connect_socket()
        self._set_state(self.OPEN)
        self._events.set(events.SOCKET_OPENED)

    def _disconnect_socket(self):
        """Close the existing socket connection"""
        self._socket.close()

    def _get_frame_from_str(self, value):
        """Get the pamqp frame from the string value.

        :param str value: The value to parse for an pamqp frame
        :return (str, int, pamqp.specification.Frame): Remainder of value,
                                                       channel id and
                                                       frame value

        """
        if not value:
            return value, None, None
        try:
            byte_count, channel_id, frame_in = frame.unmarshal(value)
        except (pamqp_exceptions.UnmarshalingException,
                specification.AMQPFrameError) as error:
            LOGGER.debug('Failed to demarshal: %r', error)
            return value, None, None
        return value[byte_count:], channel_id, frame_in

    def _read_frame(self):
        """Read in a full frame and return it, trying to read it from the
        buffer before reading it from the socket.

        :return tuple: The channel the frame came in on and the frame

        """
        channel_id, frame_value = self._read_frame_from_buffer()
        if frame_value:
            return channel_id, frame_value
        return self._read_frame_from_socket()

    def _read_frame_from_buffer(self):
        """Read from the buffer and try and get the demarshaled frame.

        :rtype (int, pamqp.specification.Frame): The channel and frame

        """
        self._buffer, chan_id, value = self._get_frame_from_str(self._buffer)
        return chan_id, value

    def _read_frame_from_socket(self):
        """Read from the socket, appending to the buffer, then try and get the
        frame from the buffer.

        :rtype (int, pamqp.specification.Frame): The channel and frame

        """
        self._buffer += self._read_socket()
        if not self._buffer:
            return None, None
        return self._read_frame_from_buffer()

    def _read_socket(self):
        """Read the negotiated maximum frame size from the socket. Default
        value for self.maximum_frame_size = pamqp.specification.FRAME_MAX_SIZE

        :rtype: str

        """
        try:
            value = self._socket.recv(self.READ_BUFFER_SIZE)
            return value
        except socket.timeout:
            return bytes() if PYTHON3 else str()
        except socket.error as error:
            raise exceptions.RemoteClosedException(-1,
                                                   'Socket Error: %s' %
                                                   error)

    def _write_frame(self, channel_id, frame_value):
        """Marshal the frame and write it to the socket.
        :param frame_value: The frame to write
        :type frame_value: pamqp.specification.Frame|pamqp.header.ProtocolHeader
        :param int channel_id: The channel id to send
        :rtype: int

        """
        frame_data = frame.marshal(frame_value, channel_id)
        bytes_sent = 0
        while bytes_sent < len(frame_data):
            bytes_sent += self._write_frame_data(frame_data)
        return bytes_sent

    def _write_frame_data(self, frame_data):
        """Write the frame data to the socket

        :param str frame_data: The frame data to write
        :return int: bytes written

        """
        if self._args['ssl']:
            return self._socket.write(frame_data)
        return self._socket.send(frame_data)
<|endoftext|>


<commit_before>from numpy import array, matrix, inf, nan, ones, zeros, isinf, isfinite

import matplotlib

matplotlib.use('nbAgg')

import matplotlib.pyplot as plt

def get_image_pos(object_distance, focal_length):
    s = object_distance
    f = focal_length
    return 1 / (1 / f - 1 / s)

class OPE(object):
    def __init__(self, a=inf, d=None, f=None, name=''):
        """
        Optical path element used for ray tracing.

        Arguments
        ---------
        a : inf or float
            Aperture of the element.
        d : float or None
            Distance travelled through optical element.
        f : flaot or None
            Focal length of optical element.
        name : str
            Name of the element.
        """
        self.aperture = a
        self.name = name
        self.focal_length = f

        if f:
            self.c = -1 / f
        else:
            self.c = 0

        if d:
            self.b = d
        else:
            self.b = 0

    def is_lens(self):
        if self.get_matrix()[1, 0] != 0:
            return True
        else:
            return False

    def get_matrix(self):
        return matrix([[1, self.b], [self.c, 1]])

    def passes_aperture(self, r, verbose=False):
        """
        Does an input ray r pass the optical element.
        """
        h = r.flatten()[0]
        if abs(h) > self.aperture:
            if verbose:
                print('{0:s}: stopped at aperture with height {1:1.3f}'.format(
                    self.name, h))
            return False
        else:
            if verbose:
                print('{0:s}: passed aperture with height {1:1.3f}'.format(
                    self.name, h))
            return True

    def pass_aperture(self, r, verbose=False):
        """ Return nan vector if ray cannot pass aperture."""
        if self.passes_aperture(r, verbose=verbose):
            return r
        else:
            return matrix(ones(r.shape) * nan)

    def transmit(self, r, verbose=False):
        """ Calculate output ray. """
        M = self.get_matrix()
        r_ = M * matrix(self.pass_aperture(r, verbose=verbose)).T
        return array(r_).flatten()

    def get_travel_length(self):
        """ Length the optical element takes. """
        return self.b

def trace_ray(r, sequence):
    """Take r as input vector and trace the ray through the sequence of
    optical path elements OLEs.

    Arguments
    ---------
    r : 2-tuple
        Vector with the first element beeing the ray height and the
        second being the angle
    sequence : list of optical path elements (OPE)

    Returns
    -------
    (dist, ray)
    dist : array
        Array representing the position of the ray through the trace.
    ray : array
        Array representing the ray vectors at the corresponding
        distances.
    """
    ray0 = zeros((2, len(sequence) + 1))
    ray0[:, 0] = r

    # Do the calculations
    distances = [0.0]
    for idx, el in enumerate(sequence):
        ray0[:, idx + 1] = el.transmit(ray0[:, idx])
        distances.append(distances[idx] + el.get_travel_length())

    dist = array(distances)

    return (dist, ray0)

def get_first_aperture(sequence):
    """
    get aperture of first lens in path and calculate distance to it.

    Arguments
    ---------
    sequence : list of OPE
        List of optical path elements

    Returns
    -------
    tuple(idx, distance, aperture)
    """
    d = 0.0
    a = inf
    for idx, m in enumerate(sequence):
        if isinf(m.aperture):
            d += m.get_travel_length()
        else:
            a = m.aperture
            break
    return (idx, d, a)

def get_lens_pos(sequence):
    """
    Calculate positions of lenses.

    Returns
    -------
    List of tuples with index and position of OPE in sequence.
    """
    d = 0.0
    d_ = []
    for idx, m in enumerate(sequence):
        if m.is_lens():
            d_.append((idx, d))
        else:
            d += m.get_travel_length()

    return d_

def get_angle_lim(h, d, a):
    """
    Calculate the upper and lower angles of a source that is at
    distance d to an aperture a.
    """
    # return (arctan((a - h) / d), arctan(-(a + h) / d))
    return ((a - h) / d, -(a + h) / d)

def trace_parser(s):
    '''
    Convert a string into a seqeunce of optical path elements.

    Example
    -------
    s = 'd15 | l15/5.5 | d15'
    would result in a seqeunce of a distance element of 15 lu
    (lu=length units), followed by a Lens with focal length 15 lu
    with aperture 5.5 lu and another distance of 15 lu.
    '''
    sequence = []

    for idx, si in enumerate(s.lower().replace(' ', '').split('|')):
        if si.startswith('d'):
            s_ = si.split('/')
            d = float(s_[0][1:])
            if len(s_) == 2:
                a = float(s_[1])
                ope = OPE(d=d, a=a, name='D' + str(idx))
            else:
                ope = OPE(d=d, name='D' + str(idx))
        elif si.startswith('l'):
            s_ = si.split('/')
            f = float(s_[0][1:])
            if len(s_) == 2:
                a = float(s_[1])
                ope = OPE(f=f, a=a, name='L' + str(idx))
            else:
                ope = OPE(f=f, name='L' + str(idx))
        else:
            print('unknown element {}'.format(si))
        sequence.append(ope)

    return sequence

def plot_ray(h, sequence, parallel=False, d=None,
             axis=None, label=None, plot_statics=True, **pltkws):
    """
    Plot the ray trace through the sequence of OPEs.

    Arguments
    ---------
    h : float
        height of ray.
    sequence : list of OPE
        sequence of optical path elements.
    parallel : bool
        Whether the source ray is a parallel beam.
    d : float
        Diameter of an incoming parallel beam.
    axis : matplotlib.Axis
    label : str
        Label of the plotted line

    Keyword Arguments
    -----------------
    kws passed to plt.plot(**pltkws)

    Returns
    -------
    maplotlib.figure
    """
    if axis:
        fig = axis.figure
        ax = axis
    else:
        fig = plt.figure()
        ax = fig.add_subplot(111)

    if not any([a in pltkws for a in ['c', 'col', 'color']]):
        pltkws['color'] = next(ax._get_lines.prop_cycler)['color']

    if plot_statics:
        # draw optical axis
        ax.axhline(color='k', linewidth=0.5, linestyle='--')
        # draw lenses
        for idx, x in get_lens_pos(sequence):
            ax.axvline(x=x, ymin=0.02, ymax=0.98,
                       linewidth=0.5, linestyle='--')
        # draw apertures
        draw_apertures(sequence, axis=ax)

    if parallel:
        d = d or 1.0
        rin_0 = [h + d/2, 0.0]
        rin_1 = [h - d/2, 0.0]
    else:
        # get distance and aperture of first aperture
        _, d, aperture = get_first_aperture(sequence)

        a1, a2 = get_angle_lim(h, d, aperture)
        rin_0 = [h, a1]
        rin_1 = [h, a2]

    dist, r0 = trace_ray(rin_0, sequence)
    dist, r1 = trace_ray(rin_1, sequence)

    #pltkws = {'linewidth': 0.5}.update(pltkws)

    ax.plot(dist, r0[0, :], label=label or 'h={:1.2f}'.format(h), **pltkws)
    ax.plot(dist, r1[0, :], **pltkws)

    return fig

def draw_apertures(sequence, axis=None):
    """
    Draw the apertures in the sequence
    """
    if not axis:
        ax = plt.gca()
    else:
        ax = axis

    plt_kws = dict(linewidth=2, linestyle='-',
                   color='darkslategrey')

    a_max = get_max_aperture(sequence)

    if a_max:
        for idx, x in get_lens_pos(sequence):
            a = sequence[idx].aperture
            ax.plot([x, x], [-2*a_max, -a], **plt_kws)
            ax.plot([x, x], [a, 2*a_max], **plt_kws)

def get_max_aperture(sequence):
    """
    Return largest aperture in sequence.

    Returns None if no apertures are defined.
    """
    apertures = [abs(ope.aperture) for ope in sequence if ope.aperture != inf]

    if not apertures:
        out = None
    else:
        out = max(apertures)

    return out

class Trace(object):
    """
    Manages a tracing.
    """

    def __init__(self, sequence):
        """
        """
        self.sequence = sequence
        self.max_y = 0.0
        self._plot_axis = None
        self._statics_drawn = None

    def _init_axis(self):
        self._statics_drawn = False
        axis = plt.figure().add_subplot(111)
        self._plot_axis = axis

    @property
    def plot_axis(self):
        if not self._plot_axis:
            self._init_axis()
        return self._plot_axis

    @plot_axis.setter
    def plot_axis(self, axis):
        self._plot_axis = axis

    def set_max_y(self, y):
        if abs(y) > self.max_y:
            self.max_y = abs(y)

    def plot_statics(self, axis=None):
        if axis:
            self.plot_axis = axis
        else:
            axis = self.plot_axis

        if not self._statics_drawn:
            # draw optical axis
            axis.axhline(color='k', linewidth=0.5, linestyle='--')
            # draw lenses
            for idx, x in get_lens_pos(self.sequence):
                axis.axvline(x=x, ymin=0.02, ymax=0.98,
                             linewidth=0.5, linestyle='--')
                # draw apertures
            draw_apertures(self.sequence, axis=axis)

            self._statics_drawn = True

        return axis

    def reset_plot(self):
        """ Reset internal plot axis."""
        self._statics_drawn = False

    def plot_rays(self, h, parallel=False, d=None, axis=None,
                  label=None, plot_statics=True, **pltkws):
        """
        Plot the ray trace through the sequence of OPEs.

        Arguments
        ---------
        h : float or iterable
            Height(s) of ray .
        sequence : list of OPE
            sequence of optical path elements.
        parallel : bool
            Whether the source ray is a parallel beam.
        d : float
            Diameter of an incoming parallel beam.
        axis : matplotlib.Axis
        label : str
            Label of the plotted line

        Keyword Arguments
        -----------------
        kws passed to plt.plot(**pltkws)

        Returns
        -------
        maplotlib.figure
        """
        try:
            heights = iter(h)
        except TypeError:
            heights = [h]

        if plot_statics:
            ax = self.plot_statics(axis=axis)
        else:
            ax = self.plot_axis

        if not any([a in pltkws for a in ['c', 'col', 'color']]):
            cycle_colors = True

        for h in heights:
            if parallel:
                d = d or 1.0
                rin_0 = [h + d/2, 0.0]
                rin_1 = [h - d/2, 0.0]
            else:
                if self.has_aperture():
                    # get distance and aperture of first aperture
                    _, d, aperture = get_first_aperture(self.sequence)
                    a1, a2 = get_angle_lim(h, d, aperture)
                    rin_0 = [h, a1]
                    rin_1 = [h, a2]
                else:
                    try:
                        _, d = get_lens_pos(self.sequence)[0]
                    except:
                        d = inf
                    rin_0 = [h, 0]
                    rin_1 = [h, -h/d]

            dist, r0 = trace_ray(rin_0, self.sequence)
            dist, r1 = trace_ray(rin_1, self.sequence)

            max_y = max([max(abs(r0[0, :])), max(abs(r1[0, :]))])
            self.set_max_y(max_y)

            pltkws['color'] = next(ax._get_lines.prop_cycler)['color']

            ax.plot(dist, r0[0, :],
                    label=label or 'h={:1.2f}'.format(h), **pltkws)
            ax.plot(dist, r1[0, :], **pltkws)

        return ax

    def adjust_ylims(self, axis):
        """
        Adjusts the y limits of the plot according to the apertures
        and rays.
        """
        max_y = self.max_y
        max_a = get_max_aperture(self.sequence)

        y = max([max_y, max_a])
        axis.set_ylim([-1.1*y, 1.1*y])

    def get_idx_aperture_stop(self):
        """
        Find the optical element the defines the aperture stop of the
        system.

        Returns the index of the OPE in the sequence.
        """
        if get_max_aperture(self.sequence) is None:
            print('No apertures found.')
            out = None
        else:
            angle = 1e-10
            while True:
                _, rays = trace_ray((0, angle), self.sequence)
                if all(isfinite(rays[0])):
                    break
                else:
                    angle = angle / 2
            ratio = 0.0
            for idx, ope in enumerate(self.sequence):
                ratio_ = rays[0, idx+1] / ope.aperture
                if ratio_ > ratio:
                    ratio = ratio_
                    out = idx

        return out

    def has_aperture(self):
        if get_max_aperture(self.sequence):
            return True
        else:
            return False

    def get_aperture_stop_position(self):
        """
        Reduce sequence upto aperture and get distance from lens pos
        function.
        """
        _, d = get_lens_pos(
            self.sequence[:self.get_idx_aperture_stop() + 1])[-1]

        return d

    def get_aperture_stop_size(self):
        """
        Return the (half) aperture stop size.
        """
        return self.sequence[self.get_idx_aperture_stop()].aperture

    def calc_entrance_pupil_position(self):
        """ sequence of OPEs preceeding the aperture stop """
        sequence_prec = self.sequence[:self.get_idx_aperture_stop()]
        d_ap = self.get_aperture_stop_position()
        x = d_ap
        for idx, lens_pos in get_lens_pos(sequence_prec)[::-1]:
            # object distance (reverse ray self (yields negative d_obj))
            d_obj = lens_pos - x
            # image distance
            d_img = get_image_pos(d_obj, sequence_prec[idx].focal_length)
            x = lens_pos + d_img

        return x

    def draw_entrance_pupil(self, axis=None, color='orangered'):
        """
        Draw the apparent entrance pupil.
        """
        if axis:
            ax = axis
        else:
            ax = self.plot_axis
        x = self.calc_entrance_pupil_position()
        a = self.get_aperture_stop_size()
        y_max = get_max_aperture(self.sequence) if get_max_aperture(
            self.sequence) else 2 * a

        plt_kws = dict(linewidth=2, linestyle='-', color=color)
        ax.plot([x, x], [a, y_max], **plt_kws)
        ax.plot([x, x], [-y_max, -a], **plt_kws)
<commit_msg>absolute value for aperture and abs in caluclation of aperture stop<commit_after>from numpy import array, matrix, inf, nan, ones, zeros, isinf, isfinite

import matplotlib

matplotlib.use('nbAgg')

import matplotlib.pyplot as plt

def get_image_pos(object_distance, focal_length):
    s = object_distance
    f = focal_length
    return 1 / (1 / f - 1 / s)

class OPE(object):
    def __init__(self, a=inf, d=None, f=None, name=''):
        """
        Optical path element used for ray tracing.

        Arguments
        ---------
        a : inf or float
            Aperture of the element.
        d : float or None
            Distance travelled through optical element.
        f : flaot or None
            Focal length of optical element.
        name : str
            Name of the element.
        """
        self.aperture = abs(a)
        self.name = name
        self.focal_length = f

        if f:
            self.c = -1 / f
        else:
            self.c = 0

        if d:
            self.b = d
        else:
            self.b = 0

    def is_lens(self):
        if self.get_matrix()[1, 0] != 0:
            return True
        else:
            return False

    def get_matrix(self):
        return matrix([[1, self.b], [self.c, 1]])

    def passes_aperture(self, r, verbose=False):
        """
        Does an input ray r pass the optical element.
        """
        h = r.flatten()[0]
        if abs(h) > self.aperture:
            if verbose:
                print('{0:s}: stopped at aperture with height {1:1.3f}'.format(
                    self.name, h))
            return False
        else:
            if verbose:
                print('{0:s}: passed aperture with height {1:1.3f}'.format(
                    self.name, h))
            return True

    def pass_aperture(self, r, verbose=False):
        """ Return nan vector if ray cannot pass aperture."""
        if self.passes_aperture(r, verbose=verbose):
            return r
        else:
            return matrix(ones(r.shape) * nan)

    def transmit(self, r, verbose=False):
        """ Calculate output ray. """
        M = self.get_matrix()
        r_ = M * matrix(self.pass_aperture(r, verbose=verbose)).T
        return array(r_).flatten()

    def get_travel_length(self):
        """ Length the optical element takes. """
        return self.b

def trace_ray(r, sequence):
    """Take r as input vector and trace the ray through the sequence of
    optical path elements OLEs.

    Arguments
    ---------
    r : 2-tuple
        Vector with the first element beeing the ray height and the
        second being the angle
    sequence : list of optical path elements (OPE)

    Returns
    -------
    (dist, ray)
    dist : array
        Array representing the position of the ray through the trace.
    ray : array
        Array representing the ray vectors at the corresponding
        distances.
    """
    ray0 = zeros((2, len(sequence) + 1))
    ray0[:, 0] = r

    # Do the calculations
    distances = [0.0]
    for idx, el in enumerate(sequence):
        ray0[:, idx + 1] = el.transmit(ray0[:, idx])
        distances.append(distances[idx] + el.get_travel_length())

    dist = array(distances)

    return (dist, ray0)

def get_first_aperture(sequence):
    """
    get aperture of first lens in path and calculate distance to it.

    Arguments
    ---------
    sequence : list of OPE
        List of optical path elements

    Returns
    -------
    tuple(idx, distance, aperture)
    """
    d = 0.0
    a = inf
    for idx, m in enumerate(sequence):
        if isinf(m.aperture):
            d += m.get_travel_length()
        else:
            a = m.aperture
            break
    return (idx, d, a)

def get_lens_pos(sequence):
    """
    Calculate positions of lenses.

    Returns
    -------
    List of tuples with index and position of OPE in sequence.
    """
    d = 0.0
    d_ = []
    for idx, m in enumerate(sequence):
        if m.is_lens():
            d_.append((idx, d))
        else:
            d += m.get_travel_length()

    return d_

def get_angle_lim(h, d, a):
    """
    Calculate the upper and lower angles of a source that is at
    distance d to an aperture a.
    """
    # return (arctan((a - h) / d), arctan(-(a + h) / d))
    return ((a - h) / d, -(a + h) / d)

def trace_parser(s):
    '''
    Convert a string into a seqeunce of optical path elements.

    Example
    -------
    s = 'd15 | l15/5.5 | d15'
    would result in a seqeunce of a distance element of 15 lu
    (lu=length units), followed by a Lens with focal length 15 lu
    with aperture 5.5 lu and another distance of 15 lu.
    '''
    sequence = []

    for idx, si in enumerate(s.lower().replace(' ', '').split('|')):
        if si.startswith('d'):
            s_ = si.split('/')
            d = float(s_[0][1:])
            if len(s_) == 2:
                a = float(s_[1])
                ope = OPE(d=d, a=a, name='D' + str(idx))
            else:
                ope = OPE(d=d, name='D' + str(idx))
        elif si.startswith('l'):
            s_ = si.split('/')
            f = float(s_[0][1:])
            if len(s_) == 2:
                a = float(s_[1])
                ope = OPE(f=f, a=a, name='L' + str(idx))
            else:
                ope = OPE(f=f, name='L' + str(idx))
        else:
            print('unknown element {}'.format(si))
        sequence.append(ope)

    return sequence

def plot_ray(h, sequence, parallel=False, d=None,
             axis=None, label=None, plot_statics=True, **pltkws):
    """
    Plot the ray trace through the sequence of OPEs.

    Arguments
    ---------
    h : float
        height of ray.
    sequence : list of OPE
        sequence of optical path elements.
    parallel : bool
        Whether the source ray is a parallel beam.
    d : float
        Diameter of an incoming parallel beam.
    axis : matplotlib.Axis
    label : str
        Label of the plotted line

    Keyword Arguments
    -----------------
    kws passed to plt.plot(**pltkws)

    Returns
    -------
    maplotlib.figure
    """
    if axis:
        fig = axis.figure
        ax = axis
    else:
        fig = plt.figure()
        ax = fig.add_subplot(111)

    if not any([a in pltkws for a in ['c', 'col', 'color']]):
        pltkws['color'] = next(ax._get_lines.prop_cycler)['color']

    if plot_statics:
        # draw optical axis
        ax.axhline(color='k', linewidth=0.5, linestyle='--')
        # draw lenses
        for idx, x in get_lens_pos(sequence):
            ax.axvline(x=x, ymin=0.02, ymax=0.98,
                       linewidth=0.5, linestyle='--')
        # draw apertures
        draw_apertures(sequence, axis=ax)

    if parallel:
        d = d or 1.0
        rin_0 = [h + d/2, 0.0]
        rin_1 = [h - d/2, 0.0]
    else:
        # get distance and aperture of first aperture
        _, d, aperture = get_first_aperture(sequence)

        a1, a2 = get_angle_lim(h, d, aperture)
        rin_0 = [h, a1]
        rin_1 = [h, a2]

    dist, r0 = trace_ray(rin_0, sequence)
    dist, r1 = trace_ray(rin_1, sequence)

    #pltkws = {'linewidth': 0.5}.update(pltkws)

    ax.plot(dist, r0[0, :], label=label or 'h={:1.2f}'.format(h), **pltkws)
    ax.plot(dist, r1[0, :], **pltkws)

    return fig

def draw_apertures(sequence, axis=None):
    """
    Draw the apertures in the sequence
    """
    if not axis:
        ax = plt.gca()
    else:
        ax = axis

    plt_kws = dict(linewidth=2, linestyle='-',
                   color='darkslategrey')

    a_max = get_max_aperture(sequence)

    if a_max:
        for idx, x in get_lens_pos(sequence):
            a = sequence[idx].aperture
            ax.plot([x, x], [-2*a_max, -a], **plt_kws)
            ax.plot([x, x], [a, 2*a_max], **plt_kws)

def get_max_aperture(sequence):
    """
    Return largest aperture in sequence.

    Returns None if no apertures are defined.
    """
    apertures = [abs(ope.aperture) for ope in sequence if ope.aperture != inf]

    if not apertures:
        out = None
    else:
        out = max(apertures)

    return out

class Trace(object):
    """
    Manages a tracing.
    """

    def __init__(self, sequence):
        """
        """
        self.sequence = sequence
        self.max_y = 0.0
        self._plot_axis = None
        self._statics_drawn = None

    def _init_axis(self):
        self._statics_drawn = False
        axis = plt.figure().add_subplot(111)
        self._plot_axis = axis

    @property
    def plot_axis(self):
        if not self._plot_axis:
            self._init_axis()
        return self._plot_axis

    @plot_axis.setter
    def plot_axis(self, axis):
        self._plot_axis = axis

    def set_max_y(self, y):
        if abs(y) > self.max_y:
            self.max_y = abs(y)

    def plot_statics(self, axis=None):
        if axis:
            self.plot_axis = axis
        else:
            axis = self.plot_axis

        if not self._statics_drawn:
            # draw optical axis
            axis.axhline(color='k', linewidth=0.5, linestyle='--')
            # draw lenses
            for idx, x in get_lens_pos(self.sequence):
                axis.axvline(x=x, ymin=0.02, ymax=0.98,
                             linewidth=0.5, linestyle='--')
                # draw apertures
            draw_apertures(self.sequence, axis=axis)

            self._statics_drawn = True

        return axis

    def reset_plot(self):
        """ Reset internal plot axis."""
        self._statics_drawn = False

    def plot_rays(self, h, parallel=False, d=None, axis=None,
                  label=None, plot_statics=True, **pltkws):
        """
        Plot the ray trace through the sequence of OPEs.

        Arguments
        ---------
        h : float or iterable
            Height(s) of ray .
        sequence : list of OPE
            sequence of optical path elements.
        parallel : bool
            Whether the source ray is a parallel beam.
        d : float
            Diameter of an incoming parallel beam.
        axis : matplotlib.Axis
        label : str
            Label of the plotted line

        Keyword Arguments
        -----------------
        kws passed to plt.plot(**pltkws)

        Returns
        -------
        maplotlib.figure
        """
        try:
            heights = iter(h)
        except TypeError:
            heights = [h]

        if plot_statics:
            ax = self.plot_statics(axis=axis)
        else:
            ax = self.plot_axis

        if not any([a in pltkws for a in ['c', 'col', 'color']]):
            cycle_colors = True

        for h in heights:
            if parallel:
                d = d or 1.0
                rin_0 = [h + d/2, 0.0]
                rin_1 = [h - d/2, 0.0]
            else:
                if self.has_aperture():
                    # get distance and aperture of first aperture
                    _, d, aperture = get_first_aperture(self.sequence)
                    a1, a2 = get_angle_lim(h, d, aperture)
                    rin_0 = [h, a1]
                    rin_1 = [h, a2]
                else:
                    try:
                        _, d = get_lens_pos(self.sequence)[0]
                    except:
                        d = inf
                    rin_0 = [h, 0]
                    rin_1 = [h, -h/d]

            dist, r0 = trace_ray(rin_0, self.sequence)
            dist, r1 = trace_ray(rin_1, self.sequence)

            max_y = max([max(abs(r0[0, :])), max(abs(r1[0, :]))])
            self.set_max_y(max_y)

            pltkws['color'] = next(ax._get_lines.prop_cycler)['color']

            ax.plot(dist, r0[0, :],
                    label=label or 'h={:1.2f}'.format(h), **pltkws)
            ax.plot(dist, r1[0, :], **pltkws)

        return ax

    def adjust_ylims(self, axis):
        """
        Adjusts the y limits of the plot according to the apertures
        and rays.
        """
        max_y = self.max_y
        max_a = get_max_aperture(self.sequence)

        y = max([max_y, max_a])
        axis.set_ylim([-1.1*y, 1.1*y])

    def get_idx_aperture_stop(self):
        """
        Find the optical element the defines the aperture stop of the
        system.

        Returns the index of the OPE in the sequence.
        """
        if get_max_aperture(self.sequence) is None:
            print('No apertures found.')
            out = None
        else:
            angle = 1e-10
            while True:
                _, rays = trace_ray((0, angle), self.sequence)
                if all(isfinite(rays[0])):
                    break
                else:
                    angle = angle / 2
            ratio = 0.0
            for idx, ope in enumerate(self.sequence):
                ratio_ = abs(rays[0, idx+1]) / ope.aperture
                if ratio_ > ratio:
                    ratio = ratio_
                    out = idx

        return out

    def has_aperture(self):
        if get_max_aperture(self.sequence):
            return True
        else:
            return False

    def get_aperture_stop_position(self):
        """
        Reduce sequence upto aperture and get distance from lens pos
        function.
        """
        _, d = get_lens_pos(
            self.sequence[:self.get_idx_aperture_stop() + 1])[-1]

        return d

    def get_aperture_stop_size(self):
        """
        Return the (half) aperture stop size.
        """
        return self.sequence[self.get_idx_aperture_stop()].aperture

    def calc_entrance_pupil_position(self):
        """ sequence of OPEs preceeding the aperture stop """
        sequence_prec = self.sequence[:self.get_idx_aperture_stop()]
        d_ap = self.get_aperture_stop_position()
        x = d_ap
        for idx, lens_pos in get_lens_pos(sequence_prec)[::-1]:
            # object distance (reverse ray self (yields negative d_obj))
            d_obj = lens_pos - x
            # image distance
            d_img = get_image_pos(d_obj, sequence_prec[idx].focal_length)
            x = lens_pos + d_img

        return x

    def draw_entrance_pupil(self, axis=None, color='orangered'):
        """
        Draw the apparent entrance pupil.
        """
        if axis:
            ax = axis
        else:
            ax = self.plot_axis
        x = self.calc_entrance_pupil_position()
        a = self.get_aperture_stop_size()
        y_max = get_max_aperture(self.sequence) if get_max_aperture(
            self.sequence) else 2 * a

        plt_kws = dict(linewidth=2, linestyle='-', color=color)
        ax.plot([x, x], [a, y_max], **plt_kws)
        ax.plot([x, x], [-y_max, -a], **plt_kws)
<|endoftext|>


<commit_before>#!/usr/bin/env python
from __future__ import division
import numpy as np

def poly(p,order,diff=0):
  if diff == 0:
    if order == 0:
      return 0.0*p + 1.0
    else:
      return p**order

  else:
    if order == 0:
      return 0.0*p
    else:
      return order*poly(p,order-1,diff-1)

def vpoly(p,order,diff=0):
  p = np.asarray(p)
  order = np.asarray(order,dtype=int)
  out = np.zeros((order.shape[0],p.shape[0]))
  for i,r in enumerate(order):
    out[i,:] = poly(p,r,diff)

  return out

def interp_matrix(basis,cnts,eps,Np=0):
  '''
  returns the matrix:

  A =   | Ar Ap |
        | Ap 0  |

  where Ar is consists of RBFs with specified cnts evaluated
  at those cnts. Ap consists of additional polynomial terms
  '''
  Ns,Ndim = cnts.shape
  eps = eps*np.ones(Ns)
  Ar = basis(cnts,cnts,eps).T
  Ap = np.zeros((0,Ns))
  for i in range(Ndim):
    if i == 0:
      Api = vpoly(cnts[:,i],range(Np))
      Ap = np.vstack((Ap,Api))
    else:
      Api = vpoly(cnts[:,i],range(1,Np))
      Ap = np.vstack((Ap,Api))

  Z = np.zeros((Ap.shape[0],Ap.shape[0]))
  left = np.vstack((Ar,Ap))
  right = np.vstack((Ap.T,Z))
  A = np.hstack((left,right))
  return A

def data_term(basis,pnt,cnts,eps,diff,Np=0):
  pnt = pnt[:,None]
  Ns,Ndim = cnts.shape
  eps = eps*np.ones(Ns)
  dr = basis(pnt,cnts,eps,diff=diff).T
  print(np.shape(dr))
  dp = np.zeros((0,1))
  for i in range(Ndim):
    if i == 0:
      dpi = vpoly(pnt[i],range(Np),diff=diff[i])
      dp = np.vstack((dp,dpi))
    else:
      dpi = vpoly(pnt[i],range(1,Np),diff=diff[i])
      dp = np.vstack((dp,dpi))

  d = np.vstack((dr,dp))
  d = d[:,0]
  return d

<commit_msg>added weight function<commit_after>#!/usr/bin/env python
from __future__ import division
import numpy as np
import rbf.basis

def poly(p,order,diff=0):
  p = np.asarray(p,dtype=float)
  if diff == 0:
    if order == 0:
      out = 0.0*p + 1.0
    else:
      out = p**order

  else:
    if order == 0:
      out = 0.0*p
    else:
      out = order*poly(p,order-1,diff-1)

  return np.asarray(out,dtype=float)

def vpoly(c,order=None):
  c = np.asarray(c,dtype=float)
  if order is None:
    order = range(c.shape[0])

  order = np.asarray(order,dtype=int)
  out = np.zeros((order.shape[0],c.shape[0]))
  for i,r in enumerate(order):
    out[i,:] = poly(c,r)

  return out

def vrbf(c,basis=rbf.basis.mq,eps=1.0,Np=0):
  '''
  returns the matrix:

  A =   | Ar Ap |
        | Ap 0  |

  where Ar is consists of RBFs with specified cnts evaluated
  at those cnts. Ap consists of additional polynomial terms
  '''
  Ns,Ndim = c.shape
  eps = eps*np.ones(Ns)
  Ar = basis(c,c,eps).T
  Ap = np.zeros((0,Ns))
  for i in range(Ndim):
    if i == 0:
      Api = vpoly(c[:,i],range(Np))
      Ap = np.vstack((Ap,Api))
    else:
      Api = vpoly(c[:,i],range(1,Np))
      Ap = np.vstack((Ap,Api))

  Z = np.zeros((Ap.shape[0],Ap.shape[0]))
  left = np.vstack((Ar,Ap))
  right = np.vstack((Ap.T,Z))
  A = np.hstack((left,right))
  return A

def drbf(x,c,diff,basis=rbf.basis.mq,eps=1.0,Np=0):
  x = np.asarray(x,dtype=float)
  c = np.asarray(c,dtype=float)
  x = x[None,:]
  Ns,Ndim = c.shape
  eps = eps*np.ones(Ns)
  dr = basis(x,c,eps,diff=diff)[0,:]
  dp = np.zeros(0)
  for i in range(Ndim):
    if i == 0:
      dpi = [poly(x[0,i],j,diff=diff[i]) for j in range(Np)]
      dp = np.concatenate((dp,dpi))
    else:
      dpi = [poly(x[0,i],j,diff=diff[i]) for j in range(1,Np)]
      dp = np.concatenate((dp,dpi))

  d = np.concatenate((dr,dp))
  return d

def rbf_weight(x,c,diff,basis=rbf.basis.mq,eps=1.0,Np=0):
  '''
  finds the weights, w, such that

  f_i(c_j) = f(||c_j - c_i||)

  | f_0(c_0) ... f_0(c_N) |     | L[f_0(y)]y=x  |
  |    :             :    | w = |     :         |
  | f_N(c_0) ... f_N(c_N) |     | L[f_N(y)]y=x  |
  '''
  x = np.asarray(x)
  c = np.asarray(c)
  A = vrbf(c,basis=basis,eps=eps,Np=Np)
  d = drbf(x,c,basis=basis,eps=eps,Np=Np,diff=diff)
  w = np.linalg.solve(A,d)[:c.shape[0]]
  return w

def poly_weight(x,c,diff):
  '''
  finds the weights, w, such that

  f_i(c_j) = c_j**i

  | f_0(c_0) ... f_0(c_N) |     | L[f_0(y)]y=x  |
  |    :             :    | w = |     :         |
  | f_N(c_0) ... f_N(c_N) |     | L[f_N(y)]y=x  |
  '''
  A =  vpoly(c)
  d =  [poly(x,j,diff=diff) for i in range(Np)]
  return

<|endoftext|>


<commit_before>#!/usr/bin/env python

import sys, h5py
import numpy as np
import scipy.ndimage as ndimage
import matplotlib
import spimage
import argparse

parser = argparse.ArgumentParser(prog='processprtfs.py', description='Compute superimposed image and phase-retrieval transfer functions.')
parser.add_argument('-a', '--phase',  metavar='PHASE', type=str, default='vs72/phasing.h5', help='Phasing results for patterns')
parser.add_argument('-r', '--ref',  metavar='REF', type=str, default='reference.mat', help='File containing reference pattern')
args = parser.parse_args()

f = h5py.File(args.phase, 'r+')

recons  = f['real_space_final'][:]
fourier = f['fourier_space_final'][:]
support = f['support_final'][:]
rerror  = f['real_error'][:]
ferror  = f['fourier_error'][:]

with h5py.File(args.ref, 'r') as reff:
    f2 = np.reshape(forig['f2'][:],(256,256))
    reference = reff['reference'][:]
    reference = np.fft.ifftshift(np.fft.fft2(np.fft.fft2(np.fft.fftshift(reference['real'] + 1j * reference['imag'])) * np.sqrt(f2)))

# Loop over 50 original images
M = 50
N = 100
subsetcount = 10

allsupers = np.zeros((M, 256, 256), dtype=np.complex128)

for m in range(M):
    indices = np.argsort(rerror[(m*N):((m+1)*N)]) + m * N
    indices = indices[0:subsetcount]
    print indices
    output_prtf = spimage.prtf(recons[indices], support[indices], enantio=True, translate=True, clearsupport=True, reference=reference)
    allsupers[m,:,:] = output_prtf['super_image'] / subsetcount

try:
    del f['super_images']
except:
    pass

f['super_images'] = allsupers

<commit_msg>Changed to reff file object, fixing error<commit_after>#!/usr/bin/env python

import sys, h5py
import numpy as np
import scipy.ndimage as ndimage
import matplotlib
import spimage
import argparse

parser = argparse.ArgumentParser(prog='processprtfs.py', description='Compute superimposed image and phase-retrieval transfer functions.')
parser.add_argument('-a', '--phase',  metavar='PHASE', type=str, default='vs72/phasing.h5', help='Phasing results for patterns')
parser.add_argument('-r', '--ref',  metavar='REF', type=str, default='reference.mat', help='File containing reference pattern')
args = parser.parse_args()

f = h5py.File(args.phase, 'r+')

recons  = f['real_space_final'][:]
fourier = f['fourier_space_final'][:]
support = f['support_final'][:]
rerror  = f['real_error'][:]
ferror  = f['fourier_error'][:]

with h5py.File(args.ref, 'r') as reff:
    f2 = np.reshape(reff['f2'][:],(256,256))
    reference = reff['reference'][:]
    reference = np.fft.ifftshift(np.fft.fft2(np.fft.fft2(np.fft.fftshift(reference['real'] + 1j * reference['imag'])) * np.sqrt(f2)))

# Loop over 50 original images
M = 50
N = 100
subsetcount = 10

allsupers = np.zeros((M, 256, 256), dtype=np.complex128)

for m in range(M):
    indices = np.argsort(rerror[(m*N):((m+1)*N)]) + m * N
    indices = indices[0:subsetcount]
    print indices
    output_prtf = spimage.prtf(recons[indices], support[indices], enantio=True, translate=True, clearsupport=True, reference=reference)
    allsupers[m,:,:] = output_prtf['super_image'] / subsetcount

try:
    del f['super_images']
except:
    pass

f['super_images'] = allsupers

<|endoftext|>


<commit_before>from django.conf.urls import url
from product.views import manage_product_categories, manage_product_types, manage_units_of_measurement, ProductList, ProductDetail, ProductCreate, ProductUpdate, ProductDelete

urlpatterns = [
    url(r'^$', ProductList.as_view(), name='product-list'),
    url(r'^detail/(?P<pk>[0-9]*)/$', ProductDetail.as_view(),
        name='product-detail'),
    url(r'^add/$', ProductCreate.as_view(), name='product-add'),
    url(r'^update/(?P<pk>[0-9]*)/$', ProductUpdate.as_view(),
        name='product-update'),
    url(r'^delete/(?P<pk>[0-9]*)/$', ProductDelete.as_view(),
        name="product-delete"),

    url(r'^type/$', manage_product_types, name='product-type-list'),
    url(r'^uom/$', manage_units_of_measurement, name='uom-list'),
    url(r'category/$', manage_product_categories, name='product-category-list'),
]
<commit_msg>Pricelist URL.<commit_after>from django.conf.urls import url
from product.views import manage_product_categories, manage_product_types, manage_units_of_measurement, ProductList, ProductDetail, ProductCreate, ProductUpdate, ProductDelete, manage_pricelist

urlpatterns = [
    url(r'^$', ProductList.as_view(), name='product-list'),
    url(r'^detail/(?P<pk>[0-9]*)/$', ProductDetail.as_view(),
        name='product-detail'),
    url(r'^add/$', ProductCreate.as_view(), name='product-add'),
    url(r'^update/(?P<pk>[0-9]*)/$', ProductUpdate.as_view(),
        name='product-update'),
    url(r'^delete/(?P<pk>[0-9]*)/$', ProductDelete.as_view(),
        name="product-delete"),

    url(r'^type/$', manage_product_types, name='product-type-list'),
    url(r'^uom/$', manage_units_of_measurement, name='uom-list'),
    url(r'category/$', manage_product_categories, name='product-category-list'),
    url(r'pricelist/$', manage_pricelist, name="pricelist"),
]
<|endoftext|>


<commit_before>from kivy.config import Config
from screeninfo import get_monitors
import sys

from RemuApp import RemuApp

"""
MAIN MODULE THAT RUNS THE APP

Accepts commandline arguments:
1st argument: if this is 'master', then the app is run
              in master-mode (not implemented)
2nd argument: if this is set, it is considered the
              slave-devices ip-address (not implemented)

"""

if __name__ == '__main__':
    windowed = True
    monitor = get_monitors()[0]
    if windowed:
        Config.set('graphics', 'height', 500)
        Config.set('graphics', 'width', 700)
        Config.set('graphics', 'borderless', '0')
    else:
        Config.set('graphics', 'fullscreen', '0')
        Config.set('graphics', 'height', str(monitor.height))
        Config.set('graphics', 'width', str(monitor.width))
        Config.set('graphics', 'borderless', '1')
    Config.write()
    args = sys.argv

    address = ''
    master = False

    if len(args) > 1:
        master = args[1] == 'master'

    if len(args) > 2:
        address = args[2]

    RemuApp().run()
<commit_msg>fullscreen switch toisinpäin<commit_after>from kivy.config import Config
from screeninfo import get_monitors
import sys

from RemuApp import RemuApp

"""
MAIN MODULE THAT RUNS THE APP

Accepts commandline arguments:
1st argument: if this is 'master', then the app is run
              in master-mode (not implemented)
2nd argument: if this is set, it is considered the
              slave-devices ip-address (not implemented)

"""

if __name__ == '__main__':
    fullscreen = False
    monitor = get_monitors()[0]
    if fullscreen:
        Config.set('graphics', 'fullscreen', '1')
        Config.set('graphics', 'height', str(monitor.height))
        Config.set('graphics', 'width', str(monitor.width))
        Config.set('graphics', 'borderless', '1')
    else:
        Config.set('graphics', 'height', 500)
        Config.set('graphics', 'width', 700)
        Config.set('graphics', 'borderless', '0')
    Config.write()
    args = sys.argv

    address = ''
    master = False

    if len(args) > 1:
        master = args[1] == 'master'

    if len(args) > 2:
        address = args[2]

    RemuApp().run()
<|endoftext|>


<commit_before>"""
Module for controlling MPI
"""
import sys
import petsc4py

comm = None
argv = sys.argv
petscInitialized = False

def init():
    global comm, petscInitialized
    if not petscInitialized:
        petsc4py.init(argv)
        petscInitialized = True
    else:
        from petsc4py import PETSc
        OptDB=PETSc.Options()
        narg = len(argv)
        for i,s in enumerate(argv):
            if len(s) > 0 and s[0] is '-':
                name = s
                if i+1 < narg and  argv[i+1][0] is not '-':
                    value = argv[i+1]
                    OptDB.setValue(name,value)
    new_comm = Comm()
    if not comm:
        comm = new_comm
    return new_comm

def get():
    if comm is None:
        return init()
    return comm

class Comm():
    """Proteus wrapper around PETSc/MPI communicators

    This is a very simple class that provides compatibility with older Proteus comm objects.
    """

    def __init__(self):
        from petsc4py import PETSc
        self.comm = PETSc.COMM_WORLD

    def isInitialized(self):
        return True

    def rank(self):
        """Return position in this communicator (0-indexed)"""
        return self.comm.rank

    def size(self):
        """Return number of processes on this communicator"""
        return self.comm.size

    def barrier(self):
        """Block all processes on this communicator until all have called this method"""
        return self.comm.Barrier()

    def isMaster(self):
        """Return true if this process is the 'master' for this communicator"""
        return self.comm.rank == 0

    def beginSequential(self, ng=1):
        """Begin a block of code to be completed sequentially by each process
        """

        if ng != 1:
            raise NotImplementedError("Only ng = 1 allowed")

        for i in range(self.comm.rank):
            self.comm.Barrier()
        return

    def endSequential(self, ng=1):
        """End a block of code to be completed sequentially by each process
        """

        if ng != 1:
            raise NotImplementedError("Only ng = 1 allowed")

        for i in range(self.comm.size - self.comm.rank - 1):
            self.comm.Barrier()
        return<commit_msg>Note in log when get is called before init<commit_after>"""
Module for controlling MPI
"""

import sys
import petsc4py
from .Profiling import logEvent as log

comm = None
argv = sys.argv
petscInitialized = False

def init():
    global comm, petscInitialized
    if not petscInitialized:
        petsc4py.init(argv)
        petscInitialized = True
    else:
        from petsc4py import PETSc
        OptDB=PETSc.Options()
        narg = len(argv)
        for i,s in enumerate(argv):
            if len(s) > 0 and s[0] is '-':
                name = s
                if i+1 < narg and  argv[i+1][0] is not '-':
                    value = argv[i+1]
                    OptDB.setValue(name,value)
    new_comm = Comm()
    if not comm:
        comm = new_comm
    return new_comm

def get():
    if comm is None:
        log("Comm.get called before init, init is being called for you.", 3)
        return init()
    return comm

class Comm():
    """Proteus wrapper around PETSc/MPI communicators

    This is a very simple class that provides compatibility with older Proteus comm objects.
    """

    def __init__(self):
        from petsc4py import PETSc
        self.comm = PETSc.COMM_WORLD

    def isInitialized(self):
        return True

    def rank(self):
        """Return position in this communicator (0-indexed)"""
        return self.comm.rank

    def size(self):
        """Return number of processes on this communicator"""
        return self.comm.size

    def barrier(self):
        """Block all processes on this communicator until all have called this method"""
        return self.comm.Barrier()

    def isMaster(self):
        """Return true if this process is the 'master' for this communicator"""
        return self.comm.rank == 0

    def beginSequential(self, ng=1):
        """Begin a block of code to be completed sequentially by each process
        """

        if ng != 1:
            raise NotImplementedError("Only ng = 1 allowed")

        for i in range(self.comm.rank):
            self.comm.Barrier()
        return

    def endSequential(self, ng=1):
        """End a block of code to be completed sequentially by each process
        """

        if ng != 1:
            raise NotImplementedError("Only ng = 1 allowed")

        for i in range(self.comm.size - self.comm.rank - 1):
            self.comm.Barrier()
        return<|endoftext|>