123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- #!/usr/bin/env python3
- # Copyright © 2019 Anton Tsyganenko <anton-tsyganenko@yandex.ru>
- #
- # Permission is hereby granted, free of charge, to any person obtaining a copy
- # of this software and associated documentation files (the "Software"), to deal
- # in the Software without restriction, including without limitation the rights
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- # copies of the Software, and to permit persons to whom the Software is
- # furnished to do so, subject to the following conditions:
- #
- # The above copyright notice and this permission notice shall be included in all
- # copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- # SOFTWARE.
- # Программа принимает на вход вариант в виде списков действующих сил,
- # распределенных нагрузок и сосредоточенных моментов (вариант задается
- # чуть ниже) и автоматически:
- #
- # - находит реакции опор;
- #
- # - из условия прочности определяет размеры прямоугольного сечения;
- #
- # - выбирает по сортаменту двутавр, удовлетворяющий условиям прочности и
- # жесткости;
- #
- # - проверяет условие прочности по нормальным и касательным напряжениям
- # для сечений в виде прямоугольника и двутавра;
- #
- # - считает коэффициент экономии;
- #
- # - строит эпюры распределения нормальных и касательных напряжений по
- # высоте сечения для сечений в виде двутавра и прямоугольника;
- #
- # - строит эпюры поперечных сил, изгибающих моментов, углов поворота
- # сечений и прогибов для сечения в виде двутавра
- #
- # - проверяет выполнение условия жесткости;
- #
- # - строит таблицу поперечных сил, изгибающих моментов, углов поворота
- # сечений и прогибов для сечения в виде двутавра, выделяя в таблице
- # строчки, где максимально значение Q, M или y.
- forces, qs, Ms = \
- ([tuple(float(i) for i in f.split()) if f != "" else (0,0,0)\
- for f in input(msg+": ").split(";")]\
- for msg in ("Силы", "Распределенные нагрузки", "Моменты"))
- Xa = float(input("Координата первой опоры/заделки: "))
- Xb_text = input("Координата второй опоры (если есть): ")
- Xmax = float(input("Длина балки: "))
- import matplotlib
- import matplotlib.pyplot as plt
- import numpy as np
- import math
- import os
- os.chdir(".") # Куда сохранять результат
- # Введем параметры материалов, допустимие значения величин и и т.д. В
- # программе в переменных сохраняются численные значения соответствующих
- # величин в основных единицах СИ.
- E = 2e11
- sigma_dop = 160e6
- tau_dop = 100e6
- hb_rel = 2 # отношение высоты прямоугольного сечения к ширине
- # Введем сортамент, по которому программа будет выбирать двутавры. Если
- # ввести не все существующие варианты, программа будет выбирать из
- # введенных.
- sortament = [
- # №, h, b, d, t, A, Jx (Ix), Wx, Sx
- ( "10", 100e-3, 55e-3, 4.5e-3, 7.2e-3, 12.0e-4, 198e-8, 39.7e-6, 23.0e-6),
- ( "12", 120e-3, 64e-3, 4.8e-3, 7.3e-3, 14.7e-4, 350e-8, 58.4e-6, 33.7e-6),
- ( "14", 140e-3, 73e-3, 4.9e-3, 7.5e-3, 17.4e-4, 572e-8, 81.7e-6, 46.8e-6),
- ( "16", 160e-3, 81e-3, 5.0e-3, 7.8e-3, 20.2e-4, 873e-8, 109e-6, 62.3e-6),
- ( "18", 180e-3, 90e-3, 5.1e-3, 8.1e-3, 23.4e-4, 1290e-8, 143e-6, 81.4e-6),
- ("18a", 180e-3, 100e-3, 5.1e-3, 8.3e-3, 25.4e-4, 1430e-8, 159e-6, 89.8e-6),
- ( "20", 200e-3, 100e-3, 5.2e-3, 8.4e-3, 25.4e-4, 1840e-8, 184e-6, 104e-6),
- ("20a", 200e-3, 110e-3, 5.2e-3, 8.6e-3, 28.9e-4, 2030e-8, 203e-6, 114e-6),
- ( "22", 220e-3, 110e-3, 5.4e-3, 8.7e-3, 30.6e-4, 2550e-8, 232e-6, 131e-6),
- ("22a", 220e-3, 120e-3, 5.4e-3, 8.9e-3, 32.8e-4, 2790e-8, 254e-6, 143e-6),
- ( "24", 240e-3, 115e-3, 5.6e-3, 9.5e-3, 34.8e-4, 3460e-8, 289e-6, 163e-6),
- ("24а", 240e-3, 125e-3, 5.6e-3, 9.8e-3, 37.5e-4, 3800e-8, 317e-6, 178e-6),
- ( "27", 270e-3, 125e-3, 6.0e-3, 9.8e-3, 40.2e-4, 5010e-8, 371e-6, 210e-6),
- ("27а", 270e-3, 135e-3, 6.0e-3, 10.2e-3, 43.2e-4, 5500e-8, 407e-6, 229e-6),
- ( "30", 300e-3, 135e-3, 6.5e-3, 10.2e-3, 46.5e-4, 7080e-8, 472e-6, 268e-6),
- ("30а", 300e-3, 145e-3, 6.5e-3, 10.7e-3, 49.9e-4, 7780e-8, 518e-6, 292e-6),
- ( "33", 330e-3, 140e-3, 7.0e-3, 11.2e-3, 53.8e-4, 9840e-8, 597e-6, 339e-6),
- ( "36", 360e-3, 145e-3, 7.5e-3, 12.3e-3, 61.9e-4, 13380e-8, 743e-6, 423e-6),
- ( "40", 400e-3, 155e-3, 8.0e-3, 13.0e-3, 71.4e-4, 18930e-8, 947e-6, 540e-6),
- ( "45", 450e-3, 160e-3, 8.6e-3, 14.2e-3, 83.0e-4, 27450e-8, 1220e-6, 699e-6),
- ( "50", 500e-3, 170e-3, 9.5e-3, 15.2e-3, 97.8e-4, 39290e-8, 1570e-6, 905e-6),
- ( "55", 550e-3, 180e-3, 10.3e-3, 16.5e-3, 114e-4, 55150e-8, 2000e-6, 1150e-6),
- ( "60", 600e-3, 190e-3, 11.1e-3, 17.8e-3, 132e-4, 75450e-8, 2510e-6, 1450e-6),
- ( "65", 650e-3, 200e-3, 12.0e-3, 19.2e-3, 153e-4, 101400e-8, 3120e-6, 1800e-6),
- ( "70", 700e-3, 210e-3, 13.0e-3, 20.8e-3, 176e-4, 134600e-8, 3840e-6, 2230e-6),
- ("70a", 700e-3, 210e-3, 15.0e-3, 24.0e-3, 202e-4, 152700e-8, 4360e-6, 2550e-6),
- ("70б", 700e-3, 210e-3, 17.5e-3, 28.2e-3, 234e-4, 175370e-8, 5010e-6, 2940e-6),
- ]
- if Xb_text:
- Xb = float(Xb_text)
- vartype = "double"
- Ydop = Xmax/300
- else:
- vartype = "hard"
- Ydop = Xmax/200
- print("Допустимый прогиб: {:g} м".format(Ydop))
- # Определим функции, вычисляющие поперечные силы и изгибающие моменты в
- # заданной точке
- def q(x):
- result = 0
- for force in forces:
- if force[1]<x:
- result += force[0]
- for q in qs:
- if q[1]<x:
- result += q[0]*(x-q[1])
- if q[2]<x:
- result -= q[0]*(x-q[2])
- return result
- def m(x):
- result = 0
- for q in qs:
- if q[1]<x:
- result += q[0]*(x-q[1])**2/2
- if q[2]<x:
- result -= q[0]*(x-q[2])**2/2
- for force in forces:
- if force[1]<x:
- result += force[0]*(x-force[1])
- for moment in Ms:
- if moment[1]<x:
- result += moment[0]
- return result
- # Найдем реакции опор исходя из равенства нулю суммы всех сил и моментов
- # сил, и выбрав точку с x=Xa.
- SF = sum(f[0] for f in forces)\
- + sum(q[0]*(q[2]-q[1]) for q in qs)
- SM = - sum(m[0] for m in Ms)\
- + sum(f[0]*(f[1]-Xa) for f in forces)\
- + sum(q[0]*(q[2]-q[1])*((q[2]+q[1])/2-Xa) for q in qs)
- if vartype == "double":
- Ra = -(SM - Xb*SF)/(Xa - Xb)
- Rb = -(Xa*SF - SM)/(Xa - Xb)
- forces.append((Rb, Xb))
- forces.append((Ra, Xa))
- print("Ra = {:g} Н; Rb = {:g} Н".format(Ra, Rb))
- elif vartype == "hard":
- Ra = -SF
- Ma = SM
- forces.append((Ra, Xa))
- Ms.append((Ma, Xa))
- print("Ra = {:g} Н; Ma = {:g} Н*м".format(Ra, Ma))
- # Для расчета углов поворота сечений и прогибов, сначала определим
- # функцию `EIy_rel`, вычисляющую условный прогиб, принимая начальную
- # координату и угол поворота сечения в начале за 0, затем вычислим угол
- # поворота сечения в начале (с точностью до множителей, зависящих только
- # от материала и параметров сечения: E и I), после чего определим
- # функцию, вычисляющую EIθ (`EIt`) и EIy
- def EIy_rel(x):
- result = 0
- for q in qs:
- if q[1]<x:
- result += q[0]*(x-q[1])**4/24
- if q[2]<x:
- result -= q[0]*(x-q[2])**4/24
- for force in forces:
- if force[1]<x:
- result += force[0]*(x-force[1])**3/6
- for moment in Ms:
- if moment[1]<x:
- result += moment[0]*(x-moment[1])**2/2
- return result
- def EIt_rel(x):
- result = 0
- for q in qs:
- if q[1]<x:
- result += q[0]*(x-q[1])**3/6
- if q[2]<x:
- result -= q[0]*(x-q[2])**3/6
- for force in forces:
- if force[1]<x:
- result += force[0]*(x-force[1])**2/2
- for moment in Ms:
- if moment[1]<x:
- result += moment[0]*(x-moment[1])
- return result
- if vartype == "double":
- EIt0 = (EIy_rel(Xb) - EIy_rel(Xa)) / (Xa - Xb)
- elif vartype == "hard":
- EIt0 = -EIt_rel(Xa)
- EIy0 = -EIy_rel(Xa)-EIt0*Xa
- print("EIθ_0 = {:g} Н*м^2; EIy_0 = {:g} Н*м^3".format(EIt0, EIy0))
- def EIt(x):
- return EIt_rel(x) + EIt0
- def EIy(x):
- return EIy_rel(x) + x*EIt0 + EIy0
- # Определим список координат, при которых будем вычислять все параметры
- # для построения эпюр (и других целей): поперечную силу, изгибающий
- # момент, угол поворота сечения и прогиб. Будем вычислять их по длине
- # балки каждый сантиметр, и через 1 нм после границ участков, чтобы на
- # эпюре скачки были вертикальные.
- X = list(np.arange(0, Xmax+0.01, 0.01))
- X += [q[1]+1e-9 for q in qs]
- X += [q[2]+1e-9 for q in qs]
- X += [f[1]+1e-9 for f in forces]
- X += [m[1]+1e-9 for m in Ms]
- X = list(set(X)) # убрать повторяющиеся координаты
- X.sort()
- # Посчитаем поперечные силы и изгибающие моменты, после чего определим
- # опасные сечения и требуемые моменты инерции и сопротивления, чтобы
- # выполнялись условия жесткости и прочности.
- Q = []
- M = []
- EIY=[]
- for x in X:
- Q.append(q(x))
- M.append(m(x))
- EIY.append(EIy(x))
- Qmax = max(Q, key=abs)
- Mmax = max(M, key=abs)
- I_need = abs(max(EIY, key=abs))/(Ydop*E)
- W_need = abs(Mmax)/sigma_dop
- print("I_тр = {:g} м^4; W_тр = {:g} м^3".format(I_need, W_need))
- # Определим ширину прямоугольного сечения, для которого будут
- # выполняться условия прочности по нормальным и касательным напряжениям
- # и условие жесткости
- b_need_sigma = (6/hb_rel**2*W_need)**(1/3)
- b_need_tau = math.sqrt(2/hb_rel * abs(Qmax)/tau_dop)
- b_need_I = (I_need*12/hb_rel**3)**(1/4)
- b_need = max(b_need_sigma, b_need_tau, b_need_I)
- # Округлим до целых см в большую сторону.
- b_rect = math.ceil(b_need*100)/100
- h_rect = b_rect*hb_rel # высота сечения
- print("b_тр = {:g} м; b = {:g} м; h = {:g} м"\
- .format(b_need, b_rect, h_rect))
- # Для дальнейших вычислений будет удобнее задействовать
- # объектно-ориентированное программирование. Определим классы сечений в
- # виде прямоугольника и двутавра (упрощенного). Объекты этих классов
- # смогут выдавать свою ширину при заданной y, статический момент
- # относительно нейтральной оси части площади сечения, лежащей по одну
- # сторону от y, требующиеся для вычисления касательного напряжения по
- # формуле Журавского, и изображение, которое потребуется для эпюр
- # распределения τ и σ.
- class Profile:
- def __init__(self, b, h):
- self.b = b
- self.h = h
- self.Wx = b*h**2/6
- self.Ix = b*h**3/12
- self.A = b*h
- self.name = "Прямоугольник {}x{} м".format(b, h)
- def S(self, y):
- return self.b/2*(self.h**2/4 - y**2)
- def wid(self, y):
- return self.b
- def image(self):
- x = self.b/2
- y = self.h/2
- return ([x, -x, -x, x], [y, y, -y, -y])
- class Dvutavr(Profile):
- def __init__(self, l):
- self.h = l[1]
- self.b = l[2]
- self.d = l[3]
- self.t = l[4]
- self.A = l[5]
- self.Ix = l[6]
- self.Wx = l[7]
- self.Sx = l[8]
- self.name = "Двутавр №{}".format(l[0])
- def wid(self, y):
- # за пределами сечения возвращаем ненулевую ширину, чтобы не
- # было неопределенности 0/0
- if abs(y)>=self.h/2-self.t:
- return self.b
- else:
- return self.d
- def S(self, y):
- ycr = self.h/2-self.t
- if abs(y) >= ycr:
- return self.wid(y)/2*(self.h**2/4 - y**2)
- else:
- return self.Sx-(self.Sx-self.S(ycr))*(y/ycr)**2
- def image(self):
- y1 = self.h/2
- y2 = self.h/2-self.t
- x1 = self.b/2
- x2 = self.d/2
- return ([x1, -x1, -x1, -x2, -x2, -x1, -x1, x1, x1, x2, x2, x1],
- [y1, y1, y2, y2, -y2, -y2, -y1, -y1, -y2, -y2, y2, y2])
- # По сортаменту найдем двутавр, для которого будут выполняться условия
- # прочности и жесткости. Создадим объекты сечений, определим опасные
- # сечения (точнее, значения поперечных сил и изгибающих моментов в них)
- # и посчитаем нормальные и касательные напряжения в опасных сечениях.
- # Попутно проверим условие прочности.
- items = []
- for item in sortament:
- if item[7] >= W_need and item[6] >= I_need:
- items.append(Dvutavr(item))
- print("Выбран двутавр №{}".format(item[0]))
- break
- else:
- print("Не удалось найти подходящий двутавр по сортаменту")
- items.append(Profile(b_rect, h_rect))
- p_plots = []
- for item in items:
- tau = []
- sigma = []
- image = list(item.image())
- ym = item.h/2
- # для эпюр напряжений используем 243 точки.
- yp = np.linspace(-ym, ym, 243)
- for y in yp:
- tau.append(Qmax*item.S(y)/(item.Ix*item.wid(y)))
- sigma.append(-y*Mmax/item.Ix)
- p_plots.append((yp, image, sigma, tau))
- tau_max, sigma_max = max(tau, key=abs), max(sigma, key=abs)
- ok = abs(tau_max)<=tau_dop and abs(sigma_max)<=sigma_dop
- print("Условие прочности {}выполняется для сечения: {}"\
- .format("" if ok else "НЕ ", item.name))
- print("τ_max = {:.4g} Па; σ_max = {:.4g} Па"\
- .format(tau_max, sigma_max))
- # Построим эпюры распределения нормальных и касательных напряжений по
- # сечению. По горизонтальным осям масштаб одинаковый. Обратите внимание
- # на множители, применяемые к значениям по осям напряжений.
- fig2 = plt.figure(2, figsize=(10,10))
- axs = fig2.subplots(2, 3, sharey="all", sharex="col")
- for i, dat in enumerate(p_plots):
- ix, iy = dat[1][0], dat[1][1] # Изображение сечения
- axs[i, 0].fill(ix, iy)
- axs[i, 0].set_aspect(1)
- axs[i, 0].set_ylabel("y / м")
- axs[i, 0].set_xlabel("x / м")
- axs[i, 1].plot(dat[2], dat[0]) # нормальные напряжения
- axs[i, 1].axvline(color="black")
- axs[i, 1].set_xlabel("σ / Па")
- axs[i, 2].axvline(color="black") # касательные
- axs[i, 2].plot(dat[3], dat[0])
- axs[i, 2].set_xlabel("τ / Па")
- for j in range(3):
- axs[i, j].grid()
- fig2.savefig("profile.png")
- # Сосчитаем углы поворота сечений и прогиб. Проверим условие жесткости
- I = items[0].Ix
- T = []
- Y = []
- for x in X:
- Y.append(EIy(x)/(E*I))
- T.append(EIt(x)/(E*I))
- Ymax = max(Y, key=abs)
- print("Условие жесткости {}выполняется для сечения: {}"\
- .format("НЕ " if abs(Ymax)>Ydop else "", items[0].name))
- # Сосчитаем коэффициент экономии
- if len(items) > 1:
- econom = items[0].A/items[1].A
- print("Коэффициент экономии: {:.4f}".format(econom))
- # Наконец, построим эпюры поперечных сил, изгибающих моментов, углов
- # закручивания и прогибов.
- fig1 = plt.figure(1, figsize=(10,15))
- axs1 = fig1.subplots(4, 1)
- for ax, s in zip(axs1, [(Q, "Q / Н"), (M, "М / (Н*м)"),\
- (T, "θ"), (Y, "y / м")]):
- ax.plot(X, s[0])
- ax.set_ylabel(s[1])
- ax.set_xlabel("x / м")
- ax.axhline(color="black")
- ax.grid()
- fig1.savefig("balka.png")
- # Построим таблицу значений поперечных сил, изгибающих моментов, углов
- # поворота сечения и прогибов в зависимости от координаты:
- with open("x_data.txt", "w") as f:
- f.writelines(" x Q M theta y\r\n")
- for x, q, m, t, y in zip(X, Q, M, T, Y):
- msg = ""
- if q == Qmax:
- msg += "Qmax "
- if m == Mmax:
- msg += "Mmax"
- if y == Ymax:
- msg += "Ymax"
- f.writelines("{:8.4f}{:8.0f}{:12.2f}{:10.5f}{:10.5f} {}\r\n"\
- .format(x, q, m, t, y, msg))
- # Выведем таблицы распределения касательных напряжений по высоте
- # сечения. Сначала идет таблица для двутавра (если его удалось найти),
- # потом для прямоугольника.
- with open("y_data.txt", "w") as f:
- for prof, item in zip(p_plots, items):
- name = item.name
- f.writelines(" y tau === {} \r\n".format(name))
- for y, tau in zip(prof[0], prof[3]):
- f.writelines("{:8.4f}{:12.4e}\r\n".format(y, tau))
- print("Результаты расчета сохранены в {}".format(os.getcwd()))
|