U
    9%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   f/var/www/html/Darija-Ai-API/env/lib/python3.8/site-packages/sympy/physics/continuum_mechanics/truss.py__init__6   s    zTruss.__init__c                 C   s   | j S )zL
        Returns the nodes of the truss along with their positions.
        )r   r   r   r   r   nodesG   s    zTruss.nodesc                 C   s   | j S )z7
        Returns the node labels of the truss.
        )r   r   r   r   r   node_labelsN   s    zTruss.node_labelsc                 C   s   | j S )zB
        Returns the positions of the nodes of the truss.
        )r   r   r   r   r   node_positionsU   s    zTruss.node_positionsc                 C   s   | j S zW
        Returns the members of the truss along with the start and end points.
        )r   r   r   r   r   members\   s    zTruss.membersc                 C   s   | j S r"   )Z_member_labelsr   r   r   r   member_labelsc   s    zTruss.member_labelsc                 C   s   | j S )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   s   | j S )z8
        Returns the loads acting on the truss.
        )r   r   r   r   r   loadsr   s    zTruss.loadsc                 C   s   | j S )z^
        Returns the reaction forces for all supports which are all initialized to 0.
        )r   r   r   r   r   reaction_loadsy   s    zTruss.reaction_loadsc                 C   s   | j S )z]
        Returns the internal forces for all members which are all initialized to 0.
        )r   r   r   r   r   internal_forces   s    zTruss.internal_forcesc                 C   s   t |}t |}|| jkr$tdn|| jkrZ|| jkrZ| j|| j|krZtdnT| 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krJtdn| j }|D ]0}|| j| d ks|| j| d krXtdqX| j	
|||f | j
| | j
||f | j
| | j
| |t| jkr| j| |t| jk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-   r.   members_duplicatememberr   r   r   remove_node   s(    



$
zTruss.remove_nodec                 C   s   || j ks|| j ks||kr&tdnf|t| jkr>tdnN| j||frXtdn4||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)   r5   r   r   getr   )r   r,   startendr   r   r   
add_member   s    


zTruss.add_memberc                 C   s|   |t | jkrtdn`| 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   r0   N)r5   r   r)   r   r6   r   )r   r,   r   r   r   remove_member  s    
$$zTruss.remove_memberc              	   C   s  || j krtdn|| j kr,tdn| jD ]}|d |kr2||d |d f| j| j||d |d f< || j | j |d < | j| | j|< | j| |d t| jkr| j|d  | j|< | j|d  |t| jkr| j| dkrdt| d t| j	krdt| d	 t| j	k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 ]X}|d d
kr|d  tdt| d	 8  < |d dkr| j
| |  q&q| j
| D ]X}|d dkr0|d  tdt| d 8  < |d dkr| j
| |  qq0| |tdt| d d | |tdt| d	 d
 | j
| n| j| dkr| j
| | j
|< | j
| D ]X}|d d
kr|d  tdt| d	 8  < |d dkrN| j
| |  qXq| |tdt| d	 d
 | j
| n,|t| j
kr| j
| | j
|< | j
| t| jD ]}| j| d |d krL|| j| d< d| j|| j| d f< d| j| j| d |f< | j|| j| d f | j| j| d |f n| 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 qq2d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   r0      pinnedR__x_yZ   rollerTN)r   r)   r   r*   r   r6   r5   r   strr   r   r   r4   
apply_loadr   r   )r   r,   	new_labelnodeloadr9   r   r   r   change_node_label1  s|    

.8(( 
 
 
zTruss.change_node_labelc                 C   s   |t | jkrtdnjt | j }|D ]V}||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   r0   N)r5   r   r)   r3   r6   r   )r   r,   rI   r8   r9   r   r   r   change_member_label  s     
"zTruss.change_member_labelc                 C   s\   t |}t |}|| jkr$tdn4|t| jkrH| j| ||g n||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)   r5   r   r+   r   locationZ	magnitude	directionr   r   r   rH     s    !

zTruss.apply_loadc                 C   sr   t |}t |}|| jkr$tdn0||g| j| kr@tdn| j| ||g | j| g krn| j| 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   r4   r6   rN   r   r   r   remove_load  s    $


zTruss.remove_loadc                 C   s  || j krtdn|t| jkr|dkrh| |tdt| d d | |tdt| d d q|dkr| |tdt| d d nj| j| dkr|dkr| |tdt| d d n4| 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 noderA   rB   rC   r   rD   rE   rF   N)r   r)   r5   r   rH   r   rG   rQ   )r   rO   typer   r   r   apply_support  s    

   zTruss.apply_supportc                 C   s   || j krtdn|t| jkr,tdn| j| dkrx| |tdt| d d | |tdt| d d n,| j| d	kr| |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 noderA   rB   rC   r   rD   rE   rF   N)r   r)   r5   r   rQ   r   rG   r6   )r   rO   r   r   r   remove_supportA  s    


 zTruss.remove_supportc              
      s`  d} j D ]L}|d t jk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 ]}|d t j	krz j	|d  D ]}|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  < q|d7 }qd}d} j D ]}|d t jkr, j|d  dkr|| |  d7  < ||d  |d   d7  < |d7 }n4 j|d  dkr,||d  |  d7  < |d7 }|d7 }qt jD ]n}	 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 }qBt|d | }i  _d}t} j D ]V}|d t j	kr؈ j	|d  D ],}t|d t
ttfkrt||d }qqtt|D ]<}t|| t
ttfkr<t|| | dk r<d||< q< j D ]}|d t jkr j|d  dkr||  jd	t|d  d
 < ||d   jd	t|d  d < |d7 }n: j|d  dkr||  jd	t|d  d < |d7 }qt jD ]}	||  j|	< |d7 }q>d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   rA   r@   rF   r0   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   ).0r7   r   r   r   
<listcomp>  s     z*Truss.solve.<locals>.<listcomp>.<listcomp>r@   )r1   r2   r   )rU   jr   r   r   rV     s     zTruss.solve.<locals>.<listcomp>rB   rC   rD      g|=N)r   r5   r   r2   r   r)   r1   r
   r   r   r   rG   r   r   r   r	   r   r   r*   r   r   r   rR   r   r   minabsr   )r   Zcount_reaction_loadsrJ   Zcoefficients_matrixZload_matrixZload_matrix_rowrK   colsrowr9   r<   r=   lengthstart_indexZ	end_indexZhorizontal_component_startZvertical_component_startZhorizontal_component_endZvertical_component_endZforces_matrixr7   Zmin_loadrW   r   r   r   solveg  s    1


 
@(.


D    

"
zTruss.solveN)__name__
__module____qualname____doc__r   propertyr   r    r!   r#   r$   r%   r&   r'   r(   r/   r:   r>   r?   rL   rM   rH   rQ   rS   rT   r`   r   r   r   r   r      s>   #








,0+#[,-2*&r   N)rd   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   