diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..c6e2f39 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,7 @@ +{ + "default": true, + "MD007": { "indent": 4 }, + "no-hard-tabs": true, + "MD013": false, + "MD041": false +} \ No newline at end of file diff --git a/Freeze_script.bat b/Freeze_script.bat index 5cdde2b..8c68b9e 100644 --- a/Freeze_script.bat +++ b/Freeze_script.bat @@ -1,11 +1,20 @@ +rmdir /s /q .\dist\HollowRC +rmdir /s /q .\build +rmdir /s /q .\HollowRC\__pycache__ +del .\HollowRC.spec + :: call activate build_env2 -pyinstaller main.py --upx-dir=%~dp0..\upx-3.95-win64 --name=HollowRC --icon=icon.ico --onedir --paths "C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86" +pyinstaller ./HollowRC/main.py ^ + --onedir --name=HollowRC ^ + --icon=./HollowRC/resources/icon.ico ^ + --upx-dir=%~dp0..\upx-3.95-win64 ^ + --upx-exclude=vcruntime140.dll ^ + --upx-exclude=msvcp140.dll ^ + --upx-exclude=qwindows.dll ^ + --upx-exclude=qwindowsvistastyle.dll ^ + --paths=%~dp0\HollowRC :: call deactivate -xcopy .\dist\no_upx_files\VCRUNTIME140.dll .\dist\HollowRC\ -xcopy .\dist\no_upx_files\MSVCP140.dll .\dist\HollowRC\ -xcopy .\dist\no_upx_files\qwindows.dll .\dist\HollowRC\PySide2\plugins\platforms\ -xcopy .\dist\no_upx_files\qwindowsvistastyle.dll .\dist\HollowRC\PySide2\plugins\styles\ -pause + del .\dist\HollowRC\Qt5WebSockets.dll del .\dist\HollowRC\_decimal.pyd del .\dist\HollowRC\_lzma.pyd @@ -21,7 +30,7 @@ del .\dist\HollowRC\libblkdta* del .\dist\HollowRC\libchkder* del .\dist\HollowRC\libcobyla2* del .\dist\HollowRC\libd_odr* -del .\dist\HollowRC\libdcosqb* +::del .\dist\HollowRC\libdcosqb* del .\dist\HollowRC\libdcsrch* del .\dist\HollowRC\libdet* del .\dist\HollowRC\libdfft_sub* @@ -51,10 +60,24 @@ rmdir /s /q .\dist\HollowRC\scipy rmdir /s /q .\dist\HollowRC\lib2to3 rmdir /s /q .\dist\HollowRC\Include rmdir /s /q .\dist\HollowRC\PySide2\translations -rmdir /s /q .\dist\HollowRC\PySide2\PySide2 -del .\dist\HollowRC\PySide2\libEGL.dll -del .\dist\HollowRC\PySide2\d3dcompiler_47.dll -del .\dist\HollowRC\PySide2\libGLESv2.dll -del .\dist\HollowRC\PySide2\opengl32sw.dll +::rmdir /s /q .\dist\HollowRC\PySide2\PySide2 +::del .\dist\HollowRC\PySide2\libEGL.dll +del .\dist\HollowRC\libEGL.dll +::del .\dist\HollowRC\PySide2\d3dcompiler_47.dll +del .\dist\HollowRC\d3dcompiler_47.dll +::del .\dist\HollowRC\PySide2\libGLESv2.dll +::del .\dist\HollowRC\PySide2\opengl32sw.dll +del .\dist\HollowRC\opengl32sw.dll del .\dist\HollowRC\PySide2\QtNetwork.pyd -pause +del .\dist\HollowRC\_multiprocessing.pyd +del .\dist\HollowRC\_hashlib.pyd +del .\dist\HollowRC\_bz2.pyd +del .\dist\HollowRC\PySide2\qt.conf +del .\dist\HollowRC\PySide2\plugins\platforms\qminimal.dll +del .\dist\HollowRC\PySide2\plugins\platforms\qoffscreen.dll +del .\dist\HollowRC\PySide2\plugins\platforms\qwebgl.dll +del .\dist\HollowRC\libopenblas.PYQHXLVVQ7VESDPUVUADXEVJOBGHJPAY.gfortran-win_amd64.dll +rmdir /s /q .\dist\HollowRC\PySide2\plugins\imageformats +rmdir /s /q .\dist\HollowRC\PySide2\plugins\iconengines +rmdir /s /q .\dist\HollowRC\PySide2\plugins\bearer +rmdir /s /q .\dist\HollowRC\importlib_metadata-0.23-py3.7.egg-info diff --git a/HollowRC/Analysis.py b/HollowRC/Analysis.py index 3226fc3..a3b025c 100644 --- a/HollowRC/Analysis.py +++ b/HollowRC/Analysis.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- """ -This module containes all the analysis functions for HollowRC +This module contains all the analysis functions for HollowRC Author: Kenneth C. Kleissl """ - # Standard library modules import math from timeit import default_timer as timer +import random # Third-party library modules import numpy as np @@ -41,7 +41,7 @@ def minCompEnergy(section, SF, Mat): @measure_time -def ULS_analysis(section, SF, Mat): +def ULS_analysis(section, SF, Mat, printing=True): """ Perform a Plane Section ULS Analysis (incl. optimizing shear flow based on capacity) """ @@ -65,47 +65,59 @@ def ULS_analysis(section, SF, Mat): # get wall shear force capacity section.set_section_dist(dist) # send the normal flow etc. all the way down to Wall level V_yield = section.get_wall_shear_capacities(Mat) - print('V_yield:', V_yield) + if printing: + print('V_yield:', V_yield) V_yield = [1e-10 if Vi < 1e-10 else Vi for Vi in V_yield] # set near zeros to small values as solver needs a minimum of manouverbility # initial guess for wall shear forces ( note: copysign(x,y) return x with the sign of y ) x0 = [Vi_wall if abs(Vi_wall) <= V_yield[i] else math.copysign(V_yield[i], Vi_wall) for i, Vi_wall in enumerate(V_wall)] - # print('x0:', x0) x0 = np.append(x0, 1.0) # append the shear load factor variable # Determine variable bounds - lower_bound = [-Vi for Vi in V_yield] + [1e-10] # define lower bound by negated shear capacity + added shear load factor <--- why not just zero? - upper_bound = V_yield + [1.0] # define upper bound list by pos. yield shear flow + added shear load factor + lower_bounds = [-Vi for Vi in V_yield] + [1e-10] # define lower bound by negated shear capacity + added shear load factor <--- why not just zero? + upper_bounds = V_yield + [1.0] # define upper bound list by pos. yield shear flow + added shear load factor # initiate optimization instance - opt = nlopt.opt(nlopt.LD_SLSQP, len(x0)) # only LD_SLSQP support equality constraints + opt = nlopt.opt(nlopt.LD_SLSQP, len(x0)) # only LD_SLSQP support equality constraints + # opt = nlopt.opt(nlopt.LD_MMA, len(x0)) # NLOPT_LD_MMA does only support inequality constraints # set bounds - opt.set_lower_bounds(lower_bound) - opt.set_upper_bounds(upper_bound) + opt.set_lower_bounds(lower_bounds) + opt.set_upper_bounds(upper_bounds) # set objective - opt.set_max_objective(myObjective) + opt.set_max_objective(myObjective) # maximize load factor (last variable) # set constraint opt.add_equality_mconstraint(lambda result, x, grad: myShearConstraints2(result, x, grad, section, SF), [1e-6, 1e-6, 1e-6]) # feasible if func < tol + # opt.add_inequality_mconstraint(lambda result, x, grad: myShearConstraints2(result, x, grad, section, SF), [1e-6, 1e-6, 1e-6]) # stop criterias / tolerances opt.set_xtol_rel(1e-10) # with the problem now being smaller the tolerance can be stepped up -> did help finding the proper solution for some situations - opt.set_ftol_rel(1e-10) # this alternative criteria allows the solver to escape when lambda factor need to go to zero + # opt.set_ftol_rel(1e-10) # this alternative criteria allows the solver to escape when lambda factor need to go to zero # solve xopt = opt.optimize(x0) - V_wall = xopt[:-1] - # print result - print("wall based shear maximized load factor =", xopt[-1]) + # multi-start search + while True: + x0 = [random.uniform(LB, UB) for LB, UB in zip(lower_bounds, upper_bounds)] # random start within bounds + xopt2 = opt.optimize(x0) + enhancement = xopt2[-1] - xopt[-1] # higher is better (load factor) + if enhancement > 1e-8: + xopt = xopt2 + else: + break + + *V_wall, load_fac = xopt # unpack optimization results + + if printing: + print("wall based shear maximized load factor =", load_fac) # print("shear final objective = ", opt.last_optimum_value()) # print("wall based shear optimum at V_wall =", V_wall) shear_error = errorFunShear2(xopt, section, SF) - print("wall based shear error at opt =", shear_error) # print("shear result code = ", opt.last_optimize_result()) if shear_error > 1e-6: print("Error too big!, shear optimization failed") @@ -132,7 +144,8 @@ def ULS_analysis(section, SF, Mat): else: shear_UR = 1 # set shear_UR to 100% if wall has zero capacity - print('shear force in wall {} ({:.2f}kN) is at {:.2f}% utilization'.format(i + 1, V_wall[i], abs(shear_UR) * 100)) + if printing: + print('shear force in wall {} ({:.2f}kN) is at {:.2f}% utilization'.format(i + 1, V_wall[i], abs(shear_UR) * 100)) # build H from scaled down H_yield j_start = i * wall.wallNodeN @@ -163,7 +176,8 @@ def ULS_analysis(section, SF, Mat): # error_msg = ["The shown results applies maximized shear load factor:", "Maximum shear load factor = " + # str(xopt[-1])] - print('Integration test Vy, Vz, T: ', integrateShearFlow(H, dist, section)) + if printing: + print('Integration test Vy, Vz, T: ', integrateShearFlow(H, dist, section)) return Res, error_msg @@ -172,13 +186,11 @@ def errorFunShear2(x, section, SF): """ This function computes the shear equilibrium errors between wall forces and SF """ - # unload variables - V = x[:-1] - load_fac = x[-1] + # unpack variables + *V, load_fac = x # integrate shear forces into sectional forces Vy, Vz, T = integrateWallShearForces(V, section) - print('wall based Integrated Vy, Vz, T: ', Vy, Vz, T) # Section force difference (sum squared diff.) return (SF.Vz*load_fac - Vz)**2 + (SF.Vy*load_fac - Vy)**2 + (SF.T*load_fac - T)**2 @@ -189,32 +201,31 @@ def myShearConstraints2(result, x, grad, section, SF): Constrain function for shear optimization for when the variables are wall shear forces """ - # unload variables - V = x[:-1] - load_fac = x[-1] + # unpack variables + *V, load_fac = x # integrate wall shear forces into sectional forces Vy, Vz, T = integrateWallShearForces(V, section) # Section force difference - result[0] = SF.Vy * load_fac - Vy - result[1] = SF.Vz * load_fac - Vz - result[2] = SF.T * load_fac - T + result[0] = Vy - SF.Vy * load_fac # horizontal equilibirum + result[1] = Vz - SF.Vz * load_fac # vertical equilibirum + result[2] = T - SF.T * load_fac # torsional equilibrium # check if gradients are requested if grad.size > 0: - e = section.get_e() # get wall eccentricity from GC + e = section.get_e() # get wall eccentricity from GC (it's in mm!) for i, wall in enumerate(section.walls): - # gradients - grad[0, i] = math.cos(wall.angle) # the term is here positive as the minus from the constraint function counters the original minus - grad[1, i] = math.sin(wall.angle) # positive up - grad[2, i] = - e[i] / 1000 + # gradients from wall shear forces + grad[0, i] = -math.cos(wall.angle) # negative, as positive shear is defined opposite wall direction + grad[1, i] = -math.sin(wall.angle) # negative, as positive shear is defined opposite wall direction + grad[2, i] = e[i] / 1000 # positive, as both wall shear force and torsion is positive counter-clockwise - # load factor gradient - grad[0, -1] = SF.Vy - grad[1, -1] = SF.Vz - grad[2, -1] = SF.T + # gradient from load factor + grad[0, -1] = -SF.Vy # all negative because the load factor is in a negative term of the objective + grad[1, -1] = -SF.Vz + grad[2, -1] = -SF.T # # check gradient matrix by finite-difference # grad2 = finite_difference2(x, section, SF) @@ -235,8 +246,10 @@ def finite_difference2(x, section, SF): grad = np.empty(shape=(3, len(x))) # initiate gradient matrix delta = 0.1 for i in range(len(x)): - result1 = constrain_function2(x + delta, section, SF) - result2 = constrain_function2(x - delta, section, SF) + x_delta = np.zeros_like(x) + x_delta[i] = x_delta[i] + delta + result1 = constrain_function2(x + x_delta, section, SF) + result2 = constrain_function2(x - x_delta, section, SF) grad[0, i] = (result1[0] - result2[0]) / (2 * delta) grad[1, i] = (result1[1] - result2[1]) / (2 * delta) grad[2, i] = (result1[2] - result2[2]) / (2 * delta) @@ -244,9 +257,8 @@ def finite_difference2(x, section, SF): def constrain_function2(x, section, SF): # constrain function to be used with finite-difference - # unload variables - V = x[:-1] - load_fac = x[-1] + # unpack variables + *V, load_fac = x # integrate shear flow into sectional forces Vy, Vz, T = integrateWallShearForces(V, section) @@ -309,7 +321,7 @@ def integrateWallShearForces(V, section): return sum(Vy), sum(Vz), sum(T) -def myObjective(x, grad): # objective function defining last variable as the objevtive +def myObjective(x, grad): # objective function defining last variable as the objective if grad.size > 0: grad[:] = [0]*len(grad) grad[-1] = 1 @@ -451,7 +463,6 @@ def uncracked_strain_state(section, SF, Mat): eps_tens = max(eps) # strain in extreme tension fiber x0 = np.array([NA_angle, eps_comp, eps_tens]) # initial guess - print('Initial strain guess =', x0) return x0 @@ -476,20 +487,18 @@ def shear_flow(dist, dist2, SF, section, dx): def bendingSolution(x0, section, SF, Mat): - opt = nlopt.opt(nlopt.LN_NELDERMEAD, len(x0)) + opt = nlopt.opt(nlopt.LN_NELDERMEAD, len(x0)) # faster than LN_SBPLX opt.set_min_objective(lambda x, grad: errorFunBending(x, section, SF, Mat)) opt.set_xtol_rel(1e-8) - x = opt.optimize(x0) - print("Optimized strain state =", x) - print("Minimized error value = ", opt.last_optimum_value()) + x = opt.optimize(x0) # Optimized strain state # print("result code = ", opt.last_optimize_result()) # check result for high error margin - if opt.last_optimum_value() > 1e-4: + if opt.last_optimum_value() > 1e-6: print("Failed to find bending equilibrium, try with less load") raise MyOptimizerError("Failed to find bending equilibrium", "The section cannot sustain the bending loads applied. Try with less load.") - # If bending equilibrium fails -> capacity is insufficient -> intro. + # If bending equilibrium fails -> capacity is insufficient -> intro. # load factor and minimize it under error constraint -> yields a UR / lambda_bending factor # ------- need to implement gradients for optimization below ------ # print("Error too big!, steps over to bigger problem including load factor") @@ -684,7 +693,7 @@ def ReinforcementStressAry(eps, Mat): # if 1 - xopt[-1] < 1e-10: # sufficiently close (within tolorance) to be considered as one # error_msg = None # else: -# error_msg = ["The section shear force utilization is at {:.1f}%".format(100/xopt[-1]), +# error_msg = ["The section shear force utilization is at {:.1f}%".format(100/xopt[-1]), # "The shown results applies maximized shear load factor of {:.4f}".format(xopt[-1])] # # error_msg = ["The shown results applies maximized shear load factor:", "Maximum shear load factor = " + # # str(xopt[-1])] diff --git a/HollowRC/Geometry.py b/HollowRC/Geometry.py index da56506..381eb13 100644 --- a/HollowRC/Geometry.py +++ b/HollowRC/Geometry.py @@ -2,11 +2,9 @@ """ Class definitions for geometry properties -History log: -Version 0.1 - first working build - Author: Kenneth C. Kleissl """ +# imports import math import numpy as np import Verification @@ -30,15 +28,24 @@ def count_instances(cls): def get_instance_count(cls): return cls.instance_count - def __init__(self): - # Instance variables - self.walls = [] # list of wall instances + def __init__(self, *args): + # Initiate list of wall instances + self.walls = [] - # Class variables + # deal with arguments + for arg in args: + if isinstance(arg, list): + self.walls.extend(arg) + else: + self.walls.append(arg) # Class methods self.count_instances() + def __repr__(self): + string = ",\n ".join([str(x) for x in self.walls]) + return f'CrossSection({string})' + def add_wall(self, wall): self.walls.append(wall) @@ -104,7 +111,7 @@ def get_angles(self, local_data=False): for wall in self.walls: angles.append(wall.angle) if local_data: - return np.repeat(angles, self.walls[0].wallNodeN) # presumes same wallNodeN for all walls + return np.repeat(angles, self.walls[0].wallNodeN) # presumes same wallNodeN for all walls else: return angles @@ -166,15 +173,15 @@ def valid(self): return True, None def set_section_dist(self, dist): - for i,wall in enumerate(self.walls): # looping over walls - N = wall.wallNodeN # nodes per wall + for i, wall in enumerate(self.walls): # looping over walls + N = wall.wallNodeN # nodes per wall wall_dist = {} for key in dist: wall_dist[key] = dist[key][i*N:(i+1)*N] wall.set_wall_dist(wall_dist) def get_wall_shear_capacities(self, Mat): - # returns a list of wall yield shear forces + # returns a list of wall yield shear forces return [wall.get_yield_shear_force(Mat) for wall in self.walls] def get_area(self): @@ -183,7 +190,7 @@ def get_area(self): ''' area = [] for wall in self.walls: - area.append( wall.area ) + area.append(wall.area) return sum(area) def get_Ix_Iy(self): @@ -197,11 +204,11 @@ def get_Ix_Iy(self): centreX, centreY = self.get_centre() for wall in self.walls: - Ix.append( wall.get_Ix() + wall.area*(wall.midY - centreY)**2 ) - Iy.append( wall.get_Iy() + wall.area*(wall.midX - centreX)**2 ) - + Ix.append(wall.get_Ix() + wall.area*(wall.midY - centreY)**2) + Iy.append(wall.get_Iy() + wall.area*(wall.midX - centreX)**2) + # print('Ix:', Ix) - # print('Iy:', Iy) + # print('Iy:', Iy) # Sum wall contributions return sum(Ix), sum(Iy) @@ -256,24 +263,24 @@ def calculate_properties(self): def set_wallNodeN(self, wallNodeN): self.wallNodeN = wallNodeN - self.ds = self.length / (self.wallNodeN - 1) # recalculate ds + self.ds = self.length / (self.wallNodeN - 1) # recalculate ds - def __str__(self): - return "member of Wall" + def __repr__(self): + return f'Wall(X={self.X}, Y={self.Y}, thick={self.thick}, rho_long={self.rho_long}, rho_trans={self.rho_trans})' def set_wall_dist(self, dist): self.dist = dist def get_yield_shear_force(self, Mat): H_yield = self.get_yield_flow(Mat) - V_yield = self.integrate_dist(H_yield) / 1000 # dividing by 1000 to get force in [kN] + V_yield = self.integrate_dist(H_yield) / 1000 # dividing by 1000 to get force in [kN] return V_yield def get_yield_flow(self, Mat): H_yield = [] for N_flow in self.dist['normal_flow']: # looping over wall data points - sigma_x = N_flow / self.thick # + sigma_x = N_flow / self.thick stress = [sigma_x, 0, 0] verification = Verification.Verify(stress, Mat, self.rho_long, self.rho_trans) H_yield.append(verification.tau_yielding() * self.thick) @@ -308,6 +315,7 @@ def get_Iy(self): Iy = 0 return Iy + # For when this script is excetuted on its own if __name__ == '__main__': thick = 200 diff --git a/HollowRC/HollowWindow.py b/HollowRC/HollowWindow.py index 223ea08..b940e2c 100644 --- a/HollowRC/HollowWindow.py +++ b/HollowRC/HollowWindow.py @@ -33,7 +33,7 @@ def __init__(self): # self.geometry_table = TableInterface.MyTable(self.geometry_table) # version tag and label - self.tag = 'v1.4' + self.tag = 'v1.5' self.label_version.setText(self.tag) # --- Triggers --- @@ -151,10 +151,10 @@ def refresh_visible_plots(self): # signal/normal function self.material_plot() elif self.tabWidget.currentIndex() == 3: # update result plot - try: - self.result_plot(self.Res) - except: - pass + # try: + self.result_plot(self.Res) + # except: + # pass def save_file(self): ''' @@ -266,8 +266,9 @@ def initiate_analysis(self): self.refresh_visible_plots() return - # print(Geometry) - print('SF: ' + SF.print_str()) + print(section) + print(Mat) + print(SF) # switch to Loading & Result tab self.tabWidget.setCurrentIndex(3) diff --git a/HollowRC/Material.py b/HollowRC/Material.py index 963ba45..e8a7f10 100644 --- a/HollowRC/Material.py +++ b/HollowRC/Material.py @@ -6,6 +6,7 @@ """ import nlopt + class MatProp: """ A container for material properties @@ -26,11 +27,26 @@ class MatProp: gamma_s = 1.15 conc_method = 'EN Parabolic-rectangular' reinf_method = 'Elastic-plastic' - conc_method_options = ['EN Parabolic-rectangular', 'EN Bi-linear', 'EN Nonlinear', 'Linear elastic', 'Linear elastic (no ten.)', 'Elastic-plastic', 'Sudden plastic'] - reinf_method_options = ['Elastic-plastic', 'Bi-linear hardening', 'Linear elastic', 'Elastic-plastic (no comp.)'] + conc_method_options = ['EN Parabolic-rectangular', + 'EN Bi-linear', + 'EN Nonlinear', + 'Linear elastic', + 'Linear elastic (no ten.)', + 'Elastic-plastic', + 'Sudden plastic'] + reinf_method_options = ['Elastic-plastic', + 'Bi-linear hardening', + 'Linear elastic', + 'Elastic-plastic (no comp.)'] + # hidden values f_ct = 2.5 + def __repr__(self): + return f'MatProp(f_ck={self.f_ck}, E_cm={self.E_cm}, f_yk={self.f_yk}, E_s={self.E_s}, ' + \ + f'alpha_cc={self.alpha_cc}, gamma_c={self.gamma_c}, gamma_s={self.gamma_s}, ' + \ + f'conc_method={self.conc_method}, reinf_method={self.reinf_method})' + def __init__(self): # Instance variables # self.setLimitState(self.limit_state) diff --git a/HollowRC/Plots.py b/HollowRC/Plots.py index 7ab66c0..a9a054c 100644 --- a/HollowRC/Plots.py +++ b/HollowRC/Plots.py @@ -29,17 +29,17 @@ def clear_scene(self): # view.scene().disconnect() # view.close() - def update_section(self, signal_value): # signal receiver + def update_section(self, signal_value): # signal receiver dx, dy, wall_id = signal_value X = self.section.get_X() Y = self.section.get_Y() X[wall_id] += dx Y[wall_id] -= dy - self.section.set_XY(X, Y) # update current section instance with the new coordinates - self.new_section.emit(self.section) # emit new_section signal for the window class to catch - self.scene_backup = self.scene # store a copy of the scene to avoid RuntimeError: Internal C++ object (MyEllipse) already deleted. - self.scene = QtWidgets.QGraphicsScene() # creates a new scene instance - self.setScene(self.scene) # set the created scene + self.section.set_XY(X, Y) # update current section instance with the new coordinates + self.new_section.emit(self.section) # emit new_section signal for the window class to catch + self.scene_backup = self.scene # store a copy of the scene to avoid RuntimeError: Internal C++ object (MyEllipse) already deleted. + self.scene = QtWidgets.QGraphicsScene() # creates a new scene instance + self.setScene(self.scene) # set the created scene self.plot_all(self.section) def plot_all(self, section): @@ -47,16 +47,12 @@ def plot_all(self, section): self.section = section self.clear_scene() - try: - # unpack geometry properties - X = self.section.get_X() - Y = self.section.get_Y() - T = self.section.get_thick() - centreX, centreY = self.section.get_centre() - wallAngles = self.section.get_angles() - except: - print('Cannot read geometry') - return + # unpack geometry properties + X = self.section.get_X() + Y = self.section.get_Y() + T = self.section.get_thick() + centreX, centreY = self.section.get_centre() + wallAngles = self.section.get_angles() # connect itemChange signal with method # scene.changed.connect(self.node_moved) @@ -149,7 +145,7 @@ def plot_all(self, section): # plot centre lines for line in centre_lines: self.scene.addLine(line, bold_pencil) - + # plot node circles for circle in node_circles: self.scene.addItem(circle) @@ -171,12 +167,12 @@ def plot_all(self, section): self.scene.mousePressEvent = self.scene_mousePressEvent # overrule QGraphicsSceneEvent # unblock scene change signals again # scene.blockSignals(False) - - def scene_mousePressEvent(self, event): # when user clicks on result the values are listed in the status line - if self.awaits_click == True: + + def scene_mousePressEvent(self, event): # when user clicks on result the values are listed in the status line + if self.awaits_click: x = event.scenePos().x() y = event.scenePos().y() - self.scene_clicked.emit({'x': round(x), 'y' : round(y)}) # emit scene_clicked signal + self.scene_clicked.emit({'x': round(x), 'y': round(y)}) # emit scene_clicked signal return QtWidgets.QGraphicsScene.mousePressEvent(self.scene, event) @@ -207,12 +203,12 @@ def set_wall_id(self, wall_id): self.wall_id = wall_id # def itemChange(self, change, value): - # """ - # Catch all item position changes and emit the changed signal with the value (which will be the position). - # :param change: - # :param value: - # """ - # if change == QtWidgets.QGraphicsItem.ItemPositionChange: + # """ + # Catch all item position changes and emit the changed signal with the value (which will be the position). + # :param change: + # :param value: + # """ + # if change == QtWidgets.QGraphicsItem.ItemPositionChange: # # value is the new position. # print("item's scene position changed: ", value) # # print(value) @@ -220,8 +216,8 @@ def set_wall_id(self, wall_id): # return QtWidgets.QGraphicsItem.itemChange(self, change, value) def mousePressEvent(self, event): - # super().mousePressEvent(event) - self.setCursor(QtCore.Qt.ClosedHandCursor) # update cursor + # super().mousePressEvent(event) + self.setCursor(QtCore.Qt.ClosedHandCursor) # update cursor self.x0 = event.scenePos().x() self.y0 = event.scenePos().y() return QtWidgets.QGraphicsEllipseItem.mousePressEvent(self, event) @@ -262,7 +258,7 @@ def clear_scene(self): # self.update() # self.scene.update() - def show_result_values(self, signal_value): # signal receiver + def show_result_values(self, signal_value): # signal receiver pass def set_check_boxes(self, checkbox_list): @@ -271,11 +267,11 @@ def set_check_boxes(self, checkbox_list): def plot_all(self, Res): # original way of plotting all self.clear_scene() - + # check for empty result if not Res: return - + # unpack results dictionary x = Res.x y = Res.y @@ -310,12 +306,12 @@ def plot_all(self, Res): # calculate largest dimension of cross-section section_dim = max(max(y) - min(y), max(x) - min(x)) - for j in range(Res.plot_count): # looping over the different result distributions + for j in range(Res.plot_count): # looping over the different result distributions # select styles - pencil = QtGui.QPen(colour_list[j]) # create pen with next colour - fill = QtGui.QBrush(colour_list[j]) # create brush with same colour + pencil = QtGui.QPen(colour_list[j]) # create pen with next colour + fill = QtGui.QBrush(colour_list[j]) # create brush with same colour pencil.setWidth(10) - + check_box = self.check_boxes[j] # check_box = getattr(self, 'checkBox_plot' + str(j+1)) @@ -328,14 +324,14 @@ def plot_all(self, Res): # plot if checked if check_box.isChecked(): scale = Res.plot_scale[j] * section_dim / max(1e-12, max(abs(Res.plot_data[j]))) - rect = QtGui.QPolygonF() # outline polygon + rect = QtGui.QPolygonF() # outline polygon for i in range(len(Res.x)): PX = Res.x[i] + scale * Res.plot_data[j][i] * math.sin(-wallAngles[i]) PY = Res.y[i] + scale * Res.plot_data[j][i] * math.cos(-wallAngles[i]) if Res.x[i] == Res.x[i - 1] and Res.y[i] == Res.y[i - 1]: # new wall element started rect.append(QtCore.QPointF(Res.x[i], -Res.y[i])) # add plot point at geometric corner # prepare/plot shading - if i>0: + if i > 0: # plot shading rect2.append(QtCore.QPointF(Res.x[i], -Res.y[i])) # add plot point at geometric corner poly_item = QtWidgets.QGraphicsPolygonItem(rect2) @@ -344,7 +340,7 @@ def plot_all(self, Res): self.scene.addItem(poly_item) # self.scene.addPolygon(rect2, brush=fill) - rect2 = QtGui.QPolygonF() # shading polygon + rect2 = QtGui.QPolygonF() # shading polygon rect2.append(QtCore.QPointF(Res.x[i], -Res.y[i])) # add plot point at geometric corner rect2.append(QtCore.QPointF(PX, -PY)) else: @@ -357,7 +353,7 @@ def plot_all(self, Res): # line_item = QtWidgets.QGraphicsLineItem(line) line_item = myLine(line) line_item.setPen(pencil) - line_item.set_data_str('{}: {:.2f} {}'.format(Res.plot_names[j], Res.plot_data[j][i], Res.plot_units[j])) # string for click ev. + line_item.set_data_str('{}: {:.2f} {}'.format(Res.plot_names[j], Res.plot_data[j][i], Res.plot_units[j])) # string for click ev. line_item.setAcceptHoverEvents(True) self.scene.addItem(line_item) @@ -381,7 +377,7 @@ def plot_all(self, Res): # ui->graphicsView->fitInView(_scene->itemsBoundingRect(),Qt::KeepAspectRatio); # consider changing to itemsBoundingRect when sceneRect not updating!! self.scene.mousePressEvent = self.scene_mousePressEvent # overrule QGraphicsSceneEvent - def scene_mousePressEvent(self, event): # when user clicks on result the values are listed in the status line + def scene_mousePressEvent(self, event): # when user clicks on result the values are listed in the status line # x = event.scenePos().x() # y = event.scenePos().y() # print('scene press event (x,y) = ({}, {})'.format(x, y)) @@ -402,6 +398,8 @@ def scene_mousePressEvent(self, event): # when user clicks on result the values # # # Creating my own line item class so I can overwrite its mousePressEvent # # line_item = QtWidgets.QGraphicsLineItem(line) + + class myLine(QtWidgets.QGraphicsLineItem): # def __init__(self, parent=None): # # QtWidgets.QGraphicsScene.__init__(self, parent) @@ -421,7 +419,7 @@ def hoverEnterEvent(self, event): pen.setWidth(20) self.setPen(pen) return QtWidgets.QGraphicsLineItem.hoverEnterEvent(self, event) - + def hoverLeaveEvent(self, event): # print('hoverLeaveEvent') pen = self.pen() diff --git a/HollowRC/Results.py b/HollowRC/Results.py index 18685b4..614ef8c 100644 --- a/HollowRC/Results.py +++ b/HollowRC/Results.py @@ -2,15 +2,13 @@ """ Class definition of a container for material properties -History log: -Version 0.1 - first working build - Author: Kenneth C. Kleissl """ + class Results: """ - A container for material properties by Kenneth C. Kleissl. + A container for results Attributes: ... @@ -54,4 +52,3 @@ def clear_plot_data(self): Res = Results(0, 0, 0) print(Res) - diff --git a/HollowRC/SectionForces.py b/HollowRC/SectionForces.py index a8aa10c..b5318b5 100644 --- a/HollowRC/SectionForces.py +++ b/HollowRC/SectionForces.py @@ -2,15 +2,13 @@ """ Class definition of a container for sectional forces -History log: -Version 0.1 - first working build - Author: Kenneth C. Kleissl """ + class SectionForces: """ - A container for sectional forces by Kenneth C. Kleissl. + A container for sectional forces Attributes: N: Normal force (neg. = compression) @@ -52,10 +50,8 @@ def __init__(self, N, My, Mz, Vy=None, Vz=None, T=None): def set_load_factor(self, fac): self.load_factor = fac - def print_str(self): - string = 'N=' + str(self.N) + ', My=' + str(self.My) + ', Mz=' + str(self.Mz) \ - + ', Vy=' + str(self.Vy) + ', Vz=' + str(self.Vz) + ', T=' + str(self.T) - return string + def __repr__(self): + return f'SectionForces(N={self.N}, My={self.My}, Mz={self.Mz}, Vy={self.Vy}, Vz={self.Vz}, T={self.T})' # For when this script is excetuted on its own @@ -68,4 +64,3 @@ def print_str(self): T = 0 SF = SectionForces(N, My, Mz, Vy, Vz, T) print(SF.My) - diff --git a/HollowRC/Verification.py b/HollowRC/Verification.py index e8eef74..ab3c83d 100644 --- a/HollowRC/Verification.py +++ b/HollowRC/Verification.py @@ -207,7 +207,7 @@ def cracked_strut_angle(self, initial_guess=45.0): theta_0 = [initial_guess] # initial guess of 45 degrees opt = nlopt.opt(nlopt.LN_NELDERMEAD, len(theta_0)) opt.set_lower_bounds([0]) # the bounding speeds up the solver time - opt.set_upper_bounds([180]) # using [180 - 10**-12] significantly slows the solver + opt.set_upper_bounds([180]) # using [180 - 10**-12] significantly slows the solver <- 90? opt.set_min_objective(lambda theta, grad: self.complementary_energy(self.cracked_equilibrium(theta[0]))) opt.set_xtol_rel(1e-6) # theta error tolerance theta = opt.optimize(theta_0) @@ -318,7 +318,7 @@ def complementary_energy(self, stresses): # only linear elastic energy! # Calculate complementary elastic energy (under linear elastic assumption!) W_ec = 10**6 * 1 / (2 * E_c) * stresses['sigma_c']**2 * (1 - self.rho_c_eq) W_es = 10**6 * self.rho_sx / (2 * E_s) * stresses['sigma_sx']**2 + \ - 10**6 * self.rho_sy / (2 * E_s) * stresses['sigma_sy']**2 + 10**6 * self.rho_sy / (2 * E_s) * stresses['sigma_sy']**2 W_e = W_ec + W_es return W_e # [Nmm/mm] energy/thickness diff --git a/HollowRC/hollow_window.py b/HollowRC/hollow_window.py index 8e8d943..97a54a0 100644 --- a/HollowRC/hollow_window.py +++ b/HollowRC/hollow_window.py @@ -3,7 +3,7 @@ # Form implementation generated from reading ui file 'hollow_window.ui', # licensing of 'hollow_window.ui' applies. # -# Created: Tue Sep 10 21:45:02 2019 +# Created: Fri Dec 6 14:24:13 2019 # by: pyside2-uic running on PySide2 5.13.1 # # WARNING! All changes made in this file will be lost! @@ -13,7 +13,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1031, 761) + MainWindow.resize(1031, 775) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/Icons/resources/Icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.On) MainWindow.setWindowIcon(icon) @@ -94,7 +94,6 @@ def setupUi(self, MainWindow): spacerItem2 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) self.verticalLayout_8.addItem(spacerItem2) self.label_24 = QtWidgets.QLabel(self.tab_about) - self.label_24.setText("") self.label_24.setPixmap(QtGui.QPixmap(":/Figures/resources/coordinates_and_sign_convention.PNG")) self.label_24.setObjectName("label_24") self.verticalLayout_8.addWidget(self.label_24) @@ -743,7 +742,7 @@ def setupUi(self, MainWindow): self.toolBar.addAction(self.analyseAct) self.retranslateUi(MainWindow) - self.tabWidget.setCurrentIndex(1) + self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): @@ -752,6 +751,7 @@ def retranslateUi(self, MainWindow): self.label_left_text.setText(QtWidgets.QApplication.translate("MainWindow", "

About

This is an easy to use design tool for analysis of hollow reinforced concrete sections under combined loading, fully embracing the interactions between bending and shear behaviour.
To propor describe the interaction between the flow of normal stesses and shear stress and to fully utilize the capacity of the cross-section one must leave the simplfied approach of the diagonal truss model.
For a linear-elastic material one could apply the Grashof\'s formula, similar to analysis of thin-walled steel-sections, to determining shear flow etc.
However, with the basic assumption of linear-elastic material behaviour being unsuitable for reinforced concrete, a more numerical extensive approach involving a series of optimization routines needs to be adopted.
To make this analysis method more approachable, this easy to use application has been developed.

This design tool will provide designers with a superior insight into the actual stress state during Service Limit State (SLS), including the shear or torsion induced stresses in the transverse reinforcement (in the circumferential direction), and will completely avoid any superposition of plastic lower bound methods from the diagonal truss model, additional shear-induced demand for longitudinal reinforcement and the corresponding strain incompatibilities introduced by separating shear and bending analysis.
For SLS the actual shear flow is determined based on a plane dual-section analysis, which just means that two nabouring plane-sections are analysed and from their differences in normal flow, simple equilibrium yields the corresponding shear flow distribution.
So only by applying the fundamental flexural member assumption of plane sections must remain plane combined with basic equilibrium equations can the actual normal and shear flow distributions be determined.
From this an in-plane membrane analysis is used to determined the reinforcement stresses etc. by choosing the resulting compressive stress direction / strut angle such that it minimizes the complementary strain energy (similar to fulfilling compatibility).

For Ultimate Limit State (ULS) this design tool will allow designers to push the capacity of the cross-section even further, as it by use of mathematical optimization algorithms are able to identify the true optimal plastic lower-bound solution that fully utilize the strength of the materials.
For ULS a classic plane section analysis is performed and from its normal flow distribution, an in-plane membrane analysis considering the yield conditions determines the leftover shear flow capacity at any given point along the cross-section, which then is integrated into a shear force capacity for each of the cross-sectional wall elements. Finally this is followed up by solving the optimization problem of maximizing the load-factor while maintaining equalibrium between the wall shear forces and the user specified global sectional forces.

Assumptions and limitations:

- The span to depth ratio of the section is sufficient for beam theory to be applicable where plane section analysis approach is considered.
- The walls are sufficient thin, compared to the cross-section dimensions, for a thin-walled approach to be applicable.
- Normal stresses in the circumferential direction are neglected even though equilibrium in principle requires the presence of these. This is a very common approach when analysing thin-walled sections..

", None, -1)) self.label_version.setText(QtWidgets.QApplication.translate("MainWindow", "v1.0", None, -1)) self.label_right_text.setText(QtWidgets.QApplication.translate("MainWindow", "

Sign convention

The sign convention generally follows a right hand system (RHS). See illustration in figure below.

Geometry
- All dimensions are in millimeters
- The section walls must be defined in the clock-wise direction
- Angles are taken positive in the counter-clockwise direction starting from the y-axis
- Reinforcement ratios are defined as the steel-to-concrete ratio (e.g. 0.01 = 1%)

Flows & Stresses
- Normal flow and normal stress are positive for tension
- Shear flow is positive in the counter-clockwise direction

Section forces
- Positive My moment yields compression in the top
- Positive Mz moment yields compression on the right side
- Negative N (normal force) yields compression
- Positive Vy yields shear in the y-direction (right)
- Positive Vz yields shear in the z-direction (upward)
- Positive T (torsion) yields counter-clockwise shear flow

", None, -1)) + self.label_24.setText(QtWidgets.QApplication.translate("MainWindow", "

", None, -1)) self.label_author.setText(QtWidgets.QApplication.translate("MainWindow", "

by Kenneth C. Kleissl
www.github.com/Kleissl/HollowRC

", None, -1)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_about), QtWidgets.QApplication.translate("MainWindow", "About", None, -1)) self.label.setText(QtWidgets.QApplication.translate("MainWindow", "

Define starting coordinates and properties of each wall element here:

", None, -1)) @@ -831,12 +831,12 @@ def retranslateUi(self, MainWindow): self.label_28.setText(QtWidgets.QApplication.translate("MainWindow", "- ", None, -1)) self.label_35.setText(QtWidgets.QApplication.translate("MainWindow", "- ", None, -1)) self.checkBox_analSLS_1.setStatusTip(QtWidgets.QApplication.translate("MainWindow", " Note: use linear elastic stress-strain for consistency with disk stresses", None, -1)) - self.checkBox_analSLS_1.setText(QtWidgets.QApplication.translate("MainWindow", "Plane dual-section & sigma-tau equilibrium (SLS)", None, -1)) + self.checkBox_analSLS_1.setText(QtWidgets.QApplication.translate("MainWindow", "Plane dual-section && sigma-tau equilibrium (SLS)", None, -1)) self.label_18.setText(QtWidgets.QApplication.translate("MainWindow", "

Note: use linear elastic stress-strain for consistency with membrane stresses

", None, -1)) - self.checkBox_analULS_1.setText(QtWidgets.QApplication.translate("MainWindow", "Plane section & optimizing tau (ULS)", None, -1)) - self.checkBox_analULS_2.setText(QtWidgets.QApplication.translate("MainWindow", "Optimizing sigma & tau (extreme ULS)", None, -1)) - self.checkBox_analSLS_2.setText(QtWidgets.QApplication.translate("MainWindow", "Minimizing comp. elastic energy for sigma & tau (extreme SLS)", None, -1)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_materials), QtWidgets.QApplication.translate("MainWindow", "Materials & Analysis", None, -1)) + self.checkBox_analULS_1.setText(QtWidgets.QApplication.translate("MainWindow", "Plane section && optimizing tau (ULS)", None, -1)) + self.checkBox_analULS_2.setText(QtWidgets.QApplication.translate("MainWindow", "Optimizing sigma && tau (extreme ULS)", None, -1)) + self.checkBox_analSLS_2.setText(QtWidgets.QApplication.translate("MainWindow", "Minimizing comp. elastic energy for sigma && tau (extreme SLS)", None, -1)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_materials), QtWidgets.QApplication.translate("MainWindow", "Materials && Analysis", None, -1)) self.SectionForces_tableWidget.verticalHeaderItem(0).setText(QtWidgets.QApplication.translate("MainWindow", "LC", None, -1)) self.SectionForces_tableWidget.horizontalHeaderItem(0).setText(QtWidgets.QApplication.translate("MainWindow", "N [kN]", None, -1)) self.SectionForces_tableWidget.horizontalHeaderItem(1).setText(QtWidgets.QApplication.translate("MainWindow", "My [kNm]", None, -1)) @@ -868,7 +868,7 @@ def retranslateUi(self, MainWindow): self.checkBox_plot8.setText(QtWidgets.QApplication.translate("MainWindow", "no plot data available", None, -1)) self.checkBox_plot9.setText(QtWidgets.QApplication.translate("MainWindow", "no plot data available", None, -1)) self.checkBox_plot10.setText(QtWidgets.QApplication.translate("MainWindow", "no plot data available", None, -1)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_loading), QtWidgets.QApplication.translate("MainWindow", "Loading & Results", None, -1)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_loading), QtWidgets.QApplication.translate("MainWindow", "Loading && Results", None, -1)) self.menuFile.setTitle(QtWidgets.QApplication.translate("MainWindow", "File", None, -1)) self.toolBar.setWindowTitle(QtWidgets.QApplication.translate("MainWindow", "toolBar", None, -1)) self.openAct.setText(QtWidgets.QApplication.translate("MainWindow", "Open", None, -1)) diff --git a/HollowRC/hollow_window.ui b/HollowRC/hollow_window.ui index be82e4f..3519f71 100644 --- a/HollowRC/hollow_window.ui +++ b/HollowRC/hollow_window.ui @@ -7,7 +7,7 @@ 0 0 1031 - 761 + 775 @@ -28,7 +28,7 @@ - 1 + 0 @@ -200,7 +200,7 @@ - + <html><head/><body><p><img src=":/Figures/resources/coordinates_and_sign_convention.png"/></p></body></html> :/Figures/resources/coordinates_and_sign_convention.PNG diff --git a/HollowRC/icon.ico b/HollowRC/resources/icon.ico similarity index 100% rename from HollowRC/icon.ico rename to HollowRC/resources/icon.ico diff --git a/HollowRC/scripting_interface.py b/HollowRC/scripting_interface.py new file mode 100644 index 0000000..4d13aaf --- /dev/null +++ b/HollowRC/scripting_interface.py @@ -0,0 +1,67 @@ +''' +Scripting interface for HollowRC + +Author: Kenneth C. Kleissl +''' +# imports +from Geometry import CrossSection, Wall +from Material import MatProp +from SectionForces import SectionForces +import Analysis + +# ----- Geometry ----- +thick = 600 +rho_long = 0.022 +rho_trans = 0.008 + +wall_1 = Wall([0, 5000], [7000, 7000], thick, rho_long, rho_trans) +wall_2 = Wall([5000, 5000], [7000, 0], thick, rho_long, rho_trans) +wall_3 = Wall([5000, 0], [0, 0], thick, rho_long, rho_trans) +wall_4 = Wall([0, 0], [0, 7000], thick, rho_long, rho_trans) + +section = CrossSection() +section.add_wall(wall_1) +section.add_wall(wall_2) +section.add_wall(wall_3) +section.add_wall(wall_4) +print(section) + +# check if geometry is valid +valid, msg = section.valid() +if not valid: + print('The defined geometry is not valid', msg) + +# ----- Material ----- +Mat = MatProp() +Mat.f_ck = 55.16 +Mat.f_yk = 414.0 +Mat.E_s = 200.0 +Mat.alpha_cc = 0.85 +Mat.gamma_c = 1.0 +Mat.gamma_s = 1.0 +Mat.update_strengths() # in case default f_ck was changed +Mat.set_methods('EN Parabolic-rectangular', 'Elastic-plastic') +Mat.update_conc_stiffness() # stiffnesses must be updated after strength update +print(Mat) + +# ----- SectionForces ----- +N = -200000 +My = 230000 +Mz = 0 +Vy = 51100 # only ok with inequality +# Vy = 51210 # only ok with equality constraint +Vz = 0 +T = 0 +SF = SectionForces(N, My, Mz, Vy, Vz, T) +print(SF) + +# ----- Analysis ----- +print() +try: + # result = Analysis.SLS_analysis(section, SF, Mat) + result, error_msg = Analysis.ULS_analysis(section, SF, Mat, printing=True) + print('error_msg:', error_msg) + +except Analysis.MyOptimizerError as e: + # caught a MyOptimizerError exception + print('exception msg:', [str(e), e.discription]) diff --git a/README.md b/README.md index a36f08c..07f2694 100644 --- a/README.md +++ b/README.md @@ -3,49 +3,60 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/41753e1cc75d466daf440d499e7543e1)](https://www.codacy.com/manual/Kleissl/HollowRC?utm_source=github.com&utm_medium=referral&utm_content=Kleissl/HollowRC&utm_campaign=Badge_Grade) # HollowRC + A Python libary for general design of hollow reinforced concrete sections under combined actions packed into an easy to use executable. ## Getting Started - Installation + The easy way: + 1. Download the [latest release](https://github.com/Kleissl/HollowRC/releases/latest/download/HollowRC.zip) 2. Unzip the distribution package 3. Execute HollowRC.exe If you're familar with Python and prefer to run the code directly: + 1. Fork/clone the repository or download the [zipped master-branch source code](https://github.com/Kleissl/HollowRC/archive/master.zip) 2. Pip install requirements.txt 3. Run python main.py ## About + This is an easy to use design tool for analysis of hollow reinforced concrete sections under combined loading, fully embracing the interactions between bending and shear behaviour. To propor describe the interaction between the flow of normal stesses and shear stress and to fully utilize the capacity of the cross-section one must leave the simplfied approach of the diagonal truss model. -For a linear-elastic material one could apply the Grashof's formula, similar to analysis of thin-walled steel-sections, to determining shear flow etc. +For a linear-elastic material one could apply the Grashof's formula, similar to analysis of thin-walled steel-sections, to determining shear flow etc. However, with the basic assumption of linear-elastic material behaviour being unsuitable for reinforced concrete, a more numerical extensive approach involving a series of optimization routines needs to be adopted. To make this analysis method more approachable, this easy to use application has been developed. ### Service Limit State + This design tool provides designers with a superior insight into the actual stress state during Service Limit State (SLS), including the shear or torsion induced stresses in the transverse reinforcement (in the circumferential direction), and will completely avoid any superposition of plastic lower bound methods from the diagonal truss model, additional shear-induced demand for longitudinal reinforcement and the corresponding strain incompatibilities introduced by separating shear and bending analysis. For SLS the actual shear flow is determined based on a plane dual-section analysis, which just means that two nabouring plane-sections are analysed and from their differences in normal flow, simple equilibrium yields the corresponding shear flow distribution. So only by applying the fundamental flexural member assumption of plane sections must remain plane combined with basic equilibrium equations can the actual normal and shear flow distributions be determined. From this an in-plane membrane analysis is used to determined the reinforcement stresses etc. by choosing the resulting compressive stress direction / strut angle such that it minimizes the complementary strain energy (similar to fulfilling compatibility). ### Ultimate Limit State + For Ultimate Limit State (ULS) this design tool allow designers to push the capacity of the cross-section even further, as it by use of mathematical optimization algorithms are able to identify the true optimal plastic lower-bound solution that fully utilize the strength of the materials. For ULS a classic plane section analysis is performed and from its normal flow distribution, an in-plane membrane analysis considering the yield conditions determines the leftover shear flow capacity at any given point along the cross-section, which then is integrated into a shear force capacity for each of the cross-sectional wall elements. Finally this is followed up by solving the optimization problem of maximizing the load-factor while maintaining equalibrium between the wall shear forces and the user specified global sectional forces. ### Assumptions and limitations + The implementation is based on the following assumptions: + * The span to depth ratio of the section is sufficient for beam theory to be applicable where plane section analysis approach is considered. * The walls are sufficient thin, compared to the cross-section dimensions, for a thin-walled approach to be applicable. * Normal stresses in the circumferential direction are neglected even though equilibrium in principle requires the presence of these (this is a very common approach when analysing thin-walled sections). * Sufficient minimum reinforcement to provide the nesessary ductility capacity for plastic redistribution. * Does not cover instabilities or shear lag effects (yet). -* For now it's limited closed single-cell hollow cross-sections +* For now it's limited closed single-cell hollow cross-sections ## Documentation + The complete verification methodology is described in [this paper](docs/Paper_Kleissl_Costa.pdf). ### Illustration of Graphical User Interface (GUI) + This sections shows a few screen-dumps of the GUI. The image below shows the "Geometry"-tab, where the cross-sectional geometry is defined. Features like specifying coordinates for new nodes via mouse click and movement of existing nodes via mouse drag-drop have been implemented to ease the input process. ![Geometry tab illustration](./docs/geometry_tab_illustration.png) @@ -58,20 +69,24 @@ An interactive shaded visualisation with overlaying of user-specified results al ![Result tab illustration](./docs/result_tab_illustration.png) ### Sign convention + The sign convention generally follows a right hand system (RHS). See illustration in figure below. ![Coordinates and sign convention](HollowRC/resources/coordinates_and_sign_convention.png) Geometry: + * All dimensions are in millimeters * The section walls must be defined in the clock-wise direction * Angles are taken positive in the counter-clockwise direction starting from the y-axis * Reinforcement ratios are defined as the steel-to-concrete ratio (e.g. 0.01 = 1%) Flows & Stresses: + * Normal flow and normal stress are positive for tension * Shear flow is positive in the counter-clockwise direction Section forces: + * Positive My moment yields compression in the top * Positive Mz moment yields compression on the right side * Negative N (normal force) yields compression @@ -80,17 +95,22 @@ Section forces: * Positive T (torsion) yields counter-clockwise shear flow ## Issues + If you experience a problem with the application please provide the feedback by raising an issue on this GitHub repository ([New issue](https://github.com/Kleissl/HollowRC/issues/new)). ## Versioning -For the versions available, see the [tags on this repository](https://github.com/Kleissl/HollowRC/tags). + +For the versions available, see the [tags on this repository](https://github.com/Kleissl/HollowRC/tags). ## Author + **Kenneth C. Kleissl** - [Kleissl](https://github.com/Kleissl) ## License + The HollowRC project is licensed under the GNU GPL license, so any code using it must also be under the same license - see the [LICENSE](LICENSE) file for details. ## Acknowledgments + * Thanks to the COWI Foundation for funding the development of the concept and drafting of the first working version. * Thanks to João Luís Domingues Costa for valuable discussions and feedback to the project