main.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. import os
  2. import sys
  3. import comtypes.client
  4. import pandas as pd
  5. from shapely import points
  6. pd.set_option('future.no_silent_downcasting', True)
  7. import itertools
  8. import tkinter as tk
  9. from tkinter import *
  10. from tkinter import ttk, messagebox, scrolledtext
  11. from tkinter.filedialog import askopenfilename
  12. import math
  13. import numpy as np
  14. import warnings
  15. warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl')
  16. import threading
  17. import time
  18. nombre_archivo = "resultados1.txt"
  19. class TimeoutException(Exception):
  20. pass
  21. def raise_timeout():
  22. raise TimeoutException()
  23. timer = threading.Timer(20.0, raise_timeout) #se crea un timer de 20 segundos
  24. timer.start()
  25. try:
  26. #Conexion con SAP2000
  27. helper = comtypes.client.CreateObject('SAP2000v1.Helper')
  28. helper = helper.QueryInterface(comtypes.gen.SAP2000v1.cHelper)
  29. mySapObject = helper.GetObject("CSI.SAP2000.API.SapObject")
  30. SapModel = mySapObject.SapModel
  31. if SapModel.GetModelisLocked(): SapModel.SetModelisLocked(False)
  32. except TimeoutException as exc:
  33. messagebox.showerror(
  34. "Error",
  35. "No se encuentra una instancia de SAP2000 abierta. Por favor, abra SAP2000 e intente de nuevo."
  36. )
  37. sys.exit(1)
  38. finally:
  39. timer.cancel()
  40. class SAPSectionDesignerGUI:
  41. def __init__(self):
  42. self.root = tk.Tk()
  43. self.root.title("Diseñador de Secciones SAP2000 - Python API")
  44. self.root.geometry("600x800")
  45. self.SapModel = None
  46. self.crear_interfaz()
  47. def crear_interfaz(self):
  48. # === Datos de la sección ===
  49. tk.Label(self.root, text="Datos de la Sección", font=("Arial", 12, "bold")).pack(pady=(20,5))
  50. frame_datos = tk.Frame(self.root)
  51. frame_datos.pack(pady=5, padx=20, fill="x")
  52. con_ref = tk.BooleanVar(value=False)
  53. chk_con_ref = ttk.Checkbutton(frame_datos, text="Con Refuerzos verticales", variable=con_ref)
  54. chk_con_ref.pack(anchor="w")
  55. self.con_ref = con_ref
  56. # Frame para parámetros fijos
  57. frame_fixed = ttk.LabelFrame(self.root, text="Parámetros Fijos", padding=5)
  58. frame_fixed.pack(fill=tk.X, pady=(0, 10))
  59. # Variables para toggle fijo/barrido
  60. self.sweep_toggle_vars = {}
  61. self.sweep_fixed_entries = {}
  62. self.sweep_range_entries = {}
  63. params_ipe = ['H', 'b', 'tf', 'tw', 'e_ref', 'L_ref']
  64. for param in params_ipe:
  65. container = ttk.Frame(frame_fixed)
  66. container.pack(pady=2)
  67. var = tk.BooleanVar(value=True)
  68. self.sweep_toggle_vars[param] = var
  69. chk = ttk.Checkbutton(container, text=f"Fijo: {param}", variable=var,
  70. command=lambda p=param: self.on_sweep_toggle(p))
  71. chk.pack(side=tk.LEFT, fill=tk.X, expand=True)
  72. entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=10)
  73. entry.pack(side=tk.LEFT, padx=5)
  74. self.sweep_fixed_entries[param] = entry
  75. # Cargar valores actuales
  76. self.sweep_fixed_entries['H'].set("1.0")
  77. self.sweep_fixed_entries['b'].set("0.45")
  78. self.sweep_fixed_entries['tf'].set("0.01")
  79. self.sweep_fixed_entries['tw'].set("0.01")
  80. self.sweep_fixed_entries['e_ref'].set("0.01")
  81. self.sweep_fixed_entries['L_ref'].set("0.01")
  82. # Frame para parámetros a barrer
  83. frame_sweep = ttk.LabelFrame(self.root, text="Parámetros a Barrer", padding=5)
  84. frame_sweep.pack(fill=tk.X, pady=(0, 10))
  85. self.sweep_range_labels = {}
  86. self.sweep_range_entries = {}
  87. for param in params_ipe:
  88. container = ttk.Frame(frame_sweep)
  89. container.pack(fill=tk.X, pady=2)
  90. lbl = ttk.Label(container, text=f"{param}:", width=5)
  91. lbl.pack(side=tk.LEFT)
  92. self.sweep_range_labels[param] = lbl
  93. ttk.Label(container, text="min:").pack(side=tk.LEFT, padx=(10, 2))
  94. min_entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=8)
  95. min_entry.pack(side=tk.LEFT, padx=2)
  96. ttk.Label(container, text="max:").pack(side=tk.LEFT, padx=(10, 2))
  97. max_entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=8)
  98. max_entry.pack(side=tk.LEFT, padx=2)
  99. ttk.Label(container, text="pasos:").pack(side=tk.LEFT, padx=(10, 2))
  100. steps_entry = ttk.Spinbox(container, from_=2, to=20, increment=1, width=8)
  101. steps_entry.pack(side=tk.LEFT, padx=2)
  102. steps_entry.set(5)
  103. self.sweep_range_entries[param] = {
  104. 'min': min_entry,
  105. 'max': max_entry,
  106. 'steps': steps_entry
  107. }
  108. # Botones de control
  109. button_frame = ttk.Frame(self.root)
  110. button_frame.pack(fill=tk.X, pady=(10, 0))
  111. # === Botón Crear ===
  112. btn_crear = tk.Button(self.root, text="🚀 Crear Sección en Section Designer",
  113. command=self.execute_parametric_sweep, width=40, height=2,
  114. bg="#2196F3", fg="white", font=("Arial", 10, "bold"))
  115. btn_crear.pack(pady=20)
  116. tk.Label(self.root, text="Nota: Todas las unidades estan en [m].",
  117. fg="gray").pack()
  118. def on_sweep_toggle(self, param):
  119. """Alterna parámetro entre fijo y barrido"""
  120. is_fixed = self.sweep_toggle_vars[param].get()
  121. # Los rangos se mostrarán solo si no está fijo
  122. # Esta lógica se puede mejorar si es necesario
  123. def execute_parametric_sweep(self):
  124. """Ejecuta el barrido paramétrico"""
  125. if SapModel.GetModelisLocked(): SapModel.SetModelisLocked(False)
  126. try:
  127. # Recopilar configuración
  128. fixed_params = {}
  129. sweep_configs = {}
  130. for param in ['H', 'b', 'tf', 'tw', 'e_ref', 'L_ref'] if self.con_ref.get() else ['H', 'b', 'tf', 'tw']:
  131. is_fixed = self.sweep_toggle_vars[param].get()
  132. if is_fixed:
  133. value = float(self.sweep_fixed_entries[param].get())
  134. fixed_params[param] = value
  135. else:
  136. min_val = float(self.sweep_range_entries[param]['min'].get())
  137. max_val = float(self.sweep_range_entries[param]['max'].get())
  138. steps = int(self.sweep_range_entries[param]['steps'].get())
  139. if steps < 1:
  140. messagebox.showerror("Error", f"El número de pasos para {param} debe ser al menos 1")
  141. return
  142. if min_val <= 0:
  143. messagebox.showerror("Error", f"Valores de {param} deben ser positivos")
  144. return
  145. if min_val >= max_val:
  146. messagebox.showerror("Error", f"Rango inválido para {param}: min >= max")
  147. return
  148. sweep_configs[param] = {
  149. 'min': min_val,
  150. 'max': max_val,
  151. 'steps': steps
  152. }
  153. if not sweep_configs:
  154. messagebox.showerror("Error", "Debe barrer al menos un parámetro")
  155. return
  156. if not fixed_params:
  157. messagebox.showwarning("Advertencia", "No hay parámetros fijos")
  158. combinations = self.generar_matriz_sweep(sweep_configs, fixed_params)
  159. if not combinations:
  160. messagebox.showerror("Error", "No se generaron combinaciones para el barrido")
  161. return
  162. results = []
  163. f = open(nombre_archivo, "w")
  164. if self.con_ref.get():
  165. f.write("H\tb\ttf\ttw\te\tL\tFrecuencia\tCoef_Impacto\tFlecha_38D\tFlecha_86E\tFlecha_Media\tArea\tIxg\tIyg\tImax\tImin\tPeso\n")
  166. else:
  167. f.write("H\tb\ttf\ttw\tFrecuencia\tCoef_Impacto\tFlecha_38D\tFlecha_86E\tFlecha_Media\tArea\tIxg\tIyg\tImax\tImin\tPeso\n")
  168. for params in combinations:
  169. SapModel.SetModelisLocked(False)
  170. SapModel.SetPresentUnits(10) #unidades en niutons metros
  171. if self.con_ref.get():
  172. points_ipe = self.generate_ipe_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
  173. points_hueco_inf = self.generate_hueco_inf_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
  174. points_hueco_sup = self.generate_hueco_sup_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
  175. points_completo = self.generate_completo_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
  176. else:
  177. points_ipe = self.generate_ipe_points(params['H'], params['b'], params['tf'], params['tw'])
  178. points_hueco_inf = self.generate_hueco_inf_points(params['H'], params['b'], params['tf'], params['tw'])
  179. points_hueco_sup = self.generate_hueco_sup_points(params['H'], params['b'], params['tf'], params['tw'])
  180. points_completo = self.generate_completo_points(params['H'], params['b'], params['tf'], params['tw'])
  181. properties_ipe = self.calculate_section_properties(points_ipe)
  182. self.crear_seccion(points_ipe, tipo="ipe", nombre_poligono = "Polygon1")
  183. self.crear_seccion(points_hueco_inf, tipo="hueco", nombre_poligono = "Polygon1")
  184. self.crear_seccion(points_hueco_sup, tipo="hueco", nombre_poligono = "Polygon2")
  185. self.crear_seccion(points_completo, tipo="completo", nombre_poligono = "Polygon1")
  186. ret = SapModel.Analyze.RunAnalysis()
  187. frequency = self.obtain_frequency()
  188. coef_impacto = self.calc_coef_impacto(frequency)
  189. ret = SapModel.SetModelisLocked(False)
  190. ret = SapModel.LoadCases.StaticLinear.SetLoads("H3. Sobrecarga UIC 71", 1, ["Load"],
  191. ["H3. Sobrecarga UIC"], [coef_impacto])
  192. if ret[3] != 0:
  193. messagebox.showerror("Error de carga", f"Error al aplicar cargas para la combinación: {params}\nCódigo de error: {ret}")
  194. return
  195. ret = SapModel.Analyze.RunAnalysis()
  196. SapModel.SetPresentUnits(9) # Unidades niutons milimetos
  197. ret = SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput()
  198. ret = SapModel.Results.Setup.SetComboSelectedForOutput("3. ENV ELS CAR")
  199. FieldKeyList = []
  200. GroupName = 'All'
  201. TableVersion = 1
  202. FieldsKeysIncluded = []
  203. NumberRecords = 1
  204. TableData = []
  205. ret = SapModel.DatabaseTables.GetTableforDisplayArray("Joint Displacements", FieldKeyList, GroupName,
  206. TableVersion, FieldsKeysIncluded, NumberRecords, TableData)
  207. if ret[-1] != 0:
  208. messagebox.showerror("Error de resultados", f"Error al obtener resultados para la combinación: {params}\nCódigo de error: {ret}")
  209. return
  210. big_tuple = ret[4]
  211. items = list(big_tuple)
  212. filas = []
  213. j = 0
  214. while j < len(items):
  215. if j + 10 <= len(items):
  216. fila = items[j:j+10]
  217. filas.append(fila)
  218. j += 10
  219. else:
  220. break
  221. flecha1 = self.filtrar_por_joint(filas, "38D")
  222. flecha2 = self.filtrar_por_joint(filas, "86E")
  223. flecha_media = (flecha1 + flecha2) / 2
  224. f = open(nombre_archivo, "a")
  225. if self.con_ref.get():
  226. string = (f"{params['H'].item() if isinstance(params['H'], np.ndarray) else params['H']}\
  227. {params['b'].item() if isinstance(params['b'], np.ndarray) else params['b']}\
  228. {params['tf'].item() if isinstance(params['tf'], np.ndarray) else params['tf']}\
  229. {params['tw'].item() if isinstance(params['tw'], np.ndarray) else params['tw']}\
  230. {params['e_ref'].item() if isinstance(params['e_ref'], np.ndarray) else params['e_ref']}\
  231. {params['L_ref'].item() if isinstance(params['L_ref'], np.ndarray) else params['L_ref']}\
  232. {frequency}\
  233. {coef_impacto}\
  234. {flecha1}\
  235. {flecha2}\
  236. {flecha_media}\
  237. {properties_ipe['area'].item()}\
  238. {properties_ipe['ixg'].item()}\
  239. {properties_ipe['iyg'].item()}\
  240. {properties_ipe['imax'].item()}\
  241. {properties_ipe['imin'].item()}\
  242. {properties_ipe['peso'].item()}\n").replace(".", ",")
  243. else:
  244. string = (f"{params['H'].item() if isinstance(params['H'], np.ndarray) else params['H']}\
  245. {params['b'].item() if isinstance(params['b'], np.ndarray) else params['b']}\
  246. {params['tf'].item() if isinstance(params['tf'], np.ndarray) else params['tf']}\
  247. {params['tw'].item() if isinstance(params['tw'], np.ndarray) else params['tw']}\
  248. {frequency}\
  249. {coef_impacto}\
  250. {flecha1}\
  251. {flecha2}\
  252. {flecha_media}\
  253. {properties_ipe['area'].item()}\
  254. {properties_ipe['ixg'].item()}\
  255. {properties_ipe['iyg'].item()}\
  256. {properties_ipe['imax'].item()}\
  257. {properties_ipe['imin'].item()}\
  258. {properties_ipe['peso'].item()}\n").replace(".", ",")
  259. f.write(string)
  260. f.close()
  261. return
  262. except Exception as e:
  263. messagebox.showerror("Error en configuración", f"Revisa los valores ingresados:\n{e}")
  264. return
  265. def filtrar_por_joint(self, filas, joint_id):
  266. for fila in filas:
  267. if fila[0] == joint_id and fila[3] == "Min":
  268. return float(fila[6].replace(",", "."))
  269. return None
  270. def calc_coef_impacto(self, frecuencia):
  271. #calculo segun EC1.2 trenes reales
  272. L_phi = 15 # m
  273. v = 80 # km/h
  274. r = 1 # calidad de mantenimiento de la vía
  275. alpha = 1 if v/3.6 > 22 else v/(3.6*22)
  276. K = v/(3.6*L_phi*frecuencia)
  277. phi1 = 1.325 if K >= 0.76 else K/(1-K+K**4)
  278. phi2 = alpha*(56*math.exp(-((L_phi/10)**2))+50*(L_phi*frecuencia/80-1)*math.exp(-((L_phi/20)**2)))/100
  279. return 1.21 * (1 + phi1 + r * phi2) #aplicado el factor alpha
  280. def obtain_frequency(self):
  281. Num_results = 0
  282. LoadCase = ""
  283. Steptype = []
  284. Stepnum = []
  285. Period = []
  286. Ux = []
  287. Uy = []
  288. Uz = []
  289. SumUx = []
  290. SumUy = []
  291. SumUz = []
  292. Rx = []
  293. Ry = []
  294. Rz = []
  295. SumRx = []
  296. SumRy = []
  297. SumRz = []
  298. ret = SapModel.Results.ModalParticipatingMassRatios(Num_results, LoadCase, Steptype, Stepnum, Period,
  299. Ux, Uy, Uz, SumUx, SumUy, SumUz,
  300. Rx, Ry, Rz, SumRx, SumRy, SumRz)
  301. if ret[17] != 0:
  302. messagebox.showerror("Error al obtener frecuencias", f"Código de error: {ret}")
  303. return None
  304. return 1/ret[4][min(ret[7].index(max(ret[7])), ret[12].index(max(ret[12])))]
  305. def calculate_section_properties(self, points):
  306. """Calcula propiedades de la sección a partir de puntos"""
  307. # Constantes de material
  308. DENS = 7850 # kg/m3
  309. FY = 355 # MPa
  310. GAMMAS = 1.05
  311. EYOUNG = 210000 # MPa
  312. NU = 0.3
  313. FYD = FY * 10**6 / GAMMAS
  314. npuntos = len(points)
  315. px = points[:, 0]
  316. py = points[:, 1]
  317. bmax = np.amax(px) - np.amin(px)
  318. hmax = np.amax(py) - np.amin(py)
  319. # Perímetro
  320. long_i = np.zeros(npuntos - 1)
  321. for i in range(npuntos - 1):
  322. long_i[i] = ((points[i+1, 0] - points[i, 0])**2 + (points[i+1, 1] - points[i, 1])**2)**(1/2)
  323. perimetro = abs(sum(long_i))
  324. # Área
  325. area_i = np.zeros(npuntos - 1)
  326. for i in range(npuntos - 1):
  327. area_i[i] = (points[i+1, 0] - points[i, 0]) * (points[i+1, 1] + points[i, 1]) / 2
  328. area = abs(sum(area_i))
  329. # Peso
  330. peso = area * DENS
  331. # Centro de gravedad
  332. cdg_i = np.zeros([npuntos, 2])
  333. for i in range(npuntos - 1):
  334. h1 = points[i, 1]
  335. h2 = points[i+1, 1]
  336. b = points[i+1, 0] - points[i, 0]
  337. d = points[i, 0]
  338. if h1 + h2 == 0:
  339. cdg_i[i, 1] = 0
  340. else:
  341. cdg_i[i, 1] = 1/3 * (h1*h1 + h1*h2 + h2*h2) / (h1 + h2)
  342. if h1 + h2 == 0:
  343. cdg_i[i, 0] = d + b/2
  344. else:
  345. cdg_i[i, 0] = d + b/3 * (h1 + 2*h2) / (h1 + h2)
  346. statico_i = np.zeros([npuntos, 2])
  347. for i in range(npuntos - 1):
  348. statico_i[i, 1] = area_i[i] * cdg_i[i, 1]
  349. statico_i[i, 0] = area_i[i] * cdg_i[i, 0]
  350. cdg = sum(statico_i) / sum(area_i)
  351. xg = cdg[0]
  352. yg = cdg[1]
  353. # Fibras más alejadas
  354. v1y = np.amax(py) - yg
  355. v2y = np.amin(py) - yg
  356. v1x = np.amax(px) - xg
  357. v2x = np.amin(px) - xg
  358. # Momentos de inercia
  359. inercia_i = np.zeros([npuntos, 3])
  360. for i in range(npuntos - 1):
  361. h1 = points[i, 1]
  362. h2 = points[i+1, 1]
  363. b = points[i+1, 0] - points[i, 0]
  364. d = points[i, 0]
  365. xgi = cdg_i[i, 0]
  366. ygi = cdg_i[i, 1]
  367. ai = area_i[i]
  368. if h2 >= h1:
  369. ixcuad_G_local = 1/12 * b * (h1**3) + b * h1 * (h1/2 - ygi)**2
  370. ixtriang_G_loc = 1/36 * b * (h2-h1)**3 + 1/2 * b * (h2-h1) * ((2*h1+h2)/3 - ygi)**2
  371. else:
  372. ixcuad_G_local = 1/12 * b * (h2**3) + b * h2 * (h2/2 - ygi)**2
  373. ixtriang_G_loc = 1/36 * b * (h1-h2)**3 + 1/2 * b * (h1-h2) * ((2*h2+h1)/3 - ygi)**2
  374. inercia_i[i, 0] = ixcuad_G_local + ixtriang_G_loc + ai * (yg - ygi)**2
  375. if h2 >= h1:
  376. iycuad = 1/12 * h1 * b**3 + h1 * b * (b/2 + d - xgi)**2
  377. iytrian = 1/36 * (h2-h1) * b**3 + 1/2 * b * (h2-h1) * (2/3*b + d - xgi)**2
  378. else:
  379. iycuad = 1/12 * h2 * b**3 + h2 * b * (b/2 + d - xgi)**2
  380. iytrian = 1/36 * (h1-h2) * b**3 + 1/2 * b * (h1-h2) * (1/3*b + d - xgi)**2
  381. inercia_i[i, 1] = iycuad + iytrian + ai * (xg - xgi)**2
  382. if h2 >= h1:
  383. pxygcuadrado = b * h1 * (-h1/2 + ygi) * (-d - b/2 + xgi)
  384. pxytriangulo = b*b * (h2-h1)**2 / 72 + b * (h2-h1) / 2 * (-(h2-h1)/3 - h1 + ygi) * (-d - 2/3*b + xgi)
  385. else:
  386. pxygcuadrado = b * h2 * (-h2/2 + ygi) * (-d - b/2 + xgi)
  387. pxytriangulo = -b*b * (h1-h2)**2 / 72 + b * (h1-h2) / 2 * (-(h1-h2)/3 - h2 + ygi) * (-d - 1/3*b + xgi)
  388. inercia_i[i, 2] = pxygcuadrado + pxytriangulo + ai * (-xg + xgi) * (-yg + ygi)
  389. ig = sum(inercia_i)
  390. ixg = abs(ig[0])
  391. iyg = abs(ig[1])
  392. pxyg = ig[2]
  393. if sum(area_i) >= 0:
  394. pxyg = pxyg
  395. else:
  396. pxyg = -pxyg
  397. # Radios de giro
  398. rx = (ixg / area)**0.5
  399. ry = (iyg / area)**0.5
  400. # Ejes principales
  401. ic = (ixg + iyg) / 2
  402. ir = (((ixg - iyg)/2)**2 + pxyg**2)**0.5
  403. imax = ic + ir
  404. imin = ic - ir
  405. rmax = (imax / area)**0.5
  406. rmin = (imin / area)**0.5
  407. # Conversión a unidades prácticas (cm, cm2, cm3, cm4, kN)
  408. pot = 2
  409. area_cm2 = area * 10**(pot*2)
  410. ixg_cm4 = ixg * 10**(pot*4)
  411. iyg_cm4 = iyg * 10**(pot*4)
  412. imax_cm4 = imax * 10**(pot*4)
  413. imin_cm4 = imin * 10**(pot*4)
  414. return {
  415. 'area': area_cm2,
  416. 'ixg': ixg_cm4,
  417. 'iyg': iyg_cm4,
  418. 'imax': imax_cm4,
  419. 'imin': imin_cm4,
  420. 'peso': peso,
  421. }
  422. def generate_completo_points(self, H, b, tf, tw):
  423. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  424. h_alma = H - 2*tf
  425. x_alma_ini = (b - .3) / 2
  426. x_alma_fin = (b + .3) / 2
  427. points = np.array([
  428. [0, 0],
  429. [b, 0],
  430. [b, tf],
  431. [x_alma_fin, 0.169],
  432. [x_alma_fin, H - .169],
  433. [b, H - tf],
  434. [b, H],
  435. [0, H],
  436. [0, H - tf],
  437. [x_alma_ini, H - .169],
  438. [x_alma_ini, 0.169],
  439. [0, tf],
  440. ])
  441. return points
  442. def generate_completo_points_ref(self, H, b, tf, tw, e_ref, L_ref):
  443. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  444. h_alma = H - 2*tf
  445. x_alma_ini = (b - .3) / 2
  446. x_alma_fin = (b + .3) / 2
  447. x = (0.169 - tf) * e_ref / (b - x_alma_fin) + tf
  448. points = np.array([
  449. [0, 0],
  450. [b, 0],
  451. [b, L_ref],
  452. [b - e_ref, L_ref],
  453. [b - e_ref, x],
  454. [x_alma_fin, 0.169],
  455. [x_alma_fin, H - .169],
  456. [b - e_ref, H - x],
  457. [b - e_ref, H - L_ref],
  458. [b, H - L_ref],
  459. [b, H],
  460. [0, H],
  461. [0, H - L_ref],
  462. [e_ref, H - L_ref],
  463. [e_ref, H - x],
  464. [x_alma_ini, H - 0.169],
  465. [x_alma_ini, 0.169],
  466. [e_ref, x],
  467. [e_ref, L_ref],
  468. [0, L_ref],
  469. ])
  470. return points
  471. def generate_hueco_inf_points(self, H, b, tf, tw):
  472. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  473. h_alma = H - 2*tf
  474. x_alma_ini = (b - tw) / 2
  475. x_alma_fin = (b + tw) / 2
  476. points = np.array([
  477. [0, 0],
  478. [b, 0],
  479. [b, tf],
  480. [x_alma_fin, tf],
  481. [x_alma_fin, H -.36 - .169],
  482. [x_alma_ini, H - .36 - .169],
  483. [x_alma_ini, tf],
  484. [0, tf],
  485. ])
  486. return points
  487. def generate_hueco_inf_points_ref(self, H, b, tf, tw, e_ref, L_ref):
  488. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  489. h_alma = H - 2*tf
  490. x_alma_ini = (b - tw) / 2
  491. x_alma_fin = (b + tw) / 2
  492. points = np.array([
  493. [0, 0],
  494. [b, 0],
  495. [b, L_ref],
  496. [b - e_ref, L_ref],
  497. [b - e_ref, tf],
  498. [x_alma_fin, tf],
  499. [x_alma_fin, H -.36 - .169],
  500. [x_alma_ini, H - .36 - .169],
  501. [x_alma_ini, tf],
  502. [e_ref, tf],
  503. [e_ref, L_ref],
  504. [0, L_ref],
  505. ])
  506. return points
  507. def generate_hueco_sup_points(self, H, b, tf, tw):
  508. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  509. h_alma = H - 2*tf
  510. x_alma_ini = (b - tw) / 2
  511. x_alma_fin = (b + tw) / 2
  512. points = np.array([
  513. [x_alma_fin, H - tf - .169],
  514. [x_alma_fin, H - tf],
  515. [b, H - tf],
  516. [b, H],
  517. [0, H],
  518. [0, H - tf],
  519. [x_alma_ini, H - tf],
  520. [x_alma_ini, H - tf - .169],
  521. ])
  522. return points
  523. def generate_hueco_sup_points_ref(self, H, b, tf, tw, e_ref, L_ref):
  524. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  525. h_alma = H - 2*tf
  526. x_alma_ini = (b - tw) / 2
  527. x_alma_fin = (b + tw) / 2
  528. points = np.array([
  529. [x_alma_fin, H - .169],
  530. [x_alma_fin, H - tf],
  531. [b - e_ref, H - tf],
  532. [b - e_ref, H - L_ref],
  533. [b, H - L_ref],
  534. [b, H],
  535. [0, H],
  536. [0, H - L_ref],
  537. [e_ref, H - L_ref],
  538. [e_ref, H - tf],
  539. [x_alma_ini, H - tf],
  540. [x_alma_ini, H - .169],
  541. ])
  542. return points
  543. def generate_ipe_points(self, H, b, tf, tw):
  544. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  545. h_alma = H - 2*tf
  546. x_alma_ini = (b - tw) / 2
  547. x_alma_fin = (b + tw) / 2
  548. points = np.array([
  549. [0, 0],
  550. [b, 0],
  551. [b, tf],
  552. [x_alma_fin, tf],
  553. [x_alma_fin, H - tf],
  554. [b, H - tf],
  555. [b, H],
  556. [0, H],
  557. [0, H - tf],
  558. [x_alma_ini, H - tf],
  559. [x_alma_ini, tf],
  560. [0, tf],
  561. ])
  562. return points
  563. def generate_ipe_points_ref(self, H, b, tf, tw, e_ref, L_ref):
  564. """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
  565. h_alma = H - 2*tf
  566. x_alma_ini = (b - tw) / 2
  567. x_alma_fin = (b + tw) / 2
  568. points = np.array([
  569. [0, 0],
  570. [b, 0],
  571. [b, L_ref],
  572. [b - e_ref, L_ref],
  573. [b - e_ref, tf],
  574. [x_alma_fin, tf],
  575. [x_alma_fin, H - tf],
  576. [b - e_ref, H - tf],
  577. [b - e_ref, H - L_ref],
  578. [b, H - L_ref],
  579. [b, H],
  580. [0, H],
  581. [0, H - L_ref],
  582. [e_ref, H - L_ref],
  583. [e_ref, H - tf],
  584. [x_alma_ini, H - tf],
  585. [x_alma_ini, tf],
  586. [e_ref, tf],
  587. [e_ref, L_ref],
  588. [0, L_ref],
  589. ])
  590. return points
  591. def _valid_geometry(self, params):
  592. """Valida que la combinación de parámetros genere una geometría válida"""
  593. H = params.get('H', 0)
  594. b = params.get('b', 0)
  595. tf = params.get('tf', 0)
  596. tw = params.get('tw', 0)
  597. if H <= 0 or b <= 0 or tf <= 0 or tw <= 0:
  598. return False
  599. if H < 0.360 + 0.169 + tf:
  600. return False
  601. if b < 0.3:
  602. return False
  603. if tf > H/2 or tf > 0.169:
  604. return False
  605. if tw > b:
  606. return False
  607. if self.con_ref.get():
  608. e_ref = params.get('e_ref', 0)
  609. L_ref = params.get('L_ref', 0)
  610. if e_ref > 0.3:
  611. return False
  612. if L_ref > 0.169:
  613. return False
  614. if L_ref < tf:
  615. return False
  616. return True
  617. def generar_matriz_sweep(self, sweep_configs, fixed_params):
  618. sweep_params = list(sweep_configs.keys())
  619. param_values = {}
  620. for param in sweep_params:
  621. config = sweep_configs[param]
  622. values = np.linspace(config['min'], config['max'], config['steps'])
  623. param_values[param] = values
  624. combinations = []
  625. for combo in itertools.product(*param_values.values()):
  626. params = fixed_params.copy()
  627. for i, param in enumerate(sweep_params):
  628. params[param] = combo[i]
  629. if self._valid_geometry(params):
  630. combinations.append(params)
  631. return combinations
  632. def crear_seccion(self, puntos = [], tipo="ipe", nombre_poligono="Nueva sección"):
  633. material = "S355"
  634. if not len(puntos) or not tipo:
  635. messagebox.showerror("Error", "Faltan puntos o tipos de sección")
  636. return
  637. # Leer coordenadas
  638. try:
  639. X = [float(p[0]) for p in puntos]
  640. Y = [float(p[1]) for p in puntos]
  641. num_points = len(X)
  642. Radius = [0.0] * num_points
  643. if num_points < 3:
  644. raise ValueError("Se necesitan al menos 3 puntos")
  645. except Exception as e:
  646. messagebox.showerror("Error en coordenadas", str(e))
  647. return
  648. if tipo == "hueco":
  649. nombre = "hueca"
  650. elif tipo == "completo":
  651. nombre = "rigida"
  652. elif tipo == "ipe":
  653. nombre = "entera"
  654. else:
  655. messagebox.showerror("Error", f"Tipo de sección desconocido: {tipo}")
  656. return
  657. try:
  658. ret = SapModel.PropFrame.SetSDSection(
  659. nombre, material, 1, -1, "", "Default"
  660. )
  661. if not (ret == 0 or ret == 1): # 1 = sección ya existe, lo cual está bien
  662. messagebox.showerror("Error SetSDSection", f"Código: {ret}")
  663. return
  664. if not (nombre == "hueca" and nombre_poligono == "Polygon2"): #si es la sección hueca, el polígono 2 es el hueco, no se borra
  665. ret = SapModel.PropFrame.SDShape.Delete(
  666. nombre,
  667. "",
  668. True
  669. )
  670. if ret != 0 :
  671. messagebox.showerror("Error Delete", f"Código: {ret}")
  672. return
  673. ret = SapModel.PropFrame.SDShape.SetPolygon(
  674. nombre, # Nombre de la sección SD
  675. nombre_poligono, # Nombre del shape
  676. material, # Material
  677. "Default",
  678. num_points, # Número de puntos
  679. X, # Lista normal de Python (X)
  680. Y, # Lista normal (Y)
  681. Radius, # Lista normal (Radius)
  682. -1, # Color
  683. False,
  684. ""
  685. )
  686. if ret[4] != 0:
  687. messagebox.showerror("Error SAP",
  688. f"SetPolygon devolvió código de error: {ret}\n\nRevisa que el material exista y las coordenadas sean correctas.")
  689. except Exception as e:
  690. messagebox.showerror("Error SAP", str(e))
  691. def run(self):
  692. self.root.mainloop()
  693. if __name__ == "__main__":
  694. app = SAPSectionDesignerGUI()
  695. app.run()