m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3566 lines
142 KiB

7 months ago
  1. """
  2. This module can be used to solve 2D beam bending problems with
  3. singularity functions in mechanics.
  4. """
  5. from sympy.core import S, Symbol, diff, symbols
  6. from sympy.core.add import Add
  7. from sympy.core.expr import Expr
  8. from sympy.core.function import (Derivative, Function)
  9. from sympy.core.mul import Mul
  10. from sympy.core.relational import Eq
  11. from sympy.core.sympify import sympify
  12. from sympy.solvers import linsolve
  13. from sympy.solvers.ode.ode import dsolve
  14. from sympy.solvers.solvers import solve
  15. from sympy.printing import sstr
  16. from sympy.functions import SingularityFunction, Piecewise, factorial
  17. from sympy.integrals import integrate
  18. from sympy.series import limit
  19. from sympy.plotting import plot, PlotGrid
  20. from sympy.geometry.entity import GeometryEntity
  21. from sympy.external import import_module
  22. from sympy.sets.sets import Interval
  23. from sympy.utilities.lambdify import lambdify
  24. from sympy.utilities.decorator import doctest_depends_on
  25. from sympy.utilities.iterables import iterable
  26. numpy = import_module('numpy', import_kwargs={'fromlist':['arange']})
  27. class Beam:
  28. """
  29. A Beam is a structural element that is capable of withstanding load
  30. primarily by resisting against bending. Beams are characterized by
  31. their cross sectional profile(Second moment of area), their length
  32. and their material.
  33. .. note::
  34. A consistent sign convention must be used while solving a beam
  35. bending problem; the results will
  36. automatically follow the chosen sign convention. However, the
  37. chosen sign convention must respect the rule that, on the positive
  38. side of beam's axis (in respect to current section), a loading force
  39. giving positive shear yields a negative moment, as below (the
  40. curved arrow shows the positive moment and rotation):
  41. .. image:: allowed-sign-conventions.png
  42. Examples
  43. ========
  44. There is a beam of length 4 meters. A constant distributed load of 6 N/m
  45. is applied from half of the beam till the end. There are two simple supports
  46. below the beam, one at the starting point and another at the ending point
  47. of the beam. The deflection of the beam at the end is restricted.
  48. Using the sign convention of downwards forces being positive.
  49. >>> from sympy.physics.continuum_mechanics.beam import Beam
  50. >>> from sympy import symbols, Piecewise
  51. >>> E, I = symbols('E, I')
  52. >>> R1, R2 = symbols('R1, R2')
  53. >>> b = Beam(4, E, I)
  54. >>> b.apply_load(R1, 0, -1)
  55. >>> b.apply_load(6, 2, 0)
  56. >>> b.apply_load(R2, 4, -1)
  57. >>> b.bc_deflection = [(0, 0), (4, 0)]
  58. >>> b.boundary_conditions
  59. {'deflection': [(0, 0), (4, 0)], 'slope': []}
  60. >>> b.load
  61. R1*SingularityFunction(x, 0, -1) + R2*SingularityFunction(x, 4, -1) + 6*SingularityFunction(x, 2, 0)
  62. >>> b.solve_for_reaction_loads(R1, R2)
  63. >>> b.load
  64. -3*SingularityFunction(x, 0, -1) + 6*SingularityFunction(x, 2, 0) - 9*SingularityFunction(x, 4, -1)
  65. >>> b.shear_force()
  66. 3*SingularityFunction(x, 0, 0) - 6*SingularityFunction(x, 2, 1) + 9*SingularityFunction(x, 4, 0)
  67. >>> b.bending_moment()
  68. 3*SingularityFunction(x, 0, 1) - 3*SingularityFunction(x, 2, 2) + 9*SingularityFunction(x, 4, 1)
  69. >>> b.slope()
  70. (-3*SingularityFunction(x, 0, 2)/2 + SingularityFunction(x, 2, 3) - 9*SingularityFunction(x, 4, 2)/2 + 7)/(E*I)
  71. >>> b.deflection()
  72. (7*x - SingularityFunction(x, 0, 3)/2 + SingularityFunction(x, 2, 4)/4 - 3*SingularityFunction(x, 4, 3)/2)/(E*I)
  73. >>> b.deflection().rewrite(Piecewise)
  74. (7*x - Piecewise((x**3, x > 0), (0, True))/2
  75. - 3*Piecewise(((x - 4)**3, x > 4), (0, True))/2
  76. + Piecewise(((x - 2)**4, x > 2), (0, True))/4)/(E*I)
  77. """
  78. def __init__(self, length, elastic_modulus, second_moment, area=Symbol('A'), variable=Symbol('x'), base_char='C'):
  79. """Initializes the class.
  80. Parameters
  81. ==========
  82. length : Sympifyable
  83. A Symbol or value representing the Beam's length.
  84. elastic_modulus : Sympifyable
  85. A SymPy expression representing the Beam's Modulus of Elasticity.
  86. It is a measure of the stiffness of the Beam material. It can
  87. also be a continuous function of position along the beam.
  88. second_moment : Sympifyable or Geometry object
  89. Describes the cross-section of the beam via a SymPy expression
  90. representing the Beam's second moment of area. It is a geometrical
  91. property of an area which reflects how its points are distributed
  92. with respect to its neutral axis. It can also be a continuous
  93. function of position along the beam. Alternatively ``second_moment``
  94. can be a shape object such as a ``Polygon`` from the geometry module
  95. representing the shape of the cross-section of the beam. In such cases,
  96. it is assumed that the x-axis of the shape object is aligned with the
  97. bending axis of the beam. The second moment of area will be computed
  98. from the shape object internally.
  99. area : Symbol/float
  100. Represents the cross-section area of beam
  101. variable : Symbol, optional
  102. A Symbol object that will be used as the variable along the beam
  103. while representing the load, shear, moment, slope and deflection
  104. curve. By default, it is set to ``Symbol('x')``.
  105. base_char : String, optional
  106. A String that will be used as base character to generate sequential
  107. symbols for integration constants in cases where boundary conditions
  108. are not sufficient to solve them.
  109. """
  110. self.length = length
  111. self.elastic_modulus = elastic_modulus
  112. if isinstance(second_moment, GeometryEntity):
  113. self.cross_section = second_moment
  114. else:
  115. self.cross_section = None
  116. self.second_moment = second_moment
  117. self.variable = variable
  118. self._base_char = base_char
  119. self._boundary_conditions = {'deflection': [], 'slope': []}
  120. self._load = 0
  121. self._area = area
  122. self._applied_supports = []
  123. self._support_as_loads = []
  124. self._applied_loads = []
  125. self._reaction_loads = {}
  126. self._ild_reactions = {}
  127. self._ild_shear = 0
  128. self._ild_moment = 0
  129. # _original_load is a copy of _load equations with unsubstituted reaction
  130. # forces. It is used for calculating reaction forces in case of I.L.D.
  131. self._original_load = 0
  132. self._composite_type = None
  133. self._hinge_position = None
  134. def __str__(self):
  135. shape_description = self._cross_section if self._cross_section else self._second_moment
  136. str_sol = 'Beam({}, {}, {})'.format(sstr(self._length), sstr(self._elastic_modulus), sstr(shape_description))
  137. return str_sol
  138. @property
  139. def reaction_loads(self):
  140. """ Returns the reaction forces in a dictionary."""
  141. return self._reaction_loads
  142. @property
  143. def ild_shear(self):
  144. """ Returns the I.L.D. shear equation."""
  145. return self._ild_shear
  146. @property
  147. def ild_reactions(self):
  148. """ Returns the I.L.D. reaction forces in a dictionary."""
  149. return self._ild_reactions
  150. @property
  151. def ild_moment(self):
  152. """ Returns the I.L.D. moment equation."""
  153. return self._ild_moment
  154. @property
  155. def length(self):
  156. """Length of the Beam."""
  157. return self._length
  158. @length.setter
  159. def length(self, l):
  160. self._length = sympify(l)
  161. @property
  162. def area(self):
  163. """Cross-sectional area of the Beam. """
  164. return self._area
  165. @area.setter
  166. def area(self, a):
  167. self._area = sympify(a)
  168. @property
  169. def variable(self):
  170. """
  171. A symbol that can be used as a variable along the length of the beam
  172. while representing load distribution, shear force curve, bending
  173. moment, slope curve and the deflection curve. By default, it is set
  174. to ``Symbol('x')``, but this property is mutable.
  175. Examples
  176. ========
  177. >>> from sympy.physics.continuum_mechanics.beam import Beam
  178. >>> from sympy import symbols
  179. >>> E, I, A = symbols('E, I, A')
  180. >>> x, y, z = symbols('x, y, z')
  181. >>> b = Beam(4, E, I)
  182. >>> b.variable
  183. x
  184. >>> b.variable = y
  185. >>> b.variable
  186. y
  187. >>> b = Beam(4, E, I, A, z)
  188. >>> b.variable
  189. z
  190. """
  191. return self._variable
  192. @variable.setter
  193. def variable(self, v):
  194. if isinstance(v, Symbol):
  195. self._variable = v
  196. else:
  197. raise TypeError("""The variable should be a Symbol object.""")
  198. @property
  199. def elastic_modulus(self):
  200. """Young's Modulus of the Beam. """
  201. return self._elastic_modulus
  202. @elastic_modulus.setter
  203. def elastic_modulus(self, e):
  204. self._elastic_modulus = sympify(e)
  205. @property
  206. def second_moment(self):
  207. """Second moment of area of the Beam. """
  208. return self._second_moment
  209. @second_moment.setter
  210. def second_moment(self, i):
  211. self._cross_section = None
  212. if isinstance(i, GeometryEntity):
  213. raise ValueError("To update cross-section geometry use `cross_section` attribute")
  214. else:
  215. self._second_moment = sympify(i)
  216. @property
  217. def cross_section(self):
  218. """Cross-section of the beam"""
  219. return self._cross_section
  220. @cross_section.setter
  221. def cross_section(self, s):
  222. if s:
  223. self._second_moment = s.second_moment_of_area()[0]
  224. self._cross_section = s
  225. @property
  226. def boundary_conditions(self):
  227. """
  228. Returns a dictionary of boundary conditions applied on the beam.
  229. The dictionary has three keywords namely moment, slope and deflection.
  230. The value of each keyword is a list of tuple, where each tuple
  231. contains location and value of a boundary condition in the format
  232. (location, value).
  233. Examples
  234. ========
  235. There is a beam of length 4 meters. The bending moment at 0 should be 4
  236. and at 4 it should be 0. The slope of the beam should be 1 at 0. The
  237. deflection should be 2 at 0.
  238. >>> from sympy.physics.continuum_mechanics.beam import Beam
  239. >>> from sympy import symbols
  240. >>> E, I = symbols('E, I')
  241. >>> b = Beam(4, E, I)
  242. >>> b.bc_deflection = [(0, 2)]
  243. >>> b.bc_slope = [(0, 1)]
  244. >>> b.boundary_conditions
  245. {'deflection': [(0, 2)], 'slope': [(0, 1)]}
  246. Here the deflection of the beam should be ``2`` at ``0``.
  247. Similarly, the slope of the beam should be ``1`` at ``0``.
  248. """
  249. return self._boundary_conditions
  250. @property
  251. def bc_slope(self):
  252. return self._boundary_conditions['slope']
  253. @bc_slope.setter
  254. def bc_slope(self, s_bcs):
  255. self._boundary_conditions['slope'] = s_bcs
  256. @property
  257. def bc_deflection(self):
  258. return self._boundary_conditions['deflection']
  259. @bc_deflection.setter
  260. def bc_deflection(self, d_bcs):
  261. self._boundary_conditions['deflection'] = d_bcs
  262. def join(self, beam, via="fixed"):
  263. """
  264. This method joins two beams to make a new composite beam system.
  265. Passed Beam class instance is attached to the right end of calling
  266. object. This method can be used to form beams having Discontinuous
  267. values of Elastic modulus or Second moment.
  268. Parameters
  269. ==========
  270. beam : Beam class object
  271. The Beam object which would be connected to the right of calling
  272. object.
  273. via : String
  274. States the way two Beam object would get connected
  275. - For axially fixed Beams, via="fixed"
  276. - For Beams connected via hinge, via="hinge"
  277. Examples
  278. ========
  279. There is a cantilever beam of length 4 meters. For first 2 meters
  280. its moment of inertia is `1.5*I` and `I` for the other end.
  281. A pointload of magnitude 4 N is applied from the top at its free end.
  282. >>> from sympy.physics.continuum_mechanics.beam import Beam
  283. >>> from sympy import symbols
  284. >>> E, I = symbols('E, I')
  285. >>> R1, R2 = symbols('R1, R2')
  286. >>> b1 = Beam(2, E, 1.5*I)
  287. >>> b2 = Beam(2, E, I)
  288. >>> b = b1.join(b2, "fixed")
  289. >>> b.apply_load(20, 4, -1)
  290. >>> b.apply_load(R1, 0, -1)
  291. >>> b.apply_load(R2, 0, -2)
  292. >>> b.bc_slope = [(0, 0)]
  293. >>> b.bc_deflection = [(0, 0)]
  294. >>> b.solve_for_reaction_loads(R1, R2)
  295. >>> b.load
  296. 80*SingularityFunction(x, 0, -2) - 20*SingularityFunction(x, 0, -1) + 20*SingularityFunction(x, 4, -1)
  297. >>> b.slope()
  298. (-((-80*SingularityFunction(x, 0, 1) + 10*SingularityFunction(x, 0, 2) - 10*SingularityFunction(x, 4, 2))/I + 120/I)/E + 80.0/(E*I))*SingularityFunction(x, 2, 0)
  299. - 0.666666666666667*(-80*SingularityFunction(x, 0, 1) + 10*SingularityFunction(x, 0, 2) - 10*SingularityFunction(x, 4, 2))*SingularityFunction(x, 0, 0)/(E*I)
  300. + 0.666666666666667*(-80*SingularityFunction(x, 0, 1) + 10*SingularityFunction(x, 0, 2) - 10*SingularityFunction(x, 4, 2))*SingularityFunction(x, 2, 0)/(E*I)
  301. """
  302. x = self.variable
  303. E = self.elastic_modulus
  304. new_length = self.length + beam.length
  305. if self.second_moment != beam.second_moment:
  306. new_second_moment = Piecewise((self.second_moment, x<=self.length),
  307. (beam.second_moment, x<=new_length))
  308. else:
  309. new_second_moment = self.second_moment
  310. if via == "fixed":
  311. new_beam = Beam(new_length, E, new_second_moment, x)
  312. new_beam._composite_type = "fixed"
  313. return new_beam
  314. if via == "hinge":
  315. new_beam = Beam(new_length, E, new_second_moment, x)
  316. new_beam._composite_type = "hinge"
  317. new_beam._hinge_position = self.length
  318. return new_beam
  319. def apply_support(self, loc, type="fixed"):
  320. """
  321. This method applies support to a particular beam object.
  322. Parameters
  323. ==========
  324. loc : Sympifyable
  325. Location of point at which support is applied.
  326. type : String
  327. Determines type of Beam support applied. To apply support structure
  328. with
  329. - zero degree of freedom, type = "fixed"
  330. - one degree of freedom, type = "pin"
  331. - two degrees of freedom, type = "roller"
  332. Examples
  333. ========
  334. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  335. applied in the clockwise direction at the end of the beam. A pointload
  336. of magnitude 8 N is applied from the top of the beam at the starting
  337. point. There are two simple supports below the beam. One at the end
  338. and another one at a distance of 10 meters from the start. The
  339. deflection is restricted at both the supports.
  340. Using the sign convention of upward forces and clockwise moment
  341. being positive.
  342. >>> from sympy.physics.continuum_mechanics.beam import Beam
  343. >>> from sympy import symbols
  344. >>> E, I = symbols('E, I')
  345. >>> b = Beam(30, E, I)
  346. >>> b.apply_support(10, 'roller')
  347. >>> b.apply_support(30, 'roller')
  348. >>> b.apply_load(-8, 0, -1)
  349. >>> b.apply_load(120, 30, -2)
  350. >>> R_10, R_30 = symbols('R_10, R_30')
  351. >>> b.solve_for_reaction_loads(R_10, R_30)
  352. >>> b.load
  353. -8*SingularityFunction(x, 0, -1) + 6*SingularityFunction(x, 10, -1)
  354. + 120*SingularityFunction(x, 30, -2) + 2*SingularityFunction(x, 30, -1)
  355. >>> b.slope()
  356. (-4*SingularityFunction(x, 0, 2) + 3*SingularityFunction(x, 10, 2)
  357. + 120*SingularityFunction(x, 30, 1) + SingularityFunction(x, 30, 2) + 4000/3)/(E*I)
  358. """
  359. loc = sympify(loc)
  360. self._applied_supports.append((loc, type))
  361. if type in ("pin", "roller"):
  362. reaction_load = Symbol('R_'+str(loc))
  363. self.apply_load(reaction_load, loc, -1)
  364. self.bc_deflection.append((loc, 0))
  365. else:
  366. reaction_load = Symbol('R_'+str(loc))
  367. reaction_moment = Symbol('M_'+str(loc))
  368. self.apply_load(reaction_load, loc, -1)
  369. self.apply_load(reaction_moment, loc, -2)
  370. self.bc_deflection.append((loc, 0))
  371. self.bc_slope.append((loc, 0))
  372. self._support_as_loads.append((reaction_moment, loc, -2, None))
  373. self._support_as_loads.append((reaction_load, loc, -1, None))
  374. def apply_load(self, value, start, order, end=None):
  375. """
  376. This method adds up the loads given to a particular beam object.
  377. Parameters
  378. ==========
  379. value : Sympifyable
  380. The value inserted should have the units [Force/(Distance**(n+1)]
  381. where n is the order of applied load.
  382. Units for applied loads:
  383. - For moments, unit = kN*m
  384. - For point loads, unit = kN
  385. - For constant distributed load, unit = kN/m
  386. - For ramp loads, unit = kN/m/m
  387. - For parabolic ramp loads, unit = kN/m/m/m
  388. - ... so on.
  389. start : Sympifyable
  390. The starting point of the applied load. For point moments and
  391. point forces this is the location of application.
  392. order : Integer
  393. The order of the applied load.
  394. - For moments, order = -2
  395. - For point loads, order =-1
  396. - For constant distributed load, order = 0
  397. - For ramp loads, order = 1
  398. - For parabolic ramp loads, order = 2
  399. - ... so on.
  400. end : Sympifyable, optional
  401. An optional argument that can be used if the load has an end point
  402. within the length of the beam.
  403. Examples
  404. ========
  405. There is a beam of length 4 meters. A moment of magnitude 3 Nm is
  406. applied in the clockwise direction at the starting point of the beam.
  407. A point load of magnitude 4 N is applied from the top of the beam at
  408. 2 meters from the starting point and a parabolic ramp load of magnitude
  409. 2 N/m is applied below the beam starting from 2 meters to 3 meters
  410. away from the starting point of the beam.
  411. >>> from sympy.physics.continuum_mechanics.beam import Beam
  412. >>> from sympy import symbols
  413. >>> E, I = symbols('E, I')
  414. >>> b = Beam(4, E, I)
  415. >>> b.apply_load(-3, 0, -2)
  416. >>> b.apply_load(4, 2, -1)
  417. >>> b.apply_load(-2, 2, 2, end=3)
  418. >>> b.load
  419. -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1) - 2*SingularityFunction(x, 2, 2) + 2*SingularityFunction(x, 3, 0) + 4*SingularityFunction(x, 3, 1) + 2*SingularityFunction(x, 3, 2)
  420. """
  421. x = self.variable
  422. value = sympify(value)
  423. start = sympify(start)
  424. order = sympify(order)
  425. self._applied_loads.append((value, start, order, end))
  426. self._load += value*SingularityFunction(x, start, order)
  427. self._original_load += value*SingularityFunction(x, start, order)
  428. if end:
  429. # load has an end point within the length of the beam.
  430. self._handle_end(x, value, start, order, end, type="apply")
  431. def remove_load(self, value, start, order, end=None):
  432. """
  433. This method removes a particular load present on the beam object.
  434. Returns a ValueError if the load passed as an argument is not
  435. present on the beam.
  436. Parameters
  437. ==========
  438. value : Sympifyable
  439. The magnitude of an applied load.
  440. start : Sympifyable
  441. The starting point of the applied load. For point moments and
  442. point forces this is the location of application.
  443. order : Integer
  444. The order of the applied load.
  445. - For moments, order= -2
  446. - For point loads, order=-1
  447. - For constant distributed load, order=0
  448. - For ramp loads, order=1
  449. - For parabolic ramp loads, order=2
  450. - ... so on.
  451. end : Sympifyable, optional
  452. An optional argument that can be used if the load has an end point
  453. within the length of the beam.
  454. Examples
  455. ========
  456. There is a beam of length 4 meters. A moment of magnitude 3 Nm is
  457. applied in the clockwise direction at the starting point of the beam.
  458. A pointload of magnitude 4 N is applied from the top of the beam at
  459. 2 meters from the starting point and a parabolic ramp load of magnitude
  460. 2 N/m is applied below the beam starting from 2 meters to 3 meters
  461. away from the starting point of the beam.
  462. >>> from sympy.physics.continuum_mechanics.beam import Beam
  463. >>> from sympy import symbols
  464. >>> E, I = symbols('E, I')
  465. >>> b = Beam(4, E, I)
  466. >>> b.apply_load(-3, 0, -2)
  467. >>> b.apply_load(4, 2, -1)
  468. >>> b.apply_load(-2, 2, 2, end=3)
  469. >>> b.load
  470. -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1) - 2*SingularityFunction(x, 2, 2) + 2*SingularityFunction(x, 3, 0) + 4*SingularityFunction(x, 3, 1) + 2*SingularityFunction(x, 3, 2)
  471. >>> b.remove_load(-2, 2, 2, end = 3)
  472. >>> b.load
  473. -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1)
  474. """
  475. x = self.variable
  476. value = sympify(value)
  477. start = sympify(start)
  478. order = sympify(order)
  479. if (value, start, order, end) in self._applied_loads:
  480. self._load -= value*SingularityFunction(x, start, order)
  481. self._original_load -= value*SingularityFunction(x, start, order)
  482. self._applied_loads.remove((value, start, order, end))
  483. else:
  484. msg = "No such load distribution exists on the beam object."
  485. raise ValueError(msg)
  486. if end:
  487. # load has an end point within the length of the beam.
  488. self._handle_end(x, value, start, order, end, type="remove")
  489. def _handle_end(self, x, value, start, order, end, type):
  490. """
  491. This functions handles the optional `end` value in the
  492. `apply_load` and `remove_load` functions. When the value
  493. of end is not NULL, this function will be executed.
  494. """
  495. if order.is_negative:
  496. msg = ("If 'end' is provided the 'order' of the load cannot "
  497. "be negative, i.e. 'end' is only valid for distributed "
  498. "loads.")
  499. raise ValueError(msg)
  500. # NOTE : A Taylor series can be used to define the summation of
  501. # singularity functions that subtract from the load past the end
  502. # point such that it evaluates to zero past 'end'.
  503. f = value*x**order
  504. if type == "apply":
  505. # iterating for "apply_load" method
  506. for i in range(0, order + 1):
  507. self._load -= (f.diff(x, i).subs(x, end - start) *
  508. SingularityFunction(x, end, i)/factorial(i))
  509. self._original_load -= (f.diff(x, i).subs(x, end - start) *
  510. SingularityFunction(x, end, i)/factorial(i))
  511. elif type == "remove":
  512. # iterating for "remove_load" method
  513. for i in range(0, order + 1):
  514. self._load += (f.diff(x, i).subs(x, end - start) *
  515. SingularityFunction(x, end, i)/factorial(i))
  516. self._original_load += (f.diff(x, i).subs(x, end - start) *
  517. SingularityFunction(x, end, i)/factorial(i))
  518. @property
  519. def load(self):
  520. """
  521. Returns a Singularity Function expression which represents
  522. the load distribution curve of the Beam object.
  523. Examples
  524. ========
  525. There is a beam of length 4 meters. A moment of magnitude 3 Nm is
  526. applied in the clockwise direction at the starting point of the beam.
  527. A point load of magnitude 4 N is applied from the top of the beam at
  528. 2 meters from the starting point and a parabolic ramp load of magnitude
  529. 2 N/m is applied below the beam starting from 3 meters away from the
  530. starting point of the beam.
  531. >>> from sympy.physics.continuum_mechanics.beam import Beam
  532. >>> from sympy import symbols
  533. >>> E, I = symbols('E, I')
  534. >>> b = Beam(4, E, I)
  535. >>> b.apply_load(-3, 0, -2)
  536. >>> b.apply_load(4, 2, -1)
  537. >>> b.apply_load(-2, 3, 2)
  538. >>> b.load
  539. -3*SingularityFunction(x, 0, -2) + 4*SingularityFunction(x, 2, -1) - 2*SingularityFunction(x, 3, 2)
  540. """
  541. return self._load
  542. @property
  543. def applied_loads(self):
  544. """
  545. Returns a list of all loads applied on the beam object.
  546. Each load in the list is a tuple of form (value, start, order, end).
  547. Examples
  548. ========
  549. There is a beam of length 4 meters. A moment of magnitude 3 Nm is
  550. applied in the clockwise direction at the starting point of the beam.
  551. A pointload of magnitude 4 N is applied from the top of the beam at
  552. 2 meters from the starting point. Another pointload of magnitude 5 N
  553. is applied at same position.
  554. >>> from sympy.physics.continuum_mechanics.beam import Beam
  555. >>> from sympy import symbols
  556. >>> E, I = symbols('E, I')
  557. >>> b = Beam(4, E, I)
  558. >>> b.apply_load(-3, 0, -2)
  559. >>> b.apply_load(4, 2, -1)
  560. >>> b.apply_load(5, 2, -1)
  561. >>> b.load
  562. -3*SingularityFunction(x, 0, -2) + 9*SingularityFunction(x, 2, -1)
  563. >>> b.applied_loads
  564. [(-3, 0, -2, None), (4, 2, -1, None), (5, 2, -1, None)]
  565. """
  566. return self._applied_loads
  567. def _solve_hinge_beams(self, *reactions):
  568. """Method to find integration constants and reactional variables in a
  569. composite beam connected via hinge.
  570. This method resolves the composite Beam into its sub-beams and then
  571. equations of shear force, bending moment, slope and deflection are
  572. evaluated for both of them separately. These equations are then solved
  573. for unknown reactions and integration constants using the boundary
  574. conditions applied on the Beam. Equal deflection of both sub-beams
  575. at the hinge joint gives us another equation to solve the system.
  576. Examples
  577. ========
  578. A combined beam, with constant fkexural rigidity E*I, is formed by joining
  579. a Beam of length 2*l to the right of another Beam of length l. The whole beam
  580. is fixed at both of its both end. A point load of magnitude P is also applied
  581. from the top at a distance of 2*l from starting point.
  582. >>> from sympy.physics.continuum_mechanics.beam import Beam
  583. >>> from sympy import symbols
  584. >>> E, I = symbols('E, I')
  585. >>> l=symbols('l', positive=True)
  586. >>> b1=Beam(l, E, I)
  587. >>> b2=Beam(2*l, E, I)
  588. >>> b=b1.join(b2,"hinge")
  589. >>> M1, A1, M2, A2, P = symbols('M1 A1 M2 A2 P')
  590. >>> b.apply_load(A1,0,-1)
  591. >>> b.apply_load(M1,0,-2)
  592. >>> b.apply_load(P,2*l,-1)
  593. >>> b.apply_load(A2,3*l,-1)
  594. >>> b.apply_load(M2,3*l,-2)
  595. >>> b.bc_slope=[(0,0), (3*l, 0)]
  596. >>> b.bc_deflection=[(0,0), (3*l, 0)]
  597. >>> b.solve_for_reaction_loads(M1, A1, M2, A2)
  598. >>> b.reaction_loads
  599. {A1: -5*P/18, A2: -13*P/18, M1: 5*P*l/18, M2: -4*P*l/9}
  600. >>> b.slope()
  601. (5*P*l*SingularityFunction(x, 0, 1)/18 - 5*P*SingularityFunction(x, 0, 2)/36 + 5*P*SingularityFunction(x, l, 2)/36)*SingularityFunction(x, 0, 0)/(E*I)
  602. - (5*P*l*SingularityFunction(x, 0, 1)/18 - 5*P*SingularityFunction(x, 0, 2)/36 + 5*P*SingularityFunction(x, l, 2)/36)*SingularityFunction(x, l, 0)/(E*I)
  603. + (P*l**2/18 - 4*P*l*SingularityFunction(-l + x, 2*l, 1)/9 - 5*P*SingularityFunction(-l + x, 0, 2)/36 + P*SingularityFunction(-l + x, l, 2)/2
  604. - 13*P*SingularityFunction(-l + x, 2*l, 2)/36)*SingularityFunction(x, l, 0)/(E*I)
  605. >>> b.deflection()
  606. (5*P*l*SingularityFunction(x, 0, 2)/36 - 5*P*SingularityFunction(x, 0, 3)/108 + 5*P*SingularityFunction(x, l, 3)/108)*SingularityFunction(x, 0, 0)/(E*I)
  607. - (5*P*l*SingularityFunction(x, 0, 2)/36 - 5*P*SingularityFunction(x, 0, 3)/108 + 5*P*SingularityFunction(x, l, 3)/108)*SingularityFunction(x, l, 0)/(E*I)
  608. + (5*P*l**3/54 + P*l**2*(-l + x)/18 - 2*P*l*SingularityFunction(-l + x, 2*l, 2)/9 - 5*P*SingularityFunction(-l + x, 0, 3)/108 + P*SingularityFunction(-l + x, l, 3)/6
  609. - 13*P*SingularityFunction(-l + x, 2*l, 3)/108)*SingularityFunction(x, l, 0)/(E*I)
  610. """
  611. x = self.variable
  612. l = self._hinge_position
  613. E = self._elastic_modulus
  614. I = self._second_moment
  615. if isinstance(I, Piecewise):
  616. I1 = I.args[0][0]
  617. I2 = I.args[1][0]
  618. else:
  619. I1 = I2 = I
  620. load_1 = 0 # Load equation on first segment of composite beam
  621. load_2 = 0 # Load equation on second segment of composite beam
  622. # Distributing load on both segments
  623. for load in self.applied_loads:
  624. if load[1] < l:
  625. load_1 += load[0]*SingularityFunction(x, load[1], load[2])
  626. if load[2] == 0:
  627. load_1 -= load[0]*SingularityFunction(x, load[3], load[2])
  628. elif load[2] > 0:
  629. load_1 -= load[0]*SingularityFunction(x, load[3], load[2]) + load[0]*SingularityFunction(x, load[3], 0)
  630. elif load[1] == l:
  631. load_1 += load[0]*SingularityFunction(x, load[1], load[2])
  632. load_2 += load[0]*SingularityFunction(x, load[1] - l, load[2])
  633. elif load[1] > l:
  634. load_2 += load[0]*SingularityFunction(x, load[1] - l, load[2])
  635. if load[2] == 0:
  636. load_2 -= load[0]*SingularityFunction(x, load[3] - l, load[2])
  637. elif load[2] > 0:
  638. load_2 -= load[0]*SingularityFunction(x, load[3] - l, load[2]) + load[0]*SingularityFunction(x, load[3] - l, 0)
  639. h = Symbol('h') # Force due to hinge
  640. load_1 += h*SingularityFunction(x, l, -1)
  641. load_2 -= h*SingularityFunction(x, 0, -1)
  642. eq = []
  643. shear_1 = integrate(load_1, x)
  644. shear_curve_1 = limit(shear_1, x, l)
  645. eq.append(shear_curve_1)
  646. bending_1 = integrate(shear_1, x)
  647. moment_curve_1 = limit(bending_1, x, l)
  648. eq.append(moment_curve_1)
  649. shear_2 = integrate(load_2, x)
  650. shear_curve_2 = limit(shear_2, x, self.length - l)
  651. eq.append(shear_curve_2)
  652. bending_2 = integrate(shear_2, x)
  653. moment_curve_2 = limit(bending_2, x, self.length - l)
  654. eq.append(moment_curve_2)
  655. C1 = Symbol('C1')
  656. C2 = Symbol('C2')
  657. C3 = Symbol('C3')
  658. C4 = Symbol('C4')
  659. slope_1 = S.One/(E*I1)*(integrate(bending_1, x) + C1)
  660. def_1 = S.One/(E*I1)*(integrate((E*I)*slope_1, x) + C1*x + C2)
  661. slope_2 = S.One/(E*I2)*(integrate(integrate(integrate(load_2, x), x), x) + C3)
  662. def_2 = S.One/(E*I2)*(integrate((E*I)*slope_2, x) + C4)
  663. for position, value in self.bc_slope:
  664. if position<l:
  665. eq.append(slope_1.subs(x, position) - value)
  666. else:
  667. eq.append(slope_2.subs(x, position - l) - value)
  668. for position, value in self.bc_deflection:
  669. if position<l:
  670. eq.append(def_1.subs(x, position) - value)
  671. else:
  672. eq.append(def_2.subs(x, position - l) - value)
  673. eq.append(def_1.subs(x, l) - def_2.subs(x, 0)) # Deflection of both the segments at hinge would be equal
  674. constants = list(linsolve(eq, C1, C2, C3, C4, h, *reactions))
  675. reaction_values = list(constants[0])[5:]
  676. self._reaction_loads = dict(zip(reactions, reaction_values))
  677. self._load = self._load.subs(self._reaction_loads)
  678. # Substituting constants and reactional load and moments with their corresponding values
  679. slope_1 = slope_1.subs({C1: constants[0][0], h:constants[0][4]}).subs(self._reaction_loads)
  680. def_1 = def_1.subs({C1: constants[0][0], C2: constants[0][1], h:constants[0][4]}).subs(self._reaction_loads)
  681. slope_2 = slope_2.subs({x: x-l, C3: constants[0][2], h:constants[0][4]}).subs(self._reaction_loads)
  682. def_2 = def_2.subs({x: x-l,C3: constants[0][2], C4: constants[0][3], h:constants[0][4]}).subs(self._reaction_loads)
  683. self._hinge_beam_slope = slope_1*SingularityFunction(x, 0, 0) - slope_1*SingularityFunction(x, l, 0) + slope_2*SingularityFunction(x, l, 0)
  684. self._hinge_beam_deflection = def_1*SingularityFunction(x, 0, 0) - def_1*SingularityFunction(x, l, 0) + def_2*SingularityFunction(x, l, 0)
  685. def solve_for_reaction_loads(self, *reactions):
  686. """
  687. Solves for the reaction forces.
  688. Examples
  689. ========
  690. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  691. applied in the clockwise direction at the end of the beam. A pointload
  692. of magnitude 8 N is applied from the top of the beam at the starting
  693. point. There are two simple supports below the beam. One at the end
  694. and another one at a distance of 10 meters from the start. The
  695. deflection is restricted at both the supports.
  696. Using the sign convention of upward forces and clockwise moment
  697. being positive.
  698. >>> from sympy.physics.continuum_mechanics.beam import Beam
  699. >>> from sympy import symbols
  700. >>> E, I = symbols('E, I')
  701. >>> R1, R2 = symbols('R1, R2')
  702. >>> b = Beam(30, E, I)
  703. >>> b.apply_load(-8, 0, -1)
  704. >>> b.apply_load(R1, 10, -1) # Reaction force at x = 10
  705. >>> b.apply_load(R2, 30, -1) # Reaction force at x = 30
  706. >>> b.apply_load(120, 30, -2)
  707. >>> b.bc_deflection = [(10, 0), (30, 0)]
  708. >>> b.load
  709. R1*SingularityFunction(x, 10, -1) + R2*SingularityFunction(x, 30, -1)
  710. - 8*SingularityFunction(x, 0, -1) + 120*SingularityFunction(x, 30, -2)
  711. >>> b.solve_for_reaction_loads(R1, R2)
  712. >>> b.reaction_loads
  713. {R1: 6, R2: 2}
  714. >>> b.load
  715. -8*SingularityFunction(x, 0, -1) + 6*SingularityFunction(x, 10, -1)
  716. + 120*SingularityFunction(x, 30, -2) + 2*SingularityFunction(x, 30, -1)
  717. """
  718. if self._composite_type == "hinge":
  719. return self._solve_hinge_beams(*reactions)
  720. x = self.variable
  721. l = self.length
  722. C3 = Symbol('C3')
  723. C4 = Symbol('C4')
  724. shear_curve = limit(self.shear_force(), x, l)
  725. moment_curve = limit(self.bending_moment(), x, l)
  726. slope_eqs = []
  727. deflection_eqs = []
  728. slope_curve = integrate(self.bending_moment(), x) + C3
  729. for position, value in self._boundary_conditions['slope']:
  730. eqs = slope_curve.subs(x, position) - value
  731. slope_eqs.append(eqs)
  732. deflection_curve = integrate(slope_curve, x) + C4
  733. for position, value in self._boundary_conditions['deflection']:
  734. eqs = deflection_curve.subs(x, position) - value
  735. deflection_eqs.append(eqs)
  736. solution = list((linsolve([shear_curve, moment_curve] + slope_eqs
  737. + deflection_eqs, (C3, C4) + reactions).args)[0])
  738. solution = solution[2:]
  739. self._reaction_loads = dict(zip(reactions, solution))
  740. self._load = self._load.subs(self._reaction_loads)
  741. def shear_force(self):
  742. """
  743. Returns a Singularity Function expression which represents
  744. the shear force curve of the Beam object.
  745. Examples
  746. ========
  747. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  748. applied in the clockwise direction at the end of the beam. A pointload
  749. of magnitude 8 N is applied from the top of the beam at the starting
  750. point. There are two simple supports below the beam. One at the end
  751. and another one at a distance of 10 meters from the start. The
  752. deflection is restricted at both the supports.
  753. Using the sign convention of upward forces and clockwise moment
  754. being positive.
  755. >>> from sympy.physics.continuum_mechanics.beam import Beam
  756. >>> from sympy import symbols
  757. >>> E, I = symbols('E, I')
  758. >>> R1, R2 = symbols('R1, R2')
  759. >>> b = Beam(30, E, I)
  760. >>> b.apply_load(-8, 0, -1)
  761. >>> b.apply_load(R1, 10, -1)
  762. >>> b.apply_load(R2, 30, -1)
  763. >>> b.apply_load(120, 30, -2)
  764. >>> b.bc_deflection = [(10, 0), (30, 0)]
  765. >>> b.solve_for_reaction_loads(R1, R2)
  766. >>> b.shear_force()
  767. 8*SingularityFunction(x, 0, 0) - 6*SingularityFunction(x, 10, 0) - 120*SingularityFunction(x, 30, -1) - 2*SingularityFunction(x, 30, 0)
  768. """
  769. x = self.variable
  770. return -integrate(self.load, x)
  771. def max_shear_force(self):
  772. """Returns maximum Shear force and its coordinate
  773. in the Beam object."""
  774. shear_curve = self.shear_force()
  775. x = self.variable
  776. terms = shear_curve.args
  777. singularity = [] # Points at which shear function changes
  778. for term in terms:
  779. if isinstance(term, Mul):
  780. term = term.args[-1] # SingularityFunction in the term
  781. singularity.append(term.args[1])
  782. singularity.sort()
  783. singularity = list(set(singularity))
  784. intervals = [] # List of Intervals with discrete value of shear force
  785. shear_values = [] # List of values of shear force in each interval
  786. for i, s in enumerate(singularity):
  787. if s == 0:
  788. continue
  789. try:
  790. shear_slope = Piecewise((float("nan"), x<=singularity[i-1]),(self._load.rewrite(Piecewise), x<s), (float("nan"), True))
  791. points = solve(shear_slope, x)
  792. val = []
  793. for point in points:
  794. val.append(abs(shear_curve.subs(x, point)))
  795. points.extend([singularity[i-1], s])
  796. val += [abs(limit(shear_curve, x, singularity[i-1], '+')), abs(limit(shear_curve, x, s, '-'))]
  797. max_shear = max(val)
  798. shear_values.append(max_shear)
  799. intervals.append(points[val.index(max_shear)])
  800. # If shear force in a particular Interval has zero or constant
  801. # slope, then above block gives NotImplementedError as
  802. # solve can't represent Interval solutions.
  803. except NotImplementedError:
  804. initial_shear = limit(shear_curve, x, singularity[i-1], '+')
  805. final_shear = limit(shear_curve, x, s, '-')
  806. # If shear_curve has a constant slope(it is a line).
  807. if shear_curve.subs(x, (singularity[i-1] + s)/2) == (initial_shear + final_shear)/2 and initial_shear != final_shear:
  808. shear_values.extend([initial_shear, final_shear])
  809. intervals.extend([singularity[i-1], s])
  810. else: # shear_curve has same value in whole Interval
  811. shear_values.append(final_shear)
  812. intervals.append(Interval(singularity[i-1], s))
  813. shear_values = list(map(abs, shear_values))
  814. maximum_shear = max(shear_values)
  815. point = intervals[shear_values.index(maximum_shear)]
  816. return (point, maximum_shear)
  817. def bending_moment(self):
  818. """
  819. Returns a Singularity Function expression which represents
  820. the bending moment curve of the Beam object.
  821. Examples
  822. ========
  823. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  824. applied in the clockwise direction at the end of the beam. A pointload
  825. of magnitude 8 N is applied from the top of the beam at the starting
  826. point. There are two simple supports below the beam. One at the end
  827. and another one at a distance of 10 meters from the start. The
  828. deflection is restricted at both the supports.
  829. Using the sign convention of upward forces and clockwise moment
  830. being positive.
  831. >>> from sympy.physics.continuum_mechanics.beam import Beam
  832. >>> from sympy import symbols
  833. >>> E, I = symbols('E, I')
  834. >>> R1, R2 = symbols('R1, R2')
  835. >>> b = Beam(30, E, I)
  836. >>> b.apply_load(-8, 0, -1)
  837. >>> b.apply_load(R1, 10, -1)
  838. >>> b.apply_load(R2, 30, -1)
  839. >>> b.apply_load(120, 30, -2)
  840. >>> b.bc_deflection = [(10, 0), (30, 0)]
  841. >>> b.solve_for_reaction_loads(R1, R2)
  842. >>> b.bending_moment()
  843. 8*SingularityFunction(x, 0, 1) - 6*SingularityFunction(x, 10, 1) - 120*SingularityFunction(x, 30, 0) - 2*SingularityFunction(x, 30, 1)
  844. """
  845. x = self.variable
  846. return integrate(self.shear_force(), x)
  847. def max_bmoment(self):
  848. """Returns maximum Shear force and its coordinate
  849. in the Beam object."""
  850. bending_curve = self.bending_moment()
  851. x = self.variable
  852. terms = bending_curve.args
  853. singularity = [] # Points at which bending moment changes
  854. for term in terms:
  855. if isinstance(term, Mul):
  856. term = term.args[-1] # SingularityFunction in the term
  857. singularity.append(term.args[1])
  858. singularity.sort()
  859. singularity = list(set(singularity))
  860. intervals = [] # List of Intervals with discrete value of bending moment
  861. moment_values = [] # List of values of bending moment in each interval
  862. for i, s in enumerate(singularity):
  863. if s == 0:
  864. continue
  865. try:
  866. moment_slope = Piecewise((float("nan"), x<=singularity[i-1]),(self.shear_force().rewrite(Piecewise), x<s), (float("nan"), True))
  867. points = solve(moment_slope, x)
  868. val = []
  869. for point in points:
  870. val.append(abs(bending_curve.subs(x, point)))
  871. points.extend([singularity[i-1], s])
  872. val += [abs(limit(bending_curve, x, singularity[i-1], '+')), abs(limit(bending_curve, x, s, '-'))]
  873. max_moment = max(val)
  874. moment_values.append(max_moment)
  875. intervals.append(points[val.index(max_moment)])
  876. # If bending moment in a particular Interval has zero or constant
  877. # slope, then above block gives NotImplementedError as solve
  878. # can't represent Interval solutions.
  879. except NotImplementedError:
  880. initial_moment = limit(bending_curve, x, singularity[i-1], '+')
  881. final_moment = limit(bending_curve, x, s, '-')
  882. # If bending_curve has a constant slope(it is a line).
  883. if bending_curve.subs(x, (singularity[i-1] + s)/2) == (initial_moment + final_moment)/2 and initial_moment != final_moment:
  884. moment_values.extend([initial_moment, final_moment])
  885. intervals.extend([singularity[i-1], s])
  886. else: # bending_curve has same value in whole Interval
  887. moment_values.append(final_moment)
  888. intervals.append(Interval(singularity[i-1], s))
  889. moment_values = list(map(abs, moment_values))
  890. maximum_moment = max(moment_values)
  891. point = intervals[moment_values.index(maximum_moment)]
  892. return (point, maximum_moment)
  893. def point_cflexure(self):
  894. """
  895. Returns a Set of point(s) with zero bending moment and
  896. where bending moment curve of the beam object changes
  897. its sign from negative to positive or vice versa.
  898. Examples
  899. ========
  900. There is is 10 meter long overhanging beam. There are
  901. two simple supports below the beam. One at the start
  902. and another one at a distance of 6 meters from the start.
  903. Point loads of magnitude 10KN and 20KN are applied at
  904. 2 meters and 4 meters from start respectively. A Uniformly
  905. distribute load of magnitude of magnitude 3KN/m is also
  906. applied on top starting from 6 meters away from starting
  907. point till end.
  908. Using the sign convention of upward forces and clockwise moment
  909. being positive.
  910. >>> from sympy.physics.continuum_mechanics.beam import Beam
  911. >>> from sympy import symbols
  912. >>> E, I = symbols('E, I')
  913. >>> b = Beam(10, E, I)
  914. >>> b.apply_load(-4, 0, -1)
  915. >>> b.apply_load(-46, 6, -1)
  916. >>> b.apply_load(10, 2, -1)
  917. >>> b.apply_load(20, 4, -1)
  918. >>> b.apply_load(3, 6, 0)
  919. >>> b.point_cflexure()
  920. [10/3]
  921. """
  922. # To restrict the range within length of the Beam
  923. moment_curve = Piecewise((float("nan"), self.variable<=0),
  924. (self.bending_moment(), self.variable<self.length),
  925. (float("nan"), True))
  926. points = solve(moment_curve.rewrite(Piecewise), self.variable,
  927. domain=S.Reals)
  928. return points
  929. def slope(self):
  930. """
  931. Returns a Singularity Function expression which represents
  932. the slope the elastic curve of the Beam object.
  933. Examples
  934. ========
  935. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  936. applied in the clockwise direction at the end of the beam. A pointload
  937. of magnitude 8 N is applied from the top of the beam at the starting
  938. point. There are two simple supports below the beam. One at the end
  939. and another one at a distance of 10 meters from the start. The
  940. deflection is restricted at both the supports.
  941. Using the sign convention of upward forces and clockwise moment
  942. being positive.
  943. >>> from sympy.physics.continuum_mechanics.beam import Beam
  944. >>> from sympy import symbols
  945. >>> E, I = symbols('E, I')
  946. >>> R1, R2 = symbols('R1, R2')
  947. >>> b = Beam(30, E, I)
  948. >>> b.apply_load(-8, 0, -1)
  949. >>> b.apply_load(R1, 10, -1)
  950. >>> b.apply_load(R2, 30, -1)
  951. >>> b.apply_load(120, 30, -2)
  952. >>> b.bc_deflection = [(10, 0), (30, 0)]
  953. >>> b.solve_for_reaction_loads(R1, R2)
  954. >>> b.slope()
  955. (-4*SingularityFunction(x, 0, 2) + 3*SingularityFunction(x, 10, 2)
  956. + 120*SingularityFunction(x, 30, 1) + SingularityFunction(x, 30, 2) + 4000/3)/(E*I)
  957. """
  958. x = self.variable
  959. E = self.elastic_modulus
  960. I = self.second_moment
  961. if self._composite_type == "hinge":
  962. return self._hinge_beam_slope
  963. if not self._boundary_conditions['slope']:
  964. return diff(self.deflection(), x)
  965. if isinstance(I, Piecewise) and self._composite_type == "fixed":
  966. args = I.args
  967. slope = 0
  968. prev_slope = 0
  969. prev_end = 0
  970. for i in range(len(args)):
  971. if i != 0:
  972. prev_end = args[i-1][1].args[1]
  973. slope_value = -S.One/E*integrate(self.bending_moment()/args[i][0], (x, prev_end, x))
  974. if i != len(args) - 1:
  975. slope += (prev_slope + slope_value)*SingularityFunction(x, prev_end, 0) - \
  976. (prev_slope + slope_value)*SingularityFunction(x, args[i][1].args[1], 0)
  977. else:
  978. slope += (prev_slope + slope_value)*SingularityFunction(x, prev_end, 0)
  979. prev_slope = slope_value.subs(x, args[i][1].args[1])
  980. return slope
  981. C3 = Symbol('C3')
  982. slope_curve = -integrate(S.One/(E*I)*self.bending_moment(), x) + C3
  983. bc_eqs = []
  984. for position, value in self._boundary_conditions['slope']:
  985. eqs = slope_curve.subs(x, position) - value
  986. bc_eqs.append(eqs)
  987. constants = list(linsolve(bc_eqs, C3))
  988. slope_curve = slope_curve.subs({C3: constants[0][0]})
  989. return slope_curve
  990. def deflection(self):
  991. """
  992. Returns a Singularity Function expression which represents
  993. the elastic curve or deflection of the Beam object.
  994. Examples
  995. ========
  996. There is a beam of length 30 meters. A moment of magnitude 120 Nm is
  997. applied in the clockwise direction at the end of the beam. A pointload
  998. of magnitude 8 N is applied from the top of the beam at the starting
  999. point. There are two simple supports below the beam. One at the end
  1000. and another one at a distance of 10 meters from the start. The
  1001. deflection is restricted at both the supports.
  1002. Using the sign convention of upward forces and clockwise moment
  1003. being positive.
  1004. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1005. >>> from sympy import symbols
  1006. >>> E, I = symbols('E, I')
  1007. >>> R1, R2 = symbols('R1, R2')
  1008. >>> b = Beam(30, E, I)
  1009. >>> b.apply_load(-8, 0, -1)
  1010. >>> b.apply_load(R1, 10, -1)
  1011. >>> b.apply_load(R2, 30, -1)
  1012. >>> b.apply_load(120, 30, -2)
  1013. >>> b.bc_deflection = [(10, 0), (30, 0)]
  1014. >>> b.solve_for_reaction_loads(R1, R2)
  1015. >>> b.deflection()
  1016. (4000*x/3 - 4*SingularityFunction(x, 0, 3)/3 + SingularityFunction(x, 10, 3)
  1017. + 60*SingularityFunction(x, 30, 2) + SingularityFunction(x, 30, 3)/3 - 12000)/(E*I)
  1018. """
  1019. x = self.variable
  1020. E = self.elastic_modulus
  1021. I = self.second_moment
  1022. if self._composite_type == "hinge":
  1023. return self._hinge_beam_deflection
  1024. if not self._boundary_conditions['deflection'] and not self._boundary_conditions['slope']:
  1025. if isinstance(I, Piecewise) and self._composite_type == "fixed":
  1026. args = I.args
  1027. prev_slope = 0
  1028. prev_def = 0
  1029. prev_end = 0
  1030. deflection = 0
  1031. for i in range(len(args)):
  1032. if i != 0:
  1033. prev_end = args[i-1][1].args[1]
  1034. slope_value = -S.One/E*integrate(self.bending_moment()/args[i][0], (x, prev_end, x))
  1035. recent_segment_slope = prev_slope + slope_value
  1036. deflection_value = integrate(recent_segment_slope, (x, prev_end, x))
  1037. if i != len(args) - 1:
  1038. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0) \
  1039. - (prev_def + deflection_value)*SingularityFunction(x, args[i][1].args[1], 0)
  1040. else:
  1041. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0)
  1042. prev_slope = slope_value.subs(x, args[i][1].args[1])
  1043. prev_def = deflection_value.subs(x, args[i][1].args[1])
  1044. return deflection
  1045. base_char = self._base_char
  1046. constants = symbols(base_char + '3:5')
  1047. return S.One/(E*I)*integrate(-integrate(self.bending_moment(), x), x) + constants[0]*x + constants[1]
  1048. elif not self._boundary_conditions['deflection']:
  1049. base_char = self._base_char
  1050. constant = symbols(base_char + '4')
  1051. return integrate(self.slope(), x) + constant
  1052. elif not self._boundary_conditions['slope'] and self._boundary_conditions['deflection']:
  1053. if isinstance(I, Piecewise) and self._composite_type == "fixed":
  1054. args = I.args
  1055. prev_slope = 0
  1056. prev_def = 0
  1057. prev_end = 0
  1058. deflection = 0
  1059. for i in range(len(args)):
  1060. if i != 0:
  1061. prev_end = args[i-1][1].args[1]
  1062. slope_value = -S.One/E*integrate(self.bending_moment()/args[i][0], (x, prev_end, x))
  1063. recent_segment_slope = prev_slope + slope_value
  1064. deflection_value = integrate(recent_segment_slope, (x, prev_end, x))
  1065. if i != len(args) - 1:
  1066. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0) \
  1067. - (prev_def + deflection_value)*SingularityFunction(x, args[i][1].args[1], 0)
  1068. else:
  1069. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0)
  1070. prev_slope = slope_value.subs(x, args[i][1].args[1])
  1071. prev_def = deflection_value.subs(x, args[i][1].args[1])
  1072. return deflection
  1073. base_char = self._base_char
  1074. C3, C4 = symbols(base_char + '3:5') # Integration constants
  1075. slope_curve = -integrate(self.bending_moment(), x) + C3
  1076. deflection_curve = integrate(slope_curve, x) + C4
  1077. bc_eqs = []
  1078. for position, value in self._boundary_conditions['deflection']:
  1079. eqs = deflection_curve.subs(x, position) - value
  1080. bc_eqs.append(eqs)
  1081. constants = list(linsolve(bc_eqs, (C3, C4)))
  1082. deflection_curve = deflection_curve.subs({C3: constants[0][0], C4: constants[0][1]})
  1083. return S.One/(E*I)*deflection_curve
  1084. if isinstance(I, Piecewise) and self._composite_type == "fixed":
  1085. args = I.args
  1086. prev_slope = 0
  1087. prev_def = 0
  1088. prev_end = 0
  1089. deflection = 0
  1090. for i in range(len(args)):
  1091. if i != 0:
  1092. prev_end = args[i-1][1].args[1]
  1093. slope_value = S.One/E*integrate(self.bending_moment()/args[i][0], (x, prev_end, x))
  1094. recent_segment_slope = prev_slope + slope_value
  1095. deflection_value = integrate(recent_segment_slope, (x, prev_end, x))
  1096. if i != len(args) - 1:
  1097. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0) \
  1098. - (prev_def + deflection_value)*SingularityFunction(x, args[i][1].args[1], 0)
  1099. else:
  1100. deflection += (prev_def + deflection_value)*SingularityFunction(x, prev_end, 0)
  1101. prev_slope = slope_value.subs(x, args[i][1].args[1])
  1102. prev_def = deflection_value.subs(x, args[i][1].args[1])
  1103. return deflection
  1104. C4 = Symbol('C4')
  1105. deflection_curve = integrate(self.slope(), x) + C4
  1106. bc_eqs = []
  1107. for position, value in self._boundary_conditions['deflection']:
  1108. eqs = deflection_curve.subs(x, position) - value
  1109. bc_eqs.append(eqs)
  1110. constants = list(linsolve(bc_eqs, C4))
  1111. deflection_curve = deflection_curve.subs({C4: constants[0][0]})
  1112. return deflection_curve
  1113. def max_deflection(self):
  1114. """
  1115. Returns point of max deflection and its corresponding deflection value
  1116. in a Beam object.
  1117. """
  1118. # To restrict the range within length of the Beam
  1119. slope_curve = Piecewise((float("nan"), self.variable<=0),
  1120. (self.slope(), self.variable<self.length),
  1121. (float("nan"), True))
  1122. points = solve(slope_curve.rewrite(Piecewise), self.variable,
  1123. domain=S.Reals)
  1124. deflection_curve = self.deflection()
  1125. deflections = [deflection_curve.subs(self.variable, x) for x in points]
  1126. deflections = list(map(abs, deflections))
  1127. if len(deflections) != 0:
  1128. max_def = max(deflections)
  1129. return (points[deflections.index(max_def)], max_def)
  1130. else:
  1131. return None
  1132. def shear_stress(self):
  1133. """
  1134. Returns an expression representing the Shear Stress
  1135. curve of the Beam object.
  1136. """
  1137. return self.shear_force()/self._area
  1138. def plot_shear_stress(self, subs=None):
  1139. """
  1140. Returns a plot of shear stress present in the beam object.
  1141. Parameters
  1142. ==========
  1143. subs : dictionary
  1144. Python dictionary containing Symbols as key and their
  1145. corresponding values.
  1146. Examples
  1147. ========
  1148. There is a beam of length 8 meters and area of cross section 2 square
  1149. meters. A constant distributed load of 10 KN/m is applied from half of
  1150. the beam till the end. There are two simple supports below the beam,
  1151. one at the starting point and another at the ending point of the beam.
  1152. A pointload of magnitude 5 KN is also applied from top of the
  1153. beam, at a distance of 4 meters from the starting point.
  1154. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1155. Using the sign convention of downwards forces being positive.
  1156. .. plot::
  1157. :context: close-figs
  1158. :format: doctest
  1159. :include-source: True
  1160. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1161. >>> from sympy import symbols
  1162. >>> R1, R2 = symbols('R1, R2')
  1163. >>> b = Beam(8, 200*(10**9), 400*(10**-6), 2)
  1164. >>> b.apply_load(5000, 2, -1)
  1165. >>> b.apply_load(R1, 0, -1)
  1166. >>> b.apply_load(R2, 8, -1)
  1167. >>> b.apply_load(10000, 4, 0, end=8)
  1168. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1169. >>> b.solve_for_reaction_loads(R1, R2)
  1170. >>> b.plot_shear_stress()
  1171. Plot object containing:
  1172. [0]: cartesian line: 6875*SingularityFunction(x, 0, 0) - 2500*SingularityFunction(x, 2, 0)
  1173. - 5000*SingularityFunction(x, 4, 1) + 15625*SingularityFunction(x, 8, 0)
  1174. + 5000*SingularityFunction(x, 8, 1) for x over (0.0, 8.0)
  1175. """
  1176. shear_stress = self.shear_stress()
  1177. x = self.variable
  1178. length = self.length
  1179. if subs is None:
  1180. subs = {}
  1181. for sym in shear_stress.atoms(Symbol):
  1182. if sym != x and sym not in subs:
  1183. raise ValueError('value of %s was not passed.' %sym)
  1184. if length in subs:
  1185. length = subs[length]
  1186. # Returns Plot of Shear Stress
  1187. return plot (shear_stress.subs(subs), (x, 0, length),
  1188. title='Shear Stress', xlabel=r'$\mathrm{x}$', ylabel=r'$\tau$',
  1189. line_color='r')
  1190. def plot_shear_force(self, subs=None):
  1191. """
  1192. Returns a plot for Shear force present in the Beam object.
  1193. Parameters
  1194. ==========
  1195. subs : dictionary
  1196. Python dictionary containing Symbols as key and their
  1197. corresponding values.
  1198. Examples
  1199. ========
  1200. There is a beam of length 8 meters. A constant distributed load of 10 KN/m
  1201. is applied from half of the beam till the end. There are two simple supports
  1202. below the beam, one at the starting point and another at the ending point
  1203. of the beam. A pointload of magnitude 5 KN is also applied from top of the
  1204. beam, at a distance of 4 meters from the starting point.
  1205. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1206. Using the sign convention of downwards forces being positive.
  1207. .. plot::
  1208. :context: close-figs
  1209. :format: doctest
  1210. :include-source: True
  1211. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1212. >>> from sympy import symbols
  1213. >>> R1, R2 = symbols('R1, R2')
  1214. >>> b = Beam(8, 200*(10**9), 400*(10**-6))
  1215. >>> b.apply_load(5000, 2, -1)
  1216. >>> b.apply_load(R1, 0, -1)
  1217. >>> b.apply_load(R2, 8, -1)
  1218. >>> b.apply_load(10000, 4, 0, end=8)
  1219. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1220. >>> b.solve_for_reaction_loads(R1, R2)
  1221. >>> b.plot_shear_force()
  1222. Plot object containing:
  1223. [0]: cartesian line: 13750*SingularityFunction(x, 0, 0) - 5000*SingularityFunction(x, 2, 0)
  1224. - 10000*SingularityFunction(x, 4, 1) + 31250*SingularityFunction(x, 8, 0)
  1225. + 10000*SingularityFunction(x, 8, 1) for x over (0.0, 8.0)
  1226. """
  1227. shear_force = self.shear_force()
  1228. if subs is None:
  1229. subs = {}
  1230. for sym in shear_force.atoms(Symbol):
  1231. if sym == self.variable:
  1232. continue
  1233. if sym not in subs:
  1234. raise ValueError('Value of %s was not passed.' %sym)
  1235. if self.length in subs:
  1236. length = subs[self.length]
  1237. else:
  1238. length = self.length
  1239. return plot(shear_force.subs(subs), (self.variable, 0, length), title='Shear Force',
  1240. xlabel=r'$\mathrm{x}$', ylabel=r'$\mathrm{V}$', line_color='g')
  1241. def plot_bending_moment(self, subs=None):
  1242. """
  1243. Returns a plot for Bending moment present in the Beam object.
  1244. Parameters
  1245. ==========
  1246. subs : dictionary
  1247. Python dictionary containing Symbols as key and their
  1248. corresponding values.
  1249. Examples
  1250. ========
  1251. There is a beam of length 8 meters. A constant distributed load of 10 KN/m
  1252. is applied from half of the beam till the end. There are two simple supports
  1253. below the beam, one at the starting point and another at the ending point
  1254. of the beam. A pointload of magnitude 5 KN is also applied from top of the
  1255. beam, at a distance of 4 meters from the starting point.
  1256. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1257. Using the sign convention of downwards forces being positive.
  1258. .. plot::
  1259. :context: close-figs
  1260. :format: doctest
  1261. :include-source: True
  1262. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1263. >>> from sympy import symbols
  1264. >>> R1, R2 = symbols('R1, R2')
  1265. >>> b = Beam(8, 200*(10**9), 400*(10**-6))
  1266. >>> b.apply_load(5000, 2, -1)
  1267. >>> b.apply_load(R1, 0, -1)
  1268. >>> b.apply_load(R2, 8, -1)
  1269. >>> b.apply_load(10000, 4, 0, end=8)
  1270. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1271. >>> b.solve_for_reaction_loads(R1, R2)
  1272. >>> b.plot_bending_moment()
  1273. Plot object containing:
  1274. [0]: cartesian line: 13750*SingularityFunction(x, 0, 1) - 5000*SingularityFunction(x, 2, 1)
  1275. - 5000*SingularityFunction(x, 4, 2) + 31250*SingularityFunction(x, 8, 1)
  1276. + 5000*SingularityFunction(x, 8, 2) for x over (0.0, 8.0)
  1277. """
  1278. bending_moment = self.bending_moment()
  1279. if subs is None:
  1280. subs = {}
  1281. for sym in bending_moment.atoms(Symbol):
  1282. if sym == self.variable:
  1283. continue
  1284. if sym not in subs:
  1285. raise ValueError('Value of %s was not passed.' %sym)
  1286. if self.length in subs:
  1287. length = subs[self.length]
  1288. else:
  1289. length = self.length
  1290. return plot(bending_moment.subs(subs), (self.variable, 0, length), title='Bending Moment',
  1291. xlabel=r'$\mathrm{x}$', ylabel=r'$\mathrm{M}$', line_color='b')
  1292. def plot_slope(self, subs=None):
  1293. """
  1294. Returns a plot for slope of deflection curve of the Beam object.
  1295. Parameters
  1296. ==========
  1297. subs : dictionary
  1298. Python dictionary containing Symbols as key and their
  1299. corresponding values.
  1300. Examples
  1301. ========
  1302. There is a beam of length 8 meters. A constant distributed load of 10 KN/m
  1303. is applied from half of the beam till the end. There are two simple supports
  1304. below the beam, one at the starting point and another at the ending point
  1305. of the beam. A pointload of magnitude 5 KN is also applied from top of the
  1306. beam, at a distance of 4 meters from the starting point.
  1307. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1308. Using the sign convention of downwards forces being positive.
  1309. .. plot::
  1310. :context: close-figs
  1311. :format: doctest
  1312. :include-source: True
  1313. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1314. >>> from sympy import symbols
  1315. >>> R1, R2 = symbols('R1, R2')
  1316. >>> b = Beam(8, 200*(10**9), 400*(10**-6))
  1317. >>> b.apply_load(5000, 2, -1)
  1318. >>> b.apply_load(R1, 0, -1)
  1319. >>> b.apply_load(R2, 8, -1)
  1320. >>> b.apply_load(10000, 4, 0, end=8)
  1321. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1322. >>> b.solve_for_reaction_loads(R1, R2)
  1323. >>> b.plot_slope()
  1324. Plot object containing:
  1325. [0]: cartesian line: -8.59375e-5*SingularityFunction(x, 0, 2) + 3.125e-5*SingularityFunction(x, 2, 2)
  1326. + 2.08333333333333e-5*SingularityFunction(x, 4, 3) - 0.0001953125*SingularityFunction(x, 8, 2)
  1327. - 2.08333333333333e-5*SingularityFunction(x, 8, 3) + 0.00138541666666667 for x over (0.0, 8.0)
  1328. """
  1329. slope = self.slope()
  1330. if subs is None:
  1331. subs = {}
  1332. for sym in slope.atoms(Symbol):
  1333. if sym == self.variable:
  1334. continue
  1335. if sym not in subs:
  1336. raise ValueError('Value of %s was not passed.' %sym)
  1337. if self.length in subs:
  1338. length = subs[self.length]
  1339. else:
  1340. length = self.length
  1341. return plot(slope.subs(subs), (self.variable, 0, length), title='Slope',
  1342. xlabel=r'$\mathrm{x}$', ylabel=r'$\theta$', line_color='m')
  1343. def plot_deflection(self, subs=None):
  1344. """
  1345. Returns a plot for deflection curve of the Beam object.
  1346. Parameters
  1347. ==========
  1348. subs : dictionary
  1349. Python dictionary containing Symbols as key and their
  1350. corresponding values.
  1351. Examples
  1352. ========
  1353. There is a beam of length 8 meters. A constant distributed load of 10 KN/m
  1354. is applied from half of the beam till the end. There are two simple supports
  1355. below the beam, one at the starting point and another at the ending point
  1356. of the beam. A pointload of magnitude 5 KN is also applied from top of the
  1357. beam, at a distance of 4 meters from the starting point.
  1358. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1359. Using the sign convention of downwards forces being positive.
  1360. .. plot::
  1361. :context: close-figs
  1362. :format: doctest
  1363. :include-source: True
  1364. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1365. >>> from sympy import symbols
  1366. >>> R1, R2 = symbols('R1, R2')
  1367. >>> b = Beam(8, 200*(10**9), 400*(10**-6))
  1368. >>> b.apply_load(5000, 2, -1)
  1369. >>> b.apply_load(R1, 0, -1)
  1370. >>> b.apply_load(R2, 8, -1)
  1371. >>> b.apply_load(10000, 4, 0, end=8)
  1372. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1373. >>> b.solve_for_reaction_loads(R1, R2)
  1374. >>> b.plot_deflection()
  1375. Plot object containing:
  1376. [0]: cartesian line: 0.00138541666666667*x - 2.86458333333333e-5*SingularityFunction(x, 0, 3)
  1377. + 1.04166666666667e-5*SingularityFunction(x, 2, 3) + 5.20833333333333e-6*SingularityFunction(x, 4, 4)
  1378. - 6.51041666666667e-5*SingularityFunction(x, 8, 3) - 5.20833333333333e-6*SingularityFunction(x, 8, 4)
  1379. for x over (0.0, 8.0)
  1380. """
  1381. deflection = self.deflection()
  1382. if subs is None:
  1383. subs = {}
  1384. for sym in deflection.atoms(Symbol):
  1385. if sym == self.variable:
  1386. continue
  1387. if sym not in subs:
  1388. raise ValueError('Value of %s was not passed.' %sym)
  1389. if self.length in subs:
  1390. length = subs[self.length]
  1391. else:
  1392. length = self.length
  1393. return plot(deflection.subs(subs), (self.variable, 0, length),
  1394. title='Deflection', xlabel=r'$\mathrm{x}$', ylabel=r'$\delta$',
  1395. line_color='r')
  1396. def plot_loading_results(self, subs=None):
  1397. """
  1398. Returns a subplot of Shear Force, Bending Moment,
  1399. Slope and Deflection of the Beam object.
  1400. Parameters
  1401. ==========
  1402. subs : dictionary
  1403. Python dictionary containing Symbols as key and their
  1404. corresponding values.
  1405. Examples
  1406. ========
  1407. There is a beam of length 8 meters. A constant distributed load of 10 KN/m
  1408. is applied from half of the beam till the end. There are two simple supports
  1409. below the beam, one at the starting point and another at the ending point
  1410. of the beam. A pointload of magnitude 5 KN is also applied from top of the
  1411. beam, at a distance of 4 meters from the starting point.
  1412. Take E = 200 GPa and I = 400*(10**-6) meter**4.
  1413. Using the sign convention of downwards forces being positive.
  1414. .. plot::
  1415. :context: close-figs
  1416. :format: doctest
  1417. :include-source: True
  1418. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1419. >>> from sympy import symbols
  1420. >>> R1, R2 = symbols('R1, R2')
  1421. >>> b = Beam(8, 200*(10**9), 400*(10**-6))
  1422. >>> b.apply_load(5000, 2, -1)
  1423. >>> b.apply_load(R1, 0, -1)
  1424. >>> b.apply_load(R2, 8, -1)
  1425. >>> b.apply_load(10000, 4, 0, end=8)
  1426. >>> b.bc_deflection = [(0, 0), (8, 0)]
  1427. >>> b.solve_for_reaction_loads(R1, R2)
  1428. >>> axes = b.plot_loading_results()
  1429. """
  1430. length = self.length
  1431. variable = self.variable
  1432. if subs is None:
  1433. subs = {}
  1434. for sym in self.deflection().atoms(Symbol):
  1435. if sym == self.variable:
  1436. continue
  1437. if sym not in subs:
  1438. raise ValueError('Value of %s was not passed.' %sym)
  1439. if length in subs:
  1440. length = subs[length]
  1441. ax1 = plot(self.shear_force().subs(subs), (variable, 0, length),
  1442. title="Shear Force", xlabel=r'$\mathrm{x}$', ylabel=r'$\mathrm{V}$',
  1443. line_color='g', show=False)
  1444. ax2 = plot(self.bending_moment().subs(subs), (variable, 0, length),
  1445. title="Bending Moment", xlabel=r'$\mathrm{x}$', ylabel=r'$\mathrm{M}$',
  1446. line_color='b', show=False)
  1447. ax3 = plot(self.slope().subs(subs), (variable, 0, length),
  1448. title="Slope", xlabel=r'$\mathrm{x}$', ylabel=r'$\theta$',
  1449. line_color='m', show=False)
  1450. ax4 = plot(self.deflection().subs(subs), (variable, 0, length),
  1451. title="Deflection", xlabel=r'$\mathrm{x}$', ylabel=r'$\delta$',
  1452. line_color='r', show=False)
  1453. return PlotGrid(4, 1, ax1, ax2, ax3, ax4)
  1454. def _solve_for_ild_equations(self):
  1455. """
  1456. Helper function for I.L.D. It takes the unsubstituted
  1457. copy of the load equation and uses it to calculate shear force and bending
  1458. moment equations.
  1459. """
  1460. x = self.variable
  1461. shear_force = -integrate(self._original_load, x)
  1462. bending_moment = integrate(shear_force, x)
  1463. return shear_force, bending_moment
  1464. def solve_for_ild_reactions(self, value, *reactions):
  1465. """
  1466. Determines the Influence Line Diagram equations for reaction
  1467. forces under the effect of a moving load.
  1468. Parameters
  1469. ==========
  1470. value : Integer
  1471. Magnitude of moving load
  1472. reactions :
  1473. The reaction forces applied on the beam.
  1474. Examples
  1475. ========
  1476. There is a beam of length 10 meters. There are two simple supports
  1477. below the beam, one at the starting point and another at the ending
  1478. point of the beam. Calculate the I.L.D. equations for reaction forces
  1479. under the effect of a moving load of magnitude 1kN.
  1480. .. image:: ildreaction.png
  1481. Using the sign convention of downwards forces being positive.
  1482. .. plot::
  1483. :context: close-figs
  1484. :format: doctest
  1485. :include-source: True
  1486. >>> from sympy import symbols
  1487. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1488. >>> E, I = symbols('E, I')
  1489. >>> R_0, R_10 = symbols('R_0, R_10')
  1490. >>> b = Beam(10, E, I)
  1491. >>> b.apply_support(0, 'roller')
  1492. >>> b.apply_support(10, 'roller')
  1493. >>> b.solve_for_ild_reactions(1,R_0,R_10)
  1494. >>> b.ild_reactions
  1495. {R_0: x/10 - 1, R_10: -x/10}
  1496. """
  1497. shear_force, bending_moment = self._solve_for_ild_equations()
  1498. x = self.variable
  1499. l = self.length
  1500. C3 = Symbol('C3')
  1501. C4 = Symbol('C4')
  1502. shear_curve = limit(shear_force, x, l) - value
  1503. moment_curve = limit(bending_moment, x, l) - value*(l-x)
  1504. slope_eqs = []
  1505. deflection_eqs = []
  1506. slope_curve = integrate(bending_moment, x) + C3
  1507. for position, value in self._boundary_conditions['slope']:
  1508. eqs = slope_curve.subs(x, position) - value
  1509. slope_eqs.append(eqs)
  1510. deflection_curve = integrate(slope_curve, x) + C4
  1511. for position, value in self._boundary_conditions['deflection']:
  1512. eqs = deflection_curve.subs(x, position) - value
  1513. deflection_eqs.append(eqs)
  1514. solution = list((linsolve([shear_curve, moment_curve] + slope_eqs
  1515. + deflection_eqs, (C3, C4) + reactions).args)[0])
  1516. solution = solution[2:]
  1517. # Determining the equations and solving them.
  1518. self._ild_reactions = dict(zip(reactions, solution))
  1519. def plot_ild_reactions(self, subs=None):
  1520. """
  1521. Plots the Influence Line Diagram of Reaction Forces
  1522. under the effect of a moving load. This function
  1523. should be called after calling solve_for_ild_reactions().
  1524. Parameters
  1525. ==========
  1526. subs : dictionary
  1527. Python dictionary containing Symbols as key and their
  1528. corresponding values.
  1529. Examples
  1530. ========
  1531. There is a beam of length 10 meters. A point load of magnitude 5KN
  1532. is also applied from top of the beam, at a distance of 4 meters
  1533. from the starting point. There are two simple supports below the
  1534. beam, located at the starting point and at a distance of 7 meters
  1535. from the starting point. Plot the I.L.D. equations for reactions
  1536. at both support points under the effect of a moving load
  1537. of magnitude 1kN.
  1538. Using the sign convention of downwards forces being positive.
  1539. .. plot::
  1540. :context: close-figs
  1541. :format: doctest
  1542. :include-source: True
  1543. >>> from sympy import symbols
  1544. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1545. >>> E, I = symbols('E, I')
  1546. >>> R_0, R_7 = symbols('R_0, R_7')
  1547. >>> b = Beam(10, E, I)
  1548. >>> b.apply_support(0, 'roller')
  1549. >>> b.apply_support(7, 'roller')
  1550. >>> b.apply_load(5,4,-1)
  1551. >>> b.solve_for_ild_reactions(1,R_0,R_7)
  1552. >>> b.ild_reactions
  1553. {R_0: x/7 - 22/7, R_7: -x/7 - 20/7}
  1554. >>> b.plot_ild_reactions()
  1555. PlotGrid object containing:
  1556. Plot[0]:Plot object containing:
  1557. [0]: cartesian line: x/7 - 22/7 for x over (0.0, 10.0)
  1558. Plot[1]:Plot object containing:
  1559. [0]: cartesian line: -x/7 - 20/7 for x over (0.0, 10.0)
  1560. """
  1561. if not self._ild_reactions:
  1562. raise ValueError("I.L.D. reaction equations not found. Please use solve_for_ild_reactions() to generate the I.L.D. reaction equations.")
  1563. x = self.variable
  1564. ildplots = []
  1565. if subs is None:
  1566. subs = {}
  1567. for reaction in self._ild_reactions:
  1568. for sym in self._ild_reactions[reaction].atoms(Symbol):
  1569. if sym != x and sym not in subs:
  1570. raise ValueError('Value of %s was not passed.' %sym)
  1571. for sym in self._length.atoms(Symbol):
  1572. if sym != x and sym not in subs:
  1573. raise ValueError('Value of %s was not passed.' %sym)
  1574. for reaction in self._ild_reactions:
  1575. ildplots.append(plot(self._ild_reactions[reaction].subs(subs),
  1576. (x, 0, self._length.subs(subs)), title='I.L.D. for Reactions',
  1577. xlabel=x, ylabel=reaction, line_color='blue', show=False))
  1578. return PlotGrid(len(ildplots), 1, *ildplots)
  1579. def solve_for_ild_shear(self, distance, value, *reactions):
  1580. """
  1581. Determines the Influence Line Diagram equations for shear at a
  1582. specified point under the effect of a moving load.
  1583. Parameters
  1584. ==========
  1585. distance : Integer
  1586. Distance of the point from the start of the beam
  1587. for which equations are to be determined
  1588. value : Integer
  1589. Magnitude of moving load
  1590. reactions :
  1591. The reaction forces applied on the beam.
  1592. Examples
  1593. ========
  1594. There is a beam of length 12 meters. There are two simple supports
  1595. below the beam, one at the starting point and another at a distance
  1596. of 8 meters. Calculate the I.L.D. equations for Shear at a distance
  1597. of 4 meters under the effect of a moving load of magnitude 1kN.
  1598. .. image:: ildshear.png
  1599. Using the sign convention of downwards forces being positive.
  1600. .. plot::
  1601. :context: close-figs
  1602. :format: doctest
  1603. :include-source: True
  1604. >>> from sympy import symbols
  1605. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1606. >>> E, I = symbols('E, I')
  1607. >>> R_0, R_8 = symbols('R_0, R_8')
  1608. >>> b = Beam(12, E, I)
  1609. >>> b.apply_support(0, 'roller')
  1610. >>> b.apply_support(8, 'roller')
  1611. >>> b.solve_for_ild_reactions(1, R_0, R_8)
  1612. >>> b.solve_for_ild_shear(4, 1, R_0, R_8)
  1613. >>> b.ild_shear
  1614. Piecewise((x/8, x < 4), (x/8 - 1, x > 4))
  1615. """
  1616. x = self.variable
  1617. l = self.length
  1618. shear_force, _ = self._solve_for_ild_equations()
  1619. shear_curve1 = value - limit(shear_force, x, distance)
  1620. shear_curve2 = (limit(shear_force, x, l) - limit(shear_force, x, distance)) - value
  1621. for reaction in reactions:
  1622. shear_curve1 = shear_curve1.subs(reaction,self._ild_reactions[reaction])
  1623. shear_curve2 = shear_curve2.subs(reaction,self._ild_reactions[reaction])
  1624. shear_eq = Piecewise((shear_curve1, x < distance), (shear_curve2, x > distance))
  1625. self._ild_shear = shear_eq
  1626. def plot_ild_shear(self,subs=None):
  1627. """
  1628. Plots the Influence Line Diagram for Shear under the effect
  1629. of a moving load. This function should be called after
  1630. calling solve_for_ild_shear().
  1631. Parameters
  1632. ==========
  1633. subs : dictionary
  1634. Python dictionary containing Symbols as key and their
  1635. corresponding values.
  1636. Examples
  1637. ========
  1638. There is a beam of length 12 meters. There are two simple supports
  1639. below the beam, one at the starting point and another at a distance
  1640. of 8 meters. Plot the I.L.D. for Shear at a distance
  1641. of 4 meters under the effect of a moving load of magnitude 1kN.
  1642. .. image:: ildshear.png
  1643. Using the sign convention of downwards forces being positive.
  1644. .. plot::
  1645. :context: close-figs
  1646. :format: doctest
  1647. :include-source: True
  1648. >>> from sympy import symbols
  1649. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1650. >>> E, I = symbols('E, I')
  1651. >>> R_0, R_8 = symbols('R_0, R_8')
  1652. >>> b = Beam(12, E, I)
  1653. >>> b.apply_support(0, 'roller')
  1654. >>> b.apply_support(8, 'roller')
  1655. >>> b.solve_for_ild_reactions(1, R_0, R_8)
  1656. >>> b.solve_for_ild_shear(4, 1, R_0, R_8)
  1657. >>> b.ild_shear
  1658. Piecewise((x/8, x < 4), (x/8 - 1, x > 4))
  1659. >>> b.plot_ild_shear()
  1660. Plot object containing:
  1661. [0]: cartesian line: Piecewise((x/8, x < 4), (x/8 - 1, x > 4)) for x over (0.0, 12.0)
  1662. """
  1663. if not self._ild_shear:
  1664. raise ValueError("I.L.D. shear equation not found. Please use solve_for_ild_shear() to generate the I.L.D. shear equations.")
  1665. x = self.variable
  1666. l = self._length
  1667. if subs is None:
  1668. subs = {}
  1669. for sym in self._ild_shear.atoms(Symbol):
  1670. if sym != x and sym not in subs:
  1671. raise ValueError('Value of %s was not passed.' %sym)
  1672. for sym in self._length.atoms(Symbol):
  1673. if sym != x and sym not in subs:
  1674. raise ValueError('Value of %s was not passed.' %sym)
  1675. return plot(self._ild_shear.subs(subs), (x, 0, l), title='I.L.D. for Shear',
  1676. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{V}$', line_color='blue',show=True)
  1677. def solve_for_ild_moment(self, distance, value, *reactions):
  1678. """
  1679. Determines the Influence Line Diagram equations for moment at a
  1680. specified point under the effect of a moving load.
  1681. Parameters
  1682. ==========
  1683. distance : Integer
  1684. Distance of the point from the start of the beam
  1685. for which equations are to be determined
  1686. value : Integer
  1687. Magnitude of moving load
  1688. reactions :
  1689. The reaction forces applied on the beam.
  1690. Examples
  1691. ========
  1692. There is a beam of length 12 meters. There are two simple supports
  1693. below the beam, one at the starting point and another at a distance
  1694. of 8 meters. Calculate the I.L.D. equations for Moment at a distance
  1695. of 4 meters under the effect of a moving load of magnitude 1kN.
  1696. .. image:: ildshear.png
  1697. Using the sign convention of downwards forces being positive.
  1698. .. plot::
  1699. :context: close-figs
  1700. :format: doctest
  1701. :include-source: True
  1702. >>> from sympy import symbols
  1703. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1704. >>> E, I = symbols('E, I')
  1705. >>> R_0, R_8 = symbols('R_0, R_8')
  1706. >>> b = Beam(12, E, I)
  1707. >>> b.apply_support(0, 'roller')
  1708. >>> b.apply_support(8, 'roller')
  1709. >>> b.solve_for_ild_reactions(1, R_0, R_8)
  1710. >>> b.solve_for_ild_moment(4, 1, R_0, R_8)
  1711. >>> b.ild_moment
  1712. Piecewise((-x/2, x < 4), (x/2 - 4, x > 4))
  1713. """
  1714. x = self.variable
  1715. l = self.length
  1716. _, moment = self._solve_for_ild_equations()
  1717. moment_curve1 = value*(distance-x) - limit(moment, x, distance)
  1718. moment_curve2= (limit(moment, x, l)-limit(moment, x, distance))-value*(l-x)
  1719. for reaction in reactions:
  1720. moment_curve1 = moment_curve1.subs(reaction, self._ild_reactions[reaction])
  1721. moment_curve2 = moment_curve2.subs(reaction, self._ild_reactions[reaction])
  1722. moment_eq = Piecewise((moment_curve1, x < distance), (moment_curve2, x > distance))
  1723. self._ild_moment = moment_eq
  1724. def plot_ild_moment(self,subs=None):
  1725. """
  1726. Plots the Influence Line Diagram for Moment under the effect
  1727. of a moving load. This function should be called after
  1728. calling solve_for_ild_moment().
  1729. Parameters
  1730. ==========
  1731. subs : dictionary
  1732. Python dictionary containing Symbols as key and their
  1733. corresponding values.
  1734. Examples
  1735. ========
  1736. There is a beam of length 12 meters. There are two simple supports
  1737. below the beam, one at the starting point and another at a distance
  1738. of 8 meters. Plot the I.L.D. for Moment at a distance
  1739. of 4 meters under the effect of a moving load of magnitude 1kN.
  1740. .. image:: ildshear.png
  1741. Using the sign convention of downwards forces being positive.
  1742. .. plot::
  1743. :context: close-figs
  1744. :format: doctest
  1745. :include-source: True
  1746. >>> from sympy import symbols
  1747. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1748. >>> E, I = symbols('E, I')
  1749. >>> R_0, R_8 = symbols('R_0, R_8')
  1750. >>> b = Beam(12, E, I)
  1751. >>> b.apply_support(0, 'roller')
  1752. >>> b.apply_support(8, 'roller')
  1753. >>> b.solve_for_ild_reactions(1, R_0, R_8)
  1754. >>> b.solve_for_ild_moment(4, 1, R_0, R_8)
  1755. >>> b.ild_moment
  1756. Piecewise((-x/2, x < 4), (x/2 - 4, x > 4))
  1757. >>> b.plot_ild_moment()
  1758. Plot object containing:
  1759. [0]: cartesian line: Piecewise((-x/2, x < 4), (x/2 - 4, x > 4)) for x over (0.0, 12.0)
  1760. """
  1761. if not self._ild_moment:
  1762. raise ValueError("I.L.D. moment equation not found. Please use solve_for_ild_moment() to generate the I.L.D. moment equations.")
  1763. x = self.variable
  1764. if subs is None:
  1765. subs = {}
  1766. for sym in self._ild_moment.atoms(Symbol):
  1767. if sym != x and sym not in subs:
  1768. raise ValueError('Value of %s was not passed.' %sym)
  1769. for sym in self._length.atoms(Symbol):
  1770. if sym != x and sym not in subs:
  1771. raise ValueError('Value of %s was not passed.' %sym)
  1772. return plot(self._ild_moment.subs(subs), (x, 0, self._length), title='I.L.D. for Moment',
  1773. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{M}$', line_color='blue', show=True)
  1774. @doctest_depends_on(modules=('numpy',))
  1775. def draw(self, pictorial=True):
  1776. """
  1777. Returns a plot object representing the beam diagram of the beam.
  1778. .. note::
  1779. The user must be careful while entering load values.
  1780. The draw function assumes a sign convention which is used
  1781. for plotting loads.
  1782. Given a right handed coordinate system with XYZ coordinates,
  1783. the beam's length is assumed to be along the positive X axis.
  1784. The draw function recognizes positve loads(with n>-2) as loads
  1785. acting along negative Y direction and positve moments acting
  1786. along positive Z direction.
  1787. Parameters
  1788. ==========
  1789. pictorial: Boolean (default=True)
  1790. Setting ``pictorial=True`` would simply create a pictorial (scaled) view
  1791. of the beam diagram not with the exact dimensions.
  1792. Although setting ``pictorial=False`` would create a beam diagram with
  1793. the exact dimensions on the plot
  1794. Examples
  1795. ========
  1796. .. plot::
  1797. :context: close-figs
  1798. :format: doctest
  1799. :include-source: True
  1800. >>> from sympy.physics.continuum_mechanics.beam import Beam
  1801. >>> from sympy import symbols
  1802. >>> R1, R2 = symbols('R1, R2')
  1803. >>> E, I = symbols('E, I')
  1804. >>> b = Beam(50, 20, 30)
  1805. >>> b.apply_load(10, 2, -1)
  1806. >>> b.apply_load(R1, 10, -1)
  1807. >>> b.apply_load(R2, 30, -1)
  1808. >>> b.apply_load(90, 5, 0, 23)
  1809. >>> b.apply_load(10, 30, 1, 50)
  1810. >>> b.apply_support(50, "pin")
  1811. >>> b.apply_support(0, "fixed")
  1812. >>> b.apply_support(20, "roller")
  1813. >>> p = b.draw()
  1814. >>> p
  1815. Plot object containing:
  1816. [0]: cartesian line: 25*SingularityFunction(x, 5, 0) - 25*SingularityFunction(x, 23, 0)
  1817. + SingularityFunction(x, 30, 1) - 20*SingularityFunction(x, 50, 0)
  1818. - SingularityFunction(x, 50, 1) + 5 for x over (0.0, 50.0)
  1819. [1]: cartesian line: 5 for x over (0.0, 50.0)
  1820. >>> p.show()
  1821. """
  1822. if not numpy:
  1823. raise ImportError("To use this function numpy module is required")
  1824. x = self.variable
  1825. # checking whether length is an expression in terms of any Symbol.
  1826. if isinstance(self.length, Expr):
  1827. l = list(self.length.atoms(Symbol))
  1828. # assigning every Symbol a default value of 10
  1829. l = {i:10 for i in l}
  1830. length = self.length.subs(l)
  1831. else:
  1832. l = {}
  1833. length = self.length
  1834. height = length/10
  1835. rectangles = []
  1836. rectangles.append({'xy':(0, 0), 'width':length, 'height': height, 'facecolor':"brown"})
  1837. annotations, markers, load_eq,load_eq1, fill = self._draw_load(pictorial, length, l)
  1838. support_markers, support_rectangles = self._draw_supports(length, l)
  1839. rectangles += support_rectangles
  1840. markers += support_markers
  1841. sing_plot = plot(height + load_eq, height + load_eq1, (x, 0, length),
  1842. xlim=(-height, length + height), ylim=(-length, 1.25*length), annotations=annotations,
  1843. markers=markers, rectangles=rectangles, line_color='brown', fill=fill, axis=False, show=False)
  1844. return sing_plot
  1845. def _draw_load(self, pictorial, length, l):
  1846. loads = list(set(self.applied_loads) - set(self._support_as_loads))
  1847. height = length/10
  1848. x = self.variable
  1849. annotations = []
  1850. markers = []
  1851. load_args = []
  1852. scaled_load = 0
  1853. load_args1 = []
  1854. scaled_load1 = 0
  1855. load_eq = 0 # For positive valued higher order loads
  1856. load_eq1 = 0 # For negative valued higher order loads
  1857. fill = None
  1858. plus = 0 # For positive valued higher order loads
  1859. minus = 0 # For negative valued higher order loads
  1860. for load in loads:
  1861. # check if the position of load is in terms of the beam length.
  1862. if l:
  1863. pos = load[1].subs(l)
  1864. else:
  1865. pos = load[1]
  1866. # point loads
  1867. if load[2] == -1:
  1868. if isinstance(load[0], Symbol) or load[0].is_negative:
  1869. annotations.append({'text':'', 'xy':(pos, 0), 'xytext':(pos, height - 4*height), 'arrowprops':dict(width= 1.5, headlength=5, headwidth=5, facecolor='black')})
  1870. else:
  1871. annotations.append({'text':'', 'xy':(pos, height), 'xytext':(pos, height*4), 'arrowprops':dict(width= 1.5, headlength=4, headwidth=4, facecolor='black')})
  1872. # moment loads
  1873. elif load[2] == -2:
  1874. if load[0].is_negative:
  1875. markers.append({'args':[[pos], [height/2]], 'marker': r'$\circlearrowright$', 'markersize':15})
  1876. else:
  1877. markers.append({'args':[[pos], [height/2]], 'marker': r'$\circlearrowleft$', 'markersize':15})
  1878. # higher order loads
  1879. elif load[2] >= 0:
  1880. # `fill` will be assigned only when higher order loads are present
  1881. value, start, order, end = load
  1882. # Positive loads have their seperate equations
  1883. if(value>0):
  1884. plus = 1
  1885. # if pictorial is True we remake the load equation again with
  1886. # some constant magnitude values.
  1887. if pictorial:
  1888. value = 10**(1-order) if order > 0 else length/2
  1889. scaled_load += value*SingularityFunction(x, start, order)
  1890. if end:
  1891. f2 = 10**(1-order)*x**order if order > 0 else length/2*x**order
  1892. for i in range(0, order + 1):
  1893. scaled_load -= (f2.diff(x, i).subs(x, end - start)*
  1894. SingularityFunction(x, end, i)/factorial(i))
  1895. if pictorial:
  1896. if isinstance(scaled_load, Add):
  1897. load_args = scaled_load.args
  1898. else:
  1899. # when the load equation consists of only a single term
  1900. load_args = (scaled_load,)
  1901. load_eq = [i.subs(l) for i in load_args]
  1902. else:
  1903. if isinstance(self.load, Add):
  1904. load_args = self.load.args
  1905. else:
  1906. load_args = (self.load,)
  1907. load_eq = [i.subs(l) for i in load_args if list(i.atoms(SingularityFunction))[0].args[2] >= 0]
  1908. load_eq = Add(*load_eq)
  1909. # filling higher order loads with colour
  1910. expr = height + load_eq.rewrite(Piecewise)
  1911. y1 = lambdify(x, expr, 'numpy')
  1912. # For loads with negative value
  1913. else:
  1914. minus = 1
  1915. # if pictorial is True we remake the load equation again with
  1916. # some constant magnitude values.
  1917. if pictorial:
  1918. value = 10**(1-order) if order > 0 else length/2
  1919. scaled_load1 += value*SingularityFunction(x, start, order)
  1920. if end:
  1921. f2 = 10**(1-order)*x**order if order > 0 else length/2*x**order
  1922. for i in range(0, order + 1):
  1923. scaled_load1 -= (f2.diff(x, i).subs(x, end - start)*
  1924. SingularityFunction(x, end, i)/factorial(i))
  1925. if pictorial:
  1926. if isinstance(scaled_load1, Add):
  1927. load_args1 = scaled_load1.args
  1928. else:
  1929. # when the load equation consists of only a single term
  1930. load_args1 = (scaled_load1,)
  1931. load_eq1 = [i.subs(l) for i in load_args1]
  1932. else:
  1933. if isinstance(self.load, Add):
  1934. load_args1 = self.load.args1
  1935. else:
  1936. load_args1 = (self.load,)
  1937. load_eq1 = [i.subs(l) for i in load_args if list(i.atoms(SingularityFunction))[0].args[2] >= 0]
  1938. load_eq1 = -Add(*load_eq1)-height
  1939. # filling higher order loads with colour
  1940. expr = height + load_eq1.rewrite(Piecewise)
  1941. y1_ = lambdify(x, expr, 'numpy')
  1942. y = numpy.arange(0, float(length), 0.001)
  1943. y2 = float(height)
  1944. if(plus == 1 and minus == 1):
  1945. fill = {'x': y, 'y1': y1(y), 'y2': y1_(y), 'color':'darkkhaki'}
  1946. elif(plus == 1):
  1947. fill = {'x': y, 'y1': y1(y), 'y2': y2, 'color':'darkkhaki'}
  1948. else:
  1949. fill = {'x': y, 'y1': y1_(y), 'y2': y2, 'color':'darkkhaki'}
  1950. return annotations, markers, load_eq, load_eq1, fill
  1951. def _draw_supports(self, length, l):
  1952. height = float(length/10)
  1953. support_markers = []
  1954. support_rectangles = []
  1955. for support in self._applied_supports:
  1956. if l:
  1957. pos = support[0].subs(l)
  1958. else:
  1959. pos = support[0]
  1960. if support[1] == "pin":
  1961. support_markers.append({'args':[pos, [0]], 'marker':6, 'markersize':13, 'color':"black"})
  1962. elif support[1] == "roller":
  1963. support_markers.append({'args':[pos, [-height/2.5]], 'marker':'o', 'markersize':11, 'color':"black"})
  1964. elif support[1] == "fixed":
  1965. if pos == 0:
  1966. support_rectangles.append({'xy':(0, -3*height), 'width':-length/20, 'height':6*height + height, 'fill':False, 'hatch':'/////'})
  1967. else:
  1968. support_rectangles.append({'xy':(length, -3*height), 'width':length/20, 'height': 6*height + height, 'fill':False, 'hatch':'/////'})
  1969. return support_markers, support_rectangles
  1970. class Beam3D(Beam):
  1971. """
  1972. This class handles loads applied in any direction of a 3D space along
  1973. with unequal values of Second moment along different axes.
  1974. .. note::
  1975. A consistent sign convention must be used while solving a beam
  1976. bending problem; the results will
  1977. automatically follow the chosen sign convention.
  1978. This class assumes that any kind of distributed load/moment is
  1979. applied through out the span of a beam.
  1980. Examples
  1981. ========
  1982. There is a beam of l meters long. A constant distributed load of magnitude q
  1983. is applied along y-axis from start till the end of beam. A constant distributed
  1984. moment of magnitude m is also applied along z-axis from start till the end of beam.
  1985. Beam is fixed at both of its end. So, deflection of the beam at the both ends
  1986. is restricted.
  1987. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  1988. >>> from sympy import symbols, simplify, collect, factor
  1989. >>> l, E, G, I, A = symbols('l, E, G, I, A')
  1990. >>> b = Beam3D(l, E, G, I, A)
  1991. >>> x, q, m = symbols('x, q, m')
  1992. >>> b.apply_load(q, 0, 0, dir="y")
  1993. >>> b.apply_moment_load(m, 0, -1, dir="z")
  1994. >>> b.shear_force()
  1995. [0, -q*x, 0]
  1996. >>> b.bending_moment()
  1997. [0, 0, -m*x + q*x**2/2]
  1998. >>> b.bc_slope = [(0, [0, 0, 0]), (l, [0, 0, 0])]
  1999. >>> b.bc_deflection = [(0, [0, 0, 0]), (l, [0, 0, 0])]
  2000. >>> b.solve_slope_deflection()
  2001. >>> factor(b.slope())
  2002. [0, 0, x*(-l + x)*(-A*G*l**3*q + 2*A*G*l**2*q*x - 12*E*I*l*q
  2003. - 72*E*I*m + 24*E*I*q*x)/(12*E*I*(A*G*l**2 + 12*E*I))]
  2004. >>> dx, dy, dz = b.deflection()
  2005. >>> dy = collect(simplify(dy), x)
  2006. >>> dx == dz == 0
  2007. True
  2008. >>> dy == (x*(12*E*I*l*(A*G*l**2*q - 2*A*G*l*m + 12*E*I*q)
  2009. ... + x*(A*G*l*(3*l*(A*G*l**2*q - 2*A*G*l*m + 12*E*I*q) + x*(-2*A*G*l**2*q + 4*A*G*l*m - 24*E*I*q))
  2010. ... + A*G*(A*G*l**2 + 12*E*I)*(-2*l**2*q + 6*l*m - 4*m*x + q*x**2)
  2011. ... - 12*E*I*q*(A*G*l**2 + 12*E*I)))/(24*A*E*G*I*(A*G*l**2 + 12*E*I)))
  2012. True
  2013. References
  2014. ==========
  2015. .. [1] http://homes.civil.aau.dk/jc/FemteSemester/Beams3D.pdf
  2016. """
  2017. def __init__(self, length, elastic_modulus, shear_modulus, second_moment,
  2018. area, variable=Symbol('x')):
  2019. """Initializes the class.
  2020. Parameters
  2021. ==========
  2022. length : Sympifyable
  2023. A Symbol or value representing the Beam's length.
  2024. elastic_modulus : Sympifyable
  2025. A SymPy expression representing the Beam's Modulus of Elasticity.
  2026. It is a measure of the stiffness of the Beam material.
  2027. shear_modulus : Sympifyable
  2028. A SymPy expression representing the Beam's Modulus of rigidity.
  2029. It is a measure of rigidity of the Beam material.
  2030. second_moment : Sympifyable or list
  2031. A list of two elements having SymPy expression representing the
  2032. Beam's Second moment of area. First value represent Second moment
  2033. across y-axis and second across z-axis.
  2034. Single SymPy expression can be passed if both values are same
  2035. area : Sympifyable
  2036. A SymPy expression representing the Beam's cross-sectional area
  2037. in a plane prependicular to length of the Beam.
  2038. variable : Symbol, optional
  2039. A Symbol object that will be used as the variable along the beam
  2040. while representing the load, shear, moment, slope and deflection
  2041. curve. By default, it is set to ``Symbol('x')``.
  2042. """
  2043. super().__init__(length, elastic_modulus, second_moment, variable)
  2044. self.shear_modulus = shear_modulus
  2045. self._area = area
  2046. self._load_vector = [0, 0, 0]
  2047. self._moment_load_vector = [0, 0, 0]
  2048. self._load_Singularity = [0, 0, 0]
  2049. self._slope = [0, 0, 0]
  2050. self._deflection = [0, 0, 0]
  2051. @property
  2052. def shear_modulus(self):
  2053. """Young's Modulus of the Beam. """
  2054. return self._shear_modulus
  2055. @shear_modulus.setter
  2056. def shear_modulus(self, e):
  2057. self._shear_modulus = sympify(e)
  2058. @property
  2059. def second_moment(self):
  2060. """Second moment of area of the Beam. """
  2061. return self._second_moment
  2062. @second_moment.setter
  2063. def second_moment(self, i):
  2064. if isinstance(i, list):
  2065. i = [sympify(x) for x in i]
  2066. self._second_moment = i
  2067. else:
  2068. self._second_moment = sympify(i)
  2069. @property
  2070. def area(self):
  2071. """Cross-sectional area of the Beam. """
  2072. return self._area
  2073. @area.setter
  2074. def area(self, a):
  2075. self._area = sympify(a)
  2076. @property
  2077. def load_vector(self):
  2078. """
  2079. Returns a three element list representing the load vector.
  2080. """
  2081. return self._load_vector
  2082. @property
  2083. def moment_load_vector(self):
  2084. """
  2085. Returns a three element list representing moment loads on Beam.
  2086. """
  2087. return self._moment_load_vector
  2088. @property
  2089. def boundary_conditions(self):
  2090. """
  2091. Returns a dictionary of boundary conditions applied on the beam.
  2092. The dictionary has two keywords namely slope and deflection.
  2093. The value of each keyword is a list of tuple, where each tuple
  2094. contains location and value of a boundary condition in the format
  2095. (location, value). Further each value is a list corresponding to
  2096. slope or deflection(s) values along three axes at that location.
  2097. Examples
  2098. ========
  2099. There is a beam of length 4 meters. The slope at 0 should be 4 along
  2100. the x-axis and 0 along others. At the other end of beam, deflection
  2101. along all the three axes should be zero.
  2102. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2103. >>> from sympy import symbols
  2104. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2105. >>> b = Beam3D(30, E, G, I, A, x)
  2106. >>> b.bc_slope = [(0, (4, 0, 0))]
  2107. >>> b.bc_deflection = [(4, [0, 0, 0])]
  2108. >>> b.boundary_conditions
  2109. {'deflection': [(4, [0, 0, 0])], 'slope': [(0, (4, 0, 0))]}
  2110. Here the deflection of the beam should be ``0`` along all the three axes at ``4``.
  2111. Similarly, the slope of the beam should be ``4`` along x-axis and ``0``
  2112. along y and z axis at ``0``.
  2113. """
  2114. return self._boundary_conditions
  2115. def polar_moment(self):
  2116. """
  2117. Returns the polar moment of area of the beam
  2118. about the X axis with respect to the centroid.
  2119. Examples
  2120. ========
  2121. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2122. >>> from sympy import symbols
  2123. >>> l, E, G, I, A = symbols('l, E, G, I, A')
  2124. >>> b = Beam3D(l, E, G, I, A)
  2125. >>> b.polar_moment()
  2126. 2*I
  2127. >>> I1 = [9, 15]
  2128. >>> b = Beam3D(l, E, G, I1, A)
  2129. >>> b.polar_moment()
  2130. 24
  2131. """
  2132. if not iterable(self.second_moment):
  2133. return 2*self.second_moment
  2134. return sum(self.second_moment)
  2135. def apply_load(self, value, start, order, dir="y"):
  2136. """
  2137. This method adds up the force load to a particular beam object.
  2138. Parameters
  2139. ==========
  2140. value : Sympifyable
  2141. The magnitude of an applied load.
  2142. dir : String
  2143. Axis along which load is applied.
  2144. order : Integer
  2145. The order of the applied load.
  2146. - For point loads, order=-1
  2147. - For constant distributed load, order=0
  2148. - For ramp loads, order=1
  2149. - For parabolic ramp loads, order=2
  2150. - ... so on.
  2151. """
  2152. x = self.variable
  2153. value = sympify(value)
  2154. start = sympify(start)
  2155. order = sympify(order)
  2156. if dir == "x":
  2157. if not order == -1:
  2158. self._load_vector[0] += value
  2159. self._load_Singularity[0] += value*SingularityFunction(x, start, order)
  2160. elif dir == "y":
  2161. if not order == -1:
  2162. self._load_vector[1] += value
  2163. self._load_Singularity[1] += value*SingularityFunction(x, start, order)
  2164. else:
  2165. if not order == -1:
  2166. self._load_vector[2] += value
  2167. self._load_Singularity[2] += value*SingularityFunction(x, start, order)
  2168. def apply_moment_load(self, value, start, order, dir="y"):
  2169. """
  2170. This method adds up the moment loads to a particular beam object.
  2171. Parameters
  2172. ==========
  2173. value : Sympifyable
  2174. The magnitude of an applied moment.
  2175. dir : String
  2176. Axis along which moment is applied.
  2177. order : Integer
  2178. The order of the applied load.
  2179. - For point moments, order=-2
  2180. - For constant distributed moment, order=-1
  2181. - For ramp moments, order=0
  2182. - For parabolic ramp moments, order=1
  2183. - ... so on.
  2184. """
  2185. x = self.variable
  2186. value = sympify(value)
  2187. start = sympify(start)
  2188. order = sympify(order)
  2189. if dir == "x":
  2190. if not order == -2:
  2191. self._moment_load_vector[0] += value
  2192. self._load_Singularity[0] += value*SingularityFunction(x, start, order)
  2193. elif dir == "y":
  2194. if not order == -2:
  2195. self._moment_load_vector[1] += value
  2196. self._load_Singularity[0] += value*SingularityFunction(x, start, order)
  2197. else:
  2198. if not order == -2:
  2199. self._moment_load_vector[2] += value
  2200. self._load_Singularity[0] += value*SingularityFunction(x, start, order)
  2201. def apply_support(self, loc, type="fixed"):
  2202. if type in ("pin", "roller"):
  2203. reaction_load = Symbol('R_'+str(loc))
  2204. self._reaction_loads[reaction_load] = reaction_load
  2205. self.bc_deflection.append((loc, [0, 0, 0]))
  2206. else:
  2207. reaction_load = Symbol('R_'+str(loc))
  2208. reaction_moment = Symbol('M_'+str(loc))
  2209. self._reaction_loads[reaction_load] = [reaction_load, reaction_moment]
  2210. self.bc_deflection.append((loc, [0, 0, 0]))
  2211. self.bc_slope.append((loc, [0, 0, 0]))
  2212. def solve_for_reaction_loads(self, *reaction):
  2213. """
  2214. Solves for the reaction forces.
  2215. Examples
  2216. ========
  2217. There is a beam of length 30 meters. It it supported by rollers at
  2218. of its end. A constant distributed load of magnitude 8 N is applied
  2219. from start till its end along y-axis. Another linear load having
  2220. slope equal to 9 is applied along z-axis.
  2221. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2222. >>> from sympy import symbols
  2223. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2224. >>> b = Beam3D(30, E, G, I, A, x)
  2225. >>> b.apply_load(8, start=0, order=0, dir="y")
  2226. >>> b.apply_load(9*x, start=0, order=0, dir="z")
  2227. >>> b.bc_deflection = [(0, [0, 0, 0]), (30, [0, 0, 0])]
  2228. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2229. >>> b.apply_load(R1, start=0, order=-1, dir="y")
  2230. >>> b.apply_load(R2, start=30, order=-1, dir="y")
  2231. >>> b.apply_load(R3, start=0, order=-1, dir="z")
  2232. >>> b.apply_load(R4, start=30, order=-1, dir="z")
  2233. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2234. >>> b.reaction_loads
  2235. {R1: -120, R2: -120, R3: -1350, R4: -2700}
  2236. """
  2237. x = self.variable
  2238. l = self.length
  2239. q = self._load_Singularity
  2240. shear_curves = [integrate(load, x) for load in q]
  2241. moment_curves = [integrate(shear, x) for shear in shear_curves]
  2242. for i in range(3):
  2243. react = [r for r in reaction if (shear_curves[i].has(r) or moment_curves[i].has(r))]
  2244. if len(react) == 0:
  2245. continue
  2246. shear_curve = limit(shear_curves[i], x, l)
  2247. moment_curve = limit(moment_curves[i], x, l)
  2248. sol = list((linsolve([shear_curve, moment_curve], react).args)[0])
  2249. sol_dict = dict(zip(react, sol))
  2250. reaction_loads = self._reaction_loads
  2251. # Check if any of the evaluated rection exists in another direction
  2252. # and if it exists then it should have same value.
  2253. for key in sol_dict:
  2254. if key in reaction_loads and sol_dict[key] != reaction_loads[key]:
  2255. raise ValueError("Ambiguous solution for %s in different directions." % key)
  2256. self._reaction_loads.update(sol_dict)
  2257. def shear_force(self):
  2258. """
  2259. Returns a list of three expressions which represents the shear force
  2260. curve of the Beam object along all three axes.
  2261. """
  2262. x = self.variable
  2263. q = self._load_vector
  2264. return [integrate(-q[0], x), integrate(-q[1], x), integrate(-q[2], x)]
  2265. def axial_force(self):
  2266. """
  2267. Returns expression of Axial shear force present inside the Beam object.
  2268. """
  2269. return self.shear_force()[0]
  2270. def shear_stress(self):
  2271. """
  2272. Returns a list of three expressions which represents the shear stress
  2273. curve of the Beam object along all three axes.
  2274. """
  2275. return [self.shear_force()[0]/self._area, self.shear_force()[1]/self._area, self.shear_force()[2]/self._area]
  2276. def axial_stress(self):
  2277. """
  2278. Returns expression of Axial stress present inside the Beam object.
  2279. """
  2280. return self.axial_force()/self._area
  2281. def bending_moment(self):
  2282. """
  2283. Returns a list of three expressions which represents the bending moment
  2284. curve of the Beam object along all three axes.
  2285. """
  2286. x = self.variable
  2287. m = self._moment_load_vector
  2288. shear = self.shear_force()
  2289. return [integrate(-m[0], x), integrate(-m[1] + shear[2], x),
  2290. integrate(-m[2] - shear[1], x) ]
  2291. def torsional_moment(self):
  2292. """
  2293. Returns expression of Torsional moment present inside the Beam object.
  2294. """
  2295. return self.bending_moment()[0]
  2296. def solve_slope_deflection(self):
  2297. x = self.variable
  2298. l = self.length
  2299. E = self.elastic_modulus
  2300. G = self.shear_modulus
  2301. I = self.second_moment
  2302. if isinstance(I, list):
  2303. I_y, I_z = I[0], I[1]
  2304. else:
  2305. I_y = I_z = I
  2306. A = self._area
  2307. load = self._load_vector
  2308. moment = self._moment_load_vector
  2309. defl = Function('defl')
  2310. theta = Function('theta')
  2311. # Finding deflection along x-axis(and corresponding slope value by differentiating it)
  2312. # Equation used: Derivative(E*A*Derivative(def_x(x), x), x) + load_x = 0
  2313. eq = Derivative(E*A*Derivative(defl(x), x), x) + load[0]
  2314. def_x = dsolve(Eq(eq, 0), defl(x)).args[1]
  2315. # Solving constants originated from dsolve
  2316. C1 = Symbol('C1')
  2317. C2 = Symbol('C2')
  2318. constants = list((linsolve([def_x.subs(x, 0), def_x.subs(x, l)], C1, C2).args)[0])
  2319. def_x = def_x.subs({C1:constants[0], C2:constants[1]})
  2320. slope_x = def_x.diff(x)
  2321. self._deflection[0] = def_x
  2322. self._slope[0] = slope_x
  2323. # Finding deflection along y-axis and slope across z-axis. System of equation involved:
  2324. # 1: Derivative(E*I_z*Derivative(theta_z(x), x), x) + G*A*(Derivative(defl_y(x), x) - theta_z(x)) + moment_z = 0
  2325. # 2: Derivative(G*A*(Derivative(defl_y(x), x) - theta_z(x)), x) + load_y = 0
  2326. C_i = Symbol('C_i')
  2327. # Substitute value of `G*A*(Derivative(defl_y(x), x) - theta_z(x))` from (2) in (1)
  2328. eq1 = Derivative(E*I_z*Derivative(theta(x), x), x) + (integrate(-load[1], x) + C_i) + moment[2]
  2329. slope_z = dsolve(Eq(eq1, 0)).args[1]
  2330. # Solve for constants originated from using dsolve on eq1
  2331. constants = list((linsolve([slope_z.subs(x, 0), slope_z.subs(x, l)], C1, C2).args)[0])
  2332. slope_z = slope_z.subs({C1:constants[0], C2:constants[1]})
  2333. # Put value of slope obtained back in (2) to solve for `C_i` and find deflection across y-axis
  2334. eq2 = G*A*(Derivative(defl(x), x)) + load[1]*x - C_i - G*A*slope_z
  2335. def_y = dsolve(Eq(eq2, 0), defl(x)).args[1]
  2336. # Solve for constants originated from using dsolve on eq2
  2337. constants = list((linsolve([def_y.subs(x, 0), def_y.subs(x, l)], C1, C_i).args)[0])
  2338. self._deflection[1] = def_y.subs({C1:constants[0], C_i:constants[1]})
  2339. self._slope[2] = slope_z.subs(C_i, constants[1])
  2340. # Finding deflection along z-axis and slope across y-axis. System of equation involved:
  2341. # 1: Derivative(E*I_y*Derivative(theta_y(x), x), x) - G*A*(Derivative(defl_z(x), x) + theta_y(x)) + moment_y = 0
  2342. # 2: Derivative(G*A*(Derivative(defl_z(x), x) + theta_y(x)), x) + load_z = 0
  2343. # Substitute value of `G*A*(Derivative(defl_y(x), x) + theta_z(x))` from (2) in (1)
  2344. eq1 = Derivative(E*I_y*Derivative(theta(x), x), x) + (integrate(load[2], x) - C_i) + moment[1]
  2345. slope_y = dsolve(Eq(eq1, 0)).args[1]
  2346. # Solve for constants originated from using dsolve on eq1
  2347. constants = list((linsolve([slope_y.subs(x, 0), slope_y.subs(x, l)], C1, C2).args)[0])
  2348. slope_y = slope_y.subs({C1:constants[0], C2:constants[1]})
  2349. # Put value of slope obtained back in (2) to solve for `C_i` and find deflection across z-axis
  2350. eq2 = G*A*(Derivative(defl(x), x)) + load[2]*x - C_i + G*A*slope_y
  2351. def_z = dsolve(Eq(eq2,0)).args[1]
  2352. # Solve for constants originated from using dsolve on eq2
  2353. constants = list((linsolve([def_z.subs(x, 0), def_z.subs(x, l)], C1, C_i).args)[0])
  2354. self._deflection[2] = def_z.subs({C1:constants[0], C_i:constants[1]})
  2355. self._slope[1] = slope_y.subs(C_i, constants[1])
  2356. def slope(self):
  2357. """
  2358. Returns a three element list representing slope of deflection curve
  2359. along all the three axes.
  2360. """
  2361. return self._slope
  2362. def deflection(self):
  2363. """
  2364. Returns a three element list representing deflection curve along all
  2365. the three axes.
  2366. """
  2367. return self._deflection
  2368. def _plot_shear_force(self, dir, subs=None):
  2369. shear_force = self.shear_force()
  2370. if dir == 'x':
  2371. dir_num = 0
  2372. color = 'r'
  2373. elif dir == 'y':
  2374. dir_num = 1
  2375. color = 'g'
  2376. elif dir == 'z':
  2377. dir_num = 2
  2378. color = 'b'
  2379. if subs is None:
  2380. subs = {}
  2381. for sym in shear_force[dir_num].atoms(Symbol):
  2382. if sym != self.variable and sym not in subs:
  2383. raise ValueError('Value of %s was not passed.' %sym)
  2384. if self.length in subs:
  2385. length = subs[self.length]
  2386. else:
  2387. length = self.length
  2388. return plot(shear_force[dir_num].subs(subs), (self.variable, 0, length), show = False, title='Shear Force along %c direction'%dir,
  2389. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{V(%c)}$'%dir, line_color=color)
  2390. def plot_shear_force(self, dir="all", subs=None):
  2391. """
  2392. Returns a plot for Shear force along all three directions
  2393. present in the Beam object.
  2394. Parameters
  2395. ==========
  2396. dir : string (default : "all")
  2397. Direction along which shear force plot is required.
  2398. If no direction is specified, all plots are displayed.
  2399. subs : dictionary
  2400. Python dictionary containing Symbols as key and their
  2401. corresponding values.
  2402. Examples
  2403. ========
  2404. There is a beam of length 20 meters. It it supported by rollers
  2405. at of its end. A linear load having slope equal to 12 is applied
  2406. along y-axis. A constant distributed load of magnitude 15 N is
  2407. applied from start till its end along z-axis.
  2408. .. plot::
  2409. :context: close-figs
  2410. :format: doctest
  2411. :include-source: True
  2412. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2413. >>> from sympy import symbols
  2414. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2415. >>> b = Beam3D(20, E, G, I, A, x)
  2416. >>> b.apply_load(15, start=0, order=0, dir="z")
  2417. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2418. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2419. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2420. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2421. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2422. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2423. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2424. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2425. >>> b.plot_shear_force()
  2426. PlotGrid object containing:
  2427. Plot[0]:Plot object containing:
  2428. [0]: cartesian line: 0 for x over (0.0, 20.0)
  2429. Plot[1]:Plot object containing:
  2430. [0]: cartesian line: -6*x**2 for x over (0.0, 20.0)
  2431. Plot[2]:Plot object containing:
  2432. [0]: cartesian line: -15*x for x over (0.0, 20.0)
  2433. """
  2434. dir = dir.lower()
  2435. # For shear force along x direction
  2436. if dir == "x":
  2437. Px = self._plot_shear_force('x', subs)
  2438. return Px.show()
  2439. # For shear force along y direction
  2440. elif dir == "y":
  2441. Py = self._plot_shear_force('y', subs)
  2442. return Py.show()
  2443. # For shear force along z direction
  2444. elif dir == "z":
  2445. Pz = self._plot_shear_force('z', subs)
  2446. return Pz.show()
  2447. # For shear force along all direction
  2448. else:
  2449. Px = self._plot_shear_force('x', subs)
  2450. Py = self._plot_shear_force('y', subs)
  2451. Pz = self._plot_shear_force('z', subs)
  2452. return PlotGrid(3, 1, Px, Py, Pz)
  2453. def _plot_bending_moment(self, dir, subs=None):
  2454. bending_moment = self.bending_moment()
  2455. if dir == 'x':
  2456. dir_num = 0
  2457. color = 'g'
  2458. elif dir == 'y':
  2459. dir_num = 1
  2460. color = 'c'
  2461. elif dir == 'z':
  2462. dir_num = 2
  2463. color = 'm'
  2464. if subs is None:
  2465. subs = {}
  2466. for sym in bending_moment[dir_num].atoms(Symbol):
  2467. if sym != self.variable and sym not in subs:
  2468. raise ValueError('Value of %s was not passed.' %sym)
  2469. if self.length in subs:
  2470. length = subs[self.length]
  2471. else:
  2472. length = self.length
  2473. return plot(bending_moment[dir_num].subs(subs), (self.variable, 0, length), show = False, title='Bending Moment along %c direction'%dir,
  2474. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{M(%c)}$'%dir, line_color=color)
  2475. def plot_bending_moment(self, dir="all", subs=None):
  2476. """
  2477. Returns a plot for bending moment along all three directions
  2478. present in the Beam object.
  2479. Parameters
  2480. ==========
  2481. dir : string (default : "all")
  2482. Direction along which bending moment plot is required.
  2483. If no direction is specified, all plots are displayed.
  2484. subs : dictionary
  2485. Python dictionary containing Symbols as key and their
  2486. corresponding values.
  2487. Examples
  2488. ========
  2489. There is a beam of length 20 meters. It it supported by rollers
  2490. at of its end. A linear load having slope equal to 12 is applied
  2491. along y-axis. A constant distributed load of magnitude 15 N is
  2492. applied from start till its end along z-axis.
  2493. .. plot::
  2494. :context: close-figs
  2495. :format: doctest
  2496. :include-source: True
  2497. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2498. >>> from sympy import symbols
  2499. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2500. >>> b = Beam3D(20, E, G, I, A, x)
  2501. >>> b.apply_load(15, start=0, order=0, dir="z")
  2502. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2503. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2504. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2505. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2506. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2507. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2508. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2509. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2510. >>> b.plot_bending_moment()
  2511. PlotGrid object containing:
  2512. Plot[0]:Plot object containing:
  2513. [0]: cartesian line: 0 for x over (0.0, 20.0)
  2514. Plot[1]:Plot object containing:
  2515. [0]: cartesian line: -15*x**2/2 for x over (0.0, 20.0)
  2516. Plot[2]:Plot object containing:
  2517. [0]: cartesian line: 2*x**3 for x over (0.0, 20.0)
  2518. """
  2519. dir = dir.lower()
  2520. # For bending moment along x direction
  2521. if dir == "x":
  2522. Px = self._plot_bending_moment('x', subs)
  2523. return Px.show()
  2524. # For bending moment along y direction
  2525. elif dir == "y":
  2526. Py = self._plot_bending_moment('y', subs)
  2527. return Py.show()
  2528. # For bending moment along z direction
  2529. elif dir == "z":
  2530. Pz = self._plot_bending_moment('z', subs)
  2531. return Pz.show()
  2532. # For bending moment along all direction
  2533. else:
  2534. Px = self._plot_bending_moment('x', subs)
  2535. Py = self._plot_bending_moment('y', subs)
  2536. Pz = self._plot_bending_moment('z', subs)
  2537. return PlotGrid(3, 1, Px, Py, Pz)
  2538. def _plot_slope(self, dir, subs=None):
  2539. slope = self.slope()
  2540. if dir == 'x':
  2541. dir_num = 0
  2542. color = 'b'
  2543. elif dir == 'y':
  2544. dir_num = 1
  2545. color = 'm'
  2546. elif dir == 'z':
  2547. dir_num = 2
  2548. color = 'g'
  2549. if subs is None:
  2550. subs = {}
  2551. for sym in slope[dir_num].atoms(Symbol):
  2552. if sym != self.variable and sym not in subs:
  2553. raise ValueError('Value of %s was not passed.' %sym)
  2554. if self.length in subs:
  2555. length = subs[self.length]
  2556. else:
  2557. length = self.length
  2558. return plot(slope[dir_num].subs(subs), (self.variable, 0, length), show = False, title='Slope along %c direction'%dir,
  2559. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{\theta(%c)}$'%dir, line_color=color)
  2560. def plot_slope(self, dir="all", subs=None):
  2561. """
  2562. Returns a plot for Slope along all three directions
  2563. present in the Beam object.
  2564. Parameters
  2565. ==========
  2566. dir : string (default : "all")
  2567. Direction along which Slope plot is required.
  2568. If no direction is specified, all plots are displayed.
  2569. subs : dictionary
  2570. Python dictionary containing Symbols as keys and their
  2571. corresponding values.
  2572. Examples
  2573. ========
  2574. There is a beam of length 20 meters. It it supported by rollers
  2575. at of its end. A linear load having slope equal to 12 is applied
  2576. along y-axis. A constant distributed load of magnitude 15 N is
  2577. applied from start till its end along z-axis.
  2578. .. plot::
  2579. :context: close-figs
  2580. :format: doctest
  2581. :include-source: True
  2582. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2583. >>> from sympy import symbols
  2584. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2585. >>> b = Beam3D(20, 40, 21, 100, 25, x)
  2586. >>> b.apply_load(15, start=0, order=0, dir="z")
  2587. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2588. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2589. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2590. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2591. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2592. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2593. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2594. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2595. >>> b.solve_slope_deflection()
  2596. >>> b.plot_slope()
  2597. PlotGrid object containing:
  2598. Plot[0]:Plot object containing:
  2599. [0]: cartesian line: 0 for x over (0.0, 20.0)
  2600. Plot[1]:Plot object containing:
  2601. [0]: cartesian line: -x**3/1600 + 3*x**2/160 - x/8 for x over (0.0, 20.0)
  2602. Plot[2]:Plot object containing:
  2603. [0]: cartesian line: x**4/8000 - 19*x**2/172 + 52*x/43 for x over (0.0, 20.0)
  2604. """
  2605. dir = dir.lower()
  2606. # For Slope along x direction
  2607. if dir == "x":
  2608. Px = self._plot_slope('x', subs)
  2609. return Px.show()
  2610. # For Slope along y direction
  2611. elif dir == "y":
  2612. Py = self._plot_slope('y', subs)
  2613. return Py.show()
  2614. # For Slope along z direction
  2615. elif dir == "z":
  2616. Pz = self._plot_slope('z', subs)
  2617. return Pz.show()
  2618. # For Slope along all direction
  2619. else:
  2620. Px = self._plot_slope('x', subs)
  2621. Py = self._plot_slope('y', subs)
  2622. Pz = self._plot_slope('z', subs)
  2623. return PlotGrid(3, 1, Px, Py, Pz)
  2624. def _plot_deflection(self, dir, subs=None):
  2625. deflection = self.deflection()
  2626. if dir == 'x':
  2627. dir_num = 0
  2628. color = 'm'
  2629. elif dir == 'y':
  2630. dir_num = 1
  2631. color = 'r'
  2632. elif dir == 'z':
  2633. dir_num = 2
  2634. color = 'c'
  2635. if subs is None:
  2636. subs = {}
  2637. for sym in deflection[dir_num].atoms(Symbol):
  2638. if sym != self.variable and sym not in subs:
  2639. raise ValueError('Value of %s was not passed.' %sym)
  2640. if self.length in subs:
  2641. length = subs[self.length]
  2642. else:
  2643. length = self.length
  2644. return plot(deflection[dir_num].subs(subs), (self.variable, 0, length), show = False, title='Deflection along %c direction'%dir,
  2645. xlabel=r'$\mathrm{X}$', ylabel=r'$\mathrm{\delta(%c)}$'%dir, line_color=color)
  2646. def plot_deflection(self, dir="all", subs=None):
  2647. """
  2648. Returns a plot for Deflection along all three directions
  2649. present in the Beam object.
  2650. Parameters
  2651. ==========
  2652. dir : string (default : "all")
  2653. Direction along which deflection plot is required.
  2654. If no direction is specified, all plots are displayed.
  2655. subs : dictionary
  2656. Python dictionary containing Symbols as keys and their
  2657. corresponding values.
  2658. Examples
  2659. ========
  2660. There is a beam of length 20 meters. It it supported by rollers
  2661. at of its end. A linear load having slope equal to 12 is applied
  2662. along y-axis. A constant distributed load of magnitude 15 N is
  2663. applied from start till its end along z-axis.
  2664. .. plot::
  2665. :context: close-figs
  2666. :format: doctest
  2667. :include-source: True
  2668. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2669. >>> from sympy import symbols
  2670. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2671. >>> b = Beam3D(20, 40, 21, 100, 25, x)
  2672. >>> b.apply_load(15, start=0, order=0, dir="z")
  2673. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2674. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2675. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2676. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2677. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2678. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2679. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2680. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2681. >>> b.solve_slope_deflection()
  2682. >>> b.plot_deflection()
  2683. PlotGrid object containing:
  2684. Plot[0]:Plot object containing:
  2685. [0]: cartesian line: 0 for x over (0.0, 20.0)
  2686. Plot[1]:Plot object containing:
  2687. [0]: cartesian line: x**5/40000 - 4013*x**3/90300 + 26*x**2/43 + 1520*x/903 for x over (0.0, 20.0)
  2688. Plot[2]:Plot object containing:
  2689. [0]: cartesian line: x**4/6400 - x**3/160 + 27*x**2/560 + 2*x/7 for x over (0.0, 20.0)
  2690. """
  2691. dir = dir.lower()
  2692. # For deflection along x direction
  2693. if dir == "x":
  2694. Px = self._plot_deflection('x', subs)
  2695. return Px.show()
  2696. # For deflection along y direction
  2697. elif dir == "y":
  2698. Py = self._plot_deflection('y', subs)
  2699. return Py.show()
  2700. # For deflection along z direction
  2701. elif dir == "z":
  2702. Pz = self._plot_deflection('z', subs)
  2703. return Pz.show()
  2704. # For deflection along all direction
  2705. else:
  2706. Px = self._plot_deflection('x', subs)
  2707. Py = self._plot_deflection('y', subs)
  2708. Pz = self._plot_deflection('z', subs)
  2709. return PlotGrid(3, 1, Px, Py, Pz)
  2710. def plot_loading_results(self, dir='x', subs=None):
  2711. """
  2712. Returns a subplot of Shear Force, Bending Moment,
  2713. Slope and Deflection of the Beam object along the direction specified.
  2714. Parameters
  2715. ==========
  2716. dir : string (default : "x")
  2717. Direction along which plots are required.
  2718. If no direction is specified, plots along x-axis are displayed.
  2719. subs : dictionary
  2720. Python dictionary containing Symbols as key and their
  2721. corresponding values.
  2722. Examples
  2723. ========
  2724. There is a beam of length 20 meters. It it supported by rollers
  2725. at of its end. A linear load having slope equal to 12 is applied
  2726. along y-axis. A constant distributed load of magnitude 15 N is
  2727. applied from start till its end along z-axis.
  2728. .. plot::
  2729. :context: close-figs
  2730. :format: doctest
  2731. :include-source: True
  2732. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2733. >>> from sympy import symbols
  2734. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2735. >>> b = Beam3D(20, E, G, I, A, x)
  2736. >>> subs = {E:40, G:21, I:100, A:25}
  2737. >>> b.apply_load(15, start=0, order=0, dir="z")
  2738. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2739. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2740. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2741. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2742. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2743. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2744. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2745. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2746. >>> b.solve_slope_deflection()
  2747. >>> b.plot_loading_results('y',subs)
  2748. PlotGrid object containing:
  2749. Plot[0]:Plot object containing:
  2750. [0]: cartesian line: -6*x**2 for x over (0.0, 20.0)
  2751. Plot[1]:Plot object containing:
  2752. [0]: cartesian line: -15*x**2/2 for x over (0.0, 20.0)
  2753. Plot[2]:Plot object containing:
  2754. [0]: cartesian line: -x**3/1600 + 3*x**2/160 - x/8 for x over (0.0, 20.0)
  2755. Plot[3]:Plot object containing:
  2756. [0]: cartesian line: x**5/40000 - 4013*x**3/90300 + 26*x**2/43 + 1520*x/903 for x over (0.0, 20.0)
  2757. """
  2758. dir = dir.lower()
  2759. if subs is None:
  2760. subs = {}
  2761. ax1 = self._plot_shear_force(dir, subs)
  2762. ax2 = self._plot_bending_moment(dir, subs)
  2763. ax3 = self._plot_slope(dir, subs)
  2764. ax4 = self._plot_deflection(dir, subs)
  2765. return PlotGrid(4, 1, ax1, ax2, ax3, ax4)
  2766. def _plot_shear_stress(self, dir, subs=None):
  2767. shear_stress = self.shear_stress()
  2768. if dir == 'x':
  2769. dir_num = 0
  2770. color = 'r'
  2771. elif dir == 'y':
  2772. dir_num = 1
  2773. color = 'g'
  2774. elif dir == 'z':
  2775. dir_num = 2
  2776. color = 'b'
  2777. if subs is None:
  2778. subs = {}
  2779. for sym in shear_stress[dir_num].atoms(Symbol):
  2780. if sym != self.variable and sym not in subs:
  2781. raise ValueError('Value of %s was not passed.' %sym)
  2782. if self.length in subs:
  2783. length = subs[self.length]
  2784. else:
  2785. length = self.length
  2786. return plot(shear_stress[dir_num].subs(subs), (self.variable, 0, length), show = False, title='Shear stress along %c direction'%dir,
  2787. xlabel=r'$\mathrm{X}$', ylabel=r'$\tau(%c)$'%dir, line_color=color)
  2788. def plot_shear_stress(self, dir="all", subs=None):
  2789. """
  2790. Returns a plot for Shear Stress along all three directions
  2791. present in the Beam object.
  2792. Parameters
  2793. ==========
  2794. dir : string (default : "all")
  2795. Direction along which shear stress plot is required.
  2796. If no direction is specified, all plots are displayed.
  2797. subs : dictionary
  2798. Python dictionary containing Symbols as key and their
  2799. corresponding values.
  2800. Examples
  2801. ========
  2802. There is a beam of length 20 meters and area of cross section 2 square
  2803. meters. It it supported by rollers at of its end. A linear load having
  2804. slope equal to 12 is applied along y-axis. A constant distributed load
  2805. of magnitude 15 N is applied from start till its end along z-axis.
  2806. .. plot::
  2807. :context: close-figs
  2808. :format: doctest
  2809. :include-source: True
  2810. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2811. >>> from sympy import symbols
  2812. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2813. >>> b = Beam3D(20, E, G, I, 2, x)
  2814. >>> b.apply_load(15, start=0, order=0, dir="z")
  2815. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2816. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2817. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2818. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2819. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2820. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2821. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2822. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2823. >>> b.plot_shear_stress()
  2824. PlotGrid object containing:
  2825. Plot[0]:Plot object containing:
  2826. [0]: cartesian line: 0 for x over (0.0, 20.0)
  2827. Plot[1]:Plot object containing:
  2828. [0]: cartesian line: -3*x**2 for x over (0.0, 20.0)
  2829. Plot[2]:Plot object containing:
  2830. [0]: cartesian line: -15*x/2 for x over (0.0, 20.0)
  2831. """
  2832. dir = dir.lower()
  2833. # For shear stress along x direction
  2834. if dir == "x":
  2835. Px = self._plot_shear_stress('x', subs)
  2836. return Px.show()
  2837. # For shear stress along y direction
  2838. elif dir == "y":
  2839. Py = self._plot_shear_stress('y', subs)
  2840. return Py.show()
  2841. # For shear stress along z direction
  2842. elif dir == "z":
  2843. Pz = self._plot_shear_stress('z', subs)
  2844. return Pz.show()
  2845. # For shear stress along all direction
  2846. else:
  2847. Px = self._plot_shear_stress('x', subs)
  2848. Py = self._plot_shear_stress('y', subs)
  2849. Pz = self._plot_shear_stress('z', subs)
  2850. return PlotGrid(3, 1, Px, Py, Pz)
  2851. def _max_shear_force(self, dir):
  2852. """
  2853. Helper function for max_shear_force().
  2854. """
  2855. dir = dir.lower()
  2856. if dir == 'x':
  2857. dir_num = 0
  2858. elif dir == 'y':
  2859. dir_num = 1
  2860. elif dir == 'z':
  2861. dir_num = 2
  2862. if not self.shear_force()[dir_num]:
  2863. return (0,0)
  2864. # To restrict the range within length of the Beam
  2865. load_curve = Piecewise((float("nan"), self.variable<=0),
  2866. (self._load_vector[dir_num], self.variable<self.length),
  2867. (float("nan"), True))
  2868. points = solve(load_curve.rewrite(Piecewise), self.variable,
  2869. domain=S.Reals)
  2870. points.append(0)
  2871. points.append(self.length)
  2872. shear_curve = self.shear_force()[dir_num]
  2873. shear_values = [shear_curve.subs(self.variable, x) for x in points]
  2874. shear_values = list(map(abs, shear_values))
  2875. max_shear = max(shear_values)
  2876. return (points[shear_values.index(max_shear)], max_shear)
  2877. def max_shear_force(self):
  2878. """
  2879. Returns point of max shear force and its corresponding shear value
  2880. along all directions in a Beam object as a list.
  2881. solve_for_reaction_loads() must be called before using this function.
  2882. Examples
  2883. ========
  2884. There is a beam of length 20 meters. It it supported by rollers
  2885. at of its end. A linear load having slope equal to 12 is applied
  2886. along y-axis. A constant distributed load of magnitude 15 N is
  2887. applied from start till its end along z-axis.
  2888. .. plot::
  2889. :context: close-figs
  2890. :format: doctest
  2891. :include-source: True
  2892. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2893. >>> from sympy import symbols
  2894. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2895. >>> b = Beam3D(20, 40, 21, 100, 25, x)
  2896. >>> b.apply_load(15, start=0, order=0, dir="z")
  2897. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2898. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2899. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2900. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2901. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2902. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2903. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2904. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2905. >>> b.max_shear_force()
  2906. [(0, 0), (20, 2400), (20, 300)]
  2907. """
  2908. max_shear = []
  2909. max_shear.append(self._max_shear_force('x'))
  2910. max_shear.append(self._max_shear_force('y'))
  2911. max_shear.append(self._max_shear_force('z'))
  2912. return max_shear
  2913. def _max_bending_moment(self, dir):
  2914. """
  2915. Helper function for max_bending_moment().
  2916. """
  2917. dir = dir.lower()
  2918. if dir == 'x':
  2919. dir_num = 0
  2920. elif dir == 'y':
  2921. dir_num = 1
  2922. elif dir == 'z':
  2923. dir_num = 2
  2924. if not self.bending_moment()[dir_num]:
  2925. return (0,0)
  2926. # To restrict the range within length of the Beam
  2927. shear_curve = Piecewise((float("nan"), self.variable<=0),
  2928. (self.shear_force()[dir_num], self.variable<self.length),
  2929. (float("nan"), True))
  2930. points = solve(shear_curve.rewrite(Piecewise), self.variable,
  2931. domain=S.Reals)
  2932. points.append(0)
  2933. points.append(self.length)
  2934. bending_moment_curve = self.bending_moment()[dir_num]
  2935. bending_moments = [bending_moment_curve.subs(self.variable, x) for x in points]
  2936. bending_moments = list(map(abs, bending_moments))
  2937. max_bending_moment = max(bending_moments)
  2938. return (points[bending_moments.index(max_bending_moment)], max_bending_moment)
  2939. def max_bending_moment(self):
  2940. """
  2941. Returns point of max bending moment and its corresponding bending moment value
  2942. along all directions in a Beam object as a list.
  2943. solve_for_reaction_loads() must be called before using this function.
  2944. Examples
  2945. ========
  2946. There is a beam of length 20 meters. It it supported by rollers
  2947. at of its end. A linear load having slope equal to 12 is applied
  2948. along y-axis. A constant distributed load of magnitude 15 N is
  2949. applied from start till its end along z-axis.
  2950. .. plot::
  2951. :context: close-figs
  2952. :format: doctest
  2953. :include-source: True
  2954. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  2955. >>> from sympy import symbols
  2956. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  2957. >>> b = Beam3D(20, 40, 21, 100, 25, x)
  2958. >>> b.apply_load(15, start=0, order=0, dir="z")
  2959. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  2960. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  2961. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  2962. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  2963. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  2964. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  2965. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  2966. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  2967. >>> b.max_bending_moment()
  2968. [(0, 0), (20, 3000), (20, 16000)]
  2969. """
  2970. max_bmoment = []
  2971. max_bmoment.append(self._max_bending_moment('x'))
  2972. max_bmoment.append(self._max_bending_moment('y'))
  2973. max_bmoment.append(self._max_bending_moment('z'))
  2974. return max_bmoment
  2975. max_bmoment = max_bending_moment
  2976. def _max_deflection(self, dir):
  2977. """
  2978. Helper function for max_Deflection()
  2979. """
  2980. dir = dir.lower()
  2981. if dir == 'x':
  2982. dir_num = 0
  2983. elif dir == 'y':
  2984. dir_num = 1
  2985. elif dir == 'z':
  2986. dir_num = 2
  2987. if not self.deflection()[dir_num]:
  2988. return (0,0)
  2989. # To restrict the range within length of the Beam
  2990. slope_curve = Piecewise((float("nan"), self.variable<=0),
  2991. (self.slope()[dir_num], self.variable<self.length),
  2992. (float("nan"), True))
  2993. points = solve(slope_curve.rewrite(Piecewise), self.variable,
  2994. domain=S.Reals)
  2995. points.append(0)
  2996. points.append(self._length)
  2997. deflection_curve = self.deflection()[dir_num]
  2998. deflections = [deflection_curve.subs(self.variable, x) for x in points]
  2999. deflections = list(map(abs, deflections))
  3000. max_def = max(deflections)
  3001. return (points[deflections.index(max_def)], max_def)
  3002. def max_deflection(self):
  3003. """
  3004. Returns point of max deflection and its corresponding deflection value
  3005. along all directions in a Beam object as a list.
  3006. solve_for_reaction_loads() and solve_slope_deflection() must be called
  3007. before using this function.
  3008. Examples
  3009. ========
  3010. There is a beam of length 20 meters. It it supported by rollers
  3011. at of its end. A linear load having slope equal to 12 is applied
  3012. along y-axis. A constant distributed load of magnitude 15 N is
  3013. applied from start till its end along z-axis.
  3014. .. plot::
  3015. :context: close-figs
  3016. :format: doctest
  3017. :include-source: True
  3018. >>> from sympy.physics.continuum_mechanics.beam import Beam3D
  3019. >>> from sympy import symbols
  3020. >>> l, E, G, I, A, x = symbols('l, E, G, I, A, x')
  3021. >>> b = Beam3D(20, 40, 21, 100, 25, x)
  3022. >>> b.apply_load(15, start=0, order=0, dir="z")
  3023. >>> b.apply_load(12*x, start=0, order=0, dir="y")
  3024. >>> b.bc_deflection = [(0, [0, 0, 0]), (20, [0, 0, 0])]
  3025. >>> R1, R2, R3, R4 = symbols('R1, R2, R3, R4')
  3026. >>> b.apply_load(R1, start=0, order=-1, dir="z")
  3027. >>> b.apply_load(R2, start=20, order=-1, dir="z")
  3028. >>> b.apply_load(R3, start=0, order=-1, dir="y")
  3029. >>> b.apply_load(R4, start=20, order=-1, dir="y")
  3030. >>> b.solve_for_reaction_loads(R1, R2, R3, R4)
  3031. >>> b.solve_slope_deflection()
  3032. >>> b.max_deflection()
  3033. [(0, 0), (10, 495/14), (-10 + 10*sqrt(10793)/43, (10 - 10*sqrt(10793)/43)**3/160 - 20/7 + (10 - 10*sqrt(10793)/43)**4/6400 + 20*sqrt(10793)/301 + 27*(10 - 10*sqrt(10793)/43)**2/560)]
  3034. """
  3035. max_def = []
  3036. max_def.append(self._max_deflection('x'))
  3037. max_def.append(self._max_deflection('y'))
  3038. max_def.append(self._max_deflection('z'))
  3039. return max_def