Der Algorithmus zur Berechnung der Profilhöhen von Freiträgern wurde erweitert, sodass nun auch die Profilhöhen von Stützträgern berechnet werden können. Ferner wurde ein Slider zur Darstellung der aktuellen Profilhöhe und des aktuellen Biegemoments ergänzt. Ergänzend dazu befindet sich im obersten Subplot nun ein Freikörperbild vom aktuellen Lastfall. Dadurch ist ein einfacheres Verständnis der Biegemomentenlinie und des Graphen zum Profilhöhenverlauf in den beiden darunterliegenden Subplots gegeben.

This commit is contained in:
2022-10-26 00:36:06 +02:00
parent ab5427929f
commit 8f2fc3842c
+472 -111
View File
@@ -1,147 +1,508 @@
import argparse
from matplotlib import pyplot as plt
from math import floor
from matplotlib.widgets import Slider
from matplotlib.ticker import FormatStrFormatter
from math import floor, ceil
import ctypes
class ProfileHeights:
bobbin_train_segment_types = {165: 0.38, 210: 0.248, 225: 0.354648} # Key = Spulenzug-Typ - Value = Gewicht
# Ortsfaktor = Universalwert
_location_factor = 9.81 # m/s^2
# Sicherheitsfaktor gemäß Bauvorschrift
safety_factor = 1.7
# Fahrstreckengewichte nach Produktprogramm "OMNIFLO"
_rail_masses = {"AP110": 1.94, # K: Art-Nr. 821106001 - V: in kg
"APG110": 1.66, # K: Art-Nr. 821106002 - V: in kg
"APS110": 3.89, # K: Art-Nr. 821716001 - V: in kg
"AP60": 1.46 # K: Art-Nr. 821096001 - V: in kg
}
# Spulenzugsegment-Gewichte nach CAD
_train_segment_masses = {"T165": 0.38, # K: Art.-Nr. 826906201 - V: in kg
"T210": 0.248, # K: Art.-Nr. 826906213 - V: in kg
"T225": 0.355 # K: Art.-Nr. 826906200 - V: in kg
}
def __init__(self,
cross_section_type,
datum,
width,
thickness,
ap_positions=None,
beam_length=2000,
min_ap_distance=200,
yield_strength=235,
segment_type=165,
cross_section_width,
cross_section_thickness,
has_two_supports,
beam_length,
individual_rail_positions=None,
individual_rail_forces=None,
rail_type="AP110",
min_rail_distance=200,
min_pillar_distance=300,
material_yield_strength=235,
bobbin_train_segment_type="T165",
bobbin_mass=4,
column_distance=3.6,
colum_row_distance=3600,
pillar_width=100
):
self.min_rail_distance = min_rail_distance
self.min_dist_first_rail_to_pillar = min_pillar_distance
self.cross_section_type = cross_section_type
self.datum = datum
self.width = width
self.thickness = thickness
self.ap_positions = ap_positions
self.cross_section_width = cross_section_width
self.cross_section_thickness = cross_section_thickness
self.individual_rail_forces = individual_rail_forces
self.has_two_supports = has_two_supports
self.beam_length = beam_length
self.min_ap_distance = min_ap_distance
self.yield_strength = yield_strength
self.segment_type = segment_type
self.bobbin_mass = bobbin_mass
self.column_distance = column_distance
self.material_yield_strength = material_yield_strength
self.column_row_distance = colum_row_distance
self.rail_type = rail_type
self.individual_rail_positions = individual_rail_positions
self.pillar_width = pillar_width
self.rail_dist_from_support_a = self._set_rail_dist_from_support_a(individual_rail_positions)
self.train_segment_type = bobbin_train_segment_type
self.single_bobbin_mass = bobbin_mass
self.train_segment_mass = self._set_train_segment_mass()
self.rail_mass = self._set_rail_mass()
self.rail_forces = self._set_rail_forces()
self.bend_moments_from_a = self._summed_bend_moment_from_a()
self.support_bending_moment = self._set_support_a_bending_moment()
self.transverse_force_support_b = self._set_transverse_force_support_b()
self.transverse_force_support_a = self._set_transverse_force_support_a()
self._section_starts = self._set_beam_section_start_positions()
self.all_force_positions = self._set_all_force_positions()
self._beam_section_lengths = self._set_beam_section_lengths()
self._beam_section_forces = self._set_section_forces()
self.allowed_bend_stress = self._set_allowed_bend_stress()
self.bending_moments = self._set_bending_moments()
self.heights = self.all_profile_heights()
def ap_position_check(self):
# Überprüft, ob alle AP's den zulässigen Mindestabstand zueinander einhalten
def _set_rail_dist_from_support_a(self, individual_rail_positions):
# Gibt Abstände zwischen den einzelnen AP-Profilen am Träger und der Säule zurück.
# Wenn 2 Säulen gegeben → Abstände zur "linken" Säule
if not [abs(new_pos - old_pos) < self.min_ap_distance for old_pos, new_pos in zip(self.ap_positions, self.ap_positions[1:])]:
raise ValueError(f"Abstände zwischen ")
else:
return True
# Fall 1: Es wurden keine individuellen Abstände zwischen den AP-Profilen und der Säule definiert.
# In diesem Fall berechnet die Methode die Abstände der AP-Schienen vom Lager "A" ausgehend unter
# Berücksichtigung der Mindestabstände zwischen den Schienen sowie zur Säule am Träger hängen kann.
def ap_amount(self):
return floor(self.beam_length / self.min_ap_distance)
if individual_rail_positions is None:
def individual_ap_positions(self):
# Funktion gibt Einzelabstände der AP-Profile zur Säule zurück
# Fall 1: Es wurden keine Einzelabstände zur Säule definiert. Abstand zwischen AP's = zulässiger Mindestabstand
# Fall 2: Es wurden individuelle AP-Abstände definiert.
if self.ap_positions is None:
return [ap_count * self.min_ap_distance for ap_count in range(1, self.ap_amount() + 1)]
elif self.ap_position_check():
return self.ap_positions
def force_per_ap_meter(self):
if self.bobbin_mass > 4:
raise ValueError(f"Gewicht der Spulen darf max. 4 kg betragen!")
else:
column_rows = 2
location_factor = 9.81 # m/s^2
ap_weight_per_meter = 1.88
total_bobbin_mass = self.bobbin_mass * (1000 / self.segment_type)
mass = total_bobbin_mass + 2 * self.bobbin_train_segment_types[self.segment_type] + ap_weight_per_meter
return mass * location_factor * self.column_distance / column_rows
def ref_moment_of_resistance(self, current_beam_length, ap_column_distances, force_per_rail):
# Funktion berechnet Widerstandsmoment für die aktuelle Trägerlänge
total_column_distance = 0
for ap_column_distance in ap_column_distances:
if current_beam_length >= ap_column_distance:
total_column_distance += ap_column_distance
if self.has_two_supports:
distance_first_to_last_rail = (self.beam_length - self.pillar_width - 2 * self.min_dist_first_rail_to_pillar)
rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance)
distance_two_rails = distance_first_to_last_rail / (rail_amount - 1)
else:
break
distance_first_to_last_rail = self.beam_length - self.pillar_width / 2 - self.min_dist_first_rail_to_pillar
rail_amount = ceil(distance_first_to_last_rail / self.min_rail_distance)
distance_two_rails = distance_first_to_last_rail / (rail_amount - 1)
return [(self.min_dist_first_rail_to_pillar + self.pillar_width / 2) + (i * distance_two_rails) for i in range(rail_amount)]
safety_factor = 1.7
bend_moment = force_per_rail * total_column_distance
allowed_bend_stress = (self.yield_strength * 1.2) / safety_factor
# Fall 2: Es wurden individuelle Abstände zwischen den Lasten und der Säule definiert.
return self.individual_rail_positions
return bend_moment / allowed_bend_stress
def _set_rail_forces(self):
# Gibt die Kräfte der einzelnen Fahrschienen am Träger zurück
# Fall 1: Es wurden keine individuellen Kräfte für die einzelnen Fahrschienen definiert.
# Methode berechnet dann für alle Fahrstrecken dieselbe Kraft.
if self.individual_rail_forces is None:
single_rail_force = ((self.train_segment_mass + self.rail_mass) * ProfileHeights._location_factor)
return [single_rail_force] * len(self.rail_dist_from_support_a)
# Fall 2: Es wurden individuelle Kräfte für die einzelnen Fahrstrecken definiert.
# Methode gibt die individuellen Kräfte unverändert zurück
return self.individual_rail_forces
def _set_train_segment_mass(self):
# Methode ermittelt das Spulenzuggewicht zwischen zwei Säulenreihen
bobbin_amount_per_meter = floor(1000 / int(self.train_segment_type.replace("T", ""))) # Anzahl Spulen pro Meter
bobbin_mass_per_meter = self.single_bobbin_mass * bobbin_amount_per_meter # Gesamtes Spulengewicht pro Meter
total_segment_mass_per_meter = 2 * ProfileHeights._train_segment_masses[self.train_segment_type] + bobbin_mass_per_meter
return total_segment_mass_per_meter * (self.column_row_distance / 1000) / 2 # kg pro Säulenreihe
def _set_rail_mass(self):
# Berechnet gesamtes Fahrstreckengewicht für Länge (Abstand) zwischen Säulenreihen zurück.
return ProfileHeights._rail_masses[self.rail_type] * self.column_row_distance / 1000
def _summed_bend_moment_from_a(self):
# Berechnet Gesamtmoment (Summe der Einzelmomente) am Balken vom Lager "A" ausgehend
return sum([force * distance for force, distance in zip(self.rail_forces, self.rail_dist_from_support_a)])
def _set_support_a_bending_moment(self):
# Berechnet Biegemoment im Lager "A", wenn Lager "A" ein dreiwertiges Lager ist (feste Einspannung)
if not self.has_two_supports:
return self.bend_moments_from_a
return 0
def _set_transverse_force_support_a(self):
# Berechnet Querkraft im Lager "A"
return abs(self.transverse_force_support_b - sum(self.rail_forces))
def _set_transverse_force_support_b(self):
# Berechnet Querkraft im Lager "B", wenn sowohl Lager "A", als auch Lager "B" existiert (nur bei Stützträgern)
if self.has_two_supports:
return self.bend_moments_from_a / self.beam_length
return 0
def _set_beam_section_start_positions(self):
# Bestimmt den Abstand der Bereichsanfänge zum Lager "A"
section_start_positions = self.rail_dist_from_support_a.copy()
section_start_positions.insert(0, 0)
return section_start_positions
def _set_section_forces(self):
# Fügt Querkraft im Lager "A" hinzu, da immer linkes Schnittufer betrachtet wird.
# Dies ist notwendig, da Querkraft "A" im Flächenschwerpunkt des Schnittes in allen Trägerbereichen ein
# Moment erzeugt
section_forces = self.rail_forces.copy()
section_forces.insert(0, self.transverse_force_support_a)
return section_forces
def _set_all_force_positions(self):
if self.has_two_supports:
all_force_positions = self._section_starts.copy()
all_force_positions.insert(len(all_force_positions), self.beam_length)
return all_force_positions
return self._section_starts
def all_forces(self):
all_forces = self._beam_section_forces.copy()
all_forces.insert(len(self.all_force_positions), self.transverse_force_support_b)
return all_forces
def _set_beam_section_lengths(self):
# Bereichslänge = Differenz zwischen nächster Kraftposition und vorherigen Kraftposition
return [new_pos - old_pos for new_pos, old_pos in zip(self.all_force_positions[1:], self.all_force_positions)]
def _set_bending_moments(self):
# Methode unterteilt Träger in Bereiche (vom linken Lager "A" ausgehend zum rechten Lager "b") und gibt die
# Schnittreaktionen (konkret Biegemomente) zurück. Aus diesen wird der Momentenverlauf gebildet.
section_bending_moments = []
subsection_lengths = self._beam_section_lengths.copy()
for i, (section_start, section_length) in enumerate(zip(self._section_starts, self._beam_section_lengths)):
section_moment = (self._beam_section_forces[0] * (section_start + section_length) - self.support_bending_moment)
summed_subsection_lengths = 0
for subsection_length, force in zip(subsection_lengths[:i], self._beam_section_forces[1:]):
summed_subsection_lengths += subsection_length
section_moment -= force * (section_start - summed_subsection_lengths + section_length)
section_bending_moments.append(abs(round((section_moment / 1000), 2)))
if self.has_two_supports:
section_bending_moments.insert(0, 0.0)
else:
section_bending_moments.insert(0, self.support_bending_moment / 1000)
return section_bending_moments
def reference_moment_of_resistance(self, bending_moment):
# Funktion berechnet Referenz-Widerstandsmoment für die aktuelle Trägerlänge
return bending_moment / self.allowed_bend_stress
def _set_allowed_bend_stress(self):
return (self.material_yield_strength * 1.2) / ProfileHeights.safety_factor
def recalculated_moment_of_resistance(self, height):
# Berechnet das Widerstandsmoment mit der derzeitigen Profilhöhe für verschiedene Querschnitts-Arten
# Aktuelle Profilhöhe wird zur Ermittlung des derzeitigen Widerstandsmoments in die Widerstandsmoment-Formeln
# der unterschiedlichen Querschnittsarten eingesetzt.
inner_width = self.width - 2 * self.thickness
inner_height = height - 2 * self.thickness
bar_width = self.width - self.thickness
inner_width = self.cross_section_width - 2 * self.cross_section_thickness
inner_height = height - 2 * self.cross_section_thickness
bar_width = self.cross_section_width - self.cross_section_thickness
# 1. Widerstandsmoment-Berechnung für idealisiertes rechteckiges Hohlprofil:
if self.cross_section_type == "hohlprofil":
return (self.width * height ** 3 - inner_width * inner_height ** 3) / (6 * height)
# 1. Idealisiertes rechteckiges Hohlprofil:
if self.cross_section_type == "Hohlprofil":
return (self.cross_section_width * height ** 3 - inner_width * inner_height ** 3) / (6 * height)
# 2. Widerstandsmoment-Berechnung für idealisiertes IPE-Profil / C-Profil:
elif self.cross_section_type in ["c", "ipe"]:
return (self.width * height ** 3 - bar_width * inner_height ** 3) / (6 * height)
def datum_reference(self):
# Legt fest, ob die notwendige Profilhöhe im Plot in Abhängigkeit von jedem Millimeter der Gesamtlänge [l]
# des Trägers oder der Trägerlänge an der Stelle jedes AP-Profils [n] dargestellt werden soll.
if self.datum == "l":
return list(range(self.min_ap_distance, self.beam_length + 1))
elif self.datum == "n":
return self.individual_ap_positions()
def height_calculation(self):
start_height = 2 * self.thickness
beam_lengths = self.datum_reference()
ap_column_distance = self.individual_ap_positions()
force_per_ap_meter = self.force_per_ap_meter()
# 2. Idealisiertes IPE-Profil / C-Profil:
elif self.cross_section_type in ["C-Profil", "IPE"]:
return (self.cross_section_width * height ** 3 - bar_width * inner_height ** 3) / (6 * height)
def all_profile_heights(self):
heights = []
lengths = []
moment_of_resistances = []
recalculated_moment_of_resistance = 0
for count, beam_length in enumerate(beam_lengths):
reference_moment_of_resistance = self.ref_moment_of_resistance(beam_length, ap_column_distance, force_per_ap_meter)
bending_moments_converted = [i * 1000 for i in self.bending_moments] # Umrechnung von Nm in Nmm
for bending_moment, force_position in zip(bending_moments_converted, self.all_force_positions):
reference_moment_of_resistance = self.reference_moment_of_resistance(abs(bending_moment))
recalculated_moment_of_resistance = 0
height = 2 * self.cross_section_thickness
while recalculated_moment_of_resistance < reference_moment_of_resistance:
recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(start_height)
start_height += 0.01
recalculated_moment_of_resistance = self.recalculated_moment_of_resistance(height)
height += 0.01
heights.append(height)
return heights
moment_of_resistances.append(reference_moment_of_resistance)
heights.append(start_height)
lengths.append(beam_length)
return heights, lengths
def current_value(self, current_beam_length, elements):
for i, force_position in enumerate(self.all_force_positions):
if current_beam_length <= force_position:
# m = Steigung, x = aktuelle Länge im derzeitigen Bereich, t = Höhe bei Bereichsbeginn
m = (elements[i] - elements[i - 1]) / self._beam_section_lengths[i - 1]
x = current_beam_length - self.all_force_positions[i - 1]
t = elements[i - 1]
return m * x + t
a = ProfileHeights(cross_section_type="hohlprofil", datum="l", width=50, thickness=2.5)
height_1 = ProfileHeights(cross_section_type="Hohlprofil",
cross_section_width=50,
cross_section_thickness=3,
individual_rail_positions=[200, 500, 700, 1100],
individual_rail_forces=[500, 500, 500, 500],
beam_length=1200,
min_rail_distance=200,
has_two_supports=True)
ya_heights, xa_lengths = a.height_calculation()
plt.plot(xa_lengths, ya_heights)
plt.xlabel("Länge des Trägers in mm")
plt.ylabel("Benötigte Querschnittshöhe des Trägers in mm")
plt.grid(True)
# ===========================================================================================================================
# = PLOTTEN =
# ===========================================================================================================================
# Monitordaten für Figure-Größe
user32 = ctypes.windll.user32
user32.SetProcessDPIAware()
monitor_width = user32.GetSystemMetrics(0)
monitor_height = user32.GetSystemMetrics(1)
dpi = 120
fig, (ax_drawing, ax_bending_moment, ax_heights) = plt.subplots(nrows=3, ncols=1, sharex=True, figsize=(monitor_width / dpi, monitor_height / dpi))
# Globale Einstellungen
ax_drawing.set_title("Tool zur Bestimmung der Mindestquerschnittshöhe eines Profilträgers", fontsize=20)
subplot_adjust_left = 0.1
plt.subplots_adjust(left=subplot_adjust_left, right=1 - subplot_adjust_left, top=1 - subplot_adjust_left, bottom=subplot_adjust_left, hspace=0)
# Abszisseneinstellungen
x_min_val = -(max(height_1.all_force_positions) * 0.1)
x_max_val = max(height_1.all_force_positions) * 1.1
ax_heights.set_xlim(x_min_val, x_max_val)
ax_heights.set_xticks(height_1.all_force_positions)
ax_heights.xaxis.set_major_formatter(FormatStrFormatter("%.1f"))
# Einstellungen Biegemoment-Plot
ax_bending_moment.set_ylabel("Biegemoment [Nm]")
ax_bending_moment_y_max = max(height_1.bending_moments) * 1.25
ax_bending_moment.set_ylim(0, ax_bending_moment_y_max)
# Einstellungen Profilhöhen-Plot
ax_heights.set_xlabel("Kraftpositionen [mm]")
ax_heights.set_ylabel("Benötigte Profilhöhe [mm]")
ax_heights_y_max = max(height_1.heights) * 1.25
ax_heights.set_ylim(0, ax_heights_y_max)
ax_heights.set_xticks(height_1.all_force_positions)
# Berechnet horizontale Pixelanzahl der Plots
x_plot_px_count = monitor_width * (1 - 2 * subplot_adjust_left)
# Rechnet Längen der x-Achse in Pixel um (wie viele Pixel sind 1 mm?).
# Wichtig, damit die Pfeillängen der Kräfte unabhängig von der Länge des Balkens immer gleich lang dargestellt werden.
mm_in_px_converter = (abs(x_min_val) + abs(x_max_val)) / x_plot_px_count
# ===========================================================================================================================
# = Zeichnung =
# ===========================================================================================================================
# Extremwerte der Zeichnungsordinate:
y_max_drawing = 300
ax_drawing.set_ylim(0, y_max_drawing)
ax_drawing.set_yticks([])
# Darstellung eines schematischen Trägers:
beam_y_pos_in_plot = y_max_drawing * 0.35
ax_drawing.hlines(beam_y_pos_in_plot, height_1.all_force_positions[0], height_1.all_force_positions[len(height_1.all_force_positions) - 1],
color="black", linewidth=3)
force_arrow_length = 0.5 * beam_y_pos_in_plot
def draw_transverse_force(x_pos, y_beam_pos, arrow_length, support_name):
# Darstellung des Balkenendes als Punkt mit Lagerbenennung
ax_drawing.plot(x_pos, y_beam_pos, "o", color="black")
# Pfeildarstellung für Querkraft
ax_drawing.annotate('', xytext=(x_pos, y_beam_pos), xycoords='data', xy=(x_pos, y_beam_pos - arrow_length), textcoords='data',
arrowprops=dict(color="red", width=1, headwidth=6))
# Pfeilbeschreibung:
if support_name == "A":
txt_alignment = "left"
txt_x_offset = -120
name_x_offset = -50
name_y_offset = 15
transverse_force = height_1.transverse_force_support_a
else:
txt_alignment = "left"
txt_x_offset = 20
name_x_offset = 20
name_y_offset = 15
transverse_force = height_1.transverse_force_support_b
# Plottet Querkraft-Wert entweder am Festlager "A", oder am Loslager "B"
ax_drawing.annotate(f"F{support_name}y: {transverse_force:.0f}N", xy=(x_pos, y_beam_pos - arrow_length),
xycoords='data', xytext=(txt_x_offset, 0), textcoords='offset pixels', horizontalalignment=txt_alignment)
# Plottet Lagerbezeichnung
ax_drawing.annotate(f"{support_name}", xy=(x_pos, y_beam_pos), xycoords='data', xytext=(name_x_offset, name_y_offset),
textcoords='offset pixels', ha=txt_alignment, fontsize=20)
def draw_fixed_support(y_beam_pos, px_amount_per_mm, arrow_length, support_name):
# Querkraft:
draw_transverse_force(x_pos=height_1.all_force_positions[0], y_beam_pos=y_beam_pos,
arrow_length=arrow_length, support_name=support_name)
# Normalkraft:
x_arrow_length = px_amount_per_mm * arrow_length
ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[0], y_beam_pos), xy=(-x_arrow_length, y_beam_pos),
textcoords='data', arrowprops=dict(color="red", width=1, headwidth=6))
def draw_couple(y_beam_pos, px_amount_per_mm, arrow_length, support_name):
# Feste Einspannung
draw_fixed_support(y_beam_pos=y_beam_pos, px_amount_per_mm=px_amount_per_mm, arrow_length=arrow_length, support_name=support_name)
# Plottet Querkraft-Pfeil entweder am Festlager "A", oder am Loslager "B"
ax_drawing.annotate(f"M1: {(height_1.support_bending_moment / 1000):.0f} Nm", xy=(height_1.all_force_positions[0], y_beam_pos - arrow_length),
xycoords='data', xytext=(-120, -20), textcoords='offset pixels', horizontalalignment="left")
ax_drawing.plot([height_1.all_force_positions[0] - 2.5], [y_beam_pos], marker=r'$\circlearrowright$', ms=30, color="red")
def drawing_dimensions(y_pos_beam, px_amount_per_mm, arrow_length):
dim_line_distance_from_beam = 70
dim_line_y_pos = y_pos_beam + dim_line_distance_from_beam
dim_leader_extension = 20
for i, (force_pos, force) in enumerate(zip(height_1.all_force_positions, height_1.all_forces())):
# Darstellung Maßhilfslinien
ax_drawing.vlines(force_pos, y_pos_beam, dim_line_y_pos + dim_leader_extension, colors="black", linewidth=1)
if height_1.has_two_supports:
end_index = len(height_1.all_force_positions) - 1
else:
end_index = len(height_1.all_force_positions)
if 0 < i < end_index:
# Kraftpfeile
# Pfeildarstellung
ax_drawing.annotate('', xycoords='data', xytext=(height_1.all_force_positions[i], beam_y_pos_in_plot + arrow_length),
xy=(height_1.all_force_positions[i], beam_y_pos_in_plot), textcoords='data',
arrowprops=dict(color="red", width=1, headwidth=6))
# Pfeilbeschreibung
ax_drawing.annotate(f'F{i}:\n{force:.1f}N', xy=(force_pos, y_pos_beam - arrow_length), xycoords='data', xytext=(0, 0),
textcoords='offset pixels', horizontalalignment="center", fontsize=10)
if i < (len(height_1.all_force_positions) - 1):
# Darstellung Maßlinien
ax_drawing.annotate('', xy=(height_1.all_force_positions[i], dim_line_y_pos), xycoords='data',
xytext=(height_1.all_force_positions[i + 1], dim_line_y_pos), textcoords='data', arrowprops={'arrowstyle': '<->'})
# Darstellung Maße (Bereichslängen)
section_length = (height_1.all_force_positions[i + 1] - force_pos)
dim_x_pos = (section_length / 2) / px_amount_per_mm
ax_drawing.annotate(f'{section_length:.1f}', xy=(force_pos, dim_line_y_pos), xycoords='data', xytext=(dim_x_pos, 5),
textcoords='offset pixels', horizontalalignment="center")
drawing_dimensions(y_pos_beam=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length)
if height_1.has_two_supports:
draw_transverse_force(x_pos=height_1.all_force_positions[len(height_1.all_force_positions) - 1], y_beam_pos=beam_y_pos_in_plot,
arrow_length=force_arrow_length, support_name="B")
draw_fixed_support(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A")
else:
draw_couple(y_beam_pos=beam_y_pos_in_plot, px_amount_per_mm=mm_in_px_converter, arrow_length=force_arrow_length, support_name="A")
# ===========================================================================================================================
# = Höhe und Biegemoment =
# ===========================================================================================================================
def text_alignment():
if height_1.has_two_supports:
return "center"
return "left"
def plot_bending_moment():
# Momentenlinie
ax_bending_moment.plot(height_1.all_force_positions, height_1.bending_moments, color="black")
ax_bending_moment.fill_between(height_1.all_force_positions, height_1.bending_moments, hatch="/", facecolor="#FFFFFF")
# Maximalmoment
bending_moments_and_beam_lengths = dict(zip(height_1.bending_moments, height_1.all_force_positions))
max_bending_moment = max(bending_moments_and_beam_lengths.keys())
len_at_max_bending_moment = bending_moments_and_beam_lengths[max_bending_moment]
text = f"max: {round(max_bending_moment, 2)} Nm"
ax_bending_moment.plot(len_at_max_bending_moment, max_bending_moment, "o", color="black")
ax_bending_moment.annotate(text, xy=(len_at_max_bending_moment, max_bending_moment), xytext=(0, 10), textcoords='offset points',
horizontalalignment=text_alignment())
def plot_heights():
# Hauptplot
ax_heights.plot(height_1.all_force_positions, height_1.heights, color="black",
label=f"{height_1.cross_section_type} - Breite: {height_1.cross_section_width} mm")
ax_heights.fill_between(height_1.all_force_positions, height_1.heights, hatch="/", facecolor="#FFFFFF")
# Plot Maximalhöhe
heights_and_beam_lengths = dict(zip(height_1.heights, height_1.all_force_positions))
max_height = max(heights_and_beam_lengths.keys())
beam_length_at_max_height = heights_and_beam_lengths[max_height]
text = f"max: {max_height:.2f} mm"
ax_heights.plot(beam_length_at_max_height, max_height, "o", color="black")
ax_heights.annotate(text, xy=(beam_length_at_max_height, max_height), xytext=(0, 10), textcoords='offset points',
horizontalalignment=text_alignment())
ax_heights.legend(loc=1)
def update_slider_length(current_length):
ax_heights.clear()
ax_bending_moment.clear()
plot_heights()
current_height = round(height_1.current_value(current_length, height_1.heights), 2)
current_bending_moment = round(height_1.current_value(current_length, height_1.bending_moments), 2)
# Plot aktuelle Höhe
plot_bending_moment()
ax_heights.plot(current_length, current_height, "o", color="black")
ax_heights.annotate(current_height, xy=(current_length, current_height), xytext=(0, 7), textcoords='offset points',
horizontalalignment="center")
# Plot aktuelles Moment
ax_bending_moment.plot(current_length, current_bending_moment, "o", color="black")
ax_bending_moment.annotate(current_bending_moment, xy=(current_length, current_bending_moment), xytext=(0, 7), textcoords='offset points',
horizontalalignment="center")
ax_heights.vlines(current_length, 0, max(height_1.heights) * 1.25, color="gray", linewidth=1)
ax_bending_moment.vlines(current_length, 0, max(height_1.bending_moments) * 1.25, color="gray", linewidth=1)
plt.draw()
plot_bending_moment()
plot_heights()
ax_slider = plt.axes([0.125, 0.0025, 0.775, 0.05], facecolor="blue")
slider = Slider(ax_slider, "Trägerlänge", valmin=0, valmax=height_1.beam_length, valstep=1, valinit=0)
slider.on_changed(update_slider_length)
plt.show()