-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanalysis.py
784 lines (660 loc) · 37.8 KB
/
analysis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
"""
Created on Tuesday Dec 4 18:00 2018
@author: mate.pentek@tum.de
Partially based on the BSc Thesis of Benedikt Schatz (TUM, Statik 2018)
"""
import json
import warnings
# still needed for plotting the (input) system, to be moved to plot_utilities
# from matplotlib import pyplot as plt
import numpy as np
from entitites.node2d import Node2D
from entitites.element2d import Element2D
from entitites.force2d import Force2D
from entitites.fixity2d import Fixity2D
from entitites.segment2d import Segment2D
from utilities.mechanical_utilities import get_force_diagram, get_space_diagram, get_reactions, decompose_force_into_components_by_directions, get_nodal_equilibrium_by_method_of_joints, sort_clockwise
from utilities.geometric_utilities import TOL, are_parallel, get_intersection, get_length, get_midpoint, get_line_coefficients
from utilities.plot_utilities import plot_input_system, plot_computation_model, plot_solved_system, plot_force_diagram, plot_space_diagram, plot_decomposed_forces, plot_reaction_forces
from utilities.geometric_utilities import sort_left_to_right
class Analysis(object):
def __init__(self, input_model_file, echo_level=0):
self.echo_level = echo_level
self.input_system = self._import_model(input_model_file)
plot_input_system(self.input_system)
# plt.show()
self.computation_model = None
self._prepare_computation_model()
plot_computation_model(self.computation_model)
# plt.show()
# if it is set to 0, no additional plots will be done
# if set to 1 additional plots will be shown
def _import_model(self, input_model_file):
model = {}
model["nodes"] = {}
model['elements'] = {}
model["forces"] = {}
# rename to fixities (as forces are also boundary conditions)
model["fixities"] = {}
with open(input_model_file) as f:
data = json.load(f)
# get bel_chord:
model["bel_chord"] = data['bel_chord']
# get node_list:
for node in data['nodes']:
model["nodes"][node['id']] = Node2D(node['id'],
node['coords'],
node['is_constrain'])
# get elements
for element in data['elements']:
model['elements'][element['id']] = Element2D(element['id'],
element['connect'],
# TODO: to be removed in future, should be only stored in 'nodes'
[model['nodes'][element['connect'][0]].coordinates,
model['nodes'][element['connect'][1]].coordinates],
element['opt_type'],
element['is_constrain'])
# get (external) forces:
for ext_force in data['external_forces']:
model["forces"]['e' + str(ext_force['id'])] = Force2D('e' + str(ext_force['id']),
ext_force['node_id'],
# maybe remove once dependency is not needed
model["nodes"][ext_force['node_id']
].coordinates,
ext_force['components'],
force_type='external')
# get fixitites
for fixity in data['fixities']:
model["fixities"][fixity['id']] = Fixity2D(fixity['id'],
fixity['node_id'],
fixity['is_fixed'])
return model
def postprocess(self, cremona):
# for example cremona-plan
print("Cremona-plan to be implemented")
plot_computation_model(self.computation_model)
self.draw_system_from_cremona(cremona)
# Steigungen anpassen
elements = self.computation_model['elements']
nodes = self.computation_model['nodes']
members = cremona.members
for i in elements:
j = str(elements[i].id) + 'i'
if j not in members:
j = str(elements[i].id) + 'j'
line1 = members[j].line
elements[i].coordinates[0] = nodes[elements[i].nodes[0]].coordinates
elements[i].coordinates[1] = nodes[elements[i].nodes[1]].coordinates
elements[i]._get_line()
elements[i].midpoint = get_midpoint(elements[i].coordinates)
elements[i].length = get_length(elements[i].coordinates)
lines = [elements[i].line, line1]
jop = are_parallel(lines)
if jop != True:
print('Member', i, 'is incorrect')
print('cremona:', line1['direction'],
'system', elements[i].line['direction'])
self.get_system_volume(elements, members, cremona.at_member, cremona.one_member)
plot_computation_model(self.computation_model)
def get_system_volume(self, elements, members, at_member, one_member):
V = 0
already_done = []
#für jeden Stab Produkt aus Länge System und Länge Cremona
for i in members:
#Länge System
el = at_member[i]
L = elements[el].length
#Prüfen ob Stab schon betrachtet
other = one_member[i]
if other in already_done:
pass
else:
P = members[i].length
V = V + (P*L)
already_done.append(i)
print('Volume of given System: ', V)
def _calculate_reaction_forces(self):
forces = []
for key, force in self.input_system["forces"].items():
# if force.force_type == 'external':
forces.append(force)
################
# force diagram
# to find the magnitude of the resultant of external forces
force_diagram = get_force_diagram(forces)
if self.echo_level == 1:
plot_force_diagram(force_diagram)
################
# funicular or space diagram
# to find the acting line of the resultant of external forces
space_diagram = get_space_diagram(force_diagram)
if self.echo_level == 1:
plot_space_diagram(space_diagram)
# for now diractions hard-coded
directions = [[1, 0], [0, 1]]
decomposed_forces, points = decompose_force_into_components_by_directions(
space_diagram['resultant'], directions)
if self.echo_level == 1:
plot_decomposed_forces(
space_diagram['resultant'], decomposed_forces, points)
################
# from decomposed resultant get reactions based upon fixities
# use inverse proportion
fixity_node_id = []
fixity_locations = []
fixity_type = []
for key, fixity in self.input_system['fixities'].items():
fixity_locations.append(
self.input_system['nodes'][fixity.node_id].coordinates)
fixity_node_id.append(fixity.node_id)
fixity_type.append(fixity.is_fixed)
# resultant line needs to be re-calculated! (as it is a copy and _get_line() is in the constructor)
space_diagram['resultant'].line = space_diagram['resultant']._get_line()
reactions = get_reactions(space_diagram['resultant'].line, decomposed_forces,
directions, fixity_locations, fixity_type, fixity_node_id)
if self.echo_level == 1:
plot_reaction_forces(reactions, space_diagram['resultant'])
################
# setup the computational model
# by discarding fixities and adding reaction into force dictionary
self.computation_model = self.input_system
key = 'fixities'
try:
del self.computation_model[key]
except KeyError:
print('Key ' + key + ' not found')
for idx, reaction in enumerate(reactions):
self.computation_model['forces']['r' + str(idx)] = reaction
self.computation_model['forces']['r' +
str(idx)].id = 'r' + str(idx)
self._check_system_equilibrium()
def _check_system_equilibrium(self):
# for checking equilibrium under external loads
# and reaction forces once calculated
forces = []
for key, force in self.computation_model["forces"].items():
# if force.force_type == 'external' or force.force_type == 'reaction':
forces.append(force)
################
# force diagram
# to find the magnitude of the resultant of external forces
force_diagram = get_force_diagram(forces)
if self.echo_level == 1:
plot_force_diagram(force_diagram)
if force_diagram['resultant'].magnitude > TOL:
raise Exception(
'Computation model not in equilibrium under external and reaction forces!')
def _check_all_nodal_equilibrium(self):
# checking equilibrium of each node under all forces
for key, node in self.computation_model['nodes'].items():
self._check_nodal_equilibrium(node.id)
def _check_nodal_equilibrium(self, node_id):
# TODO: remove need for casting from str to int
node = self.computation_model['nodes'][int(node_id)]
forces = [self.computation_model['forces'][force_id]
for force_id in node.forces]
################
# force diagram
# to find the magnitude of the resultant of external forces
force_diagram = get_force_diagram(forces)
if self.echo_level == 1:
plot_force_diagram(force_diagram)
if force_diagram['resultant'].magnitude > TOL:
warnings.warn(
'Computation model not in equilibrium at node ' + str(node.id) + '!', Warning)
def _prepare_computation_model(self):
self._calculate_reaction_forces()
# update nodal information with existing forces: external and reaction
for key, force in self.computation_model['forces'].items():
self.computation_model['nodes'][force.node_id].forces.append(key)
# update nodal information with elements: will link to internal forces
for key, element in self.computation_model['elements'].items():
self.computation_model['nodes'][element.nodes[0]
].unsolved_elements.append(key)
self.computation_model['nodes'][element.nodes[1]
].unsolved_elements.append(key)
def _solve_iteratively(self):
current_system_unsolved_degree = 0
# update nodal date with unsolved degree
# Was bedeutet key?
for key, node in self.computation_model['nodes'].items():
node.unsolved_degree = len(node.unsolved_elements)
current_system_unsolved_degree += node.unsolved_degree
# attention: infinite loop! premise that system is solvable (so no Ritter-cut needed!)
# todo: make more robust by ranking unsolved, having only one go through (if not solved, do ritter-cut)
old_system_unsolved_degree = current_system_unsolved_degree
system_solved = False
counter = 0
if self.echo_level == 1:
print("## System solve - iteratively")
change = 1
while (not(system_solved) and change > 0):
counter += 1
# setup initial values
system_solved = True
current_system_unsolved_degree = 0
# computation loop
for key, node in self.computation_model['nodes'].items():
# should work from python3.5 onwards
# for key, node in reversed(self.computation_model['nodes'].items()):
# reversed_keys = list(reversed([key for key, node in self.computation_model['nodes'].items()]))
# sel_key = 0
# reversed_keys.remove(sel_key)
# reversed_keys.insert(0, sel_key)
# for r_key in reversed_keys:
# node = self.computation_model['nodes'][r_key]
if node.unsolved_degree > 0:
# for any unsolved node set to False
system_solved = False
if node.unsolved_degree < 3:
# node solvable
# create list of foces and elements relevant to node
nodal_forces = []
for force_id in node.forces:
nodal_forces.append(
self.computation_model['forces'][force_id])
nodal_elements = []
for element_id in node.unsolved_elements:
nodal_elements.append(
self.computation_model['elements'][element_id])
if len(nodal_elements) == 2:
# for this case we can always decomponse the resultant at the node
# into the 2 elements by the method of joints
forces = get_nodal_equilibrium_by_method_of_joints(
nodal_forces, nodal_elements)
elif len(nodal_elements) == 1:
# for this case we need to calculate the existing resultant
# at the node and see if this matches the prescribed topology
# so resultant parallel with member
# otherwise topology needs to be updated/modified ONLY IF
# this is permitted
force_diagram = get_force_diagram(nodal_forces)
# move resultant to actual nodal coordinate, recalculate line
force_diagram['resultant'].coordinates = node.coordinates
# update line equation
force_diagram['resultant'].line = force_diagram['resultant']._get_line(
)
if not(are_parallel([nodal_elements[0].line, force_diagram['resultant'].line])):
# get the next node id - other end of the nodal_elements[0]
tmp = nodal_elements[0].nodes.copy()
# TODO: make consistent type for id - probably overall as str() and not int()
tmp.remove(int(node.id))
other_node_id = tmp[0]
# get list of all elements at node - for now ids
# using .copy() to create value copy of list
elements_at_other_node = self.computation_model['nodes'][other_node_id].unsolved_elements.copy(
)
elements_at_other_node += self.computation_model['nodes'][other_node_id].solved_elements.copy(
)
# now elements instead of ids
elements_at_other_node = [
self.computation_model['elements'][element_id] for element_id in elements_at_other_node]
constrained_elements_at_other_node = [
element.id for element in elements_at_other_node if element.is_constrain]
# PMT: here workaround for overconstrained geometry
if len(constrained_elements_at_other_node) != 1:
# for 2d - plane - problems only 1 line should give the constraint
# assumption for the current implementation
print("# other node id: ",
str(other_node_id))
print(
"# constrained elements at other node: ", constrained_elements_at_other_node)
raise Exception(
'Computation model (geometrically) over-(or under-)constrained at node ' + str(other_node_id) + ', cannot solve further!')
else:
# node will be move along constrained line
other_node_new_coord = get_intersection(
[self.computation_model['elements'][constrained_elements_at_other_node[0]].line, force_diagram['resultant'].line])
msg = '## Topology update:\n'
msg += 'node with id ' + \
str(other_node_id) + \
' and coordinates '
msg += ", ".join("%.3f" % coord
for coord in self.computation_model['nodes'][other_node_id].coordinates) + '\n'
msg += 'updated to coordinates ' + \
", ".join("%.3f" % coord
for coord in other_node_new_coord) + '\n'
print(msg)
# TODO - WIP: introduce model part utilities
# all model data should only be stored once in the model part
# similar changes - such as the following - should be carried out by these
# node needs to be updated in the model part
self.computation_model['nodes'][other_node_id].coordinates = other_node_new_coord
# affected elements need to be updated in the model part
for element_id in [element.id for element in elements_at_other_node]:
# get index of node which needs to be updated
# local index from the attribute of elements
idx = self.computation_model['elements'][element_id].nodes.index(
other_node_id)
# set new node coordinate
self.computation_model['elements'][element_id].coordinates[idx] = other_node_new_coord
# update midpoint
self.computation_model['elements'][element_id].midpoint = get_midpoint(
self.computation_model['elements'][element_id].coordinates)
# update line
self.computation_model['elements'][element_id].line = self.computation_model['elements'][element_id]._get_line(
)
# update length
self.computation_model['elements'][element_id].length = get_length(
self.computation_model['elements'][element_id].coordinates)
else:
print()
# carry on if parallel, no topology update needed
pass
forces = [force_diagram['resultant']]
else:
# TODO implement some special warning, should not be the case
pass
if forces is not None:
# update force coordinates and line
for force in forces:
force.coordinates = node.coordinates
force.line = force._get_line()
# update element and nodal information
# Was ist o_idx?
for o_idx, element in enumerate(nodal_elements):
# Hier weitermachen
##
##
##
# if insufficient_unsolved and o_idx == 1:
# # nothing needs to be done, already solved
# pass
# else:
# use a parameter xi to determine internal force type
dir_u = forces[o_idx].direction[0]
dir_v = forces[o_idx].direction[1]
if abs(dir_u) > 0.0:
xi_x = (
element.midpoint[0] - forces[o_idx].coordinates[0]) / dir_u
else:
xi_x = 0.0
if abs(dir_v) > 0.0:
xi_y = (
element.midpoint[1] - forces[o_idx].coordinates[1]) / dir_v
else:
xi_y = 0.0
if xi_x >= 0.0 and xi_y >= 0:
# force points towards midpoint
element.element_type = 'compression'
element.force_magnitude = forces[o_idx].magnitude
elif xi_x <= 0.0 and xi_y <= 0:
# force points away from midpoint
element.element_type = 'tension'
element.force_magnitude = forces[o_idx].magnitude
else:
raise Exception(
'Case not permitted: xi_x and xi_y must have same sign!')
# update node and force lists:
labels = ['i', 'j']
for i_idx, node_id in enumerate(element.nodes):
# if element.element_type == 'tension':
# components = []
# elif element.element_type == 'compression':
components = [self.computation_model['nodes'][node_id].coordinates[0] - element.midpoint[0],
self.computation_model['nodes'][node_id].coordinates[1] - element.midpoint[1]]
if element.element_type == 'tension':
components = [-components[0], -
components[1]]
# else:
# ValueError("Case not permitted")
force = Force2D(str(element.id) + labels[i_idx],
node_id,
self.computation_model['nodes'][node_id].coordinates,
# direction will be computed correctly
components,
forces[o_idx].force_type,
element.element_type)
# overwriting magnitude with the correct value
force.magnitude = forces[o_idx].magnitude
# update forces
self.computation_model['forces'][force.id] = force
# update nodes - force list and node unsolved degree
self.computation_model['nodes'][node_id].forces.append(
force.id)
self.computation_model['nodes'][node_id].unsolved_degree -= 1
# also pop elemnt id once solve
# using remove as ID should be unique in list
self.computation_model['nodes'][node_id].unsolved_elements.remove(
element.id)
self.computation_model['nodes'][node_id].solved_elements.append(
element.id)
else:
# TODO: implement some better workaround, as in some cases the 2 elements
# and respective directions might be colinear/parallel
pass
# once the node is solved
# check for nodal equilibrium
self._check_nodal_equilibrium(node.id)
current_system_unsolved_degree += node.unsolved_degree
change = old_system_unsolved_degree - current_system_unsolved_degree
##
if self.echo_level == 1:
print("At iteration: ", counter)
print("System unsolved degree - old: ",
old_system_unsolved_degree)
print("System unsolved degree - current: ",
current_system_unsolved_degree)
print("System unsolved degree - change: ",
change)
old_system_unsolved_degree = current_system_unsolved_degree
if current_system_unsolved_degree == 0:
print("## System solved successfully!")
self._check_all_nodal_equilibrium()
else:
warnings.warn(
"System cannot be solved iteratively, needs other solution", Warning)
def solve_system(self):
self._solve_iteratively()
plot_solved_system(self.computation_model)
pass
def draw_system_from_cremona(self, cremona):
# #draw bel_chord first
model = self.computation_model
e_line = []
#Liste mit allen externen Kraftlinien
for i in cremona.ex_forces:
self.computation_model['forces'][i].line = self.computation_model['forces'][i]._get_line()
line = self.computation_model['forces'][i].line
e_line.append(line)
pass
#sortiere Elemente bel-chord von links nach rechts
#zeichne Elemente bel_chord
bel_elements = get_elements_bel_chord(model, cremona)
#Für alle außer letzten member Schnittpunkt memberlinie und Kraftlinie bestimmen
last_one = bel_elements[-1]
bel_elements.pop(-1)
count = 0
#an linkem Punkt von linkem member starten
left_node, right_node = r_l_node(model['elements'][bel_elements[0]],model['nodes'],model['bel_chord'])
start = model['nodes'][left_node].coordinates
for i in bel_elements:
#Koordinaten des linken Punkts anpassen
left_node, right_node = r_l_node(model['elements'][i],model['nodes'],model['bel_chord'])
model['nodes'][left_node].coordinates = start
#Elementlinie mit Steigung aus Cremonaplan
model['elements'][i].line = get_line_from_cremona(model['elements'][i],model['nodes'][left_node],cremona,cremona.bel_chord,model)
#Schnittpunkt element- und external-force line
new_point = get_intersection([model['elements'][i].line,e_line[count]])
count = count+1
#new_point als neue Koordinaten für den rechten Punkt
model['nodes'][right_node].coordinates = new_point
#von dort aus nächsten member zeichnen
start = new_point
#letzten member "einhängen"
#in computation model speichern
self.computation_model = model
#überprüfen ob bel_chord richtig gezeichnet wurde
check_bel_chord(last_one,start,model['nodes'],model,cremona,e_line)
bel_elements.append(last_one)
# Diagonalen und unbel_chord zeichnen
unbel_elements = get_elements_unbel_chord(model, cremona)
last_one = unbel_elements[-1]
unbel_elements.pop(-1)
Verbdg_elements = get_elements_Verbindung(model, cremona)
# Falls keine Vernindungen mehr vorhanden -> System bleibt wie es war
if Verbdg_elements == []:
plot_computation_model(model)
else:
# Startpunkt für unbel_chord und Verbindung festlegen
left_node, right_node = r_l_node(
model['elements'][bel_elements[0]], model['nodes'], model['bel_chord'])
start_ubc = model['nodes'][left_node].coordinates
start_ver = model['nodes'][right_node].coordinates
# Schnittpunkt von Verbindung und unbel-Member finden
for i in range(len(Verbdg_elements)):
# Elementlinie unbel_chord mit Steigung aus Cremonaplan
left_node, right_node = r_l_node(
model['elements'][unbel_elements[i]], model['nodes'], model['bel_chord'])
model['nodes'][left_node].coordinates = start_ubc
model['elements'][unbel_elements[i]].line = get_line_from_cremona(
model['elements'][unbel_elements[i]], model['nodes'][left_node], cremona, cremona.unbel_chord, model)
line1 = model['elements'][unbel_elements[i]].line
# Elementlinie Vernindung mit Steigung aus Cremonaplan
fix = u_o_node(
model['elements'][Verbdg_elements[i]], model['nodes'], cremona)
# rechter Knoten des unbel_element soll beweglich sein
move = right_node
# model['nodes'][fix].coordinates = start_ver
model['elements'][Verbdg_elements[i]].line = get_line_from_cremona(
model['elements'][Verbdg_elements[i]], model['nodes'][fix], cremona, cremona.Verbindung, model)
line2 = model['elements'][Verbdg_elements[i]].line
# Schnittpunkt der Linien bestimmen
new_point = get_intersection([line1, line2])
# Neuen Punkt in Elementen speichern
model['nodes'][move].coordinates = new_point
# neuen Start festlegen
start_ubc = new_point
left, right = r_l_node(
model['elements'][bel_elements[i+1]], model['nodes'], model['bel_chord'])
start_ver = model['nodes'][right].coordinates
# plot_computation_model(model)
check_top_chord(
last_one, start_ubc, model['nodes'], model, cremona, bel_elements[-1], Verbdg_elements[-1])
def check_top_chord(last_one, start, nodes, model, cremona, last_bel, last_Ver):
# Linie des letzten unbel_chord Elements
left_node, right_node = r_l_node(
model['elements'][last_one], nodes, model['bel_chord'])
line1 = get_line_from_cremona(
model['elements'][last_one], model['nodes'][right_node], cremona, cremona.unbel_chord, model)
model['elements'][last_one].coordinates[0] = nodes[model['elements']
[last_one].nodes[0]].coordinates
model['elements'][last_one].coordinates[1] = nodes[model['elements']
[last_one].nodes[1]].coordinates
model['elements'][last_one]._get_line()
line1 = model['elements'][last_one].line
# Linie der letzten Deviation
left_node, right_node = r_l_node(
model['elements'][last_bel], nodes, model['bel_chord'])
line2 = get_line_from_cremona(
model['elements'][last_Ver], model['nodes'][left_node], cremona, cremona.Verbindung, model)
new_point = get_intersection([line1, line2])
if start[0] - TOL <= new_point[0] <= start[0] + TOL and start[1] - TOL <= new_point[1] <= start[1] + TOL:
print('unbel_chord succesful')
else:
print('unbel_chord wrong!')
dx = start[0]-new_point[0]
dy = start[1]-new_point[1]
print('difference:', dx, dy)
def check_bel_chord(last_one, start, nodes, model, cremona, e_line):
left_node, right_node = r_l_node(
model['elements'][last_one], nodes, model['bel_chord'])
line = get_line_from_cremona(
model['elements'][last_one], model['nodes'][right_node], cremona, cremona.bel_chord, model)
new_point = get_intersection([line, e_line[-1]])
model['nodes'][left_node].coordinates = new_point
if start[0] - TOL <= new_point[0] <= start[0] + TOL and start[1] - TOL <= new_point[1] <= start[1] + TOL:
print('bel_chord succesful')
else:
print('bel_chord wrong!')
def get_line_from_cremona(element, node, cremona, chord, model):
# Richtung
line = {}
i = str(element.id) + 'i'
j = str(element.id) + 'j'
# Vorzeichen der Richtung anpassen
if i in node.forces:
line['direction'] = chord[i].line['direction']
if j in node.forces:
line['direction'] = chord[j].line['direction']
# Koeffizienten
x2 = node.coordinates[0] - line['direction'][0]
y2 = node.coordinates[1] - line['direction'][1]
p2 = [x2, y2]
line['coefficients'] = get_line_coefficients([node.coordinates, p2])
return line
def r_l_node(element, nodes, bel):
# knoten von links nach rechts sortieren
n1 = element.nodes[0]
n2 = element.nodes[1]
if nodes[n1].coordinates[0] < nodes[n2].coordinates[0]:
return n1, n2
if nodes[n2].coordinates[0] < nodes[n1].coordinates[0]:
return n2, n1
if nodes[n1].coordinates[0] == nodes[n2].coordinates[0]:
if bel == 'bottom':
if nodes[n1].coordinates[1] < nodes[n2].coordinates[1]:
return n2, n1
if nodes[n2].coordinates[1] < nodes[n1].coordinates[1]:
return n1, n2
if bel == 'top':
if nodes[n1].coordinates[1] < nodes[n2].coordinates[1]:
return n1, n2
if nodes[n2].coordinates[1] < nodes[n1].coordinates[1]:
return n2, n1
def u_o_node(element, nodes, cremona):
# knoten von oben nach unten sortieren
n1 = element.nodes[0]
n2 = element.nodes[1]
# Knoten an belastetem Gurt fix
for i in cremona.bel_chord:
if i in nodes[n1].forces:
fix = n1
if i in nodes[n2].forces:
fix = n2
# rechter Knoten des unbelasteten Elements soll verschoben werden
return fix
def get_elements_bel_chord(model, cremona):
bel_elements = {}
for i in cremona.bel_chord:
element = cremona.at_member[cremona.bel_chord[i].id]
if element not in bel_elements:
bel_elements[element] = cremona.bel_chord[i]
bel_elements = sort_left_to_right(bel_elements, model['nodes'])
return bel_elements
def get_elements_unbel_chord(model, cremona):
unbel_elements = {}
for i in cremona.unbel_chord:
element = cremona.at_member[cremona.unbel_chord[i].id]
if element not in unbel_elements:
unbel_elements[element] = cremona.unbel_chord[i]
unbel_elements = sort_left_to_right(unbel_elements, model['nodes'])
return unbel_elements
def get_elements_Verbindung(model, cremona):
Verbdg_elements = {}
for i in cremona.Verbindung:
element = cremona.at_member[cremona.Verbindung[i].id]
if element not in Verbdg_elements:
Verbdg_elements[element] = cremona.Verbindung[i]
Verbdg_elements = sort_left_to_right(Verbdg_elements, model['nodes'])
return Verbdg_elements
# if __name__ == "__main__":
# '''
# Mock to check equilibrium at node 6 - example 4
# '''
# element = Element2D('6', ['6', '7'], [[7.5, -0.9],[2.5, -1.5]])
# elements = [element]
# force_m13 = Force2D('13j', '6', [7.5, -0.9], [0.19996 * 1.599296, 0.9798 * 1.599296], 'internal')
# force_m5 = Force2D('5j', '6', [7.5, -0.9], [0.98418 * 172.2154, 0.17771 * 172.2154], 'internal')
# forces = [force_m13, force_m5]
# force_diagram = get_force_diagram(forces)
# plot_force_diagram(force_diagram)
# plt.show()
# print("## ARE PARALLEL: ", are_parallel([elements[0].line,force_diagram['resultant'].line]))
# print("element id: ", elements[0].id)
# print("element_dir: ",elements[0].line['direction'])
# print("resultant_dir: ", force_diagram['resultant'].direction)
# print("element_line: ",elements[0].line['coefficients'])
# print("resultant_line: ", force_diagram['resultant'].line['coefficients'])
# print("resultant_coordinates: ", force_diagram['resultant'].coordinates)
# decomposed_forces, points = [force_diagram['resultant']], [force_diagram['resultant'].coordinates]