rgr.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. #!/usr/bin/env python3
  2. # Copyright © 2019 Anton Tsyganenko <anton-tsyganenko@yandex.ru>
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in all
  12. # copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. # Программа принимает на вход вариант в виде списков действующих сил,
  22. # распределенных нагрузок и сосредоточенных моментов (вариант задается
  23. # чуть ниже) и автоматически:
  24. #
  25. # - находит реакции опор;
  26. #
  27. # - из условия прочности определяет размеры прямоугольного сечения;
  28. #
  29. # - выбирает по сортаменту двутавр, удовлетворяющий условиям прочности и
  30. # жесткости;
  31. #
  32. # - проверяет условие прочности по нормальным и касательным напряжениям
  33. # для сечений в виде прямоугольника и двутавра;
  34. #
  35. # - считает коэффициент экономии;
  36. #
  37. # - строит эпюры распределения нормальных и касательных напряжений по
  38. # высоте сечения для сечений в виде двутавра и прямоугольника;
  39. #
  40. # - строит эпюры поперечных сил, изгибающих моментов, углов поворота
  41. # сечений и прогибов для сечения в виде двутавра
  42. #
  43. # - проверяет выполнение условия жесткости;
  44. #
  45. # - строит таблицу поперечных сил, изгибающих моментов, углов поворота
  46. # сечений и прогибов для сечения в виде двутавра, выделяя в таблице
  47. # строчки, где максимально значение Q, M или y.
  48. forces, qs, Ms = \
  49. ([tuple(float(i) for i in f.split()) if f != "" else (0,0,0)\
  50. for f in input(msg+": ").split(";")]\
  51. for msg in ("Силы", "Распределенные нагрузки", "Моменты"))
  52. Xa = float(input("Координата первой опоры/заделки: "))
  53. Xb_text = input("Координата второй опоры (если есть): ")
  54. Xmax = float(input("Длина балки: "))
  55. import matplotlib
  56. import matplotlib.pyplot as plt
  57. import numpy as np
  58. import math
  59. import os
  60. os.chdir(".") # Куда сохранять результат
  61. # Введем параметры материалов, допустимие значения величин и и т.д. В
  62. # программе в переменных сохраняются численные значения соответствующих
  63. # величин в основных единицах СИ.
  64. E = 2e11
  65. sigma_dop = 160e6
  66. tau_dop = 100e6
  67. hb_rel = 2 # отношение высоты прямоугольного сечения к ширине
  68. # Введем сортамент, по которому программа будет выбирать двутавры. Если
  69. # ввести не все существующие варианты, программа будет выбирать из
  70. # введенных.
  71. sortament = [
  72. # №, h, b, d, t, A, Jx (Ix), Wx, Sx
  73. ( "10", 100e-3, 55e-3, 4.5e-3, 7.2e-3, 12.0e-4, 198e-8, 39.7e-6, 23.0e-6),
  74. ( "12", 120e-3, 64e-3, 4.8e-3, 7.3e-3, 14.7e-4, 350e-8, 58.4e-6, 33.7e-6),
  75. ( "14", 140e-3, 73e-3, 4.9e-3, 7.5e-3, 17.4e-4, 572e-8, 81.7e-6, 46.8e-6),
  76. ( "16", 160e-3, 81e-3, 5.0e-3, 7.8e-3, 20.2e-4, 873e-8, 109e-6, 62.3e-6),
  77. ( "18", 180e-3, 90e-3, 5.1e-3, 8.1e-3, 23.4e-4, 1290e-8, 143e-6, 81.4e-6),
  78. ("18a", 180e-3, 100e-3, 5.1e-3, 8.3e-3, 25.4e-4, 1430e-8, 159e-6, 89.8e-6),
  79. ( "20", 200e-3, 100e-3, 5.2e-3, 8.4e-3, 25.4e-4, 1840e-8, 184e-6, 104e-6),
  80. ("20a", 200e-3, 110e-3, 5.2e-3, 8.6e-3, 28.9e-4, 2030e-8, 203e-6, 114e-6),
  81. ( "22", 220e-3, 110e-3, 5.4e-3, 8.7e-3, 30.6e-4, 2550e-8, 232e-6, 131e-6),
  82. ("22a", 220e-3, 120e-3, 5.4e-3, 8.9e-3, 32.8e-4, 2790e-8, 254e-6, 143e-6),
  83. ( "24", 240e-3, 115e-3, 5.6e-3, 9.5e-3, 34.8e-4, 3460e-8, 289e-6, 163e-6),
  84. ("24а", 240e-3, 125e-3, 5.6e-3, 9.8e-3, 37.5e-4, 3800e-8, 317e-6, 178e-6),
  85. ( "27", 270e-3, 125e-3, 6.0e-3, 9.8e-3, 40.2e-4, 5010e-8, 371e-6, 210e-6),
  86. ("27а", 270e-3, 135e-3, 6.0e-3, 10.2e-3, 43.2e-4, 5500e-8, 407e-6, 229e-6),
  87. ( "30", 300e-3, 135e-3, 6.5e-3, 10.2e-3, 46.5e-4, 7080e-8, 472e-6, 268e-6),
  88. ("30а", 300e-3, 145e-3, 6.5e-3, 10.7e-3, 49.9e-4, 7780e-8, 518e-6, 292e-6),
  89. ( "33", 330e-3, 140e-3, 7.0e-3, 11.2e-3, 53.8e-4, 9840e-8, 597e-6, 339e-6),
  90. ( "36", 360e-3, 145e-3, 7.5e-3, 12.3e-3, 61.9e-4, 13380e-8, 743e-6, 423e-6),
  91. ( "40", 400e-3, 155e-3, 8.0e-3, 13.0e-3, 71.4e-4, 18930e-8, 947e-6, 540e-6),
  92. ( "45", 450e-3, 160e-3, 8.6e-3, 14.2e-3, 83.0e-4, 27450e-8, 1220e-6, 699e-6),
  93. ( "50", 500e-3, 170e-3, 9.5e-3, 15.2e-3, 97.8e-4, 39290e-8, 1570e-6, 905e-6),
  94. ( "55", 550e-3, 180e-3, 10.3e-3, 16.5e-3, 114e-4, 55150e-8, 2000e-6, 1150e-6),
  95. ( "60", 600e-3, 190e-3, 11.1e-3, 17.8e-3, 132e-4, 75450e-8, 2510e-6, 1450e-6),
  96. ( "65", 650e-3, 200e-3, 12.0e-3, 19.2e-3, 153e-4, 101400e-8, 3120e-6, 1800e-6),
  97. ( "70", 700e-3, 210e-3, 13.0e-3, 20.8e-3, 176e-4, 134600e-8, 3840e-6, 2230e-6),
  98. ("70a", 700e-3, 210e-3, 15.0e-3, 24.0e-3, 202e-4, 152700e-8, 4360e-6, 2550e-6),
  99. ("70б", 700e-3, 210e-3, 17.5e-3, 28.2e-3, 234e-4, 175370e-8, 5010e-6, 2940e-6),
  100. ]
  101. if Xb_text:
  102. Xb = float(Xb_text)
  103. vartype = "double"
  104. Ydop = Xmax/300
  105. else:
  106. vartype = "hard"
  107. Ydop = Xmax/200
  108. print("Допустимый прогиб: {:g} м".format(Ydop))
  109. # Определим функции, вычисляющие поперечные силы и изгибающие моменты в
  110. # заданной точке
  111. def q(x):
  112. result = 0
  113. for force in forces:
  114. if force[1]<x:
  115. result += force[0]
  116. for q in qs:
  117. if q[1]<x:
  118. result += q[0]*(x-q[1])
  119. if q[2]<x:
  120. result -= q[0]*(x-q[2])
  121. return result
  122. def m(x):
  123. result = 0
  124. for q in qs:
  125. if q[1]<x:
  126. result += q[0]*(x-q[1])**2/2
  127. if q[2]<x:
  128. result -= q[0]*(x-q[2])**2/2
  129. for force in forces:
  130. if force[1]<x:
  131. result += force[0]*(x-force[1])
  132. for moment in Ms:
  133. if moment[1]<x:
  134. result += moment[0]
  135. return result
  136. # Найдем реакции опор исходя из равенства нулю суммы всех сил и моментов
  137. # сил, и выбрав точку с x=Xa.
  138. SF = sum(f[0] for f in forces)\
  139. + sum(q[0]*(q[2]-q[1]) for q in qs)
  140. SM = - sum(m[0] for m in Ms)\
  141. + sum(f[0]*(f[1]-Xa) for f in forces)\
  142. + sum(q[0]*(q[2]-q[1])*((q[2]+q[1])/2-Xa) for q in qs)
  143. if vartype == "double":
  144. Ra = -(SM - Xb*SF)/(Xa - Xb)
  145. Rb = -(Xa*SF - SM)/(Xa - Xb)
  146. forces.append((Rb, Xb))
  147. forces.append((Ra, Xa))
  148. print("Ra = {:g} Н; Rb = {:g} Н".format(Ra, Rb))
  149. elif vartype == "hard":
  150. Ra = -SF
  151. Ma = SM
  152. forces.append((Ra, Xa))
  153. Ms.append((Ma, Xa))
  154. print("Ra = {:g} Н; Ma = {:g} Н*м".format(Ra, Ma))
  155. # Для расчета углов поворота сечений и прогибов, сначала определим
  156. # функцию `EIy_rel`, вычисляющую условный прогиб, принимая начальную
  157. # координату и угол поворота сечения в начале за 0, затем вычислим угол
  158. # поворота сечения в начале (с точностью до множителей, зависящих только
  159. # от материала и параметров сечения: E и I), после чего определим
  160. # функцию, вычисляющую EIθ (`EIt`) и EIy
  161. def EIy_rel(x):
  162. result = 0
  163. for q in qs:
  164. if q[1]<x:
  165. result += q[0]*(x-q[1])**4/24
  166. if q[2]<x:
  167. result -= q[0]*(x-q[2])**4/24
  168. for force in forces:
  169. if force[1]<x:
  170. result += force[0]*(x-force[1])**3/6
  171. for moment in Ms:
  172. if moment[1]<x:
  173. result += moment[0]*(x-moment[1])**2/2
  174. return result
  175. def EIt_rel(x):
  176. result = 0
  177. for q in qs:
  178. if q[1]<x:
  179. result += q[0]*(x-q[1])**3/6
  180. if q[2]<x:
  181. result -= q[0]*(x-q[2])**3/6
  182. for force in forces:
  183. if force[1]<x:
  184. result += force[0]*(x-force[1])**2/2
  185. for moment in Ms:
  186. if moment[1]<x:
  187. result += moment[0]*(x-moment[1])
  188. return result
  189. if vartype == "double":
  190. EIt0 = (EIy_rel(Xb) - EIy_rel(Xa)) / (Xa - Xb)
  191. elif vartype == "hard":
  192. EIt0 = -EIt_rel(Xa)
  193. EIy0 = -EIy_rel(Xa)-EIt0*Xa
  194. print("EIθ_0 = {:g} Н*м^2; EIy_0 = {:g} Н*м^3".format(EIt0, EIy0))
  195. def EIt(x):
  196. return EIt_rel(x) + EIt0
  197. def EIy(x):
  198. return EIy_rel(x) + x*EIt0 + EIy0
  199. # Определим список координат, при которых будем вычислять все параметры
  200. # для построения эпюр (и других целей): поперечную силу, изгибающий
  201. # момент, угол поворота сечения и прогиб. Будем вычислять их по длине
  202. # балки каждый сантиметр, и через 1 нм после границ участков, чтобы на
  203. # эпюре скачки были вертикальные.
  204. X = list(np.arange(0, Xmax+0.01, 0.01))
  205. X += [q[1]+1e-9 for q in qs]
  206. X += [q[2]+1e-9 for q in qs]
  207. X += [f[1]+1e-9 for f in forces]
  208. X += [m[1]+1e-9 for m in Ms]
  209. X = list(set(X)) # убрать повторяющиеся координаты
  210. X.sort()
  211. # Посчитаем поперечные силы и изгибающие моменты, после чего определим
  212. # опасные сечения и требуемые моменты инерции и сопротивления, чтобы
  213. # выполнялись условия жесткости и прочности.
  214. Q = []
  215. M = []
  216. EIY=[]
  217. for x in X:
  218. Q.append(q(x))
  219. M.append(m(x))
  220. EIY.append(EIy(x))
  221. Qmax = max(Q, key=abs)
  222. Mmax = max(M, key=abs)
  223. I_need = abs(max(EIY, key=abs))/(Ydop*E)
  224. W_need = abs(Mmax)/sigma_dop
  225. print("I_тр = {:g} м^4; W_тр = {:g} м^3".format(I_need, W_need))
  226. # Определим ширину прямоугольного сечения, для которого будут
  227. # выполняться условия прочности по нормальным и касательным напряжениям
  228. # и условие жесткости
  229. b_need_sigma = (6/hb_rel**2*W_need)**(1/3)
  230. b_need_tau = math.sqrt(2/hb_rel * abs(Qmax)/tau_dop)
  231. b_need_I = (I_need*12/hb_rel**3)**(1/4)
  232. b_need = max(b_need_sigma, b_need_tau, b_need_I)
  233. # Округлим до целых см в большую сторону.
  234. b_rect = math.ceil(b_need*100)/100
  235. h_rect = b_rect*hb_rel # высота сечения
  236. print("b_тр = {:g} м; b = {:g} м; h = {:g} м"\
  237. .format(b_need, b_rect, h_rect))
  238. # Для дальнейших вычислений будет удобнее задействовать
  239. # объектно-ориентированное программирование. Определим классы сечений в
  240. # виде прямоугольника и двутавра (упрощенного). Объекты этих классов
  241. # смогут выдавать свою ширину при заданной y, статический момент
  242. # относительно нейтральной оси части площади сечения, лежащей по одну
  243. # сторону от y, требующиеся для вычисления касательного напряжения по
  244. # формуле Журавского, и изображение, которое потребуется для эпюр
  245. # распределения τ и σ.
  246. class Profile:
  247. def __init__(self, b, h):
  248. self.b = b
  249. self.h = h
  250. self.Wx = b*h**2/6
  251. self.Ix = b*h**3/12
  252. self.A = b*h
  253. self.name = "Прямоугольник {}x{} м".format(b, h)
  254. def S(self, y):
  255. return self.b/2*(self.h**2/4 - y**2)
  256. def wid(self, y):
  257. return self.b
  258. def image(self):
  259. x = self.b/2
  260. y = self.h/2
  261. return ([x, -x, -x, x], [y, y, -y, -y])
  262. class Dvutavr(Profile):
  263. def __init__(self, l):
  264. self.h = l[1]
  265. self.b = l[2]
  266. self.d = l[3]
  267. self.t = l[4]
  268. self.A = l[5]
  269. self.Ix = l[6]
  270. self.Wx = l[7]
  271. self.Sx = l[8]
  272. self.name = "Двутавр №{}".format(l[0])
  273. def wid(self, y):
  274. # за пределами сечения возвращаем ненулевую ширину, чтобы не
  275. # было неопределенности 0/0
  276. if abs(y)>=self.h/2-self.t:
  277. return self.b
  278. else:
  279. return self.d
  280. def S(self, y):
  281. ycr = self.h/2-self.t
  282. if abs(y) >= ycr:
  283. return self.wid(y)/2*(self.h**2/4 - y**2)
  284. else:
  285. return self.Sx-(self.Sx-self.S(ycr))*(y/ycr)**2
  286. def image(self):
  287. y1 = self.h/2
  288. y2 = self.h/2-self.t
  289. x1 = self.b/2
  290. x2 = self.d/2
  291. return ([x1, -x1, -x1, -x2, -x2, -x1, -x1, x1, x1, x2, x2, x1],
  292. [y1, y1, y2, y2, -y2, -y2, -y1, -y1, -y2, -y2, y2, y2])
  293. # По сортаменту найдем двутавр, для которого будут выполняться условия
  294. # прочности и жесткости. Создадим объекты сечений, определим опасные
  295. # сечения (точнее, значения поперечных сил и изгибающих моментов в них)
  296. # и посчитаем нормальные и касательные напряжения в опасных сечениях.
  297. # Попутно проверим условие прочности.
  298. items = []
  299. for item in sortament:
  300. if item[7] >= W_need and item[6] >= I_need:
  301. items.append(Dvutavr(item))
  302. print("Выбран двутавр №{}".format(item[0]))
  303. break
  304. else:
  305. print("Не удалось найти подходящий двутавр по сортаменту")
  306. items.append(Profile(b_rect, h_rect))
  307. p_plots = []
  308. for item in items:
  309. tau = []
  310. sigma = []
  311. image = list(item.image())
  312. ym = item.h/2
  313. # для эпюр напряжений используем 243 точки.
  314. yp = np.linspace(-ym, ym, 243)
  315. for y in yp:
  316. tau.append(Qmax*item.S(y)/(item.Ix*item.wid(y)))
  317. sigma.append(-y*Mmax/item.Ix)
  318. p_plots.append((yp, image, sigma, tau))
  319. tau_max, sigma_max = max(tau, key=abs), max(sigma, key=abs)
  320. ok = abs(tau_max)<=tau_dop and abs(sigma_max)<=sigma_dop
  321. print("Условие прочности {}выполняется для сечения: {}"\
  322. .format("" if ok else "НЕ ", item.name))
  323. print("τ_max = {:.4g} Па; σ_max = {:.4g} Па"\
  324. .format(tau_max, sigma_max))
  325. # Построим эпюры распределения нормальных и касательных напряжений по
  326. # сечению. По горизонтальным осям масштаб одинаковый. Обратите внимание
  327. # на множители, применяемые к значениям по осям напряжений.
  328. fig2 = plt.figure(2, figsize=(10,10))
  329. axs = fig2.subplots(2, 3, sharey="all", sharex="col")
  330. for i, dat in enumerate(p_plots):
  331. ix, iy = dat[1][0], dat[1][1] # Изображение сечения
  332. axs[i, 0].fill(ix, iy)
  333. axs[i, 0].set_aspect(1)
  334. axs[i, 0].set_ylabel("y / м")
  335. axs[i, 0].set_xlabel("x / м")
  336. axs[i, 1].plot(dat[2], dat[0]) # нормальные напряжения
  337. axs[i, 1].axvline(color="black")
  338. axs[i, 1].set_xlabel("σ / Па")
  339. axs[i, 2].axvline(color="black") # касательные
  340. axs[i, 2].plot(dat[3], dat[0])
  341. axs[i, 2].set_xlabel("τ / Па")
  342. for j in range(3):
  343. axs[i, j].grid()
  344. fig2.savefig("profile.png")
  345. # Сосчитаем углы поворота сечений и прогиб. Проверим условие жесткости
  346. I = items[0].Ix
  347. T = []
  348. Y = []
  349. for x in X:
  350. Y.append(EIy(x)/(E*I))
  351. T.append(EIt(x)/(E*I))
  352. Ymax = max(Y, key=abs)
  353. print("Условие жесткости {}выполняется для сечения: {}"\
  354. .format("НЕ " if abs(Ymax)>Ydop else "", items[0].name))
  355. # Сосчитаем коэффициент экономии
  356. if len(items) > 1:
  357. econom = items[0].A/items[1].A
  358. print("Коэффициент экономии: {:.4f}".format(econom))
  359. # Наконец, построим эпюры поперечных сил, изгибающих моментов, углов
  360. # закручивания и прогибов.
  361. fig1 = plt.figure(1, figsize=(10,15))
  362. axs1 = fig1.subplots(4, 1)
  363. for ax, s in zip(axs1, [(Q, "Q / Н"), (M, "М / (Н*м)"),\
  364. (T, "θ"), (Y, "y / м")]):
  365. ax.plot(X, s[0])
  366. ax.set_ylabel(s[1])
  367. ax.set_xlabel("x / м")
  368. ax.axhline(color="black")
  369. ax.grid()
  370. fig1.savefig("balka.png")
  371. # Построим таблицу значений поперечных сил, изгибающих моментов, углов
  372. # поворота сечения и прогибов в зависимости от координаты:
  373. with open("x_data.txt", "w") as f:
  374. f.writelines(" x Q M theta y\r\n")
  375. for x, q, m, t, y in zip(X, Q, M, T, Y):
  376. msg = ""
  377. if q == Qmax:
  378. msg += "Qmax "
  379. if m == Mmax:
  380. msg += "Mmax"
  381. if y == Ymax:
  382. msg += "Ymax"
  383. f.writelines("{:8.4f}{:8.0f}{:12.2f}{:10.5f}{:10.5f} {}\r\n"\
  384. .format(x, q, m, t, y, msg))
  385. # Выведем таблицы распределения касательных напряжений по высоте
  386. # сечения. Сначала идет таблица для двутавра (если его удалось найти),
  387. # потом для прямоугольника.
  388. with open("y_data.txt", "w") as f:
  389. for prof, item in zip(p_plots, items):
  390. name = item.name
  391. f.writelines(" y tau === {} \r\n".format(name))
  392. for y, tau in zip(prof[0], prof[3]):
  393. f.writelines("{:8.4f}{:12.4e}\r\n".format(y, tau))
  394. print("Результаты расчета сохранены в {}".format(os.getcwd()))