| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- import os
- import sys
- import comtypes.client
- import pandas as pd
- from shapely import points
- pd.set_option('future.no_silent_downcasting', True)
- import itertools
- import tkinter as tk
- from tkinter import *
- from tkinter import ttk, messagebox, scrolledtext
- from tkinter.filedialog import askopenfilename
- import math
- import numpy as np
- import warnings
- warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl')
- import threading
- import time
- nombre_archivo = "resultados1.txt"
- class TimeoutException(Exception):
- pass
- def raise_timeout():
- raise TimeoutException()
- timer = threading.Timer(20.0, raise_timeout) #se crea un timer de 20 segundos
- timer.start()
- try:
- #Conexion con SAP2000
- helper = comtypes.client.CreateObject('SAP2000v1.Helper')
- helper = helper.QueryInterface(comtypes.gen.SAP2000v1.cHelper)
- mySapObject = helper.GetObject("CSI.SAP2000.API.SapObject")
- SapModel = mySapObject.SapModel
- if SapModel.GetModelisLocked(): SapModel.SetModelisLocked(False)
- except TimeoutException as exc:
- messagebox.showerror(
- "Error",
- "No se encuentra una instancia de SAP2000 abierta. Por favor, abra SAP2000 e intente de nuevo."
- )
- sys.exit(1)
- finally:
- timer.cancel()
- class SAPSectionDesignerGUI:
- def __init__(self):
- self.root = tk.Tk()
- self.root.title("Diseñador de Secciones SAP2000 - Python API")
- self.root.geometry("600x800")
-
- self.SapModel = None
-
- self.crear_interfaz()
-
- def crear_interfaz(self):
- # === Datos de la sección ===
- tk.Label(self.root, text="Datos de la Sección", font=("Arial", 12, "bold")).pack(pady=(20,5))
-
- frame_datos = tk.Frame(self.root)
- frame_datos.pack(pady=5, padx=20, fill="x")
- con_ref = tk.BooleanVar(value=False)
- chk_con_ref = ttk.Checkbutton(frame_datos, text="Con Refuerzos verticales", variable=con_ref)
- chk_con_ref.pack(anchor="w")
- self.con_ref = con_ref
- # Frame para parámetros fijos
- frame_fixed = ttk.LabelFrame(self.root, text="Parámetros Fijos", padding=5)
- frame_fixed.pack(fill=tk.X, pady=(0, 10))
- # Variables para toggle fijo/barrido
- self.sweep_toggle_vars = {}
- self.sweep_fixed_entries = {}
- self.sweep_range_entries = {}
- params_ipe = ['H', 'b', 'tf', 'tw', 'e_ref', 'L_ref']
- for param in params_ipe:
- container = ttk.Frame(frame_fixed)
- container.pack(pady=2)
- var = tk.BooleanVar(value=True)
- self.sweep_toggle_vars[param] = var
- chk = ttk.Checkbutton(container, text=f"Fijo: {param}", variable=var,
- command=lambda p=param: self.on_sweep_toggle(p))
- chk.pack(side=tk.LEFT, fill=tk.X, expand=True)
- entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=10)
- entry.pack(side=tk.LEFT, padx=5)
- self.sweep_fixed_entries[param] = entry
- # Cargar valores actuales
- self.sweep_fixed_entries['H'].set("1.0")
- self.sweep_fixed_entries['b'].set("0.45")
- self.sweep_fixed_entries['tf'].set("0.01")
- self.sweep_fixed_entries['tw'].set("0.01")
- self.sweep_fixed_entries['e_ref'].set("0.01")
- self.sweep_fixed_entries['L_ref'].set("0.01")
- # Frame para parámetros a barrer
- frame_sweep = ttk.LabelFrame(self.root, text="Parámetros a Barrer", padding=5)
- frame_sweep.pack(fill=tk.X, pady=(0, 10))
- self.sweep_range_labels = {}
- self.sweep_range_entries = {}
- for param in params_ipe:
- container = ttk.Frame(frame_sweep)
- container.pack(fill=tk.X, pady=2)
- lbl = ttk.Label(container, text=f"{param}:", width=5)
- lbl.pack(side=tk.LEFT)
- self.sweep_range_labels[param] = lbl
- ttk.Label(container, text="min:").pack(side=tk.LEFT, padx=(10, 2))
- min_entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=8)
- min_entry.pack(side=tk.LEFT, padx=2)
- ttk.Label(container, text="max:").pack(side=tk.LEFT, padx=(10, 2))
- max_entry = ttk.Spinbox(container, from_=0.0, to=1000.0, increment=0.001, width=8)
- max_entry.pack(side=tk.LEFT, padx=2)
- ttk.Label(container, text="pasos:").pack(side=tk.LEFT, padx=(10, 2))
- steps_entry = ttk.Spinbox(container, from_=2, to=20, increment=1, width=8)
- steps_entry.pack(side=tk.LEFT, padx=2)
- steps_entry.set(5)
- self.sweep_range_entries[param] = {
- 'min': min_entry,
- 'max': max_entry,
- 'steps': steps_entry
- }
- # Botones de control
- button_frame = ttk.Frame(self.root)
- button_frame.pack(fill=tk.X, pady=(10, 0))
- # === Botón Crear ===
- btn_crear = tk.Button(self.root, text="🚀 Crear Sección en Section Designer",
- command=self.execute_parametric_sweep, width=40, height=2,
- bg="#2196F3", fg="white", font=("Arial", 10, "bold"))
- btn_crear.pack(pady=20)
-
- tk.Label(self.root, text="Nota: Todas las unidades estan en [m].",
- fg="gray").pack()
-
- def on_sweep_toggle(self, param):
- """Alterna parámetro entre fijo y barrido"""
- is_fixed = self.sweep_toggle_vars[param].get()
- # Los rangos se mostrarán solo si no está fijo
- # Esta lógica se puede mejorar si es necesario
- def execute_parametric_sweep(self):
- """Ejecuta el barrido paramétrico"""
- if SapModel.GetModelisLocked(): SapModel.SetModelisLocked(False)
-
- try:
- # Recopilar configuración
- fixed_params = {}
- sweep_configs = {}
-
- for param in ['H', 'b', 'tf', 'tw', 'e_ref', 'L_ref'] if self.con_ref.get() else ['H', 'b', 'tf', 'tw']:
- is_fixed = self.sweep_toggle_vars[param].get()
-
- if is_fixed:
- value = float(self.sweep_fixed_entries[param].get())
- fixed_params[param] = value
- else:
- min_val = float(self.sweep_range_entries[param]['min'].get())
- max_val = float(self.sweep_range_entries[param]['max'].get())
- steps = int(self.sweep_range_entries[param]['steps'].get())
- if steps < 1:
- messagebox.showerror("Error", f"El número de pasos para {param} debe ser al menos 1")
- return
- if min_val <= 0:
- messagebox.showerror("Error", f"Valores de {param} deben ser positivos")
- return
-
- if min_val >= max_val:
- messagebox.showerror("Error", f"Rango inválido para {param}: min >= max")
- return
-
- sweep_configs[param] = {
- 'min': min_val,
- 'max': max_val,
- 'steps': steps
- }
-
-
- if not sweep_configs:
- messagebox.showerror("Error", "Debe barrer al menos un parámetro")
- return
-
- if not fixed_params:
- messagebox.showwarning("Advertencia", "No hay parámetros fijos")
-
- combinations = self.generar_matriz_sweep(sweep_configs, fixed_params)
-
- if not combinations:
- messagebox.showerror("Error", "No se generaron combinaciones para el barrido")
- return
-
- results = []
- f = open(nombre_archivo, "w")
- if self.con_ref.get():
- f.write("H\tb\ttf\ttw\te\tL\tFrecuencia\tCoef_Impacto\tFlecha_38D\tFlecha_86E\tFlecha_Media\tArea\tIxg\tIyg\tImax\tImin\tPeso\n")
- else:
- f.write("H\tb\ttf\ttw\tFrecuencia\tCoef_Impacto\tFlecha_38D\tFlecha_86E\tFlecha_Media\tArea\tIxg\tIyg\tImax\tImin\tPeso\n")
-
- for params in combinations:
- SapModel.SetModelisLocked(False)
- SapModel.SetPresentUnits(10) #unidades en niutons metros
- if self.con_ref.get():
- points_ipe = self.generate_ipe_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
- points_hueco_inf = self.generate_hueco_inf_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
- points_hueco_sup = self.generate_hueco_sup_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
- points_completo = self.generate_completo_points_ref(params['H'], params['b'], params['tf'], params['tw'], params['e_ref'], params['L_ref'])
- else:
- points_ipe = self.generate_ipe_points(params['H'], params['b'], params['tf'], params['tw'])
- points_hueco_inf = self.generate_hueco_inf_points(params['H'], params['b'], params['tf'], params['tw'])
- points_hueco_sup = self.generate_hueco_sup_points(params['H'], params['b'], params['tf'], params['tw'])
- points_completo = self.generate_completo_points(params['H'], params['b'], params['tf'], params['tw'])
-
- properties_ipe = self.calculate_section_properties(points_ipe)
- self.crear_seccion(points_ipe, tipo="ipe", nombre_poligono = "Polygon1")
- self.crear_seccion(points_hueco_inf, tipo="hueco", nombre_poligono = "Polygon1")
- self.crear_seccion(points_hueco_sup, tipo="hueco", nombre_poligono = "Polygon2")
- self.crear_seccion(points_completo, tipo="completo", nombre_poligono = "Polygon1")
-
- ret = SapModel.Analyze.RunAnalysis()
- frequency = self.obtain_frequency()
- coef_impacto = self.calc_coef_impacto(frequency)
-
- ret = SapModel.SetModelisLocked(False)
-
- ret = SapModel.LoadCases.StaticLinear.SetLoads("H3. Sobrecarga UIC 71", 1, ["Load"],
- ["H3. Sobrecarga UIC"], [coef_impacto])
-
- if ret[3] != 0:
- messagebox.showerror("Error de carga", f"Error al aplicar cargas para la combinación: {params}\nCódigo de error: {ret}")
- return
-
- ret = SapModel.Analyze.RunAnalysis()
- SapModel.SetPresentUnits(9) # Unidades niutons milimetos
- ret = SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput()
- ret = SapModel.Results.Setup.SetComboSelectedForOutput("3. ENV ELS CAR")
- FieldKeyList = []
- GroupName = 'All'
- TableVersion = 1
- FieldsKeysIncluded = []
- NumberRecords = 1
- TableData = []
- ret = SapModel.DatabaseTables.GetTableforDisplayArray("Joint Displacements", FieldKeyList, GroupName,
- TableVersion, FieldsKeysIncluded, NumberRecords, TableData)
-
- if ret[-1] != 0:
- messagebox.showerror("Error de resultados", f"Error al obtener resultados para la combinación: {params}\nCódigo de error: {ret}")
- return
-
- big_tuple = ret[4]
- items = list(big_tuple)
-
- filas = []
- j = 0
- while j < len(items):
- if j + 10 <= len(items):
- fila = items[j:j+10]
- filas.append(fila)
- j += 10
- else:
- break
- flecha1 = self.filtrar_por_joint(filas, "38D")
- flecha2 = self.filtrar_por_joint(filas, "86E")
- flecha_media = (flecha1 + flecha2) / 2
-
- f = open(nombre_archivo, "a")
- if self.con_ref.get():
- string = (f"{params['H'].item() if isinstance(params['H'], np.ndarray) else params['H']}\
- {params['b'].item() if isinstance(params['b'], np.ndarray) else params['b']}\
- {params['tf'].item() if isinstance(params['tf'], np.ndarray) else params['tf']}\
- {params['tw'].item() if isinstance(params['tw'], np.ndarray) else params['tw']}\
- {params['e_ref'].item() if isinstance(params['e_ref'], np.ndarray) else params['e_ref']}\
- {params['L_ref'].item() if isinstance(params['L_ref'], np.ndarray) else params['L_ref']}\
- {frequency}\
- {coef_impacto}\
- {flecha1}\
- {flecha2}\
- {flecha_media}\
- {properties_ipe['area'].item()}\
- {properties_ipe['ixg'].item()}\
- {properties_ipe['iyg'].item()}\
- {properties_ipe['imax'].item()}\
- {properties_ipe['imin'].item()}\
- {properties_ipe['peso'].item()}\n").replace(".", ",")
- else:
- string = (f"{params['H'].item() if isinstance(params['H'], np.ndarray) else params['H']}\
- {params['b'].item() if isinstance(params['b'], np.ndarray) else params['b']}\
- {params['tf'].item() if isinstance(params['tf'], np.ndarray) else params['tf']}\
- {params['tw'].item() if isinstance(params['tw'], np.ndarray) else params['tw']}\
- {frequency}\
- {coef_impacto}\
- {flecha1}\
- {flecha2}\
- {flecha_media}\
- {properties_ipe['area'].item()}\
- {properties_ipe['ixg'].item()}\
- {properties_ipe['iyg'].item()}\
- {properties_ipe['imax'].item()}\
- {properties_ipe['imin'].item()}\
- {properties_ipe['peso'].item()}\n").replace(".", ",")
- f.write(string)
- f.close()
-
-
- return
-
- except Exception as e:
- messagebox.showerror("Error en configuración", f"Revisa los valores ingresados:\n{e}")
- return
-
-
- def filtrar_por_joint(self, filas, joint_id):
- for fila in filas:
- if fila[0] == joint_id and fila[3] == "Min":
- return float(fila[6].replace(",", "."))
-
- return None
-
-
-
- def calc_coef_impacto(self, frecuencia):
- #calculo segun EC1.2 trenes reales
- L_phi = 15 # m
- v = 80 # km/h
- r = 1 # calidad de mantenimiento de la vía
- alpha = 1 if v/3.6 > 22 else v/(3.6*22)
- K = v/(3.6*L_phi*frecuencia)
- phi1 = 1.325 if K >= 0.76 else K/(1-K+K**4)
- phi2 = alpha*(56*math.exp(-((L_phi/10)**2))+50*(L_phi*frecuencia/80-1)*math.exp(-((L_phi/20)**2)))/100
- return 1.21 * (1 + phi1 + r * phi2) #aplicado el factor alpha
- def obtain_frequency(self):
- Num_results = 0
- LoadCase = ""
- Steptype = []
- Stepnum = []
- Period = []
- Ux = []
- Uy = []
- Uz = []
- SumUx = []
- SumUy = []
- SumUz = []
- Rx = []
- Ry = []
- Rz = []
- SumRx = []
- SumRy = []
- SumRz = []
- ret = SapModel.Results.ModalParticipatingMassRatios(Num_results, LoadCase, Steptype, Stepnum, Period,
- Ux, Uy, Uz, SumUx, SumUy, SumUz,
- Rx, Ry, Rz, SumRx, SumRy, SumRz)
-
- if ret[17] != 0:
- messagebox.showerror("Error al obtener frecuencias", f"Código de error: {ret}")
- return None
-
- return 1/ret[4][min(ret[7].index(max(ret[7])), ret[12].index(max(ret[12])))]
- def calculate_section_properties(self, points):
- """Calcula propiedades de la sección a partir de puntos"""
- # Constantes de material
- DENS = 7850 # kg/m3
- FY = 355 # MPa
- GAMMAS = 1.05
- EYOUNG = 210000 # MPa
- NU = 0.3
- FYD = FY * 10**6 / GAMMAS
- npuntos = len(points)
- px = points[:, 0]
- py = points[:, 1]
- bmax = np.amax(px) - np.amin(px)
- hmax = np.amax(py) - np.amin(py)
- # Perímetro
- long_i = np.zeros(npuntos - 1)
- for i in range(npuntos - 1):
- long_i[i] = ((points[i+1, 0] - points[i, 0])**2 + (points[i+1, 1] - points[i, 1])**2)**(1/2)
- perimetro = abs(sum(long_i))
- # Área
- area_i = np.zeros(npuntos - 1)
- for i in range(npuntos - 1):
- area_i[i] = (points[i+1, 0] - points[i, 0]) * (points[i+1, 1] + points[i, 1]) / 2
- area = abs(sum(area_i))
- # Peso
- peso = area * DENS
- # Centro de gravedad
- cdg_i = np.zeros([npuntos, 2])
- for i in range(npuntos - 1):
- h1 = points[i, 1]
- h2 = points[i+1, 1]
- b = points[i+1, 0] - points[i, 0]
- d = points[i, 0]
- if h1 + h2 == 0:
- cdg_i[i, 1] = 0
- else:
- cdg_i[i, 1] = 1/3 * (h1*h1 + h1*h2 + h2*h2) / (h1 + h2)
- if h1 + h2 == 0:
- cdg_i[i, 0] = d + b/2
- else:
- cdg_i[i, 0] = d + b/3 * (h1 + 2*h2) / (h1 + h2)
- statico_i = np.zeros([npuntos, 2])
- for i in range(npuntos - 1):
- statico_i[i, 1] = area_i[i] * cdg_i[i, 1]
- statico_i[i, 0] = area_i[i] * cdg_i[i, 0]
- cdg = sum(statico_i) / sum(area_i)
- xg = cdg[0]
- yg = cdg[1]
- # Fibras más alejadas
- v1y = np.amax(py) - yg
- v2y = np.amin(py) - yg
- v1x = np.amax(px) - xg
- v2x = np.amin(px) - xg
- # Momentos de inercia
- inercia_i = np.zeros([npuntos, 3])
- for i in range(npuntos - 1):
- h1 = points[i, 1]
- h2 = points[i+1, 1]
- b = points[i+1, 0] - points[i, 0]
- d = points[i, 0]
- xgi = cdg_i[i, 0]
- ygi = cdg_i[i, 1]
- ai = area_i[i]
- if h2 >= h1:
- ixcuad_G_local = 1/12 * b * (h1**3) + b * h1 * (h1/2 - ygi)**2
- ixtriang_G_loc = 1/36 * b * (h2-h1)**3 + 1/2 * b * (h2-h1) * ((2*h1+h2)/3 - ygi)**2
- else:
- ixcuad_G_local = 1/12 * b * (h2**3) + b * h2 * (h2/2 - ygi)**2
- ixtriang_G_loc = 1/36 * b * (h1-h2)**3 + 1/2 * b * (h1-h2) * ((2*h2+h1)/3 - ygi)**2
- inercia_i[i, 0] = ixcuad_G_local + ixtriang_G_loc + ai * (yg - ygi)**2
- if h2 >= h1:
- iycuad = 1/12 * h1 * b**3 + h1 * b * (b/2 + d - xgi)**2
- iytrian = 1/36 * (h2-h1) * b**3 + 1/2 * b * (h2-h1) * (2/3*b + d - xgi)**2
- else:
- iycuad = 1/12 * h2 * b**3 + h2 * b * (b/2 + d - xgi)**2
- iytrian = 1/36 * (h1-h2) * b**3 + 1/2 * b * (h1-h2) * (1/3*b + d - xgi)**2
- inercia_i[i, 1] = iycuad + iytrian + ai * (xg - xgi)**2
- if h2 >= h1:
- pxygcuadrado = b * h1 * (-h1/2 + ygi) * (-d - b/2 + xgi)
- pxytriangulo = b*b * (h2-h1)**2 / 72 + b * (h2-h1) / 2 * (-(h2-h1)/3 - h1 + ygi) * (-d - 2/3*b + xgi)
- else:
- pxygcuadrado = b * h2 * (-h2/2 + ygi) * (-d - b/2 + xgi)
- pxytriangulo = -b*b * (h1-h2)**2 / 72 + b * (h1-h2) / 2 * (-(h1-h2)/3 - h2 + ygi) * (-d - 1/3*b + xgi)
- inercia_i[i, 2] = pxygcuadrado + pxytriangulo + ai * (-xg + xgi) * (-yg + ygi)
- ig = sum(inercia_i)
- ixg = abs(ig[0])
- iyg = abs(ig[1])
- pxyg = ig[2]
- if sum(area_i) >= 0:
- pxyg = pxyg
- else:
- pxyg = -pxyg
- # Radios de giro
- rx = (ixg / area)**0.5
- ry = (iyg / area)**0.5
- # Ejes principales
- ic = (ixg + iyg) / 2
- ir = (((ixg - iyg)/2)**2 + pxyg**2)**0.5
- imax = ic + ir
- imin = ic - ir
- rmax = (imax / area)**0.5
- rmin = (imin / area)**0.5
- # Conversión a unidades prácticas (cm, cm2, cm3, cm4, kN)
- pot = 2
- area_cm2 = area * 10**(pot*2)
- ixg_cm4 = ixg * 10**(pot*4)
- iyg_cm4 = iyg * 10**(pot*4)
- imax_cm4 = imax * 10**(pot*4)
- imin_cm4 = imin * 10**(pot*4)
- return {
- 'area': area_cm2,
- 'ixg': ixg_cm4,
- 'iyg': iyg_cm4,
- 'imax': imax_cm4,
- 'imin': imin_cm4,
- 'peso': peso,
- }
-
- def generate_completo_points(self, H, b, tf, tw):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - .3) / 2
- x_alma_fin = (b + .3) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, tf],
- [x_alma_fin, 0.169],
- [x_alma_fin, H - .169],
- [b, H - tf],
- [b, H],
- [0, H],
- [0, H - tf],
- [x_alma_ini, H - .169],
- [x_alma_ini, 0.169],
- [0, tf],
- ])
- return points
-
- def generate_completo_points_ref(self, H, b, tf, tw, e_ref, L_ref):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - .3) / 2
- x_alma_fin = (b + .3) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, L_ref + tf],
- [b - e_ref, L_ref + tf],
- [x_alma_fin, 0.169],
- [x_alma_fin, H - .169],
- [b - e_ref, H - L_ref - tf],
- [b, H - L_ref - tf],
- [b, H],
- [0, H],
- [0, H - L_ref - tf],
- [e_ref, H - L_ref - tf],
- [x_alma_ini, H - 0.169],
- [x_alma_ini, 0.169],
- [e_ref, L_ref + tf],
- [0, L_ref + tf],
- ])
- return points
- def generate_hueco_inf_points(self, H, b, tf, tw):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, tf],
- [x_alma_fin, tf],
- [x_alma_fin, H -.36 - .169],
- [x_alma_ini, H - .36 - .169],
- [x_alma_ini, tf],
- [0, tf],
- ])
- return points
-
- def generate_hueco_inf_points_ref(self, H, b, tf, tw, e_ref, L_ref):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, L_ref + tf],
- [b - e_ref, L_ref + tf],
- [b - e_ref, tf],
- [x_alma_fin, tf],
- [x_alma_fin, H -.36 - .169],
- [x_alma_ini, H - .36 - .169],
- [x_alma_ini, tf],
- [e_ref, tf],
- [e_ref, L_ref + tf],
- [0, L_ref + tf],
- ])
- return points
-
- def generate_hueco_sup_points(self, H, b, tf, tw):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [x_alma_fin, H - tf - .169],
- [x_alma_fin, H - tf],
- [b, H - tf],
- [b, H],
- [0, H],
- [0, H - tf],
- [x_alma_ini, H - tf],
- [x_alma_ini, H - tf - .169],
- ])
- return points
-
- def generate_hueco_sup_points_ref(self, H, b, tf, tw, e_ref, L_ref):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [x_alma_fin, H - .169],
- [x_alma_fin, H - tf],
- [b - e_ref, H - tf],
- [b - e_ref, H - L_ref - tf],
- [b, H - L_ref - tf],
- [b, H],
- [0, H],
- [0, H - L_ref - tf],
- [e_ref, H - L_ref - tf],
- [e_ref, H - tf],
- [x_alma_ini, H - tf],
- [x_alma_ini, H - .169],
- ])
- return points
- def generate_ipe_points(self, H, b, tf, tw):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, tf],
- [x_alma_fin, tf],
- [x_alma_fin, H - tf],
- [b, H - tf],
- [b, H],
- [0, H],
- [0, H - tf],
- [x_alma_ini, H - tf],
- [x_alma_ini, tf],
- [0, tf],
- ])
- return points
-
- def generate_ipe_points_ref(self, H, b, tf, tw, e_ref, L_ref):
- """Genera los puntos de una sección IPE a partir de parámetros normalizados"""
- h_alma = H - 2*tf
- x_alma_ini = (b - tw) / 2
- x_alma_fin = (b + tw) / 2
- points = np.array([
- [0, 0],
- [b, 0],
- [b, L_ref + tf],
- [b - e_ref, L_ref + tf],
- [b - e_ref, tf],
- [x_alma_fin, tf],
- [x_alma_fin, H - tf],
- [b - e_ref, H - tf],
- [b - e_ref, H - L_ref - tf],
- [b, H - L_ref - tf],
- [b, H],
- [0, H],
- [0, H - L_ref - tf],
- [e_ref, H - L_ref - tf],
- [e_ref, H - tf],
- [x_alma_ini, H - tf],
- [x_alma_ini, tf],
- [e_ref, tf],
- [e_ref, L_ref + tf],
- [0, L_ref + tf],
- ])
- return points
-
- def _valid_geometry(self, params):
- """Valida que la combinación de parámetros genere una geometría válida"""
- H = params.get('H', 0)
- b = params.get('b', 0)
- tf = params.get('tf', 0)
- tw = params.get('tw', 0)
- if H <= 0 or b <= 0 or tf <= 0 or tw <= 0:
- return False
- if H < 0.360 + 0.169 + tf:
- return False
- if b < 0.3:
- return False
- if tf > H/2 or tf > 0.169:
- return False
- if tw > b:
- return False
-
- if self.con_ref.get():
- e_ref = params.get('e_ref', 0)
- L_ref = params.get('L_ref', 0)
- if e_ref > 0.3:
- return False
- if L_ref + tf > 0.169:
- return False
-
- return True
- def generar_matriz_sweep(self, sweep_configs, fixed_params):
- sweep_params = list(sweep_configs.keys())
- param_values = {}
- for param in sweep_params:
- config = sweep_configs[param]
- values = np.linspace(config['min'], config['max'], config['steps'])
- param_values[param] = values
-
- combinations = []
- for combo in itertools.product(*param_values.values()):
- params = fixed_params.copy()
- for i, param in enumerate(sweep_params):
- params[param] = combo[i]
- if self._valid_geometry(params):
- combinations.append(params)
-
- return combinations
- def crear_seccion(self, puntos = [], tipo="ipe", nombre_poligono="Nueva sección"):
-
- material = "S355"
-
- if not len(puntos) or not tipo:
- messagebox.showerror("Error", "Faltan puntos o tipos de sección")
- return
-
- # Leer coordenadas
- try:
- X = [float(p[0]) for p in puntos]
- Y = [float(p[1]) for p in puntos]
-
- num_points = len(X)
- Radius = [0.0] * num_points
-
- if num_points < 3:
- raise ValueError("Se necesitan al menos 3 puntos")
-
- except Exception as e:
- messagebox.showerror("Error en coordenadas", str(e))
- return
-
- if tipo == "hueco":
- nombre = "hueca"
- elif tipo == "completo":
- nombre = "rigida"
- elif tipo == "ipe":
- nombre = "entera"
- else:
- messagebox.showerror("Error", f"Tipo de sección desconocido: {tipo}")
- return
-
- try:
-
- ret = SapModel.PropFrame.SetSDSection(
- nombre, material, 1, -1, "", "Default"
- )
-
- if not (ret == 0 or ret == 1): # 1 = sección ya existe, lo cual está bien
- messagebox.showerror("Error SetSDSection", f"Código: {ret}")
- return
-
- if not (nombre == "hueca" and nombre_poligono == "Polygon2"): #si es la sección hueca, el polígono 2 es el hueco, no se borra
- ret = SapModel.PropFrame.SDShape.Delete(
- nombre,
- "",
- True
- )
-
- if ret != 0 :
- messagebox.showerror("Error Delete", f"Código: {ret}")
- return
-
- ret = SapModel.PropFrame.SDShape.SetPolygon(
- nombre, # Nombre de la sección SD
- nombre_poligono, # Nombre del shape
- material, # Material
- "Default",
- num_points, # Número de puntos
- X, # Lista normal de Python (X)
- Y, # Lista normal (Y)
- Radius, # Lista normal (Radius)
- -1, # Color
- False,
- ""
- )
-
- if ret[4] != 0:
- messagebox.showerror("Error SAP",
- f"SetPolygon devolvió código de error: {ret}\n\nRevisa que el material exista y las coordenadas sean correctas.")
-
- except Exception as e:
- messagebox.showerror("Error SAP", str(e))
- def run(self):
- self.root.mainloop()
- if __name__ == "__main__":
- app = SAPSectionDesignerGUI()
- app.run()
|