Rainflow counting 계산 샘플(EXCEL, PYTHON)1

Rainflow counting 계산 엑셀 및 파이썬으로 작성된 실행 파일이 필요하시면 댓글에 이메일 남겨주세요.

웹에서 바로 실행하고 싶으시면 아래 링크를 이용하세요

Rainflow counting 소개

Rainflow counting 이란

피로 수명은 피로 곡선(S-N 곡선 등)을 사용하여 계산할 수 있습니다. 실 현상에서 피로 하중은 매우 복잡합니다. 무작위로 변화하는 하중인 경우는 특히 피로 수명을 추정하기 어렵습니다.

레인플로카운팅(Rainflow counting) 은 다른 응력(변형) 스펙트럼을 간단한 응력(변형) 집합으로 줄이기 위해 피로 데이터 분석에서 사용됩니다.

이 방법은 복잡한 하중을 받는 구조물의 피로 수명을 평가하기 위해 Miner의 규칙을 적용하는 것을 가능하게 합니다.

배경

이 알고리즘은 Tatsuo Endo와 M. Matsuishi가 개발하고 1968년 일본학회지 “Fatigue of Metals Subjected to Varying Stress – Fatigue Lives under Random Loading”에 게재했습니다. Downing과 Socie는 1982년 Rainflow cycle-counting 알고리즘 중 하나를 개발하여 광범위하게 참조되고 사용되어 왔으며, 이는 피로 분석에서 많은 cycle-counting 알고리즘 중 하나로 ASTM E 1049-85에 포함되어 있습니다. Igor Rychlik은 Rainflow counting 방법에 대한 수학적 정의를 제시하여 하중 신호의 통계적 특성으로 부터 닫혀있는 형식의 계산을 가능하게 했습니다.

Rainflow counting 계산 방법

Rainflow counting 계산 알고리즘의 이름은 빗물이 pagoda(지붕)의가장자리를 따라 흘러내리는 현상과 비교 되어 만들어진 이름입니다. Rainflow counting 알고리즘은 다음과 같은 단계로 이루어져 있습니다:

  1. 시간 기록을 (인장) peaks와 (압축) valleys의 연속으로 줄입니다.
  2. 시간 기록을 주각 지붕과 같은 단단한 시트의 템플릿으로 상상합니다.
  3. 시트를 시계 방향으로 90° 돌리면(가장 초기의 시간이 상단이 되도록), 이제 주각 지붕과 같아집니다 각각의 인장 peak은 주각에서 “물방울”처럼 떨어지는 것으로 상상합니다.
  4. 흐름의 종료점을 찾아 반쪽 주기(half-cycle)의 수를 세는데, 이는 다음과 같이 발생합니다:
  5. (a) 시간 기록의 끝에 도달할 때,
    (b) 이전의 인장 peak에서 시작한 흐름과 병합될 때, 또는
    (c) 반대 방향의 인장 peak보다 크거나 같은 크기로 흐를 때.
  6. 이 과정을 압축 valleys에 대해서도 반복합니다.
  7. 시작과 종료 사이의 응력 차이 Δ = max – min를 각 반쪽 주기에 할당합니다.
  8. 크기는 같지만 반대 방향인 반쪽 주기들을 짝지어 완전한 주기의 수를 세고, 짝이 맞지 않는 반쪽 주기들은 잔여 반쪽 주기로 처리합니다.

주어진 반쪽 주기에는 더 작은 반쪽 주기들이 포함될 수 있습니다. 일반적으로 큰 응력 주기를 작은 주기로 분할해서는 안 됩니다. 왜냐하면 이렇게 하면 피로 손상이 과소 평가될 수 있기 때문입니다. 작은 응력 주기는 큰 응력 반전의 일시적인 중단으로 처리되어야 합니다.

Rainflow counting 계산 엑셀 파일 (EXCEL)

Rainflow counting 계산 샘플 엑셀 파일입니다. 계산방법은 복잡하기 때문에 VBA로 작성되었습니다. 필요하신 분들은 댓글 남겨주세요

Rainflow counting 계산

Rainflow counting 계산 파이썬 및 실행 파일 (EXE)

작성 코드를 참고하시고 실행파일 EXE가 필요하시면 댓글남겨주세요

Rainflow counting 계산 파이썬 코드 입니다.


import pandas as pd
import tkinter as tk
from tkinter import messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

def plot_graph(load_array):
    plt.figure(figsize=(8, 5))
    plt.plot(load_array, marker='o')
    plt.title("Input Load Data")
    plt.xlabel("Index")
    plt.ylabel("Load Value")
    plt.grid(True)
    plt.tight_layout()

    # Convert the matplotlib plot to a Tkinter canvas
    canvas = FigureCanvasTkAgg(plt.gcf(), master=root)
    canvas.draw()

    # Add the plot to the GUI window
    canvas.get_tk_widget().pack()

def show_graph():
    input_data = input_text.get("1.0", tk.END).strip()
    try:
        load_array = [float(val.strip()) for val in input_data.split(',')]
        plot_graph(load_array)
    except Exception as e:
        messagebox.showerror("Error", str(e))

def next_lowest_index(i, is_active):
    done = False
    j = i - 1
    while not done:
        if is_active[j]:
            done = True
        else:
            j -= 1
    return j

def next_highest_index(i, is_active):
    done = False
    j = i + 1
    while not done:
        if is_active[j]:
            done = True
        else:
            j += 1
    return j

def peaks_and_valleys(load_array, n_loads, noise_threshold):
    reversals = [0.0]*n_loads
    local_reversal = [0.0]*n_loads
    counter = 0
    vector = 0
    max_load = load_array[0]
    min_load = max_load
    for j in range(1, n_loads):
        if vector == 0:
            if load_array[j] > max_load:
                max_load = load_array[j]
            elif load_array[j] < min_load:
                min_load = load_array[j]
            if max_load - load_array[0] >= noise_threshold:
                local_reversal[0] = min_load
                vector = 1
                counter = 1
            elif load_array[0] - min_load >= noise_threshold:
                local_reversal[0] = max_load
                vector = -1
                counter = 1
        elif vector == 1:
            if load_array[j] > max_load:
                max_load = load_array[j]
            elif max_load - load_array[j] >= noise_threshold:
                local_reversal[counter] = max_load
                vector = -1
                min_load = load_array[j]
                counter += 1
        elif vector == -1:
            if load_array[j] < min_load:
                min_load = load_array[j]
            elif load_array[j] - min_load >= noise_threshold:
                local_reversal[counter] = min_load
                vector = 1
                max_load = load_array[j]
                counter += 1
        if j == n_loads-1:
            if vector == 1:
                local_reversal[counter] = max_load
            elif vector == -1:
                local_reversal[counter] = min_load
    for j in range(counter+1):
        reversals[j] = local_reversal[j]
    return reversals[:counter+1]

def rainflow_calc(reversals):
    n_loads = len(reversals) - 1
    index = [0]*3
    is_active = [True]*(n_loads+1)
    local_cycles = [[0.0]*3 for _ in range(int(1.1*n_loads))]
    counter = 1
    start_index = 0
    index[0] = 0
    index[1] = 1
    range_previous = abs(reversals[1] - reversals[0])
    for j in range(2, n_loads+1):
        index[2] = j
        range_current = abs(reversals[index[2]] - reversals[index[1]])
        if range_current < range_previous:
            index[0] = index[1]
            index[1] = index[2]
            range_previous = range_current
        elif index[0] == start_index:
            local_cycles[counter-1][0] = range_previous
            local_cycles[counter-1][1] = (reversals[index[0]] + reversals[index[1]]) / 2
            local_cycles[counter-1][2] = 0.5
            is_active[index[0]] = False
            counter += 1
            start_index = index[1]
            index[0] = index[1]
            index[1] = index[2]
            range_previous = range_current
        else:
            local_cycles[counter-1][0] = range_previous
            local_cycles[counter-1][1] = (reversals[index[0]] + reversals[index[1]]) / 2
            local_cycles[counter-1][2] = 1
            is_active[index[0]] = False
            is_active[index[1]] = False
            counter += 1
            index[1] = next_lowest_index(index[2], is_active)
            if index[1] == start_index:
                index[0] = index[1]
                index[1] = index[2]
                range_previous = abs(reversals[index[1]] - reversals[index[0]])
            else:
                index[0] = next_lowest_index(index[1], is_active)
                done = False
                while not done:
                    range_previous = abs(reversals[index[1]] - reversals[index[0]])
                    range_current = abs(reversals[index[2]] - reversals[index[1]])
                    if range_current < range_previous:
                        index[0] = index[1]
                        index[1] = index[2]
                        range_previous = range_current
                        done = True
                    elif index[0] == start_index:
                        local_cycles[counter-1][0] = range_previous
                        local_cycles[counter-1][1] = (reversals[index[0]] + reversals[index[1]]) / 2
                        local_cycles[counter-1][2] = 0.5
                        is_active[index[0]] = False
                        counter += 1
                        start_index = index[1]
                        index[0] = index[1]
                        index[1] = index[2]
                        range_previous = range_current
                        done = True
                    else:
                        local_cycles[counter-1][0] = range_previous
                        local_cycles[counter-1][1] = (reversals[index[0]] + reversals[index[1]]) / 2
                        local_cycles[counter-1][2] = 1
                        is_active[index[0]] = False
                        is_active[index[1]] = False
                        counter += 1
                        index[1] = next_lowest_index(index[2], is_active)
                        if index[1] == start_index:
                            index[0] = index[1]
                            index[1] = index[2]
                            range_previous = abs(reversals[index[1]] - reversals[index[0]])
                            done = True
                        else:
                            index[0] = next_lowest_index(index[1], is_active)
    if start_index < j:
        index[0] = start_index
        index[1] = next_highest_index(index[0], is_active)
        done = False
        while not done:
            local_cycles[counter-1][0] = abs(reversals[index[1]] - reversals[index[0]])
            local_cycles[counter-1][1] = (reversals[index[0]] + reversals[index[1]]) / 2
            local_cycles[counter-1][2] = 0.5
            if index[1] == n_loads:
                done = True
            else:
                index[0] = index[1]
                index[1] = next_highest_index(index[0], is_active)
                counter += 1
    return local_cycles[:counter]

def process_and_save_data():
    input_data = input_text.get("1.0", tk.END).strip()
    try:
        load_array = [float(val.strip()) for val in input_data.split(',')]
        
        noise_threshold = 1
        n_loads = len(load_array)
        reversals = peaks_and_valleys(load_array, n_loads, noise_threshold)
        cycle_array = rainflow_calc(reversals)

        # 데이터 프레임을 생성하기 위해 각 정보를 리스트로 저장합니다.
        ranges = [cycle[0] for cycle in cycle_array]
        mean_values = [cycle[1] for cycle in cycle_array]
        cycle_types = [cycle[2] for cycle in cycle_array]

        # 데이터 프레임을 생성합니다.
        df = pd.DataFrame({'RANGE': ranges, 'Mean': mean_values, 'cycle': cycle_types})

        # Group by 'RANGE' and sum the 'cycle' values for each group
        df_grouped = df.groupby('RANGE', as_index=False).agg({'Mean': 'mean', 'cycle': 'sum'})

        # Save to Excel
        output_file = 'rainflowcounting.xlsx'
        df_grouped.to_excel(output_file, index=False)

        messagebox.showinfo("Success", "Data processed and saved to 'rainflowcounting.xlsx'")
    except Exception as e:
        messagebox.showerror("Error", str(e))

# Create the main application window
root = tk.Tk()
root.title("Rainflow Counting GUI")

# Create a label for input instructions
input_label = tk.Label(root, text="Enter load data (comma-separated):")
input_label.pack()

# Create a text widget for user input
input_text = tk.Text(root, height=5, width=40)
input_text.pack()

# Create a button to show the graph
show_graph_button = tk.Button(root, text="Show Graph", command=show_graph)
show_graph_button.pack(side=tk.LEFT)

# Create a button to process the data and save to Excel
process_button = tk.Button(root, text="Process and Save to Excel", command=process_and_save_data)
process_button.pack(side=tk.LEFT)

root.mainloop()

참고

Rainflow counting 계산 참고자료

1 https://en.wikipedia.org/wiki/Rainflow-counting_algorithm

2 https://fatigue-life.com/rainflow-counting/

다른 포스팅

11 thoughts on “Rainflow counting 계산 샘플(EXCEL, PYTHON)1”

댓글 남기기

%d 블로거가 이것을 좋아합니다: