beam.py 156 KB

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