K idZddlmZmZddlmZddlmZddlm Z ddl m Z ddl m Z ddlmZmZdd lmZdd lmZdd lmZdd lZdd lmZddlmZddlmZddlmZmZddgiZ edddgiZ!GddZ"y )zB This module can be used to solve problems related to 2D Trusses. )ataninf)Add)INF)Mul)Symbol)sympify)Matrixpi) import_module)sqrt)zerosN)Quantity)plot)doctest_depends_on)sincosz Truss.draw matplotlibnumpyfromlistarange) import_kwargsc eZdZdZdZedZedZedZedZ edZ edZ ed Z ed Z ed Zd Zd ZdZdZdZdZdZdZdZdZdZedddZdZdZdZdZy) TrussaZ A Truss is an assembly of members such as beams, connected by nodes, that create a rigid structure. In engineering, a truss is a structure that consists of two-force members only. Trusses are extremely important in engineering applications and can be seen in numerous real-world applications like bridges. Examples ======== There is a Truss consisting of four nodes and five members connecting the nodes. A force P acts downward on the node D and there also exist pinned and roller joints on the nodes A and B respectively. .. image:: truss_example.png >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(("node_1", 0, 0), ("node_2", 6, 0), ("node_3", 2, 2), ("node_4", 2, 0)) >>> t.add_member(("member_1", "node_1", "node_4"), ("member_2", "node_2", "node_4"), ("member_3", "node_1", "node_3")) >>> t.add_member(("member_4", "node_2", "node_3"), ("member_5", "node_3", "node_4")) >>> t.apply_load(("node_4", 10, 270)) >>> t.apply_support(("node_1", "pinned"), ("node_2", "roller")) cg|_i|_i|_i|_g|_g|_g|_g|_i|_i|_ i|_ i|_ i|_ y)z' Initializes the class N) _nodes_members_loads _supports _node_labels_node_positions_node_position_x_node_position_y_nodes_occupied_member_lengths_reaction_loads_internal_forces_node_coordinatesselfs m/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/physics/continuum_mechanics/truss.py__init__zTruss.__init__;sh   ! " "!!! "!#c|jS)zL Returns the nodes of the truss along with their positions. )rr)s r+nodesz Truss.nodesM {{r-c|jS)z7 Returns the node labels of the truss. )r r)s r+ node_labelszTruss.node_labelsTs    r-c|jS)zB Returns the positions of the nodes of the truss. )r!r)s r+node_positionszTruss.node_positions[ ###r-c|jS)zW Returns the members of the truss along with the start and end points. )rr)s r+membersz Truss.membersbs }}r-c|jS)zA Returns the length of each member of the truss. )r%r)s r+member_lengthszTruss.member_lengthsir5r-c|jS)z Returns the nodes with provided supports along with the kind of support provided i.e. pinned or roller. )rr)s r+supportszTruss.supportsps ~~r-c|jS)z8 Returns the loads acting on the truss. )rr)s r+loadsz Truss.loadsxr0r-c|jS)z^ Returns the reaction forces for all supports which are all initialized to 0. )r&r)s r+reaction_loadszTruss.reaction_loadsr5r-c|jS)z] Returns the internal forces for all members which are all initialized to 0. )r'r)s r+internal_forceszTruss.internal_forcess $$$r-c|D]}|d}|d}t|}|d}t|}||jvr td||g|jjvr td|jj |||f|j j ||jj ||f|jj ||jj |||g|j|< y)ak This method adds a node to the truss along with its name/label and its location. Multiple nodes can be added at the same time. Parameters ========== The input(s) for this method are tuples of the form (label, x, y). label: String or a Symbol The label for a node. It is the only way to identify a particular node. x: Sympifyable The x-coordinate of the position of the node. y: Sympifyable The y-coordinate of the position of the node. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0)) >>> t.nodes [('A', 0, 0)] >>> t.add_node(('B', 3, 0), ('C', 4, 1)) >>> t.nodes [('A', 0, 0), ('B', 3, 0), ('C', 4, 1)] rz!Node needs to have a unique labelz+A node already exists at the given positionN) r r( ValueErrorvaluesrappendr r!r"r#)r*argsilabelxys r+add_nodezTruss.add_nodes> 7AaDE!A AdA A... !DEEQ41188:: !NOO ""E1a=1!!((/$$++QF3%%,,Q/%%,,Q/12A&&u-% 7r-c|D]}tt|jD]3}|j||k(s|j|}|j |}5||j vr td|jj}|D]7}||j|dk(s||j|dk(s.td|jj|f|jj||jj||f|jj||j j|||jvr|jj|||jvr|jj||j j|y)a This method removes a node from the truss. Multiple nodes can be removed at the same time. Parameters ========== The input(s) for this method are the labels of the nodes to be removed. label: String or Symbol The label of the node to be removed. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0), ('C', 5, 0)) >>> t.nodes [('A', 0, 0), ('B', 3, 0), ('C', 5, 0)] >>> t.remove_node('A', 'C') >>> t.nodes [('B', 3, 0)] z No such node exists in the trussrrCz0The given node already has member attached to itN)rangelenr/r r"r#r(rErcopyrremover!rpopr)r*rHrJrIrKrLmembers_duplicatemembers r+ remove_nodezTruss.remove_nodes0 2E3tzz?+ 1$$Q'50--a0A--a0A 1 D222 !CDD%)MM$6$6$8!/]F f 5a 88ET]]SYEZ[\E]<]()[\\] ""E1a=1!!((/$$++QF3%%,,Q/%%,,Q/DKK'KKOOE*DNN*NN&&u-&&**51/ 2r-c`|D](}|d}|d}|d}||jvs||jvs||k(r td||jvr td|jj ||fr td||g|j|<t |j|d|j|dz dz|j|d|j|dz dzz|j |<d|j||f<d|j||f<d|j|<+y) aD This method adds a member between any two nodes in the given truss. Parameters ========== The input(s) of the method are tuple(s) of the form (label, start, end). label: String or Symbol The label for a member. It is the only way to identify a particular member. start: String or Symbol The label of the starting point/node of the member. end: String or Symbol The label of the ending point/node of the member. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0), ('C', 2, 2)) >>> t.add_member(('AB', 'A', 'B'), ('BC', 'B', 'C')) >>> t.members {'AB': ['A', 'B'], 'BC': ['B', 'C']} rrCrDz;The start and end points of the member must be unique nodesz9A member with the same label already exists for the trussz-A member already exists between the two nodesTN)r(rErr$getr r%r')r*rHrIrJstartends r+ add_memberzTruss.add_members6 1AaDEaDEA$CD222cAWAW6W[`be[e !^__$--' !\]]%%))5#,7 !PQQ).s| e$.2D4J4J34OPQ4RSWSiSijoSpqrSs4svw3w{|R|RSV|WXY|Z[_[q[qrw[xyz[{|{~{4/@$$U+37$$UCZ037$$S%Z0/0%%e,' 1r-c|D]}||jvr td|jj|j|d|j|df|jj|j|d|j|df|jj||jj||j j|y)a| This method removes members from the given truss. Parameters ========== labels: String or Symbol The label for the member to be removed. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0), ('C', 2, 2)) >>> t.add_member(('AB', 'A', 'B'), ('AC', 'A', 'C'), ('BC', 'B', 'C')) >>> t.members {'AB': ['A', 'B'], 'AC': ['A', 'C'], 'BC': ['B', 'C']} >>> t.remove_member('AC', 'BC') >>> t.members {'AB': ['A', 'B']} z"No such member exists in the TrussrrCN)rrEr$rSr%r')r*rHrJs r+ remove_memberzTruss.remove_member%s, 1EDMM) !EFF$$(($--*>q*A4==QVCWXYCZ)[\$$(($--*>q*A4==QVCWXYCZ)[\ !!%($$((/%%))%0 1r-c . |D]}|d}|d}||jvr td||jvr td|jD]@}|d|k(s ||d|df|j|jj||d|df<||j|jj|d<|j||j|<|jj ||d|j vr=|j |d|j |<|j j |d||j vrU|j |dk(rcdt|zdz|jvrdt|zd z|jvr|jdt|zdz|jdt|zdz<|jdt|zd z|jdt|zd z<|jj dt|zdz|jj dt|zd z|j||j|<|j|D]W}|dd k(s |dxxtdt|zd zzcc<|ddk(r|j|j|n|j|D]W}|ddk(s |dxxtdt|zdzzcc<|ddk(r|j|j|n|j|tdt|zdzd|j|tdt|zd zd |jj |n$|j |d k(r|j||j|<|j|D]W}|dd k(s |dxxtdt|zd zzcc<|ddk(r|j|j|n|j|tdt|zd zd |jj |nE||jvr7|j||j|<|jj ||jD]}|j|d|dk(r||j|d<d |j||j|df<d |j|j|d|f<|jj ||j|df|jj |j|d|f|j|d|dk(s||j|d<d |j|j|d|f<d |j||j|df<|jj |j|d|f|jj ||j|dfCy )a This method changes the label(s) of the specified node(s). Parameters ========== The input(s) of this method are tuple(s) of the form (label, new_label). label: String or Symbol The label of the node for which the label has to be changed. new_label: String or Symbol The new label of the node. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0)) >>> t.nodes [('A', 0, 0), ('B', 3, 0)] >>> t.change_node_label(('A', 'C'), ('B', 'D')) >>> t.nodes [('C', 0, 0), ('D', 3, 0)] rrCz!No such node exists for the Trussz*A node with the given label already existsrDpinnedR__x_yZrollerTN)r(rErindexr rSrstrr&rrrR apply_loadrr$)r*rHrIrJ new_labelnodeloadrUs r+change_node_labelzTruss.change_node_labelFsU6B \AaDE!ID222 !DEEd444 !MNN KK:\DAw%'U^`def`gimnoipTq DKK$5$5ud1gtAw6O$PQNW))$*;*;*A*A$q'*JK<@,E,Ed,K(- !. -1KK ,B!.D'+Aw!|(,Q6$s5z/$:N3O(O+/7a<,0KK,>,E,Ed,K(- !. !% 6$s9~BUVZBZ;[]^ _ $ 6$s9~BUVZBZ;[]_ ` $  6!% !:h!F9=U9K I 6,0KK,>!.D'+Aw"}(,Q6$s5z/$:N3O(O+/7a<,0KK,>,E,Ed,K(- !. !% 6$s9~BUVZBZ;[]_ ` $  6$ 39=U9K I 6 $  6&*mm \F#}}V4Q747B;D f 5a 8^b 4 4ivAVWXAY5Z [^b 4 4dmmF6KA6NPY5Z [ $ 4 4 8 8%vAVWXAY9Z [ $ 4 4 8 8$--:OPQ:RTY9Z [!%v!6q!9T!W!D;D f 5a 8^b 4 4dmmF6KA6NPY5Z [^b 4 4ivAVWXAY5Z [ $ 4 4 8 8$--:OPQ:RTY9Z [ $ 4 4 8 8%vAVWXAY9Z [ \]:\B \r-c0|D]}|d}|d}||jvr tdt|jj}|D]}||k(s |j|d|j|dg|j|<|jj ||j ||j |<|j j ||j ||j |<|j j |y)a This method changes the label(s) of the specified member(s). Parameters ========== The input(s) of this method are tuple(s) of the form (label, new_label) label: String or Symbol The label of the member for which the label has to be changed. new_label: String or Symbol The new label of the member. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0), ('D', 5, 0)) >>> t.nodes [('A', 0, 0), ('B', 3, 0), ('D', 5, 0)] >>> t.change_node_label(('A', 'C')) >>> t.nodes [('C', 0, 0), ('B', 3, 0), ('D', 5, 0)] >>> t.add_member(('BC', 'B', 'C'), ('BD', 'B', 'D')) >>> t.members {'BC': ['B', 'C'], 'BD': ['B', 'D']} >>> t.change_member_label(('BC', 'BC_new'), ('BD', 'BD_new')) >>> t.members {'BC_new': ['B', 'C'], 'BD_new': ['B', 'D']} rrCz#No such member exists for the TrussN)rrElistrQrSr%r')r*rHrIrJrhrTrUs r+change_member_labelzTruss.change_member_labelsB 9AaDE!IDMM) !FGG$($7$<$<$>!/9F48MM&4I!4Ldmm\bNcdeNf3g i0 ))%0:>:N:Nu:U,,Y7,,007;?;P;PQV;W--i8--11%89 9r-c|D]}|d}|d}|d}t|}t|}||jvr td||jvr!|j|j ||gp||gg|j|<y)a? This method applies external load(s) at the specified node(s). Parameters ========== The input(s) of the method are tuple(s) of the form (location, magnitude, direction). location: String or Symbol Label of the Node at which load is applied. magnitude: Sympifyable Magnitude of the load applied. It must always be positive and any changes in the direction of the load are not reflected here. direction: Sympifyable The angle, in degrees, that the load vector makes with the horizontal in the counter-clockwise direction. It takes the values 0 to 360, inclusive. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> from sympy import symbols >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0)) >>> P = symbols('P') >>> t.apply_load(('A', P, 90), ('A', P/2, 45), ('A', P/4, 90)) >>> t.loads {'A': [[P, 90], [P/2, 45], [P/4, 90]]} rrCrDz$Load must be applied at a known nodeN)r r(rErrGr*rHrIlocation magnitude directions r+rgzTruss.apply_loads@ EAtH!I!I *I *It555 !GHHt{{*KK)00)Y1GH.7-C,DDKK) Er-cf|D]}|d}|d}|d}t|}t|}||jvr td||g|j|vr td|j|j ||g|j|gk(s|jj |y)ad This method removes already present external load(s) at specified node(s). Parameters ========== The input(s) of this method are tuple(s) of the form (location, magnitude, direction). location: String or Symbol Label of the Node at which load is applied and is to be removed. magnitude: Sympifyable Magnitude of the load applied. direction: Sympifyable The angle, in degrees, that the load vector makes with the horizontal in the counter-clockwise direction. It takes the values 0 to 360, inclusive. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> from sympy import symbols >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0)) >>> P = symbols('P') >>> t.apply_load(('A', P, 90), ('A', P/2, 45), ('A', P/4, 90)) >>> t.loads {'A': [[P, 90], [P/2, 45], [P/4, 90]]} >>> t.remove_load(('A', P/4, 90), ('A', P/2, 45)) >>> t.loads {'A': [[P, 90]]} rrCrDz&Load must be removed from a known nodezENo load of this magnitude and direction has been applied at this nodeN)r r(rErrRrSrps r+ remove_loadzTruss.remove_loadsF *AtH!I!I *I *It555 !IJJy)X1FF$%lmmKK)00)Y1GH{{8$* )! *r-c |D]Z}|d}|d}||jvr td||jvr|dk(rY|j|t dt |zdzdf|j|t dt |zdzdfn|d k(r|j|t dt |zdzdfn|j|dk(r2|d k(rp|j |t dt |zdzdfnC|j|d k(r1|dk(r,|j|t dt |zdzdf||j|<]y ) a This method adds a pinned or roller support at specified node(s). Parameters ========== The input(s) of this method are of the form (location, type). location: String or Symbol Label of the Node at which support is added. type: String Type of the support being provided at the node. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0)) >>> t.apply_support(('A', 'pinned'), ('B', 'roller')) >>> t.supports {'A': 'pinned', 'B': 'roller'} rrCz%Support must be added on a known noder_r`rarbrcrdN)r(rErrgrrfru)r*rHrIrqtypes r+ apply_supportzTruss.apply_support;sb0 0AtHQ4Dt555 !HII4>>1x'6$s8}:LT:Q3RTU(VW6$s8}:LT:Q3RTV(WX)6$s8}:LT:Q3RTV(WX^^H-9x'(((F4H ;Md;R4SUV)WX^^H-9x'6$s8}:LT:Q3RTU(VW+/x(' 0r-c |D]}||jvr td||jvr td|j|dk(rY|j|t dt |zdzdf|j|t dt |zdzdfn>|j|d k(r,|j|t dt |zdzdf|jj |y ) aL This method removes support from specified node(s.) Parameters ========== locations: String or Symbol Label of the Node(s) at which support is to be removed. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(('A', 0, 0), ('B', 3, 0)) >>> t.apply_support(('A', 'pinned'), ('B', 'roller')) >>> t.supports {'A': 'pinned', 'B': 'roller'} >>> t.remove_support('A','B') >>> t.supports {} z No such node exists in the Trussz+No support has been added to the given noder_r`rarrbrcrdN)r(rErrurrfrS)r*rHrqs r+remove_supportzTruss.remove_supporths. -Ht555 !CDD/ !NOO>>(+x7$$htCM7I$7N0OQR%ST$$htCM7I$7N0OQS%TU^^H-9$$htCM7I$7N0OQS%TU""8, -r-c F d}|jD]J}|d|jvs|j|ddk(r|dz }0|j|ddk(sF|dz }Ldt|jzt|j|zk7r t dt dt|jzDcgc]1}t dt|jzDcgc]}dc}3}}}t dt|jzd}d}|jD]}|d|jvr|j|dD]}|dtdt|dzdzk7s'|dtdt|dzd zk7sK||xx|dtt|dzd z zzcc<||dzxx|dtt|dzd z zzcc<|dz }d} d} |jD]}|d|jvrn|j|ddk(r,|| | xxdz cc<|| dz| dzxxdz cc<| dz } n-|j|ddk(r|| dz| xxdz cc<| dz } | dz } |jD]} |j| d} |j| d} t|j| d|j| dz dz|j| d|j| dz dzz}|j j#| }|j j#| }|j| d|j| dz |z }|j| d|j| dz |z }|j| d|j| dz |z }|j| d|j| dz |z }||dz| xx|z cc<||dzdz| xx|z cc<||dz| xx|z cc<||dzdz| xx|z cc<| dz } t%|d z|z}i|_d}t(}|jD]Z}|d|jvs|j|dD]1}t+|dtt,t.fvs#t1||d}3\t t|D]<}t+||tt,t.fvs#t3|||z d ks8d||<>|jD]}|d|jvs|j|ddk(rQ|||j&dt|dzdz<||dz|j&dt|dzd z<|dz }{|j|ddk(s|||j&dt|dzd z<|dz }|jD]} |||j4| <|dz }y cc}wcc}}w)a This method solves for all reaction forces of all supports and all internal forces of all the members in the truss, provided the Truss is solvable. A Truss is solvable if the following condition is met, 2n >= r + m Where n is the number of nodes, r is the number of reaction forces, where each pinned support has 2 reaction forces and each roller has 1, and m is the number of members. The given condition is derived from the fact that a system of equations is solvable only when the number of variables is lesser than or equal to the number of equations. Equilibrium Equations in x and y directions give two equations per node giving 2n number equations. However, the truss needs to be stable as well and may be unstable if 2n > r + m. The number of variables is simply the sum of the number of reaction forces and member forces. .. note:: The sign convention for the internal forces present in a member revolves around whether each force is compressive or tensile. While forming equations for each node, internal force due to a member on the node is assumed to be away from the node i.e. each force is assumed to be compressive by default. Hence, a positive value for an internal force implies the presence of compressive force in the member and a negative value implies a tensile force. Examples ======== >>> from sympy.physics.continuum_mechanics.truss import Truss >>> t = Truss() >>> t.add_node(("node_1", 0, 0), ("node_2", 6, 0), ("node_3", 2, 2), ("node_4", 2, 0)) >>> t.add_member(("member_1", "node_1", "node_4"), ("member_2", "node_2", "node_4"), ("member_3", "node_1", "node_3")) >>> t.add_member(("member_4", "node_2", "node_3"), ("member_5", "node_3", "node_4")) >>> t.apply_load(("node_4", 10, 270)) >>> t.apply_support(("node_1", "pinned"), ("node_2", "roller")) >>> t.solve() >>> t.reaction_loads {'R_node_1_x': 0, 'R_node_1_y': 20/3, 'R_node_2_y': 10/3} >>> t.internal_forces {'member_1': 20/3, 'member_2': 20/3, 'member_3': -20*sqrt(2)/3, 'member_4': -10*sqrt(5)/3, 'member_5': 10} rr_rDrdrCz The given truss cannot be solvedr`rarbg|=N)rrrPrrErOrr/rrrfrr rr r(r rer r&rrwrrminabsr')r*count_reaction_loadsrijrIcoefficients_matrix load_matrixload_matrix_rowrjcolsrowrUrYrZlength start_index end_indexhorizontal_component_startvertical_component_starthorizontal_component_endvertical_component_end forces_matrixmin_loads r+solvez Truss.solvesT !KK .DAw$..(>>$q'*H4(A-(^^DG,h6(A-(  . S  T]]!36J!J J?@ @OTUVWZ[_[f[fWgUgOhi!53t{{3C1C+DEaEiiAc$**o-q1 KK !DAw$++% KKQ0XDAwtCQL'8'= >>47FSWX[\`ab\cXdSdeiSiLjCj#O4QBtAwJsN@S8SS4#Oa$78DGC4PQ7 SVDW>$q'*H4',T2a72'A.tAv6!;6AID^^DG,h6'A.t494AID 1HC mm FMM&)!,E--'*C411%8;D(>s(CA(FtG]G]^cGdefGg(gio'o $(,(>(>u(Ea(HI_I_`cIdefIg(gio'o $&*&<&>#&xa#9: : s=)* )AM!$%fc3-??}Q'0158'(M!$ )KK DAw$..(>>$q'*H4CPQRCSD((c$q'l):4)?@CPQRSTQTCUD((c$q'l):4)?@FA^^DG,h6CPQRCSD((c$q'l):4)?@FA mm F,9!,>> from sympy.physics.continuum_mechanics.truss import Truss >>> import math >>> t = Truss() >>> t.add_node(("A", -4, 0), ("B", 0, 0), ("C", 4, 0), ("D", 8, 0)) >>> t.add_node(("E", 6, 2/math.sqrt(3))) >>> t.add_node(("F", 2, 2*math.sqrt(3))) >>> t.add_node(("G", -2, 2/math.sqrt(3))) >>> t.add_member(("AB","A","B"), ("BC","B","C"), ("CD","C","D")) >>> t.add_member(("AG","A","G"), ("GB","G","B"), ("GF","G","F")) >>> t.add_member(("BF","B","F"), ("FC","F","C"), ("CE","C","E")) >>> t.add_member(("FE","F","E"), ("DE","D","E")) >>> t.apply_support(("A","pinned"), ("D","roller")) >>> t.apply_load(("G", 3, 90), ("E", 3, 90), ("F", 2, 90)) >>> p = t.draw() >>> p # doctest: +ELLIPSIS Plot object containing: [0]: cartesian line: 1 for x over (1.0, 1.0) ... >>> p.show() z-To use this function numpy module is requiredrKrrC皙?皙?Fg?)markersshow annotationsxlimylimaxis rectangles) r ImportErrorr _draw_nodes _draw_members_draw_supports _draw_loadsrr(maxr~r)r* subs_dictrKrrr node_markersmember_rectanglessupport_markersload_annotationsxmaxxminymaxyminrilim sing_plots r+drawz Truss.drawsdMN N 3K  '' 2 < ..0'' --/?"++-'' tt** >DtT33D9!<=DtT33D9!<=DtT33D9!<=DtT33D9!<=D  > $s(48#A%tCxS'8':; Sc!!# #QAq 7T_gklpqtltgtvz{~v~fHLMQRUMUHUW[\_W_G`glyCDI QAq 7T_gklpqtltgtvz{~v~fHLMQRUMUHUW[\_W_G`glyCDIr-c,g}|jD]6}t|j|dttfvrE|j|d|vr&||j|d|j|d<nt dt|j|dt k(r|j|dj }|D]e}t|ttfvs|dk(s||vr t d|j|dxx|zcc<|j|dxx||zcc<gt|j|dttfvrF|j|d|vr'||j|d|j|d<~t dt|j|dt k(s|j|dj }|D]e}t|ttfvs|dk(s||vr t d|j|dxx|zcc<|j|dxx||zcc<g9|jD]<}|j|j|dg|j|dggdddd>|S)Nrz/provided substituted dictionary is not adequaterCoblackrHmarker markersizecolor)r(rwrrrEr as_coeff_MulrG)r*rrriobjectsobjects r+rzTruss._draw_nodes_s ** QDT++D1!45&(9KK))$/2i?6?@V@VW[@\]^@_6`D**403$%VWWt--d3A673>006q9FFH%QFF|'99$d?fI.E",-^"__ 2248;vE; 2248;y?PP; QT++D1!45&(9KK))$/2i?6?@V@VW[@\]^@_6`D**403$%VWWt--d3A673>006q9FFH%QFF|'99$d?fI.E",-^"__ 2248;vE; 2248;y?PP; Q/ Q>** D   !33D9!<=@V@VW[@\]^@_?`a !"#   r-c g}t }t}t }t}|jD]r}t||j|d}t||j|d}t||j|d}t||j|d}tt d|zd|zz t d|zd|zz kDr d|zd|zz }n d|zd|zz }|j D]}|j|j |dd} |j|j |dd} |j|j |dd} |j|j |dd} | | k7r| | k7r| | kDr|j | d|zttdz t| | z | | z z zzdz z | d|zttdz t| | z | | z z zzdz z ft| | z dz| | z dzzd|ztjdz zd|zdt| | z | | z z ztz d d m|j | d|zttdz t| | z | | z z zzdz z | d|zttdz t| | z | | z z zzdz z ft| | z dz| | z dzzd|ztjdz zd|zdt| | z | | z z ztz d d =| | k(r| | kDre|j | d|zdz z | d|zdz z ft| | z dz| | z dzzd|zd dtjd| | z z zd d |j | d|zdz z | d|zdz z ft| | z dz| | z dzzd |zd dtjd| | z z zd d | | krk|j | d|zdz z | d|zdz z ft| | z dz| | z dzzd|zdz zd|zd tjd| | z zd d |j | d|zdz z | d|zdz z ft| | z dz| | z dzzd|zdz z d|zd tjd| | z zd d |S) NrrCrrg{Gzt?rDr|brown)xywidthheightanglerrcg{Gzt)rr(rr~rrrGrr rrr mathcopysign) r*rrrrrrimax_diffrUx1y1x2y2s r+rzTruss._draw_memberss tt** >DtT33D9!<=DtT33D9!<=DtT33D9!<=DtT33D9!<=D  > s4xD !#c$hs4x&7"8 84xD(H4xD(HmmC F'' f(=a(@A!DB'' f(=a(@A!DB'' f(=a(@A!DB'' f(=a(@A!DB2v"b&b5%,,"$U8^C1T2b52b5/=R8R4S%STU%U"UWYZ_`hZhilmopqmqrvxz{}x}ACDFAFxGsHnHjI[IJK[KXK"L$("R%!RUQJ)>$?htyyYZ|@[$[%*8^$'bebe_(=$=b$@$+ &,,"$U8^C1T2b52b5/=R8R4S%STU%U"UWYZ_`hZhilmopqmqrvxz{}x}ACDFAFxGsHnHjI[IJK[KXK"L$("R%!RUQJ)>$?htyyYZ|@[$[%*8^$'bebe_(=$=b$@$+ Rb5%,,"$U8^A%5"5r%.:J7J!K$("R%!RUQJ)>$?%*8^$&$--2b5*A(A$B$+ &,,"$U8^A%5"5r%.:J7J!K$("R%!RUQJ)>$?%+H_$&$--2b5*A(A$B$+ b5%,,"$U8^A%5"5r%.:J7J!K$("R%!RUQJ)>$?hq@P$P%*8^$&t}}Q2'>$>$+ &,,"$U8^A%5"5r%.:J7J!K&*BrEA:r"uqj+@&A%.QRBR&R$S%*8^$&t}}Q2'>$>$+ wC J! r-c *g}t }t}t }t}|jD]r}t||j|d}t||j|d}t||j|d}t||j|d}tt d|zd|zz t d|zd|zz kDr d|zd|zz }n d|zd|zz }|j D]%}|j |dk(r||j |j|dg|j|dggdddd d |j |j|dg|j|dd |zz ggd d dd|j |dk(s|j |j|dg|j|dd|zz ggdddd d |j |j|dg|j|dd|zz ggd d dd(|S)NrrCrrr_rnone)rHrrrmarkerfacecolorgQ?_rrdg{Gz?r g333333?)rr(rr~rrrG)r*rrrrrrirs r+rzTruss._draw_supportsstt** >DtT33D9!<=DtT33D9!<=DtT33D9!<=DtT33D9!<=D  > s4xD !#c$hs4x&7"8 84xD(H4xD(HNN1 D~~d#X-&&"33D9!<=!33D9!<= "#%' '*0   &&"33D9!<=!33D9!|j|d} |j|d} |jd | tjt|dzd z |d z zz | tjt|dzd z |d z zz f| |d z t ||z zt ||z ztjt|dzd z zd z z | |d z t ||z zt ||z ztjt|dzd z zd z z fd dddddi|S)NrrCrrrr`rarbr|dg?r)r headlength headwidth facecolor)textrxytext arrowprops) rr(rr~rrrrfrGrrr r) r*rrrrrrirrjrKrLs r+rzTruss._draw_loads-stt** >DtT33D9!<=DtT33D9!<=DtT33D9!<=DtT33D9!<=D  > s4xD !#c$hs4x&7"8 84xD(*H4xD(*HKK D D) 7vd3t9nT&9:F4D >RVCVI488TVW[\]W^T^_bTbKccdfffx|CT N:3tDy>I488TVW[\]W^T^_bTbKccdfff"/2q^e%f     ( r-)N) __name__ __module__ __qualname____doc__r,propertyr/r2r4r7r9r;r=r?rArMrVr[r]rkrnrgrurxrzrrrrrrrr-r+rrs;8$$ !! $$  $$  $$ %% 17j/2f.1`1B]\~/9b.E`3*j+0Z%-Nqf +Y,Yx+ZY!vDL' r-r)#rcmathrrsympy.core.addrsympy.core.evalfrsympy.core.mulrsympy.core.symbolrsympy.core.sympifyr sympyr r sympy.external.importtoolsr (sympy.functions.elementary.miscellaneousr sympy.matrices.denserrsympy.physics.units.quantitiesrsympy.plottingrsympy.utilities.decoratorrrr__doctest_requires__rrrr-r+rsi  $&49& 38&7 gj(-DEv v r-