|
1 | | - |
| 1 | +.. _constrain: |
2 | 2 |
|
3 | 3 | constrain |
4 | 4 | ^^^^^^^^^ |
5 | 5 |
|
| 6 | +The ``constrain`` command imposes a multi-point constraint between two nodes. |
| 7 | +When no rotation is specified, it behaves like :ref:`equalDOF`, constraining the degrees-of-freedom at the constrained node to equal those at the retained node. |
| 8 | +When a rotation vector is provided, the constraint enforces a rotated relationship between the nodes through a rotation matrix. |
6 | 9 |
|
7 | 10 | .. tabs:: |
8 | 11 |
|
9 | | - .. tab:: Python (RT) |
10 | | - |
| 12 | + .. tab:: Python |
| 13 | + |
11 | 14 | .. py:method:: Model.constrain(rNode, cNode, rotate=None) |
12 | | - |
13 | | - :param rNode: tag of the retained :ref:`node` |
14 | | - :type rNode: |integer| |
15 | | - :param cNode: tag of the constrained node |
16 | | - :type cNode: |integer| |
17 | | - :param rotate: optional rotation vector |
18 | | - :type rotate: |list| of |float| |
19 | 15 |
|
| 16 | + Impose a multi-point constraint between a retained node and a constrained node. |
| 17 | + |
| 18 | + :param int|tuple rNode: integer tag identifying the retained node, or tuple (rNode, cNode) for both nodes |
| 19 | + :param int cNode: integer tag identifying the constrained node (required if rNode is not a tuple) |
| 20 | + :param list rotate: optional rotation vector [v1, v2, v3] representing axis-angle in radians (see :ref:`Rotation Vector <constrain-rotation-vector>` below). Only available for 3D problems (ndm=3, ndf=6). |
| 21 | + :return: None |
20 | 22 |
|
21 | 23 | .. tab:: Tcl |
22 | 24 |
|
23 | | - .. function:: constrain rNode cNode <-rotate vector> |
| 25 | + .. function:: constrain $rNodeTag $cNodeTag <-rotate {v1 v2 v3}> |
24 | 26 |
|
25 | | - :param rNode: tag of the retained node |
26 | | - :type rNode: |integer| |
27 | | - :param cNode: tag of the constrained node |
28 | | - :type cNode: |integer| |
| 27 | + .. csv-table:: |
| 28 | + :header: "Argument", "Type", "Description" |
| 29 | + :widths: 10, 10, 40 |
29 | 30 |
|
| 31 | + $rNodeTag, |integer|, integer tag identifying the retained node (*rNode*) |
| 32 | + $cNodeTag, |integer|, integer tag identifying the constrained node (*cNode*) |
| 33 | + -rotate {v1 v2 v3}, |optional|, rotation vector representing axis-angle in radians (see :ref:`Rotation Vector <constrain-rotation-vector>` below). Only available for 3D problems (ndm=3, ndf=6). |
30 | 34 |
|
31 | | -Theory |
| 35 | + |
| 36 | +Theory |
32 | 37 | ------ |
33 | 38 |
|
34 | | -``code``. *italic* **bold**. |
| 39 | +The ``constrain`` command creates a multi-point constraint that enforces the relationship :math:`\boldsymbol{u}_c = \boldsymbol{C}_{cr} \boldsymbol{u}_r`, where :math:`\boldsymbol{u}_c` is the response vector at the constrained node, :math:`\boldsymbol{u}_r` is the response vector at the retained node, and :math:`\boldsymbol{C}_{cr}` is the constraint matrix. |
| 40 | + |
| 41 | +.. _constrain-without-rotation: |
| 42 | + |
| 43 | +Without Rotation |
| 44 | +~~~~~~~~~~~~~~~~ |
| 45 | + |
| 46 | +When no rotation is specified, the constraint matrix is the identity matrix, resulting in behavior equivalent to :ref:`equalDOF`: :math:`\boldsymbol{C}_{cr} = \boldsymbol{I}`. |
| 47 | +This form of the constraint works for both 2D and 3D problems. |
| 48 | + |
| 49 | +With Rotation |
| 50 | +~~~~~~~~~~~~~ |
| 51 | + |
| 52 | +When a rotation vector :math:`\boldsymbol{v} = [v_1, v_2, v_3]` is provided, the rotation matrix :math:`\boldsymbol{R}` is computed using `Rodrigues' rotation formula <https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula#:~:text=In%20terms%20of%20the%20matrix%20exponential%2C>`_ as the matrix exponential :math:`\boldsymbol{R} = \exp(\theta \boldsymbol{K})`, where :math:`\theta` is the magnitude of the rotation vector and :math:`\boldsymbol{K}` is the skew-symmetric matrix corresponding to the normalized rotation axis. |
| 53 | + |
| 54 | +.. _constrain-rotation-vector: |
| 55 | + |
| 56 | +Rotation Vector |
| 57 | +~~~~~~~~~~~~~~~ |
| 58 | + |
| 59 | +The rotation vector uses the **axis-angle representation**: the normalized direction defines the rotation axis, and the magnitude is the rotation angle in radians. The rotation follows the **right-hand rule**: a positive angle rotates counterclockwise when viewed from the tip of the axis vector. |
| 60 | + |
| 61 | +In 3D models, the components :math:`[v_1, v_2, v_3]` correspond to rotations about the global X, Y, and Z axes respectively: |
| 62 | +- :math:`[\theta, 0, 0]` rotates about the X-axis |
| 63 | +- :math:`[0, \theta, 0]` rotates about the Y-axis |
| 64 | +- :math:`[0, 0, \theta]` rotates about the Z-axis |
| 65 | + |
| 66 | +The coordinate system and degrees-of-freedom conventions are documented in :ref:`Conventions <conventions>`. |
| 67 | + |
| 68 | +For 3D problems (ndm=3, ndf=6), the constraint matrix is: |
| 69 | + |
| 70 | +.. math:: |
| 71 | +
|
| 72 | + \boldsymbol{C}_{cr} = \begin{bmatrix} |
| 73 | + \boldsymbol{R} & \boldsymbol{0} \\ |
| 74 | + \boldsymbol{0} & \boldsymbol{R} |
| 75 | + \end{bmatrix} |
35 | 76 |
|
36 | | -Example |
| 77 | +where the upper-left block applies the rotation matrix :math:`\boldsymbol{R}` to translational degrees-of-freedom (1-3), and the lower-right block applies the same rotation matrix to rotational degrees-of-freedom (4-6). |
| 78 | + |
| 79 | +.. note:: |
| 80 | + |
| 81 | + **3D limitation:** The rotation option is only implemented for 3D problems (ndm=3, ndf=6) because Rodrigues' formula requires a 3D rotation vector, and the constraint matrix applies the rotation to all six degrees-of-freedom. |
| 82 | + |
| 83 | + **2D alternative:** For 2D problems requiring rotated constraints (e.g., skewed supports), use the penalty method with :ref:`zeroLength <zeroLength>` elements, as demonstrated in the :ref:`Penalty Method for Skewed Supports in 2D <zeroLength-penalty-2d>` example. |
| 84 | + |
| 85 | + **Rotational DOF approximation:** The same rotation matrix :math:`\boldsymbol{R}` is applied to both translational and rotational DOFs. While this is a proper coordinate transformation for translational DOFs, it is an approximation for rotational DOFs (treating rotations as vectors) that is valid for small rotations but may have limitations for large finite rotations. |
| 86 | + |
| 87 | +Examples |
37 | 88 | -------- |
| 89 | + |
| 90 | +Basic Constraint (No Rotation) |
| 91 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 92 | + |
| 93 | +This example demonstrates the basic usage of ``constrain`` without rotation, corresponding to the :ref:`Without Rotation <constrain-without-rotation>` theory section above. When no rotation is specified, the constraint matrix is the identity matrix :math:`\boldsymbol{C}_{cr} = \boldsymbol{I}`, making this equivalent to :ref:`equalDOF`. |
| 94 | + |
| 95 | +The following example constrains node **5** to have the same degrees-of-freedom as node **100**. This works for both 2D and 3D problems: |
| 96 | + |
| 97 | +.. tabs:: |
| 98 | + .. tab:: Tcl |
| 99 | + |
| 100 | + .. code-block:: none |
| 101 | +
|
| 102 | + constrain 100 5 |
| 103 | +
|
| 104 | + .. tab:: Python |
| 105 | + |
| 106 | + .. code-block:: python |
| 107 | +
|
| 108 | + model.constrain(100, 5) |
| 109 | +
|
| 110 | +Constraint with Rotation |
| 111 | +~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 112 | + |
| 113 | +The following example constrains node **5** to node **100** with a rotation about the Y-axis. This requires a 3D model (ndm=3, ndf=6): |
| 114 | + |
| 115 | +.. tabs:: |
| 116 | + .. tab:: Tcl |
| 117 | + |
| 118 | + .. code-block:: none |
| 119 | +
|
| 120 | + constrain 100 5 -rotate {0 -0.6435 0} |
| 121 | +
|
| 122 | + .. tab:: Python |
| 123 | + |
| 124 | + .. code-block:: python |
| 125 | +
|
| 126 | + angle = 0.5 # Rotation angle in radians (about 28.6 degrees) |
| 127 | + # Negative sign rotates clockwise about Y-axis (right-hand rule) |
| 128 | + model.constrain(100, 5, rotate=[0, -angle, 0]) |
| 129 | +
|
| 130 | +Frame with Skewed Support |
| 131 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 132 | + |
| 133 | +This example demonstrates using ``constrain`` to model a frame with a skewed support in 3D, where the support is not aligned with the global coordinate system. |
| 134 | + |
| 135 | +.. code-block:: python |
| 136 | + :linenos: |
| 137 | +
|
| 138 | + import xara |
| 139 | + from xara.units.english import inch, kip, ksi |
| 140 | + from shps.rotor import exp |
| 141 | +
|
| 142 | + # Geometry |
| 143 | + H = 144.0 * inch |
| 144 | + W = 144.0 * inch |
| 145 | +
|
| 146 | + # Section properties |
| 147 | + width = 12.0 * inch |
| 148 | + height = 12.0 * inch |
| 149 | + Iz = (width * height**3) / 12.0 |
| 150 | + Iy = (height * width**3) / 12.0 |
| 151 | + A = width * height |
| 152 | + J = (width * height**3) / 3.0 |
| 153 | +
|
| 154 | + # Material |
| 155 | + E = 29000 * ksi |
| 156 | + G = 11500 * ksi |
| 157 | + nu = 0.30 |
| 158 | +
|
| 159 | + # Load |
| 160 | + P = -10.0 * kip |
| 161 | + angle = 0.5 # Rotation angle in radians (about 28.6 degrees) |
| 162 | + # Negative sign rotates clockwise about Y-axis (right-hand rule), |
| 163 | + # rotating the support coordinate system clockwise from vertical |
| 164 | +
|
| 165 | + # Compute rotation matrix for reaction transformation |
| 166 | + R = exp([0.0, -angle, 0.0]) |
| 167 | +
|
| 168 | + # Model (3D required for rotation) |
| 169 | + model = xara.Model('basic', ndm=3, ndf=6) |
| 170 | +
|
| 171 | + # Nodes |
| 172 | + model.node(1, (0.0, 0.0, 0.0)) # Fixed support |
| 173 | + model.node(2, (0.0, 0.0, H)) # Top-left |
| 174 | + model.node(3, (W/2, 0.0, H)) # Load point |
| 175 | + model.node(4, (W, 0.0, H)) # Top-right |
| 176 | + model.node(5, (W, 0.0, 0.0)) # Skewed support |
| 177 | +
|
| 178 | + # Fixed support at node 1 |
| 179 | + model.fix(1, (1, 1, 1, 1, 1, 1)) |
| 180 | +
|
| 181 | + # Ground node for skewed support |
| 182 | + ground_node = 100 |
| 183 | + model.node(ground_node, (W, 0.0, 0.0)) |
| 184 | + model.fix(ground_node, (0, 1, 1, 0, 0, 0)) # Fixed in Y and Z, free in X |
| 185 | +
|
| 186 | + # Apply rotated constraint: node 5 is constrained to ground_node with rotation |
| 187 | + model.constrain((ground_node, 5), rotate=[0, -angle, 0]) |
| 188 | +
|
| 189 | + # Frame section and elements |
| 190 | + model.material('ElasticIsotropic', 1, E, nu) |
| 191 | + model.section("ElasticFrame", 1, E=E, G=G, A=A, Iy=Iy, Iz=Iz, J=J) |
| 192 | + model.geomTransf("Linear", 1, (0, 1, 0)) # Columns |
| 193 | + model.geomTransf("Linear", 2, (0, 1, 0)) # Beams |
| 194 | +
|
| 195 | + model.element('PrismFrame', 1, (1, 2), section=1, transform=1) |
| 196 | + model.element('PrismFrame', 2, (2, 3), section=1, transform=2) |
| 197 | + model.element('PrismFrame', 3, (3, 4), section=1, transform=2) |
| 198 | + model.element('PrismFrame', 4, (4, 5), section=1, transform=1) |
| 199 | +
|
| 200 | + # Load |
| 201 | + model.timeSeries('Constant', 1) |
| 202 | + model.pattern('Plain', 1, 1) |
| 203 | + model.load(3, (0.0, 0.0, P, 0.0, 0.0, 0.0)) |
| 204 | +
|
| 205 | + # Analysis |
| 206 | + model.system('BandGeneral') |
| 207 | + model.numberer('RCM') |
| 208 | + model.constraints('Transformation') |
| 209 | + model.integrator('LoadControl', 1.0) |
| 210 | + model.algorithm('Newton') |
| 211 | + model.test('Energy', 1e-8, 10) |
| 212 | + model.analysis('Static') |
| 213 | + model.analyze(1) |
| 214 | +
|
| 215 | + # Results |
| 216 | + model.reactions() |
| 217 | +
|
| 218 | + # Reaction at fixed support (node 1) |
| 219 | + Fz_jt1 = model.nodeReaction(1, 3) / kip |
| 220 | + print(f"Reaction at node 1 (Z-direction): {Fz_jt1:.3f} kip") |
| 221 | +
|
| 222 | + # Reaction at skewed support (node 5) - transform to rotated coordinate system |
| 223 | + R4 = model.nodeReaction(5)[:3] # Get translational reactions (X, Y, Z) |
| 224 | + r4 = R.T @ R4 / kip # Transform to rotated coordinate system |
| 225 | + F3_jt4 = r4[2] # Z-component in rotated coordinate system |
| 226 | + print(f"Reaction at skewed support (node 5, normal direction): {F3_jt4:.3f} kip") |
| 227 | +
|
| 228 | +References |
| 229 | +---------- |
| 230 | + |
| 231 | +* Cook, R.D., Malkus, D.S., Plesha, M. E., and Witt, R. J., "Concepts and Applications of Finite Element Analysis," 4th edition, John Wiley and Sons publishers, 2002. |
| 232 | + |
| 233 | +* `Rodrigues' rotation formula <https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula>`_ - Wikipedia article on Rodrigues' rotation formula. |
| 234 | + |
0 commit comments