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:
+472
-111
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user