o
    ePo                     @   s   d Z ddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
 ddlmZmZ ddlmZ dd	lmZ dd
lmZmZ G dd dZdS )zB
This module can be used to solve problems related
to 2D Trusses.
    )inf)Add)Mul)Symbol)sympify)Matrixpi)sqrt)zeros)sincosc                   @   s   e Zd ZdZdd Zedd Zedd Zedd	 Zed
d Z	edd Z
edd Zedd Zedd Zedd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Zd,S )-Trussa  
    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)
    >>> t.add_node("node_2", 6, 0)
    >>> t.add_node("node_3", 2, 2)
    >>> t.add_node("node_4", 2, 0)
    >>> t.add_member("member_1", "node_1", "node_4")
    >>> t.add_member("member_2", "node_2", "node_4")
    >>> t.add_member("member_3", "node_1", "node_3")
    >>> t.add_member("member_4", "node_2", "node_3")
    >>> t.add_member("member_5", "node_3", "node_4")
    >>> t.apply_load("node_4", magnitude=10, direction=270)
    >>> t.apply_support("node_1", type="fixed")
    >>> t.apply_support("node_2", type="roller")
    c                 C   sL   g | _ i | _i | _i | _g | _g | _g | _g | _i | _i | _	i | _
i | _dS )z'
        Initializes the class
        N)_nodes_members_loads	_supports_node_labels_node_positions_node_position_x_node_position_y_nodes_occupied_reaction_loads_internal_forces_node_coordinatesself r   WD:\Projects\ConvertPro\env\Lib\site-packages\sympy/physics/continuum_mechanics/truss.py__init__6   s   
zTruss.__init__c                 C      | j S )zL
        Returns the nodes of the truss along with their positions.
        )r   r   r   r   r   nodesG      zTruss.nodesc                 C   r   )z7
        Returns the node labels of the truss.
        )r   r   r   r   r   node_labelsN   r!   zTruss.node_labelsc                 C   r   )zB
        Returns the positions of the nodes of the truss.
        )r   r   r   r   r   node_positionsU   r!   zTruss.node_positionsc                 C   r   zW
        Returns the members of the truss along with the start and end points.
        )r   r   r   r   r   members\   r!   zTruss.membersc                 C   r   r$   )Z_member_labelsr   r   r   r   member_labelsc   r!   zTruss.member_labelsc                 C   r   )z
        Returns the nodes with provided supports along with the kind of support provided i.e.
        pinned or roller.
        )r   r   r   r   r   supportsj   s   zTruss.supportsc                 C   r   )z8
        Returns the loads acting on the truss.
        )r   r   r   r   r   loadsr   r!   zTruss.loadsc                 C   r   )z^
        Returns the reaction forces for all supports which are all initialized to 0.
        )r   r   r   r   r   reaction_loadsy   r!   zTruss.reaction_loadsc                 C   r   )z]
        Returns the internal forces for all members which are all initialized to 0.
        )r   r   r   r   r   internal_forces   r!   zTruss.internal_forcesc                 C   s   t |}t |}|| jv rtd|| jv r+|| jv r+| j|| j|kr+td| j|||f | j| | j||f | j| | j| ||g| j	|< dS )a  
        This method adds a node to the truss along with its name/label and its location.

        Parameters
        ==========
        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)
        >>> t.nodes
        [('A', 0, 0), ('B', 3, 0)]
        z!Node needs to have a unique labelz+A node already exists at the given positionN)
r   r   
ValueErrorr   r   indexr   appendr   r   )r   labelxyr   r   r   add_node   s   
,zTruss.add_nodec                 C   s  t t| jD ]}| j| |kr| j| }| j| }q|| jvr$td| j }|D ]}|| j| d ks?|| j| d krCtdq+| j	
|||f | j
| | j
||f | j
| | j
| |t| jv rt| j| |t| jv r| j| | j| dS )a%  
        This method removes a node from the truss.

        Parameters
        ==========
        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)
        >>> t.nodes
        [('A', 0, 0)]
        >>> t.add_node('B', 3, 0)
        >>> t.nodes
        [('A', 0, 0), ('B', 3, 0)]
        >>> t.remove_node('A')
        >>> t.nodes
        [('B', 3, 0)]
        z No such node exists in the trussr      z1The node given has members already attached to itN)rangelenr    r   r   r   r+   r   copyr   remover   listr   popr   r   )r   r.   ir/   r0   members_duplicatememberr   r   r   remove_node   s,   



$zTruss.remove_nodec                 C   s   || j vs|| j vs||krtd|t| jv rtd| j||fr)td||g| j|< d| j||f< d| j||f< d| j|< dS )a  
        This method adds a member between any two nodes in the given truss.

        Parameters
        ==========
        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)
        >>> t.add_node('B', 3, 0)
        >>> t.add_node('C', 2, 2)
        >>> t.add_member('AB', 'A', 'B')
        >>> t.members
        {'AB': ['A', 'B']}
        z;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 nodesTr   N)r   r+   r7   r   r   getr   )r   r.   startendr   r   r   
add_member   s   zTruss.add_memberc                 C   sz   |t | jvrtd| j| j| d | j| d f | j| j| d | j| d f | j| | j| dS )a  
        This method removes a member from the given truss.

        Parameters
        ==========
        label: 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)
        >>> t.add_node('B', 3, 0)
        >>> t.add_node('C', 2, 2)
        >>> t.add_member('AB', 'A', 'B')
        >>> t.add_member('AC', 'A', 'C')
        >>> t.add_member('BC', 'B', 'C')
        >>> t.members
        {'AB': ['A', 'B'], 'AC': ['A', 'C'], 'BC': ['B', 'C']}
        >>> t.remove_member('AC')
        >>> t.members
        {'AB': ['A', 'B'], 'BC': ['B', 'C']}
        z"No such member exists in the Trussr   r2   N)r7   r   r+   r   r8   r   )r   r.   r   r   r   remove_member  s   $$zTruss.remove_memberc              	   C   s  || j vr	td|| j v rtd| jD ]N}|d |krd||d |d f| j| j||d |d f< || j | j |d < | j| | j|< | j| |d t| jv rj| j|d  | j|< | j|d  |t| jv r| j| dkrbdt| d t| j	v rdt| d	 t| j	v r| j	dt| d  | j	dt| d < | j	dt| d	  | j	dt| d	 < | j	dt| d  | j	dt| d	  | j
| | j
|< | j
| D ]*}|d d
kr|d  tdt| d	 8  < |d dkr	| j
| |  nq| j
| D ]+}|d dkr;|d  tdt| d 8  < |d dkr9| j
| |  nq| |tdt| d d | |tdt| d	 d
 | j
| nm| j| dkr| j
| | j
|< | j
| D ]+}|d d
kr|d  tdt| d	 8  < |d dkr| j
| |  nqw| |tdt| d	 d
 | j
| n|t| j
v r| j
| | j
|< | j
| t| jD ]}| j| d |d kr|| j| d< d| j|| j| d f< d| j| j| d |f< | j|| j| d f | j| j| d |f q| j| d |d krb|| j| d< d| j| j| d |f< d| j|| j| d f< | j| j| d |f | j|| j| d f qqdS )a  
        This method changes the label of a node.

        Parameters
        ==========
        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)
        >>> t.add_node('B', 3, 0)
        >>> t.nodes
        [('A', 0, 0), ('B', 3, 0)]
        >>> t.change_node_label('A', 'C')
        >>> t.nodes
        [('C', 0, 0), ('B', 3, 0)]
        z!No such node exists for the Trussz*A node with the given label already existsr   r2      pinnedR__x_yZ   rollerTN)r   r+   r   r,   r   r8   r7   r   strr   r   r   r6   
apply_loadr   r   )r   r.   	new_labelnodeloadr;   r   r   r   change_node_label1  s   

.4((   zTruss.change_node_labelc                 C   s   |t | jvrtdt | j }|D ]+}||kr?| j| d | j| d g| j|< | j| | j| | j|< | j| qdS )aG  
        This method changes the label of a member.

        Parameters
        ==========
        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)
        >>> t.add_node('B', 3, 0)
        >>> t.nodes
        [('A', 0, 0), ('B', 3, 0)]
        >>> t.change_node_label('A', 'C')
        >>> t.nodes
        [('C', 0, 0), ('B', 3, 0)]
        >>> t.add_member('BC', 'B', 'C')
        >>> t.members
        {'BC': ['B', 'C']}
        >>> t.change_member_label('BC', 'BC_new')
        >>> t.members
        {'BC_new': ['B', 'C']}
        z#No such member exists for the Trussr   r2   N)r7   r   r+   r5   r8   r   )r   r.   rK   r:   r;   r   r   r   change_member_label  s    "zTruss.change_member_labelc                 C   s\   t |}t |}|| jvrtd|t| jv r$| j| ||g dS ||gg| j|< dS )a  
        This method applies an external load at a particular node

        Parameters
        ==========
        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)
        >>> t.add_node('B', 3, 0)
        >>> P = symbols('P')
        >>> t.apply_load('A', P, 90)
        >>> t.apply_load('A', P/2, 45)
        >>> t.apply_load('A', P/4, 90)
        >>> t.loads
        {'A': [[P, 90], [P/2, 45], [P/4, 90]]}
        z$Load must be applied at a known nodeN)r   r"   r+   r7   r   r-   r   locationZ	magnitude	directionr   r   r   rJ     s   !
zTruss.apply_loadc                 C   sr   t |}t |}|| jvrtd||g| j| vrtd| j| ||g | j| g kr7| j| dS dS )a;  
        This method removes an already
        present external load at a particular node

        Parameters
        ==========
        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)
        >>> t.add_node('B', 3, 0)
        >>> P = symbols('P')
        >>> t.apply_load('A', P, 90)
        >>> t.apply_load('A', P/2, 45)
        >>> t.apply_load('A', P/4, 90)
        >>> t.loads
        {'A': [[P, 90], [P/2, 45], [P/4, 90]]}
        >>> t.remove_load('A', P/4, 90)
        >>> t.loads
        {'A': [[P, 90], [P/2, 45]]}
        z&Load must be removed from a known nodezENo load of this magnitude and direction has been applied at this nodeN)r   r"   r+   r   r6   r8   rP   r   r   r   remove_load  s   $
zTruss.remove_loadc                 C   s  || j vr	td|t| jvrG|dkr3| |tdt| d d | |tdt| d d nI|dkrF| |tdt| d d n5| j| dkrb|dkra| |tdt| d d n| j| dkr||dkr|| |tdt| d d || j|< d	S )
aH  
        This method adds a pinned or roller support at a particular node

        Parameters
        ==========

        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)
        >>> t.add_node('B', 3, 0)
        >>> t.apply_support('A', 'pinned')
        >>> t.supports
        {'A': 'pinned'}
        z%Support must be added on a known noderC   rD   rE   r   rF   rG   rH   N)r   r+   r7   r   rJ   r   rI   rS   )r   rQ   typer   r   r   apply_support  s"   
 zTruss.apply_supportc                 C   s   || j vr	td|t| jvrtd| j| dkr:| |tdt| d d | |tdt| d d n| j| d	krP| |tdt| d d | j| d
S )a4  
        This method removes support from a particular node

        Parameters
        ==========

        location: String or Symbol
            Label of the Node at which support is to be removed.

        Examples
        ========

        >>> from sympy.physics.continuum_mechanics.truss import Truss
        >>> t = Truss()
        >>> t.add_node('A', 0, 0)
        >>> t.add_node('B', 3, 0)
        >>> t.apply_support('A', 'pinned')
        >>> t.supports
        {'A': 'pinned'}
        >>> t.remove_support('A')
        >>> t.supports
        {}
        z No such node exists in the Trussz+No support has been added to the given noderC   rD   rE   r   rF   rG   rH   N)r   r+   r7   r   rS   r   rI   r8   )r   rQ   r   r   r   remove_supportA  s   
 zTruss.remove_supportc              
      sZ  d} j D ]&}|d t jv r+ j|d  dkr|d7 }q j|d  dkr+|d7 }qdt j  t j| kr>td fddtdt j  D }tdt j d}d} j D ]c}|d t j	v r j	|d  D ]L}|d t
d	t|d  d
 kr|d t
d	t|d  d kr||  |d tt|d  d  8  < ||d   |d tt|d  d  8  < qo|d7 }q]d}d} j D ]P}|d t jv r j|d  dkr|| |  d7  < ||d  |d   d7  < |d7 }n j|d  dkr||d  |  d7  < |d7 }|d7 }qt jD ]}	 j|	 d }
 j|	 d }t j|
 d  j| d  d  j|
 d  j| d  d  } j|
} j|} j| d  j|
 d  | } j| d  j|
 d  | } j|
 d  j| d  | } j|
 d  j| d  | }||d  |  |7  < ||d d  |  |7  < ||d  |  |7  < ||d d  |  |7  < |d7 }qt|d | }i  _d}t} j D ]+}|d t j	v r j	|d  D ]}t|d t
ttfvrt||d }qqtt|D ]}t|| t
ttfvr7t|| | dk r7d||< q j D ]Z}|d t jv r j|d  dkrx||  jd	t|d  d
 < ||d   jd	t|d  d < |d7 }q< j|d  dkr||  jd	t|d  d < |d7 }q<t jD ]}	||  j|	< |d7 }qdS )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)
        >>> t.add_node("node_2", 6, 0)
        >>> t.add_node("node_3", 2, 2)
        >>> t.add_node("node_4", 2, 0)
        >>> t.add_member("member_1", "node_1", "node_4")
        >>> t.add_member("member_2", "node_2", "node_4")
        >>> t.add_member("member_3", "node_1", "node_3")
        >>> t.add_member("member_4", "node_2", "node_3")
        >>> t.add_member("member_5", "node_3", "node_4")
        >>> t.apply_load("node_4", magnitude=10, direction=270)
        >>> t.apply_support("node_1", type="pinned")
        >>> t.apply_support("node_2", type="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}
        r   rC   rB   rH   r2   z The given truss cannot be solvedc                    s(   g | ]}d d t dt j D qS )c                 S   s   g | ]}d qS )r   r   ).0r9   r   r   r   
<listcomp>  s    z*Truss.solve.<locals>.<listcomp>.<listcomp>rB   )r3   r4   r   )rW   jr   r   r   rX     s   ( zTruss.solve.<locals>.<listcomp>rD   rE   rF      g|=N)r   r7   r   r4   r   r+   r3   r
   r    r   r   rI   r   r   r   r	   r   r   r,   r   r   r   rT   r   r   minabsr   )r   Zcount_reaction_loadsrL   Zcoefficients_matrixZload_matrixZload_matrix_rowrM   colsrowr;   r>   r?   lengthZstart_indexZ	end_indexZhorizontal_component_startZvertical_component_startZhorizontal_component_endZvertical_component_endZforces_matrixr9   Zmin_loadrY   r   r   r   solveg  s   1

 
@(,



D    

"zTruss.solveN)__name__
__module____qualname____doc__r   propertyr    r"   r#   r%   r&   r'   r(   r)   r*   r1   r<   r@   rA   rN   rO   rJ   rS   rU   rV   ra   r   r   r   r   r      s@    #








,0+#[,-2*&r   N)re   Zcmathr   Zsympy.core.addr   Zsympy.core.mulr   Zsympy.core.symbolr   Zsympy.core.sympifyr   Zsympyr   r   Z(sympy.functions.elementary.miscellaneousr	   Zsympy.matrices.denser
   r   r   r   r   r   r   r   <module>   s    