beam.py 145 KB

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