def test_simply_supported_beam_unequal_non_symmetric_loads(): """Simple beam: Two unequal concentrated loads non-symmetrically placed load case 11 """ P1 = -900 P2 = -1200 a = L / 4 # location of first load from right b = L / 5 # location of second load from left R1 = -(P1 * (L - a) + P2 * b) / L R2 = -(P1 * a + P2 * (L - b)) / L M1 = R1 * a x = L / 2 Mx = R1 * x + P1 * (x - a) M2 = R2 * b p = [ PointLoad(magnitude=P1, location=a), PointLoad(magnitude=P2, location=L - b) ] r = [PinnedReaction(x) for x in [0, L]] beam = Beam(length=L, loads=p, reactions=r, E=E, Ixx=Ixx) beam.solve() # verify reactions for m, loc in zip((M1, Mx, M2), (a, L / 2, L - b)): # assert approx(beam.reactions[0].value[0], rel=1e-4) == R1*1.25 validate(beam, loc=loc, R=[(R1, 0), (R2, 0)], M_loc=m, d_loc=None)
def test_deflection_for_fixed_cantilevered_beam_with_load_at_any_point(): P = -1000 # load in lbs down a = 7 # position of load in inches L = 25 # length of beam in inches E = 29e6 # Young's modulus in psi Ixx = 345 # area moment of inertia in in**4 b = L - a # function to calculate deflection anywhere along the length of the beam d1 = lambda x: P * b**2 / (6 * E * Ixx) * (3 * L - 3 * x - b) # x < a d2 = lambda x: P * (L - x)**2 / (6 * E * Ixx) * (3 * b - L + x) # x > a d3 = lambda x: P * b**3 / (3 * E * Ixx) # x == a beam = Beam(L, [PointLoad(P, a)], [FixedReaction(L)], E, Ixx) for x in [0, 7, 12.5, 25]: # check the deflection of the beam at both end points, and the center if x < a: exact = d1(x) elif x > a: exact = d2(x) else: exact = d3(x) assert pytest.approx(beam.deflection(x), rel=1e-12) == exact, \ f"Calculated deflection does not match expected deflection at {x}"
def test_simply_supported_beam_offset_load(): """simple beam - concentrated load at arbitrary points Load case 8 """ locations = [2, 3, 5, 7, 8] for location in locations: # Exact setup a = location b = L - a R1 = -P * b / L R2 = -P * a / L M_loc = -P * a * b / L # moment at load d_loc = P * a**2 * b**2 / (3 * EI * L) # deflection at load # numerical result beam = Beam( L, loads=[PointLoad(P, location)], reactions=[PinnedReaction(x) for x in [0, L]], E=E, Ixx=Ixx, ) beam.solve() # verify reactions validate(beam, loc=location, R=[(R1, 0), (R2, 0)], M_loc=M_loc, d_loc=d_loc)
def test_stiffness_matrix_k(): beam = Beam(25, [PointLoad(-100, 25)], [FixedReaction(0)], 29e6, 345) assert beam.K.shape == (4, 4), "stiffness matrix is not expected size" # add another point load and verify the stiffness matrix changes size # accordingly beam.loads.append(PointLoad(-500, 12)) beam.remesh() assert beam.K.shape == (6, 6), "stiffness matrix size did not update"
def test_plot_default_labels(): b = Beam(10, [PointLoad(10, 10)], [FixedReaction(0)]) fig, axes = b.plot() x_labels = ("", "", "Beam position, x") y_labels = ("shear", "moment", "deflection") assert len(axes) == len(x_labels), "wrong number of sub-plots" for ax, x_label, y_label in zip(axes, x_labels, y_labels): assert ax.get_xlabel() == x_label assert ax.get_ylabel() == y_label
def test_plot_custom_labels(): b = Beam(10, [PointLoad(10, 10)], [FixedReaction(0)]) diagrams = ("deflection", "deflection", "moment", "shear") labels = ("def1", "def2", "M", "V") fig, axes = b.plot(diagrams=diagrams, diagram_labels=labels) assert len(axes) == len(diagrams), "wrong number of sub-plots" x_labels = ["" for _ in range(len(diagrams) - 1)] x_labels.append("Beam position, x") for ax, x_label, y_label in zip(axes, x_labels, labels): assert ax.get_xlabel() == x_label assert ax.get_ylabel() == y_label
def test_solve_method(): beam = Beam(25, [PointLoad(-100, 25)], [FixedReaction(0)], 29e6, 345) reaction = beam.reactions[0] assert reaction.force is None, "Reaction force was not None before being solved" assert reaction.moment is None, "Reaction moment was not None before being solved" beam.solve() reaction = beam.reactions[0] assert reaction.force == 100, "Reaction force must be equal to and opposite load" assert ( reaction.moment == 100 * 25), "Reaction moment must be equal to the load times the moment arm"
def test_apply_boundary_conditions(): beam = Beam(25, [PointLoad(-100, 25), PointLoad(-100, 12)], [FixedReaction(0)], 29e6, 345) k = beam.K bcs = [(None, None), (0, 0)] initial_shape = beam.K.shape assert initial_shape == ( 6, 6), "stiffness matrix does not match expected size" ki = beam.apply_boundary_conditions(k, bcs) final_shape = ki.shape assert initial_shape == final_shape, ("stiffness matrix changed shape " "when applying boundary conditions")
def test_shear(): beam = Beam(25, [PointLoad(-1000, 25)], [FixedReaction(0)]) for x in [0.5, 5, 13, 20, 24.5]: assert pytest.approx(beam.shear(x), rel=1e-5) == 1000, \ f"shear does not equal load at location {x}" # right now, the derivative function will try to calculate shear outside # of the beam when calculating shear at or near endpoints. Verify that # calculating shear at ends raises a ValueError. It should also raise a # ValueError when the input is outside the beam for x in [-5, 0, 25, 35]: with pytest.raises(ValueError): beam.shear(x)
def test_reaction_load_warnings(): reactions = [PinnedReaction(x) for x in [0, 120]] loads = [PointLoad(magnitude=-120, location=50)] # with pytest.raises(TypeError): # create a beam where the reactions are not a list Beam(120, reactions=PinnedReaction(0), loads=loads) with pytest.raises(TypeError): # create a beam where the loads are not a list Beam(120, reactions=reactions, loads=PointLoad(-10, 5)) with pytest.raises(ValueError): # beam length must be positive Beam(-10, reactions=reactions, loads=loads) with pytest.raises(TypeError): # beam length must be a number Beam("length is not a number", reactions=reactions, loads=loads)
def test_simply_supported_beam_equal_symmetric_loads(): """Simple beam: Two equal concentrated loads symmetrically placed load case 9 """ R = -P # both reactions are equal a = L / 4 M_loc = -P * a # max moment (at center between loads) d_loc = P * a / (24 * EI) * (3 * L**2 - 4 * a**2 ) # max deflection (at center) p = [PointLoad(magnitude=P, location=x) for x in [a, L - a]] r = [PinnedReaction(x) for x in [0, L]] beam = Beam(length=L, loads=p, reactions=r, E=E, Ixx=Ixx) beam.solve() # verify reactions validate(beam, loc=L / 2, R=[(R, 0), (R, 0)], M_loc=M_loc, d_loc=d_loc)
def test_beam_params(): reactions = [PinnedReaction(x) for x in [1, 120]] loads = [PointLoad(magnitude=-120, location=50)] beam = Beam(length=120, loads=loads, reactions=reactions, E=29e6, Ixx=350) # check parameters of the beam to ensure they match the input assert beam.length == 120, "beam length does not match input" assert beam.E == 29e6, "Young's modulus does not match input" assert beam.Ixx == 350, "area moment of inertia does not match input" # update parameters and verify update was successful beam.length = 130 beam.E = 29.9e6 beam.Ixx = 345 assert beam.length == 130, "beam length does not match input" assert beam.E == 29.9e6, "Young's modulus does not match input" assert beam.Ixx == 345, "area moment of inertia does not match input"
def test_moment_for_fixed_cantilevered_beam_with_load_at_end(): P = -1000 # load in lbs down L = 25 # length of beam in inches E = 29e6 # Young's modulus in psi Ixx = 345 # area moment of inertia in in**4 # function to calculate moment anywhere along the length of the beam m = lambda x: P * x beam = Beam(L, [PointLoad(P, 0)], [FixedReaction(L)], E, Ixx) for x in [7, 12.5, 25]: # check the deflection of the beam at both end points, and the center assert pytest.approx(beam.moment(x), rel=0.01) == m(x), \ f"Calculated moment does not match expected moment at {x}" with pytest.warns(UserWarning): beam.moment(0)
def test_invalid_deflection_location(): beam = Beam(25, [PointLoad(-100, 25)], [FixedReaction(0)], 29e6, 345) with pytest.raises(ValueError): beam.deflection(-4) # a value less than 0 with pytest.raises(ValueError): beam.deflection(beam.length + 5) # a value greater then the length with pytest.raises(TypeError): beam.deflection('a string (not a number)')
def test_node_deflections_at_free_end(): for load in [PointLoad(-100, 25), MomentLoad(-100, 25)]: beam = Beam(25, [load], [FixedReaction(0)], 29e6, 345) # check that the deflection at the free end is non-zero and negative msgs = [ "displacement at free end is not negative", "angular displacement at free end is not negative", ] for i, msg in enumerate(msgs, 2): assert beam.node_deflections[i][0] < 0, msg
def example_2(): """ Cantilevered Beam with 3 Pinned Supports and End Loading """ print("=" * 79) print("Example 2") print("Show an example with 3 Pinned Supports and End Loading\n") beam_len = 10 # Note that both the reaction and load are both lists. They must always be # given to Beam as a list, r = [PinnedReaction(0), PinnedReaction(2), PinnedReaction(6)] # define reactions p = [PointLoad(magnitude=-2, location=beam_len)] # define loads b = Beam(beam_len, loads=p, reactions=r, E=29e6, Ixx=125) # an explicit solve is required to calculate the reaction values b.solve() print(b)
def test_node_deflections_at_fixed_end(): for load in [PointLoad(-100, 25), MomentLoad(-100, 25)]: beam = Beam(25, [load], [FixedReaction(0)], 29e6, 345) # check that the deflection at the fixed end is 0 msgs = [ "displacement at fixed end is non-zero", "angular displacement at fixed end is non-zero", ] for i, msg in enumerate(msgs): assert beam.node_deflections[i][0] == 0, msg
def test_simply_supported_beam_center_load(): """simple beam - concentrated load at center Load case 7 """ # Exact setup R = -P / 2 # lbs, reactions M_max = -P * L / 4 # psi, maximum moment d_max = P * L**3 / (48 * EI) # max displacement # Numerical setup beam = Beam( length=L, loads=[PointLoad(magnitude=P, location=L / 2)], reactions=[PinnedReaction(x) for x in [0, L]], E=E, Ixx=Ixx, ) beam.solve() validate(beam, loc=L / 2, R=[(R, 0), (R, 0)], M_loc=M_max, d_loc=d_max)
def test_invalid_load_placement(): reactions = [PinnedReaction(x) for x in [0, 50, 100]] loads = [PointLoad(-100, x) for x in [0, 50, 100]] # there should be a warning indicating that the load position was moved # slightly so it does not line up with the reaction. with pytest.warns(UserWarning): beam = Beam(100, loads=loads, reactions=reactions) for load, reaction in zip(beam.loads, beam.reactions): assert load.location != reaction.location, \ 'moved load is still the same as a reaction'
def example_1(): """ Cantilevered Beam with Fixed Support and End Loading """ print("=" * 79) print("Example 1") print( "Show an example with a cantilevered beam with a fixed support and " "point load at the end\n" ) beam_len = 10 # Note that both the reaction and load are both lists. They must always be # given to Beam as a list, r = [FixedReaction(0)] # define reactions as list p = [PointLoad(magnitude=-2, location=beam_len)] # define loads as list b = Beam(beam_len, loads=p, reactions=r, E=29e6, Ixx=125) # an explicit solve is required to calculate the reaction values b.solve() print(b)
def test_cantilevered_beam_load_at_end(): """fixed beam with concentrated load at free end case 13 """ R = -P M_max = P * L # at fixed end d_max = P * L**3 / (3 * EI) # at free end beam = Beam( length=L, loads=[PointLoad(magnitude=P, location=0)], reactions=[FixedReaction(L)], E=E, Ixx=Ixx, ) beam.solve() validate(beam, loc=0, R=[(R, M_max)], M_loc=0, d_loc=d_max) assert pytest.approx(beam.moment(L), rel=TOL) == M_max
def test_simply_supported_beam_equal_non_symmetric_loads(): """Simple beam: Two equal concentrated loads non-symmetrically placed load case 10 """ a = L / 4 # location of first load from right b = L / 5 # location of second load from left R1 = -P / L * (L - a + b) R2 = -P / L * (L + a - b) M1 = R1 * a # moment at first load x = L / 2 Mx = R1 * x + P * (x - a) # moment at center M2 = R2 * b # moment at second load p = [PointLoad(magnitude=P, location=x) for x in [a, L - b]] r = [PinnedReaction(x) for x in [0, L]] beam = Beam(length=L, loads=p, reactions=r, E=E, Ixx=Ixx) beam.solve() # verify reactions for m, loc in zip((M1, Mx, M2), (a, L / 2, L - b)): validate(beam, loc=loc, R=[(R1, 0), (R2, 0)], M_loc=m, d_loc=None)
def test_shape_function(): reactions = [PinnedReaction(x) for x in [0, 50, 100]] loads = [PointLoad(-100, x) for x in [0, 50, 100]] beam = Beam(100, loads, reactions, 29e6, 345) assert beam.shape(0).shape == (4, ), "unexpected shape of shape functions" n1, n2, n3, n4 = beam.shape(0) assert n1 == 1, "N1(x=0) != 1" assert n3 == 0, "N3(x=0) != 0" # verify changing the length will not change the end points n1, n2, n3, n4 = beam.shape(0, L=15) assert n1 == 1, "N1(x=0) != 1" assert n3 == 0, "N3(x=0) != 0" n1, n2, n3, n4 = beam.shape(100, L=100) # at x==L assert n1 == 0, "N1(x=L) != 0" assert n3 == 1, "N3(x=L) != 1"
def test_plot_one_diagram(): b = Beam(10, [PointLoad(10, 10)], [FixedReaction(0)]) fig, axes = b.plot(diagrams=("deflection", )) assert len(axes) == 1, "expected length of axes was 1" for ax, y_label in zip(axes, ("deflection", )): assert ax.get_ylabel() == y_label
def beam_fixed(length): yield Beam(length=length, loads=[PointLoad(-100, length)], reactions=[FixedReaction(0)])
def beam_simply_supported(length, reaction_simple, load_centered): yield Beam(length=length, loads=load_centered, reactions=reaction_simple)
def test_plot_diagram_labels_without_diagrams(): with pytest.raises(ValueError): b = Beam(10, [PointLoad(10, 10)], [FixedReaction(0)]) b.plot(diagram_labels=("V, lb", "M, in/lb", "delta, in"))
def test_invalid_reaction_errors(): # Check for an TypeError for a variety of invalid reactions for invalid_reaction in ['a string', PointLoad(25, 15), [], 10]: with pytest.raises(TypeError): Beam(25, loads=[PointLoad(-100, 15)], reactions=[invalid_reaction])
def test_bending_stress_depreciation_warning(): with pytest.warns(DeprecationWarning): b = Beam(10, [PointLoad(10, 10)], [FixedReaction(0)]) b.bending_stress(x=5, c=1)
def test_invalid_load_errors(): # Check for a TypeError for a variety of invalid loads for invalid_load in ['a string', FixedReaction(0), [], 10]: with pytest.raises(TypeError): Beam(25, loads=[invalid_load], reactions=[FixedReaction(0)])