├── .gitignore ├── .spyproject ├── codestyle.ini ├── encoding.ini ├── vcs.ini └── workspace.ini ├── Algorithm ├── FEMAlgorithm.py ├── NewmarkAlgorithm.py ├── NewtonRaphson.py └── __init__.py ├── Element ├── AxisymmetricElement.py ├── FEMBoundary.py ├── FEMElement.py ├── PolarElement.py ├── QuadElement.py ├── TriangularElement.py └── __init__.py ├── InOut ├── FEMOutput.py ├── MatlabSupport.py └── __init__.py ├── LICENSE ├── Material ├── Material.py └── __init__.py ├── Math ├── IntegrationData.py ├── SingularIntegration.py ├── Solver.py ├── __init__.py └── injectionArray.py ├── Mesh ├── FEMMesh.py ├── FEMNode.py ├── MeshGenerator.py └── __init__.py ├── README.md ├── Tests ├── circular_magnet_1.py ├── circular_magnet_2.py ├── circular_magnet_3.py ├── circular_magnet_4.py ├── circular_magnet_post.py ├── circular_plate_buckling.py ├── circular_plate_buckling_1.py ├── circular_plate_buckling_2.py ├── circular_plate_buckling_3.py ├── magnetic_plate_buckling.py ├── symmetric_boundary_cartesian.py ├── symmetric_boundary_magnetic.py ├── test_boundary.py ├── test_induction_motor.py ├── test_induction_motor_no_move.py ├── test_magnetic.py ├── test_mesh.py ├── test_post_processing.py ├── test_post_processing_1.py └── test_structure.py ├── __init__.py └── plot_field.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # Profile # 30 | ############ 31 | *.profile 32 | 33 | # Python cache # 34 | ################ 35 | __pycache__/ 36 | 37 | # Input and Output data # 38 | ######################### 39 | *.dat 40 | 41 | # OS generated files # 42 | ###################### 43 | .DS_Store 44 | .DS_Store? 45 | ._* 46 | .Spotlight-V100 47 | .Trashes 48 | ehthumbs.db 49 | Thumbs.db 50 | -------------------------------------------------------------------------------- /.spyproject/codestyle.ini: -------------------------------------------------------------------------------- 1 | [codestyle] 2 | indentation = True 3 | 4 | [main] 5 | version = 0.1.0 6 | 7 | -------------------------------------------------------------------------------- /.spyproject/encoding.ini: -------------------------------------------------------------------------------- 1 | [encoding] 2 | text_encoding = utf-8 3 | 4 | [main] 5 | version = 0.1.0 6 | 7 | -------------------------------------------------------------------------------- /.spyproject/vcs.ini: -------------------------------------------------------------------------------- 1 | [vcs] 2 | use_version_control = False 3 | version_control_system = 4 | 5 | [main] 6 | version = 0.1.0 7 | 8 | -------------------------------------------------------------------------------- /.spyproject/workspace.ini: -------------------------------------------------------------------------------- 1 | [workspace] 2 | restore_data_on_startup = True 3 | save_data_on_exit = True 4 | save_history = True 5 | save_non_project_files = False 6 | 7 | [main] 8 | version = 0.1.0 9 | recent_files = ['/home/haiau/Documents/Python_projects/PyFEM/PyFEM/test_structure.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/test_boundary.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/test_magnetic.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/test_induction_motor_no_move.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/test_induction_motor.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/AxisymmetricElement.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMElement.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/QuadElement.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMAlgorithm.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/NewmarkAlgorithm.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/NewtonRaphson.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMOutput.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/MeshGenerator.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/Solver.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMMesh.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/Material.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMBoundary.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/FEMNode.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/injectionArray.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_plate_buckling.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_plate_buckling_2.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_plate_buckling_3.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/SingularIntegration.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/symmetric_boundary_cartesian.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_magnet_1.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_magnet_2.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/symmetric_boundary_magnetic.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_magnet_3.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_magnet_4.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/IntegrationData.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/PolarElement.py', '/home/haiau/Documents/Python_projects/PyFEM/PyFEM/circular_magnet_post.py'] 10 | 11 | -------------------------------------------------------------------------------- /Algorithm/FEMAlgorithm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 10 18:55:59 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import numpy as np 9 | import Mesh.FEMMesh as FM 10 | 11 | class Algorithm(object): 12 | """ 13 | Algorithm class stores properties and methods for algorithms 14 | """ 15 | def __init__(self, Mesh, timeOrder, outputs, solver, dtype = 'float64'): 16 | """ 17 | Initialize algorithm 18 | Input: 19 | Mesh: mesh to be analysed 20 | timeOrder: order of differential equation in time 21 | outputs: the outputs to write data 22 | solver: matrix solver 23 | """ 24 | self.dtype = dtype 25 | self.output = outputs 26 | if isinstance(outputs, (list, tuple)): 27 | self.outputPool = True 28 | else: 29 | self.outputPool = False 30 | self.solver = solver 31 | self.mesh = Mesh 32 | self.timeOrder = timeOrder 33 | for i in range(Mesh.getNnod()): 34 | if Mesh.getNodes()[i].getTimeOrder() != timeOrder: 35 | raise AlgorithmTimeOrderMismatch 36 | 37 | try: 38 | self.mesh.generateID() 39 | except FM.EmptyMesh: 40 | pass 41 | 42 | self.Neq = self.mesh.getNeq() 43 | self.NeqD = self.mesh.getNeqD() 44 | # Matrices and vectors 45 | # Stiffness matrix 46 | self.Kt = np.zeros((self.Neq,self.Neq),dtype) 47 | self.Ktd = np.zeros((self.Neq,self.NeqD),dtype) 48 | self.KtL = np.zeros((self.Neq,self.Neq),dtype) 49 | self.KtLd = np.zeros((self.Neq,self.NeqD),dtype) 50 | # Internal load vector 51 | self.Ri = np.zeros(self.Neq,dtype) 52 | self.Rid = np.zeros(self.NeqD,dtype) 53 | self.RiL = np.zeros(self.Neq,dtype) 54 | self.RiLd = np.zeros(self.NeqD,dtype) 55 | self.__tempRi__ = np.zeros(self.Neq,dtype) 56 | # External load vector 57 | self.Re = np.zeros(self.Neq,dtype) 58 | # Displacement 59 | self.U = np.zeros(self.Neq,dtype) 60 | self.Ud = np.zeros(self.NeqD,dtype) 61 | # Mass matrix 62 | self.M = None 63 | self.Md = None 64 | self.ML = None 65 | self.MLd = None 66 | # Damping matrix 67 | self.D = None 68 | self.Dd = None 69 | self.DL = None 70 | self.DLd = None 71 | # Velocity and acceleration vectors 72 | self.V = None 73 | self.A = None 74 | # If this is a dynamic problem, initialize D or M matrix 75 | if timeOrder > 0: 76 | self.D = np.zeros((self.Neq,self.Neq),dtype) 77 | self.Dd = np.zeros((self.Neq,self.NeqD),dtype) 78 | self.DL = np.zeros((self.Neq,self.Neq),dtype) 79 | self.DLd = np.zeros((self.Neq,self.NeqD),dtype) 80 | self.V = np.zeros(self.Neq,dtype) 81 | if timeOrder == 2: 82 | self.M = np.zeros((self.Neq,self.Neq),dtype) 83 | self.Md = np.zeros((self.Neq,self.NeqD),dtype) 84 | self.ML = np.zeros((self.Neq,self.Neq),dtype) 85 | self.MLd = np.zeros((self.Neq,self.NeqD),dtype) 86 | self.A = np.zeros(self.Neq) 87 | # Nonhomogeneous Dirichlet boundary condition 88 | self.NonhomogeneousDirichlet = False 89 | for node in self.mesh.getNodes(): 90 | if node.hasNonHomogeneousDirichlet(): 91 | self.NonhomogeneousDirichlet = True 92 | break 93 | 94 | self.istep = 0 95 | 96 | def getCurrentStep(self): 97 | """ 98 | Return current time step 99 | """ 100 | return self.istep 101 | 102 | def getMesh(self): 103 | """ 104 | Return the mesh 105 | """ 106 | return self.mesh 107 | 108 | def getTimeOrder(self): 109 | """ 110 | return time order 111 | """ 112 | return self.timeOrder 113 | 114 | def getRe(self): 115 | return self.Re 116 | 117 | def getRi(self): 118 | return self.Ri 119 | 120 | def getRid(self): 121 | return self.Rid 122 | 123 | def getRiL(self): 124 | return self.RiL 125 | 126 | def getRiLD(self): 127 | return self.RiLd 128 | 129 | def getKt(self): 130 | return self.Kt 131 | 132 | def getKtd(self): 133 | return self.Ktd 134 | 135 | def getD(self): 136 | return self.D 137 | 138 | def getDd(self): 139 | return self.Dd 140 | 141 | def getM(self): 142 | return self.M 143 | 144 | def getMd(self): 145 | return self.Md 146 | 147 | def getKtL(self): 148 | return self.KtL 149 | 150 | def getKtLd(self): 151 | return self.KtLd 152 | 153 | def getDL(self): 154 | return self.DL 155 | 156 | def getDLd(self): 157 | return self.DLd 158 | 159 | def getML(self): 160 | return self.ML 161 | 162 | def getMLd(self): 163 | return self.MLd 164 | 165 | def getU(self): 166 | return self.U 167 | 168 | def getV(self): 169 | return self.V 170 | 171 | def getA(self): 172 | return self.A 173 | 174 | def checkDirichletBC(self): 175 | """ 176 | check if system has nonhomogeneous Dirichlet boundary conditions 177 | """ 178 | return self.NonhomogeneousDirichlet 179 | 180 | def calculateExternalPointLoad(self): 181 | """ 182 | Calculate external point load and 183 | nonhomogeneous Dirichlet boundary conditions 184 | """ 185 | self.Re.fill(0.0) 186 | for node in self.mesh.getNodes(): 187 | node.addLoadTo(self.Re) 188 | if self.NonhomogeneousDirichlet: 189 | node.assembleGlobalDirichlet(self.Ud) 190 | 191 | def calculateExternalBodyLoad(self): 192 | for e in self.mesh.getElements(): 193 | try: 194 | e.calculateBodyLoad(self) 195 | except AttributeError: 196 | continue 197 | 198 | def prepareElements(self): 199 | for e in self.mesh.getElements(): 200 | try: 201 | e.prepareElement() 202 | except AttributeError: 203 | continue 204 | 205 | 206 | def updateValues(self): 207 | """ 208 | update global displacement, velocity and acceleration to every nodes 209 | """ 210 | for node in self.mesh.getNodes(): 211 | node.updateU(self.U) 212 | node.updateV(self.V) 213 | node.updateA(self.A) 214 | 215 | def connect(self): 216 | """ 217 | Connect global vectors and matrices to nodes so that the assembling 218 | processes can be ignored 219 | """ 220 | for n in self.mesh.getNodes(): 221 | n.connect(self.U,self.V,self.A,self.Ud) 222 | 223 | try: 224 | for e in self.mesh.getElements(): 225 | e.connect(self.Ri, self.Rid, self.Kt, self.Ktd,\ 226 | self.D, self.Dd, self.M, self.Md) 227 | except AttributeError: 228 | pass 229 | 230 | def calculateMatrices(self): 231 | """ 232 | calculate non linear parts of matrices required for calculation 233 | """ 234 | self.Kt.fill(0.0) 235 | self.Ktd.fill(0.0) 236 | if self.timeOrder == 2: 237 | self.M.fill(0.0) 238 | if self.timeOrder > 0: 239 | self.D.fill(0.0) 240 | self.Ri.fill(0.0) 241 | for element in self.mesh: 242 | if not element.isLinear(): 243 | element.calculate(self) 244 | 245 | def calculateLinearMatrices(self): 246 | """ 247 | calculate linear parts of matrices required for calculation 248 | """ 249 | self.KtL.fill(0.0) 250 | if self.timeOrder == 2: 251 | self.ML.fill(0.0) 252 | if self.timeOrder > 0: 253 | self.DL.fill(0.0) 254 | for i,element in enumerate(self.mesh): 255 | if not element.current: 256 | element.calculate(self,linear=True) 257 | try: 258 | self.mesh.calculateHarmonicsMatrix(self) 259 | except AttributeError: 260 | pass 261 | 262 | def calculateLinearCurrentMatrices(self): 263 | """ 264 | calculate linear parts of matrices at current configuration 265 | """ 266 | for i,element in enumerate(self.mesh): 267 | if element.current: 268 | element.calculate(self,linear=True) 269 | 270 | def addLinearMatrices(self): 271 | 272 | self.Kt += self.KtL 273 | self.RiL.fill(0.0) 274 | np.dot(self.KtL,self.U,self.RiL) 275 | try: 276 | self.D += self.DL 277 | np.dot(self.D,self.V,self.__tempRi__) 278 | self.RiL += self.__tempRi__ 279 | except: 280 | pass 281 | try: 282 | self.M += self.ML 283 | np.dot(self.M,self.A,self.__tempRi__) 284 | self.RiL += self.__tempRi__ 285 | except: 286 | pass 287 | self.Ri += self.RiL 288 | 289 | def outputData(self): 290 | """ 291 | write data to ouputs 292 | """ 293 | if self.outputPool: 294 | for ou in self.output: 295 | ou.outputData(self) 296 | else: 297 | self.output.outputData(self) 298 | 299 | def finishOutput(self): 300 | """ 301 | finish writing data to outputs 302 | """ 303 | if self.outputPool: 304 | for ou in self.output: 305 | ou.finishOutput() 306 | else: 307 | self.output.finishOutput() 308 | 309 | def calculate(self): 310 | """ 311 | start analysis 312 | """ 313 | pass 314 | 315 | class AlgorithmTimeOrderMismatch(Exception): 316 | """ 317 | Exception in case of time order of node is different than time order of 318 | algorithm 319 | """ 320 | pass 321 | 322 | class StaticAlgorithm(Algorithm): 323 | """ 324 | Static algorithm 325 | """ 326 | def __init__(self, Mesh, output, solver): 327 | Algorithm.__init__(self, Mesh, 0, output, solver) 328 | 329 | class DynamicAlgorithm(Algorithm): 330 | """ 331 | Dynamic algorithm 332 | """ 333 | def __init__(self, Mesh, timeOrder, output, solver,\ 334 | totalTime, numberTimeSteps): 335 | Algorithm.__init__(self, Mesh, timeOrder, output, solver) 336 | self.totalTime = totalTime 337 | self.numberSteps = numberTimeSteps 338 | self.deltaT = self.totalTime/self.numberSteps 339 | 340 | def getTime(self): 341 | """ 342 | Return the current time (of the problem) 343 | """ 344 | return self.istep*self.deltaT 345 | 346 | class LinearStaticAlgorithm(StaticAlgorithm): 347 | """ 348 | Linar static algorithm 349 | """ 350 | def getTime(self): 351 | """ 352 | Return lambda of current load step 353 | """ 354 | return 1.0 355 | 356 | def calculate(self): 357 | """ 358 | start analysis 359 | """ 360 | print("Start Analysis") 361 | self.prepareElements() 362 | self.connect() 363 | 364 | # calculate external point load 365 | self.U.fill(0.0) 366 | self.calculateExternalPointLoad() 367 | self.calculateExternalBodyLoad() 368 | 369 | self.calculateLinearMatrices() 370 | self.addLinearMatrices() 371 | # homogeneous Dirichlet Boundary Condition 372 | if self.checkDirichletBC(): 373 | self.Re -= np.dot(self.Ktd,self.Ud) 374 | 375 | # solve system 376 | self.U = self.solver.solve(self.Kt, self.Re) 377 | self.updateValues() 378 | # write data to output 379 | self.output.outputData(self) 380 | self.output.finishOutput() 381 | print("Finished!") 382 | 383 | 384 | class NotConverged(Exception): 385 | """ 386 | Exception for convergence 387 | """ 388 | pass -------------------------------------------------------------------------------- /Algorithm/NewmarkAlgorithm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Nov 12 18:36:10 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import Algorithm.FEMAlgorithm as FA 9 | import numpy as np 10 | 11 | class GeneralizedAlphaAlgorithm(FA.DynamicAlgorithm): 12 | """ 13 | Newmark algorithm 14 | """ 15 | def __init__(self, Mesh, timeOrder, output, solver,\ 16 | totalTime, numberTimeSteps, spectral_radius): 17 | """ 18 | Initialize Newmark Algorithm Object 19 | Input: 20 | Mesh: mesh of problem, Mesh object 21 | timeOrder: degree of differential equation in time, integer 22 | output: output of problem, FEMOutput object 23 | solver: matrix solver, Solver object 24 | totalTime: the final time instance, float 25 | numberTimeSteps: number of time steps, integer 26 | spectral_radius: spectral radius of method, float 0 0: 76 | return 77 | if self.point_load: 78 | for n in self.mesh.getNodes(): 79 | n.getPointLoadToGlobal(self.Re, self.deltaT*self.istep) 80 | 81 | def initialConditions(self): 82 | """ 83 | generate initial condition 84 | """ 85 | for node in self.mesh.getNodes(): 86 | node.assembleU(self.u_n) 87 | if self.timeOrder > 0: 88 | node.assembleV(self.v_n) 89 | if self.timeOrder == 2: 90 | node.assembleA(self.a_n) 91 | 92 | def calculateExternalBodyLoad(self): 93 | for e in self.mesh.getElements(): 94 | try: 95 | e.calculateBodyLoad(self) 96 | except AttributeError: 97 | continue 98 | if self.istep == 0: 99 | return 100 | self.Re *= (1.0-self.alpha_f) 101 | self.__tempR__ *= self.alpha_f 102 | self.Re += self.__tempR__ 103 | 104 | def NewmarkApproximation(self, vn1, an1): 105 | """ 106 | calculate Newmark approximation 107 | """ 108 | a = self.gamma/(self.beta*self.deltaT) 109 | b = (self.gamma - self.beta)/self.beta 110 | c = (self.gamma - 2*self.beta)*self.deltaT/(2.0*self.beta) 111 | d = 1.0/(self.beta*self.deltaT*self.deltaT) 112 | e = 1.0/(self.beta*self.deltaT) 113 | f = (1.0 - 2*self.beta)/(2.0*self.beta) 114 | 115 | if self.timeOrder > 0: 116 | np.copyto(vn1,self.u_n1) 117 | vn1 -= self.u_n 118 | vn1 *= a 119 | np.copyto(self.__tempR__,self.v_n) 120 | self.__tempR__ *= b 121 | vn1 -= self.__tempR__ 122 | 123 | if self.timeOrder == 2 and not an1 is None: 124 | np.copyto(an1,self.u_n1) 125 | an1 -= self.u_n 126 | an1 *= d 127 | np.copyto(self.__tempR__,self.v_n) 128 | self.__tempR__ *= e 129 | an1 -= self.__tempR__ 130 | 131 | np.copyto(self.__tempR__,self.a_n) 132 | self.__tempR__ *= c 133 | vn1 -= self.__tempR__ 134 | 135 | np.copyto(self.__tempR__,self.a_n) 136 | self.__tempR__ *= f 137 | an1 -= self.__tempR__ 138 | 139 | #return vn1,an1 140 | 141 | def midpointApproximation(self, vn1, an1): 142 | """ 143 | midpoint approximation 144 | Notice: this method mutate vn1 and an1! 145 | """ 146 | if self.timeOrder == 2 and not an1 is None: 147 | np.copyto(self.A, self.a_n) 148 | self.A -= an1 149 | self.A *= self.alpha_m 150 | self.A += an1 151 | 152 | if self.timeOrder > 0: 153 | np.copyto(self.V,self.v_n) 154 | self.V -= vn1 155 | self.V *= self.alpha_f 156 | self.V += vn1 157 | 158 | np.copyto(self.U,self.u_n) 159 | self.U -= self.u_n1 160 | self.U *= self.alpha_f 161 | self.U += self.u_n1 162 | 163 | def calculateKEffect(self): 164 | """ 165 | effective stiffness matrix 166 | """ 167 | self.Kt *= (1-self.alpha_f) 168 | a = (1-self.alpha_m)/(self.beta*self.deltaT*self.deltaT) 169 | if self.timeOrder == 2 and not self.M is None: 170 | np.copyto(self.__tempM__,self.M) 171 | self.__tempM__ *= a 172 | #self.Kt += a*self.M 173 | self.Kt += self.__tempM__ 174 | if self.timeOrder > 0: 175 | b = self.gamma*(1-self.alpha_f)/(self.beta*self.deltaT) 176 | np.copyto(self.__tempM__,self.D) 177 | self.__tempM__ *= b 178 | #self.Kt += b*self.D 179 | self.Kt += self.__tempM__ 180 | 181 | def calculateREffect(self): 182 | """ 183 | effective load vector 184 | """ 185 | self.Ri *= -1.0 186 | self.Ri += self.Re 187 | #if self.timeOrder > 0: 188 | # np.dot(self.D,self.V,self.__tempR__) 189 | # self.Ri -= self.__tempR__ 190 | #if self.timeOrder == 2: 191 | # np.dot(self.M,self.A,self.__tempR__) 192 | # self.Ri -= self.__tempR__ 193 | 194 | 195 | class LinearAlphaAlgorithm(GeneralizedAlphaAlgorithm): 196 | """ 197 | Linear Newmark Algorithm 198 | """ 199 | def calculateREffect(self): 200 | """ 201 | effective load vector 202 | """ 203 | a = self.gamma*(1.0-self.alpha_f)/(self.beta*self.deltaT) 204 | b = (self.gamma-self.gamma*self.alpha_f-self.beta)/(self.beta) 205 | c = (self.gamma-2.0*self.beta)*(1.0-self.alpha_f) 206 | c *= self.deltaT/(2.0*self.beta) 207 | d = (1.0-self.alpha_m)/(self.beta*self.deltaT*self.deltaT) 208 | e = (1.0-self.alpha_m)/(self.beta*self.deltaT) 209 | f = (1.0-self.alpha_m-2*self.beta)/(2.0*self.beta) 210 | 211 | v = a*self.u_n + b*self.v_n 212 | if self.timeOrder == 2: 213 | v += c*self.a_n 214 | a = d*self.u_n + e*self.v_n + f*self.a_n 215 | 216 | self.Ri.fill(0.0) 217 | self.Ri += self.Re 218 | self.Ri -= np.dot(self.Kt,self.alpha_f*self.u_n) 219 | self.Ri += np.dot(self.D,v) 220 | if self.timeOrder == 2: 221 | self.Ri += np.dot(self.M,a) 222 | 223 | def addLinearMatrices(self): 224 | 225 | # self.Kt.fill(0.0) 226 | # self.Kt += self.KtL 227 | np.copyto(self.Kt,self.KtL) 228 | 229 | try: 230 | # self.D.fill(0.0) 231 | # self.D += self.DL 232 | np.copyto(self.D,self.DL) 233 | except: 234 | pass 235 | try: 236 | # self.M.fill(0.0) 237 | # self.M += self.ML 238 | np.copyto(self.M,self.ML) 239 | except: 240 | pass 241 | 242 | def calculate(self): 243 | """ 244 | start analysis 245 | """ 246 | print("Start Analysis") 247 | self.prepareElements() 248 | self.connect() 249 | self.check_point_load() 250 | self.calculateParameters() 251 | self.initialConditions() 252 | np.copyto(self.u_n1,self.u_n) 253 | vn1 = np.empty(self.Neq) 254 | an1 = None 255 | if self.timeOrder == 2: 256 | an1 = np.empty(self.Neq) 257 | # self.generateExternalLoad() 258 | # self.calculateExternalBodyLoad() 259 | self.calculateLinearMatrices() 260 | if not self.mesh.current: 261 | self.addLinearMatrices() 262 | self.calculateKEffect() 263 | # loop over time steps 264 | for self.istep in range(1,self.numberSteps+1): 265 | print("Time step "+str(self.istep)) 266 | if self.mesh.current: 267 | self.calculateLinearCurrentMatrices() 268 | self.addLinearMatrices() 269 | self.calculateKEffect() 270 | self.generateExternalLoad() 271 | self.calculateExternalBodyLoad() 272 | self.calculateREffect() 273 | 274 | # self.NewmarkApproximation(vn1,an1) 275 | # self.midpointApproximation(vn1,an1) 276 | # solve system 277 | self.u_n1 = self.solver.solve(self.Kt,self.Ri) 278 | 279 | self.NewmarkApproximation(vn1,an1) 280 | np.copyto(self.U,self.u_n1) 281 | np.copyto(self.u_n,self.u_n1) 282 | try: 283 | np.copyto(self.v_n,vn1) 284 | np.copyto(self.V,vn1) 285 | np.copyto(self.a_n,an1) 286 | np.copyto(self.A,an1) 287 | except TypeError: 288 | pass 289 | self.outputData() 290 | print("Finished!") 291 | self.finishOutput() 292 | 293 | class NonlinearAlphaAlgorithm(GeneralizedAlphaAlgorithm): 294 | """ 295 | Nonlinear Newmark algorithm 296 | """ 297 | def __init__(self, Mesh, timeOrder, output, solver,\ 298 | totalTime, numberTimeSteps, spectrum_radius, maxiter = 200,\ 299 | tol = 1.0e-6, toltype = 0): 300 | """ 301 | Initialize Nonlinear Newmark Algorithm 302 | """ 303 | GeneralizedAlphaAlgorithm.__init__(self,Mesh,timeOrder,output,solver,\ 304 | totalTime,numberTimeSteps, spectrum_radius) 305 | self.Niter = maxiter 306 | self.tol = tol 307 | self.toltype = toltype 308 | self.eta = 100*tol 309 | 310 | def isConverged(self): 311 | """ 312 | Check if solution converged 313 | Return True if converged, False otherwise 314 | """ 315 | nrmdeltaU = np.linalg.norm(self.Ri) 316 | if self.toltype == 0: 317 | eta = nrmdeltaU/np.linalg.norm(self.u_n1-self.u_n) 318 | self.eta = eta 319 | print('eta = '+str(eta)) 320 | if (eta <= self.tol): 321 | return True 322 | # deltaU is too small, the nonconvergence is due to floating point 323 | # error 324 | nrmU = np.linalg.norm(self.u_n1) 325 | eta = nrmdeltaU/nrmU 326 | if (eta <= 1.0e-14): 327 | raise FloatingPointError 328 | return False 329 | 330 | def getMaxNumberIter(self): 331 | """ 332 | get maximum number of iterations 333 | """ 334 | return self.Niter 335 | 336 | def calculate(self): 337 | """ 338 | start analysis 339 | """ 340 | print("Start Analysis") 341 | self.prepareElements() 342 | self.connect() 343 | self.check_point_load() 344 | self.calculateParameters() 345 | self.initialConditions() 346 | np.copyto(self.u_n1,self.u_n) 347 | vn1 = np.empty(self.Neq) 348 | an1 = None 349 | if self.timeOrder == 2: 350 | an1 = np.empty(self.Neq) 351 | self.generateExternalLoad() 352 | self.calculateExternalBodyLoad() 353 | # loop over time steps 354 | self.calculateLinearMatrices() 355 | for self.istep in range(1,self.numberSteps+1): 356 | print("Time step "+str(self.istep)) 357 | self.generateExternalLoad() 358 | self.calculateExternalBodyLoad() 359 | self.calculateLinearCurrentMatrices() 360 | #print('Re',np.linalg.norm(self.Re)) 361 | # loop over iterations 362 | for iiter in range(self.Niter): 363 | self.NewmarkApproximation(vn1,an1) 364 | self.midpointApproximation(vn1,an1) 365 | #self.updateValues() 366 | self.calculateMatrices() 367 | self.addLinearMatrices() 368 | #print(np.linalg.norm(self.Ri)) 369 | self.calculateKEffect() 370 | self.calculateREffect() 371 | 372 | self.solver.isolve(self.Kt,self.Ri) 373 | self.u_n1 += self.Ri 374 | try: 375 | if self.isConverged(): 376 | break 377 | except FloatingPointError: 378 | self.eta = 0.0 379 | print("Increment is smaller than round off error") 380 | break 381 | if self.eta <= self.tol: 382 | print("Converged with eta = " + str(self.eta)) 383 | else: 384 | raise FA.NotConverged 385 | self.NewmarkApproximation(vn1,an1) 386 | np.copyto(self.U,self.u_n1) 387 | np.copyto(self.u_n,self.u_n1) 388 | try: 389 | np.copyto(self.v_n,vn1) 390 | np.copyto(self.V,vn1) 391 | np.copyto(self.a_n,an1) 392 | np.copyto(self.A,an1) 393 | except TypeError: 394 | pass 395 | self.outputData() 396 | print("Finished!") 397 | self.finishOutput() 398 | 399 | 400 | class LinearNewmarkAlgorithm(LinearAlphaAlgorithm): 401 | """ 402 | Newmark Algorithm 403 | alpha_f = 0.0 404 | alpha_m = 0.0 405 | beta = 0.5 406 | gamma = 0.5 407 | """ 408 | def calculateParameters(self): 409 | """ 410 | calculate parameters for alpha methods 411 | """ 412 | self.alpha_f = 0.0 413 | self.alpha_m = 0.0 414 | self.beta = 1.0/(self.rho_inf+1.0)/(self.rho_inf+1.0) 415 | self.gamma = (3.0-self.rho_inf)/(2.0*self.rho_inf+2.0) 416 | 417 | class NonlinearNewmarkAlgorithm(NonlinearAlphaAlgorithm): 418 | """ 419 | Newmark Algorithm 420 | alpha_f = 0.0 421 | alpha_m = 0.0 422 | beta = 0.5 423 | gamma = 0.5 424 | """ 425 | def calculateParameters(self): 426 | """ 427 | calculate parameters for alpha methods 428 | """ 429 | self.alpha_f = 0.0 430 | self.alpha_m = 0.0 431 | self.beta = 1.0/(self.rho_inf+1.0)/(self.rho_inf+1.0) 432 | self.gamma = (3.0-self.rho_inf)/(2.0*self.rho_inf+2.0) 433 | 434 | 435 | 436 | -------------------------------------------------------------------------------- /Algorithm/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /Element/AxisymmetricElement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 14 14:29:13 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import math 9 | import numpy as np 10 | import scipy.special as scs 11 | import Element.QuadElement as qa 12 | import Element.FEMBoundary as FB 13 | #import warnings 14 | 15 | class AxisymmetricQuadElement(qa.QuadElement): 16 | """ 17 | Axisymmetric Quadrilateral Element 18 | """ 19 | def __init__(self, Nodes, pd, basisFunction, nodeOrder, material, intData): 20 | """ 21 | Initialize an Axisymmetric Element 22 | Input: 23 | Nodes: nodes of elements 24 | pd: basis function degree(s), 25 | if number of dimension == 1, pd is an integer 26 | else, pd is an list 27 | basisFunction: basis function, see super class info 28 | nodeOrder: the order of nodes in elements, 29 | for 1 dimensional element, nodeOrder is an increasing array, 30 | for >1 dimensional elelement, nodeOrder is a n-dim-array 31 | material: material of element 32 | intData: integration data 33 | Raise: 34 | DimensionMismatch: if dimensions in Nodes is not equal 2 35 | """ 36 | for node in Nodes: 37 | if node.getNdim() != 2: 38 | raise DimensionMismatch 39 | 40 | qa.QuadElement.__init__(self,Nodes,pd,basisFunction,\ 41 | nodeOrder,material,intData) 42 | 43 | def getFactor(self): 44 | """ 45 | Return: factor for integration, 2*pi*radius*det(J) 46 | """ 47 | return 2*np.pi*self.x_[0]*self.factor[self.ig] 48 | # return self.factor[self.ig] 49 | 50 | class AxisymmetricStaticBoundary(FB.StandardStaticBoundary): 51 | """ 52 | Boundary of Axisymmetric element that is not moving during simulation 53 | """ 54 | def calculateGreen(self, x, xp): 55 | if np.allclose(x,xp,rtol=1.0e-13) or math.fabs(xp[0])<1.0e-14: 56 | raise FB.SingularPoint 57 | r = x[0] 58 | rp = xp[0] 59 | z = x[1] 60 | zp = xp[1] 61 | 62 | mt = (z-zp)**2 + (r+rp)**2 63 | m = 4*r*rp/mt 64 | self.G = 0.0 65 | self.gradG[0] = 0.0 66 | self.gradG[1] = 0.0 67 | mt = math.sqrt(mt)*np.pi 68 | kint = scs.ellipk(m) 69 | eint = scs.ellipe(m) 70 | 71 | 72 | if np.isnan(kint) or np.isnan(eint): 73 | raise Exception('False elliptic integral m = '+str(m)) 74 | self.G = rp*((2.0-m)*kint-2.0*eint)/(m*mt) 75 | self.gradG[0] = r*(2.0*m-4.0)*kint 76 | self.gradG[0] += r*(m*m-8.0*m+8.0)/(2.0*(1.0-m))*eint 77 | self.gradG[0] += rp*m*kint 78 | self.gradG[0] -= rp*m*(2.0-m)/(2.0*(1.0-m))*eint 79 | self.gradG[0] /= (math.sqrt(m)*(math.sqrt(r*rp)**3)) 80 | self.gradG[0] *= rp/(4.0*np.pi) 81 | 82 | self.gradG[1] = (2.0-m)/(1.0-m)*eint - 2.0*kint 83 | self.gradG[1] *= math.sqrt(m)*(z-zp)/(2.0*math.sqrt(r*rp)**3) 84 | self.gradG[1] *= rp/(4.0*np.pi) 85 | #raise Exception('False elliptic integral') 86 | 87 | def postCalculateF(self, N_, dN_, factor, res): 88 | idofA = 0 89 | idofJ = 1 90 | r = self.x_[0] 91 | k1 = self.u_[idofA]*\ 92 | (self.normv[0]*self.gradG[0]+self.normv[1]*self.gradG[1]) 93 | k1 *= factor 94 | k1 += self.u_[idofA]*self.G*self.normv[0]*factor/r 95 | k2 = self.u_[idofJ]*self.G 96 | k2 *= factor 97 | res += (k1 + k2) 98 | # k1 = self.u_[idofA]*np.dot(self.normv,self.gradG)*factor 99 | # k2 = -self.u_[idofJ]*self.G*factor 100 | # res += k1 + k2 101 | 102 | 103 | def subCalculateKLinear(self, K, element, i, j): 104 | #if np.allclose(element.xx_[0],0.0,rtol = 1.0e-14): 105 | # K.fill(0.0) 106 | # return 107 | idofA = 0 108 | idofJ = 1 109 | # wfac = self.getFactor()*2.0*np.pi*self.x_[0] 110 | wfac = self.getFactor() 111 | wfact = element.getFactorX(element.detJ) 112 | wfacx = element.getFactorXR(element.detJ) 113 | Nx_ = element.Nx_[j] 114 | K[idofJ,idofA] = self.N_[i]*Nx_*\ 115 | (element.normv[0]*self.gradG[0]+element.normv[1]*self.gradG[1]) 116 | K[idofJ,idofA] *= wfac*wfacx 117 | K[idofJ,idofA] += self.N_[i]*Nx_*self.G*\ 118 | element.normv[0]*wfac*wfact/element.xx_[0] 119 | # K[idofJ,idofA] += self.N_[i]*element.Nx_[j]*self.G*\ 120 | # element.normv[0]*wfac*wfact 121 | K[idofJ,idofJ] = self.N_[i]*Nx_*self.G 122 | K[idofJ,idofJ] *= wfac*wfact 123 | 124 | # K[idofJ,idofA] /= self.material.mu00 125 | # K[idofJ,idofJ] /= self.material.mu00 126 | # mu0 = self.material.mu00 127 | # r = self.x_[0] 128 | # K[idofA,idofJ] = self.N_[i]*element.Nx_[j] 129 | # K[idofA,idofJ] *= np.dot(element.normv,self.gradG) 130 | # K[idofA,idofJ] *= wfac*wfacx*2.0*np.pi*r/mu0 131 | # K[idofJ,idofA] = K[idofA,idofJ] 132 | # 133 | # K[idofA,idofA] = -np.einsum('i,ij,j',self.normv,self.grgrG,\ 134 | # element.normv) 135 | # K[idofA,idofA] *= self.N_[i]*element.Nx_[j] 136 | # K[idofA,idofA] *= wfac*wfacx*2.0*np.pi*r/mu0 137 | # 138 | # K[idofJ,idofJ] = -self.N_[i]*element.Nx_[j]*self.G 139 | # K[idofJ,idofJ] *= wfac*wfact*2.0*np.pi*r/mu0 140 | 141 | # def subCalculateR(self, R, element, i): 142 | # #if np.allclose(element.xx_[0],0.0,rtol = 1.0e-14): 143 | # # return 144 | # wfac = self.getFactor() 145 | # wfact = element.getFactorX(element.detJ) 146 | # wfacx = element.getFactorXR(element.detJ) 147 | # r0 = self.N_[i]*element.ux_[0]*\ 148 | # (element.normv[0]*self.gradG[0]+element.normv[1]*self.gradG[1]) 149 | # r0 *= wfac*wfacx 150 | # r0 += self.N_[i]*element.ux_[0]*self.G*\ 151 | # element.normv[0]*wfac*wfact/element.xx_[0] 152 | # r1 = self.N_[i]*element.ux_[1]*self.G 153 | # r1 *= wfac*wfact 154 | # R[1] += (r0 + r1) 155 | 156 | class AxiSymNeumann(FB.NeumannBoundary): 157 | """ 158 | Neumann boundary condition in Axisymmetric case 159 | """ 160 | def getFactor(self): 161 | """ 162 | Return: factor for integration, 2*pi*radius*det(J) 163 | """ 164 | return 2*np.pi*self.x_[0]*self.factor[self.ig] 165 | 166 | 167 | 168 | class DimensionMismatch(Exception): 169 | """ 170 | Exception for dimensions in node and element do not match 171 | """ 172 | pass -------------------------------------------------------------------------------- /Element/QuadElement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 10 14:19:01 2017 4 | 5 | @author: haiau 6 | """ 7 | import itertools as it 8 | import Element.FEMElement as FE 9 | import Element.PolarElement as PE 10 | import numpy as np 11 | import pylab as pl 12 | import math 13 | 14 | class QuadElement(FE.StandardElement): 15 | """ 16 | Quadrilateral Element 17 | Derived from standard element 18 | """ 19 | def basisND(self, x_, N_, dN_): 20 | """ 21 | n-dimensional basis functions, n = 1, n = 2 or n = 3 22 | n = self.Ndim 23 | """ 24 | if self.nodeOrder is None: 25 | self.nodeOrder = generateQuadNodeOrder(self.pd, self.Ndim) 26 | FE.StandardElement.basisND(self, x_, N_, dN_) 27 | 28 | 29 | class PolarQuadElement(PE.PolarElement,QuadElement): 30 | """ 31 | Quadrilateral Element in Polar Coordinates 32 | """ 33 | def plot(self, fig = None, col = '-b', fill_mat = False, number = None, \ 34 | deformed = False, deformed_factor=1.0): 35 | if fig is None: 36 | fig = pl.figure() 37 | # deformed structure not implemented 38 | 39 | X1 = self.Nodes[0].getX(self.loop[0]) 40 | X2 = self.Nodes[2].getX(self.loop[2]) 41 | xa1 = np.linspace(min(X1[1],X2[1]),max(X1[1],X2[1]),20) 42 | xa2 = np.linspace(min(X1[0],X2[0]),max(X1[0],X2[0]),20) 43 | pl.polar(xa1,xa2,col) 44 | 45 | X1 = self.Nodes[2].getX(self.loop[2]) 46 | X2 = self.Nodes[8].getX(self.loop[8]) 47 | xa1 = np.linspace(min(X1[1],X2[1]),max(X1[1],X2[1]),20) 48 | xa2 = np.linspace(min(X1[0],X2[0]),max(X1[0],X2[0]),20) 49 | pl.polar(xa1,xa2,col) 50 | 51 | X1 = self.Nodes[8].getX(self.loop[8]) 52 | X2 = self.Nodes[6].getX(self.loop[6]) 53 | xa1 = np.linspace(min(X1[1],X2[1]),max(X1[1],X2[1]),20) 54 | xa2 = np.linspace(min(X1[0],X2[0]),max(X1[0],X2[0]),20) 55 | pl.polar(xa1,xa2,col) 56 | 57 | X1 = self.Nodes[6].getX(self.loop[6]) 58 | X2 = self.Nodes[0].getX(self.loop[0]) 59 | xa1 = np.linspace(min(X1[1],X2[1]),max(X1[1],X2[1]),20) 60 | xa2 = np.linspace(min(X1[0],X2[0]),max(X1[0],X2[0]),20) 61 | pl.polar(xa1,xa2,col) 62 | 63 | nodes = self.Nodes 64 | for n in nodes: 65 | pl.polar(n.getX()[1],n.getX()[0],'.b') 66 | 67 | if number is not None: 68 | c = 0.5*(nodes[2].getX(self.loop[2])+nodes[6].getX(self.loop[6])) 69 | pl.text(c[1],c[0],str(number)) 70 | 71 | return fig, [nodes[0],nodes[2],nodes[8],nodes[6]] 72 | 73 | class LagrangeElement1D(QuadElement): 74 | """ 75 | 1-dimensional Lagrange Elment 76 | """ 77 | def __init__(self, Nodes, material, intData): 78 | """ 79 | Initialize 1-dimensional Lagrange element 80 | Input: 81 | Nodes: nodes of elements 82 | intData: integration data 83 | """ 84 | QuadElement.__init__(self,Nodes,len(Nodes)-1,\ 85 | LagrangeBasis1D,None,material,intData) 86 | 87 | 88 | class LagrangeElement2D(QuadElement): 89 | """ 90 | 2-dimensional Lagrange Elment 91 | """ 92 | def __init__(self, Nodes, pd, nodeOrder, material, intData): 93 | """ 94 | Initialize an Lagrange Element 95 | Input: 96 | Nodes: nodes of elements 97 | pd: basis function degree(s), 98 | nodeOrder: the order of nodes in elements, 99 | material: material of element 100 | intData: integration data 101 | """ 102 | QuadElement.__init__(self,Nodes,pd,\ 103 | LagrangeBasis1D,nodeOrder,material,intData) 104 | 105 | class Quad9Element(QuadElement): 106 | """ 107 | Quad9 element: quadrilateral element with 9 nodes 108 | """ 109 | def plot(self, fig = None, col = '-b', fill_mat = False, number = None, \ 110 | deformed = False, deformed_factor = 1.0): 111 | if fig is None: 112 | fig = pl.figure() 113 | 114 | dfact = deformed_factor 115 | if deformed: 116 | X1 = self.Nodes[0].getX() + dfact*self.Nodes[0].getU()[0:2] 117 | X2 = self.Nodes[2].getX() + dfact*self.Nodes[2].getU()[0:2] 118 | else: 119 | X1 = self.Nodes[0].getX() 120 | X2 = self.Nodes[2].getX() 121 | pl.plot(np.array([X1[0],X2[0]]),np.array([X1[1],X2[1]]),col) 122 | 123 | if not deformed: 124 | X1 = self.Nodes[2].getX() 125 | X2 = self.Nodes[8].getX() 126 | else: 127 | X1 = self.Nodes[2].getX() + dfact*self.Nodes[2].getU()[0:2] 128 | X2 = self.Nodes[8].getX() + dfact*self.Nodes[8].getU()[0:2] 129 | pl.plot(np.array([X1[0],X2[0]]),np.array([X1[1],X2[1]]),col) 130 | 131 | if not deformed: 132 | X1 = self.Nodes[8].getX() 133 | X2 = self.Nodes[6].getX() 134 | else: 135 | X1 = self.Nodes[8].getX() + dfact*self.Nodes[8].getU()[0:2] 136 | X2 = self.Nodes[6].getX() + dfact*self.Nodes[6].getU()[0:2] 137 | pl.plot(np.array([X1[0],X2[0]]),np.array([X1[1],X2[1]]),col) 138 | 139 | if not deformed: 140 | X1 = self.Nodes[6].getX() 141 | X2 = self.Nodes[0].getX() 142 | else: 143 | X1 = self.Nodes[6].getX() + dfact*self.Nodes[6].getU()[0:2] 144 | X2 = self.Nodes[0].getX() + dfact*self.Nodes[0].getU()[0:2] 145 | pl.plot(np.array([X1[0],X2[0]]),np.array([X1[1],X2[1]]),col) 146 | 147 | nodes = self.Nodes 148 | # if not deformed: 149 | # for n in nodes: 150 | # pl.plot(n.getX()[0],n.getX()[1],'.b') 151 | # else: 152 | # for n in nodes: 153 | # pl.plot(n.getX()[0]+dfact*n.getU()[0],n.getX()[1]+dfact*n.getU()[1],'.b') 154 | 155 | if number is not None: 156 | c = 0.5*(nodes[2].getX()+nodes[6].getX()) 157 | pl.text(c[0],c[1],str(number)) 158 | 159 | return fig, [nodes[0],nodes[2],nodes[8],nodes[6]] 160 | 161 | class Quad9Flat(Quad9Element): 162 | """ 163 | Flat Quad9 element: quadrilateral element with 9 nodes and all edges are 164 | straight lines. 165 | """ 166 | # def getXi(self, x, N_ = None, dN_ = None, xi = None, max_iter = 100,\ 167 | # rtol = 1.0e-8): 168 | # """ 169 | # Return natural coordinate xi corresponding to physical coordinate x 170 | # N_: array to store shape functions 171 | # dN_: array to store derivatives of shape functions 172 | # max_iter: maximum number of iterations for Newton method 173 | # Raise OutsideEdlement if x is not inside element. 174 | # """ 175 | # if x[0] > self.maxx and x[0] < self.minx\ 176 | # and x[1] > self.maxy and x[1] < self.miny: 177 | # raise FE.OutsideElement 178 | # 179 | # for i in range(9): 180 | # n1 = self.nodeOrder[0][i] 181 | # n2 = self.nodeOrder[1][i] 182 | # if n1 == 0 and n2 == 0: 183 | # X1 = self.Nodes[i].getX() 184 | # if n1 == 0 and n2 == 2: 185 | # X3 = self.Nodes[i].getX() 186 | # if n1 == 2 and n2 == 0: 187 | # X2 = self.Nodes[i].getX() 188 | # if n1 == 2 and n2 == 2: 189 | # X4 = self.Nodes[i].getX() 190 | # 191 | # A = X1[0] + X2[0] + X3[0] + X4[0] 192 | # B = X1[0] - X2[0] - X3[0] + X4[0] 193 | # C = -X1[0] + X2[0] - X3[0] + X4[0] 194 | # D = -X1[0] - X2[0] + X3[0] + X4[0] 195 | # E = X1[1] + X2[1] + X3[1] + X4[1] 196 | # F = X1[1] - X2[1] - X3[1] + X4[1] 197 | # G = -X1[1] + X2[1] - X3[1] + X4[1] 198 | # H = -X1[1] - X2[1] + X3[1] + X4[1] 199 | # 200 | # if xi is None: 201 | # xi = np.zeros(2,self.dtype) 202 | # 203 | # alpha = B*H + F*D 204 | # beta = B*E + F*x[0] - F*A - G*D + H*C 205 | # gamma = E*C + G*x[0] - G*A - x[1] 206 | # if math.fabs(alpha) < 1.0e-14: 207 | # if math.fabs(beta) < 1.0e-14: 208 | # raise FE.ElementError 209 | # xi[1] = -gamma/beta 210 | # else: 211 | # delta = beta*beta - 4.0*alpha*gamma 212 | # if delta < -1.0e-15: 213 | # raise FE.ElementError 214 | # 215 | # xi1 = (-beta + math.sqrt(delta))/(2.0*alpha) 216 | # xi2 = (-beta - math.sqrt(delta))/(2.0*alpha) 217 | # if math.fabs(xi1) > 1.0+1.0e-14: 218 | # xi[1] = xi1 219 | # elif math.fabs(xi2) > 1.0+1.0e-14: 220 | # xi[1] = xi2 221 | # else: 222 | # raise FE.OutsideElement 223 | # denom = x[1]*B + C 224 | # if np.isinf(denom): 225 | # raise FE.ElementError 226 | # if math.fabs(denom) < 1.0e-15: 227 | # raise FE.OutsideElement 228 | # xi[0] = (x[0] - A - D*xi[1])/denom 229 | # if math.fabs(xi[0]) > 1.0+1.0e-14: 230 | # raise FE.OutsideElement 231 | # 232 | # return xi 233 | 234 | 235 | 236 | def LagrangeBasis1D(x_, pd, N_ = None, dN_ = None, Order = None): 237 | """ 238 | Calculate Lagrange Basis functions and its derivatives 239 | Input: 240 | x_ : parametric coordinates 241 | pd : polynomial degree 242 | N_= None : create new array for N_ 243 | dN_ = None: create new array for dN_ 244 | Return: the updated arrays N_ and dN_ 245 | """ 246 | n = pd + 1 247 | if N_ is None: 248 | N_ = np.empty(n) 249 | if dN_ is None: 250 | dN_ = np.empty(n) 251 | 252 | if pd == 1: 253 | N_[1] = 0.5*(x_ + 1.0) 254 | N_[0] = 0.5*(1.0 - x_) 255 | dN_[0] = -0.5 256 | dN_[1] = 0.5 257 | elif pd == 2: 258 | N_[0] = 0.5*x_*(x_ - 1.0) 259 | N_[1] = 1.0 - x_*x_ 260 | N_[2] = 0.5*x_*(1.0 + x_) 261 | dN_[0] = x_ - 0.5 262 | dN_[1] = -2.0*x_ 263 | dN_[2] = 0.5 + x_ 264 | else: 265 | pass 266 | 267 | return N_, dN_ 268 | 269 | def RampBasis1D3N(x_, pd, N_ = None, dN_ = None, Order = None): 270 | """ 271 | Calculate Ramp Basis functions and its derivatives for 1D element with 272 | 3 nodes 273 | Input: 274 | x_ : parametric coordinates 275 | pd : polynomial degree (no effect here, because pd always equals 1) 276 | N_= None : create new array for N_ 277 | dN_ = None: create new array for dN_ 278 | Return: the updated arrays N_ and dN_ 279 | """ 280 | n = 2 + 1 281 | if N_ is None: 282 | N_ = np.empty(n) 283 | if dN_ is None: 284 | dN_ = np.empty(n) 285 | 286 | if x_ < 0.0 and x_ >= -1.0: 287 | N_[0] = -x_ 288 | N_[1] = x_ + 1.0 289 | N_[2] = 0.0 290 | dN_[0] = -1.0 291 | dN_[1] = 1.0 292 | dN_[2] = 0.0 293 | elif x_ == 0.0: 294 | N_[0] = 0.0 295 | N_[1] = 1.0 296 | N_[2] = 0.0 297 | dN_[0] = 0.0 298 | dN_[1] = 0.0 299 | dN_[2] = 0.0 300 | elif x_ > 0 and x_ <= 1.0: 301 | N_[0] = 0.0 302 | N_[1] = -x_ + 1.0 303 | N_[2] = x_ 304 | dN_[0] = 0.0 305 | dN_[1] = -1.0 306 | dN_[2] = 1.0 307 | 308 | def PulseBasis1D(x_, pd, N_ = None, dN_ = None, Order = None): 309 | n = pd + 1 310 | if N_ is None: 311 | N_ = np.empty(n) 312 | if dN_ is None: 313 | dN_ = np.empty(n) 314 | 315 | for i in range(n): 316 | N_[i] = 1.0 317 | dN_[i] = 0.0 318 | 319 | 320 | 321 | def generateQuadNodeOrder(pd, ndim, typeE = 'rcd'): 322 | """ 323 | Generate Node Order for Quadrilateral Element 324 | Input: 325 | pd: polynomial degree(s), array or scalar 326 | ndim: number of dimension, 1 <= ndim <= 3 327 | typeE = 'rc', default node in row-column order 328 | 'cr', column-row 329 | 'rcd', row-column-depth 330 | and so on 331 | Return node order list. 332 | """ 333 | if isinstance(pd, str): 334 | raise Exception('first parameters cannot be a string') 335 | if ndim == 1: 336 | try: 337 | p = pd[0] 338 | except (TypeError, IndexError): 339 | p = pd 340 | return list(range(p+1)) 341 | if ndim == 2: 342 | n1,n2 = pd[0] + 1,pd[1] + 1 343 | order = \ 344 | np.array(list(it.product(range(n1),range(n2)))).transpose().tolist() 345 | if typeE[0:2] == 'rc': 346 | return list(reversed(order)) 347 | elif typeE[0:2] == 'cr': 348 | return order 349 | raise Exception('Cannot generate node order of type '+str(typeE)) 350 | 351 | if ndim == 3: 352 | n1,n2,n3 = pd[0] + 1,pd[1] + 1,pd[2] + 1 353 | order = np.array(list(it.product(range(n1),range(n2),range(n3)))) 354 | order = order.transpose().tolist() 355 | if typeE == 'rcd': 356 | return order 357 | elif typeE == 'crd': 358 | return [order[1],order[0],order[2]] 359 | elif typeE == 'rdc': 360 | return [order[0],order[2],order[1]] 361 | elif typeE == 'cdr': 362 | return [order[1],order[2],order[0]] 363 | elif typeE == 'dcr': 364 | return [order[2],order[1],order[0]] 365 | elif typeE == 'drc': 366 | return [order[2],order[0],order[1]] 367 | raise Exception('Cannot generate node order of type '+str(typeE)) 368 | 369 | 370 | 371 | 372 | -------------------------------------------------------------------------------- /Element/TriangularElement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jan 29 16:22:19 2018 4 | 5 | @author: haiau 6 | """ 7 | 8 | import Element.FEMElement as FE 9 | import Math.IntegrationData as IntDat 10 | import numpy as np 11 | 12 | class T6Element(FE.StandardElement): 13 | """ 14 | T6 element (Triangular element with 6 nodes) 15 | The standard nodes order is following 16 | *1 17 | / \ 18 | / \ 19 | 5* *4 20 | / \ 21 | / \ 22 | 2*-----*6----*3 23 | """ 24 | def __init__(self, Nodes, material, dtype = 'float64',\ 25 | commonData = None, nG = 4): 26 | """ 27 | Initialize an Standard Element (in both 2-d and 3-d case) 28 | In 1-d case, this is a normal 1 dimensional element 29 | Input: 30 | Nodes: nodes of elements 31 | material: material of element 32 | dtype: datatype of arrays, default = 'float64' 33 | commonData: common data shared between elements 34 | nG: number of Gaussian quadrature points 35 | """ 36 | FE.StandardElement.__init__(self,Nodes,[2,2],None,None,material,\ 37 | TriangularGaussian(nG),dtype,commonData,2) 38 | 39 | def basisND(self, x_, N_, dN_): 40 | """ 41 | NOTICE: x_ will be area coordinates, i.e. size(x_) = 3 42 | n-dimensional basis functions, n = 1, n = 2 or n = 3 43 | n = self.Ndim 44 | """ 45 | sz = 6 46 | if N_ is None: 47 | N_ = np.empty(sz,self.dtype) 48 | if dN_ is None: 49 | dN_ = np.empty((self.Ndim,sz),self.dtype) 50 | 51 | N_[0] = 0.5*x_[0]*(x_[0]+1.0) 52 | dN_[0,0] = -(x_[0]+0.5) 53 | dN_[1,0] = dN_[0,0] 54 | N_[1] = 0.5*x_[1]*(x_[1]+1.0) 55 | dN_[0,1] = x_[1]+0.5 56 | dN_[1,1] = 0.0 57 | N_[2] = 0.5*x_[2]*(x_[2]+1.0) 58 | dN_[1,2] = x_[2]+0.5 59 | dN_[0,2] = 0.0 60 | N21eta = 0.5*(1.0+x_[0]) 61 | dN21eta = 0.5 62 | N21xi1 = 0.5*(1.0+x_[1]) 63 | dN21xi1 = 0.5 64 | N21xi2 = 0.5*(1.0+x_[2]) 65 | dN21xi2 = 0.5 66 | N_[3] = 4.0*N21eta*N21xi2 67 | dN_[0,3] = -4.0*dN21eta*N21xi2 68 | dN_[1,3] = -4.0*dN21eta*N21xi2 + 4.0*N21eta*dN21xi2 69 | N_[4] = 4.0*N21eta*N21xi1 70 | dN_[0,4] = -4.0*dN21eta*N21xi1 + 4.0*N21eta*dN21xi1 71 | dN_[1,4] = -4.0*dN21eta*N21xi1 72 | N_[5] = 4.0*N21xi1*N21xi2 73 | dN_[0,5] = 4.0*dN21xi1*N21xi2 74 | dN_[1,5] = 4.0*N21xi1*dN21xi2 75 | 76 | return N_, dN_ 77 | 78 | class TriangularGaussian(IntDat.GaussianQuadrature): 79 | """ 80 | Integration Data for Triangular element 81 | """ 82 | def __init__(self, npoint): 83 | assert npoint==1 or npoint==3 or npoint==4 or npoint==7,\ 84 | 'unsupport number of points' 85 | IntDat.GaussianQuadrature.__init__(self,npoint,3,None) 86 | self.xg = np.zeros((3,npoint),dtype='float64') 87 | self.wg = np.zeros(npoint,dtype='float64') 88 | if npoint == 1: 89 | self.xg[:,0] = [1.0/3.0, 1.0/3.0, 1.0/3.0] 90 | self.wg[0] = 1.0 91 | elif npoint == 3: 92 | self.xg[:,0] = [0.5, 0.5, 0.0] 93 | self.wg[0] = 1.0/3.0 94 | self.xg[:,1] = [0.5, 0.0, 0.5] 95 | self.wg[1] = 1.0/3.0 96 | self.xg[:,2] = [0.0, 0.5, 0.5] 97 | self.wg[2] = 1.0/3.0 98 | elif npoint == 4: 99 | self.xg[:,0] = [1.0/3.0, 1.0/3.0, 1.0/3.0] 100 | self.wg[0] = -27.0/48.0 101 | self.xg[:,1] = [0.6, 0.2, 0.2] 102 | self.wg[1] = 25.0/48.0 103 | self.xg[:,2] = [0.2, 0.6, 0.2] 104 | self.wg[2] = 25.0/48.0 105 | self.xg[:,3] = [0.2, 0.2, 0.6] 106 | self.wg[3] = 25.0/48.0 107 | elif npoint == 7: 108 | a1 = 0.0597158717 109 | b1 = 0.4701420641 110 | a2 = 0.7974269853 111 | b2 = 0.1012865073 112 | self.xg[:,0] = [1.0/3.0, 1.0/3.0, 1.0/3.0] 113 | self.wg[0] = 0.225 114 | self.xg[:,1] = [a1, b1, b1] 115 | self.wg[1] = 0.1323941527 116 | self.xg[:,2] = [b1, a1, b1] 117 | self.wg[2] = 0.1323941527 118 | self.xg[:,3] = [b1, b1, a1] 119 | self.wg[3] = 0.1323941527 120 | self.xg[:,4] = [a2, b2, b2] 121 | self.wg[4] = 0.1259391805 122 | self.xg[:,5] = [b2, a2, b2] 123 | self.wg[5] = 0.1259391805 124 | self.xg[:,6] = [b2, b2, a2] 125 | self.wg[6] = 0.1259391805 126 | 127 | self.xg *= 2.0 128 | self.xg -= 1.0 129 | -------------------------------------------------------------------------------- /Element/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /InOut/FEMOutput.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Nov 12 17:52:32 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import sys 9 | import numpy as np 10 | import Mesh.FEMMesh as fm 11 | import warnings 12 | 13 | class FEMOutput(object): 14 | """ 15 | Output data 16 | """ 17 | def __init__(self): 18 | """ 19 | Initialize FEMOutput object 20 | """ 21 | pass 22 | 23 | def outputData(self, data): 24 | """ 25 | write data to output file 26 | """ 27 | pass 28 | 29 | def finishOutput(self): 30 | """ 31 | Finish output data 32 | Close all files or return all objects 33 | """ 34 | 35 | class FileOutput(FEMOutput): 36 | """ 37 | Output to file 38 | """ 39 | def __init__(self, outfile, p = "w"): 40 | """ 41 | Initialize FileOutput object and open file 42 | Input: 43 | outfile: output filename 44 | """ 45 | self.outfile = outfile 46 | try: 47 | self.file = open(self.outfile, p) 48 | except IOError: 49 | sys.exit('Cannot open file '+self.outfile) 50 | 51 | def finishOutput(self): 52 | """ 53 | Finish output data 54 | Close output file 55 | """ 56 | self.file.close() 57 | 58 | class HarmonicsOutput(FileOutput): 59 | """ 60 | Output harmonics paramereters to file 61 | """ 62 | def outputData(self, data): 63 | """ 64 | write data to output file 65 | """ 66 | try: 67 | if data.mesh.nHarmonic < 0: 68 | return 69 | except AttributeError: 70 | return 71 | outstr = 'Time '+str(data.getTime())+' NH '+str(data.mesh.nHarmonic) 72 | self.file.write(outstr+'\n') 73 | for i in range(data.mesh.nHarmonic): 74 | outstr = str(data.U[data.mesh.harmonicsIDa[i]])+' ' 75 | outstr += str(data.U[data.mesh.harmonicsIDb[i]])+' ' 76 | outstr += str(data.U[data.mesh.harmonicsIDc[i]])+' ' 77 | outstr += str(data.U[data.mesh.harmonicsIDd[i]])+'\n' 78 | self.file.write(outstr) 79 | 80 | @staticmethod 81 | def __read_hdr__(file): 82 | hdr = file.readline() 83 | ex = Exception('The file is corrupted or false formatted!') 84 | if hdr == '': 85 | raise EOFError 86 | hdr = hdr.split() 87 | if hdr[0] != 'Time': 88 | raise ex 89 | try: 90 | time = float(hdr[1]) 91 | except Exception: 92 | raise ex 93 | if hdr[2] != 'NH': 94 | raise ex 95 | try: 96 | numHar = int(hdr[3]) 97 | except Exception: 98 | raise ex 99 | return time,numHar 100 | 101 | @staticmethod 102 | def __nextStep__(file, step, prev = -1): 103 | for i in range(step-prev-1): 104 | time,numHar = HarmonicsOutput.__read_hdr__(file) 105 | for har in range(numHar): 106 | file.readline() 107 | 108 | 109 | @staticmethod 110 | def __readOneStep__(file): 111 | time,numHar = HarmonicsOutput.__read_hdr__(file) 112 | res = [] 113 | for har in range(numHar): 114 | try: 115 | dat = file.readline().split() 116 | except AttributeError: 117 | break 118 | res.append(np.array([float(a) for a in dat])) 119 | return time, res 120 | 121 | @staticmethod 122 | def readOutput(filen, timeStep = 0): 123 | """ 124 | This function read the ouput file that was produced by 125 | this output class 126 | Input: 127 | filen: name of output file 128 | timeStep: 129 | scalar: read the specific time step 130 | list, tuple: read the time steps in list or tuple 131 | 'all': read all time steps 132 | Return: 133 | list of numpy array 134 | """ 135 | __max_allowed_steps__ = 1000000 136 | file = open(filen,'r') 137 | 138 | alltime = True and timeStep == 'all' 139 | res = [] 140 | time = [] 141 | if alltime: 142 | i = 0 143 | while i < __max_allowed_steps__: 144 | i += 1 145 | try: 146 | t,r = HarmonicsOutput.__readOneStep__(file) 147 | time.append(t) 148 | res.append(r) 149 | except EOFError: 150 | file.close() 151 | return time, res 152 | file.close() 153 | return time,res 154 | else: 155 | asc = isAscending(timeStep) 156 | prev = -1 157 | for step in timeStep: 158 | if not asc: 159 | file.seek(0) 160 | try: 161 | HarmonicsOutput.__nextStep__(file,step,prev) 162 | t,r = HarmonicsOutput.__readOneStep__(file) 163 | except EOFError: 164 | file.close() 165 | return time, res 166 | time.append(t) 167 | res.append(r) 168 | if asc: 169 | prev = step 170 | file.close() 171 | return time, res 172 | 173 | 174 | 175 | 176 | class StandardFileOutput(FileOutput): 177 | """ 178 | This object class gives the output in simple standard format. 179 | The format of the output file is following: 180 | Each time step begin with 181 | NNOD data.getNnod() TIME data.getTime() ORDER data.getTimeOrder() 182 | and follow by NNOD rows, each row is data of one node with format: 183 | Ndof X[0] X[1] X[2] U[0]...U[Ndof-1] V[0] ... V[Ndof-1] A[0] ... A[Ndof-1] 184 | if data.getTimeOrder() == 0, there is no V and A 185 | 1, A 186 | """ 187 | def chooseSteps(self, steps = 'all'): 188 | """ 189 | Choose time steps to be output 190 | steps = 'all' : output all steps 191 | otherwise: output all in list, tuple, iterator, generator steps 192 | """ 193 | if steps == 'all': 194 | self.allsteps = True 195 | else: 196 | self.allsteps = False 197 | self.steps = steps 198 | 199 | def writeX(self, node): 200 | self.file.write(str(node.getX()[0])+' ') 201 | if node.getNdim() > 1: 202 | self.file.write(str(node.getX()[1])+' ') 203 | if node.getNdim() == 3: 204 | self.file.write(str(node.getX()[2])+' ') 205 | else: 206 | self.file.write('0.0 ') 207 | else: 208 | self.file.write('0.0 0.0 ') 209 | 210 | def writeU(self, node): 211 | for i in range(node.getNdof()): 212 | self.file.write(str(node.getU()[i])+' ') 213 | 214 | def writeV(self, node): 215 | for i in range(node.getNdof()): 216 | self.file.write(str(node.getV()[i])+' ') 217 | 218 | def writeA(self, node): 219 | for i in range(node.getNdof()): 220 | self.file.write(str(node.getA()[i])+' ') 221 | 222 | def writeHeader(self, data): 223 | self.file.write('NNOD '+str(data.getMesh().getNnod())) 224 | if data.getTimeOrder() > 0: 225 | self.file.write(' TIME '+str(data.getTime())+' ORDER '+\ 226 | str(data.getTimeOrder())+'\n') 227 | else: 228 | try: 229 | self.file.write(' LOAD '+str(data.getLambda())+' ORDER '+\ 230 | str(data.getTimeOrder())+'\n') 231 | except AttributeError: 232 | self.file.write(' TIME 0.0 ORDER 0\n') 233 | 234 | def outputData(self, data): 235 | """ 236 | write data to output file 237 | """ 238 | try: 239 | if not self.allsteps: 240 | if data.getCurrentStep() not in self.steps: 241 | return 242 | except: 243 | self.allsteps = True 244 | 245 | self.writeHeader(data) 246 | nodes = data.getMesh().getNodes() 247 | ndof = nodes[0].getNdof() 248 | for i in range(data.getMesh().getNnod()): 249 | node = nodes[i] 250 | assert node.getNdof() == ndof,'Incompatible DOFs between nodes' 251 | self.file.write(str(node.getNdof())+' ') 252 | self.writeX(node) 253 | self.writeU(node) 254 | if data.getTimeOrder() > 0: 255 | self.writeV(node) 256 | if data.getTimeOrder() == 2: 257 | self.writeA(node) 258 | self.file.write('\n') 259 | 260 | @staticmethod 261 | def readHeader(file): 262 | hdr = file.readline() 263 | ex = Exception('The file is corrupted or false formatted!') 264 | if hdr == '': 265 | raise EOFError 266 | hdr = hdr.split() 267 | if hdr[0] != 'NNOD': 268 | raise ex 269 | try: 270 | Nnod = int(hdr[1]) 271 | except: 272 | raise ex 273 | if hdr[2] != 'TIME' and hdr[2] != 'LOAD': 274 | raise ex 275 | try: 276 | t = float(hdr[3]) 277 | except: 278 | raise ex 279 | if hdr[4] != 'ORDER': 280 | raise ex 281 | try: 282 | torder = int(hdr[5]) 283 | except: 284 | raise ex 285 | 286 | return Nnod, t, torder 287 | 288 | @staticmethod 289 | def __readNodes(file, node, val, Nnod): 290 | res = [] 291 | allnode = True and isinstance(node, str) 292 | somenode = not allnode and isinstance(node, (tuple, list)) 293 | 294 | for i in range(Nnod): 295 | line = file.readline() 296 | if line is None: 297 | raise EOFError 298 | if not allnode: 299 | if not somenode: 300 | if node != i: 301 | continue 302 | else: 303 | if i not in node: 304 | continue 305 | line = line.split() 306 | Ndof = int(line[0]) 307 | try: 308 | if val == 'x': 309 | X_ = list(float(x) for x in line[1:4]) 310 | res.append(X_) 311 | elif val == 'u': 312 | u = list(float(x) for x in line[4:4+Ndof]) 313 | res.append(u) 314 | elif val == 'v': 315 | v = list(float(x) for x in line[4+Ndof:4+Ndof*2]) 316 | if len(v) == 0: 317 | file.close() 318 | raise Exception 319 | res.append(v) 320 | elif val == 'a': 321 | a = list(float(x) for x in line[4+Ndof*2:4+Ndof*3]) 322 | if len(a) == 0: 323 | file.close() 324 | raise Exception 325 | res.append(a) 326 | elif val == 'all': 327 | al = list(float(x) for x in line[1:-1]) 328 | res.append(al) 329 | else: 330 | file.close() 331 | raise Exception('Cannot read '+val+' in this file') 332 | except IndexError: 333 | file.close() 334 | raise Exception('Cannot read '+val+' in this file') 335 | return np.array(res) 336 | 337 | @staticmethod 338 | def readOutput(filen, timeStep = 0, node = 'all', val = 'u'): 339 | """ 340 | This function read the ouput file that was produced by 341 | this output class 342 | Input: 343 | filen: name of output file 344 | timeStep: 345 | scalar: read the specific time step 346 | list, tuple: read the time steps in list or tuple 347 | 'all': read all time steps 348 | node: 349 | scalar: read at specific node 350 | 'all': read all nodes 351 | val: 352 | 'u': read displacement u 353 | 'v': read velocity v 354 | 'a': read acceleration a 355 | 'x': read coordinates of nodes 356 | 'all': everything 357 | Return: 358 | list of numpy array 359 | """ 360 | file = open(filen,'r') 361 | Nnod, ti, timeOrder = StandardFileOutput.readHeader(file) 362 | assert not(isinstance(timeStep,str) and timeStep != 'all'),\ 363 | 'unknown option '+ str(timeStep) 364 | 365 | alltime = True and timeStep == 'all' 366 | res = [] 367 | if not alltime: 368 | if isinstance(timeStep,(list,tuple)): 369 | rest = [] 370 | for i in timeStep: 371 | try: 372 | a,t = StandardFileOutput.readOutput(filen,i,node,val) 373 | res.append(a) 374 | rest.append(t) 375 | except EOFError: 376 | warnings.warn('file ended before all steps were read') 377 | break 378 | file.close() 379 | return res,rest 380 | else: 381 | gotoLine(file, (Nnod+1)*timeStep) 382 | _,t,_ = StandardFileOutput.readHeader(file) 383 | return StandardFileOutput.__readNodes(file,node,val,Nnod),t 384 | try: 385 | rest = [] 386 | file.seek(0) 387 | while 1: 388 | _,t,_ = StandardFileOutput.readHeader(file) 389 | rest.append(t) 390 | res.append(StandardFileOutput.__readNodes(file,node,val,Nnod)) 391 | except EOFError: 392 | file.close() 393 | return res,rest 394 | 395 | def updateToMesh(self, mesh, istep = 0): 396 | """ 397 | update output data to mesh at a specific time step 398 | """ 399 | StandardFileOutput.updateMesh(self.outfile,mesh,istep) 400 | 401 | @staticmethod 402 | def updateMesh(file, mesh, istep = 0): 403 | """ 404 | update output data to mesh at a specific time step 405 | """ 406 | resu,_ = StandardFileOutput.readOutput(file,istep) 407 | try: 408 | resv,_ = StandardFileOutput.readOutput(file,istep,val='v') 409 | except Exception: 410 | resv = None 411 | try: 412 | resa,_ = StandardFileOutput.readOutput(file,istep,val='a') 413 | except Exception: 414 | resa = None 415 | nodes = mesh.getNodes() 416 | for i,n in enumerate(nodes): 417 | n.setU(resu[i]) 418 | if resv is not None: 419 | for i,n in enumerate(nodes): 420 | n.setV(resv[i]) 421 | if resa is not None: 422 | for i,n in enumerate(nodes): 423 | try: 424 | n.setA(resa[i]) 425 | except ValueError: 426 | print('error here') 427 | raise ValueError 428 | 429 | def getValueInElement(self, mesh, e, x, isteps = 0, val='u'): 430 | """ 431 | get output value at position x(natural coordinate) in element e of mesh 432 | """ 433 | IT = fm.get_connection(mesh,e) 434 | try: 435 | resuu = [] 436 | for i in isteps: 437 | resu,tout =StandardFileOutput.readOutput(self.outfile,i,IT,val) 438 | for j,n in enumerate(e.getNodes()): 439 | n.setU(resu[i][j]) 440 | resuu.append(e.values_at(x,val)) 441 | return resuu 442 | except Exception: 443 | resu,tout =StandardFileOutput.readOutput(self.outfile,i,IT,val) 444 | for j,n in enumerate(e.getNodes()): 445 | n.setU(resu[i][j]) 446 | return e.values_at(x,val) 447 | 448 | 449 | 450 | def gotoLine(file, lineno): 451 | """ 452 | This function goto the specific line in an opened file 453 | Input: 454 | file: file object that is opened 455 | lineno: line number 456 | Raise: 457 | EOFError if file ended before line numer is reached 458 | """ 459 | file.seek(0) 460 | for i in range(lineno): 461 | line = file.readline() 462 | if line == '': 463 | raise EOFError 464 | 465 | def isAscending(ar): 466 | """ 467 | check if array is ascending ordered 468 | """ 469 | for i,a in enumerate(ar): 470 | try: 471 | if a > ar[i+1]: 472 | return False 473 | except IndexError: 474 | return True 475 | return True 476 | 477 | class NoXStardardFileOutput(StandardFileOutput): 478 | """ 479 | This object class gives the output in simple standard format without 480 | coordinates of nodes. 481 | The format of the output file is following: 482 | Each time step begin with 483 | NNOD data.getNnod() TIME data.getTime() ORDER data.getTimeOrder() 484 | and follow by NNOD rows, each row is data of one node with format: 485 | Ndof U[0]...U[Ndof-1] V[0] ... V[Ndof-1] A[0] ... A[Ndof-1] 486 | if data.getTimeOrder() == 0, there is no V and A 487 | 1, A 488 | """ 489 | def outputData(self, data): 490 | """ 491 | write data to output file 492 | """ 493 | self.writeHeader(data) 494 | nodes = data.getMesh().getNodes() 495 | for i in range(data.getMesh().getNnod()): 496 | node = nodes[i] 497 | self.file.write(str(node.getNdof())+' ') 498 | self.writeU(node) 499 | if data.getTimeOrder() > 0: 500 | self.writeV(node) 501 | if data.getTimeOrder() == 2: 502 | self.writeA(node) 503 | self.file.write('\n') 504 | 505 | def __readNodes(file, node, val, Nnod): 506 | res = [] 507 | allnode = True and isinstance(node, str) 508 | somenode = not allnode and isinstance(node, (tuple, list)) 509 | 510 | for i in range(Nnod): 511 | line = file.readline() 512 | if line is None: 513 | raise EOFError 514 | if not allnode: 515 | if not somenode: 516 | if node != i: 517 | continue 518 | else: 519 | if i not in node: 520 | continue 521 | line = line.split() 522 | Ndof = int(line[0]) 523 | try: 524 | if val == 'u': 525 | u = list(float(x) for x in line[1:1+Ndof]) 526 | res.append(u) 527 | elif val == 'v': 528 | v = list(float(x) for x in line[1+Ndof:1+Ndof*2]) 529 | res.append(v) 530 | elif val == 'a': 531 | a = list(float(x) for x in line[1+Ndof*2:1+Ndof*3]) 532 | res.append(a) 533 | elif val == 'all': 534 | al = list(float(x) for x in line[1:-1]) 535 | res.append(al) 536 | else: 537 | file.close() 538 | raise Exception('Cannot read '+val+' in this file') 539 | except IndexError: 540 | file.close() 541 | raise Exception('Cannot read '+val+' in this file') 542 | return np.array(res) 543 | -------------------------------------------------------------------------------- /InOut/MatlabSupport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Dec 21 15:59:50 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import matlab 9 | import Mesh.FEMMesh as FM 10 | 11 | def plot_field(eng, mesh, val='u'): 12 | Ne = mesh.Ne 13 | it = FM.get_connections(mesh) 14 | mat = [] 15 | for e in mesh.getElements(): 16 | mat.append(e.getMaterial().getID()+1) 17 | 18 | xyz = [] 19 | field = [] 20 | for n in mesh.getNodes(): 21 | xyz.append(n.getX().tolist()) 22 | if val == 'u': 23 | for n in mesh.getNodes(): 24 | field.append(n.getU().tolist()) 25 | elif val == 'v': 26 | for n in mesh.getNodes(): 27 | field.append(n.getV().tolist()) 28 | elif val == 'a': 29 | for n in mesh.getNodes(): 30 | field.append(n.getU().tolist()) 31 | else: 32 | return 33 | 34 | #eng = matlab.engine.start_matlab() 35 | IT = matlab.int64(it) 36 | MAT = matlab.int64(mat) 37 | XYZ = matlab.double(xyz) 38 | Field = matlab.double(field) 39 | eng.plot_field(IT,Field,XYZ,Ne,MAT,0) 40 | return eng 41 | -------------------------------------------------------------------------------- /InOut/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /Material/Material.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 10 15:57:14 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | class Material(object): 9 | """ 10 | Material class stores all properties and methods of an material 11 | """ 12 | def calculate(self, data = None): 13 | """ 14 | calculate material parameters. These parameters is stored in this 15 | material object. 16 | Input: 17 | data: optional, object class that can return required value for 18 | calculation. It is normally an Element object 19 | """ 20 | pass 21 | 22 | def getID(self): 23 | """ 24 | Get id number of this materal 25 | """ 26 | return 0 -------------------------------------------------------------------------------- /Material/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /Math/SingularIntegration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Dec 6 12:54:02 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import math 9 | import numpy as np 10 | import scipy.special as scs 11 | import scipy.integrate as sci 12 | import Math.IntegrationData as Idat 13 | 14 | __all__ = ['SingularGaussian1D','Gaussian_1D_log','Gaussian_1D_rat', 15 | 'Gaussian_1D_Pn_Log','Gaussian_1D_Pn_Log_Rat'] 16 | 17 | class SingularGaussian1D(Idat.GaussianQuadrature): 18 | """ 19 | Gaussian Quadrature data for Singular function in one dimension 20 | """ 21 | def __init__(self, ng, sing_points, gen1, gen2 = None): 22 | Idat.GaussianQuadrature.__init__(self,ng,1,None) 23 | self.Nsing = len(sing_points) 24 | self.wg = [] 25 | self.wgx = None 26 | self.gen1 = gen1 27 | self.gen2 = gen2 28 | if gen2 is not None: 29 | self.wgx = [] 30 | 31 | for t in sing_points: 32 | self.xg,wg = gen1(t, ng) 33 | if gen2 is not None: 34 | _,wgx = gen2(t,ng) 35 | self.wg.append(wg) 36 | self.wgx.append(wgx) 37 | 38 | if gen2 is None: 39 | self.wgx = self.wg 40 | 41 | def __next__(self): 42 | if self.iter_index == self.Npoint: 43 | raise StopIteration 44 | idx = self.iter_index 45 | self.iter_index += 1 46 | if self.Ndim == 1: 47 | return self.xg[idx], self.wg[0][idx] 48 | return self.xg[:,idx], self.wg[0][:,idx] 49 | 50 | def s_iter(self, op): 51 | """ 52 | second iterator 53 | Notice: this generator yields one Gaussian point and two weights at one 54 | loop step. 55 | """ 56 | for i in range(self.Ng): 57 | yield self.xg[i], self.wg[op][i], self.wgx[op][i] 58 | 59 | def Gaussian_1D_log(t, ng): 60 | """ 61 | Gaussian quadrature points and weights for logarithmic function log|x-t| 62 | integration 63 | """ 64 | x,w = Idat.Gaussian1D(ng) 65 | w1 = np.zeros(ng) 66 | for i in range(ng): 67 | for j in range(1,ng-1): 68 | w1[i] += (poly_Legendre(x[i],j-1)-poly_Legendre(x[i],j+1))*\ 69 | R_function(t,j) 70 | 71 | w1[i] += (poly_Legendre(x[i],0)-poly_Legendre(x[i],1))*R_function(t,0) 72 | w1[i] += poly_Legendre(x[i],ng-2)*R_function(t,ng-1) 73 | w1[i] += poly_Legendre(x[i],ng-1)*R_function(t,ng) 74 | w1[i] *= w[i] 75 | return x, w1 76 | 77 | def Gaussian_1D_rat(t, ng): 78 | """ 79 | Gaussian quadrature points and weights for rational function 1/|x-t| 80 | integration 81 | """ 82 | x,w = Idat.Gaussian1D(ng) 83 | w1 = np.zeros(ng) 84 | for i in range(ng): 85 | for j in range(ng): 86 | w1[i] += (2.0*j+1.0)*poly_Legendre(x[i],j)*Legendre_Qn(t,j) 87 | w1[i] *= w[i] 88 | 89 | return x, w1 90 | 91 | def Gaussian_1D_rat2(t, ng): 92 | """ 93 | Gaussian quadrature points and weights for rational function 1/|x-t| 94 | integration 95 | """ 96 | x,w = Idat.Gaussian1D(ng) 97 | w1 = np.zeros(ng) 98 | for i in range(ng): 99 | # for j in range(ng-1): 100 | # for k in range(j,math.trunc((ng+j-3)/2)+1): 101 | # w1[i] -= (2*j+1)*(4*k+3-2*i)*Legendre_Qn(t,j)*\ 102 | # poly_Legendre(x[i],2*k+1-(i+1)) 103 | for j in range(1,ng): 104 | for k in range(math.trunc((j-1)/2)+1): 105 | w1[i] -= (2*j-4*k-1)*poly_Legendre(x[i],j)*(2*j+1)*\ 106 | Legendre_Qn(t,j-2*k-1) 107 | for j in range(ng): 108 | w1[i] += (2.0*j+1.0)/2*poly_Legendre(x[i],j)*\ 109 | (1.0/(t-1.0) - ((-1.0)**j)/(t+1)) 110 | w1[i] *= w[i] 111 | 112 | return x, w1 113 | 114 | def integration_PnLog(t, pn): 115 | x,w = Gaussian_1D_log(t, 20) 116 | y = 0.0 117 | for i in range(20): 118 | y += w[i]*poly_Legendre(x[i],pn) 119 | return y 120 | 121 | def Gaussian_1D_Pn_Log(t, ng, m = -1): 122 | """ 123 | Gaussian quadrature points and weights for function Pn(x)*log|x-t| 124 | """ 125 | if m <= 0: 126 | m = math.ceil(ng/3) 127 | x,_ = Idat.Gaussian1D(ng) 128 | xi = np.empty((2*m,ng)) 129 | mi = np.zeros(2*m) 130 | mi[0] = 2.0 131 | 132 | for i in range(m): 133 | for j in range(ng): 134 | pol = poly_Legendre(x[j],i) 135 | xi[i,j] = pol 136 | xi[i+m,j] = pol*math.log(math.fabs(x[j]-t)) 137 | mi[i+m] = integration_PnLog(t,i) 138 | 139 | w1,_,_,_ = np.linalg.lstsq(xi, mi) 140 | 141 | return x, w1 142 | 143 | def integration_PnRat(t, pn): 144 | x,w = Gaussian_1D_rat(t, 20) 145 | y = 0.0 146 | for i in range(20): 147 | y += w[i]*poly_Legendre(x[i],pn) 148 | 149 | return y 150 | 151 | def integration_PnRat2(t, pn): 152 | 153 | # def Leg_Rat2(x): 154 | # return poly_Legendre(x, pn)/((t-x)**2) 155 | # 156 | # val,err=sci.quad(Leg_Rat2,-1.0,1.0,epsabs=1.49e-14, epsrel=1.49e-14,\ 157 | # points = [t]) 158 | # return val 159 | 160 | x,w = Gaussian_1D_rat2(t, 24) 161 | y = 0.0 162 | for i in range(24): 163 | y += w[i]*poly_Legendre(x[i],pn) 164 | 165 | return y 166 | 167 | def Gaussian_1D_Pn_Log_Rat(t, ng, m = -1): 168 | if m <= 0: 169 | m = math.ceil(ng/3) 170 | x,_ = Idat.Gaussian1D(ng) 171 | xi = np.empty((3*m,ng)) 172 | mi = np.zeros(3*m) 173 | mi[0] = 2.0 174 | 175 | for i in range(m): 176 | for j in range(ng): 177 | pol = poly_Legendre(x[j],i) 178 | xi[i,j] = pol 179 | xi[i+m,j] = pol*math.log(math.fabs(x[j]-t)) 180 | xi[i+2*m,j] = pol/(t-x[j]) 181 | mi[i+m] = integration_PnLog(t,i) 182 | mi[i+2*m] = integration_PnRat(t,i) 183 | 184 | w1,_,_,_ = np.linalg.lstsq(xi, mi) 185 | return x, w1 186 | 187 | def Gaussian_1D_Pn_Log_Rat_Rat2(t, ng, m = -1): 188 | if m <= 0: 189 | m = math.ceil(ng/3) 190 | x,_ = Idat.Gaussian1D(ng) 191 | xi = np.empty((4*m,ng)) 192 | mi = np.zeros(4*m) 193 | mi[0] = 2.0 194 | 195 | for i in range(m): 196 | for j in range(ng): 197 | pol = poly_Legendre(x[j],i) 198 | xi[i,j] = pol 199 | xi[i+m,j] = pol*math.log(math.fabs(x[j]-t)) 200 | xi[i+2*m,j] = pol/(t-x[j]) 201 | xi[i+3*m,j] = pol/((t-x[j])**2) 202 | mi[i+m] = integration_PnLog(t,i) 203 | mi[i+2*m] = integration_PnRat(t,i) 204 | mi[i+3*m] = integration_PnRat2(t,i) 205 | 206 | w1,_,_,_ = np.linalg.lstsq(xi, mi) 207 | return x, w1 208 | 209 | 210 | def poly_Legendre(x, n): 211 | if n < 0: 212 | # return poly_Legendre(x,-n-1) 213 | return 1.0 214 | p = scs.legendre(n) 215 | return p(x) 216 | 217 | def Legendre_Qn(x, n): 218 | yn_1 = 0.5*math.log((1.0+x)/(1.0-x)) 219 | yn = x*0.5*math.log((1.0+x)/(1.0-x))-1.0 220 | if n == 0: 221 | y = yn_1 222 | elif n == 1: 223 | y = yn 224 | else: 225 | for i in range(1,n): 226 | y = ((2.0*i+1.0)*x*yn-i*yn_1)/(i+1.0) 227 | yn_1 = yn 228 | yn = y 229 | return y 230 | 231 | def R_function(x, n): 232 | return Legendre_Qn(x,n) + 0.25*math.log((x-1.0)*(x-1.0)) 233 | 234 | if __name__ == '__main__': 235 | print(poly_Legendre(0.5,3)) 236 | print(R_function(0.5,3)) -------------------------------------------------------------------------------- /Math/Solver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Nov 13 10:10:50 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import numpy as np 9 | import scipy.linalg as la 10 | 11 | class Solver(object): 12 | """ 13 | Matrix solver 14 | Different solver can be used by deriving a subclass of this class 15 | and overwrite solve method 16 | """ 17 | def solve(self, A, b): 18 | """ 19 | solve system of equations Ax = b 20 | return x as solution 21 | Raise SingularMatrix if matrix is singular 22 | This method is often used in linear algorithms 23 | """ 24 | pass 25 | 26 | def isolve(self, A, b): 27 | """ 28 | Solve system of equation Ax = b 29 | replace b by solution x after calculation 30 | Raise SingularMatrix if matrix is singular 31 | This method is often used in nonlinear algorithms 32 | """ 33 | pass 34 | 35 | class numpySolver(Solver): 36 | """ 37 | numpy and scipy solver 38 | """ 39 | def solve(self, A, b): 40 | """ 41 | solve system of equations Ax = b 42 | return x as solution 43 | Raise SingularMatrix if matrix is singular 44 | This method is often used in linear algorithms 45 | """ 46 | try: 47 | return np.linalg.solve(A,b) 48 | except np.linalg.LinAlgError: 49 | raise SingularMatrix 50 | 51 | def isolve(self, A, b): 52 | """ 53 | Solve system of equation Ax = b 54 | replace b by solution x after calculation 55 | Raise SingularMatrix if matrix is singular 56 | This method is often used in nonlinear algorithms 57 | """ 58 | try: 59 | a = np.linalg.solve(A,b) 60 | np.copyto(b,a) 61 | # la.solve(A,b,overwrite_a = True, overwrite_b = True) 62 | except la.LinAlgError: 63 | raise SingularMatrix 64 | 65 | def LDLdiag(self, A): 66 | """ 67 | return D of LDLdecomposition 68 | """ 69 | return np.diag(la.cholesky(A)) 70 | 71 | def LUdecomp(self, A): 72 | return la.lu(A) 73 | 74 | class SingularMatrix(Exception): 75 | """ 76 | Exception for Singular matrix 77 | """ 78 | pass 79 | -------------------------------------------------------------------------------- /Math/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /Math/injectionArray.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Nov 27 09:42:21 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import numpy as np 9 | import itertools as itl 10 | 11 | __all__ = ['injectArray','zeros','array'] 12 | 13 | class injectArray(object): 14 | """ 15 | Array of different elements of other array, only support maximum 2-d array 16 | """ 17 | def __init__(self, size, dtype = 'float64', data = None): 18 | """ 19 | Initialize injection array 20 | Input: 21 | size: either size of 1d array or tuple of sizes of dimensions 22 | dtype: data type of each element, similar to numpy array datatype 23 | """ 24 | assert isinstance(size,(list,tuple,int)),'Wrong array size' 25 | assert dtype is not None, 'unsupported data type' 26 | self.dtype = dtype 27 | self.size = size 28 | try: 29 | self.ndim = len(size) 30 | self.length = np.prod(size) 31 | except TypeError: 32 | self.length = size 33 | self.ndim = 1 34 | 35 | if data is not None: 36 | self.data = data 37 | return 38 | 39 | self.data = np.empty(size,dtype=np.ndarray) 40 | 41 | a = np.array([0.0]*self.length,dtype) 42 | it =np.nditer(self.data,flags=['refs_ok','c_index','multi_index'],\ 43 | op_flags=['readwrite']) 44 | while not it.finished: 45 | idx = it.index 46 | try: 47 | id1 = it.multi_index[0] 48 | id2 = it.multi_index[1] 49 | self.data[id1,id2] = a[idx:idx+1] 50 | except IndexError: 51 | self.data[idx] = a[idx:idx+1] 52 | it.iternext() 53 | 54 | def __iter__(self): 55 | self.it = np.nditer(self.data,\ 56 | flags=['refs_ok','c_index','multi_index'],op_flags=['readwrite']) 57 | return self 58 | 59 | def __next__(self): 60 | if self.it.finished: 61 | del self.it 62 | raise StopIteration 63 | try: 64 | id1 = self.it.multi_index[0] 65 | id2 = self.it.multi_index[1] 66 | self.it.iternext() 67 | return self.data[id1,id2][0] 68 | except: 69 | idx = self.it.index 70 | return self.data[idx][0] 71 | 72 | def __str__(self): 73 | """ 74 | print array 75 | """ 76 | s = '[' 77 | try: 78 | for i in range(self.size[0]): 79 | 80 | try: 81 | s += '[' 82 | for j in range(self.size[1]): 83 | s+= str(self.data[i,j][0]) 84 | s+= ', ' 85 | s = s[:-2] 86 | s+= '], ' 87 | #s = s[:-2] 88 | except IndexError: 89 | s = s[:-1] 90 | s+= str(self.data[i][0]) 91 | s+= ', ' 92 | s = s[:-2] 93 | except TypeError: 94 | for i in range(self.size): 95 | s+= str(self.data[i][0]) 96 | s+= ' ,' 97 | s = s[:-2] 98 | s+= ']' 99 | return s 100 | 101 | def __getitem__(self, k): 102 | try: 103 | datx = np.empty((k[0].stop-k[0].start,k[0].stop-k[0].start),\ 104 | self.dtype) 105 | for i in range(k[0].start,k[0].stop): 106 | for j in range(k[1].start,k[1].stop): 107 | datx[i,j] = self.data[i,j][0] 108 | 109 | return datx 110 | except TypeError: 111 | try: 112 | datx = np.empty(k.stop-k.start,self.dtype) 113 | for i in range(k.start,k.stop): 114 | datx[i] = self.data[i][0] 115 | return datx 116 | except AttributeError: 117 | pass 118 | except AttributeError: 119 | pass 120 | return self.data[k][0] 121 | 122 | def __setitem__(self, k, val): 123 | try: 124 | for i in range(k[0].start,k[0].stop): 125 | for j in range(k[1].start,k[1].stop): 126 | self.data[i,j][0] = val[i,j] 127 | except TypeError: 128 | try: 129 | for i in range(k.start,k.stop): 130 | self.data[i][0] = val[i] 131 | except AttributeError: 132 | pass 133 | except AttributeError: 134 | pass 135 | self.data[k][0] = val 136 | 137 | def __iadd__(self, other): 138 | if self.ndim == 1: 139 | a = range(self.size) 140 | try: 141 | for i in a: 142 | self.data[i] += other[i] 143 | except (TypeError, IndexError): 144 | for i in a: 145 | self.data[i] += other 146 | elif self.ndim == 2: 147 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 148 | op_flags=['readwrite']) 149 | while not it.finished: 150 | id1 = it.multi_index[0] 151 | id2 = it.multi_index[1] 152 | try: 153 | self.data[id1,id2] += other[id1,id2] 154 | except (TypeError, IndexError): 155 | self.data[id1,id2] += other 156 | it.iternext() 157 | return self 158 | 159 | def __add__(self, other): 160 | res = np.empty(self.size, self.dtype) 161 | if self.ndim == 1: 162 | a = range(self.size) 163 | try: 164 | for i in a: 165 | res[i] = self.data[i] + other[i] 166 | except (TypeError, IndexError): 167 | for i in a: 168 | res[i] = self.data[i] + other 169 | elif self.ndim == 2: 170 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 171 | op_flags=['readwrite']) 172 | while not it.finished: 173 | id1 = it.multi_index[0] 174 | id2 = it.multi_index[1] 175 | try: 176 | res[id1,id2] = self.data[id1,id2] + other[id1,id2] 177 | except (TypeError, IndexError): 178 | res[id1,id2] = self.data[id1,id2] + other 179 | it.iternext() 180 | return res 181 | 182 | def __isub__(self, other): 183 | if self.ndim == 1: 184 | a = range(self.size) 185 | try: 186 | for i in a: 187 | self.data[i] -= other[i] 188 | except (TypeError, IndexError): 189 | for i in a: 190 | self.data[i] -= other 191 | elif self.ndim == 2: 192 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 193 | op_flags=['readwrite']) 194 | while not it.finished: 195 | id1 = it.multi_index[0] 196 | id2 = it.multi_index[1] 197 | try: 198 | self.data[id1,id2] -= other[id1,id2] 199 | except (TypeError, IndexError): 200 | self.data[id1,id2] -= other 201 | it.iternext() 202 | return self 203 | 204 | def __sub__(self, other): 205 | res = np.empty(self.size, self.dtype) 206 | if self.ndim == 1: 207 | a = range(self.size) 208 | try: 209 | for i in a: 210 | res[i] = self.data[i] - other[i] 211 | except (TypeError, IndexError): 212 | for i in a: 213 | res[i] = self.data[i] - other 214 | elif self.ndim == 2: 215 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 216 | op_flags=['readwrite']) 217 | while not it.finished: 218 | id1 = it.multi_index[0] 219 | id2 = it.multi_index[1] 220 | try: 221 | res[id1,id2] = self.data[id1,id2] - other[id1,id2] 222 | except (TypeError, IndexError): 223 | res[id1,id2] = self.data[id1,id2] - other 224 | it.iternext() 225 | return res 226 | 227 | def __imul__(self, other): 228 | if self.ndim == 1: 229 | a = range(self.size) 230 | try: 231 | for i in a: 232 | self.data[i] *= other[i] 233 | except (TypeError, IndexError): 234 | for i in a: 235 | self.data[i] *= other 236 | elif self.ndim == 2: 237 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 238 | op_flags=['readwrite']) 239 | while not it.finished: 240 | id1 = it.multi_index[0] 241 | id2 = it.multi_index[1] 242 | try: 243 | self.data[id1,id2] *= other[id1,id2] 244 | except (TypeError, IndexError): 245 | self.data[id1,id2] *= other 246 | it.iternext() 247 | return self 248 | 249 | def __mul__(self, other): 250 | res = np.empty(self.size, self.dtype) 251 | if self.ndim == 1: 252 | a = range(self.size) 253 | try: 254 | for i in a: 255 | res[i] = self.data[i] * other[i] 256 | except (TypeError, IndexError): 257 | for i in a: 258 | res[i] = self.data[i] * other 259 | elif self.ndim == 2: 260 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 261 | op_flags=['readwrite']) 262 | while not it.finished: 263 | id1 = it.multi_index[0] 264 | id2 = it.multi_index[1] 265 | try: 266 | res[id1,id2] = self.data[id1,id2] * other[id1,id2] 267 | except (TypeError, IndexError): 268 | res[id1,id2] = self.data[id1,id2] * other 269 | it.iternext() 270 | return res 271 | 272 | def __idiv__(self, other): 273 | if self.ndim == 1: 274 | a = range(self.size) 275 | try: 276 | for i in a: 277 | self.data[i] /= other[i] 278 | except (TypeError, IndexError): 279 | for i in a: 280 | self.data[i] /= other 281 | elif self.ndim == 2: 282 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 283 | op_flags=['readwrite']) 284 | while not it.finished: 285 | id1 = it.multi_index[0] 286 | id2 = it.multi_index[1] 287 | try: 288 | self.data[id1,id2] /= other[id1,id2] 289 | except (TypeError, IndexError): 290 | self.data[id1,id2] /= other 291 | it.iternext() 292 | return self 293 | 294 | def __div__(self, other): 295 | res = np.empty(self.size, self.dtype) 296 | if self.ndim == 1: 297 | a = range(self.size) 298 | try: 299 | for i in a: 300 | res[i] = self.data[i] / other[i] 301 | except (TypeError, IndexError): 302 | for i in a: 303 | res[i] = self.data[i] / other 304 | elif self.ndim == 2: 305 | it =np.nditer(self.data,flags=['refs_ok','multi_index'],\ 306 | op_flags=['readwrite']) 307 | while not it.finished: 308 | id1 = it.multi_index[0] 309 | id2 = it.multi_index[1] 310 | try: 311 | res[id1,id2] = self.data[id1,id2] / other[id1,id2] 312 | except (TypeError, IndexError): 313 | res[id1,id2] = self.data[id1,id2] / other 314 | it.iternext() 315 | return res 316 | 317 | def __itruediv__(self, other): 318 | return self.__idiv__(other) 319 | 320 | def __truediv__(self, other): 321 | return self.__div__(other) 322 | 323 | def connect(self, k, A, ka): 324 | """ 325 | connect indices k of this array to corresponding indices ka of array A 326 | array A must be a Numpy array 327 | """ 328 | if isinstance(ka,(list,np.ndarray,tuple)): 329 | A[ka[0],ka[1]] = self.data[k][0] 330 | self.data[k] = A[ka[0]:ka[0]+1,ka[1]:ka[1]+1] 331 | else: 332 | A[ka] = self.data[k][0] 333 | self.data[k] = A[ka:ka+1] 334 | 335 | def tolist(self): 336 | if self.ndim == 1: 337 | return [self.data[i][0] for i in range(self.size)] 338 | elif self.ndim == 2: 339 | res = np.empty(self.size,self.dtype) 340 | it =np.nditer(self.data,flags=['refs_ok','multi_index']) 341 | while not it.finished: 342 | id1 = it.multi_index[0] 343 | id2 = it.multi_index[1] 344 | res[id1,id2] = self.data[id1,id2][0] 345 | it.iternext() 346 | return res.tolist() 347 | return [] 348 | 349 | def toNumpy(self): 350 | if self.ndim == 1: 351 | return np.array([self.data[i][0] for i in range(self.size)]) 352 | elif self.ndim == 2: 353 | res = np.empty(self.size,self.dtype) 354 | it =np.nditer(self.data,flags=['refs_ok','multi_index']) 355 | while not it.finished: 356 | id1 = it.multi_index[0] 357 | id2 = it.multi_index[1] 358 | res[id1,id2] = self.data[id1,id2][0] 359 | it.iternext() 360 | return res 361 | return None 362 | 363 | def zeros(size, dtype = 'float64'): 364 | """ 365 | Create a injection array with all member set to zero 366 | This is equivalent to injectArray(size,dtype) 367 | """ 368 | return injectArray(size,dtype) 369 | 370 | def array(data, dtype = None): 371 | """ 372 | Create an injection array from an array like data 373 | """ 374 | 375 | if dtype is None: 376 | try: 377 | dtype = data.dtype 378 | except AttributeError: 379 | try: 380 | for a in itl.chain.from_iterable(data): 381 | if isinstance(a, float): 382 | dtype = 'float64' 383 | break 384 | elif isinstance(a, int): 385 | dtype = 'int32' 386 | except TypeError: 387 | for a in data: 388 | if isinstance(a, float): 389 | dtype = 'float64' 390 | break 391 | elif isinstance(a, int): 392 | dtype = 'int32' 393 | 394 | try: 395 | size = data.shape 396 | try: 397 | size[1] 398 | except IndexError: 399 | size = size[0] 400 | 401 | except AttributeError: 402 | try: 403 | size = data.size 404 | except AttributeError: 405 | size1 = len(data) 406 | try: 407 | size2 = len(data[1]) 408 | size = (size1,size2) 409 | except TypeError: 410 | size = len(data) 411 | 412 | 413 | res = injectArray(size,dtype) 414 | 415 | it = np.nditer(res.data,flags=['c_index','multi_index','refs_ok'],\ 416 | op_flags=['readwrite']) 417 | while not it.finished: 418 | try: 419 | id1 = it.multi_index[0] 420 | id2 = it.multi_index[1] 421 | res.data[id1][id2][0] = data[id1][id2] 422 | except IndexError: 423 | idx = it.index 424 | res.data[idx][0] = data[idx] 425 | it.iternext() 426 | 427 | return res 428 | 429 | 430 | if __name__ == '__main__': 431 | test = injectArray((2,2)) 432 | print(test[0,0]) 433 | testCon = np.array([1,2,3,4,5,6],dtype = 'float64') 434 | test.connect((0,0),testCon,2) 435 | print('test[0,0] = ',test[0,0]) 436 | test[0,0] = 4 437 | test += np.array([[0.5,0.5],[0.6,0.6]]) 438 | test += 0.1 439 | print('test += operator', test) 440 | test *= 2.0 441 | test /= 0.5 442 | testlist = test.tolist() 443 | print('test tolist ',testlist) 444 | for e in test: 445 | print(e) 446 | 447 | testarr1 = array([1,2,3,4]) 448 | print(testarr1) 449 | testarr2 = array([[1,2,3],[4.0,5,6]]) 450 | print(testarr2) 451 | testarr3 = array(np.array([1.0,2.0,3.0])) 452 | print(testarr3) 453 | testarr4 = np.array(array([4,5,6.0])) 454 | print(testarr4) 455 | print(testarr3*2) 456 | print(testarr2/2) 457 | testarr5 = np.array([[4.0],[5.0],[6.0]]) 458 | print(testarr5) 459 | testarr7 = array(np.array([[1,2,3],[4,5,6]])) 460 | print(testarr7) 461 | testarr4 += testarr3 462 | print(testarr4) 463 | testarr2[1,1] += 5 464 | print(testarr2) 465 | #testarr8 = zeros((3,3),dtype = 'float64') 466 | #testarr6 = np.outer(testarr3,testarr5,testarr8) 467 | #print(array(testarr6)) 468 | -------------------------------------------------------------------------------- /Mesh/FEMNode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 10 10:26:43 2017 4 | 5 | @author: Hai Au Bui 6 | University of Kassel 7 | @email: au.bui(_at_)uni-kassel.de 8 | """ 9 | import math 10 | import numpy as np 11 | from Mesh.MeshGenerator import GeneralNode 12 | import Math.injectionArray as ia 13 | 14 | class Node(GeneralNode): 15 | """ 16 | Node, basic component of Finite Element Method 17 | This class has all of the properties and methods that are necessary for 18 | a FEM Node 19 | """ 20 | def __init__(self, X_, Ndof, timeOrder = 0, id_number = 0,\ 21 | dtype = 'float64'): 22 | """ 23 | Initialize Node with the coordinate X and number of Degree of Fredoms 24 | Input: 25 | X_: an array like object that store coordinates 26 | Ndof: number of degree of freedom 27 | timeOrder: optional, the order of differential quation in time 28 | default value: "Zeroth" 29 | other possible values: "First" and "Second" 30 | """ 31 | GeneralNode.__init__(self, X_, len(X_)) 32 | self.dtype = dtype 33 | self.id_number = id_number 34 | self.Ndof = Ndof 35 | # Initialize a list that stores the indices of equations 36 | self.ID = [-1]*Ndof 37 | # Initialize a list that stores the contraints of this node 38 | self.freedom = [True]*Ndof 39 | # Initialize an array that store point external load or source 40 | self.load = [0.0]*Ndof 41 | # Initialize a list that stores the diplacements, velocities 42 | # accelerations 43 | self.u_ = ia.zeros(Ndof) 44 | self.v_ = None 45 | self.a_ = None 46 | self.timeOrder = timeOrder 47 | self.callPointLoad = False 48 | if timeOrder > 0: 49 | self.v_ = ia.zeros(Ndof) 50 | if timeOrder > 1: 51 | self.a_ = ia.zeros(Ndof) 52 | 53 | self.NonHomogeneousDirichlet = False 54 | self.friendNode = None 55 | self.friendDOF = -1 56 | self.controlledDOF = -1 57 | self.IDC = -1 58 | 59 | def copyToPosition(self, X): 60 | """ 61 | Copy this node to some other position X 62 | Return node of the same type 63 | id_number will be 0 64 | This method does not copy the boundary conditions, equation numbers 65 | and load 66 | """ 67 | n = Node(X, self.Ndof, timeOrder = self.timeOrder) 68 | 69 | return n 70 | 71 | def copy(self): 72 | n = Node(self.X_,self.Ndof,timeOrder = self.timeOrder) 73 | for i in range(self.Ndof): 74 | n.freedom[i] = self.freedom[i] 75 | n.load = self.load 76 | n.callPointLoad = self.callPointLoad 77 | return n 78 | 79 | def __str__(self): 80 | """ 81 | Print out information of node 82 | """ 83 | s = 'Position: ' + str(self.X_.tolist()) 84 | s += '\n' 85 | s += 'Number of degree of freedoms: ' + str(self.Ndof) 86 | s += '\n' 87 | s += 'Time order: '+str(self.timeOrder) 88 | s += '\n' 89 | s += 'Equation number and Values: \n' 90 | s += ' \t number \t U \t V \t A: \n' 91 | for i in range(self.Ndof): 92 | s += 'DOF '+ str(i) + ':\t'+ \ 93 | str(self.ID[i])+'\t'+"{:.3e}".format(self.u_[i]) 94 | if self.timeOrder > 0: 95 | s+='\t'+"{:.3e}".format(self.v_[i]) 96 | else: 97 | s+='\tNaN' 98 | if self.timeOrder == 2: 99 | s+='\t'+"{:.3e}".format(self.a_[i]) 100 | else: 101 | s+='\tNaN' 102 | s += '\n' 103 | s += 'Load: \n' 104 | for i in range(self.Ndof): 105 | s += 'DOF '+ str(i) + ':\t'+ "{:.3e}".format(self.load[i]) 106 | s += '\n' 107 | return s 108 | 109 | def get_id_number(self): 110 | return self.id_number 111 | 112 | def get_dtype(self): 113 | """ 114 | Return datatype of arrays in this node 115 | """ 116 | return self.dtype 117 | 118 | def getNdof(self): 119 | """ 120 | Return number of degree of freedoms 121 | """ 122 | return self.Ndof 123 | 124 | def getID(self): 125 | """ 126 | Return the indices of equations 127 | """ 128 | return self.ID 129 | 130 | def getU(self): 131 | """ 132 | Return the displacements at this node 133 | """ 134 | return self.u_ 135 | 136 | def getV(self): 137 | """ 138 | Return the velocity at this node 139 | """ 140 | return self.v_ 141 | 142 | def getA(self): 143 | """ 144 | Return the acceleration at this node 145 | """ 146 | return self.a_ 147 | 148 | def getValue(self, val = 'u'): 149 | """ 150 | Return value at node 151 | val = 'u' return displacement, variable 152 | val = 'v' velocity , time derivative of variable 153 | val = 'a' acceleration, second time derivative of variable 154 | """ 155 | if val == 'u': 156 | return self.u_ 157 | if val == 'v': 158 | return self.v_ 159 | if val == 'a': 160 | return self.a_ 161 | 162 | def setU(self, u): 163 | """ 164 | set displacement 165 | """ 166 | self.u_ = ia.array(u,self.dtype) 167 | 168 | def setV(self, v): 169 | """ 170 | set velocity 171 | """ 172 | self.v_ = ia.array(v,self.dtype) 173 | 174 | def setA(self, a): 175 | """ 176 | set acceleration 177 | """ 178 | self.a_ = ia.array(a,self.dtype) 179 | 180 | def setX(self, x): 181 | self.X_[0] = x[0] 182 | self.X_[1] = x[1] 183 | 184 | def friendOF(self, n, idof = 0): 185 | # if n is self: 186 | # return 187 | if (n is not None) and (n.friendNode is None): 188 | self.friendNode = n 189 | self.friendDOF = idof 190 | self.freedom[idof] = False 191 | self.u_[idof] = 0.0 192 | 193 | def controlVar(self, idof): 194 | if idof < 0: 195 | return 196 | self.controlledDOF = idof 197 | 198 | def connect(self, *arg): 199 | """ 200 | Connect global vector U,V,A to local u_,v_,a_ 201 | """ 202 | U = arg[0] 203 | V = arg[1] 204 | A = arg[2] 205 | Ud = arg[3] 206 | 207 | for i in range(self.Ndof): 208 | idx = self.ID[i] 209 | if idx >= 0: 210 | self.u_.connect(i, U, idx) 211 | elif idx < -1: 212 | self.u_.connect(i, Ud, -idx - 2) 213 | try: 214 | for i in range(self.Ndof): 215 | idx = self.ID[i] 216 | if idx >= 0: 217 | self.v_.connect(i, V, idx) 218 | except (AttributeError, TypeError): 219 | pass 220 | 221 | try: 222 | for i in range(self.Ndof): 223 | idx = self.ID[i] 224 | if idx >= 0: 225 | self.a_.connect(i, A, idx) 226 | except (AttributeError, TypeError): 227 | pass 228 | 229 | def getFromGlobalU(self, uGlob, x): 230 | """ 231 | Get nodal value from global vector 232 | Input: 233 | uGlob : global vector 234 | x: to store result 235 | Return nodal value, None if uGlob == None 236 | """ 237 | if not uGlob is None: 238 | if x is None: 239 | x = np.empty(self.Ndof,self.dtype) 240 | for i in range(self.Ndof): 241 | idx = self.ID[i] 242 | if idx >= 0: 243 | x[i] = uGlob[idx] 244 | else: 245 | x[i] = self.u_[i] 246 | else: 247 | return None 248 | return x 249 | 250 | def getFromGlobalV(self, uGlob, x): 251 | """ 252 | Get nodal value from global vector 253 | Input: 254 | uGlob : global vector 255 | x: to store result 256 | Return nodal value, None if uGlob == None 257 | """ 258 | if not uGlob is None: 259 | if x is None: 260 | x = np.empty(self.Ndof,self.dtype) 261 | for i in range(self.Ndof): 262 | idx = self.ID[i] 263 | if idx >= 0: 264 | x[i] = uGlob[idx] 265 | else: 266 | x[i] = self.v_[i] 267 | else: 268 | x = None 269 | return x 270 | 271 | def getFromGlobalA(self, uGlob, x): 272 | """ 273 | Get nodal value from global vector 274 | Input: 275 | uGlob : global vector 276 | x: to store result 277 | Return nodal value, None if uGlob == None 278 | """ 279 | if not uGlob is None: 280 | if x is None: 281 | x = np.empty(self.Ndof,self.dtype) 282 | for i in range(self.Ndof): 283 | idx = self.ID[i] 284 | if idx >= 0: 285 | x[i] = uGlob[idx] 286 | else: 287 | x[i] = self.a_[i] 288 | else: 289 | x = None 290 | return x 291 | 292 | def getFromGlobal(self, data, u, v, a): 293 | """ 294 | Get nodal values from global vectors 295 | Input: 296 | data: a class that have methods return global vectors 297 | Return nodal displacement, velocity and acceleration 298 | None if global displacement, velocity or acceleration is None 299 | correspondingly 300 | """ 301 | uGlob = data.getU() 302 | vGlob = data.getV() 303 | aGlob = data.getA() 304 | return self.getFromGlobalU(uGlob,u), self.getFromGlobalV(vGlob,v),\ 305 | self.getFromGlobalA(aGlob,a) 306 | 307 | 308 | def updateU(self, uGlobal): 309 | """ 310 | update current value of displacement 311 | Input: 312 | uGlobal: global vector of displacement 313 | """ 314 | if uGlobal is None: 315 | return 316 | for i in range(self.Ndof): 317 | idx = self.ID[i] 318 | if idx >= 0: 319 | self.u_[i] = uGlobal[idx] 320 | 321 | def updateV(self, vGlobal): 322 | """ 323 | update current value of velocity 324 | Input: 325 | vGlobal: global vector of velocity 326 | """ 327 | if self.v_ is None: 328 | return 329 | if vGlobal is None: 330 | return 331 | for i in range(self.Ndof): 332 | idx = self.ID[i] 333 | if idx >= 0: 334 | self.v_[i] = vGlobal[idx] 335 | 336 | def updateA(self, aGlobal): 337 | """ 338 | update current value of acceleration 339 | Input: 340 | aGlobal: global vector of acceleration 341 | """ 342 | if self.a_ is None: 343 | return 344 | if aGlobal is None: 345 | return 346 | for i in range(self.Ndof): 347 | idx = self.ID[i] 348 | if idx >= 0: 349 | self.a_[i] = aGlobal[idx] 350 | 351 | def getTimeOrder(self): 352 | """ 353 | get time order of this node 354 | """ 355 | return self.timeOrder 356 | 357 | def setConstraint(self, free, val, dof): 358 | """ 359 | Set the constraint for specific dof 360 | Input: 361 | free: True if this dof is free 362 | val: Value of this dof 363 | dof: index of dof 364 | """ 365 | if dof < 0 or dof >= self.Ndof: 366 | raise WrongDofIndexException 367 | self.u_[dof] = val 368 | self.freedom[dof] = free 369 | if not free: 370 | if math.fabs(val) > 1.0e-14: 371 | self.NonHomogeneousDirichlet = True 372 | 373 | def hasNonHomogeneousDirichlet(self): 374 | """ 375 | Return True if node has nonhomogeneous Dirichlet boundary conditions 376 | """ 377 | return self.NonHomogeneousDirichlet 378 | 379 | def assembleGlobalDirichlet(self, vGlobalD): 380 | """ 381 | assemble nonhomogeneous Dirichlet boundary conditions to 382 | global displacement vector 383 | Input: 384 | vGlobalD: global displacement vector 385 | """ 386 | if vGlobalD is None: 387 | return 388 | for i in range(self.Ndof): 389 | idx = self.ID[i] 390 | if idx < -1: 391 | vGlobalD[-idx-2] = self.u_[i] 392 | 393 | def assembleU(self, uGlobal): 394 | """ 395 | assemble diplacement of this node to global displacement vector 396 | Input: 397 | uGlobal: global displacement vector 398 | """ 399 | if uGlobal is None: 400 | return 401 | for i in range(self.Ndof): 402 | idx = self.ID[i] 403 | if idx >= 0: 404 | uGlobal[idx] = self.u_[i] 405 | 406 | def assembleV(self, vGlobal): 407 | """ 408 | assemble velocity of this node to global velocity vector 409 | Input: 410 | uGlobal: global velocity vector 411 | """ 412 | if vGlobal is None: 413 | return 414 | for i in range(self.Ndof): 415 | idx = self.ID[i] 416 | if idx >= 0: 417 | vGlobal[idx] = self.v_[i] 418 | 419 | def assembleA(self, aGlobal): 420 | """ 421 | assemble acceleration of this node to global acceleration vector 422 | Input: 423 | uGlobal: global acceleration vector 424 | """ 425 | if aGlobal is None: 426 | return 427 | for i in range(self.Ndof): 428 | idx = self.ID[i] 429 | if idx >= 0: 430 | aGlobal[idx] = self.a_[i] 431 | 432 | def setLoad(self, load, dof): 433 | """ 434 | Set the load or source for specific dof 435 | Input: 436 | load: value or function of load or source 437 | if load is a function, it should has form f(t) 438 | with t is time 439 | dof: index of dof 440 | """ 441 | if dof < 0 or dof >= self.Ndof: 442 | raise WrongDofIndexException 443 | if callable(load): 444 | self.callPointLoad = True 445 | self.load[dof] = load 446 | 447 | def getPointLoad(self, dof, t = 0): 448 | """ 449 | Get point load at degree of freedom dof 450 | Input: 451 | dof: degree of freedom 452 | t: time, default is t=0 453 | Return value of load 454 | """ 455 | if self.callPointLoad: 456 | return self.load[dof](self.X_,t) 457 | return self.load[dof] 458 | 459 | def getPointLoadToGlobal(self, Load, t= 0): 460 | """ 461 | Get point load to global load vector 462 | Input: 463 | Load: global load vector 464 | t: time, default is t=0 465 | """ 466 | for i in range(self.Ndof): 467 | idx = self.ID[i] 468 | if idx >= 0: 469 | Load[idx] = self.getPointLoad(i,t) 470 | 471 | def hasPointLoad(self): 472 | """ 473 | If this node has point load, return True 474 | else, return False 475 | """ 476 | for i in range(self.Ndof): 477 | if callable(self.load[i]): 478 | return True 479 | if math.fabs(self.load[i]) > 1.0e-14: 480 | return True 481 | return False 482 | 483 | def timeDependentLoad(self): 484 | """ 485 | Return True if there is time dependent load, False otherwise 486 | """ 487 | return any(callable(ld) for ld in self.load) 488 | 489 | def addLoadTo(self, vGlobal): 490 | """ 491 | add point load or source to global load vector 492 | Input: 493 | vGlobal: global load vector 494 | """ 495 | for i in range(self.Ndof): 496 | idx = self.ID[i] 497 | if idx >= 0: 498 | vGlobal[idx] += self.load[i] 499 | 500 | def setID(self, cnt, cntd): 501 | """ 502 | Set the indices ID, return the next counter 503 | Input: 504 | cnt: the counter, current equation index 505 | cntd: the counter of contrainted dofs 506 | Return: (cnt, cntd) counter cnt and cntd after the IDs have been set 507 | """ 508 | for dof in range(self.Ndof): 509 | if self.freedom[dof]: 510 | self.ID[dof] = cnt 511 | cnt += 1 512 | elif math.fabs(self.u_[dof]) > 1.0e-14: 513 | cntd -= 1 514 | self.ID[dof] = cntd 515 | if self.controlledDOF >= 0: 516 | self.IDC = cntd 517 | 518 | return cnt, cntd 519 | 520 | # End of Node class definition 521 | 522 | class WrongDofIndexException(Exception): 523 | """ 524 | Exception for wrong degree of freedom index 525 | """ 526 | pass -------------------------------------------------------------------------------- /Mesh/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyFEM 2 | Python code for Finite Element Method 3 | -------------------------------------------------------------------------------- /Tests/magnetic_plate_buckling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue May 29 17:45:53 2018 5 | 6 | @author: haiau 7 | """ 8 | 9 | import math 10 | import numpy as np 11 | import pylab as pl 12 | import Element.FEMBoundary as FB 13 | import Element.QuadElement as QE 14 | import Mesh.FEMNode as FN 15 | import Mesh.FEMMesh as FM 16 | import InOut.FEMOutput as FO 17 | import Material.Material as mat 18 | import Algorithm.NewtonRaphson as NR 19 | import Math.Solver as sv 20 | import Math.IntegrationData as idat 21 | import Mesh.MeshGenerator as mg 22 | 23 | 24 | class LinearMagneticMaterial(mat.Material): 25 | def __init__(self, mur, epsr, sigma, idx): 26 | self.mu0 = mur*4.0e-7*np.pi 27 | self.sigma = sigma 28 | self.eps = epsr*8.854187817e-12 29 | self.dM = np.zeros(2) 30 | self.idx = idx 31 | self.Mu = np.zeros(2) 32 | self.hysteresis = False 33 | 34 | def getID(self): 35 | return self.idx 36 | 37 | class LinearMechanicMaterial(mat.Material): 38 | def __init__(self, Emod, nu, rho, mur): 39 | self.Emod = Emod 40 | self.nu = nu 41 | self.rho = rho 42 | self.lmd = Emod*nu/(1+nu)/(1-2*nu) 43 | self.mu = Emod/2.0/(1+nu) 44 | bigI = np.zeros((2,2,2,2),np.float64) 45 | smallI = np.zeros((2,2,2,2),np.float64) 46 | for i in range(2): 47 | for j in range(2): 48 | for k in range(2): 49 | for n in range(2): 50 | part1 = 0 51 | if(i == n and j == k): 52 | part1 = 1 53 | part2 = 0 54 | if(i == k and j == n): 55 | part2 = 1 56 | bigI[i,j,k,n] = part1 + part2 57 | if(i == j and k == n): 58 | smallI[i,j,k,n] = 1 59 | else: 60 | smallI[i,j,k,n] = 0 61 | bigI *= self.mu 62 | smallI *= self.lmd 63 | self.Cmat = bigI + smallI 64 | self.mu0 = mur*4.0e-7*np.pi 65 | self.mu00 = 4.0e-7*np.pi 66 | self.mur = mur 67 | 68 | def getID(self): 69 | return self.idx 70 | 71 | class MechElement(QE.Quad9Element): 72 | def __init__(self, Nodes, pd, basisFunction, nodeOrder, \ 73 | material, intData, condt = np.array([0.0,0.0])): 74 | QE.Quad9Element.__init__(self,Nodes,pd,basisFunction,\ 75 | nodeOrder,material,intData) 76 | self.rotNs = [] 77 | for i in range(self.intData.getNumberPoint()): 78 | self.rotNs.append(np.empty((self.Ndim,self.Nnod),self.dtype)) 79 | self.rotA = np.empty(self.Ndim,self.dtype) 80 | self.Fg = np.empty((self.Ndim,self.Ndim),self.dtype) 81 | self.Fmg = np.empty((self.Ndim,self.Ndim),self.dtype) 82 | self.Eg = np.empty((self.Ndim,self.Ndim),self.dtype) 83 | self.Sg = np.empty((self.Ndim,self.Ndim),self.dtype) 84 | self.ident = np.eye(self.Ndim,dtype = self.dtype) 85 | self.kt = np.empty((2,2),self.dtype) 86 | self.ktT = np.empty((self.Ndof,self.Ndof),self.dtype) 87 | self.rie = np.empty(2,self.dtype) 88 | self.condt = condt 89 | 90 | def getBasis(self): 91 | QE.Quad9Element.getBasis(self) 92 | self.rotN = self.rotNs[self.ig] 93 | 94 | def updateMat(self, material): 95 | self.material = material 96 | 97 | def prepareElement(self): 98 | self.calculateBasis(self.nodeOrder) 99 | x = np.empty(self.Ndim,self.dtype) 100 | for ig in range(self.intData.getNumberPoint()): 101 | x.fill(0.0) 102 | self.getX(x,self.Ns_[ig]) 103 | for i in range(self.Nnod): 104 | self.rotNs[ig][0,i]= -self.dNs_[ig][1,i] 105 | self.rotNs[ig][1,i]= self.dNs_[ig][0,i] 106 | 107 | def calculateFES(self): 108 | """ 109 | calculate deformation gradient F 110 | """ 111 | # self.rotA[0] = -self.gradu_[1,2] 112 | # self.rotA[1] = self.gradu_[0,2] 113 | np.copyto(self.Fg,self.gradu_[0:2,0:2].T) 114 | self.Fg += self.ident 115 | self.JF = self.Fg[0,0]*self.Fg[1,1]-self.Fg[0,1]*self.Fg[1,0] 116 | self.Fmg[0,0] = self.Fg[1,1]/self.JF 117 | self.Fmg[1,1] = self.Fg[0,0]/self.JF 118 | self.Fmg[0,1] = -self.Fg[0,1]/self.JF 119 | self.Fmg[1,0] = -self.Fg[1,0]/self.JF 120 | self.Cg = np.dot(self.Fg.T,self.Fg) 121 | self.Eg = self.Cg - self.ident 122 | self.Eg *= 0.5 123 | np.einsum('ijkl,kl',self.material.Cmat,self.Eg,out=self.Sg) 124 | 125 | def multiplyCderiv(self, a,b,jnod): 126 | kt = np.einsum('i,i,kj,j',a,self.dN_[:,jnod],self.Fg,b) 127 | kt += np.einsum('i,ki,j,j',a,self.Fg,self.dN_[:,jnod],b) 128 | kt /= self.JF 129 | return kt 130 | 131 | def multiplyFmderiv(self, inod, jnod): 132 | en = -np.einsum('i,ij,j',self.dN_[:,jnod],self.Fmg,self.dN_[:,inod]) 133 | kt = en*self.Fmg 134 | return kt 135 | 136 | def multiplyFderiv(self,b,c,inod,jnod): 137 | en = np.dot(c,self.dN_[:,jnod]) 138 | kt = en*np.einsum('i,j',b,self.dN_[:,inod]) 139 | kt /= self.JF 140 | return kt 141 | 142 | def derivativeJinv(self, jnod): 143 | return -1.0/(self.JF*self.JF)*np.dot(self.dN_[:,jnod],self.Fmg) 144 | # def calculateKLinear(self, K, inod, jnod, t): 145 | # """ 146 | # Calculate Stiffness matrix K 147 | # """ 148 | # 149 | # r = self.x_[0] 150 | # K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 151 | # K[0,0] += self.N_[inod]*self.dN_[0,jnod]/r 152 | # K[0,0] += self.dN_[0,inod]*self.N_[jnod]/r 153 | # K[0,0] += self.N_[inod]*self.N_[jnod]/(r*r) 154 | # K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod] 155 | # K[0,0] /= self.material.mu0 156 | # K[0,0] *= self.getFactor() 157 | 158 | def calculateK(self, K, inod, jnod, t): 159 | kg = np.einsum('i,ij,j',self.dN_[:,inod],self.Sg,self.dN_[:,jnod]); 160 | np.einsum('ij,k,jklm,l,nm',self.Fg,self.dN_[:,inod],\ 161 | self.material.Cmat,self.dN_[:,jnod],self.Fg,out=self.kt) 162 | self.kt += kg*self.ident 163 | self.kt *= self.getFactor() 164 | K[0,0] += self.kt[0,0] 165 | K[0,1] += self.kt[0,1] 166 | K[1,0] += self.kt[1,0] 167 | K[1,1] += self.kt[1,1] 168 | 169 | # Stress tensor coupling 170 | kt33 = np.einsum('i,ij,j',self.rotN[:,inod],self.Cg,self.rotN[:,jnod]) 171 | kt33 /= self.JF*self.material.mu0 172 | # kt33 = np.einsum('i,i',self.rotN[:,inod],self.rotN[:,jnod]) 173 | 174 | # test 175 | # r = self.x_[0] 176 | # Ktest = self.dN_[0,inod]*self.dN_[0,jnod] 177 | # Ktest += self.N_[inod]*self.dN_[0,jnod]/r 178 | # Ktest += self.dN_[0,inod]*self.N_[jnod]/r 179 | # Ktest += self.N_[inod]*self.N_[jnod]/(r*r) 180 | # Ktest += self.dN_[1,inod]*self.dN_[1,jnod] 181 | # Ktest /= self.material.mu0 182 | # kt33 = Ktest 183 | # endtest 184 | 185 | # kt33 = np.dot(self.rotN[:,inod],self.rotN[:,jnod]) 186 | # kt33 /= self.material.mu0 187 | kt33 *= self.getFactor() 188 | kt321 = self.multiplyCderiv(self.rotN[:,inod],self.rotA,jnod) 189 | kt321 /= self.material.mu0 190 | kt321 *= self.getFactor() 191 | 192 | condt = t*self.condt 193 | # condt = 1.0*self.rotA 194 | en = np.einsum('i,ij,j',condt,self.Cg,condt) 195 | en *= 0.5/(self.JF) 196 | kt1212 = self.multiplyFmderiv(inod,jnod) 197 | kt1212 *= en 198 | ktx = self.multiplyCderiv(condt,condt,jnod) 199 | kty = np.dot(self.Fmg,self.dN_[:,inod]) 200 | kt1212 += 0.5*np.einsum('i,j',kty,ktx) 201 | ktxx = self.multiplyFderiv(condt,condt,inod,jnod) 202 | kt1212 -= ktxx 203 | 204 | en = 0.5*np.einsum('i,ij,j',condt,self.Cg,condt) 205 | te = en*self.Fmg 206 | te -= np.einsum('i,jk,k',condt,self.Fg,condt) 207 | ktj = self.derivativeJinv(jnod) 208 | kt1212 += np.einsum('kj,j,l',te,self.dN_[:,inod],ktj) 209 | 210 | kt1212 *= (2.0-self.material.mur) 211 | kt1212 *= self.getFactor()/self.material.mu0 212 | 213 | en1 = np.einsum('i,ij,j',self.rotN[:,jnod],self.Cg,self.rotA) 214 | en1 += np.einsum('i,ij,j',self.rotA,self.Cg,self.rotN[:,jnod]) 215 | en1 /= 2.0*self.JF 216 | kt123 = np.dot(self.Fmg,self.dN_[:,inod]) 217 | kt123 *= en1 218 | en2 = np.einsum('i,ij,j',self.dN_[:,inod],self.Fg,self.rotA) 219 | en3 = np.einsum('i,ij,j',self.dN_[:,inod],self.Fg,self.rotN[:,jnod]) 220 | kt123 -= en2/self.JF*self.rotN[:,jnod] 221 | kt123 -= en3/self.JF*self.rotA 222 | kt123 /= self.material.mu0 223 | kt123 *= (2.0-self.material.mur) 224 | kt123 *= self.getFactor() 225 | 226 | K[0,0] -= kt1212[0,0] 227 | K[0,1] -= kt1212[0,1] 228 | K[1,0] -= kt1212[1,0] 229 | K[1,1] -= kt1212[1,1] 230 | 231 | # K[2,2] += kt33 232 | # K[2,0] += kt321[0] 233 | # K[2,1] += kt321[1] 234 | 235 | # K[0,2] -= kt123[0] 236 | # K[1,2] -= kt123[1] 237 | 238 | # def calculateDLinear(self, D, inod, jnod, t): 239 | # """ 240 | # Calculate Damping matrix D 241 | # """ 242 | # D[0,0] = self.N_[inod]*self.N_[jnod] 243 | # D *= self.material.sigma*self.getFactor() 244 | # 245 | # def calculateMLinear(self, M, inod, jnod, t): 246 | # """ 247 | # Calculate Mass matrix M 248 | # """ 249 | # M[0,0] = self.N_[inod]*self.N_[jnod] 250 | # M *= self.material.eps*self.getFactor() 251 | 252 | def calculateR(self, R, inod, t): 253 | """ 254 | Calculate load matrix R 255 | """ 256 | np.einsum('ij,jk,k',self.Fg,self.Sg,self.dN_[:,inod],out=self.rie[0:2]) 257 | # np.einsum('i,ijkl,kl',self.dN_[:,inod],self.material.Cmat,\ 258 | # self.gradu_,out=self.rie) 259 | self.rie *= self.getFactor() 260 | R[0] += self.rie[0] 261 | R[1] += self.rie[1] 262 | 263 | #stress tensor coupling 264 | condt = self.condt 265 | # condt = 1.0*self.rotA 266 | en = np.einsum('i,ij,j',condt,self.Cg,condt) 267 | en *= 0.5/(self.JF*self.material.mu0) 268 | rie = np.dot(self.Fmg,self.dN_[:,inod]) 269 | rie *= en 270 | en1 = np.einsum('i,ij,j',self.dN_[:,inod],self.Fg,condt) 271 | en1 /= self.JF*self.material.mu0 272 | rie -= en1*condt 273 | 274 | rie *= (2.0-self.material.mur) 275 | rie *= self.getFactor() 276 | 277 | R[0] -= t*t*rie[0] 278 | R[1] -= t*t*rie[1] 279 | self.RiLambd[inod][0] += t*rie[0] 280 | self.RiLambd[inod][1] += t*rie[1] 281 | 282 | # R[0] -= rie[0] 283 | # R[1] -= rie[1] 284 | 285 | r3 = np.einsum('i,ij,j',self.rotN[:,inod],self.Cg,self.rotA) 286 | r3 /= self.JF*self.material.mu0 287 | # r3 = np.einsum('i,i',self.rotN[:,inod],self.rotA) 288 | # r3 = np.dot(self.rotN[:,inod],self.rotA) 289 | # r3 /= self.material.mu0 290 | # R[2] += r3*self.getFactor() 291 | 292 | # def calculateRe(self, R, inod, t): 293 | # re = self.N_[inod]*self.getBodyLoad(t) 294 | # re *= -self.getFactor()*self.material.rho 295 | # R[0] = 0.0 296 | # R[1] = re 297 | # R[2] = 10.0*self.getFactor() 298 | 299 | 300 | class MagMech(FB.NeumannBoundary,MechElement): 301 | def __init__(self, Nodes, pd, basisFunction, nodeOrder, intData,\ 302 | normv, ide, condt): 303 | FB.NeumannBoundary.__init__(self,Nodes,pd,basisFunction,\ 304 | nodeOrder,intData,normv,ide) 305 | self.rotNs = [] 306 | for i in range(self.intData.getNumberPoint()): 307 | self.rotNs.append(np.empty((self.Ndim,self.Nnod),self.dtype)) 308 | self.rotA = np.empty(self.Ndim,self.dtype) 309 | self.Fg = np.empty((self.Ndim,self.Ndim),self.dtype) 310 | self.Fmg = np.empty((self.Ndim,self.Ndim),self.dtype) 311 | self.Eg = np.empty((self.Ndim,self.Ndim),self.dtype) 312 | self.Sg = np.empty((self.Ndim,self.Ndim),self.dtype) 313 | self.ident = np.eye(self.Ndim,dtype = self.dtype) 314 | self.kt = np.empty((self.Ndof,self.Ndof),self.dtype) 315 | self.ktT = np.empty((self.Ndof,self.Ndof),self.dtype) 316 | self.rie = np.empty(self.Ndof,self.dtype) 317 | self.tangent = np.cross(np.array([self.normv[0],self.normv[1],0.0]),\ 318 | np.array([0.0,0.0,1.0]))[0:2] 319 | self.tangent /= np.linalg.norm(self.tangent) 320 | self.condt = condt 321 | 322 | def multiplyFmderiv(self, b, jnod): 323 | kt = np.einsum('n,nj,i,ik',self.dN_[:,jnod],self.Fmg,b,self.Fmg) 324 | kt *= -1.0 325 | return kt 326 | 327 | def multiplyFderiv(self,a,b,c,jnod): 328 | nB = np.dot(a,b) 329 | nB *= np.dot(self.dN_[:,jnod],c) 330 | kt = nB*self.ident 331 | kt /= self.JF 332 | return kt 333 | 334 | def calculateK(self, K, inod, jnod, t): 335 | condt = t*self.condt 336 | kt1212 = self.multiplyFderiv(self.normv,condt,condt,jnod) 337 | kt1212 *= -self.N_[inod] 338 | kt = self.multiplyCderiv(condt,condt,jnod) 339 | kt *= 0.5 340 | normv = np.dot(self.normv,self.Fmg)*self.N_[inod] 341 | kt1212 += np.einsum('i,j',normv,kt) 342 | ktm = self.multiplyFmderiv(self.normv,jnod) 343 | ktm *= self.N_[inod] 344 | ktm *= np.einsum('i,ij,j',condt,self.Cg,condt)*0.5/self.JF 345 | kt1212 += ktm 346 | 347 | te = np.einsum('i,ij,j',condt,self.Cg,condt)*np.dot(self.normv,self.Fmg) 348 | te -= np.dot(self.normv,condt)*np.dot(self.Fg,condt) 349 | te *= self.N_[inod] 350 | ktx = self.derivativeJinv(jnod) 351 | kt1212 += np.einsum('i,j',te,ktx) 352 | 353 | kt1212 *= self.getFactor()/self.material.mu00 354 | 355 | # kt321 = self.multiplyCderiv(self.tangent,self.tangent,jnod) 356 | # kt321 *= np.dot(condt,self.tangent)*self.N_[inod] 357 | # kt321 *= -self.getFactor()/self.material.mu00 358 | 359 | K[0,0] += kt1212[0,0] 360 | K[0,1] += kt1212[0,1] 361 | K[1,0] += kt1212[1,0] 362 | K[1,1] += kt1212[1,1] 363 | # K[2,0] += kt321[0] 364 | # K[2,1] += kt321[1] 365 | 366 | 367 | def calculateR(self, R, inod, t): 368 | condt = 1.0*self.condt 369 | en = np.einsum('i,ij,j',condt,self.Cg,condt) 370 | en /= 2.0*self.JF 371 | ri12 = en*np.dot(self.normv,self.Fmg) 372 | en = np.dot(self.normv,condt)/self.JF 373 | ri12 -= en*np.dot(condt,self.Fg) 374 | ri12 *= self.N_[inod]*self.getFactor()/self.material.mu00 375 | 376 | # en = np.einsum('i,ij,j',self.tangent,self.Cg,self.tangent)/self.JF 377 | # en *= np.dot(self.tangent,condt) 378 | # en = np.dot(self.tangent,self.condt) 379 | # ri3 = -en*self.N_[inod]*self.getFactor()/self.material.mu00 380 | 381 | R[0] += t*t*ri12[0] 382 | R[1] += t*t*ri12[1] 383 | self.RiLambd[inod][0] += t*ri12[0] 384 | self.RiLambd[inod][1] += t*ri12[1] 385 | # R[2] += ri3 386 | 387 | Ndof = 2 388 | tOrder = 0 389 | Ng = [3,3] 390 | numberSteps = 20 391 | tol = 1.0e-8 392 | 393 | intDat = idat.GaussianQuadrature(Ng, 2, idat.Gaussian1D) 394 | intDatB = idat.GaussianQuadrature(3, 1, idat.Gaussian1D) 395 | 396 | condt = np.array([0.0,1.0]) 397 | 398 | def loadfunc(x, t): 399 | return 9.8 400 | 401 | nodeOrder = [[0,1,2,0,1,2,0,1,2], 402 | [0,0,0,1,1,1,2,2,2]] 403 | 404 | def create_mesh(): 405 | H = 0.001 406 | R = 0.1 407 | nodes = [] 408 | nodes.append(FN.Node([0,0],Ndof,timeOrder = tOrder)) 409 | nodes.append(FN.Node([R,0],Ndof,timeOrder = tOrder)) 410 | 411 | edges = [mg.Edge(nodes[i],nodes[i+1]) for i in range(len(nodes)-1)] 412 | 413 | geo = mg.Geometry() 414 | d = np.array([0.0,1.0]) 415 | s = H 416 | 417 | for e in edges: 418 | geo.addPolygons(e.extendToQuad(d,s)) 419 | 420 | polys = geo.getPolygons() 421 | polys[0].setDivisionEdge13(4) 422 | polys[0].setDivisionEdge24(1) 423 | 424 | mat1 = LinearMechanicMaterial(2.1e11, 0.3, 7.8e3,10000.0) 425 | 426 | polys[0].setMaterial(mat1) 427 | 428 | geo.mesh() 429 | 430 | [nodesx, elems, mats, bdls] = geo.getMesh(None,mg.nodesQuad9,2) 431 | 432 | for n in nodesx: 433 | # n.setConstraint(False, 0.0, 0) 434 | # n.setConstraint(False, 0.0, 1) 435 | # if math.fabs(n.getX()[0]-R)<1.0e-14: 436 | # n.setConstraint(False, 0.0, 0) 437 | # n.setConstraint(False, 0.0, 1) 438 | # n.setConstraint(False, condt[1]*n.getX()[0],2) 439 | if math.fabs(n.getX()[0])<1.0e-14 and math.fabs(n.getX()[1]-H*0.5)<1.0e-14: 440 | n.setConstraint(False, 0.0, 1) 441 | if math.fabs(n.getX()[0])<1.0e-14: 442 | n.setConstraint(False, 0.0, 0) 443 | # n.setConstraint(False,0.0,2) 444 | # if math.fabs(n.getX()[1])<1.0e-14 or\ 445 | # math.fabs(n.getX()[1]-H)<1.0e-14: 446 | # n.setConstraint(False, condt[1]*n.getX()[0],2) 447 | 448 | 449 | elements = [] 450 | for i,e in enumerate(elems): 451 | m = mats[i] 452 | elements.append(MechElement(e,[2,2],QE.LagrangeBasis1D,\ 453 | nodeOrder,m,intDat,condt)) 454 | 455 | mesh = FM.MeshWithBoundaryElement() 456 | mesh.addNodes(nodesx) 457 | mesh.addElements(elements) 458 | 459 | geo.meshBoundary() 460 | [nodes1, elems1, normBndVec] = geo.getBoundaryMesh(None,\ 461 | mg.nodesBoundaryQuad9,Ndof) 462 | 463 | elementBs = [] 464 | for i,e in enumerate(elems1): 465 | elementBs.append(MagMech(e,2,QE.LagrangeBasis1D,\ 466 | QE.generateQuadNodeOrder(2,1),intDatB,normBndVec[i],i,condt)) 467 | elementBs[-1].setMaterial(mat1) 468 | 469 | 470 | mesh.addBoundaryElements(elementBs) 471 | 472 | return mesh 473 | 474 | def contraction21412(A,b,C,d,E,out=None): 475 | if out is None: 476 | out = np.zeros([2,2],np.float64) 477 | for i in range(2): 478 | for j in range(2): 479 | for k in range(2): 480 | for l in range(2): 481 | for m in range(2): 482 | for n in range(2): 483 | out[i,j] += A[i,k]*b[l]*C[k,l,m,n]*d[m]*E[j,n] 484 | return out 485 | 486 | mesh = create_mesh() 487 | 488 | mesh.generateID() 489 | 490 | output = FO.StandardFileOutput('/home/haiau/Documents/result.dat') 491 | 492 | #alg = NR.LoadControlledNewtonRaphson(mesh,output,sv.numpySolver(),numberSteps) 493 | alg = NR.ArcLengthControlledNewtonRaphson(mesh,output,sv.numpySolver(),numberSteps,\ 494 | arcl=100.0) 495 | #alg.enableVariableConstraint() 496 | alg.calculate(True) 497 | 498 | #_,inod = mesh.findNodeNear(np.array([0.05,-0.005])) 499 | #testout,tout = output.readOutput('/home/haiau/Documents/result.dat',list(range(10)),inod,'u') 500 | #testout = [t[0][1] for t in testout] 501 | 502 | #output.updateToMesh(mesh,9) 503 | #X,Y,Z = mesh.meshgridValue([0.0,1.0,-0.05,0.05],0.01,1.0e-8) 504 | -------------------------------------------------------------------------------- /Tests/test_induction_motor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Jan 3 14:58:32 2018 4 | 5 | @author: haiau 6 | """ 7 | 8 | import math 9 | import numpy as np 10 | import pylab as pl 11 | import Element.AxisymmetricElement as AE 12 | import Element.FEMElement as FE 13 | import Element.QuadElement as QE 14 | import Element.PolarElement as PE 15 | import Mesh.FEMNode as FN 16 | import Mesh.FEMMesh as FM 17 | import InOut.FEMOutput as FO 18 | import Material.Material as mat 19 | import Algorithm.NewmarkAlgorithm as NM 20 | import Math.Solver as sv 21 | import Math.IntegrationData as idat 22 | import Math.SingularIntegration as SI 23 | import Mesh.MeshGenerator as mg 24 | 25 | 26 | class LinearMagneticMaterial(mat.Material): 27 | def __init__(self, mur, epsr, sigma, idx): 28 | self.mu0 = mur*4.0e-7*np.pi 29 | self.sigma = sigma 30 | self.eps = epsr*8.854187817e-12 31 | self.dM = np.zeros(2) 32 | self.idx = idx 33 | self.Mu = np.zeros(2) 34 | self.hysteresis = False 35 | 36 | def getID(self): 37 | return self.idx 38 | 39 | class PolarMagnetic(QE.PolarQuadElement): 40 | def __init__(self, Nodes, pd, basisFunction, nodeOrder,material, intData): 41 | QE.PolarQuadElement.__init__(self,Nodes,pd,basisFunction,\ 42 | nodeOrder,material,intData) 43 | self.store = True 44 | self.linear = True 45 | 46 | def getB(self): 47 | B = np.array([self.gradu_[1,0]/self.x_[0],-self.gradu_[0,0]]) 48 | return B 49 | 50 | def updateMat(self, material): 51 | self.material = material 52 | 53 | def calculateKLinear(self, K, inod, jnod, t): 54 | """ 55 | Calculate Stiffness matrix K 56 | """ 57 | 58 | r = self.x_[0] 59 | K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 60 | K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod]/(r*r) 61 | K[0,0] /= self.material.mu0 62 | K[0,0] *= self.getFactor() 63 | 64 | def calculateDLinear(self, D, inod, jnod, t): 65 | """ 66 | Calculate Damping matrix D 67 | """ 68 | D[0,0] = self.N_[inod]*self.N_[jnod] 69 | D *= self.material.sigma*self.getFactor() 70 | 71 | def calculateMLinear(self, M, inod, jnod, t): 72 | """ 73 | Calculate Mass matrix M 74 | """ 75 | M[0,0] = self.N_[inod]*self.N_[jnod] 76 | M *= self.material.eps*self.getFactor() 77 | 78 | # def calculateR(self, R, inod, t): 79 | # """ 80 | # Calculate load matrix R 81 | # """ 82 | # r = self.x_[0] 83 | # re = 0.0 84 | # if self.material.hysteresis: 85 | # re += self.material.Mu[0]*self.dN_[1,inod] 86 | # re -= self.material.Mu[1]*(self.N_[inod]/r+self.dN_[0,inod]) 87 | # R[0] += re 88 | 89 | def calculateRe(self, R, inod, t): 90 | re = self.N_[inod]*self.getBodyLoad(t) 91 | re *= self.getFactor() 92 | R[0] = re 93 | 94 | class TriangularPolarMagnetic(PE.PolarT6Element): 95 | def __init__(self, Nodes, material, dtype='float64', commonData=None,nG=4): 96 | PE.PolarT6Element.__init__(self,Nodes,material,dtype,commonData,nG) 97 | self.store = True 98 | self.linear = True 99 | 100 | def getB(self): 101 | B = np.array([self.gradu_[1,0]/self.x_[0],-self.gradu_[0,0]]) 102 | return B 103 | 104 | def updateMat(self, material): 105 | self.material = material 106 | 107 | def calculateKLinear(self, K, inod, jnod, t): 108 | """ 109 | Calculate Stiffness matrix K 110 | """ 111 | 112 | r = self.x_[0] 113 | K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 114 | K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod]/(r*r) 115 | K[0,0] /= self.material.mu0 116 | K[0,0] *= self.getFactor() 117 | 118 | def calculateDLinear(self, D, inod, jnod, t): 119 | """ 120 | Calculate Damping matrix D 121 | """ 122 | D[0,0] = self.N_[inod]*self.N_[jnod] 123 | D[0,0] *= self.material.sigma*self.getFactor() 124 | 125 | def calculateMLinear(self, M, inod, jnod, t): 126 | """ 127 | Calculate Mass matrix M 128 | """ 129 | M[0,0] = self.N_[inod]*self.N_[jnod] 130 | M[0,0] *= self.material.eps*self.getFactor() 131 | 132 | # def calculateR(self, R, inod, t): 133 | # """ 134 | # Calculate load matrix R 135 | # """ 136 | # r = self.x_[0] 137 | # re = 0.0 138 | # if self.material.hysteresis: 139 | # re += self.material.Mu[0]*self.dN_[1,inod] 140 | # re -= self.material.Mu[1]*(self.N_[inod]/r+self.dN_[0,inod]) 141 | # R[0] += re 142 | 143 | def calculateRe(self, R, inod, t): 144 | re = self.N_[inod]*self.getBodyLoad(t) 145 | re *= self.getFactor() 146 | R[0] = re 147 | 148 | Ndof = 1 149 | tOrder = 1 150 | Ng = [3,3] 151 | totalTime = 1.0e-1 152 | nrad = -1200.0 153 | #numberTimeSteps = nrad*10 154 | numberTimeSteps = 1200 155 | rho_inf = 0.8 156 | tol = 1.0e-8 157 | load = 3.1e6*math.sqrt(2) 158 | #load = 2045.175/((5.2e-2**2-3.2e-2**2)*np.pi/8.0)*math.sqrt(2.0) 159 | #load = 2045.175*math.sqrt(2)/(np.pi/8*(5.2**2-3.2**2)) 160 | 161 | 162 | def d_to_r(deg): 163 | return deg*2.0*np.pi/360.0 164 | 165 | def quad_mesh_block(phi0,phi1,r0,r1,nphi,nr,bnd=0): 166 | if phi0 > phi1: 167 | phi1 += 2*np.pi 168 | if r0 > r1: 169 | r0,r1 = r1,r0 170 | if r0 < 0.0 or r1 < 0.0: 171 | raise ValueError 172 | if phi0 == phi1 or r0 == r1 or nphi <= 0 or nr <= 0: 173 | raise ValueError 174 | phia = np.linspace(phi0,phi1,nphi*2+1) 175 | ra = np.linspace(r0,r1,nr*2+1) 176 | R,PHI = np.meshgrid(ra,phia) 177 | elements = [] 178 | nodes = [] 179 | for iphi in range(nphi*2+1): 180 | for ir in range(nr*2+1): 181 | nodes.append([R[iphi,ir],PE.reduce_2pi(PHI[iphi,ir])]) 182 | numr = nr*2+1 183 | for iphi in range(nphi): 184 | for ir in range(nr): 185 | it = [] 186 | st = iphi*2*numr+ir*2 187 | it.append(st) 188 | it.append(st+1) 189 | it.append(st+2) 190 | it.append(st+numr) 191 | it.append(st+numr+1) 192 | it.append(st+numr+2) 193 | it.append(st+numr*2) 194 | it.append(st+numr*2+1) 195 | it.append(st+numr*2+2) 196 | elements.append(it) 197 | if bnd == 1: 198 | belm = [] 199 | # boundary on inner face 200 | for iphi in range(nphi): 201 | it = [] 202 | st = iphi*2*numr 203 | it.append(st) 204 | it.append(st+numr) 205 | it.append(st+numr*2) 206 | belm.append(it) 207 | return nodes,elements,belm 208 | elif bnd == 2: 209 | belm = [] 210 | # boundary on outer face 211 | for iphi in range(nphi): 212 | it = [] 213 | st = iphi*2*numr+numr-1 214 | it.append(st) 215 | it.append(st+numr) 216 | it.append(st+numr*2) 217 | belm.append(it) 218 | return nodes,elements,belm 219 | return nodes,elements,None 220 | 221 | def quad_mesh_ring(r0,r1,n,nr,bnd=0): 222 | if r0 > r1: 223 | r0,r1 = r1,r0 224 | if r0 == r1 or n <= 1 or nr <= 0: 225 | raise ValueError 226 | nodes,elements,belm = quad_mesh_block(0.0,2.0*np.pi,r0,r1,n,nr,bnd) 227 | nlastnodes = nr*2+1 228 | lastnode = len(nodes) 229 | lastnodes = list(range(lastnode-nlastnodes,lastnode)) 230 | nodes = nodes[0:-nlastnodes] 231 | for e in elements: 232 | for i in range(len(e)): 233 | try: 234 | idx = lastnodes.index(e[i]) 235 | e[i] = idx 236 | except ValueError: 237 | pass 238 | 239 | if bnd != 0: 240 | for e in belm: 241 | for i in range(len(e)): 242 | try: 243 | idx = lastnodes.index(e[i]) 244 | e[i] = idx 245 | except ValueError: 246 | pass 247 | return nodes,elements,belm 248 | return nodes,elements,None 249 | 250 | def t6_mesh_pizza(r,n): 251 | if n <= 1 or r <= 0.0: 252 | raise ValueError 253 | phia = np.linspace(0.0,2.0*np.pi,n*2+1) 254 | phia = phia[0:-1] 255 | phib = np.linspace(0.0,2.0*np.pi,n+1) 256 | phib = phib[0:-1] 257 | nodes = [[0.0,0.0]] 258 | elements = [] 259 | for phi in phia: 260 | nodes.append([r,phi]) 261 | for phi in phib: 262 | nodes.append([r*0.5,phi]) 263 | tolnod = n*2+1 264 | for i in range(n): 265 | it = [0] 266 | it.append(i*2+1) 267 | it.append((i*2+2)%(tolnod-1)+1) 268 | it.append(tolnod+(i+1)%n) 269 | it.append(tolnod+i) 270 | it.append(i*2+2) 271 | elements.append(it) 272 | return nodes,elements 273 | 274 | def polarMesh(r0, r1, phi0, phi1 , nphi, nr,\ 275 | Ndof = 1,timeOrder=0,id_number=0,dtype='float64',typ='quad',bnd=0): 276 | if typ == 'pizzat6': 277 | ns,els = t6_mesh_pizza(r1,nphi) 278 | elif typ == 'quad': 279 | ns,els,belm = quad_mesh_block(phi0,phi1,r0,r1,nphi,nr,bnd) 280 | elif typ == 'quadring': 281 | ns,els,belm = quad_mesh_ring(r0,r1,nphi,nr,bnd) 282 | else: 283 | raise ValueError 284 | nodes = [] 285 | for n in ns: 286 | nodes.append(PE.PolarNode(n,Ndof,timeOrder,id_number,dtype)) 287 | elements = [] 288 | for e in els: 289 | elements.append([]) 290 | for ie in e: 291 | elements[-1].append(nodes[ie]) 292 | 293 | if bnd != 0: 294 | belms = [] 295 | for e in belm: 296 | belms.append([]) 297 | for ie in e: 298 | belms[-1].append(nodes[ie]) 299 | return elements,belms 300 | 301 | return elements,None 302 | 303 | nodeOrder = [[0,1,2,0,1,2,0,1,2], 304 | [0,0,0,1,1,1,2,2,2]] 305 | 306 | intDat = idat.GaussianQuadrature(Ng, 2, idat.Gaussian1D) 307 | 308 | def loadfuncA(x,t): 309 | return load*math.cos(60.0*2*np.pi*t) 310 | # return load 311 | def loadfuncAm(x,t): 312 | return -load*math.cos(60.0*2*np.pi*t) 313 | # return -load 314 | 315 | def loadfuncB(x,t): 316 | return load*math.cos(60.0*2*np.pi*t-2.0*np.pi/3.0) 317 | # return 0.0 318 | 319 | def loadfuncBm(x,t): 320 | return -load*math.cos(60.0*2*np.pi*t-2.0*np.pi/3.0) 321 | # return 0.0 322 | 323 | def loadfuncC(x,t): 324 | return load*math.cos(60.0*2*np.pi*t-4.0*np.pi/3.0) 325 | # return 0.0 326 | 327 | def loadfuncCm(x,t): 328 | return -load*math.cos(60.0*2*np.pi*t-4.0*np.pi/3.0) 329 | # return 0.0 330 | 331 | loadfunc = [loadfuncA,loadfuncBm,loadfuncC,loadfuncAm,loadfuncB,loadfuncCm] 332 | numH = 20 333 | 334 | def create_mesh(): 335 | r0 = 0.0 336 | r1 = 2.0e-2 337 | r2 = 3.0e-2 338 | r2x = 3.05e-2 339 | r3 = 3.2e-2 340 | r3x = 3.15e-2 341 | r4 = 5.2e-2 342 | r5 = 5.7e-2 343 | r6 = 1.0e-1 344 | phi = np.array([22.5,37.5,82.5,97.5,142.5,157.5,202.5,217.5,262.5,277.5,322.5,337.5]) 345 | phi = d_to_r(phi) 346 | mesh = FM.MeshWithGapElement() 347 | mesh.setMaterialConstant(1.0/(np.pi*4.0e-7)) 348 | mesh.setNumberHarmonic(numH) 349 | mesh.setRadiusRatio(r2/r3) 350 | basisf = QE.LagrangeBasis1D 351 | 352 | elements,belm = polarMesh(r0,r1,0.0,2.0*np.pi,50,1,Ndof,tOrder,typ='quadring') 353 | mat_rotor = LinearMagneticMaterial(30.0,0.0,3.72e6,0) 354 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_rotor,intDat) for e in elements] 355 | mesh.addElements(elm) 356 | 357 | elements,belm = polarMesh(r1,r2,0.0,2.0*np.pi,50,2,Ndof,tOrder,typ='quadring',bnd=2) 358 | mat_ind = LinearMagneticMaterial(1.0,0.0,3.72e7,1) 359 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_ind,intDat) for e in elements] 360 | mesh.addElements(elm) 361 | 362 | # elements,belm = polarMesh(r2,r2x,0.0,2.0*np.pi,30,1,Ndof,tOrder,typ='quadring',bnd=2) 363 | # mat_air = LinearMagneticMaterial(1.0,0.0,0.0,4) 364 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_air,intDat) for e in elements] 365 | # mesh.addElements(elm) 366 | 367 | if belm is not None: 368 | belms = [PE.PolarElementGapBoundary(e,2,basisf,1.0/(np.pi*4.0e-7),nHarmonic=numH,rRatio=r2/r3) for e in belm] 369 | for e in belms: 370 | # e.current = False 371 | e.setMovingVelocity([0.0,nrad]) 372 | mesh.addGapElements(belms) 373 | 374 | for i in range(-1,len(phi)-1): 375 | if i%2 == 1: 376 | nr = 8 377 | else: 378 | nr = 2 379 | 380 | # elements,belm = polarMesh(r0,r1,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad') 381 | # mat_rotor = LinearMagneticMaterial(30.0,0.0,3.72e6,0) 382 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_rotor,intDat) for e in elements] 383 | # mesh.addElements(elm) 384 | # 385 | # elements,belm = polarMesh(r1,r2,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad',bnd=2) 386 | # mat_ind = LinearMagneticMaterial(1.0,0.0,3.72e7,1) 387 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_ind,intDat) for e in elements] 388 | # mesh.addElements(elm) 389 | # 390 | # if belm is not None: 391 | # belms = [PE.PolarElementGapBoundary(e,2,basisf,1.0/(np.pi*4.0e-7),nHarmonic=numH,rRatio=r2/r3) for e in belm] 392 | # for e in belms: 393 | # e.current = False 394 | ## e.setMovingVelocity([0.0,nrad*2.0*np.pi]) 395 | ## e.setMovingVelocity([0.0,nrad]) 396 | # mesh.addGapElements(belms) 397 | 398 | # elements,belm = polarMesh(r3x,r3,phi[i],phi[i+1],nr,1,Ndof,tOrder,typ='quadring',bnd=2) 399 | ## mat_air = LinearMagneticMaterial(1.0,0.0,0.0,4) 400 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_air,intDat) for e in elements] 401 | # mesh.addElements(elm) 402 | 403 | # if belm is not None: 404 | # belms = [PE.PolarElementGapBoundary(e,2,basisf,1.0/(np.pi*4.0e-7),normv=[-1.0,0.0]) for e in belm] 405 | # for e in belms: 406 | # e.current = False 407 | # mesh.addGapElements(belms) 408 | 409 | elements,belm = polarMesh(r3,r4,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad',bnd=1) 410 | mat_phase = LinearMagneticMaterial(1.0,0.0,0.0,2) 411 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_phase,intDat) for e in elements] 412 | if i%2 == 1: 413 | for e in elm: 414 | e.setBodyLoad(loadfunc[math.ceil(i/2)],math.ceil(i/2)) 415 | # for n in e.Nodes: 416 | # n.setLoad(loadfunc[math.ceil(i/2)],0) 417 | mesh.addElements(elm) 418 | 419 | 420 | if belm is not None: 421 | belms = [PE.PolarElementGapBoundary(e,2,basisf,1.0/(np.pi*4.0e-7),nHarmonic=numH,rRatio=r2/r3,normv=[-1.0,0.0]) for e in belm] 422 | for e in belms: 423 | e.current = False 424 | mesh.addGapElements(belms) 425 | 426 | elements,belm = polarMesh(r4,r5,phi[i],phi[i+1],nr,1,Ndof,tOrder,typ='quad') 427 | mat_stator = LinearMagneticMaterial(30.0,0.0,0.0,3) 428 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_stator,intDat) for e in elements] 429 | mesh.addElements(elm) 430 | 431 | # elements,belm = polarMesh(r5,r6,phi[i],phi[i+1],nr,1,Ndof,tOrder,typ='quad') 432 | # mat_outside = LinearMagneticMaterial(1.0,0.0,0.0,4) 433 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_outside,intDat) for e in elements] 434 | # mesh.addElements(elm) 435 | 436 | for n in mesh.Nodes: 437 | if n.getX()[0] <= 1.0e-14: 438 | n.setConstraint(False,0.0,0) 439 | if math.fabs(n.getX()[0] - r5) < 1.0e-14: 440 | n.setConstraint(False,0.0,0) 441 | 442 | mesh.generateID() 443 | return mesh 444 | 445 | mesh = create_mesh() 446 | #mesh.plot() 447 | 448 | output1 = FO.StandardFileOutput('/home/haiau/Documents/result_.dat') 449 | output2 = FO.HarmonicsOutput('/home/haiau/Documents/result_harmonics_.dat') 450 | output = [output1,output2] 451 | #alg = NM.NonlinearNewmarkAlgorithm(mesh,tOrder,output,sv.numpySolver(),\ 452 | #totalTime, numberTimeSteps,rho_inf,tol=1.0e-8) 453 | 454 | alg = NM.LinearNewmarkAlgorithm(mesh,tOrder,output,sv.numpySolver(),\ 455 | totalTime, numberTimeSteps,rho_inf) 456 | 457 | alg.calculate() 458 | 459 | #testout1,tout1 = output1.readOutput('/home/haiau/Documents/result_.dat',list(range(0,numberTimeSteps,4)),135,'u') 460 | #testout1 = [t[0][0] for t in testout1] 461 | 462 | tout2,testout2 = output2.readOutput('/home/haiau/Documents/result_harmonics_.dat',list(range(0,numberTimeSteps,4))) 463 | 464 | torque = [] 465 | for t in testout2: 466 | torque.append(0.0) 467 | for i in range(mesh.nHarmonic): 468 | j = i+1 469 | torque[-1] += -j**2*(mesh.rRatio**j)*(t[i][0]*t[i][3]-t[i][2]*t[i][1]) 470 | torque[-1] *= 2.0*np.pi/(4.0e-7*np.pi) 471 | 472 | def intFunc(element): 473 | return element.v_[0]*element.getFactor() 474 | 475 | windingA1 = [] 476 | windingA2 = [] 477 | for e in mesh: 478 | if e.bodyLoad == loadfuncA: 479 | windingA1.append(e) 480 | if e.bodyLoad == loadfuncAm: 481 | windingA2.append(e) 482 | 483 | voltage = [] 484 | times = [] 485 | for i in list(range(numberTimeSteps)): 486 | if i%(numberTimeSteps/200) > 0: 487 | continue 488 | times.append(i*alg.deltaT) 489 | output1.updateToMesh(mesh,i) 490 | res1 = 0.0 491 | for e in windingA1: 492 | res1 = e.integrate(intFunc,res1) 493 | res2 = 0.0 494 | for e in windingA2: 495 | res2 = e.integrate(intFunc,res2) 496 | voltage.append((res1-res2)*3.1e6/2045.175/math.sqrt(2.0)) 497 | 498 | testKtL = alg.KtL 499 | 500 | #output1.updateToMesh(mesh,30) 501 | #X,Y,Z = mesh.meshgridValue([0.0,5.7e-2,0.0,2.0*np.pi-0.005],0.005,1.0e-8) 502 | #X,Y,Z = mesh.meshgridValue([0.0,3.0,-2.0,3.0],0.02,1.0e-8) 503 | -------------------------------------------------------------------------------- /Tests/test_induction_motor_no_move.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Wed Feb 14 12:51:20 2018 4 | 5 | @author: haiau 6 | """ 7 | 8 | # -*- coding: utf-8 -*- 9 | """ 10 | Created on Wed Jan 3 14:58:32 2018 11 | 12 | @author: haiau 13 | """ 14 | 15 | import math 16 | import numpy as np 17 | import pylab as pl 18 | import Element.AxisymmetricElement as AE 19 | import Element.FEMElement as FE 20 | import Element.QuadElement as QE 21 | import Element.PolarElement as PE 22 | import Mesh.FEMNode as FN 23 | import Mesh.FEMMesh as FM 24 | import InOut.FEMOutput as FO 25 | import Material.Material as mat 26 | import Algorithm.NewmarkAlgorithm as NM 27 | import Math.Solver as sv 28 | import Math.IntegrationData as idat 29 | import Math.SingularIntegration as SI 30 | import Mesh.MeshGenerator as mg 31 | 32 | class LinearMagneticMaterial(mat.Material): 33 | def __init__(self, mur, epsr, sigma, idx): 34 | self.mu0 = mur*4.0e-7*np.pi 35 | self.sigma = sigma 36 | self.eps = epsr*8.854187817e-12 37 | self.dM = np.zeros(2) 38 | self.idx = idx 39 | self.Mu = np.zeros(2) 40 | self.hysteresis = False 41 | 42 | def getID(self): 43 | return self.idx 44 | 45 | class PolarMagnetic(QE.PolarQuadElement): 46 | def __init__(self, Nodes, pd, basisFunction, nodeOrder,material, intData): 47 | QE.PolarQuadElement.__init__(self,Nodes,pd,basisFunction,\ 48 | nodeOrder,material,intData) 49 | self.store = True 50 | self.linear = True 51 | 52 | def getB(self): 53 | B = np.array([self.gradu_[1,0]/self.x_[0],-self.gradu_[0,0]]) 54 | return B 55 | 56 | def updateMat(self, material): 57 | self.material = material 58 | 59 | def calculateKLinear(self, K, inod, jnod, t): 60 | """ 61 | Calculate Stiffness matrix K 62 | """ 63 | 64 | r = self.x_[0] 65 | K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 66 | K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod]/(r*r) 67 | K[0,0] /= self.material.mu0 68 | K[0,0] *= self.getFactor() 69 | 70 | def calculateDLinear(self, D, inod, jnod, t): 71 | """ 72 | Calculate Damping matrix D 73 | """ 74 | D[0,0] = self.N_[inod]*self.N_[jnod] 75 | D *= self.material.sigma*self.getFactor() 76 | 77 | def calculateMLinear(self, M, inod, jnod, t): 78 | """ 79 | Calculate Mass matrix M 80 | """ 81 | M[0,0] = self.N_[inod]*self.N_[jnod] 82 | M *= self.material.eps*self.getFactor() 83 | 84 | # def calculateR(self, R, inod, t): 85 | # """ 86 | # Calculate load matrix R 87 | # """ 88 | # r = self.x_[0] 89 | # re = 0.0 90 | # if self.material.hysteresis: 91 | # re += self.material.Mu[0]*self.dN_[1,inod] 92 | # re -= self.material.Mu[1]*(self.N_[inod]/r+self.dN_[0,inod]) 93 | # R[0] += re 94 | 95 | def calculateRe(self, R, inod, t): 96 | re = self.N_[inod]*self.getBodyLoad(t) 97 | re *= self.getFactor() 98 | R[0] = re 99 | 100 | class TriangularPolarMagnetic(PE.PolarT6Element): 101 | def __init__(self, Nodes, material, dtype='float64', commonData=None,nG=4): 102 | PE.PolarT6Element.__init__(self,Nodes,material,dtype,commonData,nG) 103 | self.store = True 104 | self.linear = True 105 | 106 | def getB(self): 107 | B = np.array([self.gradu_[1,0]/self.x_[0],-self.gradu_[0,0]]) 108 | return B 109 | 110 | def updateMat(self, material): 111 | self.material = material 112 | 113 | def calculateKLinear(self, K, inod, jnod, t): 114 | """ 115 | Calculate Stiffness matrix K 116 | """ 117 | 118 | r = self.x_[0] 119 | K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 120 | K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod]/(r*r) 121 | K[0,0] /= self.material.mu0 122 | K[0,0] *= self.getFactor() 123 | 124 | def calculateDLinear(self, D, inod, jnod, t): 125 | """ 126 | Calculate Damping matrix D 127 | """ 128 | D[0,0] = self.N_[inod]*self.N_[jnod] 129 | D[0,0] *= self.material.sigma*self.getFactor() 130 | 131 | def calculateMLinear(self, M, inod, jnod, t): 132 | """ 133 | Calculate Mass matrix M 134 | """ 135 | M[0,0] = self.N_[inod]*self.N_[jnod] 136 | M[0,0] *= self.material.eps*self.getFactor() 137 | 138 | # def calculateR(self, R, inod, t): 139 | # """ 140 | # Calculate load matrix R 141 | # """ 142 | # r = self.x_[0] 143 | # re = 0.0 144 | # if self.material.hysteresis: 145 | # re += self.material.Mu[0]*self.dN_[1,inod] 146 | # re -= self.material.Mu[1]*(self.N_[inod]/r+self.dN_[0,inod]) 147 | # R[0] += re 148 | 149 | def calculateRe(self, R, inod, t): 150 | re = self.N_[inod]*self.getBodyLoad(t) 151 | re *= self.getFactor() 152 | R[0] = re 153 | 154 | Ndof = 1 155 | tOrder = 1 156 | Ng = [3,3] 157 | totalTime = 1.0e-1 158 | nrad = 39.79351 159 | #numberTimeSteps = nrad*10 160 | numberTimeSteps = 200 161 | rho_inf = 0.8 162 | tol = 1.0e-8 163 | load = 3.1e6*math.sqrt(2) 164 | #load = 2045.175*math.sqrt(2)/(np.pi/8*(5.2**2-3.2**2)) 165 | 166 | 167 | def d_to_r(deg): 168 | return deg*2.0*np.pi/360.0 169 | 170 | def quad_mesh_block(phi0,phi1,r0,r1,nphi,nr,bnd=0): 171 | if phi0 > phi1: 172 | phi1 += 2*np.pi 173 | if r0 > r1: 174 | r0,r1 = r1,r0 175 | if r0 < 0.0 or r1 < 0.0: 176 | raise ValueError 177 | if phi0 == phi1 or r0 == r1 or nphi <= 0 or nr <= 0: 178 | raise ValueError 179 | phia = np.linspace(phi0,phi1,nphi*2+1) 180 | ra = np.linspace(r0,r1,nr*2+1) 181 | R,PHI = np.meshgrid(ra,phia) 182 | elements = [] 183 | nodes = [] 184 | for iphi in range(nphi*2+1): 185 | for ir in range(nr*2+1): 186 | nodes.append([R[iphi,ir],PE.reduce_2pi(PHI[iphi,ir])]) 187 | numr = nr*2+1 188 | for iphi in range(nphi): 189 | for ir in range(nr): 190 | it = [] 191 | st = iphi*2*numr+ir*2 192 | it.append(st) 193 | it.append(st+1) 194 | it.append(st+2) 195 | it.append(st+numr) 196 | it.append(st+numr+1) 197 | it.append(st+numr+2) 198 | it.append(st+numr*2) 199 | it.append(st+numr*2+1) 200 | it.append(st+numr*2+2) 201 | elements.append(it) 202 | if bnd == 1: 203 | belm = [] 204 | # boundary on inner face 205 | for iphi in range(nphi): 206 | it = [] 207 | st = iphi*2*numr 208 | it.append(st) 209 | it.append(st+numr) 210 | it.append(st+numr*2) 211 | belm.append(it) 212 | return nodes,elements,belm 213 | elif bnd == 2: 214 | belm = [] 215 | # boundary on outer face 216 | for iphi in range(nphi): 217 | it = [] 218 | st = iphi*2*numr+numr-1 219 | it.append(st) 220 | it.append(st+numr) 221 | it.append(st+numr*2) 222 | belm.append(it) 223 | return nodes,elements,belm 224 | return nodes,elements,None 225 | 226 | def quad_mesh_ring(r0,r1,n,nr,bnd=0): 227 | if r0 > r1: 228 | r0,r1 = r1,r0 229 | if r0 == r1 or n <= 1 or nr <= 0: 230 | raise ValueError 231 | nodes,elements,belm = quad_mesh_block(0.0,2.0*np.pi,r0,r1,n,nr,bnd) 232 | nlastnodes = nr*2+1 233 | lastnode = len(nodes) 234 | lastnodes = list(range(lastnode-nlastnodes,lastnode)) 235 | nodes = nodes[0:-nlastnodes] 236 | for e in elements: 237 | for i in range(len(e)): 238 | try: 239 | idx = lastnodes.index(e[i]) 240 | e[i] = idx 241 | except ValueError: 242 | pass 243 | 244 | if bnd != 0: 245 | for e in belm: 246 | for i in range(len(e)): 247 | try: 248 | idx = lastnodes.index(e[i]) 249 | e[i] = idx 250 | except ValueError: 251 | pass 252 | return nodes,elements,belm 253 | return nodes,elements,None 254 | 255 | def t6_mesh_pizza(r,n): 256 | if n <= 1 or r <= 0.0: 257 | raise ValueError 258 | phia = np.linspace(0.0,2.0*np.pi,n*2+1) 259 | phia = phia[0:-1] 260 | phib = np.linspace(0.0,2.0*np.pi,n+1) 261 | phib = phib[0:-1] 262 | nodes = [[0.0,0.0]] 263 | elements = [] 264 | for phi in phia: 265 | nodes.append([r,phi]) 266 | for phi in phib: 267 | nodes.append([r*0.5,phi]) 268 | tolnod = n*2+1 269 | for i in range(n): 270 | it = [0] 271 | it.append(i*2+1) 272 | it.append((i*2+2)%(tolnod-1)+1) 273 | it.append(tolnod+(i+1)%n) 274 | it.append(tolnod+i) 275 | it.append(i*2+2) 276 | elements.append(it) 277 | return nodes,elements 278 | 279 | def polarMesh(r0, r1, phi0, phi1 , nphi, nr,\ 280 | Ndof = 1,timeOrder=0,id_number=0,dtype='float64',typ='quad',bnd=0): 281 | if typ == 'pizzat6': 282 | ns,els = t6_mesh_pizza(r1,nphi) 283 | elif typ == 'quad': 284 | ns,els,belm = quad_mesh_block(phi0,phi1,r0,r1,nphi,nr,bnd) 285 | elif typ == 'quadring': 286 | ns,els,belm = quad_mesh_ring(r0,r1,nphi,nr,bnd) 287 | else: 288 | raise ValueError 289 | nodes = [] 290 | for n in ns: 291 | nodes.append(PE.PolarNode(n,Ndof,timeOrder,id_number,dtype)) 292 | elements = [] 293 | for e in els: 294 | elements.append([]) 295 | for ie in e: 296 | elements[-1].append(nodes[ie]) 297 | 298 | if bnd != 0: 299 | belms = [] 300 | for e in belm: 301 | belms.append([]) 302 | for ie in e: 303 | belms[-1].append(nodes[ie]) 304 | return elements,belms 305 | 306 | return elements,None 307 | 308 | nodeOrder = [[0,1,2,0,1,2,0,1,2], 309 | [0,0,0,1,1,1,2,2,2]] 310 | 311 | intDat = idat.GaussianQuadrature(Ng, 2, idat.Gaussian1D) 312 | 313 | def loadfuncA(x,t): 314 | return load*math.sin(60.0*2*np.pi*t) 315 | # return load 316 | def loadfuncAm(x,t): 317 | return -load*math.sin(60.0*2*np.pi*t) 318 | # return -load 319 | 320 | def loadfuncB(x,t): 321 | return load*math.sin(60.0*2*np.pi*t-2.0*np.pi/3.0) 322 | # return 0.0 323 | 324 | def loadfuncBm(x,t): 325 | return -load*math.sin(60.0*2*np.pi*t-2.0*np.pi/3.0) 326 | # return 0.0 327 | 328 | def loadfuncC(x,t): 329 | return load*math.sin(60.0*2*np.pi*t-4.0*np.pi/3.0) 330 | # return 0.0 331 | 332 | def loadfuncCm(x,t): 333 | return -load*math.sin(60.0*2*np.pi*t-4.0*np.pi/3.0) 334 | # return 0.0 335 | 336 | loadfunc = [loadfuncA,loadfuncBm,loadfuncC,loadfuncAm,loadfuncB,loadfuncCm] 337 | 338 | def create_mesh(): 339 | r0 = 0.0 340 | r1 = 2.0e-2 341 | r2 = 3.0e-2 342 | r3 = 3.2e-2 343 | r4 = 5.2e-2 344 | r5 = 5.7e-2 345 | r6 = 1.0e-1 346 | phi = np.array([22.5,37.5,82.5,97.5,142.5,157.5,202.5,217.5,262.5,277.5,322.5,337.5]) 347 | phi = d_to_r(phi) 348 | mesh = FM.Mesh() 349 | basisf = QE.LagrangeBasis1D 350 | 351 | for i in range(-1,len(phi)-1): 352 | if i%2 == 1: 353 | nr = 4 354 | else: 355 | nr = 2 356 | elements,belm = polarMesh(r0,r1,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad',bnd=1) 357 | mat_rotor = LinearMagneticMaterial(30.0,0.0,3.72e6,0) 358 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_rotor,intDat) for e in elements] 359 | mesh.addElements(elm) 360 | 361 | elements,belm = polarMesh(r1,r2,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad',bnd=1) 362 | mat_ind = LinearMagneticMaterial(1.0,0.0,3.72e7,1) 363 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_ind,intDat) for e in elements] 364 | mesh.addElements(elm) 365 | 366 | elements,belm = polarMesh(r2,r3,phi[i],phi[i+1],nr,4,Ndof,tOrder,typ='quad',bnd=1) 367 | mat_air = LinearMagneticMaterial(1.0,0.0,0.0,4) 368 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_air,intDat) for e in elements] 369 | mesh.addElements(elm) 370 | 371 | elements,belm = polarMesh(r3,r4,phi[i],phi[i+1],nr,2,Ndof,tOrder,typ='quad',bnd=1) 372 | mat_phase = LinearMagneticMaterial(1.0,0.0,0.0,2) 373 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_phase,intDat) for e in elements] 374 | if i%2 == 1: 375 | for e in elm: 376 | e.setBodyLoad(loadfunc[math.ceil(i/2)],math.ceil(i/2)) 377 | # for n in e.Nodes: 378 | # n.setLoad(loadfunc[math.ceil(i/2)],0) 379 | mesh.addElements(elm) 380 | # if belm is not None: 381 | # belms = [PE.PolarElementGapBoundary(e,2,basisf,1.0/(np.pi*4.0e-7),normv=[-1.0,0.0]) for e in belm] 382 | # for e in belms: 383 | # e.current = False 384 | # mesh.addGapElements(belms) 385 | 386 | elements,belm = polarMesh(r4,r5,phi[i],phi[i+1],nr,1,Ndof,tOrder,typ='quad') 387 | mat_stator = LinearMagneticMaterial(30.0,0.0,0.0,3) 388 | elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_stator,intDat) for e in elements] 389 | mesh.addElements(elm) 390 | 391 | # elements,belm = polarMesh(r5,r6,phi[i],phi[i+1],nr,1,Ndof,tOrder,typ='quad') 392 | # mat_outside = LinearMagneticMaterial(1.0,0.0,0.0,4) 393 | # elm = [PolarMagnetic(e,[2,2],basisf,nodeOrder,mat_outside,intDat) for e in elements] 394 | # mesh.addElements(elm) 395 | 396 | for n in mesh.Nodes: 397 | if n.getX()[0] <= 1.0e-14: 398 | n.setConstraint(False,0.0,0) 399 | if math.fabs(n.getX()[0] - r5) < 1.0e-14: 400 | n.setConstraint(False,0.0,0) 401 | 402 | mesh.generateID() 403 | return mesh 404 | 405 | #nodes,elements = quad_mesh_block(d_to_r(337.5),d_to_r(22.5),1.0,2.0,2,2) 406 | #nodes,elements = quad_mesh_ring(1.0,2.0,4,2) 407 | #nodes,elements = t6_mesh_pizza(1.0,4) 408 | 409 | mesh = create_mesh() 410 | #mesh.plot() 411 | 412 | output = FO.StandardFileOutput('/home/haiau/Documents/result_.dat') 413 | #alg = NM.NonlinearAlphaAlgorithm(mesh,tOrder,output,sv.numpySolver(),\ 414 | #totalTime, numberTimeSteps,rho_inf,tol=1.0e-8) 415 | alg = NM.LinearAlphaAlgorithm(mesh,tOrder,output,sv.numpySolver(),\ 416 | totalTime, numberTimeSteps,rho_inf) 417 | 418 | alg.calculate() 419 | 420 | #testout1,tout1 = output1.readOutput('/home/haiau/Documents/result_.dat',list(range(0,numberTimeSteps,4)),135,'u') 421 | #testout1 = [t[0][0] for t in testout1] 422 | 423 | def intFunc(element): 424 | return element.v_[0]*element.getFactor() 425 | 426 | def intLineFunc(element): 427 | mu0 = element.material.mu0 428 | res = -1.0/mu0*element.gradu_[1]*element.gradu_[0]*element.x_[0] 429 | return res 430 | 431 | def intTorque(element): 432 | mu0 = element.material.mu0 433 | res = -1.0/mu0*element.gradu_[1]*element.gradu_[0]*element.getFactor() 434 | return res 435 | 436 | windingA1 = [] 437 | windingA2 = [] 438 | for e in mesh: 439 | if e.bodyLoad == loadfuncA: 440 | windingA1.append(e) 441 | if e.bodyLoad == loadfuncAm: 442 | windingA2.append(e) 443 | 444 | lineint = [] 445 | side = [] 446 | for e in mesh: 447 | if e.material.getID() == 4 and e.Nodes[0].getX()[0]>3.0e-2 + 1.0e-8\ 448 | and e.Nodes[2].getX()[0]<3.2e-2 - 1.0e-8: 449 | lineint.append(e) 450 | if e.Nodes[0].getX()[0] > 3.0+1.0e8: 451 | side.append(4) 452 | else: 453 | side.append(2) 454 | 455 | 456 | def calculateIntegrals(): 457 | torque = [] 458 | voltage = [] 459 | times = [] 460 | # intDatL2 = idat.GaussianQuadratureOnEdge(Ng,idat.Gaussian1D,2) 461 | # intDatL4 = idat.GaussianQuadratureOnEdge(Ng,idat.Gaussian1D,4) 462 | for i in list(range(numberTimeSteps)): 463 | times.append(i*totalTime/numberTimeSteps) 464 | FO.StandardFileOutput.updateMesh('/home/haiau/Documents/result_.dat',mesh,i) 465 | res1 = 0.0 466 | for e in windingA1: 467 | res1 = e.integrate(intFunc,res1) 468 | res2 = 0.0 469 | for e in windingA2: 470 | res2 = e.integrate(intFunc,res2) 471 | voltage.append((res1-res2)*3.1e6/2045.175/math.sqrt(2)) 472 | 473 | res3 = 0.0 474 | for i,e in enumerate(lineint): 475 | # if side[i] == 2: 476 | # res3 = e.integrate(intLineFunc,res3,intDatL2,edg=True) 477 | # else: 478 | # res3 = e.integrate(intLineFunc,res3,intDatL4,edg=True) 479 | res3 = e.integrate(intTorque,res3) 480 | # torque.append(res3[0]/2) 481 | torque.append(res3[0]/(0.1e-2)) 482 | 483 | return times,voltage,torque 484 | 485 | output.updateToMesh(mesh,30) 486 | testnodes = [n for n in mesh.Nodes if n.getX()[0]==3.2e-2] 487 | testu = [n.getU()[0] for n in testnodes] 488 | testphi = [n.getX()[1] for n in testnodes] 489 | testphi = testphi[5:] + testphi[0:5] 490 | testu = testu[5:] + testu[0:5] 491 | 492 | 493 | #output.updateToMesh(mesh,30) 494 | #X,Y,Z = mesh.meshgridValue([0.0,5.7e-2,0.0,2.0*np.pi-0.005],0.005,1.0e-8) 495 | #X,Y,Z = mesh.meshgridValue([0.0,3.0,-2.0,3.0],0.02,1.0e-8) 496 | -------------------------------------------------------------------------------- /Tests/test_magnetic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 14 15:57:29 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import math 9 | import numpy as np 10 | import pylab as pl 11 | import Element.AxisymmetricElement as AE 12 | import Element.QuadElement as QE 13 | import Element.FEMElement as FE 14 | import Mesh.FEMNode as FN 15 | import Mesh.FEMMesh as FM 16 | import InOut.FEMOutput as FO 17 | import Material.Material as mat 18 | import Algorithm.NewmarkAlgorithm as NM 19 | import Math.Solver as sv 20 | import Math.IntegrationData as idat 21 | import Mesh.MeshGenerator as mg 22 | import cProfile 23 | import pstats 24 | import Algorithm.NewtonRaphson as NR 25 | 26 | class LinearMagneticMaterial(mat.Material): 27 | def __init__(self, mur, epsr, sigma, idx): 28 | self.mu0 = mur*4.0e-7*np.pi 29 | self.sigma = sigma 30 | self.eps = epsr*8.854187817e-12 31 | self.dM = np.zeros(2) 32 | self.idx = idx 33 | self.Mu = np.zeros(2) 34 | self.hysteresis = False 35 | 36 | def getID(self): 37 | return self.idx 38 | 39 | class JAMaterial(mat.Material): 40 | def __init__(self, sigma, ng, idx): 41 | self.a = 0.0 42 | self.alpha = 0.0 43 | self.c = 0.0 44 | self.Ms = 0.0 45 | self.k = 0.0 46 | self.delta = 1 47 | self.sigma = sigma 48 | self.eps = 8.854187817e-12 49 | self.dM = np.zeros(2) 50 | self.mu0 = 4.0e-7*np.pi 51 | self.ng = ng 52 | self.Bprev = np.zeros((2,ng)) 53 | self.Mprev = np.zeros((2,ng)) 54 | self.dMprev = np.zeros((2,ng)) 55 | self.Hprev = np.zeros((2,ng)) 56 | self.idx = idx 57 | self.Mu = np.zeros(2) 58 | self.Ndof = 1 59 | self.hystereis = True 60 | 61 | def getID(self): 62 | return self.idx 63 | 64 | def updateParamters(self, a, alpha, c, Ms, k): 65 | self.a = a 66 | self.alpha = alpha 67 | self.c = c 68 | self.Ms = Ms 69 | self.k = k 70 | 71 | def calculateParameters(self, temperature): 72 | t0 = temperature/1.0213513430455913e3 73 | self.Ms = 1.6666270496980909e6*math.pow((1.0-t0),2.0588027319169142e-1) 74 | self.a = math.exp(1.1065973379588542e1)*\ 75 | math.pow((1-t0),1.7544087504777564e-1) 76 | self.alpha=math.exp(-2.7711734827753376e0)*\ 77 | math.pow((1-t0),-1.1702805122223958e-1) 78 | self.c = math.exp(-1.339064360358903e0)*\ 79 | math.pow((1-t0),-3.4877155040447291e-2) 80 | self.k = math.exp(8.8017026926921e0)*\ 81 | math.pow((1-t0),2.4926461785971135e-1) 82 | 83 | def calculateOne(self,data,b,ix): 84 | try: 85 | T = data.getU()[1] 86 | except: 87 | T = 298.0 88 | self.calculateParameters(T) 89 | try: 90 | ig = data.ig 91 | except: 92 | ig = 0 93 | if T>=1.02407149938785e3: 94 | self.Mu[ix,ig] = 0.0 95 | self.dM[ix,ig] = 0.0 96 | return 97 | 98 | nstep = 400 99 | try: 100 | Bprev = self.Bprev[ix,ig] 101 | except IndexError: 102 | print(ix,data.ig) 103 | Hprev = self.Hprev[ix,ig] 104 | Mprev = self.Mprev[ix,ig] 105 | if(b < Bprev): 106 | self.delta = -1 107 | else: 108 | self.delta = 1 109 | 110 | deltab = (b - Bprev)/nstep 111 | barr = Bprev 112 | h = Hprev 113 | mu1 = Mprev 114 | 115 | if math.fabs(deltab) > 0: 116 | for i in range(nstep): 117 | he = h + self.alpha*mu1 118 | man = self.Ms*Langevin(he/self.a) 119 | try: 120 | dman = self.Ms/self.a*dLangevin(he/self.a) 121 | except: 122 | print(Bprev,Hprev,barr,h,b,mu1,self.dMprev,he/self.a) 123 | raise 124 | dman = dman/(1.0-self.alpha*dman) 125 | c1 = 1.0/(1.0+self.c) 126 | dmu1 = c1*(man-mu1)/(self.delta*self.k-\ 127 | self.alpha*(man-mu1))+self.c*c1*dman 128 | if dmu1 <0: 129 | dmu1 = -dmu1 130 | dmu1 = dmu1/(self.mu0*(1.0+dmu1)) 131 | mu1 = mu1 + dmu1*deltab 132 | 133 | barr = barr + deltab 134 | h = barr/self.mu0 - mu1 135 | self.Mu[ix] = mu1 136 | self.dM[ix] = dmu1 137 | else: 138 | self.Mu[ix] = Mprev 139 | self.dM[ix] = self.dMprev[ix,ig] 140 | 141 | try: 142 | if data.store: 143 | self.Bprev[ix,ig] = b 144 | self.Hprev[ix,ig] = h 145 | self.Mprev[ix,ig] = self.Mu[ix] 146 | self.dMprev[ix,ig] = self.dM[ix] 147 | except AttributeError: 148 | self.Bprev[ix,ig] = b 149 | self.Hprev[ix,ig] = h 150 | self.Mprev[ix,ig] = self.Mu[ix] 151 | self.dMprev[ix,ig] = self.dM[ix] 152 | 153 | return self.Mu[ix] 154 | 155 | def calculate(self,data): 156 | B = data.getB() 157 | self.calculateOne(data,B[0],0) 158 | self.calculateOne(data,B[1],1) 159 | 160 | def calculateX(self, b): 161 | return self.calculateOne(None,b,0) 162 | 163 | def Langevin(x): 164 | n = 8 165 | if math.fabs(x)>1.0: 166 | return 1.0e0/math.tanh(x) - 1.0e0/x 167 | else: 168 | g = 0.0 169 | for k in range(n,1,-1): 170 | bk = 2.0*k + 1.0 171 | g = x*x/(bk + g) 172 | return x/(3.0 + g) 173 | 174 | def dLangevin(x): 175 | if math.fabs(x) < 1.0e-5: 176 | return 1.0e0/3.0e0 177 | else: 178 | a = math.sinh(x) 179 | return 1.0/(x*x)-1.0/(a*a) 180 | 181 | class AxiSymMagnetic(AE.AxisymmetricQuadElement, QE.Quad9Element): 182 | def __init__(self, Nodes, pd, basisFunction, nodeOrder,material, intData): 183 | AE.AxisymmetricQuadElement.__init__(self,Nodes,pd,basisFunction,\ 184 | nodeOrder,material,intData) 185 | self.store = True 186 | self.linear = True 187 | 188 | def getB(self): 189 | B = np.array([-self.gradu_[1,0],self.gradu_[0,0]+self.u_[0]/self.x_[0]]) 190 | return B 191 | 192 | def updateMat(self, material): 193 | self.material = material 194 | 195 | def calculateKLinear(self, K, inod, jnod, t): 196 | """ 197 | Calculate Stiffness matrix K 198 | """ 199 | 200 | r = self.x_[0] 201 | K[0,0] = self.dN_[0,inod]*self.dN_[0,jnod] 202 | K[0,0] += self.N_[inod]*self.dN_[0,jnod]/r 203 | K[0,0] += self.dN_[0,inod]*self.N_[jnod]/r 204 | K[0,0] += self.N_[inod]*self.N_[jnod]/(r*r) 205 | K[0,0] += self.dN_[1,inod]*self.dN_[1,jnod] 206 | K[0,0] /= self.material.mu0 207 | K[0,0] *= self.getFactor() 208 | 209 | def calculateK(self, K, inod, jnod, t): 210 | r = self.x_[0] 211 | # magnetization 212 | if self.material.hysteresis: 213 | dNs = self.dN_ 214 | Ns = self.N_ 215 | dm1 = self.material.dM[0] 216 | dm2 = self.material.dM[1] 217 | ke = dm2*dNs[0][inod]*dNs[0][jnod]; 218 | ke += dm2*Ns[inod]*dNs[0][jnod]/r; 219 | ke += dm2*dNs[0][inod]*Ns[jnod]/r; 220 | ke += dm2*Ns[inod]*Ns[jnod]/(r*r); 221 | ke += dm1*dNs[1][inod]*dNs[1][jnod]; 222 | ke *= self.getFactor() 223 | K[0,0] -= ke 224 | 225 | def calculateDLinear(self, D, inod, jnod, t): 226 | """ 227 | Calculate Damping matrix D 228 | """ 229 | D[0,0] = self.N_[inod]*self.N_[jnod] 230 | D *= self.material.sigma*self.getFactor() 231 | 232 | def calculateMLinear(self, M, inod, jnod, t): 233 | """ 234 | Calculate Mass matrix M 235 | """ 236 | M[0,0] = self.N_[inod]*self.N_[jnod] 237 | M *= self.material.eps*self.getFactor() 238 | 239 | def calculateR(self, R, inod, t): 240 | """ 241 | Calculate load matrix R 242 | """ 243 | r = self.x_[0] 244 | re = 0.0 245 | if self.material.hysteresis: 246 | re += self.material.Mu[0]*self.dN_[1,inod] 247 | re -= self.material.Mu[1]*(self.N_[inod]/r+self.dN_[0,inod]) 248 | R[0] += re 249 | 250 | def calculateRe(self, R, inod, t): 251 | re = self.N_[inod]*self.getBodyLoad(t) 252 | re *= self.getFactor() 253 | R[0] = re 254 | 255 | 256 | def readInput(filename,nodeOrder,timeOrder,intData,Ndof = 1): 257 | mesh = FM.Mesh() 258 | file = open(filename,'r') 259 | int(file.readline().split()[1]) 260 | nnode = int(file.readline().split()[1]) 261 | nnod = int(file.readline().split()[1]) 262 | nelm = int(file.readline().split()[1]) 263 | int(file.readline().split()[1]) 264 | int(file.readline().split()[1]) 265 | file.readline() 266 | for i in range(nnod): 267 | a = list(float(x) for x in file.readline().split()) 268 | x_ = np.array(a[1:3]) 269 | mesh.addNode(FN.Node(x_,Ndof,timeOrder,i)) 270 | file.readline() 271 | for i in range(nelm): 272 | a = list(int(x) for x in file.readline().split()) 273 | nodes = [] 274 | for j in range(nnode): 275 | nodes.append(findNode(mesh.getNodes(),a[j+1]-1)) 276 | e = AxiSymMagnetic(nodes,[2,2],\ 277 | QE.LagrangeBasis1D,nodeOrder,None,intData) 278 | mesh.addElement(e) 279 | file.readline() 280 | for i in range(nnod): 281 | a = list(float(x) for x in file.readline().split()) 282 | for j in range(Ndof): 283 | mesh.getNodes()[i].setLoad(a[j+1],j) 284 | 285 | file.readline() 286 | for i in range(nnod): 287 | a = file.readline().split() 288 | for j in range(Ndof): 289 | mesh.getNodes()[i].setConstraint(int(a[2*j+1+2])==0,float(a[2*(j+1)+2]),j) 290 | 291 | air = LinearMagneticMaterial(1.0,1.0,0.0,2) 292 | cooper = LinearMagneticMaterial(1.0,1.0,5.0e6,3) 293 | steel = LinearMagneticMaterial(100.0,1.0,5.0e6,1) 294 | #steel = JAMaterial(5.0e6,9,1) 295 | file.readline() 296 | for i in range(nelm): 297 | a = list(int(x) for x in file.readline().split()) 298 | if a[1] == 2: 299 | mesh.getElements()[i].updateMat(air) 300 | if a[1] == 3: 301 | mesh.getElements()[i].updateMat(cooper) 302 | if a[1] == 1: 303 | mesh.getElements()[i].updateMat(steel) 304 | #mesh.getElements()[i].setLinearity(False) 305 | #mesh.getElements()[i].updateMat(JAMaterial(5.0e6,intData.getNumberPoint(),1)) 306 | file.close() 307 | return mesh 308 | 309 | 310 | 311 | def findNode(nodes,id_number): 312 | for node in nodes: 313 | if node.get_id_number() == id_number: 314 | return node 315 | raise Exception() 316 | 317 | Ndof = 1 318 | tOrder = 0 319 | Ng = [3,3] 320 | totalTime = 1.0e-3 321 | numberTimeSteps = 100 322 | rho_inf = 0.9 323 | tol = 1.0e-8 324 | load = 355.0/0.015/0.01 325 | #load = 355.0 326 | 327 | intDat = idat.GaussianQuadrature(Ng, 2, idat.Gaussian1D) 328 | 329 | nodeOrder = [[0,1,2,0,1,2,0,1,2], 330 | [0,0,0,1,1,1,2,2,2]] 331 | 332 | #mesh = readInput('/home/haiau/Documents/testfortran_.dat',nodeOrder,tOrder,intDat) 333 | #for e in mesh.getElements(): 334 | # if e.material.getID() == 3: 335 | # def loadfunc(x,t): 336 | # return -load*math.sin(8.1e3*2*np.pi*t) 337 | # e.setBodyLoad(loadfunc) 338 | 339 | def loadfunc(x,t): 340 | #return -load*math.sin(8.1e3*2*np.pi*t) 341 | return -load 342 | 343 | def create_mesh(): 344 | nodes = [] 345 | nodes.append(FN.Node([0.0,-0.2],Ndof,timeOrder = tOrder)) 346 | nodes.append(FN.Node([0.015,-0.2],Ndof,timeOrder = tOrder)) 347 | nodes.append(FN.Node([0.0225,-0.2],Ndof,timeOrder = tOrder)) 348 | nodes.append(FN.Node([0.0325,-0.2],Ndof,timeOrder = tOrder)) 349 | nodes.append(FN.Node([0.2,-0.2],Ndof,timeOrder = tOrder)) 350 | 351 | edges = [mg.Edge(nodes[i],nodes[i+1]) for i in range(len(nodes)-1)] 352 | 353 | geo = mg.Geometry() 354 | d = np.array([0.0,1.0]) 355 | s = [0.1,0.05,0.014,0.015,0.0135,0.015,0.0135,0.015,0.014,0.05,0.1] 356 | #s = [0.1,0.064,0.015,0.0135,0.015,0.0135,0.015,0.064,0.1] 357 | for e in edges: 358 | geo.addPolygons(e.extendToQuad(d,s)) 359 | 360 | polys = geo.getPolygons() 361 | for i in range(11): 362 | polys[i].setDivisionEdge13(4) 363 | 364 | for i in range(11,22): 365 | polys[i].setDivisionEdge13(2) 366 | 367 | for i in range(33,44): 368 | polys[i].setDivisionEdge13(5) 369 | 370 | for i in range(0,34,11): 371 | polys[i].setDivisionEdge24(4) 372 | 373 | for i in range(1,35,11): 374 | polys[i].setDivisionEdge24(2) 375 | 376 | for i in range(9,43,11): 377 | polys[i].setDivisionEdge24(2) 378 | 379 | for i in range(10,44,11): 380 | polys[i].setDivisionEdge24(4) 381 | 382 | mat3 = LinearMagneticMaterial(1.0,1.0,5.0e6,3) 383 | mat2 = LinearMagneticMaterial(1.0,1.0,0.0,2) 384 | #mat1 = JAMaterial(5.0e6,9,1) 385 | mat1 = LinearMagneticMaterial(1.0,1.0,5.0e6,1) 386 | for i in range(1,10): 387 | polys[i].setMaterial(mat1) 388 | 389 | polys[25].setMaterial(mat3) 390 | polys[25].setBodyLoad(load) 391 | polys[27].setMaterial(mat3) 392 | polys[27].setBodyLoad(load) 393 | polys[29].setMaterial(mat3) 394 | polys[29].setBodyLoad(load) 395 | 396 | for poly in polys: 397 | if poly.getMaterial() is None: 398 | poly.setMaterial(mat2) 399 | 400 | geo.mesh() 401 | 402 | [nodesx, elems, mats, bdls] = geo.getMesh(None,mg.nodesQuad9,2) 403 | 404 | #fig = geo.plot(poly_number = True, fill_mat = True) 405 | 406 | # geo.plotMesh(col = 'b-',fill_mat = True) 407 | # for i,node in enumerate(nodesx): 408 | # pl.plot(node.getX()[0],node.getX()[1],'.b') 409 | # if math.fabs(node.getX()[0] - 0.015)<1.0e-14: 410 | # pl.text(node.getX()[0],node.getX()[1],str(i)) 411 | 412 | for n in nodesx: 413 | if math.fabs(n.getX()[0]-0.0)<1.0e-14 or \ 414 | math.fabs(n.getX()[1]-0.2)<1.0e-14 or \ 415 | math.fabs(n.getX()[0]-0.2)<1.0e-14 or \ 416 | math.fabs(n.getX()[1]+0.2)<1.0e-14: 417 | n.setConstraint(False, 0.0, 0) 418 | #n.setConstraint(False, 0.0, 1) 419 | #pl.plot(n.getX()[0],n.getX()[1],'.r') 420 | 421 | elements = [] 422 | for i,e in enumerate(elems): 423 | #if mats[i] is JAMaterial: 424 | # m = JAMaterial(5.0e6,9,1) 425 | #else: 426 | # m = mats[i] 427 | m = mats[i] 428 | #elements.append(AxiSymMagnetic(e,[2,2],QE.LagrangeBasis1D,\ 429 | #QE.generateQuadNodeOrder([2,2],2),m,intDat)) 430 | elements.append(AxiSymMagnetic(e,[2,2],QE.LagrangeBasis1D,\ 431 | nodeOrder,m,intDat)) 432 | #if m.getID() == 1: 433 | # elements[-1].setLinearity(False) 434 | #if bdls[i] is not None: 435 | if elements[-1].material.getID() == 3: 436 | elements[-1].setBodyLoad(loadfunc) 437 | 438 | 439 | mesh = FM.Mesh() 440 | mesh.addNodes(nodesx) 441 | mesh.addElements(elements) 442 | 443 | return mesh 444 | 445 | def create_simple_mesh(): 446 | nodes = [] 447 | nodes.append(FN.Node([0.0,0.0],Ndof,timeOrder = tOrder)) 448 | nodes.append(FN.Node([1.0,0.0],Ndof,timeOrder = tOrder)) 449 | 450 | edge = mg.Edge(nodes[0],nodes[1]) 451 | poly = edge.extendToQuad(np.array([0.0,1.0]),1.0) 452 | 453 | 454 | geo = mg.Geometry() 455 | geo.addPolygon(poly) 456 | 457 | poly.setDivisionEdge13(4) 458 | 459 | mat2 = LinearMagneticMaterial(100.0,1.0,5.0e6,2) 460 | poly.setMaterial(mat2) 461 | 462 | 463 | geo.mesh() 464 | [nodesx, elems, mats, bdls] = geo.getMesh(None,mg.nodesQuad9,2) 465 | for n in nodesx: 466 | if math.fabs(n.getX()[0]-0.0)<1.0e-14: 467 | n.setConstraint(False, 0.0, 0) 468 | if math.fabs(n.getX()[1])<1.0e-14 or math.fabs(n.getX()[1]-1.0)<1.0e-14: 469 | n.setConstraint(False, 10*0.5*n.getX()[0],0) 470 | if math.fabs(n.getX()[0]-1.0)<1.0e-14: 471 | n.setConstraint(False, 10*0.5*n.getX()[0],0) 472 | elements = [] 473 | for i,e in enumerate(elems): 474 | m = mats[i] 475 | elements.append(AxiSymMagnetic(e,[2,2],QE.LagrangeBasis1D,\ 476 | QE.generateQuadNodeOrder([2,2],2),m,intDat)) 477 | 478 | mesh = FM.Mesh() 479 | mesh.addNodes(nodesx) 480 | mesh.addElements(elements) 481 | 482 | def loadfunc(t): 483 | #return load*math.cos(8.1e3*2*np.pi*t) 484 | #return load*math.cos(8.1e3*2*np.pi*t) 485 | return load 486 | 487 | # mesh.Nodes[4].setLoad(loadfunc,0) 488 | 489 | return mesh 490 | 491 | 492 | 493 | #mesh = create_simple_mesh() 494 | 495 | #matx = JAMaterial(0.0,1,1) 496 | #btest1 = [0.02*i for i in range(100)]+[0.02*i for i in range(100,-1,-1)] 497 | #btest = np.array(btest1) 498 | #mtest = np.array([matx.calculateX(b) for b in btest]) 499 | #pl.plot((btest/(np.pi*4.0e-7)-mtest),mtest) 500 | 501 | #mesh = create_mesh() 502 | mesh = create_simple_mesh() 503 | 504 | #cnt = 0 505 | #for e in mesh.Elements: 506 | # if e.bodyLoad is not None: 507 | # cnt += 1 508 | 509 | #mesh.plot(fill_mat = True) 510 | 511 | mesh.generateID() 512 | 513 | output = FO.StandardFileOutput('/home/haiau/Documents/result.dat') 514 | #alg = NM.NonlinearNewmarkAlgorithm(mesh,tOrder,output,sv.numpySolver(),\ 515 | #totalTime, numberTimeSteps,rho_inf,tol=1.0e-8) 516 | 517 | alg = NR.LoadControlledNewtonRaphson(mesh,output,sv.numpySolver(),1) 518 | 519 | alg.calculate() 520 | 521 | #output.updateToMesh(mesh,10) 522 | #X,Y,Z = mesh.meshgridValue([0.0,0.2,-0.2,0.2],0.01,1.0e-8) 523 | # 524 | #cProfile.run('alg.calculate()','calculate.profile') 525 | #stats = pstats.Stats('calculate.profile') 526 | #stats.strip_dirs().sort_stats('time').print_stats() 527 | # 528 | # 529 | #_,inod = mesh.findNodeNear(np.array([0.015,0.0])) 530 | #testout,tout = output.readOutput('/home/haiau/Documents/result.dat',list(range(50)),inod,'v') 531 | #testout = [t[0][0] for t in testout] 532 | -------------------------------------------------------------------------------- /Tests/test_mesh.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 21 10:38:01 2017 4 | 5 | @author: haiau 6 | """ 7 | 8 | import Mesh.MeshGenerator as mg 9 | import Element.FEMElement as fe 10 | import Mesh.FEMNode as fn 11 | import Material.Material as MAT 12 | import numpy as np 13 | import pylab as pl 14 | import math 15 | 16 | def test1(): 17 | #node1 = mg.GeneralNode([0,0],2) 18 | #node2 = mg.GeneralNode([1,0],2) 19 | #node3 = mg.GeneralNode([1,1],2) 20 | #node4 = mg.GeneralNode([0,1],2) 21 | #node5 = mg.GeneralNode([2,0],2) 22 | #node6 = mg.GeneralNode([2,1],2) 23 | node1 = fn.Node([0,0],2,2) 24 | node2 = fn.Node([1,0],2,2) 25 | node3 = fn.Node([1,1],2,2) 26 | node4 = fn.Node([0,1],2,2) 27 | node5 = fn.Node([2,0],2,2) 28 | node6 = fn.Node([2,1],2,2) 29 | quad = mg.Quadrilateral([node1,node2,node3,node4]) 30 | #quad.getQuadMesh() 31 | #quad.plotQuadMesh() 32 | #quadtest = Quadrilateral([node1,node2,node4,node3],[0,1,3,2]) 33 | #print(quadtest == quad) 34 | quadtest = mg.Quadrilateral([node2,node5,node6,node3]) 35 | #quadtest.addNode(node1) 36 | #quadtest.addNode(node2) 37 | #quadtest.addNode(node5) 38 | #quadtest.addNode(node6) 39 | geo = mg.Geometry() 40 | geo.addPolygon(quad) 41 | geo.addPolygon(quadtest) 42 | quad.setWeightParallelEdges(2.0,0.7,0.1,0) 43 | quad.setWeightParallelEdges(2.0,0.7,0.1,1) 44 | quad.setDivisionEdge13(4) 45 | quad.setDivisionEdge24(4) 46 | quadtest.setWeightParallelEdges(2.0,0.7,0.1,0) 47 | quadtest.setWeightParallelEdges(2.0,0.7,0.1,1) 48 | quadtest.setDivisionEdge13(4) 49 | quadtest.setDivisionEdge24(4) 50 | #quad.getQuadMesh() 51 | #quad.plotMesh() 52 | geo.mesh() 53 | fig = geo.plotMesh() 54 | [nodes, elems, mats, bdls] = geo.getMesh(fn.Node,mg.nodesQuad9,2) 55 | for node in nodes: 56 | pl.plot(node.getX()[0],node.getX()[1],'xb') 57 | 58 | elements = [] 59 | for e in elems: 60 | elements.append(fe.StandardElement(e,[2,2],None,[i for i in range(9)],\ 61 | None,None)) 62 | 63 | #test1() 64 | 65 | class test_material(MAT.Material): 66 | def __init__(self, ID): 67 | self.ID = ID 68 | def getID(self): 69 | return self.ID 70 | 71 | def test2(): 72 | nodes = [] 73 | nodes.append(fn.Node([0.0,-0.2],2,2)) 74 | nodes.append(fn.Node([0.015,-0.2],2,2)) 75 | nodes.append(fn.Node([0.0225,-0.2],2,2)) 76 | nodes.append(fn.Node([0.0325,-0.2],2,2)) 77 | nodes.append(fn.Node([0.2,-0.2],2,2)) 78 | 79 | edges = [mg.Edge(nodes[i],nodes[i+1]) for i in range(len(nodes)-1)] 80 | 81 | geo = mg.Geometry() 82 | d = np.array([0.0,1.0]) 83 | s = [0.1,0.064,0.015,0.0135,0.015,0.0135,0.015,0.064,0.1] 84 | for e in edges: 85 | geo.addPolygons(e.extendToQuad(d,s)) 86 | 87 | polys = geo.getPolygons() 88 | for i in range(9): 89 | polys[i].setDivisionEdge13(10) 90 | 91 | for i in range(9,18): 92 | polys[i].setDivisionEdge13(2) 93 | 94 | for i in range(27,36): 95 | polys[i].setDivisionEdge13(5) 96 | 97 | for i in range(0,28,9): 98 | polys[i].setDivisionEdge24(4) 99 | 100 | for i in range(8,36,9): 101 | polys[i].setDivisionEdge24(4) 102 | 103 | for i in range(1,29,9): 104 | polys[i].setDivisionEdge24(2) 105 | 106 | for i in range(7,35,9): 107 | polys[i].setDivisionEdge24(4) 108 | 109 | mat1 = test_material(1) 110 | mat2 = test_material(2) 111 | mat3 = test_material(3) 112 | 113 | for i in range(1,8): 114 | polys[i].setMaterial(mat1) 115 | 116 | polys[20].setMaterial(mat2) 117 | polys[22].setMaterial(mat2) 118 | polys[24].setMaterial(mat2) 119 | 120 | for poly in polys: 121 | if poly.getMaterial() is None: 122 | poly.setMaterial(mat3) 123 | 124 | geo.mesh() 125 | 126 | [nodesx, elems, mats, bdls] = geo.getMesh(fn.Node,mg.nodesQuad9,2) 127 | 128 | #fig = geo.plot(poly_number = True, fill_mat = True) 129 | 130 | geo.plotMesh(col = 'b-',fill_mat = True) 131 | #for i,node in enumerate(nodesx): 132 | # #pl.plot(node.getX()[0],node.getX()[1],'.b') 133 | # if math.fabs(node.getX()[0] - 0.0)<1.0e-14: 134 | # pl.text(node.getX()[0],node.getX()[1],str(i)) 135 | 136 | for n in nodesx: 137 | if math.fabs(n.getX()[0]-0.0)<1.0e-14 or \ 138 | math.fabs(n.getX()[1]+0.2)<1.0e-14 or \ 139 | math.fabs(n.getX()[0]-0.2)<1.0e-14 or \ 140 | math.fabs(n.getX()[1]-0.2)<1.0e-14: 141 | n.setConstraint(False, 0.0, 0) 142 | n.setConstraint(False, 0.0, 1) 143 | #pl.plot(n.getX()[0],n.getX()[1],'.r') 144 | 145 | elements = [] 146 | for i,e in enumerate(elems): 147 | elements.append(fe.StandardElement(e,[2,2],None,[i for i in range(9)],\ 148 | mats[i],None)) 149 | if bdls[i] is not None: 150 | def loadfunc(t): 151 | return bdls[i]*math.sin(8.1e3*2*np.pi*t) 152 | else: 153 | loadfunc = None 154 | elements[i].setBodyLoad(loadfunc) 155 | 156 | return [nodesx, elements] 157 | 158 | [nodes, elems] = test2() 159 | -------------------------------------------------------------------------------- /Tests/test_post_processing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Jan 11 14:29:18 2018 4 | 5 | @author: haiau 6 | """ 7 | 8 | import Mesh.FEMMesh as FM 9 | import InOut.FEMOutput as FO 10 | import Mesh.FEMNode as FN 11 | import numpy as np 12 | import pylab as pl 13 | 14 | filen = '/media/haiau/Data/PyFEM_Results/result_100.dat' 15 | Ndof = 2 16 | tOrder = 2 17 | 18 | res100,_ = FO.StandardFileOutput.readOutput(filen,val='x') 19 | 20 | res = res100.tolist() 21 | nodes = [] 22 | for x in res: 23 | nodes.append(FN.Node(x,Ndof,timeOrder=tOrder)) 24 | 25 | node = FM.findNodeNearX(nodes,np.array([0.015,0.0,0.0])) 26 | 27 | #testres,t = FO.StandardFileOutput.readOutput(filen,list(range(100)),val='u') 28 | 29 | fldrn = '/media/haiau/Data/PyFEM_Results/result_' 30 | 31 | num_steps = [100,200,400,800,1600,3200] 32 | 33 | def error_analysis(fldrn): 34 | 35 | resu = [] 36 | rest = [] 37 | 38 | for n in num_steps: 39 | filen = fldrn + str(n)+'.dat' 40 | print('reading data file: '+filen) 41 | testout,tout = FO.StandardFileOutput.readOutput(filen,timeStep='all',val='u') 42 | resu.append(testout) 43 | rest.append(tout) 44 | 45 | erru = [[]] 46 | 47 | for i in range(1,len(num_steps)): 48 | maxu = np.max([np.max(r[:,0]) for r in resu[i]]) 49 | for j in range(len(rest[i-1])): 50 | uref = resu[i][j*2][:,0] 51 | ucur = resu[i-1][j][:,0] 52 | erru[-1].append(np.linalg.norm(uref-ucur)/maxu) 53 | erru.append([]) 54 | 55 | averr = [] 56 | for i in range(1,len(num_steps)): 57 | averr.append(np.average(erru[i-1])) 58 | 59 | aparm = np.polyfit(np.log10([1.0e-3/n for n in num_steps[0:-1]]),np.log10(averr),1) 60 | 61 | return resu,erru,rest,aparm,averr 62 | 63 | resu_nl,erru_nl,rest,aparm_nl,averr_nl = error_analysis(fldrn) 64 | 65 | fldrn = '/media/haiau/Data/PyFEM_Results/result_linear_' 66 | 67 | resu_lin,erru_lin,rest,aparm_lin,averr_lin = error_analysis(fldrn) 68 | 69 | fldrn = '/media/haiau/Data/PyFEM_Results/result_linear_si_' 70 | 71 | resu_lin_si,erru_lin_si,rest,aparm_lin_si,averr_lin_si = error_analysis(fldrn) 72 | 73 | #fig = pl.Figure() 74 | # 75 | for i in range(1,len(num_steps)): 76 | pl.semilogy(rest[i-1],erru_nl[i-1],label='$\Delta_t=$'+str(1.0e-3/num_steps[i-1])+'$s$') 77 | 78 | pl.legend(loc=4,prop={'size': 8}) 79 | pl.xlabel('$t$') 80 | pl.ylabel('$e$') 81 | 82 | fig = pl.Figure() 83 | 84 | for i in range(1,len(num_steps)): 85 | pl.semilogy(rest[i-1],erru_lin[i-1],label='$\Delta_t=$'+str(1.0e-3/num_steps[i-1])+'$s$') 86 | 87 | pl.legend(loc=4,prop={'size': 8}) 88 | pl.xlabel('$t$') 89 | pl.ylabel('$e$') 90 | 91 | fig = pl.Figure() 92 | 93 | for i in range(1,len(num_steps)): 94 | pl.semilogy(rest[i-1],erru_lin_si[i-1],label='$\Delta_t=$'+str(1.0e-3/num_steps[i-1])+'$s$') 95 | 96 | pl.legend(loc=4,prop={'size': 8}) 97 | pl.xlabel('$t$') 98 | pl.ylabel('$e$') 99 | 100 | 101 | 102 | fig = pl.figure() 103 | 104 | resun = [[]] 105 | for i in range(len(num_steps)): 106 | for j in range(len(rest[i])): 107 | resun[i].append(resu_nl[i][j][node[1],0]) 108 | resun.append([]) 109 | 110 | resun_lin = [[]] 111 | for i in range(len(num_steps)): 112 | for j in range(len(rest[i])): 113 | resun_lin[i].append(resu_lin[i][j][node[1],0]) 114 | resun_lin.append([]) 115 | 116 | resun_lin_si = [[]] 117 | for i in range(len(num_steps)): 118 | for j in range(len(rest[i])): 119 | resun_lin_si[i].append(resu_lin_si[i][j][node[1],0]) 120 | resun_lin_si.append([]) 121 | 122 | err_loc = [] 123 | for i in range(len(num_steps)-1): 124 | errl = np.linalg.norm(np.array(\ 125 | resun_lin[i]-np.array(\ 126 | [resun_lin[i+1][j] for j in range(len(resun_lin[i]))])))/np.linalg.norm(\ 127 | np.array(\ 128 | [resun_lin[i+1][j] for j in range(len(resun_lin[i]))])) 129 | err_loc.append(errl) 130 | 131 | 132 | for i in range(len(num_steps)): 133 | pl.plot(rest[i],resun[i],label='$\Delta_t=$'+str(1.0e-3/num_steps[i-1])+'$s$') 134 | 135 | pl.legend(prop={'size': 10}) 136 | pl.xlabel('$t$[$s$]') 137 | pl.ylabel('$A$[$Vms^{-1}$]') 138 | 139 | fig = pl.figure() 140 | # 141 | pl.loglog([1.0e-3/n for n in num_steps[0:-1]],averr_nl,'-x',label='Nonlinear') 142 | pl.loglog([1.0e-3/n for n in num_steps[0:-1]],averr_lin,'-x',label='Linear with standard Gaussian Quadrature') 143 | pl.loglog([1.0e-3/n for n in num_steps[0:-1]],averr_lin_si,'-x',label='Linear with singular Gaussian Quadrature') 144 | pl.legend(loc=2,prop={'size':10}) 145 | pl.xlabel('$\Delta t$[$s$]') 146 | pl.ylabel('e') 147 | 148 | #pl.loglog([1.0e-3/n for n in num_steps[0:-1]],averr) 149 | 150 | -------------------------------------------------------------------------------- /Tests/test_post_processing_1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jan 12 14:38:48 2018 4 | 5 | @author: haiau 6 | """ 7 | 8 | import Mesh.FEMMesh as FM 9 | import InOut.FEMOutput as FO 10 | import Mesh.FEMNode as FN 11 | import numpy as np 12 | import pylab as pl 13 | import matplotlib.ticker as ticker 14 | 15 | fldrn = '/media/haiau/Data/PyFEM_Results/result_linear_ndiv_' 16 | 17 | Ndof = 2 18 | tOrder = 2 19 | 20 | ndiv = [1,2,4,8] 21 | res = [] 22 | 23 | for n in ndiv: 24 | filen = fldrn + str(n)+'.dat' 25 | print('reading data file: '+filen) 26 | res100,_ = FO.StandardFileOutput.readOutput(filen,val='x') 27 | resx = res100.tolist() 28 | nodes = [] 29 | for x in resx: 30 | nodes.append(FN.Node(x,Ndof,timeOrder=tOrder)) 31 | _,inode = FM.findNodeNearX(nodes,np.array([0.015,-0.1,0.0])) 32 | testout,tout = FO.StandardFileOutput.readOutput(filen,timeStep='all',node=inode,val='u') 33 | rest = [t[0][0] for t in testout] 34 | res.append(np.array(rest)) 35 | 36 | err = [] 37 | 38 | for i,n in enumerate(ndiv[0:-1]): 39 | err.append(np.linalg.norm(res[i+1]-res[i])/np.linalg.norm(res[i+1])) 40 | 41 | #pl.plot(ndiv[0:-1],err) 42 | pl.plot(ndiv,np.array(res)[:,200],'-x') 43 | pl.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.2e')) 44 | #testres,t = FO.StandardFileOutput.readOutput(filen,list(range(100)),val='u') 45 | 46 | 47 | -------------------------------------------------------------------------------- /Tests/test_structure.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Nov 13 10:35:33 2017 4 | 5 | @author: haiau 6 | """ 7 | import math 8 | import numpy as np 9 | import pylab as pl 10 | import mpl_toolkits.mplot3d.axes3d as p3 11 | import Element.FEMElement as FE 12 | import Mesh.FEMNode as FN 13 | import Mesh.FEMMesh as FM 14 | import InOut.FEMOutput as FO 15 | import Material.Material as mat 16 | import Algorithm.NewmarkAlgorithm as NM 17 | import Algorithm.NewtonRaphson as NR 18 | import Math.Solver as sv 19 | 20 | # Material 21 | class TrussMaterial(mat.Material): 22 | def __init__(self, E, A, rho): 23 | self.E = E 24 | self.A = A 25 | self.rho = rho 26 | 27 | def getE(self): 28 | return self.E 29 | 30 | def getA(self): 31 | return self.A 32 | 33 | def getEA(self): 34 | return self.E*self.A 35 | 36 | def getRhoA(self): 37 | return self.rho*self.A 38 | 39 | # Crisfield element: 40 | class CrisfieldElement(FE.Element): 41 | def __init__(self,Nodes, timeOrder, material, ndim): 42 | FE.Element.__init__(self,Nodes,0,None,material,None) 43 | self.L = np.linalg.norm(self.Nodes[1].getX()-self.Nodes[0].getX()) 44 | self.Ndim = ndim 45 | 46 | def calculate(self, data, linear = True): 47 | Kt = data.getKtL() 48 | Ktd = data.getKtLd() 49 | M = data.getML() 50 | Md = data.getMLd() 51 | 52 | u = (self.Nodes[1].getX()-self.Nodes[0].getX()) 53 | a = self.material.getEA()/((self.L)**3) 54 | k = np.outer(u,u) 55 | k *= a 56 | m = (self.material.getRhoA()*self.L/12)*np.identity(self.Ndof) 57 | for i in range(self.Nnod): 58 | for j in range(self.Nnod): 59 | K = (-1)**(i+j)*k 60 | m1 = ((-1)**(i+j)+3)*m 61 | FE.assembleMatrix(Kt,Ktd,K,self.Nodes[i],self.Nodes[j]) 62 | FE.assembleMatrix(M,Md,m1,self.Nodes[i],self.Nodes[j]) 63 | 64 | # Nonlinear Crisfiedl Element: 65 | class NonlinearCrisfieldElement(CrisfieldElement): 66 | def calculate(self, data, linear = False): 67 | if linear: 68 | return 69 | Kt = data.getKt() 70 | Ktd = data.getKtd() 71 | M = data.getM() 72 | Md = data.getMd() 73 | Ri = data.getRi() 74 | Rid = data.getRid() 75 | 76 | 77 | x = (self.Nodes[1].getX()-self.Nodes[0].getX()) 78 | u = (self.Nodes[1].getU()-\ 79 | self.Nodes[0].getU()) 80 | b = self.material.getE()/(2.0*self.L*self.L) 81 | s11 = b*np.dot(-2*x-u,-u) 82 | re = self.material.getA()/self.L*s11*(x+u) 83 | a = self.material.getEA()/((self.L)**3) 84 | km = np.outer(x+u,x+u) 85 | kg = self.material.getA()/self.L*s11*np.identity(self.Ndof) 86 | km *= a 87 | k = kg + km 88 | m = (self.material.getRhoA()*self.L/12)*np.identity(self.Ndof) 89 | for i in range(self.Nnod): 90 | R = (-1)**(i+1)*re 91 | FE.assembleVector(Ri,Rid,R,self.Nodes[i]) 92 | for j in range(self.Nnod): 93 | K = (-1)**(i+j)*k 94 | m1 = ((-1)**(i+j)+3)*m 95 | FE.assembleMatrix(Kt,Ktd,K,self.Nodes[i],self.Nodes[j]) 96 | FE.assembleMatrix(M,Md,m1,self.Nodes[i],self.Nodes[j]) 97 | 98 | # mesh 99 | class TrussMesh(FM.Mesh): 100 | def __init__(self, Ndim): 101 | FM.Mesh.__init__(self) 102 | self.Ndim = Ndim 103 | self.fig = None 104 | 105 | def updateValues(self, uGlob, vGlob, aGlob): 106 | for node in self.Nodes: 107 | node.updateU(uGlob) 108 | node.updateV(vGlob) 109 | node.updateA(aGlob) 110 | 111 | def plot(self, init = True, col='b'): 112 | """ 113 | plot mesh 114 | This is an example to plot a truss structure, i.e. Ndime = 1 115 | The other plot function can be made by overide this method 116 | Input: 117 | col: color spec 118 | """ 119 | if self.fig is None: 120 | self.fig = pl.figure() 121 | if self.Ndim == 3: 122 | ax = p3.Axes3D(self.fig) 123 | #loop over elements 124 | for i in range(self.Ne): 125 | e = self.Elements[i] 126 | #loop over dimensions 127 | if e.getNdim() == 1: 128 | for inod in range(e.getNnod()-1): 129 | if init: 130 | X = e.getNodes()[inod].getX() 131 | X1 = e.getNodes()[inod+1].getX() 132 | else: 133 | X = e.getNodes()[inod].getX() +\ 134 | e.getNodes()[inod].getU().toNumpy() 135 | X1 = e.getNodes()[inod+1].getX() +\ 136 | e.getNodes()[inod+1].getU().toNumpy() 137 | xc = np.array([X[0],X1[0]]) 138 | if self.Ndim == 2: 139 | yc = np.array([X[1],X1[1]]) 140 | pl.plot(xc,yc,col) 141 | if self.Ndim == 3: 142 | yc = np.array([X[1],X1[1]]) 143 | zc = np.array([X[2],X1[2]]) 144 | ax.plot(xc,yc,zc,col) 145 | return self.fig 146 | 147 | # Build structure 148 | def build_structure(E,rho,A,H,B1,B2,alpha,beta,timeOrder): 149 | node1 = FN.Node([-B2,H],2,timeOrder) 150 | node1.setConstraint(False, 0.0, 0) 151 | node1.setConstraint(False, 0.0, 1) 152 | node2 = FN.Node([0.0,0.0],2,timeOrder) 153 | node2.setConstraint(True, 0.0, 0) 154 | node2.setConstraint(False, 0.0, 1) 155 | node3 = FN.Node([B1,H],2,timeOrder) 156 | node3.setConstraint(False, 0.0, 0) 157 | node3.setConstraint(True, 0.0, 1) 158 | # node3.setConstraint(False,-0.05,1) 159 | 160 | 161 | mat1 = TrussMaterial(E,A,rho) 162 | mat2 = TrussMaterial(alpha*E,A,beta*rho) 163 | # element1 = CrisfieldElement([node1,node2],timeOrder,mat2,1) 164 | # element2 = CrisfieldElement([node2,node3],timeOrder,mat1,1) 165 | element1 = NonlinearCrisfieldElement([node1,node2],timeOrder,mat2,1) 166 | element2 = NonlinearCrisfieldElement([node2,node3],timeOrder,mat1,1) 167 | node3.setLoad(-2.0*E*A*H**3/(3.0*math.sqrt(3.0)*(element2.L**3)),1) 168 | 169 | 170 | mesh = TrussMesh(2) 171 | mesh.addNode(node1) 172 | mesh.addNode(node2) 173 | mesh.addNode(node3) 174 | mesh.addElement(element1) 175 | mesh.addElement(element2) 176 | mesh.generateID() 177 | return mesh 178 | 179 | timeOrder = 0 180 | mesh = build_structure(2.0e7,1.0,1.0,0.7,1.0,0.1,100,100,timeOrder) 181 | #mesh.Nodes[1].setU([0.050633741721625,0.0]) 182 | #mesh.Nodes[2].setU([0.0,0.574573486600207]) 183 | #mesh.Nodes[1].setA([0.041777453220745e6,0.0]) 184 | #mesh.Nodes[2].setA([0.0,-6.650981196224420e6]) 185 | 186 | # Output 187 | class plotOutput(FO.FEMOutput): 188 | def __init__(self, Nstep, Neq, timeOrder): 189 | self.Nsetp = Nstep 190 | self.Neq = Neq 191 | self.U = np.zeros((Neq,Nstep)) 192 | self.timeOrder = timeOrder 193 | self.load = np.zeros(Nstep) 194 | if timeOrder > 0: 195 | self.V = np.zeros((Neq,Nstep)) 196 | if timeOrder == 2: 197 | self.A = np.zeros((Neq,Nstep)) 198 | self.st_points = [] 199 | self.st_u = [] 200 | 201 | def getU(self, istep=0): 202 | if self.U.ndim == 1: 203 | return self.U 204 | return self.U[:,istep] 205 | 206 | def getV(self, istep=0): 207 | if self.timeOrder >0: 208 | if self.V.ndim == 1: 209 | return self.V 210 | return self.V[:,istep] 211 | 212 | def getA(self, istep=0): 213 | if self.timeOrder ==2: 214 | if self.A.ndim == 1: 215 | return self.A 216 | return self.A[:,istep] 217 | 218 | def outputData(self,data): 219 | if self.timeOrder == 0: 220 | self.U[:,data.getCurrentStep()] = data.getU() 221 | self.load[data.getCurrentStep()]= data.lmd 222 | 223 | if self.timeOrder > 0: 224 | self.U[:,data.getCurrentStep()-1] = data.getU() 225 | self.V[:,data.getCurrentStep()-1] = data.getV() 226 | if self.timeOrder == 2: 227 | self.A[:,data.getCurrentStep()] = data.getA() 228 | 229 | def outputStability(self,lmd,u): 230 | self.st_points.append(lmd) 231 | self.st_u.append(u) 232 | 233 | class StructureNonlinearNewmark(NM.NonlinearAlphaAlgorithm): 234 | def calculateREffect(self): 235 | """ 236 | effective load vector 237 | """ 238 | self.Ri *= -1.0 239 | self.Ri += self.Re 240 | if self.timeOrder > 0: 241 | np.dot(self.D,self.V,self.__tempR__) 242 | self.Ri -= self.__tempR__ 243 | if self.timeOrder == 2: 244 | np.dot(self.M,self.A,self.__tempR__) 245 | self.Ri -= self.__tempR__ 246 | 247 | # Algorithm 248 | Nstep = 100 249 | time = 2.0e-2 250 | #output = plotOutput(Nstep,mesh.getNeq(),1) 251 | output = plotOutput(Nstep,mesh.getNeq(),0) 252 | #static = FA.LinearStaticAlgorithm(mesh,output,sv.numpySolver()) 253 | #static.calculate() 254 | #mesh.plot() 255 | #mesh.updateValues(output.getU(),None,None) 256 | #mesh.plot(init=False,col='r') 257 | #alg = NM.LinearNewmarkAlgorithm(mesh,2,output,sv.numpySolver(),time,Nstep,1.0) 258 | #alg = NR.LoadControlledNewtonRaphson(mesh,output,sv.numpySolver(),100) 259 | alg = NR.ArcLengthControlledNewtonRaphson(mesh,output,sv.numpySolver(),Nstep,\ 260 | arcl=2.0) 261 | #alg.enableVariableConstraint() 262 | #alg = StructureNonlinearNewmark(mesh,2,output,sv.numpySolver(),time,Nstep,1.0) 263 | alg.calculate(False) 264 | #tarr = np.array(list(range(0,200)))*2.0e-2/200 265 | #pl.plot(tarr,output.U[0,:],'b',tarr,output.U[1,:],'r') 266 | 267 | #tarr = np.array(list(range(0,100)))*0.6/100 268 | #pl.plot(output.U[1,:],tarr) 269 | pl.plot(output.U[0,:],output.load) 270 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Created on Tue Mar 5 11:35:59 2019 5 | 6 | @author: haiau 7 | """ 8 | 9 | -------------------------------------------------------------------------------- /plot_field.m: -------------------------------------------------------------------------------- 1 | function [a ] = plot_field( it,field,xyz,Ne,mata,imat ) 2 | %UNTITLED4 Summary of this function goes here 3 | % Detailed explanation goes here 4 | 5 | a = 1 6 | its = zeros(4,4); 7 | ite = zeros(4,4); 8 | ite(1,:)=[1,2,5,4]; 9 | ite(2,:)=[2,3,6,5]; 10 | ite(3,:)=[4,5,8,7]; 11 | ite(4,:)=[5,6,9,8]; 12 | for i=1:Ne 13 | if(mata(i)==imat||imat==0) 14 | its(1,:)=[it(i,1),it(i,2),it(i,5),it(i,4)]; 15 | its(2,:)=[it(i,2),it(i,3),it(i,6),it(i,5)]; 16 | its(3,:)=[it(i,4),it(i,5),it(i,8),it(i,7)]; 17 | its(4,:)=[it(i,5),it(i,6),it(i,9),it(i,8)]; 18 | for j=1:4 19 | x = xyz(its(j,:),1); 20 | y = xyz(its(j,:),2); 21 | % c = field(i,ite(j,:)); 22 | c = field(its(j,:)); 23 | h=patch(x,y,c); 24 | set(h,'LineStyle','none','FaceColor','interp'); 25 | hold on; 26 | end 27 | end 28 | end 29 | colorbar vertical; 30 | hbar = colorbar; 31 | set(hbar,'TickLabelInterpreter','latex'); 32 | colormap jet; 33 | axis equal; 34 | axis off; 35 | hold off; 36 | 37 | end 38 | 39 | --------------------------------------------------------------------------------