def system(pdf, variable_time, variables_idt, epsilon, dt, logbase="log2"):
    """
    Input:
        pdf                     joint pdf class
        variable_time           variable identifying the time series (time 0 must be included). 
                                Must be ordered, since the first index is used to calculate the entropy used to intersect the entropy curve.
        variables_idt           variables to calculate their idt. Could fit multiple labels (e.g. "var" fits "var_1", "var_2", "var_3",...)
        epsilon                 epsilon parameter
        dt                      Number of timesteps between time series
        logbase                 Base for the logarithm ("log2", "log", "log10")

        returns                 idt calculated on the variables_idt.
    """
    assert np.isscalar(variable_time), "Only one time variable can be specified"

    labels_idt = pdf.get_labels(variables_idt)
    num_time_series = pdf.get_num_bins_of(variable_time)
    num_vars = len(labels_idt)

    """Calculate entropies"""
    h_t = np.ndarray((num_time_series, num_vars))
    for t in xrange(num_time_series):
        """Obtain entropy at time t"""
        """Filtering time"""
        joint_time_t = pdf.filter_joint_probabilities([variable_time], [t])
        joint_time_t.normalize()
        h_t[t, :] = shannon.calculate(joint_time_t, variables_idt)

    """IDT calculation"""
    IDT_var = np.ndarray((num_vars))
    for i, l in enumerate(labels_idt):
        """Set target entropy"""
        h_target = h_t[0, i]
        if h_target - epsilon > 0:
            h_target = h_target - epsilon

        """Search intersection with entropy time curve"""
        found = False
        for t in xrange(1, num_time_series):
            if h_target - h_t[t, i] < 0:
                t1 = t - 1
                t2 = t
                found = True
                break

        """Interpolate value"""
        if found:
            h1 = h_t[t1, i]
            h2 = h_t[t2, i]
            x1 = t1 + 1
            x2 = t2 + 1
            if h2 - h1 == 0:
                IDT_var[i] = 0
            else:
                IDT_var[i] = (x1 + (x2-x1)*(h_target - h1) /(h2-h1)) * dt
        else:
            IDT_var[i] = num_time_series * dt

    return IDT_var
def entropies(pdf, variable, conditional_variable, logbase="log2"):
    """
    Input:
        pdf                     joint pdf class
        variable                the variable to calculate the entropy. Could fit multiple labels (e.g. "var" fits "var_1", "var_2", "var_3",...)
        conditional_variable    conditional variable
        logbase                 Base for the logarithm ("log2", "log", "log10")

        returns                 mutual information based on conditional entropy
    """

    return shannon.calculate(pdf, variable, logbase) - shannon.conditional(pdf, variable, conditional_variable, logbase)
def entropies(pdf, cond_pdf, logbase="log2"):
    '''
    Mutual information metric based on entropies.

    Input:
        pdf_a       probability A variable NxB
                        N = elements
                        B = bins
        cond_pdf    conditional probability NxBAxBB
                        N = elements
                        BA = bins A
                        BB = bins B
        logbase        Base for the logarithm ("log2", "log", "log10")
    Returns:
                    mutual information N
                        N = elements
    '''

    return shannon.calculate(pdf, logbase) - shannon.conditional(pdf, cond_pdf, logbase)
def calculateMetric(metric_name, param_vals):
	if metric_name == 'count':
		if len(param_vals)!= 1:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be count(data)'
			raise Exception()
		return red.count(*param_vals)
	elif metric_name == 'pdf':
		if len(param_vals)!= 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be pdf(data, bin_values, continuous_bins)'
			raise Exception()
		return PDF.single(*param_vals)
	elif metric_name == 'deft':
		if len(param_vals) < 2 or len(param_vals) > 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be deft(data, g, alpha)'
			raise Exception()
		return deft.deft(*param_vals)
	elif metric_name == 'pdf_joint':
		if len(param_vals)!= 6:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be pdf_joint(dataA, bin_valuesA, continuous_binsA, dataB, bin_valuesB, continuous_binsB)'
			raise Exception()
		return PDF.joint(*param_vals)
	elif metric_name == 'mutual_information':
		if len(param_vals) < 3 or len(param_vals) > 4:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be mutual_information(pdfA, pdfB, joint_pdf, logbase="log2")'
			raise Exception()
		return MI.calculate(*param_vals)
	elif metric_name == 'shannon':
		if len(param_vals) < 1 or len(param_vals) > 2:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be shannon(pdf, logbase="log2")'
			raise Exception()
		return shannon.calculate(*param_vals)
	elif metric_name == 'kullback-leibler':
		if len(param_vals) < 2 or len(param_vals) > 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be kullback-leibler(pdf_p, pdf_q, logbase="log2")'
			raise Exception()
		return kullback.calculate(*param_vals)
	elif metric_name == 'fisher':
		if len(param_vals) < 2 or len(param_vals) > 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be fisher(pdf, eps, logbase="log2")'
			raise Exception()
		return fis.calculate(*param_vals)
	elif metric_name == 'hellinger-distance':
		if len(param_vals) != 2:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be hellinger-distance(pdf_p, pdf_q)'
			raise Exception()
		return hellinger.calculate(*param_vals)
	elif metric_name == 'surprise':
		if len(param_vals)!= 1:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be surprise(prob)'
			raise Exception()
		return surprise.calculate(*param_vals)
	elif metric_name == 'idt':
		if len(param_vals) < 6 or len(param_vals) > 7:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be idt(initial, time_series, epsilon, dt, bin_values, continuous_bins, logbase="log2")'
			raise Exception()
		return IDT.system(*param_vals)
	elif metric_name == 'idt_individual':
		if len(param_vals) < 8 or len(param_vals) > 9:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be idt_individual(initial, time_series, dt, bin_values, continuous_bins, sample_state_0, sample_state_t, sample_time, logbase="log2")'
			raise Exception()
		return IDT.individual(*param_vals)
	elif metric_name == 'information_integration':
		if len(param_vals) < 9 or len(param_vals) > 10:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be information_integration(initial, group, dt, bin_values, continuous_bins, sample_N1, sample_N2, sample_G, sample_t, logbase="log2")'
			raise Exception()
		return II.calculate(*param_vals)
	elif metric_name == 'multi_information':
		if len(param_vals) < 6 or len(param_vals) > 7:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be multi_information(data, bin_values, continuous_bins, sample_var, sample_elems, sample_pop, logbase="log2")'
			raise Exception()
		return multi.calculate(*param_vals)
	elif metric_name == 'swap_axes':
		if len(param_vals)!= 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be swap_axes(data, axis0, axis1)'
			raise Exception()
		return np.swapaxes(*param_vals)
	elif metric_name == 'add_dimension':
		if len(param_vals)!= 2:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be add_dimension(data, dimNumber)'
			raise Exception()
		return  np.expand_dims(*param_vals)
	elif metric_name == 'join_dimensions':
		if len(param_vals)!= 3:
			print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be join_dimensions(data, dimNumberA, dimNumberB)'
			raise Exception()
		return  red.join(*param_vals)
	else :
		# Try to get a numpy function
		try :
			func = getattr(np, metric_name)
			return func(*param_vals)
		except:
			print 'ERROR:Metric ', metric_name, ' does not exist'
			raise Exception()
def calculateMetric(metric_name, param_vals):
    '''
    Calculates a metric.

    Input:
        metric_name     metric name
        param_vals      metric parameters
    Returns:
                        result of the metric
    '''
    if metric_name == 'count':
        if len(param_vals)!= 1:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be count(data)'
            raise Exception()
        return red.count(*param_vals)
    elif metric_name == 'pdf':
        if len(param_vals)!= 3:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be pdf(data, bin_values, continuous_bins)'
            raise Exception()
        return PDF.single(*param_vals)
    elif metric_name == 'deft':
        if len(param_vals) < 4 or len(param_vals) > 5:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be deft(data, g, minLimit, maxLimit, alpha=2)'
            raise Exception()
        return deft.deft(*param_vals)
    elif metric_name == 'deft_joint':
        if len(param_vals) < 7 or len(param_vals) > 8:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be deft_joint(dataA, dataB, g, minLimitA, maxLimitA, minLimitB, maxLimitB, alpha=2)'
            raise Exception()
        return deft.deft(*param_vals)
    elif metric_name == 'pdf_joint':
        if len(param_vals)!= 6:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be pdf_joint(dataA, bin_valuesA, continuous_binsA, dataB, bin_valuesB, continuous_binsB)'
            raise Exception()
        return PDF.joint(*param_vals)
    elif metric_name == 'mutual_information':
        if len(param_vals) < 3 or len(param_vals) > 4:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be mutual_information(pdfA, pdfB, joint_pdf, logbase="log2")'
            raise Exception()
        return MI.calculate(*param_vals)
    elif metric_name == 'shannon':
        if len(param_vals) < 1 or len(param_vals) > 2:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be shannon(pdf, logbase="log2")'
            raise Exception()
        return shannon.calculate(*param_vals)
    elif metric_name == 'kullback-leibler':
        if len(param_vals) < 2 or len(param_vals) > 3:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be kullback-leibler(pdf_p, pdf_q, logbase="log2")'
            raise Exception()
        return kullback.calculate(*param_vals)
    elif metric_name == 'fisher':
        if len(param_vals) < 2 or len(param_vals) > 3:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be fisher(pdf, eps, logbase="log2")'
            raise Exception()
        return fis.calculate(*param_vals)
    elif metric_name == 'hellinger-distance':
        if len(param_vals) != 2:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be hellinger-distance(pdf_p, pdf_q)'
            raise Exception()
        return hellinger.calculate(*param_vals)
    elif metric_name == 'surprise':
        if len(param_vals)!= 1:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be surprise(prob)'
            raise Exception()
        return surprise.calculate(*param_vals)
    elif metric_name == 'idt':
        if len(param_vals) < 6 or len(param_vals) > 7:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be idt(initial, time_series, epsilon, dt, bin_values, continuous_bins, logbase="log2")'
            raise Exception()
        return IDT.system(*param_vals)
    elif metric_name == 'idt_individual':
        if len(param_vals) < 8 or len(param_vals) > 9:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be idt_individual(initial, time_series, dt, bin_values, continuous_bins, sample_state_0, sample_state_t, sample_time, logbase="log2")'
            raise Exception()
        return IDT.individual(*param_vals)
    elif metric_name == 'information_integration':
        if len(param_vals) < 9 or len(param_vals) > 10:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be information_integration(initial, group, dt, bin_values, continuous_bins, sample_N1, sample_N2, sample_G, sample_t, logbase="log2")'
            raise Exception()
        return II.calculate(*param_vals)
    elif metric_name == 'multi_information':
        if len(param_vals) < 6 or len(param_vals) > 7:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be multi_information(data, bin_values, continuous_bins, sample_var, sample_elems, sample_pop, logbase="log2")'
            raise Exception()
        return multi.calculate(*param_vals)
    elif metric_name == 'early_warning_difference':
        if len(param_vals) < 4 or len(param_vals) > 5:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be early_warning_difference(time_series_ref, time_series_comp, change_values, warning_values, histogram_limit=50)'
            raise Exception()
        return ew.early_warning_difference(*param_vals)
    elif metric_name == 'early_warning_flips':
        if len(param_vals) != 2:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be early_warning_flips(time_series, change_values)'
            raise Exception()
        return ew.early_warning_flips(*param_vals)
    elif metric_name == 'add_dimension':
        if len(param_vals)!= 2:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be add_dimension(data, dimNumber)'
            raise Exception()
        return  np.expand_dims(*param_vals)
    elif metric_name == 'join_dimensions':
        if len(param_vals)!= 3:
            print 'ERROR:Error in ', metric_name, ', number of parameters incorrect. It must be join_dimensions(data, dimNumberA, dimNumberB)'
            raise Exception()
        return  red.join(*param_vals)
    else :
        # Try to get a numpy function
        try :
            func = getattr(np, metric_name)
            return func(*param_vals)
        except:
            print 'ERROR:Metric ', metric_name, ' does not exist'
            raise Exception()
def system(initial, times, epsilon, dt, bin_values, continuous_bins, logbase="log2"):
    """
    IDT system metric

    Input:
        initial         Initial data NxP
                            N = elements
                            P = population
        times           Time state data TxNxP
                            T = time series 
                            N = elements
                            P = population
        epsilon         epsilon parameter
        dt              Number of timesteps between time series
        bin_values      values of the bins
        continuous_bins true if the values of the bins are continuous
        logbase         Base for the logarithm ("log2", "log", "log10")
    Returns:
                        IDT N
                            N = elements
    """
    assert logbase in ["log2", "log", "log10"], 'Logbase parameter must be one of ("log2", "log", "log10")'

    numTime = len(times)
    numElems = len(times[0])

    # Initial entropy
    h_initial = shannon.calculate(PDF.single(initial, bin_values, continuous_bins), logbase)

    # Temporal entropy
    h_t = np.ndarray((numTime, numElems), dtype="float")
    for t in xrange(numTime):
        h_t[t] = shannon.calculate(PDF.single(times[t], bin_values, continuous_bins), logbase)

    IDT_var = np.ndarray(numElems, dtype="float")
    for i in xrange(numElems):
        h_target = h_initial[i]

        if h_target - epsilon > 0:
            h_target = h_target - epsilon

        found = False
        for t in xrange(numTime):
            if h_target - h_t[t, i] < 0:
                t1 = t - 1
                t2 = t
                found = True
                break

        if found:
            if t1 < 0:
                h1 = 0
            else:
                h1 = h_t[t1, i]
            h2 = h_t[t2, i]
            x1 = t1 + 1
            x2 = t2 + 1
            if h2 - h1 == 0:
                IDT_var[i] = 0
            else:
                IDT_var[i] = (x1 + (x2 - x1) * (h_target - h1) / (h2 - h1)) * dt
        else:
            IDT_var[i] = numTime * dt

    return IDT_var
def individual(initial, times, dt, bin_values, continuous_bins, sample_N1, sample_N2, sample_t, logbase="log2"):
    """
    IDT individual metric

    Input:
        initial         Initial data NxP
                            N = elements
                            P = population
        times           Time state data TxNxP
                            T = time series 
                            N = elements
                            P = population
        dt              Number of timesteps between time series
        bin_values      values of the bins
        continuous_bins true if the values of the bins are continuous
        sample_N1       percentage of elements to choose as a sample for state 0
        sample_N2       percentage of elements to choose as a sample for state t
        sample_time     percentage of elements to choose as a sample for time series
        logbase         Base for the logarithm ("log2", "log", "log10")
    Returns:
                        IDT N
                            N = elements
    """
    assert logbase in ["log2", "log", "log10"], 'Logbase parameter must be one of ("log2", "log", "log10")'
    assert 0 < sample_N1 <= 1, "Sample for N1 must be within (0, 1]"
    assert 0 < sample_N2 <= 1, "Sample for N2 must be within (0, 1]"
    assert 0 < sample_time <= 1, "Sample for time must be within (0, 1]"

    number_of_bins = len(bin_values)
    if continuous_bins:
        number_of_bins = number_of_bins - 1
    # Sampling input data
    sample_elements_1 = np.arange(len(initial))
    sample_elements_2 = np.arange(len(initial))
    sample_time = np.arange(len(times))
    np.random.shuffle(sample_elements_1)
    np.random.shuffle(sample_elements_2)
    np.random.shuffle(sample_time)
    sample_elements_1 = sample_elements_1[: len(initial) * sample_N1]
    sample_elements_2 = sample_elements_2[: len(initial) * sample_N2]
    sample_time = sample_time[: len(times) * sample_t]
    sample_time = np.sort(sample_time)
    times_sampled = times[sample_time]
    initial_sampled = initial[sample_elements_1]
    initial_sampled_2 = initial[sample_elements_2]
    initial_sampled_len = len(initial_sampled)
    initial_sampled_len_2 = len(initial_sampled_2)
    times_sampled_len = len(times_sampled)

    # Maximum value for IDT when there is no enough decay
    IDT_max = len(times) * dt

    # Initial marginals pdf
    pdf_initial = PDF.single(initial_sampled, bin_values, continuous_bins)
    pdf_initial_2 = PDF.single(initial_sampled_2, bin_values, continuous_bins)
    # Initial entropy
    h_initial = shannon.calculate(pdf_initial, logbase)
    # Temporal marginals pdf
    pdf_t = np.ndarray((len(sample_time), len(sample_elements_2), number_of_bins), dtype="float")
    for t in xrange(len(sample_time)):
        pdf_t[t] = PDF.single(times_sampled[t][sample_elements_2, ...], bin_values, continuous_bins)

    # Calculate IDT for each element (sample)
    IDT_var = np.ndarray(initial_sampled_len, dtype="float")
    init = time.clock()
    for i in xrange(initial_sampled_len):
        # Target decay limit
        h_target = h_initial[i] / 2

        # Maximum mutual information
        max_I = np.ndarray(times_sampled_len + 1, dtype="float")
        initial_sampled_i_len = len(initial_sampled[i])

        found = False
        # Initial mutual information
        mi_init = np.ndarray((len(times_sampled[t][sample_elements_2])), dtype="float")
        for j in xrange(len(times_sampled[t][sample_elements_2])):
            initial_sampled_i_len_2 = len(initial_sampled_2[j])
            # Calculate joint pdf from initial state
            pdf_joint = PDF.joint(
                initial_sampled[i].reshape(1, initial_sampled_i_len),
                bin_values,
                continuous_bins,
                initial_sampled_2[j].reshape(1, initial_sampled_i_len_2),
                bin_values,
                continuous_bins,
            )
            # Mutual information
            mi_init[j] = MI.calculate(
                pdf_initial[i].reshape(1, number_of_bins),
                pdf_initial_2[j].reshape(1, number_of_bins),
                pdf_joint,
                logbase,
            )
        max_I[0] = np.amax(mi_init)

        # Time series mutual information
        for t in xrange(times_sampled_len):
            mi = np.ndarray((len(times_sampled[t][sample_elements_2])), dtype="float")
            # Mutual information in time t
            for j in xrange(len(times_sampled[t][sample_elements_2])):
                # Calculate joint pdf from initial state and time t
                pdf_joint = PDF.joint(
                    initial_sampled[i].reshape(1, initial_sampled_i_len),
                    bin_values,
                    continuous_bins,
                    times_sampled[t][sample_elements_2][j].reshape(1, len(times_sampled[t][sample_elements_2][j])),
                    bin_values,
                    continuous_bins,
                )
                # Mutual information
                mi[j] = MI.calculate(
                    pdf_initial[i].reshape(1, number_of_bins),
                    pdf_t[t, j, :].reshape(1, number_of_bins),
                    pdf_joint,
                    logbase,
                )
            max_I[t + 1] = np.amax(mi)

        # Find t crossing target decay
        for t in xrange(times_sampled_len):
            # Interpolate when found
            if max_I[t + 1] - h_target < 0:
                t1 = t
                t2 = t + 1
                found = True
                h1 = max_I[t1]
                h2 = max_I[t2]

                if h2 - h1 == 0:
                    IDT_var[i] = 0
                else:
                    IDT_var[i] = (t1 + (t2 - t1) * (h_target - h1) / (h2 - h1)) * dt
                break
        # Setting maximum IDT value when not found
        if not found:
            IDT_var[i] = IDT_max

    return IDT_var
def individual(pdf, variable_time, variables_idt, dt, sample_N=1, sample_T=1, logbase="log2"):
    """
    Input:
        pdf                     joint pdf class
        variable_time           variable identifying the time series (time 0 must be included). 
                                Must be ordered, since the first index is used to calculate the entropy used to intersect the entropy curve.
        variables_idt           variables to calculate their idt. Could fit multiple labels (e.g. "var" fits "var_1", "var_2", "var_3",...)
        dt                      Number of timesteps between time series
        sample_N                sample percentage for the idt variables
        sample_T                sample percentage for the time variable
        logbase                 Base for the logarithm ("log2", "log", "log10")

        returns                 idt calculated on the variables_idt.
    """
    assert np.isscalar(variable_time), "Only one time variable can be specified"

    """Sample variables"""
    labels_idt = pdf.get_labels(variables_idt)
    sampled_pdf = pdf.sample_variables(labels_idt, sample_N)
    sampled_labels_idt = sampled_pdf.get_labels(variables_idt)

 #   labels_initial_idt = pdf_initial.get_labels(variables_idt)
 #   sampled_initial_pdf = pdf_initial.sample_variables(labels_initial_idt, sample_N)

    """Before sampling time, calculate entropy at time 0"""
    joint_time_0 = sampled_pdf.filter_joint_probabilities([variable_time], [0])
    joint_time_0.normalize()
    h_initial = shannon.calculate(joint_time_0, sampled_labels_idt)

    """Sample time"""
    sampled_pdf = sampled_pdf.sample_values([variable_time], [sample_T], True)
    num_time_series = sampled_pdf.get_num_bins_of(variable_time)

    """Maximum value for IDT when there is no enough decay"""
    IDT_max = num_time_series * dt

    num_variables = len(sampled_labels_idt)
    IDT_var = np.ndarray((num_variables))
    IDT_var[:] = IDT_max
    """Calculate IDT for each element"""
    for i, l_i in enumerate(sampled_labels_idt):
        """Target decay limit"""
        h_target = h_initial[i]/2

        """Maximum mutual information variable"""
        max_I = np.ndarray((num_time_series))

        """Time series mutual information"""
        for t in xrange(num_time_series - 1):
            """Filter combination with time t"""
            pdf_t = sampled_pdf.filter_joint_probabilities([variable_time], [[t+1]])
            pdf_t.normalize()
            mi = np.ndarray((num_variables))
            """Mutual information in time t"""
            for j, l_j in enumerate(sampled_labels_idt):
                """Joint pdf with var i from initial state and var j from time t"""
                """Mutual information"""
                if t == 0 and i == 0 and j == 1:
                    print joint_time_0.shrink_dimensions_to([l_i]).joint_probabilities, pdf_t.shrink_dimensions_to([l_j]).joint_probabilities
                mi[j] = MI.calculate(pdf_t, l_i, l_j, logbase)
            max_I[t] = np.amax(mi)
        print max_I[1]
        """Find t crossing target decay"""
        for t in xrange(num_time_series - 1):
            """Interpolate when found"""
            if max_I[t + 1] - h_target < 0:
                t1 = t
                t2 = t + 1
                found = True
                h1 = max_I[t1]
                h2 = max_I[t2]

                if h2 - h1 == 0:
                    IDT_var[i] = 0
                else:
                    IDT_var[i] = (t1 + (t2-t1)*(h_target - h1) /(h2-h1)) * dt
                break
    
    return IDT_var