import os
import numpy
from operator import itemgetter

from amuse.community import *
from amuse.community.interface.se import StellarEvolution, StellarEvolutionInterface, \
    InternalStellarStructure, InternalStellarStructureInterface

from amuse.units.quantities import VectorQuantity
from amuse.support.interface import InCodeComponentImplementation
from amuse.support.options import option

class MESAInterface(CodeInterface, LiteratureReferencesMixIn, StellarEvolutionInterface,
        InternalStellarStructureInterface, CodeWithDataDirectories):
    """
    The software project MESA (Modules for Experiments in Stellar Astrophysics,
    http://mesa.sourceforge.net/), aims to provide state-of-the-art, robust,
    and efficient open source modules, usable singly or in combination for a
    wide range of applications in stellar astrophysics. The AMUSE interface to
    MESA can create and evolve stars using the MESA/STAR module. If you order a
    metallicity you haven't used before, starting models will be computed
    automatically and saved in the `mesa/src/data/star_data/starting_models`
    directory (please be patient...). All metallicities are supported, even the
    interesting case of Z=0. The supported stellar mass range is from
    about 0.1 to 100 Msun.

    References:
        .. [#] Paxton, Bildsten, Dotter, Herwig, Lesaffre & Timmes 2011, ApJS, arXiv:1009.1622 [2011ApJS..192....3P]
        .. [#] http://mesa.sourceforge.net/
    """
    def __init__(self, **options):
        CodeInterface.__init__(self, name_of_the_worker="mesa_worker", **options)
        LiteratureReferencesMixIn.__init__(self)
        CodeWithDataDirectories.__init__(self)

    @property
    def default_path_to_inlist(self):
        return os.path.join(self.get_data_directory(), 'AMUSE_inlist')

    @option(type="string", sections=('data'))
    def default_path_to_MESA_data(self):
        return os.path.join(self.amuse_root_directory, 'src', 'amuse', 'community', 'mesa', 'src', 'mesa', 'data')

    @legacy_function
    def set_MESA_paths():
        """
        Set the paths to the MESA inlist and data directories.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('inlist_path', dtype='string', direction=function.IN,
            description = "Path to the inlist file.")
        function.addParameter('MESA_data_path', dtype='string', direction=function.IN,
            description = "Path to the data directory.")
        function.addParameter('local_data_path', dtype='string', direction=function.IN,
            description = "Path to the data directory.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was set
        -1 - ERROR
            Directory does not exist
        """
        return function

    @legacy_function
    def get_maximum_number_of_stars():
        """
        Retrieve the maximum number of stars that can be
        handled by this instance.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('maximum_number_of_stars', dtype='int32', direction=function.OUT,
            description = "The current value of the maximum number of stars")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value of was retrieved
        """
        return function

    @legacy_function
    def new_zams_model():
        function = LegacyFunctionSpecification()
        function.addParameter('status', dtype='int32', direction=function.OUT)
        return function

    @legacy_function
    def new_pre_ms_particle():
        """
        Define a new pre-main-sequence star in the code. The star will start with the given mass.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.OUT
            , description="The new index for the star. This index can be used to refer to this star in other functions")
        function.addParameter('mass', dtype='float64', direction=function.IN
            , description="The initial mass of the star")
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_time_step():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('time_step', dtype='float64', direction=function.IN
            , description="The next timestep for the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            A star with the given index was not found.
        """
        return function

    @legacy_function
    def get_core_mass():
        """
        Retrieve the current core mass of the star, where hydrogen abundance is <= h1_boundary_limit
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('core_mass', dtype='float64', direction=function.OUT
            , description="The current core mass of the star, where hydrogen abundance is <= h1_boundary_limit")
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_mass_loss_rate():
        """
        Retrieve the current mass loss rate of the star. (positive for winds, negative for accretion)
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('mass_loss_rate', dtype='float64', direction=function.OUT
            , description="The current mass loss rate of the star. (positive for winds, negative for accretion)")
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_manual_mass_transfer_rate():
        """
        Retrieve the current user-specified mass transfer rate of the star. (negative for winds, positive for accretion)
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('mass_change', dtype='float64', direction=function.OUT
            , description="The current user-specified mass transfer rate of the star. (negative for winds, positive for accretion)")
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_manual_mass_transfer_rate():
        """
        Set a new user-specified mass transfer rate of the star. (negative for winds, positive for accretion)
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('mass_change', dtype='float64', direction=function.IN
            , description="The new user-specified mass transfer rate of the star. (negative for winds, positive for accretion)")
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_accrete_same_as_surface():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('accrete_same_as_surface_flag', dtype='int32', direction=function.OUT)
        function.result_type = 'int32'
        return function
    @legacy_function
    def set_accrete_same_as_surface():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('accrete_same_as_surface_flag', dtype='int32', direction=function.IN)
        function.result_type = 'int32'
        return function
    @legacy_function
    def get_accrete_composition_non_metals():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('h1', dtype='float64', direction=function.OUT)
        function.addParameter('h2', dtype='float64', direction=function.OUT)
        function.addParameter('he3', dtype='float64', direction=function.OUT)
        function.addParameter('he4', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function
    @legacy_function
    def set_accrete_composition_non_metals():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('h1', dtype='float64', direction=function.IN)
        function.addParameter('h2', dtype='float64', direction=function.IN)
        function.addParameter('he3', dtype='float64', direction=function.IN)
        function.addParameter('he4', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function
    @legacy_function
    def get_accrete_composition_metals_identifier():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('accrete_composition_metals_identifier', dtype='int32', direction=function.OUT)
        function.result_type = 'int32'
        return function
    @legacy_function
    def set_accrete_composition_metals_identifier():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('accrete_composition_metals_identifier', dtype='int32', direction=function.IN)
        function.result_type = 'int32'
        return function
    @legacy_function
    def get_accrete_composition_metals():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('li', dtype='float64', direction=function.OUT)
        function.addParameter('be', dtype='float64', direction=function.OUT)
        function.addParameter('b', dtype='float64', direction=function.OUT)
        function.addParameter('c', dtype='float64', direction=function.OUT)
        function.addParameter('n', dtype='float64', direction=function.OUT)
        function.addParameter('o', dtype='float64', direction=function.OUT)
        function.addParameter('f', dtype='float64', direction=function.OUT)
        function.addParameter('ne', dtype='float64', direction=function.OUT)
        function.addParameter('na', dtype='float64', direction=function.OUT)
        function.addParameter('mg', dtype='float64', direction=function.OUT)
        function.addParameter('al', dtype='float64', direction=function.OUT)
        function.addParameter('si', dtype='float64', direction=function.OUT)
        function.addParameter('p', dtype='float64', direction=function.OUT)
        function.addParameter('s', dtype='float64', direction=function.OUT)
        function.addParameter('cl', dtype='float64', direction=function.OUT)
        function.addParameter('ar', dtype='float64', direction=function.OUT)
        function.addParameter('k', dtype='float64', direction=function.OUT)
        function.addParameter('ca', dtype='float64', direction=function.OUT)
        function.addParameter('sc', dtype='float64', direction=function.OUT)
        function.addParameter('ti', dtype='float64', direction=function.OUT)
        function.addParameter('v', dtype='float64', direction=function.OUT)
        function.addParameter('cr', dtype='float64', direction=function.OUT)
        function.addParameter('mn', dtype='float64', direction=function.OUT)
        function.addParameter('fe', dtype='float64', direction=function.OUT)
        function.addParameter('co', dtype='float64', direction=function.OUT)
        function.addParameter('ni', dtype='float64', direction=function.OUT)
        function.addParameter('cu', dtype='float64', direction=function.OUT)
        function.addParameter('zn', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function
    @legacy_function
    def set_accrete_composition_metals():
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN)
        function.addParameter('li', dtype='float64', direction=function.IN)
        function.addParameter('be', dtype='float64', direction=function.IN)
        function.addParameter('b', dtype='float64', direction=function.IN)
        function.addParameter('c', dtype='float64', direction=function.IN)
        function.addParameter('n', dtype='float64', direction=function.IN)
        function.addParameter('o', dtype='float64', direction=function.IN)
        function.addParameter('f', dtype='float64', direction=function.IN)
        function.addParameter('ne', dtype='float64', direction=function.IN)
        function.addParameter('na', dtype='float64', direction=function.IN)
        function.addParameter('mg', dtype='float64', direction=function.IN)
        function.addParameter('al', dtype='float64', direction=function.IN)
        function.addParameter('si', dtype='float64', direction=function.IN)
        function.addParameter('p', dtype='float64', direction=function.IN)
        function.addParameter('s', dtype='float64', direction=function.IN)
        function.addParameter('cl', dtype='float64', direction=function.IN)
        function.addParameter('ar', dtype='float64', direction=function.IN)
        function.addParameter('k', dtype='float64', direction=function.IN)
        function.addParameter('ca', dtype='float64', direction=function.IN)
        function.addParameter('sc', dtype='float64', direction=function.IN)
        function.addParameter('ti', dtype='float64', direction=function.IN)
        function.addParameter('v', dtype='float64', direction=function.IN)
        function.addParameter('cr', dtype='float64', direction=function.IN)
        function.addParameter('mn', dtype='float64', direction=function.IN)
        function.addParameter('fe', dtype='float64', direction=function.IN)
        function.addParameter('co', dtype='float64', direction=function.IN)
        function.addParameter('ni', dtype='float64', direction=function.IN)
        function.addParameter('cu', dtype='float64', direction=function.IN)
        function.addParameter('zn', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_number_of_backups_in_a_row():
        """
        Retrieve the number_of_backups_in_a_row of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of number_of_backups_in_a_row")
        function.addParameter('n_backup', dtype='int32', direction=function.OUT
            , description="The current number_of_backups_in_a_row of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The number_of_backups_in_a_row was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        """
        return function

    @legacy_function
    def reset_number_of_backups_in_a_row():
        """
        Reset number_of_backups_in_a_row of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to reset the value of number_of_backups_in_a_row")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The number_of_backups_in_a_row was reset.
        -1 - ERROR
            A star with the given index was not found.
        """
        return function

    @legacy_function
    def get_mass_fraction_at_zone():
        """
        Retrieve the mass fraction at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to get the value of")
        function.addParameter('dq_i', dtype='float64', direction=function.OUT
            , description="The mass fraction at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def set_mass_fraction_at_zone():
        """
        Set the mass fraction at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to set the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to set the value of")
        function.addParameter('dq_i', dtype='float64', direction=function.IN
            , description="The mass fraction at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was set.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def get_luminosity_at_zone():
        """
        Retrieve the luminosity at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to get the value of")
        function.addParameter('lum_i', dtype='float64', direction=function.OUT
            , description="The luminosity at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def set_luminosity_at_zone():
        """
        Set the luminosity at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to set the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to set the value of")
        function.addParameter('lum_i', dtype='float64', direction=function.IN
            , description="The luminosity at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was set.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def get_entropy_at_zone():
        """
        Retrieve the entropy at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to get the value of")
        function.addParameter('S_i', dtype='float64', direction=function.OUT
            , description="The specific entropy at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def get_thermal_energy_at_zone():
        """
        Retrieve the entropy at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('zone', dtype='int32', direction=function.IN
            , description="The zone/mesh-cell of the star to get the value of")
        function.addParameter('E_i', dtype='float64', direction=function.OUT
            , description="The specific thermal energy at the specified zone/mesh-cell of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        -2 - ERROR
            A zone with the given index was not found.
        """
        return function

    @legacy_function
    def get_brunt_vaisala_frequency_squared_at_zone():
        """
        Retrieve the Brunt-Vaisala frequency squared at the specified zone/mesh-cell of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN, unit=INDEX)
        function.addParameter('zone', dtype='int32', direction=function.IN, unit=NO_UNIT)
        function.addParameter('brunt_N2', dtype='float64', direction=function.OUT, unit=units.s**-2)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_id_of_species():
        """
        Retrieve the chem_ID of the chemical abundance variable of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('species', dtype='int32', direction=function.IN
            , description="The species of the star to get the name of")
        function.addParameter('species_id', dtype='int32', direction=function.OUT
            , description="The chem_ID of the chemical abundance variable of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        """
        return function

    @legacy_function
    def get_mass_of_species():
        """
        Retrieve the mass number of the chemical abundance variable of the star.
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.addParameter('species', dtype='int32', direction=function.IN
            , description="The species of the star to get the mass number of")
        function.addParameter('species_mass', dtype='float64', direction=function.OUT
            , description="The mass number of the chemical abundance variable of the star.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value was retrieved.
        -1 - ERROR
            A star with the given index was not found.
        """
        return function

    @legacy_function
    def erase_memory():
        """
        Erase memory of the star, i.e. copy the current structure over the memory of
        the structure of the previous steps. Useful after setting the stucture of
        the star, to prevent backup steps to undo changes
        """
        function = LegacyFunctionSpecification()
        function.can_handle_array = True
        function.addParameter('index_of_the_star', dtype='int32', direction=function.IN
            , description="The index of the star to get the value of")
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_max_age_stop_condition():
        """
        Retrieve the current maximum age stop condition of this instance (in years).
        Evolution will stop once the star has reached this maximum age.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('max_age_stop_condition', dtype='float64', direction=function.OUT
            , description="The current maximum age stop condition of this instance (in years).")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_max_age_stop_condition():
        """
        Set the new maximum age stop condition of this instance (in years).
        Evolution will stop once the star has reached this maximum age.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('max_age_stop_condition', dtype='float64', direction=function.IN
            , description="The new maximum age stop condition of this instance (in years).")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_min_timestep_stop_condition():
        """
        Retrieve the current minimum timestep stop condition of this instance (in years).
        Evolution will stop if the timestep required by the solver in order to converge
        has decreased below this minimum timestep.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('min_timestep_stop_condition', dtype='float64', direction=function.OUT
            , description="The current minimum timestep stop condition of this instance (in years).")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_min_timestep_stop_condition():
        """
        Set the new minimum timestep stop condition of this instance (in years).
        Evolution will stop if the timestep required by the solver in order to converge
        has decreased below this minimum timestep.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('min_timestep_stop_condition', dtype='float64', direction=function.IN
            , description="The new minimum timestep stop condition of this instance (in years).")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_max_iter_stop_condition():
        """
        Retrieve the current maximum number of iterations of this instance. (Negative means no maximum)
        Evolution will stop after this number of iterations.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('max_iter_stop_condition', dtype='int32', direction=function.OUT
            , description="The current maximum number of iterations of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_max_iter_stop_condition():
        """
        Set the new maximum number of iterations of this instance. (Negative means no maximum)
        Evolution will stop after this number of iterations.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('max_iter_stop_condition', dtype='int32', direction=function.IN
            , description="The new maximum number of iterations of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_convective_overshoot_parameter():
        function = LegacyFunctionSpecification()
        function.addParameter('convective_overshoot_parameter', dtype='float64', direction=function.OUT,
            description="The current value of the convective overshoot parameter.")
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_convective_overshoot_parameter():
        function = LegacyFunctionSpecification()
        function.addParameter('convective_overshoot_parameter', dtype='float64', direction=function.IN,
            description="The new value of the convective overshoot parameter.")
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_mixing_length_ratio():
        """
        Retrieve the current value of the mixing length ratio.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('mixing_length_ratio', dtype='float64', direction=function.OUT
            , description="The current value of the mixing length ratio.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_mixing_length_ratio():
        """
        Set the value of the mixing length ratio.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('mixing_length_ratio', dtype='float64', direction=function.IN
            , description="The new value of the mixing length ratio.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_semi_convection_efficiency():
        """
        Retrieve the current value of the efficiency of semi-convection,
        after Heger, Langer, & Woosley 2000 (ApJ), which goes back to
        Langer, Sugimoto & Fricke 1983 (A&A).
        """
        function = LegacyFunctionSpecification()
        function.addParameter('semi_convection_efficiency', dtype='float64', direction=function.OUT
            , description="The current value of the efficiency of semi-convection.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_semi_convection_efficiency():
        """
        Set the value of the efficiency of semi-convection,
        after Heger, Langer, & Woosley 2000 (ApJ), which goes back to
        Langer, Sugimoto & Fricke 1983 (A&A).
        """
        function = LegacyFunctionSpecification()
        function.addParameter('semi_convection_efficiency', dtype='float64', direction=function.IN
            , description="The new value of the efficiency of semi-convection.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_RGB_wind_scheme():
        """
        Retrieve the current wind (mass loss) scheme for RGB stars:
        No automatic wind (0)
        Reimers (1): e.g. see: Baschek, Kegel, Traving (eds), Springer, Berlin, 1975, p. 229.
        Blocker (2): T. Blocker, A&A 297, 727-738 (1995)
        de Jager (3): de Jager, C., Nieuwenhuijzen, H., & van der Hucht, K. A. 1988, A&AS, 72, 259
        Dutch (4): Glebbeek et al 2009, Vink et al 2001, Nugis & Lamers 2000, de Jager 1990
        Mattsson (5)
        """
        function = LegacyFunctionSpecification()
        function.addParameter('RGB_wind_scheme', dtype='int32', direction=function.OUT
            , description="The current wind (mass loss) scheme for RGB stars of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_RGB_wind_scheme():
        """
        Set the new wind (mass loss) scheme for RGB stars:
        No automatic wind (0)
        Reimers (1): e.g. see: Baschek, Kegel, Traving (eds), Springer, Berlin, 1975, p. 229.
        Blocker (2): T. Blocker, A&A 297, 727-738 (1995)
        de Jager (3): de Jager, C., Nieuwenhuijzen, H., & van der Hucht, K. A. 1988, A&AS, 72, 259
        Dutch (4): Glebbeek et al 2009, Vink et al 2001, Nugis & Lamers 2000, de Jager 1990
        Mattsson (5)
        """
        function = LegacyFunctionSpecification()
        function.addParameter('RGB_wind_scheme', dtype='int32', direction=function.IN
            , description="The new wind (mass loss) scheme for RGB stars of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_AGB_wind_scheme():
        """
        Retrieve the current wind (mass loss) scheme for AGB stars:
        No automatic wind (0)
        Reimers (1): e.g. see: Baschek, Kegel, Traving (eds), Springer, Berlin, 1975, p. 229.
        Blocker (2): T. Blocker, A&A 297, 727-738 (1995)
        de Jager (3): de Jager, C., Nieuwenhuijzen, H., & van der Hucht, K. A. 1988, A&AS, 72, 259
        Dutch (4): Glebbeek et al 2009, Vink et al 2001, Nugis & Lamers 2000, de Jager 1990
        Mattsson (5)
        """
        function = LegacyFunctionSpecification()
        function.addParameter('AGB_wind_scheme', dtype='int32', direction=function.OUT
            , description="The current wind (mass loss) scheme for AGB stars of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            Current value was retrieved
        -1 - ERROR
            The code could not retrieve the value.
        """
        return function

    @legacy_function
    def set_AGB_wind_scheme():
        """
        Set the new wind (mass loss) scheme for AGB stars:
        No automatic wind (0)
        Reimers (1): e.g. see: Baschek, Kegel, Traving (eds), Springer, Berlin, 1975, p. 229.
        Blocker (2): T. Blocker, A&A 297, 727-738 (1995)
        de Jager (3): de Jager, C., Nieuwenhuijzen, H., & van der Hucht, K. A. 1988, A&AS, 72, 259
        Dutch (4): Glebbeek et al 2009, Vink et al 2001, Nugis & Lamers 2000, de Jager 1990
        Mattsson (5)
        """
        function = LegacyFunctionSpecification()
        function.addParameter('AGB_wind_scheme', dtype='int32', direction=function.IN
            , description="The new wind (mass loss) scheme for AGB stars of this instance.")
        function.result_type = 'int32'
        function.result_doc = """
        0 - OK
            The value has been set.
        -1 - ERROR
            The code could not set the value.
        """
        return function

    @legacy_function
    def get_reimers_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('reimers_wind_efficiency', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_reimers_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('reimers_wind_efficiency', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_blocker_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('blocker_wind_efficiency', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_blocker_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('blocker_wind_efficiency', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_de_jager_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('de_jager_wind_efficiency', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_de_jager_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('de_jager_wind_efficiency', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_dutch_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('dutch_wind_efficiency', dtype='float64', direction=function.OUT)
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_dutch_wind_efficiency():
        function = LegacyFunctionSpecification()
        function.addParameter('dutch_wind_efficiency', dtype='float64', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def get_stabilize_new_stellar_model_flag():
        function = LegacyFunctionSpecification()
        function.addParameter('stabilize_new_stellar_model_flag', dtype='int32', direction=function.OUT)
        function.result_type = 'int32'
        return function

    @legacy_function
    def set_stabilize_new_stellar_model_flag():
        function = LegacyFunctionSpecification()
        function.addParameter('stabilize_new_stellar_model_flag', dtype='int32', direction=function.IN)
        function.result_type = 'int32'
        return function

    @legacy_function
    def new_stellar_model():
        """
        Define a new star model in the code. The star needs to be finalized
        before it can evolve, see 'finalize_stellar_model'.
        """
        function = LegacyFunctionSpecification()
        function.must_handle_array = True
        for par in ['d_mass', 'radius', 'rho', 'temperature', 'luminosity',
                'X_H', 'X_He', 'X_C', 'X_N', 'X_O', 'X_Ne', 'X_Mg', 'X_Si', 'X_Fe']:
            function.addParameter(par, dtype='float64', direction=function.IN)
        function.addParameter('n', 'int32', function.LENGTH)
        function.result_type = 'int32'
        return function

    @legacy_function
    def finalize_stellar_model():
        """
        Finalize the new star model defined by 'new_stellar_model'.
        """
        function = LegacyFunctionSpecification()
        function.addParameter('index_of_the_star', dtype='int32',
            direction=function.OUT, description = "The new index for the star. "
            "This index can be used to refer to this star in other functions")
        function.addParameter('age_tag', dtype='float64', direction=function.IN,
            description = "The initial age of the star")
        function.result_type = 'int32'
        return function

class MESA(StellarEvolution, InternalStellarStructure):

    def __init__(self, **options):
        InCodeComponentImplementation.__init__(self, MESAInterface(**options), **options)

        output_dir = self.get_output_directory()
        if not self.channel_type == 'distributed':
            self.ensure_data_directory_exists(os.path.join(output_dir, 'star_data', 'starting_models'))

        self.set_MESA_paths(
            self.default_path_to_inlist,
            self.default_path_to_MESA_data,
            output_dir
        )
        self.model_time = 0.0 | units.yr

    def define_parameters(self, handler):

        handler.add_method_parameter(
            "get_metallicity",
            "set_metallicity",
            "metallicity",
            "Metallicity of all stars",
            default_value = 0.02
        )

        handler.add_method_parameter(
            "get_max_age_stop_condition",
            "set_max_age_stop_condition",
            "max_age_stop_condition",
            "The maximum age stop condition of this instance.",
            default_value = 1.0e36 | units.yr
        )

        handler.add_method_parameter(
            "get_min_timestep_stop_condition",
            "set_min_timestep_stop_condition",
            "min_timestep_stop_condition",
            "The minimum timestep stop condition of this instance.",
            default_value = 1.0e-6 | units.s
        )

        handler.add_method_parameter(
            "get_max_iter_stop_condition",
            "set_max_iter_stop_condition",
            "max_iter_stop_condition",
            "The maximum number of iterations of this instance. (Negative means no maximum)",
            default_value = -1111
        )

        handler.add_method_parameter(
            "get_convective_overshoot_parameter",
            "set_convective_overshoot_parameter",
            "herwig_convective_overshoot_parameter",
            "The convective overshoot parameter (Herwig 2000), f=0.016 is argued to be a reasonable value.",
            default_value = 0.0
        )

        handler.add_method_parameter(
            "get_mixing_length_ratio",
            "set_mixing_length_ratio",
            "mixing_length_ratio",
            "The mixing-length ratio (alpha).",
            default_value = 2.0
        )

        handler.add_method_parameter(
            "get_semi_convection_efficiency",
            "set_semi_convection_efficiency",
            "semi_convection_efficiency",
            "The efficiency of semi-convection, after Heger, Langer, & Woosley 2000 (ApJ), "
               "which goes back to Langer, Sugimoto & Fricke 1983 (A&A).",
            default_value = 0.0
        )

        handler.add_method_parameter(
            "get_RGB_wind_scheme",
            "set_RGB_wind_scheme",
            "RGB_wind_scheme",
            "The mass loss scheme for RGB stars: none (0), Reimers (1), "
                "Blocker (2), de Jager (3), Dutch (4), Mattsson (5)",
            default_value = 1
        )

        handler.add_method_parameter(
            "get_AGB_wind_scheme",
            "set_AGB_wind_scheme",
            "AGB_wind_scheme",
            "The mass loss scheme for AGB stars: none (0), Reimers (1), "
                "Blocker (2), de Jager (3), Dutch (4), Mattsson (5)",
            default_value = 1
        )

        handler.add_method_parameter(
            "get_reimers_wind_efficiency",
            "set_reimers_wind_efficiency",
            "reimers_wind_efficiency",
            "The Reimers mass loss efficiency. Only used if (RGB/AGB_wind_scheme == 1).",
            default_value = 0.5
        )
        handler.add_method_parameter(
            "get_blocker_wind_efficiency",
            "set_blocker_wind_efficiency",
            "blocker_wind_efficiency",
            "The Blocker mass loss efficiency. Only used if (RGB/AGB_wind_scheme == 2).",
            default_value = 0.1
        )
        handler.add_method_parameter(
            "get_de_jager_wind_efficiency",
            "set_de_jager_wind_efficiency",
            "de_jager_wind_efficiency",
            "The de Jager mass loss efficiency. Only used if (RGB/AGB_wind_scheme == 3).",
            default_value = 0.8
        )
        handler.add_method_parameter(
            "get_dutch_wind_efficiency",
            "set_dutch_wind_efficiency",
            "dutch_wind_efficiency",
            "The Dutch mass loss efficiency. Only used if (RGB/AGB_wind_scheme == 4).",
            default_value = 0.8
        )
        handler.add_boolean_parameter(
            "get_stabilize_new_stellar_model_flag",
            "set_stabilize_new_stellar_model_flag",
            "stabilize_new_stellar_model_flag",
            "Flag specifying whether to stabilize any loaded stellar models first.",
            default_value = True
        )

    def define_particle_sets(self, handler):
        handler.define_super_set('particles', ['native_stars', 'imported_stars', 'pre_ms_stars'],
            index_to_default_set = 0)

        handler.define_set('imported_stars', 'index_of_the_star')
        handler.set_new('imported_stars', 'finalize_stellar_model')
        handler.set_delete('imported_stars', 'delete_star')

        handler.define_set('native_stars', 'index_of_the_star')
        handler.set_new('native_stars', 'new_particle')
        handler.set_delete('native_stars', 'delete_star')

        handler.define_set('pre_ms_stars', 'index_of_the_star')
        handler.set_new('pre_ms_stars', 'new_pre_ms_particle')
        handler.set_delete('pre_ms_stars', 'delete_star')

        for particle_set_name in ['native_stars', 'imported_stars', 'pre_ms_stars']:
            handler.add_getter(particle_set_name, 'get_radius', names = ('radius',))
            handler.add_getter(particle_set_name, 'get_stellar_type', names = ('stellar_type',))
            handler.add_getter(particle_set_name, 'get_mass', names = ('mass',))
            handler.add_setter(particle_set_name, 'set_mass', names = ('mass',))
            handler.add_getter(particle_set_name, 'get_core_mass', names = ('core_mass',))
            handler.add_getter(particle_set_name, 'get_mass_loss_rate', names = ('wind',))
            handler.add_getter(particle_set_name, 'get_age', names = ('age',))
            handler.add_getter(particle_set_name, 'get_time_step', names = ('time_step',))
            handler.add_setter(particle_set_name, 'set_time_step', names = ('time_step',))
            handler.add_getter(particle_set_name, 'get_luminosity', names = ('luminosity',))
            handler.add_getter(particle_set_name, 'get_temperature', names = ('temperature',))

            handler.add_getter(particle_set_name, 'get_manual_mass_transfer_rate', names = ('mass_change',))
            handler.add_setter(particle_set_name, 'set_manual_mass_transfer_rate', names = ('mass_change',))

            handler.add_method(particle_set_name, 'get_accrete_same_as_surface')
            handler.add_method(particle_set_name, 'set_accrete_same_as_surface')
            handler.add_method(particle_set_name, 'get_accrete_composition_non_metals')
            handler.add_method(particle_set_name, 'set_accrete_composition_non_metals')
            handler.add_method(particle_set_name, 'get_accrete_composition_metals_identifier')
            handler.add_method(particle_set_name, 'set_accrete_composition_metals_identifier')
            handler.add_method(particle_set_name, 'get_accrete_composition_metals')
            handler.add_method(particle_set_name, 'set_accrete_composition_metals')

            handler.add_method(particle_set_name, 'evolve_one_step')
            handler.add_method(particle_set_name, 'evolve_for')
            InternalStellarStructure.define_particle_sets(
                self,
                handler,
                set_name = particle_set_name
            )
            handler.add_method(particle_set_name, 'get_mass_profile')
            handler.add_method(particle_set_name, 'set_mass_profile')
            handler.add_method(particle_set_name, 'get_cumulative_mass_profile')
            handler.add_method(particle_set_name, 'get_luminosity_profile')
            handler.add_method(particle_set_name, 'set_luminosity_profile')
            handler.add_method(particle_set_name, 'get_entropy_profile')
            handler.add_method(particle_set_name, 'get_thermal_energy_profile')
            handler.add_method(particle_set_name, 'get_brunt_vaisala_frequency_squared_profile')
            handler.add_method(particle_set_name, 'get_IDs_of_species')
            handler.add_method(particle_set_name, 'get_masses_of_species')
            handler.add_method(particle_set_name, 'get_number_of_backups_in_a_row')
            handler.add_method(particle_set_name, 'reset_number_of_backups_in_a_row')

    def define_state(self, handler):
        StellarEvolution.define_state(self, handler)
        handler.add_method('EDIT', 'new_pre_ms_particle')
        handler.add_method('UPDATE', 'new_pre_ms_particle')
        handler.add_transition('RUN', 'UPDATE', 'new_pre_ms_particle', False)
        handler.add_method('EDIT', 'finalize_stellar_model')
        handler.add_method('UPDATE', 'finalize_stellar_model')
        handler.add_transition('RUN', 'UPDATE', 'finalize_stellar_model', False)

    def define_errorcodes(self, handler):
        InternalStellarStructure.define_errorcodes(self, handler)
        handler.add_errorcode(-1, 'Something went wrong...')
        handler.add_errorcode(-4, 'Not implemented.')
        handler.add_errorcode(-11, 'Evolve terminated: Unspecified stop condition reached.')
        handler.add_errorcode(-12, 'Evolve terminated: Maximum age reached.')
        handler.add_errorcode(-13, 'Evolve terminated: Maximum number of iterations reached.')
        handler.add_errorcode(-14, 'Evolve terminated: Maximum number of backups reached.')
        handler.add_errorcode(-15, 'Evolve terminated: Minimum timestep limit reached.')

    def define_methods(self, handler):
        InternalStellarStructure.define_methods(self, handler)
        StellarEvolution.define_methods(self, handler)
        handler.add_method(
            "new_pre_ms_particle",
            (units.MSun),
            (handler.INDEX, handler.ERROR_CODE)
        )
        handler.add_method(
            "set_time_step",
            (handler.INDEX, units.yr),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_core_mass",
            (handler.INDEX,),
            (units.MSun, handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_mass_loss_rate",
            (handler.INDEX,),
            (units.g / units.s, handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_manual_mass_transfer_rate",
            (handler.INDEX,),
            (units.MSun / units.yr, handler.ERROR_CODE,)
        )
        handler.add_method(
            "set_manual_mass_transfer_rate",
            (handler.INDEX, units.MSun / units.yr),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_number_of_backups_in_a_row",
            (handler.INDEX,),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )
        handler.add_method(
            "reset_number_of_backups_in_a_row",
            (handler.INDEX,),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_mass_fraction_at_zone",
            (handler.INDEX,handler.NO_UNIT,),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )
        handler.add_method(
            "set_mass_fraction_at_zone",
            (handler.INDEX, handler.NO_UNIT, handler.NO_UNIT,),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_luminosity_at_zone",
            (handler.INDEX,handler.NO_UNIT,),
            (units.erg/units.s, handler.ERROR_CODE,)
        )
        handler.add_method(
            "set_luminosity_at_zone",
            (handler.INDEX, handler.NO_UNIT, units.erg/units.s,),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_entropy_at_zone",
            (handler.INDEX,handler.NO_UNIT,),
            (units.erg/units.K, handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_thermal_energy_at_zone",
            (handler.INDEX,handler.NO_UNIT,),
            (units.erg/units.g, handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_id_of_species",
            (handler.INDEX,handler.NO_UNIT,),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )
        handler.add_method(
            "get_mass_of_species",
            (handler.INDEX,handler.NO_UNIT,),
            (units.amu, handler.ERROR_CODE,)
        )
        handler.add_method(
            "erase_memory",
            (handler.INDEX,),
            (handler.ERROR_CODE,),
            public_name = "_erase_memory"
        )
        handler.add_method(
            "new_stellar_model",
            (units.MSun, units.cm, units.g / units.cm**3, units.K, units.erg / units.s,
                handler.NO_UNIT, handler.NO_UNIT, handler.NO_UNIT, handler.NO_UNIT, handler.NO_UNIT,
                handler.NO_UNIT, handler.NO_UNIT, handler.NO_UNIT, handler.NO_UNIT,),
            (handler.ERROR_CODE,)
        )
        handler.add_method(
            "finalize_stellar_model",
            (units.yr,),
            (handler.INDEX, handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_max_age_stop_condition",
            (),
            (units.yr, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_max_age_stop_condition",
            (units.yr, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_min_timestep_stop_condition",
            (),
            (units.s, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_min_timestep_stop_condition",
            (units.s, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_max_iter_stop_condition",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_max_iter_stop_condition",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_mixing_length_ratio",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_mixing_length_ratio",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_semi_convection_efficiency",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_semi_convection_efficiency",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_RGB_wind_scheme",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_RGB_wind_scheme",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_AGB_wind_scheme",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_AGB_wind_scheme",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_reimers_wind_efficiency",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_reimers_wind_efficiency",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_blocker_wind_efficiency",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_blocker_wind_efficiency",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_de_jager_wind_efficiency",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_de_jager_wind_efficiency",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

        handler.add_method(
            "get_dutch_wind_efficiency",
            (),
            (handler.NO_UNIT, handler.ERROR_CODE,)
        )

        handler.add_method(
            "set_dutch_wind_efficiency",
            (handler.NO_UNIT, ),
            (handler.ERROR_CODE,)
        )

    def initialize_module_with_default_parameters(self):
        self.parameters.set_defaults()
        self.initialize_code()

    def initialize_module_with_current_parameters(self):
        self.initialize_code()

    def commit_parameters(self):
        self.parameters.send_not_set_parameters_to_code()
        self.parameters.send_cached_parameters_to_code()
        self.overridden().commit_parameters()

    def get_mass_profile(self, indices_of_the_stars, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying mass profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        return self.get_mass_fraction_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none)

    def get_cumulative_mass_profile(self, indices_of_the_stars, number_of_zones = None):
        frac_profile = self.get_mass_profile(indices_of_the_stars, number_of_zones = number_of_zones)
        return frac_profile.cumsum()

    def set_mass_profile(self, indices_of_the_stars, values, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Setting mass profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        self._check_supplied_values(len(values), number_of_zones)
        self.set_mass_fraction_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none, values)
        if hasattr(self, "_erase_memory"):
            self._erase_memory(indices_of_the_stars)

    def get_luminosity_profile(self, indices_of_the_stars, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying luminosity profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        return self.get_luminosity_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none)

    def set_luminosity_profile(self, indices_of_the_stars, values, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Setting luminosity profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        self._check_supplied_values(len(values), number_of_zones)
        self.set_luminosity_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none, values)
        if hasattr(self, "_erase_memory"):
            self._erase_memory(indices_of_the_stars)

    def get_entropy_profile(self, indices_of_the_stars, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying entropy profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        return self.get_entropy_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none)

    def get_thermal_energy_profile(self, indices_of_the_stars, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying thermal energy profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        return self.get_thermal_energy_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none)

    def get_brunt_vaisala_frequency_squared_profile(self, indices_of_the_stars, number_of_zones = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying brunt-vaisala-frequency-squared profiles")
        if number_of_zones is None:
            number_of_zones = self.get_number_of_zones(indices_of_the_stars)
        return self.get_brunt_vaisala_frequency_squared_at_zone([indices_of_the_stars]*number_of_zones, list(range(number_of_zones)) | units.none)

    def get_IDs_of_species(self, indices_of_the_stars, number_of_species = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying chemical abundance IDs")
        if number_of_species is None:
            number_of_species = self.get_number_of_species(indices_of_the_stars)
        return list(self.get_id_of_species(
            [indices_of_the_stars]*number_of_species,
            list(range(1,number_of_species+1))
        ))

    def get_masses_of_species(self, indices_of_the_stars, number_of_species = None):
        indices_of_the_stars = self._check_number_of_indices(indices_of_the_stars, action_string = "Querying chemical abundance mass numbers")
        if number_of_species is None:
            number_of_species = self.get_number_of_species(indices_of_the_stars)
        return self.get_mass_of_species(
            [indices_of_the_stars]*number_of_species,
            list(range(1,number_of_species+1))
        )

    def new_particle_from_model(self, internal_structure, current_age=0|units.Myr, key=None):
        if isinstance(internal_structure, dict):
            if "dmass" in internal_structure:
                mass_profile = internal_structure['dmass'][::-1]
            else:
                cumulative_mass_profile = [0.0] | units.MSun
                cumulative_mass_profile.extend(internal_structure['mass'])
                mass_profile = (cumulative_mass_profile[1:] - cumulative_mass_profile[:-1])[::-1]
            self.new_stellar_model(
                mass_profile,
                internal_structure['radius'][::-1],
                internal_structure['rho'][::-1],
                internal_structure['temperature'][::-1],
                internal_structure['luminosity'][::-1],
                internal_structure['X_H'][::-1],
                internal_structure['X_He'][::-1],
                internal_structure['X_C'][::-1],
                internal_structure['X_N'][::-1],
                internal_structure['X_O'][::-1],
                internal_structure['X_Ne'][::-1],
                internal_structure['X_Mg'][::-1],
                internal_structure['X_Si'][::-1],
                internal_structure['X_Fe'][::-1]
            )
        else:
            if hasattr(internal_structure, "dmass"):
                mass_profile = internal_structure.dmass[::-1]
            else:
                cumulative_mass_profile = [0.0] | units.MSun
                cumulative_mass_profile.extend(internal_structure.mass)
                mass_profile = (cumulative_mass_profile[1:] - cumulative_mass_profile[:-1])[::-1]
            self.new_stellar_model(
                mass_profile,
                internal_structure.radius[::-1],
                internal_structure.rho[::-1],
                internal_structure.temperature[::-1],
                internal_structure.luminosity[::-1],
                internal_structure.X_H[::-1],
                internal_structure.X_He[::-1],
                internal_structure.X_C[::-1],
                internal_structure.X_N[::-1],
                internal_structure.X_O[::-1],
                internal_structure.X_Ne[::-1],
                internal_structure.X_Mg[::-1],
                internal_structure.X_Si[::-1],
                internal_structure.X_Fe[::-1]
            )
        tmp_star = datamodel.Particle(key=key)
        tmp_star.age_tag = current_age
        return self.imported_stars.add_particle(tmp_star)

Mesa = MESA


# -*- coding: utf-8 -*-
"""
Signal processing functions for RtMemory objects.

For sequential packet processing that requires memory (which includes recursive
filtering), each processing function (e.g., :mod:`obspy.realtime.signal`)
needs to manage the initialization and update of
:class:`~obspy.realtime.rtmemory.RtMemory` object(s), and needs to know when
and how to get values from this memory.

For example: Boxcar smoothing: For each new data point available past the end
of the boxcar, the original, un-smoothed data point value at the beginning of
the boxcar has to be subtracted from the running boxcar sum, this value may be
in a previous packet, so has to be retrieved from memory see
:func:`obspy.realtime.signal.boxcar`.

:copyright:
    The ObsPy Development Team (devs@obspy.org), Anthony Lomax & Alessia Maggi
:license:
    GNU Lesser General Public License, Version 3
    (https://www.gnu.org/copyleft/lesser.html)
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
from future.builtins import *  # NOQA

import math
import sys

import numpy as np

from obspy.core.trace import Trace, UTCDateTime
from obspy.realtime.rtmemory import RtMemory

_PI = math.pi
_TWO_PI = 2.0 * math.pi
_MIN_FLOAT_VAL = 1.0e-20

def offset(trace, offset=0.0, rtmemory_list=None):  # @UnusedVariable
    """
    Add the specified offset to the data.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace: :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type offset: float, optional
    :param offset: offset (default is 0.0)
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object
    """

    if not isinstance(trace, Trace):
        msg = "Trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    trace.data += offset
    return trace.data

def scale(trace, factor=1.0, rtmemory_list=None):  # @UnusedVariable
    """
    Scale array data samples by specified factor.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type factor: float, optional
    :param factor: Scale factor (default is 1.0).
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)
    # XXX not sure how this should be for realtime analysis, here
    # I assume, we do not want to change the underlying dtype
    trace.data *= np.array(factor, dtype=trace.data.dtype)
    return trace.data

def integrate(trace, rtmemory_list=None):
    """
    Apply simple rectangular integration to array data.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    if not rtmemory_list:
        rtmemory_list = [RtMemory()]

    sample = trace.data
    if np.size(sample) < 1:
        return sample

    delta_time = trace.stats.delta

    rtmemory = rtmemory_list[0]

    # initialize memory object
    if not rtmemory.initialized:
        memory_size_input = 0
        memory_size_output = 1
        rtmemory.initialize(sample.dtype, memory_size_input,
                            memory_size_output, 0, 0)

    sum_ = rtmemory.output[0]

    for i in range(np.size(sample)):
        sum_ += sample[i] * delta_time
        sample[i] = sum_

    rtmemory.output[0] = sum_

    return sample

def differentiate(trace, rtmemory_list=None):
    """
    Apply simple differentiation to array data.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    if not rtmemory_list:
        rtmemory_list = [RtMemory()]

    sample = trace.data
    if np.size(sample) < 1:
        return(sample)

    delta_time = trace.stats.delta

    rtmemory = rtmemory_list[0]

    # initialize memory object
    if not rtmemory.initialized:
        memory_size_input = 1
        memory_size_output = 0
        rtmemory.initialize(sample.dtype, memory_size_input,
                            memory_size_output, 0, 0)
        # avoid large diff value for first output sample
        rtmemory.input[0] = sample[0]

    previous_sample = rtmemory.input[0]

    for i in range(np.size(sample)):
        diff = (sample[i] - previous_sample) / delta_time
        previous_sample = sample[i]
        sample[i] = diff

    rtmemory.input[0] = previous_sample

    return sample

def boxcar(trace, width, rtmemory_list=None):
    """
    Apply boxcar smoothing to data in array sample.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type width: int
    :param width: Width in number of sample points for filter.
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    if not width > 0:
        msg = "width parameter not specified or < 1."
        raise ValueError(msg)

    if not rtmemory_list:
        rtmemory_list = [RtMemory()]

    sample = trace.data

    rtmemory = rtmemory_list[0]

    # initialize memory object
    if not rtmemory.initialized:
        memory_size_input = width
        memory_size_output = 0
        rtmemory.initialize(sample.dtype, memory_size_input,
                            memory_size_output, 0, 0)

    # initialize array for time-series results
    new_sample = np.zeros(np.size(sample), sample.dtype)

    i = 0
    i1 = i - width
    i2 = i  # causal boxcar of width width
    sum_ = 0.0
    icount = 0
    for i in range(np.size(sample)):
        value = 0.0
        if (icount == 0):  # first pass, accumulate sum
            for n in range(i1, i2 + 1):
                if (n < 0):
                    value = rtmemory.input[width + n]
                else:
                    value = sample[n]
                sum_ += value
                icount = icount + 1
        else:  # later passes, update sum
            if ((i1 - 1) < 0):
                value = rtmemory.input[width + (i1 - 1)]
            else:
                value = sample[(i1 - 1)]
            sum_ -= value
            if (i2 < 0):
                value = rtmemory.input[width + i2]
            else:
                value = sample[i2]
            sum_ += value
        if (icount > 0):
            new_sample[i] = (float)(sum_ / float(icount))
        else:
            new_sample[i] = 0.0
        i1 = i1 + 1
        i2 = i2 + 1

    rtmemory.update_input(sample)

    return new_sample

def tauc(trace, width, rtmemory_list=None):
    """
    Calculate instantaneous period in a fixed window (Tau_c).

    .. seealso::

        Implements equations 1-3 in [Allen2003]_ except use a fixed width
        window instead of decay function.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type width: int
    :param width: Width in number of sample points for tauc window.
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    if not width > 0:
        msg = "tauc: width parameter not specified or < 1."
        raise ValueError(msg)

    if not rtmemory_list:
        rtmemory_list = [RtMemory(), RtMemory()]

    sample = trace.data
    delta_time = trace.stats.delta

    rtmemory = rtmemory_list[0]
    rtmemory_dval = rtmemory_list[1]

    sample_last = 0.0

    # initialize memory object
    if not rtmemory.initialized:
        memory_size_input = width
        memory_size_output = 1
        rtmemory.initialize(sample.dtype, memory_size_input,
                            memory_size_output, 0, 0)
        sample_last = sample[0]
    else:
        sample_last = rtmemory.input[width - 1]

    # initialize memory object
    if not rtmemory_dval.initialized:
        memory_size_input = width
        memory_size_output = 1
        rtmemory_dval.initialize(sample.dtype, memory_size_input,
                                 memory_size_output, 0, 0)

    new_sample = np.zeros(np.size(sample), sample.dtype)
    deriv = np.zeros(np.size(sample), sample.dtype)

    # sample_last = rtmemory.input[width - 1]
    sample_d = 0.0
    deriv_d = 0.0
    xval = rtmemory.output[0]
    dval = rtmemory_dval.output[0]

    for i in range(np.size(sample)):

        sample_d = sample[i]
        deriv_d = (sample_d - sample_last) / delta_time
        index_begin = i - width
        if (index_begin >= 0):
            xval = xval - (sample[index_begin]) * (sample[index_begin]) \
                + sample_d * sample_d
            dval = dval - deriv[index_begin] * deriv[index_begin] \
                + deriv_d * deriv_d
        else:
            index = i
            xval = xval - rtmemory.input[index] * rtmemory.input[index] \
                + sample_d * sample_d
            dval = dval \
                - rtmemory_dval.input[index] * rtmemory_dval.input[index] \
                + deriv_d * deriv_d
        deriv[i] = deriv_d
        sample_last = sample_d
        # if (xval > _MIN_FLOAT_VAL &  & dval > _MIN_FLOAT_VAL) {
        if (dval > _MIN_FLOAT_VAL):
            new_sample[i] = _TWO_PI * math.sqrt(xval / dval)
        else:
            new_sample[i] = 0.0

    # update memory
    rtmemory.output[0] = xval
    rtmemory.update_input(sample)
    rtmemory_dval.output[0] = dval
    rtmemory_dval.update_input(deriv)

    return new_sample

# memory object indices for storing specific values
_AMP_AT_PICK = 0
_HAVE_USED_MEMORY = 1
_FLAG_COMPETE_MWP = 2
_INT_INT_SUM = 3
_POLARITY = 4
_MEMORY_SIZE_OUTPUT = 5

def mwpintegral(trace, max_time, ref_time, mem_time=1.0, gain=1.0,
                rtmemory_list=None):
    """
    Calculate Mwp integral on a displacement trace.

    .. seealso:: [Tsuboi1999]_ and [Tsuboi1995]_

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace:  :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type max_time: float
    :param max_time: Maximum time in seconds after ref_time to apply Mwp
        integration.
    :type ref_time: :class:`~obspy.core.utcdatetime.UTCDateTime`
    :param ref_time: Reference date and time of the data sample
        (e.g. P pick time) at which to begin Mwp integration.
    :type mem_time: float, optional
    :param mem_time: Length in seconds of data memory (must be much larger
        than maximum delay between pick declaration and pick time). Defaults
        to ``1.0``.
    :type gain: float, optional
    :param gain: Nominal gain to convert input displacement trace to meters
        of ground displacement. Defaults to ``1.0``.
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace.
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object.
    """
    if not isinstance(trace, Trace):
        msg = "trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    if not isinstance(ref_time, UTCDateTime):
        msg = "ref_time must be an obspy.core.utcdatetime.UTCDateTime object."
        raise ValueError(msg)

    if not max_time >= 0:
        msg = "max_time parameter not specified or < 0."
        raise ValueError(msg)

    if not rtmemory_list:
        rtmemory_list = [RtMemory()]

    sample = trace.data
    delta_time = trace.stats.delta

    rtmemory = rtmemory_list[0]

    # initialize memory object
    if not rtmemory.initialized:
        memory_size_input = int(0.5 + mem_time * trace.stats.sampling_rate)
        memory_size_output = _MEMORY_SIZE_OUTPUT
        rtmemory.initialize(sample.dtype, memory_size_input,
                            memory_size_output, 0, 0)

    new_sample = np.zeros(np.size(sample), sample.dtype)

    ioffset_pick = int(round(
                       (ref_time - trace.stats.starttime) *
                       trace.stats.sampling_rate))
    ioffset_mwp_min = ioffset_pick

    # set reference amplitude
    if ioffset_mwp_min >= 0 and ioffset_mwp_min < trace.data.size:
        # value in trace data array
        rtmemory.output[_AMP_AT_PICK] = trace.data[ioffset_mwp_min]
    elif ioffset_mwp_min >= -(np.size(rtmemory.input)) and ioffset_mwp_min < 0:
        # value in memory array
        index = ioffset_mwp_min + np.size(rtmemory.input)
        rtmemory.output[_AMP_AT_PICK] = rtmemory.input[index]
    elif ioffset_mwp_min < -(np.size(rtmemory.input)) \
            and not rtmemory.output[_HAVE_USED_MEMORY]:
        msg = "mem_time not large enough to buffer required input data."
        raise ValueError(msg)
    if ioffset_mwp_min < 0 and rtmemory.output[_HAVE_USED_MEMORY]:
        ioffset_mwp_min = 0
    else:
        rtmemory.output[_HAVE_USED_MEMORY] = 1
    # set Mwp end index corresponding to maximum duration
    mwp_end_index = int(round(max_time / delta_time))
    ioffset_mwp_max = mwp_end_index + ioffset_pick
    if ioffset_mwp_max < trace.data.size:
        rtmemory.output[_FLAG_COMPETE_MWP] = 1  # will complete
    if ioffset_mwp_max > trace.data.size:
        ioffset_mwp_max = trace.data.size
    # apply double integration, check for extrema
    mwp_amp_at_pick = rtmemory.output[_AMP_AT_PICK]
    mwp_int_int_sum = rtmemory.output[_INT_INT_SUM]
    polarity = rtmemory.output[_POLARITY]
    amplitude = 0.0
    for n in range(ioffset_mwp_min, ioffset_mwp_max):
        if n >= 0:
            amplitude = trace.data[n]
        elif n >= -(np.size(rtmemory.input)):
            # value in memory array
            index = n + np.size(rtmemory.input)
            amplitude = rtmemory.input[index]
        else:
            msg = "Error: Mwp: attempt to access rtmemory.input array of " + \
                "size=%d at invalid index=%d: this should not happen!" % \
                (np.size(rtmemory.input), n + np.size(rtmemory.input))
            print(msg)
            continue  # should never reach here
        disp_amp = amplitude - mwp_amp_at_pick
        # check displacement polarity
        if disp_amp >= 0.0:  # pos
            # check if past extremum
            if polarity < 0:  # passed from neg to pos displacement
                mwp_int_int_sum *= -1.0
                mwp_int_int_sum = 0
            polarity = 1
        elif disp_amp < 0.0:  # neg
            # check if past extremum
            if polarity > 0:  # passed from pos to neg displacement
                mwp_int_int_sum = 0
            polarity = -1
        mwp_int_int_sum += (amplitude - mwp_amp_at_pick) * delta_time / gain
        new_sample[n] = mwp_int_int_sum

    rtmemory.output[_INT_INT_SUM] = mwp_int_int_sum
    rtmemory.output[_POLARITY] = polarity

    # update memory
    rtmemory.update_input(sample)

    return new_sample

MWP_INVALID = -9.9
# 4.213e19 - Tsuboi 1995, 1999
MWP_CONST = 4.0 * _PI  # 4 PI
MWP_CONST *= 3400.0  # rho
MWP_CONST *= 7900.0 * 7900.0 * 7900.0  # Pvel**3
MWP_CONST *= 2.0  # FP average radiation pattern
MWP_CONST *= (10000.0 / 90.0)  # distance deg -> km
MWP_CONST *= 1000.0  # distance km -> meters
# https://mail.python.org/pipermail/python-list/2010-February/567089.html, ff.
try:
    FLOAT_MIN = sys.float_info.min
except AttributeError:
    FLOAT_MIN = 1.1e-37

def calculate_mwp_mag(peak, epicentral_distance):
    """
    Calculate Mwp magnitude.

    .. seealso:: [Tsuboi1999]_ and [Tsuboi1995]_

    :type peak: float
    :param peak: Peak value of integral of displacement seismogram.
    :type epicentral_distance: float
    :param epicentral_distance: Great-circle epicentral distance from station
        in degrees.
    :rtype: float
    :returns: Calculated Mwp magnitude.
    """
    moment = MWP_CONST * peak * epicentral_distance
    mwp_mag = MWP_INVALID
    if moment > FLOAT_MIN:
        mwp_mag = (2.0 / 3.0) * (math.log10(moment) - 9.1)
    return mwp_mag

def kurtosis(trace, win=3.0, rtmemory_list=None):
    """
    Apply recursive kurtosis calculation on data.

    Recursive kurtosis is computed using the [ChassandeMottin2002]_
    formulation adjusted to give the kurtosis of a Gaussian distribution = 0.0.

    :type trace: :class:`~obspy.core.trace.Trace`
    :param trace: :class:`~obspy.core.trace.Trace` object to append to this
        RtTrace
    :type win: float, optional
    :param win: window length in seconds for the kurtosis (default is 3.0 s)
    :type rtmemory_list: list of :class:`~obspy.realtime.rtmemory.RtMemory`,
        optional
    :param rtmemory_list: Persistent memory used by this process for specified
        trace
    :rtype: NumPy :class:`numpy.ndarray`
    :return: Processed trace data from appended Trace object
    """
    if not isinstance(trace, Trace):
        msg = "Trace parameter must be an obspy.core.trace.Trace object."
        raise ValueError(msg)

    # if this is the first appended trace, the rtmemory_list will be None
    if not rtmemory_list:
        rtmemory_list = [RtMemory(), RtMemory(), RtMemory()]

    # deal with case of empty trace
    sample = trace.data
    if np.size(sample) < 1:
        return sample

    # get simple info from trace
    npts = len(sample)
    dt = trace.stats.delta

    # set some constants for the kurtosis calculation
    c_1 = dt / float(win)
    a1 = 1.0 - c_1
    c_2 = (1.0 - a1 * a1) / 2.0
    bias = -3 * c_1 - 3.0

    # prepare the output array
    kappa4 = np.empty(npts, sample.dtype)

    # initialize the real-time memory needed to store
    # the recursive kurtosis coefficients until the
    # next bloc of data is added
    rtmemory_mu1 = rtmemory_list[0]
    rtmemory_mu2 = rtmemory_list[1]
    rtmemory_k4_bar = rtmemory_list[2]

    # there are three memory objects, one for each "last" coefficient
    # that needs carrying over
    # initialize mu1_last to 0
    if not rtmemory_mu1.initialized:
        memory_size_input = 1
        memory_size_output = 0
        rtmemory_mu1.initialize(sample.dtype, memory_size_input,
                                memory_size_output, 0, 0)

    # initialize mu2_last (sigma) to 1
    if not rtmemory_mu2.initialized:
        memory_size_input = 1
        memory_size_output = 0
        rtmemory_mu2.initialize(sample.dtype, memory_size_input,
                                memory_size_output, 1, 0)

    # initialize k4_bar_last to 0
    if not rtmemory_k4_bar.initialized:
        memory_size_input = 1
        memory_size_output = 0
        rtmemory_k4_bar.initialize(sample.dtype, memory_size_input,
                                   memory_size_output, 0, 0)

    mu1_last = rtmemory_mu1.input[0]
    mu2_last = rtmemory_mu2.input[0]
    k4_bar_last = rtmemory_k4_bar.input[0]

    # do recursive kurtosis
    for i in range(npts):
        mu1 = a1 * mu1_last + c_1 * sample[i]
        dx2 = (sample[i] - mu1_last) * (sample[i] - mu1_last)
        mu2 = a1 * mu2_last + c_2 * dx2
        dx2 = dx2 / mu2_last
        k4_bar = (1 + c_1 - 2 * c_1 * dx2) * k4_bar_last + c_1 * dx2 * dx2
        kappa4[i] = k4_bar + bias
        mu1_last = mu1
        mu2_last = mu2
        k4_bar_last = k4_bar

    rtmemory_mu1.input[0] = mu1_last
    rtmemory_mu2.input[0] = mu2_last
    rtmemory_k4_bar.input[0] = k4_bar_last

    return kappa4


"""
The default unit symbol lookup table.

"""

#-----------------------------------------------------------------------------
# Copyright (c) 2013, yt Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

from yt.units import dimensions
from yt.utilities.physical_ratios import \
    cm_per_pc, cm_per_ly, cm_per_au, cm_per_rsun, cm_per_m, \
    mass_sun_grams, sec_per_year, sec_per_day, sec_per_hr, \
    sec_per_min, temp_sun_kelvin, luminosity_sun_ergs_per_sec, \
    metallicity_sun, erg_per_eV, amu_grams, mass_electron_grams, \
    cm_per_ang, jansky_cgs, mass_jupiter_grams, mass_earth_grams, \
    kelvin_per_rankine, speed_of_light_cm_per_s, planck_length_cm, \
    planck_charge_esu, planck_energy_erg, planck_mass_grams, \
    planck_temperature_K, planck_time_s, mass_hydrogen_grams, \
    grams_per_pound, standard_gravity_cm_per_s2, pascal_per_atm, \
    newton_cgs, cm_per_rearth, cm_per_rjup
import numpy as np

# Lookup a unit symbol with the symbol string, and provide a tuple with the
# conversion factor to cgs and dimensionality.

default_unit_symbol_lut = {
    # base
    "g":  (1.0, dimensions.mass, 0.0, r"\rm{g}"),
    "s":  (1.0, dimensions.time, 0.0, r"\rm{s}"),
    "K":  (1.0, dimensions.temperature, 0.0, r"\rm{K}"),
    "radian": (1.0, dimensions.angle, 0.0, r"\rm{radian}"),

    # other cgs
    "dyne": (1.0, dimensions.force, 0.0, r"\rm{dyn}"),
    "erg":  (1.0, dimensions.energy, 0.0, r"\rm{erg}"),
    "esu":  (1.0, dimensions.charge_cgs, 0.0, r"\rm{esu}"),
    "gauss": (1.0, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}"),
    "degC": (1.0, dimensions.temperature, -273.15, r"^\circ\rm{C}"),
    "statA": (1.0, dimensions.current_cgs, 0.0, r"\rm{statA}"),
    "statV": (1.0, dimensions.electric_potential_cgs, 0.0, r"\rm{statV}"),
    "statohm": (1.0, dimensions.resistance_cgs, 0.0, r"\rm{statohm}"),

    # some SI
    "m": (1.0e2, dimensions.length, 0.0, r"\rm{m}"),
    "J": (1.0e7, dimensions.energy, 0.0, r"\rm{J}"),
    "W": (1.0e7, dimensions.power, 0.0, r"\rm{W}"),
    "Hz": (1.0, dimensions.rate, 0.0, r"\rm{Hz}"),
    "N": (1.0e5, dimensions.force, 0.0, r"\rm{N}"),
    "C": (1.0, dimensions.charge_mks, 0.0, r"\rm{C}"),
    "A": (1.0, dimensions.current_mks, 0.0, r"\rm{A}"),
    "T": (1000.0, dimensions.magnetic_field_mks, 0.0, r"\rm{T}"),
    "Pa": (10.0, dimensions.pressure, 0.0, r"\rm{Pa}"),
    "V": (1.0e7, dimensions.electric_potential_mks, 0.0, r"\rm{V}"),
    "ohm": (1.0e7, dimensions.resistance_mks, 0.0, r"\Omega"),

    # Imperial and other non-metric units
    "ft": (30.48, dimensions.length, 0.0, r"\rm{ft}"),
    "mile": (160934, dimensions.length, 0.0, r"\rm{mile}"),
    "degF": (kelvin_per_rankine, dimensions.temperature, -459.67,
             "^\circ\rm{F}"),
    "R": (kelvin_per_rankine, dimensions.temperature, 0.0, r"^\circ\rm{R}"),
    "lbf": (grams_per_pound*standard_gravity_cm_per_s2, dimensions.force, 0.0, r"\rm{lbf}"),
    "lbm": (grams_per_pound, dimensions.mass, 0.0, r"\rm{lbm}"),
    "atm": (pascal_per_atm*10., dimensions.pressure, 0.0, r"\rm{atm}"),

    # dimensionless stuff
    "h": (1.0, dimensions.dimensionless, 0.0, r"h"),  # needs to be added for rho_crit_now
    "dimensionless": (1.0, dimensions.dimensionless, 0.0, r""),

    # times
    "min": (sec_per_min, dimensions.time, 0.0, r"\rm{min}"),
    "hr":  (sec_per_hr, dimensions.time, 0.0, r"\rm{hr}"),
    "day": (sec_per_day, dimensions.time, 0.0, r"\rm{d}"),
    "d":   (sec_per_day, dimensions.time, 0.0, r"\rm{d}"),
    "yr":  (sec_per_year, dimensions.time, 0.0, r"\rm{yr}"),

    # Velocities
    "c": (speed_of_light_cm_per_s, dimensions.velocity, 0.0, r"\rm{c}"),

    # Solar units
    "Msun": (mass_sun_grams, dimensions.mass, 0.0, r"M_\odot"),
    "msun": (mass_sun_grams, dimensions.mass, 0.0, r"M_\odot"),
    "Rsun": (cm_per_rsun, dimensions.length, 0.0, r"R_\odot"),
    "rsun": (cm_per_rsun, dimensions.length, 0.0, r"R_\odot"),
    "R_sun": (cm_per_rsun, dimensions.length, 0.0, r"R_\odot"),
    "r_sun": (cm_per_rsun, dimensions.length, 0.0, r"R_\odot"),
    "Lsun": (luminosity_sun_ergs_per_sec, dimensions.power, 0.0, r"L_\odot"),
    "Tsun": (temp_sun_kelvin, dimensions.temperature, 0.0, r"T_\odot"),
    "Zsun": (metallicity_sun, dimensions.dimensionless, 0.0, r"Z_\odot"),
    "Mjup": (mass_jupiter_grams, dimensions.mass, 0.0, r"M_{\rm{Jup}}"),
    "Mearth": (mass_earth_grams, dimensions.mass, 0.0, r"M_\oplus"),

    # astro distances
    "AU": (cm_per_au, dimensions.length, 0.0, r"\rm{AU}"),
    "au": (cm_per_au, dimensions.length, 0.0, r"\rm{AU}"),
    "ly": (cm_per_ly, dimensions.length, 0.0, r"\rm{ly}"),
    "pc": (cm_per_pc, dimensions.length, 0.0, r"\rm{pc}"),

    # angles
    "degree": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}"),  # degrees
    "arcmin": (np.pi/10800., dimensions.angle, 0.0,
               r"\rm{arcmin}"),  # arcminutes
    "arcsec": (np.pi/648000., dimensions.angle, 0.0,
               r"\rm{arcsec}"),  # arcseconds
    "mas": (np.pi/648000000., dimensions.angle, 0.0,
            r"\rm{mas}"),  # milliarcseconds
    "hourangle": (np.pi/12., dimensions.angle, 0.0, r"\rm{HA}"),  # hour angle
    "steradian": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}"),
    "lat": (-np.pi/180.0, dimensions.angle, 90.0, r"\rm{Latitude}"),
    "lon": (np.pi/180.0, dimensions.angle, -180.0, r"\rm{Longitude}"),

    # misc
    "eV": (erg_per_eV, dimensions.energy, 0.0, r"\rm{eV}"),
    "amu": (amu_grams, dimensions.mass, 0.0, r"\rm{amu}"),
    "angstrom": (cm_per_ang, dimensions.length, 0.0, r"\AA"),
    "Jy": (jansky_cgs, dimensions.specific_flux, 0.0, r"\rm{Jy}"),
    "counts": (1.0, dimensions.dimensionless, 0.0, r"\rm{counts}"),
    "photons": (1.0, dimensions.dimensionless, 0.0, r"\rm{photons}"),
    "me": (mass_electron_grams, dimensions.mass, 0.0, r"m_e"),
    "mp": (mass_hydrogen_grams, dimensions.mass, 0.0, r"m_p"),
    "mol": (1.0/amu_grams, dimensions.dimensionless, 0.0, r"\rm{mol}"),
    'Sv': (cm_per_m**2, dimensions.specific_energy, 0.0, r"\rm{Sv}"),
    "rayleigh": (0.25e6/np.pi, dimensions.count_intensity, 0.0, r"\rm{R}"),

    # for AstroPy compatibility
    "solMass": (mass_sun_grams, dimensions.mass, 0.0, r"M_\odot"),
    "solRad": (cm_per_rsun, dimensions.length, 0.0, r"R_\odot"),
    "solLum": (luminosity_sun_ergs_per_sec, dimensions.power, 0.0, r"L_\odot"),
    "dyn": (1.0, dimensions.force, 0.0, r"\rm{dyn}"),
    "sr": (1.0, dimensions.solid_angle, 0.0, r"\rm{sr}"),
    "rad": (1.0, dimensions.angle, 0.0, r"\rm{rad}"),
    "deg": (np.pi/180., dimensions.angle, 0.0, r"\rm{deg}"),
    "Fr":  (1.0, dimensions.charge_cgs, 0.0, r"\rm{Fr}"),
    "G": (1.0, dimensions.magnetic_field_cgs, 0.0, r"\rm{G}"),
    "Angstrom": (cm_per_ang, dimensions.length, 0.0, r"\AA"),
    "statC": (1.0, dimensions.charge_cgs, 0.0, r"\rm{statC}"),

    # Planck units
    "m_pl": (planck_mass_grams, dimensions.mass, 0.0, r"m_{\rm{P}}"),
    "l_pl": (planck_length_cm, dimensions.length, 0.0, r"\ell_\rm{P}"),
    "t_pl": (planck_time_s, dimensions.time, 0.0, r"t_{\rm{P}}"),
    "T_pl": (planck_temperature_K, dimensions.temperature, 0.0, r"T_{\rm{P}}"),
    "q_pl": (planck_charge_esu, dimensions.charge_cgs, 0.0, r"q_{\rm{P}}"),
    "E_pl": (planck_energy_erg, dimensions.energy, 0.0, r"E_{\rm{P}}"),

    # Geometrized units
    "m_geom": (mass_sun_grams, dimensions.mass, 0.0, r"M_\odot"),
    "l_geom": (newton_cgs*mass_sun_grams/speed_of_light_cm_per_s**2, dimensions.length, 0.0, r"M_\odot"),
    "t_geom": (newton_cgs*mass_sun_grams/speed_of_light_cm_per_s**3, dimensions.time, 0.0, r"M_\odot"),

    # Some Solar System units
    "R_earth": (cm_per_rearth, dimensions.length, 0.0, r"R_\oplus"),
    "r_earth": (cm_per_rearth, dimensions.length, 0.0, r"R_\oplus"),
    "R_jup": (cm_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}"),
    "r_jup": (cm_per_rjup, dimensions.length, 0.0, r"R_\mathrm{Jup}"),
}

# This dictionary formatting from magnitude package, credit to Juan Reyero.
unit_prefixes = {
    'Y': 1e24,   # yotta
    'Z': 1e21,   # zetta
    'E': 1e18,   # exa
    'P': 1e15,   # peta
    'T': 1e12,   # tera
    'G': 1e9,    # giga
    'M': 1e6,    # mega
    'k': 1e3,    # kilo
    'd': 1e1,    # deci
    'c': 1e-2,   # centi
    'm': 1e-3,   # mili
    'u': 1e-6,   # micro
    'n': 1e-9,   # nano
    'p': 1e-12,  # pico
    'f': 1e-15,  # femto
    'a': 1e-18,  # atto
    'z': 1e-21,  # zepto
    'y': 1e-24,  # yocto
}

latex_prefixes = {
    "u": r"\mu",
    }

prefixable_units = [
    "m",
    "pc",
    "mcm",
    "pccm",
    "g",
    "eV",
    "s",
    "yr",
    "K",
    "dyne",
    "erg",
    "esu",
    "J",
    "Hz",
    "W",
    "gauss",
    "G",
    "Jy",
    "N",
    "T",
    "A",
    "C",
    "statA",
    "Pa",
    "V",
    "statV",
    "ohm",
    "statohm",
    "Sv",
]

default_base_units = {
    dimensions.mass: 'g',
    dimensions.length: 'cm',
    dimensions.time: 's',
    dimensions.temperature: 'K',
    dimensions.angle: 'radian',
    dimensions.current_mks: 'A',
}