def calc_fall_flush_timings_durations(flow_matrix, summer_timings):
    max_zero_allowed_per_year = fall_params['max_zero_allowed_per_year']
    max_nan_allowed_per_year = fall_params['max_nan_allowed_per_year']
    min_flow_rate = fall_params['min_flow_rate']
    sigma = fall_params['sigma']
    wet_sigma = fall_params['wet_sigma']
    peak_sensitivity = fall_params['peak_sensitivity']
    max_flush_duration = fall_params['max_flush_duration']
    wet_threshold_perc = fall_params['wet_threshold_perc']
    flush_threshold_perc = fall_params['flush_threshold_perc']
    min_flush_threshold = fall_params['min_flush_threshold']
    date_cutoff = fall_params['date_cutoff']
    start_dates = []
    wet_dates = []
    durations = []
    mags = []
    for (column_number, column_flow) in enumerate(flow_matrix[0]):
        start_dates.append(None)
        wet_dates.append(None)
        durations.append(None)
        mags.append(None)
        'Check to see if water year has more than allowed nan or zeros'
        if ((np.isnan(flow_matrix[:, column_number]).sum() > max_nan_allowed_per_year) or (np.count_nonzero((flow_matrix[:, column_number] == 0)) > max_zero_allowed_per_year) or (max(flow_matrix[:, column_number]) < min_flow_rate)):
            continue
        'Get flow data'
        flow_data = flow_matrix[:, column_number]
        x_axis = list(range(len(flow_data)))
        'Interpolate between None values'
        flow_data = replace_nan(flow_data)
        'Return to Wet Season'
        wet_filter_data = gaussian_filter1d(flow_data, wet_sigma)
        return_date = return_to_wet_date(wet_filter_data, wet_threshold_perc)
        wet_dates[(- 1)] = (return_date + 10)
        'Filter noise data with small sigma to find fall flush hump'
        filter_data = gaussian_filter1d(flow_data, sigma)
        'Fit spline'
        x_axis = list(range(len(filter_data)))
        spl = ip.UnivariateSpline(x_axis, filter_data, k=3, s=3)
        'Find the peaks and valleys of the filtered data'
        mean_flow = np.nanmean(filter_data)
        (maxarray, minarray) = peakdet(spl(x_axis), (mean_flow * peak_sensitivity))
        'Find max and min of filtered flow data'
        max_flow = max(filter_data[20:])
        max_flow_index = (find_index(filter_data[20:], max_flow) + 20)
        min_flow = min(wet_filter_data[:max_flow_index])
        'If could not find any max and find'
        if ((not list(maxarray)) or (not list(minarray)) or (minarray[0][0] > max_flow_index)):
            continue
        "Get flow magnitude threshold from previous summer's baseflow"
        baseflows = []
        if (column_number == 0):
            wet_date = wet_dates[0]
            baseflow = list(flow_matrix[:wet_date, column_number])
            bs_mean = np.mean(baseflow)
            bs_med = np.nanpercentile(baseflow, 50)
        else:
            summer_date = summer_timings[(column_number - 1)]
            if (wet_dates[column_number] > 20):
                wet_date = (wet_dates[column_number] - 20)
            baseflow = (list(flow_matrix[summer_date:, (column_number - 1)]) + list(flow_matrix[:wet_date, column_number]))
            bs_mean = np.mean(baseflow)
            bs_med = np.nanpercentile(baseflow, 50)
        'Get fall flush peak'
        counter = 0
        half_duration = int((max_flush_duration / 2))
        if (bs_med > 25):
            min_flush_magnitude = (bs_med * 1.5)
        else:
            min_flush_magnitude = (bs_med * 2)
        if (min_flush_magnitude < min_flush_threshold):
            min_flush_magnitude = min_flush_threshold
        for flow_index in maxarray:
            if (counter == 0):
                if ((flow_index[0] < half_duration) and (flow_index[0] != 0) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude)):
                    'if index found is before the half duration allowed'
                    start_dates[(- 1)] = int(flow_index[0])
                    mags[(- 1)] = flow_index[1]
                    break
                elif (bool(((((flow_index[1] - spl((maxarray[counter][0] - half_duration))) / flow_index[1]) > flush_threshold_perc) or ((minarray[counter][0] - maxarray[counter][0]) < half_duration))) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude)):
                    'If peak and valley is separted by half duration, or half duration to the left is less than 30% of its value'
                    start_dates[(- 1)] = int(flow_index[0])
                    mags[(- 1)] = flow_index[1]
                    break
            elif (counter == len(minarray)):
                start_dates[(- 1)] = None
                mags[(- 1)] = None
                break
            elif (bool((((minarray[counter][0] - maxarray[counter][0]) < half_duration) or ((maxarray[counter][0] - minarray[(counter - 1)][0]) < half_duration))) and bool(((flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude) and (flow_index[0] <= date_cutoff)))):
                'valley and peak are distanced by less than half dur from either side'
                start_dates[(- 1)] = int(flow_index[0])
                mags[(- 1)] = flow_index[1]
                break
            elif ((((spl((flow_index[0] - half_duration)) - min_flow) / (flow_index[1] - min_flow)) < flush_threshold_perc) and (((spl((flow_index[0] + half_duration)) - min_flow) / (flow_index[1] - min_flow)) < flush_threshold_perc) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude) and (flow_index[0] <= date_cutoff)):
                'both side of flow value at the peak + half duration index fall below flush_threshold_perc'
                start_dates[(- 1)] = int(flow_index[0])
                mags[(- 1)] = flow_index[1]
                break
            counter = (counter + 1)
        'Check to see if last start_date falls behind the max_allowed_date'
        if (bool(((start_dates[(- 1)] is None) or (start_dates[(- 1)] > wet_dates[(- 1)]))) and wet_dates[(- 1)]):
            start_dates[(- 1)] = None
            mags[(- 1)] = None
        'Get duration of each fall flush'
        (current_duration, left, right) = calc_fall_flush_durations_2(filter_data, start_dates[(- 1)])
        durations[(- 1)] = current_duration
        _plotter(x_axis, flow_data, filter_data, wet_filter_data, start_dates, wet_dates, column_number, left, right, maxarray, minarray, min_flush_magnitude)
    return (start_dates, mags, wet_dates, durations)
def calc_fall_flush_timings_durations(flow_matrix, summer_timings):
    max_zero_allowed_per_year = fall_params['max_zero_allowed_per_year']
    max_nan_allowed_per_year = fall_params['max_nan_allowed_per_year']
    min_flow_rate = fall_params['min_flow_rate']
    sigma = fall_params['sigma']
    wet_sigma = fall_params['wet_sigma']
    peak_sensitivity = fall_params['peak_sensitivity']
    max_flush_duration = fall_params['max_flush_duration']
    wet_threshold_perc = fall_params['wet_threshold_perc']
    flush_threshold_perc = fall_params['flush_threshold_perc']
    min_flush_threshold = fall_params['min_flush_threshold']
    date_cutoff = fall_params['date_cutoff']
    start_dates = []
    wet_dates = []
    durations = []
    mags = []
    for (column_number, column_flow) in enumerate(flow_matrix[0]):
        start_dates.append(None)
        wet_dates.append(None)
        durations.append(None)
        mags.append(None)
        'Check to see if water year has more than allowed nan or zeros'
        if ((np.isnan(flow_matrix[:, column_number]).sum() > max_nan_allowed_per_year) or (np.count_nonzero((flow_matrix[:, column_number] == 0)) > max_zero_allowed_per_year) or (max(flow_matrix[:, column_number]) < min_flow_rate)):
            continue
        'Get flow data'
        flow_data = flow_matrix[:, column_number]
        x_axis = list(range(len(flow_data)))
        'Interpolate between None values'
        flow_data = replace_nan(flow_data)
        'Return to Wet Season'
        wet_filter_data = gaussian_filter1d(flow_data, wet_sigma)
        return_date = return_to_wet_date(wet_filter_data, wet_threshold_perc)
        wet_dates[(- 1)] = (return_date + 10)
        'Filter noise data with small sigma to find fall flush hump'
        filter_data = gaussian_filter1d(flow_data, sigma)
        'Fit spline'
        x_axis = list(range(len(filter_data)))
        spl = ip.UnivariateSpline(x_axis, filter_data, k=3, s=3)
        'Find the peaks and valleys of the filtered data'
        mean_flow = np.nanmean(filter_data)
        (maxarray, minarray) = peakdet(spl(x_axis), (mean_flow * peak_sensitivity))
        'Find max and min of filtered flow data'
        max_flow = max(filter_data[20:])
        max_flow_index = (find_index(filter_data[20:], max_flow) + 20)
        min_flow = min(wet_filter_data[:max_flow_index])
        'If could not find any max and find'
        if ((not list(maxarray)) or (not list(minarray)) or (minarray[0][0] > max_flow_index)):
            continue
        "Get flow magnitude threshold from previous summer's baseflow"
        baseflows = []
        if (column_number == 0):
            wet_date = wet_dates[0]
            baseflow = list(flow_matrix[:wet_date, column_number])
            bs_mean = np.mean(baseflow)
            bs_med = np.nanpercentile(baseflow, 50)
        else:
            summer_date = summer_timings[(column_number - 1)]
            if (wet_dates[column_number] > 20):
                wet_date = (wet_dates[column_number] - 20)
            baseflow = (list(flow_matrix[summer_date:, (column_number - 1)]) + list(flow_matrix[:wet_date, column_number]))
            bs_mean = np.mean(baseflow)
            bs_med = np.nanpercentile(baseflow, 50)
        'Get fall flush peak'
        counter = 0
        half_duration = int((max_flush_duration / 2))
        if (bs_med > 25):
            min_flush_magnitude = (bs_med * 1.5)
        else:
            min_flush_magnitude = (bs_med * 2)
        if (min_flush_magnitude < min_flush_threshold):
            min_flush_magnitude = min_flush_threshold
        for flow_index in maxarray:
            if (counter == 0):
                if ((flow_index[0] < half_duration) and (flow_index[0] != 0) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude)):
                    'if index found is before the half duration allowed'
                    start_dates[(- 1)] = int(flow_index[0])
                    mags[(- 1)] = flow_index[1]
                    break
                elif (bool(((((flow_index[1] - spl((maxarray[counter][0] - half_duration))) / flow_index[1]) > flush_threshold_perc) or ((minarray[counter][0] - maxarray[counter][0]) < half_duration))) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude)):
                    'If peak and valley is separted by half duration, or half duration to the left is less than 30% of its value'
                    start_dates[(- 1)] = int(flow_index[0])
                    mags[(- 1)] = flow_index[1]
                    break
            elif (counter == len(minarray)):
                start_dates[(- 1)] = None
                mags[(- 1)] = None
                break
            elif (bool((((minarray[counter][0] - maxarray[counter][0]) < half_duration) or ((maxarray[counter][0] - minarray[(counter - 1)][0]) < half_duration))) and bool(((flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude) and (flow_index[0] <= date_cutoff)))):
                'valley and peak are distanced by less than half dur from either side'
                start_dates[(- 1)] = int(flow_index[0])
                mags[(- 1)] = flow_index[1]
                break
            elif ((((spl((flow_index[0] - half_duration)) - min_flow) / (flow_index[1] - min_flow)) < flush_threshold_perc) and (((spl((flow_index[0] + half_duration)) - min_flow) / (flow_index[1] - min_flow)) < flush_threshold_perc) and (flow_index[1] > wet_filter_data[int(flow_index[0])]) and (flow_index[1] > min_flush_magnitude) and (flow_index[0] <= date_cutoff)):
                'both side of flow value at the peak + half duration index fall below flush_threshold_perc'
                start_dates[(- 1)] = int(flow_index[0])
                mags[(- 1)] = flow_index[1]
                break
            counter = (counter + 1)
        'Check to see if last start_date falls behind the max_allowed_date'
        if (bool(((start_dates[(- 1)] is None) or (start_dates[(- 1)] > wet_dates[(- 1)]))) and wet_dates[(- 1)]):
            start_dates[(- 1)] = None
            mags[(- 1)] = None
        'Get duration of each fall flush'
        (current_duration, left, right) = calc_fall_flush_durations_2(filter_data, start_dates[(- 1)])
        durations[(- 1)] = current_duration
        _plotter(x_axis, flow_data, filter_data, wet_filter_data, start_dates, wet_dates, column_number, left, right, maxarray, minarray, min_flush_magnitude)
    return (start_dates, mags, wet_dates, durations)