├── .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 | 
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 | 
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 | 
30 |
31 | where
32 | 
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)
--------------------------------------------------------------------------------