From ac748529b8e53aad3ece8aa7ba1ac384afacfa03 Mon Sep 17 00:00:00 2001 From: obouchaara Date: Thu, 13 Jun 2024 14:49:23 +0000 Subject: [PATCH] update displacement --- notebooks/symbolic/displacement.ipynb | 118 ++++++++++++++++------- src/mechpy/core/symbolic/displacement.py | 14 +-- src/mechpy/core/symbolic/field.py | 39 +++++--- tests/test_core.py | 30 ++++++ 4 files changed, 143 insertions(+), 58 deletions(-) diff --git a/notebooks/symbolic/displacement.ipynb b/notebooks/symbolic/displacement.ipynb index 604ccba..01bbe74 100644 --- a/notebooks/symbolic/displacement.ipynb +++ b/notebooks/symbolic/displacement.ipynb @@ -49,7 +49,7 @@ ], "source": [ "data = sp.Array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n", - "displacement_field = SymbolicDisplacement.create_linear(data)\n", + "displacement_field = SymbolicDisplacement.create_linear(data=data)\n", "display(displacement_field.data)" ] }, @@ -104,10 +104,34 @@ { "data": { "text/latex": [ - "$\\displaystyle \\left[\\begin{matrix}\\sqrt{x^{2} + y^{2}} & \\operatorname{atan}_{2}{\\left(y,x \\right)} & z\\end{matrix}\\right]$" + "$\\displaystyle \\sqrt{x^{2} + y^{2}}$" ], "text/plain": [ - "[sqrt(x**2 + y**2), atan2(y, x), z]" + "sqrt(x**2 + y**2)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$\\displaystyle \\operatorname{atan}_{2}{\\left(y,x \\right)}$" + ], + "text/plain": [ + "atan2(y, x)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$\\displaystyle z$" + ], + "text/plain": [ + "z" ] }, "metadata": {}, @@ -117,7 +141,7 @@ "source": [ "cartesian_system = SymbolicCartesianCoordSystem()\n", "coord_system = cartesian_system.to_cylindrical()\n", - "display(coord_system.basis_symbols)" + "display(*coord_system.basis)" ] }, { @@ -139,9 +163,9 @@ } ], "source": [ - "x1, x2, x3 = coord_system.basis_symbols\n", - "data = [x1*x1+x3, x2, x1+x3]\n", - "displacement_field = SymbolicDisplacement.create(data, cartesian_system)\n", + "x1, x2, x3 = coord_system.basis\n", + "data = sp.NDimArray([x1*x1+x3, x2, x1+x3])\n", + "displacement_field = SymbolicDisplacement(coord_system=cartesian_system, data=data)\n", "display(displacement_field.data)" ] }, @@ -194,7 +218,7 @@ { "data": { "text/plain": [ - "(r(x, y), theta(x), z)" + "(x(x, y), theta(x), z)" ] }, "metadata": {}, @@ -203,14 +227,13 @@ ], "source": [ "cartesian_system = SymbolicCartesianCoordSystem()\n", - "display(cartesian_system.basis_symbols)\n", - "x1_1, x2_1, x3_1 = cartesian_system.basis_symbols\n", + "display(cartesian_system.basis)\n", + "x1, x2, x3 = cartesian_system.basis\n", "cylindrical_system = SymbolicCylindricalCoordSystem()\n", - "display(cylindrical_system.basis_symbols)\n", - "x1_2, x2_2, x3_2 = cylindrical_system.basis_symbols\n", - "custom_basis_symbols = sp.Function(x1_2)(x1_1, x2_1), sp.Function(x2_2)(x1_1), x3_2\n", - "display(custom_basis_symbols)\n", - "custom_cylindrical_system = SymbolicCylindricalCoordSystem(custom_basis_symbols)" + "display(cylindrical_system.basis)\n", + "y1, y2, y3 = cylindrical_system.basis\n", + "custom_basis = sp.Function(x1)(x1, x2), sp.Function(y2)(x1), y3\n", + "display(custom_basis)" ] }, { @@ -219,42 +242,64 @@ "metadata": {}, "outputs": [ { - "ename": "ValueError", - "evalue": "The field data contains symbols not in the basis or field parameters: y, x", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[8], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m x1, x2, x3 \u001b[38;5;241m=\u001b[39m custom_cylindrical_system\u001b[38;5;241m.\u001b[39mbasis_symbols\n\u001b[1;32m 2\u001b[0m data \u001b[38;5;241m=\u001b[39m [x1\u001b[38;5;241m*\u001b[39mx1, x1\u001b[38;5;241m+\u001b[39mx2, x3\u001b[38;5;241m*\u001b[39mx3]\n\u001b[0;32m----> 3\u001b[0m displacement_field \u001b[38;5;241m=\u001b[39m \u001b[43mSymbolicDisplacement\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcustom_cylindrical_system\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m display(displacement_field\u001b[38;5;241m.\u001b[39mdata)\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/field.py:265\u001b[0m, in \u001b[0;36mSymbolicVectorField.create\u001b[0;34m(cls, data, coord_system, field_params)\u001b[0m\n\u001b[1;32m 261\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mConversion error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 263\u001b[0m data \u001b[38;5;241m=\u001b[39m sp\u001b[38;5;241m.\u001b[39mImmutableDenseNDimArray(data)\n\u001b[0;32m--> 265\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoord_system\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfield_params\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/displacement.py:10\u001b[0m, in \u001b[0;36mSymbolicDisplacement.__init__\u001b[0;34m(self, data, coord_system, field_params)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, data, coord_system\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, field_params\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoord_system\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfield_params\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/field.py:176\u001b[0m, in \u001b[0;36mSymbolicSpatialField.__init__\u001b[0;34m(self, data, coord_system, field_params)\u001b[0m\n\u001b[1;32m 173\u001b[0m data \u001b[38;5;241m=\u001b[39m sp\u001b[38;5;241m.\u001b[39mImmutableDenseNDimArray(data)\n\u001b[1;32m 175\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(data, (sp\u001b[38;5;241m.\u001b[39mExpr, sp\u001b[38;5;241m.\u001b[39mImmutableDenseNDimArray)):\n\u001b[0;32m--> 176\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoord_system\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfield_params\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 177\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 178\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInput data must be a SymPy Expr or SymPy Array\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/field.py:21\u001b[0m, in \u001b[0;36mSymbolicField.__init__\u001b[0;34m(self, data, coord_system, field_params)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcoord_system \u001b[38;5;241m=\u001b[39m coord_system\n\u001b[1;32m 20\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfield_params \u001b[38;5;241m=\u001b[39m field_params \u001b[38;5;129;01mor\u001b[39;00m {}\n\u001b[0;32m---> 21\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_field\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 23\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcoord system must be a SymbolicCoordSystem\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/field.py:30\u001b[0m, in \u001b[0;36mSymbolicField.validate_field\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mvalidate_field\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 29\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalidate_field_params()\n\u001b[0;32m---> 30\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_basis_symbols\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/mechpy/src/mechpy/core/symbolic/field.py:83\u001b[0m, in \u001b[0;36mSymbolicField.validate_basis_symbols\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 81\u001b[0m invalid_symbols \u001b[38;5;241m=\u001b[39m free_symbols \u001b[38;5;241m-\u001b[39m valid_symbols\n\u001b[1;32m 82\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m invalid_symbols:\n\u001b[0;32m---> 83\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 84\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe field data contains symbols not in the basis or field parameters: \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 85\u001b[0m \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m, \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(\u001b[38;5;28mstr\u001b[39m(symbol) \u001b[38;5;28;01mfor\u001b[39;00m symbol \u001b[38;5;129;01min\u001b[39;00m invalid_symbols)\n\u001b[1;32m 86\u001b[0m )\n", - "\u001b[0;31mValueError\u001b[0m: The field data contains symbols not in the basis or field parameters: y, x" - ] + "data": { + "text/plain": [ + "SymbolicCylindricalCoordSystem(origin=(0, 0, 0), basis=(x(x, y), theta(x), z))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}x^{2}{\\left(x,y \\right)} & \\theta{\\left(x \\right)} + x{\\left(x,y \\right)} & z^{2}\\end{matrix}\\right]$" + ], + "text/plain": [ + "[x(x, y)**2, theta(x) + x(x, y), z**2]" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "x1, x2, x3 = custom_cylindrical_system.basis_symbols\n", - "data = [x1*x1, x1+x2, x3*x3]\n", - "displacement_field = SymbolicDisplacement.create(data, custom_cylindrical_system)\n", + "custom_cylindrical_system = SymbolicCylindricalCoordSystem(basis=custom_basis)\n", + "display(custom_cylindrical_system)\n", + "x1, x2, x3 = custom_cylindrical_system.basis\n", + "data = sp.NDimArray([x1 * x1, x1 + x2, x3 * x3])\n", + "displacement_field = SymbolicDisplacement.create(\n", + " coord_system=custom_cylindrical_system,\n", + " data=data,\n", + " symbols_validation=False,\n", + ")\n", "display(displacement_field.data)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/latex": [ - "$\\displaystyle \\left[\\begin{matrix}2 r{\\left(x,y \\right)} \\frac{\\partial}{\\partial x} r{\\left(x,y \\right)} & \\frac{\\partial}{\\partial y} r{\\left(x,y \\right)} & 2 z\\\\\\frac{\\partial}{\\partial y} r{\\left(x,y \\right)} & r{\\left(x,y \\right)} \\frac{\\partial}{\\partial y} r{\\left(x,y \\right)} + \\frac{\\frac{\\partial}{\\partial x} r{\\left(x,y \\right)}}{2} + \\frac{\\frac{d}{d x} \\theta{\\left(x \\right)}}{2} & 0\\\\2 z & 0 & 0\\end{matrix}\\right]$" + "$\\displaystyle \\left[\\begin{matrix}2 x{\\left(x,y \\right)} \\frac{\\partial}{\\partial x} x{\\left(x,y \\right)} & \\frac{\\partial}{\\partial y} x{\\left(x,y \\right)} & 2 z & 2 x{\\left(x,y \\right)} \\frac{\\partial}{\\partial y} x{\\left(x,y \\right)} + \\frac{d}{d x} \\theta{\\left(x \\right)} + \\frac{\\partial}{\\partial x} x{\\left(x,y \\right)} & 0 & 0\\end{matrix}\\right]$" + ], + "text/plain": [ + "[2*x(x, y)*Derivative(x(x, y), x), Derivative(x(x, y), y), 2*z, 2*x(x, y)*Derivative(x(x, y), y) + Derivative(theta(x), x) + Derivative(x(x, y), x), 0, 0]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}2 x{\\left(x,y \\right)} \\frac{\\partial}{\\partial x} x{\\left(x,y \\right)} & \\frac{\\partial}{\\partial y} x{\\left(x,y \\right)} & 2 z\\\\\\frac{\\partial}{\\partial y} x{\\left(x,y \\right)} & x{\\left(x,y \\right)} \\frac{\\partial}{\\partial y} x{\\left(x,y \\right)} + \\frac{\\frac{d}{d x} \\theta{\\left(x \\right)}}{2} + \\frac{\\frac{\\partial}{\\partial x} x{\\left(x,y \\right)}}{2} & 0\\\\2 z & 0 & 0\\end{matrix}\\right]$" ], "text/plain": [ - "[[2*r(x, y)*Derivative(r(x, y), x), Derivative(r(x, y), y), 2*z], [Derivative(r(x, y), y), r(x, y)*Derivative(r(x, y), y) + Derivative(r(x, y), x)/2 + Derivative(theta(x), x)/2, 0], [2*z, 0, 0]]" + "[[2*x(x, y)*Derivative(x(x, y), x), Derivative(x(x, y), y), 2*z], [Derivative(x(x, y), y), x(x, y)*Derivative(x(x, y), y) + Derivative(theta(x), x)/2 + Derivative(x(x, y), x)/2, 0], [2*z, 0, 0]]" ] }, "metadata": {}, @@ -263,6 +308,7 @@ ], "source": [ "strain_tensor = displacement_field.strain_tensor(cartesian_system)\n", + "display(strain_tensor.data)\n", "display(strain_tensor.to_general().data)" ] } diff --git a/src/mechpy/core/symbolic/displacement.py b/src/mechpy/core/symbolic/displacement.py index 1c23daf..8e793a3 100644 --- a/src/mechpy/core/symbolic/displacement.py +++ b/src/mechpy/core/symbolic/displacement.py @@ -1,22 +1,22 @@ import sympy as sp -from .strain import SymbolicStrainTensor from .coord import SymbolicCartesianCoordSystem from .field import SymbolicVectorField +from .strain import SymbolicStrainTensor class SymbolicDisplacement(SymbolicVectorField): - def __init__(self, data, coord_system=None, field_params=None): - super().__init__(data, coord_system, field_params) + def __init__(self, coord_system, data, field_params=None, symbols_validation=True): + super().__init__(coord_system, data, field_params, symbols_validation) def __repr__(self): return f"SymbolicDisplacement(\n{self.data}\n)" def strain_tensor(self, coord_system=None) -> SymbolicStrainTensor: - if not coord_system: - coord_system = SymbolicCartesianCoordSystem() + if coord_system is None: + coord_system = self.coord_system - x1, x2, x3 = coord_system.basis_symbols + x1, x2, x3 = coord_system.basis u, v, w = self.data e_11 = sp.diff(u, x1) e_22 = sp.diff(v, x2) @@ -25,5 +25,5 @@ def strain_tensor(self, coord_system=None) -> SymbolicStrainTensor: e_23 = sp.diff(v, x3) + sp.diff(w, x2) e_31 = sp.diff(w, x1) + sp.diff(u, x3) components = [e_11, e_22, e_33, e_12, e_23, e_31] - strain_tensor = SymbolicStrainTensor.from_list(components, notation=2) + strain_tensor = SymbolicStrainTensor.from_list(components, notation="voigt") return strain_tensor diff --git a/src/mechpy/core/symbolic/field.py b/src/mechpy/core/symbolic/field.py index 6344866..ff2bda8 100644 --- a/src/mechpy/core/symbolic/field.py +++ b/src/mechpy/core/symbolic/field.py @@ -13,7 +13,7 @@ class SymbolicField: - def __init__(self, coord_system, data, field_params=None): + def __init__(self, coord_system, data, field_params=None, symbols_validation=True): """ Initialize a SymbolicField instance. @@ -47,23 +47,30 @@ def __init__(self, coord_system, data, field_params=None): "Field parameters must not overlap with coordinate system basis symbols." ) - invalid_symbols = self.get_invalid_symbols() - if len(invalid_symbols): - raise ValueError( - "The field data contains symbols not in the basis or field parameters: " - + ", ".join(str(s) for s in invalid_symbols) - ) + if symbols_validation: + invalid_symbols = self.get_invalid_symbols() + if len(invalid_symbols): + raise ValueError( + "The field data contains symbols not in the basis or field parameters: " + + ", ".join(str(s) for s in invalid_symbols) + ) def __repr__(self): return f"{self.__class__.__name__}(\n{self.coord_system.basis},\n{self.data},\n{self.field_params})" def get_invalid_symbols(self): + def get_ignored_symbols(data): + symbols_set = set() + # to implement + return symbols_set + basis = set(self.coord_system.basis) field_param = set(self.field_params) valid_symbols = basis.union(field_param) free_symbols = self.data.free_symbols free_symbols = {s for s in free_symbols if not isinstance(s, sp.Number)} - invalid_symbols = free_symbols - valid_symbols + ignored_symbols = get_ignored_symbols(self.data) + invalid_symbols = free_symbols - valid_symbols - ignored_symbols return invalid_symbols def subs_field_params(self, param_values): @@ -135,8 +142,8 @@ def subs(self, subs_dict, keys=False): class SymbolicSpatialField(SymbolicField): - def __init__(self, coord_system, data, field_params=None): - super().__init__(coord_system, data, field_params) + def __init__(self, coord_system, data, field_params=None, symbols_validation=True): + super().__init__(coord_system, data, field_params, symbols_validation) def lambdify(self): """ @@ -276,7 +283,7 @@ class SymbolicVectorField(SymbolicSpatialField): shape = (3,) @classmethod - def create(cls, coord_system=None, data=None, field_params=None): + def create(cls, coord_system=None, data=None, field_params=None, symbols_validation=True): if data is None: if coord_system is None: coord_system = SymbolicCartesianCoordSystem() @@ -290,16 +297,18 @@ def create(cls, coord_system=None, data=None, field_params=None): if coord_system is None: coord_system = SymbolicCartesianCoordSystem() try: - components = sp.NDimArray(data, shape=(3, 3)) - is_symbolic = lambda _: isinstance(_, (sp.Number, sp.Symbol, sp.Expr)) # to validation module + components = sp.NDimArray(data, shape=(3,)) + is_symbolic = lambda _: isinstance( + _, (sp.Number, sp.Symbol, sp.Expr) + ) # to validation module if not all(is_symbolic(_) for _ in components): raise ValueError("data type error") except: raise ValueError("Conversion error") - data = sp.NDimArray(data) + data = sp.NDimArray(data, shape=(3,)) - return cls(data, coord_system, field_params) + return cls(coord_system, data, field_params, symbols_validation) @classmethod def create_linear(cls, coord_system=None, data=None, field_params=None): diff --git a/tests/test_core.py b/tests/test_core.py index a20582a..38e6997 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -30,6 +30,10 @@ SymbolicStrainTensor, ) +from mechpy.core.symbolic.displacement import ( + SymbolicDisplacement, +) + class TestSymbolicCoordSystem(unittest.TestCase): def test_init(self): @@ -1089,5 +1093,31 @@ def test_volumetric_strain(self): pass +class TestSymbolicDisplacement(unittest.TestCase): + def test_init(self): + coord_system = SymbolicCartesianCoordSystem() + data = sp.NDimArray([1, 2, 3]) + displacement_field = SymbolicDisplacement(coord_system=coord_system, data=data) + self.assertEqual(displacement_field.data, data) + + def test_strain_tensor(self): + coord_system = SymbolicCartesianCoordSystem() + x1, x2, x3 = coord_system.basis + data = sp.NDimArray([x1, x2, x3]) + displacement_field = SymbolicDisplacement(coord_system=coord_system, data=data) + strain_tensor = displacement_field.strain_tensor() + self.assertEqual(strain_tensor.data, sp.NDimArray([1, 1, 1, 0, 0, 0])) + + data = sp.NDimArray([x2, x3, x1]) + displacement_field = SymbolicDisplacement(coord_system=coord_system, data=data) + strain_tensor = displacement_field.strain_tensor() + self.assertEqual(strain_tensor.data, sp.NDimArray([0, 0, 0, 1, 1, 1])) + + data = sp.NDimArray([x3, x2, x1]) + displacement_field = SymbolicDisplacement(coord_system=coord_system, data=data) + strain_tensor = displacement_field.strain_tensor() + self.assertEqual(strain_tensor.data, sp.NDimArray([0, 1, 0, 0, 0, 2])) + + if __name__ == "__main__": unittest.main()