├── .gitignore ├── App.py ├── LICENSE ├── Procfile ├── README.md ├── assets └── custom.css ├── core.py ├── csv_read.py ├── cult_i.py ├── draw_diagram.py ├── images ├── Cult-I.png ├── Cult-I_formula.png ├── Excel.png └── Main_Screen.png ├── intersection.py ├── requirements.txt └── xls_read.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *.pyc 3 | .DS_Store 4 | .env 5 | .idea 6 | .vscode 7 | -------------------------------------------------------------------------------- /App.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import dash 3 | import dash_core_components as dcc 4 | import dash_html_components as html 5 | from dash.dependencies import Input, Output, State 6 | from draw_diagram import draw 7 | import dash_table 8 | import core 9 | import csv_read 10 | import xls_read 11 | import base64 12 | import io 13 | import pandas as pd 14 | import cult_i 15 | from textwrap import dedent 16 | 17 | # external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] 18 | 19 | 20 | def Header(title): 21 | return html.Div( 22 | style={'borderBottom': 'thin lightgrey solid', 'marginRight': 20}, 23 | children=[html.Div(title, style={'fontSize': 25})] 24 | ) 25 | 26 | 27 | def Row(children=None, **kwargs): 28 | return html.Div( 29 | children, 30 | className="row", 31 | **kwargs 32 | ) 33 | 34 | 35 | def Column(children=None, width=1, **kwargs): 36 | number_mapping = { 37 | 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 38 | 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', 11: 'eleven', 39 | 12: 'twelve' 40 | } 41 | return html.Section( 42 | children, 43 | className="{} columns".format(number_mapping[width]), 44 | **kwargs 45 | ) 46 | 47 | 48 | def NamedDropdown(myId, name, **kwargs): 49 | return html.Div( 50 | style={'margin': '5px 0px', 51 | 'fontSize': 12, 52 | 'font-family': 'Arial'}, 53 | children=[ 54 | html.P( 55 | children=f'{name}', 56 | style={'margin-left': '0px', 57 | 'fontSize': 12, 58 | 'font-family': 'Arial'} 59 | ), 60 | dcc.Dropdown(id=myId, **kwargs) 61 | ] 62 | ) 63 | 64 | 65 | def NamedInput(myId, name, **kwargs): 66 | return html.Div( 67 | style={'margin': '5px 0px', 68 | 'fontSize': 12, 69 | 'font-family': 'Arial'}, 70 | children=[ 71 | html.P( 72 | children=f'{name}', 73 | style={'margin-left': '0px', 74 | 'fontSize': 12, 75 | 'font-family': 'Arial'} 76 | ), 77 | dcc.Input(id=myId, **kwargs) 78 | ] 79 | ) 80 | 81 | 82 | def NamedRadioItems(myId, name, **kwargs): 83 | return html.Div( 84 | style={'margin': '5px 0px', 85 | 'fontSize': 12, 86 | 'font-family': 'Arial'}, 87 | children=[ 88 | html.P( 89 | children=f'{name}', 90 | style={'margin-left': '0px', 91 | 'fontSize': 12, 92 | 'font-family': 'Arial' 93 | } 94 | ), 95 | dcc.RadioItems(id=myId, **kwargs) 96 | ] 97 | ) 98 | 99 | 100 | def type_casting(number): 101 | """take the text input and type cast them into floating numbers""" 102 | try: 103 | number = float(number) 104 | except Exception: 105 | # exception occurs from comma utilization for decimal points 106 | number = float(number.replace(',', '.')) 107 | return number 108 | 109 | 110 | app = dash.Dash(__name__, 111 | # external_scripts=external_js, 112 | # external_stylesheets=external_stylesheets 113 | ) 114 | 115 | server = app.server 116 | 117 | 118 | app.layout = html.Div([ 119 | Header('Design of Reinforced Concrete Sections acc. to Eurocode 2 (EN ' 120 | '1992-1-1)'), 121 | Row([ 122 | dcc.Upload( 123 | id='upload-data', 124 | children=html.Div([ 125 | 'Drag and Drop File: ', 126 | html.I('"N-M.xlsx"'), ' or ', 127 | html.I('"ZSoil.csv"') 128 | ]), 129 | style={ 130 | 'width': '80%', 131 | 'height': '40px', 132 | 'lineHeight': '40px', 133 | 'borderWidth': '1px', 134 | 'borderStyle': 'dashed', 135 | 'borderRadius': '5px', 136 | 'textAlign': 'center', 137 | 'margin': '10px' 138 | }, 139 | multiple=False 140 | ), 141 | ]), 142 | Row([ 143 | Column(width=2, 144 | style={'width': '10%', 145 | 'display': 'inline-block', 146 | 'marginBottom': 0, 147 | 'marginTop': 0, 148 | 'marginLeft': 0, 149 | 'marginRight': 0, 150 | 'padding': 0}, 151 | children=[ 152 | html.Div([ 153 | NamedInput( 154 | myId='height', 155 | name='Height', 156 | type='text', 157 | value=0.35, 158 | style={'width': 150} 159 | ) 160 | ]), 161 | html.Div([ 162 | NamedInput( 163 | myId='width', 164 | name='Width', 165 | placeholder='Enter a value...', 166 | inputMode='numeric', 167 | type='text', 168 | value=1.0, 169 | style={'width': 150} 170 | ) 171 | ]), 172 | html.Div([ 173 | NamedInput( 174 | myId='concrete_cover_top', 175 | name='Concrete Cover, Top', 176 | placeholder='Enter a value...', 177 | type='text', 178 | value=0.03, 179 | style={'width': 150} 180 | ) 181 | ]), 182 | html.Div([ 183 | NamedInput( 184 | myId='concrete_cover_bottom', 185 | name='Concrete Cover, Bottom', 186 | placeholder='Enter a value...', 187 | type='text', 188 | value=0.03, 189 | style={'width': 150} 190 | ) 191 | ]), 192 | html.Div([ 193 | NamedDropdown( 194 | myId='gamma_concrete', 195 | name='Gamma Concrete', 196 | options=[ 197 | {'label': '1.50', 'value': 1.50}, 198 | {'label': '1.20', 'value': 1.20}, 199 | {'label': '1.00', 'value': 1.00}, 200 | {'label': '0.85', 'value': 0.85}, 201 | ], 202 | style={'width': 150}, 203 | multi=False, 204 | value=1.50 205 | ) 206 | ]), 207 | html.Div([ 208 | NamedDropdown( 209 | myId='gamma_steel', 210 | name='Gamma Steel', 211 | options=[ 212 | {'label': '1.15', 'value': 1.15}, 213 | {'label': '1.00', 'value': 1.00} 214 | ], 215 | style={'width': 150}, 216 | multi=False, 217 | value=1.15 218 | ) 219 | ]), 220 | html.Div([ 221 | NamedDropdown( 222 | myId='concrete_quality', 223 | name='Concrete quality', 224 | options=[ 225 | {'label': 'C20/25', 'value': 20}, 226 | {'label': 'C25/30', 'value': 25}, 227 | {'label': 'C30/37', 'value': 30}, 228 | {'label': 'C40/50', 'value': 40}, 229 | ], 230 | style={'width': 150}, 231 | multi=False, 232 | value=20 233 | ) 234 | ]), 235 | html.Div([ 236 | NamedDropdown( 237 | myId='steel_quality', 238 | name='Steel quality', 239 | options=[ 240 | {'label': 'B420', 'value': 420}, 241 | {'label': 'B500', 'value': 500}, 242 | {'label': 'B550', 'value': 550}, 243 | ], 244 | style={'width': 150}, 245 | multi=False, 246 | value=550 247 | ) 248 | ]), 249 | html.Div([ 250 | NamedDropdown( 251 | myId='load_factor', 252 | name='Load factor', 253 | options=[ 254 | {'label': '1.35', 'value': 1.35}, 255 | {'label': '1.40', 'value': 1.40}, 256 | {'label': '1.00', 'value': 1.00}, 257 | ], 258 | style={'width': 150}, 259 | multi=False, 260 | value=1.35 261 | ) 262 | ]), 263 | html.Div([ 264 | NamedInput( 265 | myId='reinforcement_area_top', 266 | name='Reinforcement Area, Top', 267 | placeholder='Enter a value...', 268 | type='text', 269 | value=2.58, 270 | style={'width': 150} 271 | ) 272 | ]), 273 | html.Div([ 274 | NamedInput( 275 | myId='reinforcement_area_bottom', 276 | name='Reinforcement Area, Bottom', 277 | placeholder='Enter a value...', 278 | type='text', 279 | value=2.58, 280 | style={'width': 150} 281 | ) 282 | ]), 283 | html.Div([ 284 | NamedInput( 285 | myId='alpha_cc', 286 | name='Alpha cc', 287 | placeholder='Enter a value...', 288 | type='text', 289 | value=1.00, 290 | style={'width': 150} 291 | ) 292 | ]), 293 | html.Div([ 294 | NamedRadioItems( 295 | myId='eccentricity', 296 | name='Limit capacity?', 297 | options=[ 298 | {'label': 'Yes', 'value': 'yes'}, 299 | {'label': 'No', 'value': 'no'} 300 | ], 301 | value='no', 302 | labelStyle={'display': 'inline-block'} 303 | ) 304 | ]), 305 | html.Div([ 306 | NamedRadioItems( 307 | myId='cult-i', 308 | name='CULT-I?', 309 | options=[ 310 | {'label': 'Yes', 'value': 'yes'}, 311 | {'label': 'No', 'value': 'no'} 312 | ], 313 | value='yes', 314 | labelStyle={'display': 'inline-block'} 315 | ) 316 | ]) 317 | ]), 318 | Column( 319 | width=5, 320 | style={'width': '60%', 321 | 'display': 'inline-block', 322 | 'marginBottom': 0, 323 | 'marginTop': 0, 324 | 'marginLeft': 0, 325 | 'marginRight': 0, 326 | 'padding': 0}, 327 | children=[ 328 | html.Div(id='output-diagram'), 329 | Row(id='Source Code', 330 | children=[ 331 | dcc.Markdown(dedent( 332 | ''' 333 | [Source Code](https://github.com/onurkoc/interaction-diagram) 334 | '''))] 335 | ) 336 | ] 337 | ), 338 | Column( 339 | width=3, 340 | style={'width': '10%', 341 | 'display': 'inline-block', 342 | 'marginBottom': 0, 343 | 'marginTop': 0, 344 | 'marginLeft': 0, 345 | 'marginRight': 0, 346 | 'padding': 0}, 347 | children=[ 348 | html.Br(), 349 | html.P('Design Values', 350 | style={'color': 'red', 'fontSize': 18}), 351 | html.Div(id='output-table') 352 | ] 353 | ) 354 | ]) 355 | ]) 356 | 357 | 358 | def parse_contents(contents, filename, last_modified): 359 | file_A = contents 360 | content_type, content_string = file_A.split(',') 361 | decoded = base64.b64decode(content_string) 362 | try: 363 | if 'csv' in filename: 364 | # Assuming user uploaded a csv file 365 | try: 366 | decoded_str = io.StringIO(decoded.decode('utf-8')) 367 | x, y = csv_read.read(decoded_str) 368 | except Exception as e: 369 | print(e) 370 | return html.H3(['There has been an upload error
First ' 371 | 'exception']) 372 | elif 'xls' in filename: 373 | # Assuming user uploaded an excel file 374 | decoded_str = io.BytesIO(decoded) 375 | x, y = xls_read.read(decoded_str) 376 | else: 377 | return html.H3(['There has been an upload error
Second ' 378 | 'exception']) 379 | except Exception as e: 380 | print(e) 381 | return html.H3(['There has been an upload error
Third exception']) 382 | else: 383 | return x, y 384 | 385 | 386 | @app.callback( 387 | Output(component_id='output-diagram', component_property='children'), 388 | [Input(component_id='height', component_property='value'), 389 | Input(component_id='width', component_property='value'), 390 | Input(component_id='concrete_cover_top', component_property='value'), 391 | Input(component_id='concrete_cover_bottom', component_property='value'), 392 | Input(component_id='gamma_concrete', component_property='value'), 393 | Input(component_id='gamma_steel', component_property='value'), 394 | Input(component_id='load_factor', component_property='value'), 395 | Input(component_id='reinforcement_area_top', component_property='value'), 396 | Input(component_id='reinforcement_area_bottom', 397 | component_property='value'), 398 | Input(component_id='concrete_quality', component_property='value'), 399 | Input(component_id='steel_quality', component_property='value'), 400 | Input(component_id='alpha_cc', component_property='value'), 401 | Input(component_id='eccentricity', component_property='value'), 402 | Input(component_id='cult-i', component_property='value'), 403 | Input(component_id='upload-data', component_property='contents')], 404 | [State(component_id='upload-data', component_property='filename'), 405 | State(component_id='upload-data', component_property='last_modified')] 406 | ) 407 | def update_output_fig(h, b, d_1, d_2, gamma_c, gamma_s, gamma_d, a_s1, a_s2, 408 | f_ck, f_yk, alpha_cc, eccentricity, cult, contents, 409 | filename, 410 | last_modified): 411 | # type casting text into floating numbers: 412 | h = type_casting(h) 413 | b = type_casting(b) 414 | d_1 = type_casting(d_1) 415 | d_2 = type_casting(d_2) 416 | gamma_c = type_casting(gamma_c) 417 | gamma_s = type_casting(gamma_s) 418 | a_s1 = type_casting(a_s1) 419 | a_s2 = type_casting(a_s2) 420 | alpha_cc = type_casting(alpha_cc) 421 | 422 | if filename is not None: 423 | try: 424 | x, y = parse_contents(contents, filename, last_modified) 425 | except (ValueError, TypeError, AttributeError) as e: 426 | print(e) 427 | return html.H3([ 428 | 'Please feed data' 429 | ]) 430 | else: 431 | return html.H3(['No data available']) 432 | 433 | if eccentricity == 'yes': 434 | ecc = True 435 | else: 436 | ecc = False 437 | 438 | values = core.int_diagram(h_=h, b_=b, d_1=d_1, d_2=d_2, gamma_c=gamma_c, 439 | gamma_s=gamma_s, gamma_d=gamma_d, a_s1=a_s1, 440 | a_s2=a_s2, f_ck=f_ck, f_yk=f_yk, 441 | alpha_cc=alpha_cc, eccentricity=ecc) 442 | i_val, val = values 443 | 444 | if cult == 'yes': 445 | index = [] 446 | for m, n in zip(gamma_d*x/1000, gamma_d*y/1000): 447 | index.append(cult_i.cult_I(m=m, 448 | n=n, 449 | input_values=i_val, 450 | values=val) 451 | ) 452 | val['cult-I'] = index 453 | values = (i_val, val) 454 | try: 455 | graph = dcc.Graph( 456 | figure=draw(values=values, 457 | x=gamma_d*x/1000, 458 | y=gamma_d*y/1000) 459 | ) 460 | except (NameError, TypeError) as e: 461 | print(e) 462 | return html.H3(['No data available']) 463 | else: 464 | return graph 465 | 466 | 467 | @app.callback( 468 | Output(component_id='output-table', component_property='children'), 469 | [Input(component_id='upload-data', component_property='contents'), 470 | Input(component_id='load_factor', component_property='value')], 471 | [State(component_id='upload-data', component_property='filename'), 472 | State(component_id='upload-data', component_property='last_modified')] 473 | ) 474 | def update_output_table(contents, factor, filename, last_modified): 475 | if contents is None: 476 | return 477 | x, y = parse_contents(contents, filename, last_modified) 478 | df = pd.DataFrame.from_dict({'m [kN.m]': (factor*x).round(decimals=3), 479 | 'n [kN]': (factor*y).round(decimals=1)}) 480 | return dash_table.DataTable(data=df.to_dict('rows'), 481 | columns=[{'id': c, 'name': c} 482 | for c in df.columns], 483 | style_table={'maxHeight': '500', 484 | 'overflowY': 'scroll'}, 485 | style_header={ 486 | 'fontWeight': 'bold' 487 | }, 488 | n_fixed_rows=1, 489 | style_as_list_view=True, 490 | style_cell={ 491 | 'minWidth': '0px', 492 | 'maxWidth': '20px', 493 | 'textAlign': 'center' 494 | } 495 | ) 496 | 497 | 498 | if __name__ == '__main__': 499 | app.run_server(debug=True) 500 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 onurkoc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn App:server -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interaction Diagram according to Eurocode 2 2 | The source code for an interaction diagram for reinforced concrete sections according to EN 1992-1-1 is presented in this repository. 3 | 4 | Works with Python > 3.5 5 | 6 | ![Main_Screen](https://github.com/onurkoc/interaction-diagram/blob/master/images/Main_Screen.png) 7 | 8 | 👉 [View the Dash App](https://interaction-diagram.herokuapp.com/) 9 | 10 | ### How does it work? 11 | - Either open up your Excel and type your (characteristical) normal force (n) and moment values (m) as shown below (they can be unlimited):
12 | Attention: Your file format should be .xlsx
13 | ![Excel](https://github.com/onurkoc/interaction-diagram/blob/master/images/Excel.png)
14 | Feed this file to the dashed region per drag & drop
15 | - or feed a raw [ZSoil](https://www.zsoil.com/) .csv file per drag & drop to the dashed region 16 |

17 | ### Limiting Capacity 18 | The limit of the capacity can also be activated using the radio buttons left below the screen.
19 | See minimum eccentricity e0=h/30 > 20mm EN1992-1-1 §6.1 (4) 20 |

21 | ### CULT-I 22 | The concept CULT-I (Index for capacity utilization of linings in tunnels) is introduced in the following scientific paper:
23 |       Published in: Geomechanics and Tunnelling 9 (2016), No.2
24 |       Title: **Performance indicator of tunnel linings under geotechnical uncertainty**
25 |       Authors: Spyridis, Panagiotis; Konstantis, Spyridon; Gakis, Angelos
26 | 27 | CULT-I is calculated using the following formula:
28 | *0 meaning totally utilized, 1 meaning no utilization at all!*
29 | ![CULT-I_Formula](https://github.com/onurkoc/interaction-diagram/blob/master/images/Cult-I_formula.png)
30 | 31 | where
32 | ![CULT-I](https://github.com/onurkoc/interaction-diagram/blob/master/images/Cult-I.png)
33 | 34 | ### License 35 | This project is licensed under the MIT License - see the [License](https://github.com/onurkoc/interaction-diagram/blob/master/LICENSE) for details 36 | -------------------------------------------------------------------------------- /assets/custom.css: -------------------------------------------------------------------------------- 1 | /* Table of contents 2 | –––––––––––––––––––––––––––––––––––––––––––––––––– 3 | - Plotly.js 4 | - Grid 5 | - Base Styles 6 | - Typography 7 | - Links 8 | - Buttons 9 | - Forms 10 | - Lists 11 | - Code 12 | - Tables 13 | - Spacing 14 | - Utilities 15 | - Clearing 16 | - Media Queries 17 | */ 18 | 19 | /* PLotly.js 20 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 21 | /* plotly.js's modebar's z-index is 1001 by default 22 | * https://github.com/plotly/plotly.js/blob/7e4d8ab164258f6bd48be56589dacd9bdd7fded2/src/css/_modebar.scss#L5 23 | * In case a dropdown is above the graph, the dropdown's options 24 | * will be rendered below the modebar 25 | * Increase the select option's z-index 26 | */ 27 | 28 | /* This was actually not quite right - 29 | dropdowns were overlapping each other (edited October 26) 30 | 31 | .Select { 32 | z-index: 1002; 33 | }*/ 34 | 35 | /* Grid 36 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 37 | .container { 38 | position: relative; 39 | width: 100%; 40 | max-width: 960px; 41 | margin: 0 auto; 42 | padding: 0 20px; 43 | box-sizing: border-box; } 44 | .column, 45 | .columns { 46 | width: 100%; 47 | float: left; 48 | box-sizing: border-box; } 49 | 50 | /* For devices larger than 400px */ 51 | @media (min-width: 400px) { 52 | .container { 53 | width: 85%; 54 | padding: 0; } 55 | } 56 | 57 | /* For devices larger than 550px */ 58 | @media (min-width: 550px) { 59 | .container { 60 | width: 80%; } 61 | .column, 62 | .columns { 63 | margin-left: 4%; } 64 | .column:first-child, 65 | .columns:first-child { 66 | margin-left: 0; } 67 | 68 | .one.column, 69 | .one.columns { width: 4.66666666667%; } 70 | .two.columns { width: 13.3333333333%; } 71 | .three.columns { width: 22%; } 72 | .four.columns { width: 30.6666666667%; } 73 | .five.columns { width: 39.3333333333%; } 74 | .six.columns { width: 48%; } 75 | .seven.columns { width: 56.6666666667%; } 76 | .eight.columns { width: 65.3333333333%; } 77 | .nine.columns { width: 74.0%; } 78 | .ten.columns { width: 82.6666666667%; } 79 | .eleven.columns { width: 91.3333333333%; } 80 | .twelve.columns { width: 100%; margin-left: 0; } 81 | 82 | .one-third.column { width: 30.6666666667%; } 83 | .two-thirds.column { width: 65.3333333333%; } 84 | 85 | .one-half.column { width: 48%; } 86 | 87 | /* Offsets */ 88 | .offset-by-one.column, 89 | .offset-by-one.columns { margin-left: 8.66666666667%; } 90 | .offset-by-two.column, 91 | .offset-by-two.columns { margin-left: 17.3333333333%; } 92 | .offset-by-three.column, 93 | .offset-by-three.columns { margin-left: 26%; } 94 | .offset-by-four.column, 95 | .offset-by-four.columns { margin-left: 34.6666666667%; } 96 | .offset-by-five.column, 97 | .offset-by-five.columns { margin-left: 43.3333333333%; } 98 | .offset-by-six.column, 99 | .offset-by-six.columns { margin-left: 52%; } 100 | .offset-by-seven.column, 101 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 102 | .offset-by-eight.column, 103 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 104 | .offset-by-nine.column, 105 | .offset-by-nine.columns { margin-left: 78.0%; } 106 | .offset-by-ten.column, 107 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 108 | .offset-by-eleven.column, 109 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 110 | 111 | .offset-by-one-third.column, 112 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 113 | .offset-by-two-thirds.column, 114 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 115 | 116 | .offset-by-one-half.column, 117 | .offset-by-one-half.columns { margin-left: 52%; } 118 | 119 | } 120 | 121 | 122 | /* Base Styles 123 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 124 | /* NOTE 125 | html is set to 62.5% so that all the REM measurements throughout Skeleton 126 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 127 | html { 128 | font-size: 62.5%; } 129 | body { 130 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 131 | line-height: 1.6; 132 | font-weight: 400; 133 | font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 134 | color: rgb(50, 50, 50); } 135 | 136 | 137 | /* Typography 138 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 139 | h1, h2, h3, h4, h5, h6 { 140 | margin-top: 0; 141 | margin-bottom: 0; 142 | font-weight: 300; } 143 | h1 { font-size: 4.5rem; line-height: 1.2; letter-spacing: -.1rem; margin-bottom: 2rem; } 144 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; margin-bottom: 1.8rem; margin-top: 1.8rem;} 145 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; margin-bottom: 1.5rem; margin-top: 1.5rem;} 146 | h4 { font-size: 2.6rem; line-height: 1.35; letter-spacing: -.08rem; margin-bottom: 1.2rem; margin-top: 1.2rem;} 147 | h5 { font-size: 2.2rem; line-height: 1.5; letter-spacing: -.05rem; margin-bottom: 0.6rem; margin-top: 0.6rem;} 148 | h6 { font-size: 2.0rem; line-height: 1.6; letter-spacing: 0; margin-bottom: 0.75rem; margin-top: 0.75rem;} 149 | 150 | p { 151 | margin-top: 0; } 152 | 153 | 154 | /* Blockquotes 155 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 156 | blockquote { 157 | border-left: 4px lightgrey solid; 158 | padding-left: 1rem; 159 | margin-top: 2rem; 160 | margin-bottom: 2rem; 161 | margin-left: 0rem; 162 | } 163 | 164 | 165 | /* Links 166 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 167 | a { 168 | color: #1EAEDB; 169 | text-decoration: underline; 170 | cursor: pointer;} 171 | a:hover { 172 | color: #0FA0CE; } 173 | 174 | 175 | /* Buttons 176 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 177 | .button, 178 | button, 179 | input[type="submit"], 180 | input[type="reset"], 181 | input[type="button"] { 182 | display: inline-block; 183 | height: 38px; 184 | padding: 0 30px; 185 | color: #555; 186 | text-align: center; 187 | font-size: 11px; 188 | font-weight: 600; 189 | line-height: 38px; 190 | letter-spacing: .1rem; 191 | text-transform: uppercase; 192 | text-decoration: none; 193 | white-space: nowrap; 194 | background-color: transparent; 195 | border-radius: 4px; 196 | border: 1px solid #bbb; 197 | cursor: pointer; 198 | box-sizing: border-box; } 199 | .button:hover, 200 | button:hover, 201 | input[type="submit"]:hover, 202 | input[type="reset"]:hover, 203 | input[type="button"]:hover, 204 | .button:focus, 205 | button:focus, 206 | input[type="submit"]:focus, 207 | input[type="reset"]:focus, 208 | input[type="button"]:focus { 209 | color: #333; 210 | border-color: #888; 211 | outline: 0; } 212 | .button.button-primary, 213 | button.button-primary, 214 | input[type="submit"].button-primary, 215 | input[type="reset"].button-primary, 216 | input[type="button"].button-primary { 217 | color: #FFF; 218 | background-color: #33C3F0; 219 | border-color: #33C3F0; } 220 | .button.button-primary:hover, 221 | button.button-primary:hover, 222 | input[type="submit"].button-primary:hover, 223 | input[type="reset"].button-primary:hover, 224 | input[type="button"].button-primary:hover, 225 | .button.button-primary:focus, 226 | button.button-primary:focus, 227 | input[type="submit"].button-primary:focus, 228 | input[type="reset"].button-primary:focus, 229 | input[type="button"].button-primary:focus { 230 | color: #FFF; 231 | background-color: #1EAEDB; 232 | border-color: #1EAEDB; } 233 | 234 | 235 | /* Forms 236 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 237 | input[type="email"], 238 | input[type="number"], 239 | input[type="search"], 240 | input[type="text"], 241 | input[type="tel"], 242 | input[type="url"], 243 | input[type="password"], 244 | textarea, 245 | select { 246 | height: 38px; 247 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 248 | background-color: #fff; 249 | border: 1px solid #D1D1D1; 250 | border-radius: 4px; 251 | box-shadow: none; 252 | box-sizing: border-box; 253 | font-family: inherit; 254 | font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/} 255 | /* Removes awkward default styles on some inputs for iOS */ 256 | input[type="email"], 257 | input[type="number"], 258 | input[type="search"], 259 | input[type="text"], 260 | input[type="tel"], 261 | input[type="url"], 262 | input[type="password"], 263 | textarea { 264 | -webkit-appearance: none; 265 | -moz-appearance: none; 266 | appearance: none; } 267 | textarea { 268 | min-height: 65px; 269 | padding-top: 6px; 270 | padding-bottom: 6px; } 271 | input[type="email"]:focus, 272 | input[type="number"]:focus, 273 | input[type="search"]:focus, 274 | input[type="text"]:focus, 275 | input[type="tel"]:focus, 276 | input[type="url"]:focus, 277 | input[type="password"]:focus, 278 | textarea:focus, 279 | select:focus { 280 | border: 1px solid #33C3F0; 281 | outline: 0; } 282 | label, 283 | legend { 284 | display: block; 285 | margin-bottom: 0px; } 286 | fieldset { 287 | padding: 0; 288 | border-width: 0; } 289 | input[type="checkbox"], 290 | input[type="radio"] { 291 | display: inline; } 292 | label > .label-body { 293 | display: inline-block; 294 | margin-left: .5rem; 295 | font-weight: normal; } 296 | 297 | 298 | /* Lists 299 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 300 | ul { 301 | list-style: circle inside; } 302 | ol { 303 | list-style: decimal inside; } 304 | ol, ul { 305 | padding-left: 0; 306 | margin-top: 0; } 307 | ul ul, 308 | ul ol, 309 | ol ol, 310 | ol ul { 311 | margin: 1.5rem 0 1.5rem 3rem; 312 | font-size: 90%; } 313 | li { 314 | margin-bottom: 1rem; } 315 | 316 | 317 | /* Tables 318 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 319 | table { 320 | border-collapse: collapse; 321 | } 322 | th, 323 | td { 324 | padding: 12px 15px; 325 | text-align: left; 326 | border-bottom: 1px solid #E1E1E1; } 327 | th:first-child, 328 | td:first-child { 329 | padding-left: 0; } 330 | th:last-child, 331 | td:last-child { 332 | padding-right: 0; } 333 | 334 | 335 | /* Spacing 336 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 337 | button, 338 | .button { 339 | margin-bottom: 0rem; } 340 | input, 341 | textarea, 342 | select, 343 | fieldset { 344 | margin-bottom: 0rem; } 345 | pre, 346 | dl, 347 | figure, 348 | table, 349 | form { 350 | margin-bottom: 0rem; } 351 | p, 352 | ul, 353 | ol { 354 | margin-bottom: 0.75rem; } 355 | 356 | /* Utilities 357 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 358 | .u-full-width { 359 | width: 100%; 360 | box-sizing: border-box; } 361 | .u-max-full-width { 362 | max-width: 100%; 363 | box-sizing: border-box; } 364 | .u-pull-right { 365 | float: right; } 366 | .u-pull-left { 367 | float: left; } 368 | 369 | 370 | /* Misc 371 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 372 | hr { 373 | margin-top: 3rem; 374 | margin-bottom: 3.5rem; 375 | border-width: 0; 376 | border-top: 1px solid #E1E1E1; } 377 | 378 | 379 | /* Clearing 380 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 381 | 382 | /* Self Clearing Goodness */ 383 | .container:after, 384 | .row:after, 385 | .u-cf { 386 | content: ""; 387 | display: table; 388 | clear: both; } 389 | 390 | 391 | /* Media Queries 392 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 393 | /* 394 | Note: The best way to structure the use of media queries is to create the queries 395 | near the relevant code. For example, if you wanted to change the styles for buttons 396 | on small devices, paste the mobile query code up in the buttons section and style it 397 | there. 398 | */ 399 | 400 | 401 | /* Larger than mobile */ 402 | @media (min-width: 400px) {} 403 | 404 | /* Larger than phablet (also point when grid becomes active) */ 405 | @media (min-width: 550px) {} 406 | 407 | /* Larger than tablet */ 408 | @media (min-width: 750px) {} 409 | 410 | /* Larger than desktop */ 411 | @media (min-width: 1000px) {} 412 | 413 | /* Larger than Desktop HD */ 414 | @media (min-width: 1200px) {} 415 | 416 | /* Turn off undo-redo button 417 | */ 418 | ._dash-undo-redo { 419 | display: none; 420 | } -------------------------------------------------------------------------------- /core.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import pandas as pd 4 | import intersection as inter 5 | 6 | 7 | def int_diagram(h_=0.35, 8 | b_=1.0, 9 | d_1=0.03, 10 | d_2=0.03, 11 | gamma_c=1.50, 12 | gamma_s=1.15, 13 | gamma_d=1.35, 14 | a_s1=5, 15 | a_s2=5, 16 | f_ck=40, 17 | f_yk=500, 18 | alpha_cc=1.00, 19 | eccentricity=False): 20 | 21 | # epsilon values of the steel and the concrete 22 | # a. steel strains tension side 23 | epsilon_s1 = np.array([ 24 | -0.003499, -0.0032, -0.0029, -0.0026, -0.0023, -0.002, -0.0017, 25 | -0.0014, -0.0011, -0.0008, -0.0005, -0.0002, 0.0, 0.0004, 0.0007, 26 | 0.001, 0.0013, 0.0016, 0.0019, 0.0022, 0.0025, 0.0028, 0.0031, 27 | 0.0034, 0.0037, 0.004, 0.0043, 0.0049, 0.0052, 0.0055, 0.006, 28 | 0.0065, 0.007, 0.0075, 0.008, 0.009, 0.0095, 0.01, 0.011, 0.012, 29 | 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 0.012, 30 | 0.012, 0.012, 0.012]) 31 | 32 | # b. concrete strains compression side 33 | epsilon_c = np.array([ 34 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, 35 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, 36 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, 37 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, 38 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, 39 | -0.0035, -0.0035, -0.0035, -0.0035, -0.0035, -0.0032, -0.0029, 40 | -0.0026, -0.0023, -0.002, -0.0017, -0.0014, -0.0011, -0.0008, 41 | -0.0005, -0.0002, 0.0]) 42 | 43 | e_steel = 200000 # N/mm2 44 | try: 45 | alpha = a_s1 / a_s2 46 | except ZeroDivisionError: 47 | # in case a_s2 defined as zero 48 | alpha = 0 49 | if alpha_cc < 0.85: 50 | # alpha_cc cannot be smaller than 0.85 51 | alpha_cc = 0.85 52 | f_cd = f_ck / gamma_c * alpha_cc 53 | f_yd = f_yk / gamma_s 54 | d = h_ - d_1 55 | a_s1_ = 0 # indicating naked concrete interaction line 56 | alpha_ = 1 # again for naked concrete interaction line 57 | 58 | min_ecc = min(h_ / 30, 0.02) 59 | # min eccentricity as defined in EN1992-1 6.1(4) 60 | n_max = f_ck * b_ * d 61 | m_min = min_ecc * n_max 62 | 63 | # mapping the epsilon values as pandas data frame 64 | df = pd.DataFrame({'eps_c': epsilon_c, 65 | 'eps_s1': epsilon_s1}) 66 | 67 | # iterating for every epsilon value pair 68 | moment = [] 69 | n_force = [] 70 | moment_reinf = [] 71 | n_force_reinf = [] 72 | for i in range(len(df.eps_c)): 73 | # calling eps_c and eps_s1 from now on as y and x 74 | x = df.eps_s1[i] 75 | y = df.eps_c[i] 76 | 77 | if d <= 0 or a_s1 < 0 or a_s2 < 0 or b_ <= 0 or gamma_c < 0 or \ 78 | gamma_d < 0 or gamma_s < 0 or d_1 < 0 or d_2 < 0 or alpha_cc <= 0: 79 | # parameter problems returns NoneType 80 | return 81 | 82 | # calling for rectangular part 83 | if (-0.002 - y) * d / (x - y) > 0: 84 | xi1 = min((-0.002 - y) * d / (x - y), h_) 85 | else: 86 | xi1 = 0 87 | 88 | # compression zone height 89 | xi2 = min((-y * d) / (x - y), h_) 90 | 91 | # factor (Hilfswert) 92 | h_w = (y - x) / d 93 | 94 | # compression force of concrete 95 | f_c = f_cd * b_ * \ 96 | (-xi1 + (xi2 - xi1) * (1000 * y + 250000 * y ** 2) - 97 | (xi2 ** 2 - xi1 ** 2) * (500 * h_w + 250000 * h_w * y) + 98 | (xi2 ** 3 - xi1 ** 3) * 250000 * h_w ** 2 / 3) 99 | 100 | # moment concrete 101 | m_c = f_cd * b_ * (-0.5 * xi1 ** 2 + 0.5 * 102 | (1000 * y + 250000 * y ** 2) * 103 | (xi2 ** 2 - xi1 ** 2) - 104 | (1000 + 500000 * y) * h_w / 3 * 105 | (xi2 ** 3 - xi1 ** 3) + 106 | 62500 * h_w ** 2 * 107 | (xi2 ** 4 - xi1 ** 4)) 108 | 109 | # eccentricity concrete (avoid division by zero) 110 | if m_c == 0 or f_c == 0: 111 | e_ausm = h_ / 2 112 | else: 113 | e_ausm = h_ / 2 - m_c / f_c 114 | 115 | # steel strain on the compression side 116 | 117 | eps_s2 = y - (y - x) / d * d_2 118 | 119 | # compression force steel 120 | f_s2 = (math.copysign(1, eps_s2) * min((e_steel * abs(eps_s2)), 121 | f_yd) * alpha_ * a_s1_ / 1000) 122 | 123 | # tension force steel 124 | f_s1 = math.copysign(1, x) * min((e_steel * abs(x)), 125 | f_yd) * a_s1_ / 1000 126 | 127 | # moment 128 | m_r = f_s1 * (h_ / 2 - d_1) - f_s2 * (h_ / 2 - d_2) - f_c * e_ausm 129 | 130 | # normal force 131 | n_r = f_s2 + f_s1 + f_c 132 | 133 | # m und n for pure concrete 134 | moment.append(m_r) 135 | n_force.append(n_r) 136 | 137 | # compression force steel (reinforcement) 138 | f_s2_reinf = math.copysign(1, eps_s2) * \ 139 | min((e_steel * abs(eps_s2)), f_yd) * alpha * a_s1 / 10000 140 | 141 | # tension force steel (reinforcement) 142 | f_s1_reinf = math.copysign(1, x) * \ 143 | min((e_steel * abs(x)), f_yd) * a_s1 / 10000 144 | 145 | # moment (reinforcement) 146 | m_r_reinf = (f_s1_reinf * (h_ / 2 - d_1) - f_s2_reinf * 147 | (h_ / 2 - d_2) - f_c * e_ausm) 148 | 149 | # normal force (reinforcement) 150 | n_r_reinf = f_s2_reinf + f_s1_reinf + f_c 151 | 152 | # m und n (reinforcement) 153 | moment_reinf.append(m_r_reinf) 154 | n_force_reinf.append(n_r_reinf) 155 | 156 | moment_neg = [] 157 | # left side of the diagram (concrete) 158 | for i in range(len(moment)): 159 | moment_neg.append(float(abs(moment[i]) * -1)) 160 | 161 | moment_reinf_neg = [] 162 | # left side of the diagram (with reinforcement) 163 | for i in range(len(moment_reinf)): 164 | moment_reinf_neg.append(float(abs(moment_reinf[i]) * -1)) 165 | 166 | if eccentricity: 167 | # if user wants to visualize the eccentricity line 168 | limit_line_pos_m = [m_min, m_min] 169 | limit_line_neg_m = [-m_min, -m_min] 170 | limit_line_n = [0, -n_max] 171 | 172 | if a_s1 == 0 and a_s2 == 0: 173 | # no reinforcements 174 | x1, y1 = inter.intersection(np.array(limit_line_neg_m), 175 | np.array(limit_line_n), 176 | np.array(moment_neg), 177 | np.array(n_force)) 178 | x2, y2 = inter.intersection(np.array(limit_line_pos_m), 179 | np.array(limit_line_n), 180 | np.array(moment), 181 | np.array(n_force)) 182 | elif a_s1 != 0: 183 | x1, y1 = inter.intersection(np.array(limit_line_neg_m), 184 | np.array(limit_line_n), 185 | np.array(moment_reinf_neg), 186 | np.array(n_force_reinf)) 187 | if a_s2 == 0: 188 | # only a_s1 defined 189 | x2, y2 = inter.intersection(np.array(limit_line_pos_m), 190 | np.array(limit_line_n), 191 | np.array(moment), 192 | np.array(n_force)) 193 | else: 194 | # both of them defined 195 | x2, y2 = inter.intersection(np.array(limit_line_pos_m), 196 | np.array(limit_line_n), 197 | np.array(moment_reinf), 198 | np.array(n_force_reinf)) 199 | elif a_s2 != 0 and a_s1 == 0: 200 | x1, y1 = inter.intersection(np.array(limit_line_pos_m), 201 | np.array(limit_line_n), 202 | np.array(moment), 203 | np.array(n_force)) 204 | x2, y2 = inter.intersection(np.array(limit_line_pos_m), 205 | np.array(limit_line_n), 206 | np.array(moment_reinf), 207 | np.array(n_force_reinf)) 208 | else: 209 | raise Exception('Cannot calculate eccentricity') 210 | 211 | input_values = {'h': h_, 212 | 'b': b_, 213 | 'd_1': d_1, 214 | 'd_2': d_2, 215 | 'gamma_c': gamma_c, 216 | 'gamma_s': gamma_s, 217 | 'gamma_d': gamma_d, 218 | 'a_s1': a_s1, 219 | 'a_s2': a_s2, 220 | 'f_ck': f_ck, 221 | 'f_yk': f_yk, 222 | 'alpha_cc': alpha_cc, 223 | 'eccentricity': eccentricity} 224 | if eccentricity: 225 | values = {'Moment': moment, 226 | 'Moment Neg': moment_neg, 227 | 'Normal Force': n_force, 228 | 'Moment Reinf': moment_reinf, 229 | 'Moment Reinf Neg': moment_reinf_neg, 230 | 'Normal Force Reinf': n_force_reinf, 231 | 'x1_y1': [x1, y1], 232 | 'x2_y2': [x2, y2]} 233 | else: 234 | values = {'Moment': moment, 235 | 'Moment Neg': moment_neg, 236 | 'Normal Force': n_force, 237 | 'Moment Reinf': moment_reinf, 238 | 'Moment Reinf Neg': moment_reinf_neg, 239 | 'Normal Force Reinf': n_force_reinf} 240 | 241 | return input_values, values 242 | 243 | 244 | if __name__ == '__main__': 245 | i_val, val = int_diagram(eccentricity=True) # initiate with default values 246 | # i['X'] = np.array([0, 100]) 247 | # i['Y'] = np.array([0, -1000]) 248 | print(val['x1_y1']) 249 | print(val['x2_y2']) 250 | print(max(np.array(val['Moment']))) 251 | print(max(np.array(val['Moment Reinf']))) 252 | -------------------------------------------------------------------------------- /csv_read.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | 4 | def read(data: str): 5 | """read the data from a .csv file and return the relevant dict data""" 6 | df = pd.read_csv(data, sep=';', header=1) 7 | df.drop(0, axis=0, inplace=True) 8 | df.reset_index(drop=True, inplace=True) 9 | for item in df.columns: 10 | if 'Forces-NX' in item: 11 | y = df[item].astype(float) 12 | 13 | if 'Moments-MZ' in item: 14 | x = df[item].astype(float) 15 | try: 16 | x, y 17 | except AttributeError as e: 18 | print(e) 19 | else: 20 | return x, y 21 | 22 | 23 | if __name__ == '__main__': 24 | data = r"C:\temp\ZSoil\Road_60\New_Tunnel\V3.3\Results" + \ 25 | r"\SF_1.3_NoAbutment.csv" 26 | x, y = read(data) 27 | print(x) 28 | -------------------------------------------------------------------------------- /cult_i.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import intersection as inter 3 | 4 | 5 | def cult_I(m, n, input_values, values): 6 | """ 7 | capacity utilization of linings in tunnels (CULT-I) 8 | according to the paper: 9 | Performance indicator of tunnel linings under geotechnical uncertainty; 10 | Spyridis, Panagiotis; Konstantis, Spyridon; Gakis, Angelos 11 | cult_I = sqrt( (N_rel,i^2 + M_rel,i^2) / (N_rel,max^2 + M_rel,max^2) ) 12 | :input: 13 | m = x coordinate of a point, moment, float 14 | n = y coordinate of a point, normal force, float 15 | input_values = dict of input values, dict[str:float] 16 | values = dict of resulting values from core function, dict[str:float] 17 | :return: 18 | cult_I: float 19 | """ 20 | if input_values['a_s1'] == 0: 21 | limit_x_pos = values['Moment'] 22 | limit_y = values['Normal Force'] 23 | n_rel_max = abs(min(limit_y) - max(limit_y)) 24 | m_rel_max = abs(max(limit_x_pos)) 25 | else: 26 | limit_x_pos = values['Moment Reinf'] 27 | limit_y = values['Normal Force Reinf'] 28 | n_rel_max = abs(min(limit_y) - max(limit_y)) / 2 # take the half 29 | m_rel_max = abs(max(limit_x_pos)) 30 | if input_values['a_s2'] == 0: 31 | limit_x_neg = values['Moment Neg'] 32 | else: 33 | limit_x_neg = values['Moment Reinf Neg'] 34 | limit_x_pos = np.array(limit_x_pos) 35 | limit_x_neg = np.array(limit_x_neg) 36 | limit_y = np.array(limit_y) 37 | 38 | horizontal_cut_x = np.array([min(limit_x_neg), 39 | max(limit_x_pos)]) 40 | horizontal_cut_y = np.array([n, n]) 41 | 42 | vertical_cut_x = np.array([m, m]) 43 | vertical_cut_y = np.array([max(limit_y), 44 | min(limit_y)]) 45 | if m > 0: 46 | x1, _ = inter.intersection(horizontal_cut_x, 47 | horizontal_cut_y, 48 | limit_x_pos, 49 | limit_y) 50 | _, y2 = inter.intersection(vertical_cut_x, 51 | vertical_cut_y, 52 | limit_x_pos, 53 | limit_y) 54 | if len(x1) == 0 or len(y2) == 0: 55 | cult_I = 'outside' 56 | return cult_I 57 | m_rel_i = x1 - m 58 | if len(y2) > 1: 59 | n_rel_i = min(abs(y2[0] - n), abs(y2[1] - n)) 60 | else: 61 | n_rel_i = abs(y2[0] - n) 62 | if n_rel_i > n_rel_max: 63 | n_rel_i = min(abs(min(limit_y)) - abs(n), 64 | max(limit_y) - abs(n)) 65 | if m_rel_i < 0: 66 | cult_I = 'outside' 67 | return cult_I 68 | elif m == 0: 69 | m_rel_i = m_rel_max 70 | n_rel_i = min(abs(min(limit_y)) - abs(n), max(limit_y) - abs(n)) 71 | if max(limit_y) < n < min(limit_y): 72 | cult_I = 'outside' 73 | return cult_I 74 | else: 75 | x1, _ = inter.intersection(horizontal_cut_x, 76 | horizontal_cut_y, 77 | limit_x_neg, 78 | limit_y) 79 | _, y2 = inter.intersection(vertical_cut_x, 80 | vertical_cut_y, 81 | limit_x_neg, 82 | limit_y) 83 | if len(x1) == 0 or len(y2) == 0: 84 | cult_I = 'outside' 85 | return cult_I 86 | m_rel_i = x1 - m 87 | if len(y2) > 1: 88 | n_rel_i = min(abs(y2[0] - n), abs(y2[1] - n)) 89 | else: 90 | n_rel_i = abs(y2[0] - n) 91 | if n_rel_i > n_rel_max: 92 | n_rel_i = min(abs(min(limit_y)) - abs(n), 93 | max(limit_y) - abs(n)) 94 | if m_rel_i > 0: 95 | cult_I = 'outside' 96 | return cult_I 97 | cult_I = np.sqrt((m_rel_i*m_rel_i + n_rel_i*n_rel_i) / ( 98 | m_rel_max*m_rel_max + n_rel_max*n_rel_max)) 99 | if cult_I is not str: 100 | if type(cult_I) is np.float64: 101 | cult_I = round(cult_I, 3) 102 | else: 103 | cult_I = round(cult_I[0], 3) 104 | return cult_I 105 | 106 | 107 | if __name__ == '__main__': 108 | import core 109 | i_val, val = core.int_diagram() # initiate with default 110 | print(cult_I(m=-0.21, n=-1, input_values=i_val, values=val)) 111 | -------------------------------------------------------------------------------- /draw_diagram.py: -------------------------------------------------------------------------------- 1 | import plotly.graph_objs as go 2 | import numpy as np 3 | from typing import Dict, Tuple, Any 4 | 5 | 6 | def draw(values: Tuple[Any, Any], x: np.array, y: np.array): 7 | """ 8 | input:: 9 | val: dict of capacity curves 10 | x: design values, moment 11 | y: design values, normal force 12 | 13 | return:: plotly figure object 14 | """ 15 | i_val: Dict[str, float] 16 | val: Dict[str, float] 17 | i_val, val = values 18 | ecc = i_val['eccentricity'] # eccentricity true or false 19 | 20 | hover_mom_pos = ['Moment: ' + '{:.2f}'.format(text_x) + 21 | '
Normal force: ' + '{:.2f}'.format(text_y) 22 | for text_x, text_y in zip(list(val['Moment']), 23 | list(val['Normal Force']))] 24 | 25 | hover_mom_neg = ['Moment: ' + '{:.2f}'.format(text_x) + 26 | '
Normal force: ' + '{:.2f}'.format(text_y) 27 | for text_x, text_y in zip(list(val['Moment Neg']), 28 | list(val['Normal Force']))] 29 | 30 | hover_reinf_pos = ['Moment: ' + '{:.2f}'.format(text_x) + 31 | '
Normal force: ' + '{:.2f}'.format(text_y) 32 | for text_x, text_y in 33 | zip(list(val['Moment Reinf']), 34 | list(val['Normal Force Reinf']))] 35 | 36 | hover_reinf_neg = ['Moment: ' + '{:.2f}'.format(text_x) + 37 | '
Normal force: ' + '{:.2f}'.format(text_y) 38 | for text_x, text_y in 39 | zip(list(val['Moment Reinf Neg']), 40 | list(val['Normal Force Reinf']))] 41 | try: 42 | val['cult-I'] 43 | except Exception: 44 | hover_design = ['Moment: ' + '{:.2f}'.format(text_x) + ' [MN.m]' + 45 | '
Normal force: ' + '{:.2f}'.format(text_y) + 46 | ' [MN]' 47 | for text_x, text_y in zip(list(x), list(y))] 48 | name_design_values = f'Design values' 49 | else: 50 | hover_design = ['Moment: ' + '{:.2f}'.format(text_x) + ' [MN.m]' + 51 | '
Normal force: ' + '{:.2f}'.format(text_y) + 52 | ' [MN]
CULT-I: {}'.format(index) 53 | for text_x, text_y, index in zip(list(x), 54 | list(y), 55 | val['cult-I'])] 56 | try: 57 | min_cult_I = min([i for i in val['cult-I'] if type(i) != str]) 58 | except Exception as e: 59 | # print(e) 60 | name_design_values = f'Design values' 61 | else: 62 | name_design_values = f'Design values
min cult-I: {min_cult_I}' 63 | 64 | trace0 = go.Scatter( 65 | x=val['Moment'] + val['Moment Neg'][::-1], 66 | y=val['Normal Force'] + val['Normal Force'][::-1], 67 | mode='lines', 68 | name=f'Concrete
' 69 | f'f_cd = {i_val["f_ck"]/i_val["gamma_c"]:.2f} N/mm²
' 70 | f'h = {i_val["h"]} m
b = {i_val["b"]} m
' 71 | f'd_1 = {i_val["d_1"]} m
d_2 = {i_val["d_2"]} m
', 72 | line=dict( 73 | color='blue', 74 | width=3 75 | ), 76 | text=hover_mom_pos + hover_mom_neg[::-1], 77 | hoverinfo='text', 78 | hoverlabel=dict( 79 | bordercolor='gray', 80 | bgcolor='lightgray', 81 | font=dict( 82 | size=12, 83 | family='consolas' 84 | ) 85 | ) 86 | ) 87 | 88 | trace1 = go.Scatter( 89 | x=val['Moment Reinf'] + val['Moment Reinf Neg'][::-1], 90 | y=val['Normal Force Reinf'] + val['Normal Force Reinf'][::-1], 91 | mode='lines', 92 | name=f'Reinforcement
' 93 | f'a_s1 = {i_val["a_s1"]:.2f} cm²
' 94 | f'a_s2 = {i_val["a_s2"]} cm²
' 95 | f'f_yd = {i_val["f_yk"]/i_val["gamma_s"]:.2f} N/mm²
', 96 | line=dict( 97 | color='green', 98 | dash='dash', 99 | width=3 100 | ), 101 | text=hover_reinf_pos + hover_reinf_neg[::-1], 102 | hoverinfo='text', 103 | hoverlabel=dict( 104 | bordercolor='gray', 105 | bgcolor='lightgray', 106 | font=dict( 107 | size=12, 108 | family='consolas' 109 | ) 110 | ) 111 | ) 112 | 113 | trace2 = go.Scatter( 114 | x=x, 115 | y=y, 116 | mode='markers', 117 | name=name_design_values, 118 | showlegend=True, 119 | marker=dict( 120 | color='red', 121 | size=8, 122 | symbol='diamond', 123 | line=dict( 124 | color='black', 125 | width=1 126 | ) 127 | ), 128 | text=hover_design, 129 | hoverinfo='text', 130 | hoverlabel=dict( 131 | bordercolor='gray', 132 | bgcolor='lightgray', 133 | font=dict( 134 | size=12, 135 | family='consolas' 136 | ) 137 | ) 138 | ) 139 | 140 | if ecc: 141 | x1, y1 = val['x1_y1'] 142 | x1 = min(x1) 143 | y1 = min(y1) 144 | x2, y2 = val['x2_y2'] 145 | x2 = min(x2) 146 | y2 = min(y2) 147 | hover_ecc = ['Eccentricity:
' + 148 | 'Moment: ' + '{:.2f}'.format(text_x) + ' [MN.m]' + 149 | '
Normal force: ' + '{:.2f}'.format(text_y) + ' [MN]' 150 | for text_x, text_y in zip([x1, x2], [y1, y2])] 151 | trace3 = go.Scatter( 152 | x=[x1, x2], 153 | y=[y1, y2], 154 | mode='lines', 155 | name='eccentricity', 156 | showlegend=False, 157 | line=dict( 158 | color='gray', 159 | dash='longdashdot', 160 | width=1.5 161 | ), 162 | text=hover_ecc, 163 | hoverinfo='text', 164 | hoverlabel=dict( 165 | bordercolor='gray', 166 | bgcolor='lightgray', 167 | font=dict( 168 | size=12, 169 | family='consolas' 170 | ) 171 | ) 172 | ) 173 | 174 | layout = go.Layout( 175 | plot_bgcolor='#f9f7f7', 176 | margin=dict( 177 | l=50, 178 | r=50, 179 | b=50, 180 | t=50, 181 | pad=4 182 | ), 183 | titlefont=dict( 184 | size=20, 185 | ), 186 | hovermode='closest', 187 | autosize=True, 188 | # width=1200, 189 | height=900, 190 | xaxis=dict( 191 | rangemode='normal', 192 | tickformat='.2f', 193 | title='Moment [MN.m]', 194 | titlefont=dict( 195 | size=18) 196 | ), 197 | yaxis=dict( 198 | scaleanchor='x', 199 | scaleratio=0.1, 200 | autorange='reversed', 201 | tickformat='.0f', 202 | title='Normal Force [MN]', 203 | titlefont=dict( 204 | size=18) 205 | ), 206 | legend=dict( 207 | x=0.82, 208 | y=0.98, 209 | traceorder='normal', 210 | font=dict( 211 | family='arial', 212 | size=12, 213 | color='#000' 214 | ), 215 | bgcolor='#E2E2E2', 216 | bordercolor='#FFFFFF', 217 | borderwidth=1.5 218 | ) 219 | ) 220 | 221 | if ecc: 222 | data = [trace0, trace1, trace2, trace3] 223 | else: 224 | data = [trace0, trace1, trace2] 225 | 226 | return go.Figure( 227 | data=data, 228 | layout=layout 229 | ) 230 | -------------------------------------------------------------------------------- /images/Cult-I.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onurkoc/interaction-diagram/95e9e0ea0f13ee7fa35d94126d22d89040958869/images/Cult-I.png -------------------------------------------------------------------------------- /images/Cult-I_formula.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onurkoc/interaction-diagram/95e9e0ea0f13ee7fa35d94126d22d89040958869/images/Cult-I_formula.png -------------------------------------------------------------------------------- /images/Excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onurkoc/interaction-diagram/95e9e0ea0f13ee7fa35d94126d22d89040958869/images/Excel.png -------------------------------------------------------------------------------- /images/Main_Screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onurkoc/interaction-diagram/95e9e0ea0f13ee7fa35d94126d22d89040958869/images/Main_Screen.png -------------------------------------------------------------------------------- /intersection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Intersection of two curves 3 | Source: https://github.com/sukhbinder/intersection 4 | """ 5 | import numpy as np 6 | 7 | 8 | def _rect_inter_inner(x1, x2): 9 | n1 = x1.shape[0]-1 10 | n2 = x2.shape[0]-1 11 | X1 = np.c_[x1[:-1], x1[1:]] 12 | X2 = np.c_[x2[:-1], x2[1:]] 13 | S1 = np.tile(X1.min(axis=1), (n2, 1)).T 14 | S2 = np.tile(X2.max(axis=1), (n1, 1)) 15 | S3 = np.tile(X1.max(axis=1), (n2, 1)).T 16 | S4 = np.tile(X2.min(axis=1), (n1, 1)) 17 | return S1, S2, S3, S4 18 | 19 | 20 | def _rectangle_intersection_(x1, y1, x2, y2): 21 | S1, S2, S3, S4 = _rect_inter_inner(x1, x2) 22 | S5, S6, S7, S8 = _rect_inter_inner(y1, y2) 23 | 24 | C1 = np.less_equal(S1, S2) 25 | C2 = np.greater_equal(S3, S4) 26 | C3 = np.less_equal(S5, S6) 27 | C4 = np.greater_equal(S7, S8) 28 | 29 | ii, jj = np.nonzero(C1 & C2 & C3 & C4) 30 | return ii, jj 31 | 32 | 33 | def intersection(x1, y1, x2, y2): 34 | """ 35 | Intersections of curves. 36 | Computes the (x,y) locations where two curves intersect. The curves 37 | can be broken with NaNs or have vertical segments. 38 | usage: 39 | x,y=intersection(x1,y1,x2,y2) 40 | Example: 41 | a, b = 1, 2 42 | phi = np.linspace(3, 10, 100) 43 | x1 = a*phi - b*np.sin(phi) 44 | y1 = a - b*np.cos(phi) 45 | x2=phi 46 | y2=np.sin(phi)+2 47 | x,y=intersection(x1,y1,x2,y2) 48 | plt.plot(x1,y1,c='r') 49 | plt.plot(x2,y2,c='g') 50 | plt.plot(x,y,'*k') 51 | plt.show() 52 | """ 53 | ii, jj = _rectangle_intersection_(x1, y1, x2, y2) 54 | n = len(ii) 55 | 56 | dxy1 = np.diff(np.c_[x1, y1], axis=0) 57 | dxy2 = np.diff(np.c_[x2, y2], axis=0) 58 | 59 | T = np.zeros((4, n)) 60 | AA = np.zeros((4, 4, n)) 61 | AA[0:2, 2, :] = -1 62 | AA[2:4, 3, :] = -1 63 | AA[0::2, 0, :] = dxy1[ii, :].T 64 | AA[1::2, 1, :] = dxy2[jj, :].T 65 | 66 | BB = np.zeros((4, n)) 67 | BB[0, :] = -x1[ii].ravel() 68 | BB[1, :] = -x2[jj].ravel() 69 | BB[2, :] = -y1[ii].ravel() 70 | BB[3, :] = -y2[jj].ravel() 71 | 72 | for i in range(n): 73 | try: 74 | T[:, i] = np.linalg.solve(AA[:, :, i], BB[:, i]) 75 | except Exception: 76 | T[:, i] = np.NaN 77 | 78 | in_range = (T[0, :] >= 0) & (T[1, :] >= 0) & (T[0, :] <= 1) & ( 79 | T[1, :] <= 1) 80 | 81 | xy0 = T[2:, in_range] 82 | xy0 = xy0.T 83 | return xy0[:, 0], xy0[:, 1] 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asn1crypto==0.24.0 2 | attrs==19.1.0 3 | certifi==2019.3.9 4 | cffi==1.12.3 5 | chardet==3.0.4 6 | Click==7.0 7 | cryptography==2.6.1 8 | dash==0.43.0 9 | dash-bootstrap-components==0.6.1 10 | dash-core-components==0.48.0 11 | dash-html-components==0.16.0 12 | dash-renderer==0.24.0 13 | dash-table==3.7.0 14 | decorator==4.4.0 15 | Flask==1.0.3 16 | Flask-Compress==1.4.0 17 | idna==2.8 18 | ipython-genutils==0.2.0 19 | itsdangerous==1.1.0 20 | Jinja2==2.10.1 21 | jsonschema==3.0.1 22 | jupyter-core==4.4.0 23 | MarkupSafe==1.1.1 24 | nbformat==4.4.0 25 | numpy==1.16.3 26 | pandas==0.24.2 27 | plotly==3.8.1 28 | pycparser==2.19 29 | pyOpenSSL==19.0.0 30 | pyrsistent==0.14.11 31 | PySocks==1.7.0 32 | python-dateutil==2.8.0 33 | pytz==2019.1 34 | requests==2.21.0 35 | retrying==1.3.3 36 | six==1.12.0 37 | traitlets==4.3.2 38 | urllib3==1.24.2 39 | Werkzeug==0.15.4 40 | win-inet-pton==1.1.0 41 | wincertstore==0.2 42 | gunicorn==19.9.0 43 | xlrd==1.2.0 44 | -------------------------------------------------------------------------------- /xls_read.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | 4 | def read(decoded: str): 5 | """read the data from a .csv file and return the relevant dict data""" 6 | df = pd.read_excel(decoded) 7 | for item in df.columns: 8 | if 'n' in item.lower(): 9 | y = df[item].astype(float) 10 | 11 | if 'm' in item.lower(): 12 | x = df[item].astype(float) 13 | return x, y 14 | 15 | 16 | if __name__ == '__main__': 17 | data = r"C:\Users\okoc\OneDrive - Dr. Sauer & " \ 18 | r"Partners\Programming\Python\Dash\interaction_diagram " + \ 19 | r"\NM.xlsx" 20 | x, y = read(data) 21 | print(x) --------------------------------------------------------------------------------