Coverage for src/meshpy/four_c/material.py: 88%

77 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-28 04:21 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2025 MeshPy Authors 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining a copy 

6# of this software and associated documentation files (the "Software"), to deal 

7# in the Software without restriction, including without limitation the rights 

8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

9# copies of the Software, and to permit persons to whom the Software is 

10# furnished to do so, subject to the following conditions: 

11# 

12# The above copyright notice and this permission notice shall be included in 

13# all copies or substantial portions of the Software. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

21# THE SOFTWARE. 

22"""This file implements materials for 4C beams and solids.""" 

23 

24from meshpy.core.material import Material as _Material 

25from meshpy.core.material import MaterialBeam as _MaterialBeam 

26 

27 

28class MaterialReissner(_MaterialBeam): 

29 """Holds material definition for Reissner beams.""" 

30 

31 def __init__(self, shear_correction=1, **kwargs): 

32 super().__init__(material_string="MAT_BeamReissnerElastHyper", **kwargs) 

33 

34 # Shear factor for Reissner beam. 

35 self.shear_correction = shear_correction 

36 

37 def dump_to_list(self): 

38 """Return a list with the (single) item representing this material.""" 

39 if ( 

40 self.area is None 

41 and self.mom2 is None 

42 and self.mom3 is None 

43 and self.polar is None 

44 ): 

45 area, mom2, mom3, polar = self.calc_area_stiffness() 

46 elif ( 

47 self.area is not None 

48 and self.mom2 is not None 

49 and self.mom3 is not None 

50 and self.polar is not None 

51 ): 

52 area = self.area 

53 mom2 = self.mom2 

54 mom3 = self.mom3 

55 polar = self.polar 

56 else: 

57 raise ValueError( 

58 "Either all relevant material parameters are set " 

59 "by the user, or a circular cross-section will be assumed. " 

60 "A combination is not possible" 

61 ) 

62 

63 data = { 

64 "YOUNG": self.youngs_modulus, 

65 "POISSONRATIO": self.nu, 

66 "DENS": self.density, 

67 "CROSSAREA": area, 

68 "SHEARCORR": self.shear_correction, 

69 "MOMINPOL": polar, 

70 "MOMIN2": mom2, 

71 "MOMIN3": mom3, 

72 } 

73 if self.interaction_radius is not None: 

74 data["INTERACTIONRADIUS"] = self.interaction_radius 

75 return [{"MAT": self.i_global, self.material_string: data}] 

76 

77 

78class MaterialReissnerElastoplastic(MaterialReissner): 

79 """Holds elasto-plastic material definition for Reissner beams.""" 

80 

81 def __init__( 

82 self, 

83 *, 

84 yield_moment=None, 

85 isohardening_modulus_moment=None, 

86 torsion_plasticity=False, 

87 **kwargs, 

88 ): 

89 super().__init__(**kwargs) 

90 self.material_string = "MAT_BeamReissnerElastPlastic" 

91 

92 if yield_moment is None or isohardening_modulus_moment is None: 

93 raise ValueError( 

94 "The yield moment and the isohardening modulus for moments must be specified " 

95 "for plasticity." 

96 ) 

97 

98 self.yield_moment = yield_moment 

99 self.isohardening_modulus_moment = isohardening_modulus_moment 

100 self.torsion_plasticity = torsion_plasticity 

101 

102 def dump_to_list(self): 

103 """Return a list with the (single) item representing this material.""" 

104 super_list = super().dump_to_list() 

105 mat_dict = super_list[0][self.material_string] 

106 mat_dict["YIELDM"] = self.yield_moment 

107 mat_dict["ISOHARDM"] = self.isohardening_modulus_moment 

108 mat_dict["TORSIONPLAST"] = self.torsion_plasticity 

109 return super_list 

110 

111 

112class MaterialKirchhoff(_MaterialBeam): 

113 """Holds material definition for Kirchhoff beams.""" 

114 

115 def __init__(self, is_fad=False, **kwargs): 

116 super().__init__(material_string="MAT_BeamKirchhoffElastHyper", **kwargs) 

117 self.is_fad = is_fad 

118 

119 def dump_to_list(self): 

120 """Return a list with the (single) item representing this material.""" 

121 if ( 

122 self.area is None 

123 and self.mom2 is None 

124 and self.mom3 is None 

125 and self.polar is None 

126 ): 

127 area, mom2, mom3, polar = self.calc_area_stiffness() 

128 elif ( 

129 self.area is not None 

130 and self.mom2 is not None 

131 and self.mom3 is not None 

132 and self.polar is not None 

133 ): 

134 area = self.area 

135 mom2 = self.mom2 

136 mom3 = self.mom3 

137 polar = self.polar 

138 else: 

139 raise ValueError( 

140 "Either all relevant material parameters are set " 

141 "by the user, or a circular cross-section will be assumed. " 

142 "A combination is not possible" 

143 ) 

144 data = { 

145 "YOUNG": self.youngs_modulus, 

146 "SHEARMOD": self.youngs_modulus / (2.0 * (1.0 + self.nu)), 

147 "DENS": self.density, 

148 "CROSSAREA": area, 

149 "MOMINPOL": polar, 

150 "MOMIN2": mom2, 

151 "MOMIN3": mom3, 

152 "FAD": self.is_fad, 

153 } 

154 if self.interaction_radius is not None: 

155 data["INTERACTIONRADIUS"] = self.interaction_radius 

156 return [{"MAT": self.i_global, self.material_string: data}] 

157 

158 

159class MaterialEulerBernoulli(_MaterialBeam): 

160 """Holds material definition for Euler Bernoulli beams.""" 

161 

162 def __init__(self, **kwargs): 

163 super().__init__( 

164 material_string="MAT_BeamKirchhoffTorsionFreeElastHyper", **kwargs 

165 ) 

166 

167 def dump_to_list(self): 

168 """Return a list with the (single) item representing this material.""" 

169 area, mom2, _mom3, _polar = self.calc_area_stiffness() 

170 if self.area is None and self.mom2 is None: 

171 area, mom2, _mom3, _polar = self.calc_area_stiffness() 

172 elif self.area is not None and self.mom2 is not None: 

173 area = self.area 

174 mom2 = self.mom2 

175 else: 

176 raise ValueError( 

177 "Either all relevant material parameters are set " 

178 "by the user, or a circular cross-section will be assumed. " 

179 "A combination is not possible" 

180 ) 

181 data = { 

182 "YOUNG": self.youngs_modulus, 

183 "DENS": self.density, 

184 "CROSSAREA": area, 

185 "MOMIN": mom2, 

186 } 

187 return [{"MAT": self.i_global, self.material_string: data}] 

188 

189 

190class MaterialSolid(_Material): 

191 """Base class for a material for solids.""" 

192 

193 def __init__( 

194 self, material_string=None, youngs_modulus=-1.0, nu=0.0, density=0.0, **kwargs 

195 ): 

196 """Set the material values for a solid.""" 

197 super().__init__(**kwargs) 

198 

199 self.material_string = material_string 

200 self.youngs_modulus = youngs_modulus 

201 self.nu = nu 

202 self.density = density 

203 

204 def dump_to_list(self): 

205 """Return a list with the (single) item representing this material.""" 

206 

207 return [ 

208 { 

209 "MAT": self.i_global, 

210 self.material_string: { 

211 "YOUNG": self.youngs_modulus, 

212 "NUE": self.nu, 

213 "DENS": self.density, 

214 }, 

215 } 

216 ] 

217 

218 

219class MaterialStVenantKirchhoff(MaterialSolid): 

220 """Holds material definition for StVenant Kirchhoff solids.""" 

221 

222 def __init__(self, **kwargs): 

223 super().__init__(material_string="MAT_Struct_StVenantKirchhoff", **kwargs)