diff --git a/notebooks/symbolic/material.ipynb b/notebooks/symbolic/material.ipynb index 6669733..4a28238 100644 --- a/notebooks/symbolic/material.ipynb +++ b/notebooks/symbolic/material.ipynb @@ -45,21 +45,7 @@ { "data": { "text/plain": [ - "SymbolicIsotropicMaterial(\n", - "{'youngs_modulus': E, 'poisson_ratio': nu}\n", - ")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "{'youngs_modulus': E,\n", - " 'poisson_ratio': nu,\n", - " 'mechanical_props': {'youngs_modulus': E, 'poisson_ratio': nu},\n", - " 'thermic_props': {}}" + "SymbolicIsotropicMaterial(E=E, nu=nu)" ] }, "metadata": {}, @@ -67,9 +53,9 @@ } ], "source": [ - "symbolic_isotropic_material = SymbolicIsotropicMaterial()\n", - "display(symbolic_isotropic_material)\n", - "display(symbolic_isotropic_material.__dict__)" + "E, nu = sp.symbols(\"E nu\")\n", + "symbolic_isotropic_material = SymbolicIsotropicMaterial(E=E, nu=nu)\n", + "display(symbolic_isotropic_material)" ] }, { @@ -110,22 +96,7 @@ { "data": { "text/plain": [ - "SymbolicIsotropicMaterial(\n", - "{'youngs_modulus': mu*(3*lamda + 2*mu)/(lamda + mu), 'poisson_ratio': lamda/(2*lamda + 2*mu)}\n", - ")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "{'lamda': lamda,\n", - " 'mu': mu,\n", - " 'mechanical_props': {'youngs_modulus': mu*(3*lamda + 2*mu)/(lamda + mu),\n", - " 'poisson_ratio': lamda/(2*lamda + 2*mu)},\n", - " 'thermic_props': {}}" + "SymbolicIsotropicMaterial(lamda=lamda, mu=mu)" ] }, "metadata": {}, @@ -135,8 +106,7 @@ "source": [ "lamda, mu = sp.symbols(\"lamda mu\")\n", "symbolic_isotropic_material = SymbolicIsotropicMaterial(lamda=lamda, mu=mu)\n", - "display(symbolic_isotropic_material)\n", - "display(symbolic_isotropic_material.__dict__)" + "display(symbolic_isotropic_material)" ] }, { @@ -191,9 +161,16 @@ { "data": { "text/plain": [ - "SymbolicTransverseIsotropicMaterial(\n", - "{'youngs_modulus_parallel': E_L, 'youngs_modulus_transverse': E_T, 'poisson_ratio': nu, 'shear_modulus_parallel': G_L, 'shear_modulus_transverse': G_T}\n", - ")" + "{'E_L': E_L, 'E_T': E_T, 'nu': nu, 'G_L': G_L, 'G_T': G_T}" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "SymbolicTransverseIsotropicMaterial(E_L=E_L, E_T=E_T, nu=nu, G_L=G_L, G_T=G_T)" ] }, "metadata": {}, @@ -201,7 +178,15 @@ } ], "source": [ - "symbolic_transverse_isotropic_material = SymbolicTransverseIsotropicMaterial()\n", + "material_props = {\n", + " \"E_L\": sp.symbols(\"E_L\"),\n", + " \"E_T\": sp.symbols(\"E_T\"),\n", + " \"nu\": sp.symbols(\"nu\"),\n", + " \"G_L\": sp.symbols(\"G_L\"),\n", + " \"G_T\": sp.symbols(\"G_T\"),\n", + "}\n", + "display(material_props)\n", + "symbolic_transverse_isotropic_material = SymbolicTransverseIsotropicMaterial(**material_props)\n", "display(symbolic_transverse_isotropic_material)" ] }, @@ -243,9 +228,24 @@ { "data": { "text/plain": [ - "SymbolicOrthotropicMaterial(\n", - "{'E1': E1, 'E2': E2, 'E3': E3, 'G12': G12, 'G23': G23, 'G31': G31, 'nu12': nu12, 'nu23': nu23, 'nu31': nu31}\n", - ")" + "{'E2': E2,\n", + " 'nu23': nu23,\n", + " 'G31': G31,\n", + " 'G12': G12,\n", + " 'nu12': nu12,\n", + " 'E3': E3,\n", + " 'G23': G23,\n", + " 'nu31': nu31,\n", + " 'E1': E1}" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "SymbolicOrthotropicMaterial(E1=E1, E2=E2, E3=E3, G12=G12, G23=G23, G31=G31, nu12=nu12, nu23=nu23, nu31=nu31)" ] }, "metadata": {}, @@ -253,7 +253,9 @@ } ], "source": [ - "symbolic_orthotropic_material = SymbolicOrthotropicMaterial()\n", + "material_props = {_:sp.symbols(_) for _ in SymbolicOrthotropicMaterial.props_keys}\n", + "display(material_props)\n", + "symbolic_orthotropic_material = SymbolicOrthotropicMaterial(**material_props)\n", "display(symbolic_orthotropic_material)" ] }, @@ -308,23 +310,8 @@ "outputs": [ { "data": { - "text/latex": [ - "$\\displaystyle {..}\\atop{E}$" - ], - "text/plain": [ - "{..}\\atop{E}" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/latex": [ - "$\\displaystyle \\nu^{°}$" - ], "text/plain": [ - "nu^°" + "{'E': {..}\\atop{E}, 'nu': nu^°}" ] }, "metadata": {}, @@ -333,9 +320,7 @@ { "data": { "text/plain": [ - "SymbolicIsotropicMaterial(\n", - "{'youngs_modulus': {..}\\atop{E}, 'poisson_ratio': nu^°}\n", - ")" + "SymbolicIsotropicMaterial(E={..}\\atop{E}, nu=nu^°)" ] }, "metadata": {}, @@ -343,11 +328,12 @@ } ], "source": [ - "E = sp.symbols(\"{..}\\\\atop{E}\")\n", - "nu = sp.symbols(\"nu^°\")\n", - "display(E)\n", - "display(nu)\n", - "symbolic_isotropic_material = SymbolicIsotropicMaterial(E, nu)\n", + "material_props = {\n", + " \"E\" : sp.symbols(\"{..}\\\\atop{E}\"),\n", + " \"nu\": sp.symbols(\"nu^°\")\n", + "}\n", + "display(material_props)\n", + "symbolic_isotropic_material = SymbolicIsotropicMaterial(**material_props)\n", "display(symbolic_isotropic_material)" ] }, @@ -373,53 +359,6 @@ "compliance_tensor = symbolic_isotropic_material.compliance_tensor()\n", "display(compliance_tensor.data)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "SymbolicTransverseIsotropicMaterial(\n", - "{'youngs_modulus_parallel': E_3, 'youngs_modulus_transverse': E_1, 'poisson_ratio': nu, 'shear_modulus_parallel': G_3, 'shear_modulus_transverse': G_1}\n", - ")" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/latex": [ - "$\\displaystyle \\left[\\begin{matrix}- \\frac{E_{3}}{\\nu^{2} - 1} & - \\frac{E_{3} \\nu}{\\nu - 1} & - \\frac{E_{3} \\nu}{\\nu - 1} & 0 & 0 & 0\\\\- \\frac{E_{3} \\nu}{\\nu - 1} & - \\frac{E_{3}}{\\nu^{2} - 1} & - \\frac{E_{3} \\nu}{\\nu - 1} & 0 & 0 & 0\\\\- \\frac{E_{3} \\nu}{\\nu - 1} & - \\frac{E_{3} \\nu}{\\nu - 1} & E_{1} & 0 & 0 & 0\\\\0 & 0 & 0 & G_{3} & 0 & 0\\\\0 & 0 & 0 & 0 & G_{3} & 0\\\\0 & 0 & 0 & 0 & 0 & G_{1}\\end{matrix}\\right]$" - ], - "text/plain": [ - "[[-E_3/(nu**2 - 1), -E_3*nu/(nu - 1), -E_3*nu/(nu - 1), 0, 0, 0], [-E_3*nu/(nu - 1), -E_3/(nu**2 - 1), -E_3*nu/(nu - 1), 0, 0, 0], [-E_3*nu/(nu - 1), -E_3*nu/(nu - 1), E_1, 0, 0, 0], [0, 0, 0, G_3, 0, 0], [0, 0, 0, 0, G_3, 0], [0, 0, 0, 0, 0, G_1]]" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "symbolic_transverse_isotropic_material = SymbolicTransverseIsotropicMaterial(\n", - " youngs_modulus_parallel=sp.symbols(\"E_3\"),\n", - " youngs_modulus_transverse=sp.symbols(\"E_1\"),\n", - " shear_modulus_parallel=sp.symbols(\"G_3\"),\n", - " shear_modulus_transverse=sp.symbols(\"G_1\"),\n", - ")\n", - "display(symbolic_transverse_isotropic_material)\n", - "display(symbolic_transverse_isotropic_material.stiffness_tensor().data)" - ] } ], "metadata": { diff --git a/src/mechpy/core/symbolic/material.py b/src/mechpy/core/symbolic/material.py index 65e44ab..0faed52 100644 --- a/src/mechpy/core/symbolic/material.py +++ b/src/mechpy/core/symbolic/material.py @@ -43,6 +43,12 @@ def __init__(self, **material_props): raise ValueError( "Material properties must include either both 'E' and 'nu' or both 'lamda' and 'mu'" ) + if {"E", "nu"} <= set(keys): + self.E = material_props.pop("E") + self.nu = material_props.pop("nu") + if {"lamda", "mu"} <= set(keys): + self.lamda = material_props.pop("lamda") + self.mu = material_props.pop("mu") super().__init__(**material_props) @staticmethod @@ -80,7 +86,7 @@ def stiffness_tensor(self, lames_param=True) -> SymbolicStiffnessTensor: [0, 0, 0, 0, 0, C_44], ] ) - return SymbolicStiffnessTensor(sp.simplify(sp.factor(C))) + return SymbolicStiffnessTensor(sp.simplify(C)) def compliance_tensor(self) -> SymbolicComplianceTensor: if hasattr(self, "E") and hasattr(self, "nu"): @@ -107,84 +113,84 @@ def compliance_tensor(self) -> SymbolicComplianceTensor: ] ) - return SymbolicComplianceTensor(sp.simplify(sp.factor(S))) + return SymbolicComplianceTensor(sp.simplify(S)) class SymbolicTransverseIsotropicMaterial(SymbolicElasticMaterial): - props_dict = { - "youngs_modulus_parallel": "E_L", - "youngs_modulus_transverse": "E_T", - "poisson_ratio": "nu", - "shear_modulus_parallel": "G_L", - "shear_modulus_transverse": "G_T", - } + props_keys = {"E_L", "E_T", "nu", "G_L", "G_T"} def __init__(self, **material_props): - # check the props - + keys = material_props.keys() + if not self.props_keys <= set(keys): + raise AttributeError(f"Material properties must include {self.props_keys}") + self.E_L = material_props.pop("E_L") + self.E_T = material_props.pop("E_T") + self.nu = material_props.pop("nu") + self.G_L = material_props.pop("G_L") + self.G_T = material_props.pop("G_T") super().__init__(**material_props) - # def stiffness_tensor(self) -> SymbolicStiffnessTensor: - # E_L = self.youngs_modulus_parallel - # E_T = self.youngs_modulus_transverse - # nu = self.poisson_ratio - # G_L = self.shear_modulus_parallel - # G_T = self.shear_modulus_transverse - - # C_11 = E_L / (1 - nu**2) - # C_12 = E_L * nu / (1 - nu) - # C_33 = E_T - # C_44 = G_L - # C_66 = G_T - - # C = sp.ImmutableDenseNDimArray( - # [ - # [C_11, C_12, C_12, 0, 0, 0], - # [C_12, C_11, C_12, 0, 0, 0], - # [C_12, C_12, C_33, 0, 0, 0], - # [0, 0, 0, C_44, 0, 0], - # [0, 0, 0, 0, C_44, 0], - # [0, 0, 0, 0, 0, C_66], - # ] - # ) - - # return SymbolicStiffnessTensor(sp.factor(sp.simplify(C))) + def stiffness_tensor(self) -> SymbolicStiffnessTensor: + E_L = self.E_L + E_T = self.E_T + nu = self.nu + G_L = self.G_L + G_T = self.G_T + + C_11 = E_L / (1 - nu**2) + C_12 = E_L * nu / (1 - nu) + C_33 = E_T + C_44 = G_L + C_66 = G_T + + C = sp.NDimArray( + [ + [C_11, C_12, C_12, 0, 0, 0], + [C_12, C_11, C_12, 0, 0, 0], + [C_12, C_12, C_33, 0, 0, 0], + [0, 0, 0, C_44, 0, 0], + [0, 0, 0, 0, C_44, 0], + [0, 0, 0, 0, 0, C_66], + ] + ) + + return SymbolicStiffnessTensor(sp.simplify(C)) class SymbolicOrthotropicMaterial(SymbolicElasticMaterial): - props_keys = [ - "E1", - "E2", - "E3", - "G12", - "G23", - "G31", - "nu12", - "nu23", - "nu31", - ] + props_keys = {"E1", "E2", "E3", "G12", "G23", "G31", "nu12", "nu23", "nu31"} def __init__(self, **material_props): - # check the props - + keys = material_props.keys() + if not self.props_keys <= set(keys): + raise AttributeError(f"Material properties must include {self.props_keys}") + self.E1 = material_props.pop("E1") + self.E2 = material_props.pop("E2") + self.E3 = material_props.pop("E3") + self.G12 = material_props.pop("G12") + self.G23 = material_props.pop("G23") + self.G31 = material_props.pop("G31") + self.nu12 = material_props.pop("nu12") + self.nu23 = material_props.pop("nu23") + self.nu31 = material_props.pop("nu31") super().__init__(**material_props) - # def stiffness_tensor(self) -> SymbolicStiffnessTensor: - # C11, C22, C33 = self.E1, self.E2, self.E3 - # C44, C55, C66 = self.G23, self.G31, self.G12 - # C12 = self.nu12 * C22 - # C13 = self.nu31 * C11 - # C23 = self.nu23 * C33 - - # C = sp.ImmutableDenseNDimArray( - # [ - # [C11, C12, C13, 0, 0, 0], - # [C12, C22, C23, 0, 0, 0], - # [C13, C23, C33, 0, 0, 0], - # [0, 0, 0, C44, 0, 0], - # [0, 0, 0, 0, C55, 0], - # [0, 0, 0, 0, 0, C66], - # ] - # ) - - # return SymbolicStiffnessTensor(sp.factor(sp.simplify(C))) + def stiffness_tensor(self) -> SymbolicStiffnessTensor: + C11, C22, C33 = self.E1, self.E2, self.E3 + C44, C55, C66 = self.G23, self.G31, self.G12 + C12 = self.nu12 * C22 + C13 = self.nu31 * C11 + C23 = self.nu23 * C33 + + C = sp.NDimArray( + [ + [C11, C12, C13, 0, 0, 0], + [C12, C22, C23, 0, 0, 0], + [C13, C23, C33, 0, 0, 0], + [0, 0, 0, C44, 0, 0], + [0, 0, 0, 0, C55, 0], + [0, 0, 0, 0, 0, C66], + ] + ) + + return SymbolicStiffnessTensor(sp.simplify(C)) diff --git a/tests/test_material.py b/tests/test_material.py index c80db45..45977cd 100644 --- a/tests/test_material.py +++ b/tests/test_material.py @@ -91,7 +91,7 @@ def test_stiffness_tensor(self): C_11 = lamda + 2 * mu C_12 = lamda C_44 = mu - expected_data = sp.factor( + expected_data = sp.sympify( sp.NDimArray( [ [C_11, C_12, C_12, 0, 0, 0], @@ -148,7 +148,7 @@ def test_compliance_tensor(self): ) ) self.assertEqual(tensor.data, expected_data) - + lamda, mu = sp.symbols("lamda mu") material = SymbolicIsotropicMaterial(lamda=lamda, mu=mu) tensor = material.compliance_tensor() @@ -171,5 +171,94 @@ def test_compliance_tensor(self): ) self.assertEqual(tensor.data, expected_data) + +class TestSymbolicTransverseIsotropicMaterial(unittest.TestCase): + def test_init(self): + cls = SymbolicTransverseIsotropicMaterial + material_props = {_: sp.symbols(_) for _ in cls.props_keys} + material = cls(**material_props) + for key in material_props.keys(): + self.assertEqual( + material.__getattribute__(key), + material_props[key], + ) + + def test_stiffness_tensor(self): + cls = SymbolicTransverseIsotropicMaterial + material_props = {_: sp.symbols(_) for _ in cls.props_keys} + material = cls(**material_props) + tensor = material.stiffness_tensor() + self.assertIsInstance(tensor, SymbolicStiffnessTensor) + + E_L = material_props["E_L"] + E_T = material_props["E_T"] + nu = material_props["nu"] + G_L = material_props["G_L"] + G_T = material_props["G_T"] + + C_11 = E_L / (1 - nu**2) + C_12 = E_L * nu / (1 - nu) + C_33 = E_T + C_44 = G_L + C_66 = G_T + + expected_data = sp.simplify( + sp.NDimArray( + [ + [C_11, C_12, C_12, 0, 0, 0], + [C_12, C_11, C_12, 0, 0, 0], + [C_12, C_12, C_33, 0, 0, 0], + [0, 0, 0, C_44, 0, 0], + [0, 0, 0, 0, C_44, 0], + [0, 0, 0, 0, 0, C_66], + ] + ) + ) + self.assertEqual(tensor.data, expected_data) + + +class TestSymbolicOrthotropicMaterial(unittest.TestCase): + def test_init(self): + cls = SymbolicOrthotropicMaterial + material_props = {_: sp.symbols(_) for _ in cls.props_keys} + material = cls(**material_props) + for key in material_props.keys(): + self.assertEqual( + material.__getattribute__(key), + material_props[key], + ) + + def test_stiffness_tensor(self): + cls = SymbolicOrthotropicMaterial + material_props = {_: sp.symbols(_) for _ in cls.props_keys} + material = cls(**material_props) + tensor = material.stiffness_tensor() + self.assertIsInstance(tensor, SymbolicStiffnessTensor) + + C11 = material_props["E1"] + C22 = material_props["E2"] + C33 = material_props["E3"] + C44 = material_props["G23"] + C55 = material_props["G31"] + C66 = material_props["G12"] + C12 = material_props["nu12"] * C22 + C13 = material_props["nu31"] * C11 + C23 = material_props["nu23"] * C33 + + expected_data = sp.sympify( + sp.NDimArray( + [ + [C11, C12, C13, 0, 0, 0], + [C12, C22, C23, 0, 0, 0], + [C13, C23, C33, 0, 0, 0], + [0, 0, 0, C44, 0, 0], + [0, 0, 0, 0, C55, 0], + [0, 0, 0, 0, 0, C66], + ] + ) + ) + self.assertEqual(tensor.data, expected_data) + + if __name__ == "__main__": unittest.main()