├── ANSYS
├── models
│ └── empty.txt
└── results
│ └── empty.txt
├── LICENSE
├── Python
├── finite_element_analysis.py
├── global_variable.py
├── optimization_simp.py
├── postprocessor.py
├── shorthaircat.py
├── top2d_minf.txt
├── top2d_rinf.txt
├── top3d_minf.txt
├── top3d_rinf.txt
└── vtu2stl.py
├── README.md
├── REASULTS
├── Be.png
├── GUI.png
├── GUI_stress.png
├── L_shape.gif
├── L_shape_density.png
├── L_shape_stl.png
├── MBB.gif
├── MBB_stl.png
├── cantilever2D.gif
├── center_load.gif
├── center_load_density.png
├── center_load_stl.png
├── chejia.jpg
├── cheshen_1.gif
├── cheshen_2.gif
├── complex2D.gif
├── complex2D_stress.png
├── complex3D.gif
├── converge.png
├── filter.png
├── objective_function.png
├── sensitivity.png
├── updating_scheme.png
└── weight_factor.png
├── density
└── empty.txt
└── fig
├── density1.png
├── density10.png
├── density11.png
├── density12.png
├── density13.png
├── density14.png
├── density15.png
├── density16.png
├── density17.png
├── density18.png
├── density2.png
├── density3.png
├── density4.png
├── density5.png
├── density6.png
├── density7.png
├── density8.png
├── density9.png
└── empty.txt
/ANSYS/models/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pep-pig/Topology-optimization-of-structure-via-simp-method/c9e81e2254f01e4babb69a337370a7a86bf31fb8/ANSYS/models/empty.txt
--------------------------------------------------------------------------------
/ANSYS/results/empty.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pep-pig/Topology-optimization-of-structure-via-simp-method/c9e81e2254f01e4babb69a337370a7a86bf31fb8/ANSYS/results/empty.txt
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Python/finite_element_analysis.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import numpy as np
3 | from numpy import *
4 | import re
5 | import global_variable
6 |
7 |
8 | class FiniteElementAnalysis(object):
9 | """
10 | ANSYS 求解器
11 |
12 | Parameters
13 | ----------
14 | 各路径的说明:
15 | cwd:存储ANSYS_APDL脚本,与ANSYS日志文件
16 | awd: 此目录为ANSYS的工作目录,所有需要在APDL中读入或输入的文件都在此目录下
17 | ANSYS_APDL读取文件与写入文件命名规范:
18 | 读取文件:
19 | material.txt:材料文件,对于各向同性材料:Nx2,第一列杨氏模量,第二列泊松比,N为单元数
20 | 写入文件:
21 | elements_nodes_counts.txt:单元数与节点数,1x2第一行单元数,第二行节点数
22 | elements_stiffness.out:未经处理的单元刚度矩阵文件
23 | elements_nodes.txt: 各单元对应的节点编号,Nx(k+1),N为单元数,第一列单元编号,剩下k列为节点编号
24 | elements_centers.txt: 各单元的中心坐标,Nx4,N为单元数,第一列为单元编号,其余三列为中心坐标值,(x,y,z)
25 | elements_volumn.txt: 各单元的体积,Nx2,N为单元数,第一列单元编号,第二列体积
26 | nodal_solution_u.txt: 节点位移,3列分别表示X,Y,Z方向位移
27 | nodal_solution_stress.txt: 节点应力,Vonmiss应力,一列,行数等于节点数
28 | nodal_solution_strain.txt: 节点应变,一列,行数等于节点数
29 | """
30 | def __init__(self):
31 | # 输入文件(APDL)和输出文件都将在cwd目录中,而ANSYS需要的其他输入数据或输出数据的路径,将由ANSYS的APDL指定
32 |
33 |
34 | self.ANSYS_path = "E:\Program Files\ANSYS Inc\\v170\ANSYS\\bin\winx64\MAPDL.exe"
35 | # self.ANSYS_path = "D:\Program Files\ANSYS2017\ANSYS17.0\ANSYS Inc\\v170\ANSYS\\bin\winx64\MAPDL.exe"
36 | # self.ANSYS_path = "F:\ANSYS 17.0\ANSYS Inc\\v170\ANSYS\\bin\winx64\MAPDL.exe"
37 |
38 | self.awd = 'H:/GitHub/Topology-optimization-of-structure-via-simp-method/ANSYS/results/'
39 | if global_variable.TYPE == 'top2d':
40 | #---------------实验室台式机路径--------------------
41 | self.meshdata_cmd = [self.ANSYS_path, '-b', '-i',
42 | 'top2d_minf.txt', '-o', 'top2d_minf.out']
43 | self.result_cmd = [self.ANSYS_path, '-b', '-i',
44 | 'top2d_rinf.txt', '-o', 'top2d_rinf.out']
45 | self.dim = global_variable.DIM
46 |
47 | if global_variable.TYPE == 'top3d':
48 | # ---------------实验室台式机路径--------------------
49 | self.meshdata_cmd = [self.ANSYS_path, '-b', '-i',
50 | 'top3d_minf.txt', '-o', 'top3d_minf.out']
51 | self.result_cmd = [self.ANSYS_path, '-b', '-i',
52 | 'top3d_rinf.txt', '-o', 'top3d_rinf.out']
53 | self.dim = global_variable.DIM
54 |
55 |
56 |
57 | def boot(self):
58 | subprocess.call(self.meshdata_cmd)
59 |
60 | def get_counts(self,element_nodes_file):
61 | """
62 | 获取单元数和节点数
63 |
64 | Parameters
65 | ----------
66 | element_nodes_file:存储单元数和节点数的文件
67 |
68 | Returns
69 | ----------
70 | 返回单元数和节点数
71 | """
72 | counts = loadtxt(element_nodes_file,dtype = int)
73 | return counts[0],counts[1]
74 |
75 |
76 | def generate_material_properties(self,x):
77 | """
78 | 将OC方法获得的x生成新的材料文件,对于各向异性的点阵材料而言,其材料属性文件将由子类实现
79 |
80 | Parameters
81 | ----------
82 | x : 单元密度
83 | penal : 惩罚因子
84 |
85 | Returns
86 | ----------
87 | 将生成的材料文件存入material.txt
88 | """
89 | nu = global_variable.NU * np.ones((global_variable.ELEMENT_COUNTS))
90 | ex = (x**global_variable.PENAL)*(global_variable.E)
91 | material = np.array([nu, ex]).T
92 | np.savetxt(self.awd+"material.txt", material, fmt=' %-.7E', newline='\n')
93 |
94 |
95 | def get_meshmodel_data(self):
96 | """
97 | 获取有限元模型相关数据,这些数据在迭代计算中属于不变量,只需单独调用该函数一次
98 |
99 | Parameters
100 | ----------
101 | dim:单元刚度矩阵的维度
102 |
103 | Returns
104 | ----------
105 | k:单元刚度矩阵集合
106 | element_attributes:单元对应的节点编号
107 | centers:单元的中心坐标
108 | v:单元体积
109 | """
110 |
111 | element_attributes = loadtxt(self.awd+'elements_nodes.txt', dtype=int)
112 | centers = loadtxt(self.awd+'elements_centers.txt')
113 | v = loadtxt(self.awd+'elements_volumn.txt')
114 | node_coordinates =loadtxt(self.awd+'node_coordinates.txt')
115 | return element_attributes,centers,v,node_coordinates
116 |
117 |
118 | def get_result_data(self,x):
119 | """
120 | 更新材料密度,进行有限元分析并获取结果数据文件
121 | """
122 | self.generate_material_properties(x)
123 | subprocess.call(self.result_cmd)
124 | u = loadtxt(self.awd+'nodal_solution_u.txt',dtype=float)
125 | stress = loadtxt(self.awd+'nodal_solution_stress.txt',dtype = float)
126 | strain = loadtxt(self.awd+'nodal_solution_strain.txt',dtype = float)
127 | return u,stress[:,1],strain[:,1]
128 |
129 | #单元测试
130 | if __name__=='__main__':
131 | global_variable.initialize_global_variable(DIM = 24)
132 | x = 0.4 * np.ones(global_variable.ELEMENT_COUNTS)
133 | ansys_solver = FiniteElementAnalysis(dim = 24)
134 | ansys_solver.get_result_data(x=x,penal=3)
135 |
136 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/Python/global_variable.py:
--------------------------------------------------------------------------------
1 | from finite_element_analysis import *
2 | from postprocessor import *
3 | from numpy import *
4 |
5 |
6 | global ELEMENT_COUNTS,ELEMENT_ATTRIBUTES,NODE_COORDINATES,NODE_COUNTS,CENTERS,V,DIM,GRID_TYPE,TYPE
7 | global R, E, NU, PENAL, MOVE,VOLFAC
8 |
9 | def hyperparameter(r,penal,volfac,move,e,nu):
10 | global R, E, NU, PENAL, MOVE,VOLFAC
11 | R=r
12 | PENAL=penal
13 | MOVE=move
14 | E=e
15 | NU=nu
16 | VOLFAC = volfac
17 |
18 | def initialize_global_variable(type):
19 | '''
20 | 以待求解的有限元模型座位输入,产生全局变量
21 | Parameter
22 | ----------
23 | DIM:单元刚度矩阵的维度
24 |
25 | Returns
26 | ----------
27 | 在整个计算过程中不发生改变的全局变量
28 |
29 | '''
30 | global ELEMENT_COUNTS, ELEMENT_ATTRIBUTES, NODE_COORDINATES, NODE_COUNTS, CENTERS, V, K,DIM,GRID_TYPE,TYPE
31 | TYPE = type
32 | if TYPE =='top2d':
33 | DIM = 8
34 | GRID_TYPE = 'Polygon'
35 | if TYPE =='top3d':
36 | DIM = 24
37 | GRID_TYPE = 'Hexahedron'
38 | ANSYS_SOLVER = FiniteElementAnalysis()
39 | # ANSYS_SOLVER.boot()
40 | ELEMENT_COUNTS, NODE_COUNTS = ANSYS_SOLVER.get_counts(ANSYS_SOLVER.awd + 'elements_nodes_counts.txt')
41 | ELEMENT_ATTRIBUTES, CENTERS, V, NODE_COORDINATES = ANSYS_SOLVER.get_meshmodel_data()
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Python/optimization_simp.py:
--------------------------------------------------------------------------------
1 | from numpy import *
2 | import numpy as np
3 | import global_variable
4 | from finite_element_analysis import *
5 | from postprocessor import *
6 | from traits.api import HasTraits, Instance, Property, Enum,Range,on_trait_change,Int
7 | import time
8 |
9 | from mayavi import mlab
10 | class Simp(HasTraits):
11 | """
12 | 函数中各个parameter:
13 |
14 | Parameters
15 | ----------
16 | 需要从ANSYS获得以及需要用户输入的数据:
17 | U: NxL列向量,节点解,N为节点数,L为坐标轴数,2D对应x,y.3D对应x,y,z
18 | stress:Nx2数组,第一列节点编号,第二列修匀后的节点应力
19 | strain:Nx2数组,第一类节点编号,第二列修匀后的节点应变
20 | K: ExRxL数组,单元刚度矩阵汇总,E为单元数,R、L为单元刚度矩阵的行和列
21 | ELEMENT_ATTRIBUTES: ExN数组,单元信息,E是单元数,N为单元节点编号
22 | CENTERS: Ex3数组,单元中心坐标, E单元数目,每行3列分别为中心x、y、z坐标
23 | V:1xE数组,单元体积,E单元数目
24 | penal: 标量,simp法的密度惩罚因子
25 | volfrac: 标量,体积减少百分比
26 | rmin: 标量,SIMP法棋盘格现象抑制范围,与结构最小杆件的尺寸将与rmin接近
27 |
28 | SIMP法需要的arguments:ue,k0,center,v,penal,volfrac,rmin,xe
29 | Ue:各个单元的节点解
30 | Ke:各个单元刚度矩阵
31 | center:各个单元的中心坐标
32 | Ve:各个单元的体积
33 | penal: 标量,simp法的密度惩罚因子
34 | volfrac: 标量,体积减少百分比
35 | rmin:标量,SIMP法棋盘格现象抑制范围,与结构最小杆件的尺寸将与rmin接近
36 | x: 1xE数组,各个单元的相对密度,也是simp法求解的目标
37 | """
38 |
39 | loop = Int(0)#以loop作为图形更新的监听对象
40 | def __init__(self):
41 | #初始化所需要的数据
42 | self.resultdata = ResultData()
43 | self.ansys_solver = FiniteElementAnalysis()
44 | self.strain_energy = []
45 | self.volume_rate=[]
46 | self.finished = False
47 |
48 | # A new algorithm
49 | def get_distance_table(self):
50 | neibors = np.loadtxt(self.ansys_solver.awd+'neibors.txt', dtype=int)
51 | neiborslist = []
52 | for i in range(neibors.shape[0]):
53 | index = neibors[i, np.where(neibors[i, :] > 0)]
54 | neiborslist.append(index-1)
55 |
56 | coordinates = np.loadtxt(self.ansys_solver.awd+'elements_centers.txt', dtype=float)[:, 1:]
57 | weights = []
58 | i = 0
59 | for neibor in neiborslist:
60 | b = coordinates[neibor[:]]
61 | distance = np.sqrt(np.sum((coordinates[neibor,:] - coordinates[i,:]) ** 2, axis=-1))
62 | weight = (global_variable.R-distance)
63 | weight[np.where(weight<0)] = 0
64 | # weight = (global_variable.R) * np.max(distance) - distance
65 | weights.append(weight)
66 | i = i+1
67 | return neiborslist,weights
68 |
69 | def de_checkboard(self,x, dc):
70 | corrected_dc = []
71 | i = 0
72 | x = np.array(x)
73 | dc = np.array(dc)
74 | index = np.where(dc<0)
75 | j = 0
76 | for _ in dc:
77 | corrected_dc_demonimator = 0.0
78 | corrected_dc_numerator = 0.0
79 | elements =self.neiborslist[j].tolist()[0]
80 | corrected_dc_demonimator = np.sum(self.weights[j])
81 | corrected_dc_numerator = np.sum(self.weights[j] * x[elements[:]] * dc[elements[:]])
82 | corrected_dc.append(corrected_dc_numerator / (x[j] * corrected_dc_demonimator))
83 | j=j+1
84 | index = np.where(array(corrected_dc)<0)
85 | return corrected_dc
86 |
87 | def oc(self, x, volfrac, corrected_dc):
88 | """
89 | 优化准则法
90 | """
91 | lambda1 = 0; lambda2 = 100000; move = global_variable.MOVE
92 | while(lambda2-lambda1>1e-4):
93 | lambda_mid = 0.5*(lambda2+lambda1)
94 | #index = np.where(array(corrected_dc)<0)
95 | B = x*sqrt((array(corrected_dc,dtype = float))/(lambda_mid * global_variable.V[:,1]))
96 | xnew = maximum(0.001, maximum(x-move, minimum(1.0, minimum(x + move,B))))#由里到外,先比上界,再比下界
97 | if sum(xnew*global_variable.V[:,1])-volfrac*sum(global_variable.V[:,1])>0:
98 | lambda1 = lambda_mid
99 | else:
100 | lambda2 = lambda_mid
101 | self.volume_rate.append(sum(xnew*global_variable.V[:,1])/sum(global_variable.V[:,1]))
102 | print("volume rate:" , self.volume_rate[-1])
103 | print("lamda:",lambda_mid)
104 |
105 | return xnew
106 |
107 |
108 |
109 | def simp(self):
110 | """
111 | SIMP优化算法
112 | """
113 | #初始化数据
114 | penal = global_variable.PENAL
115 | volfrac = global_variable.VOLFAC
116 | rmin = global_variable.R
117 | self.neiborslist,self.weights = self.get_distance_table()
118 | x = volfrac*np.ones(global_variable.ELEMENT_COUNTS)
119 | change_c = change_x = 1;
120 | c_total= 0
121 | Emin = 1e-9
122 | #开始迭代
123 | while self.loop<26:
124 | c_old =c_total
125 | xold = x;
126 | U, stress, strain = self.ansys_solver.get_result_data(x)
127 | c = np.loadtxt(self.ansys_solver.awd+'strain_energy.txt',dtype = float)[0:global_variable.ELEMENT_COUNTS].reshape(global_variable.ELEMENT_COUNTS,1)*2
128 | uku = c/(x.reshape((global_variable.ELEMENT_COUNTS,1))**penal)
129 | dc = (penal*(x.reshape((global_variable.ELEMENT_COUNTS,1))** (penal-1)))*uku
130 | c_total = sum(c, axis=0)[0]
131 | dc = dc[:,0].tolist()
132 | #更新每个单元应变能对与密度的敏度:消除棋盘格现象的敏度过滤算法
133 | corrected_dc = self.de_checkboard(x, dc)
134 | #OC优化准则发对密度进行更新
135 | x = self.oc(x, volfrac, corrected_dc)
136 | change_x = abs(max(x-xold))
137 | change_c = abs(c_old - c_total)
138 | self.strain_energy.append(c_total)
139 | print("c:",c_total," loop: ",self.loop)
140 | #更新每一次迭代的结果,整个内存只保存了当前迭代结果
141 | self.resultdata.undate_ansys_date(U, stress, strain, x)
142 | #将每一步的迭代结果写入本地内存,以为后续生成动画
143 | self.resultdata.write_unstructured_data(loop=self.loop)
144 | self.loop = self.loop + 1
145 |
146 | # if self.loop>3:
147 | # break
148 | self.finished = True
149 | return x
150 |
151 | #单元测试
152 | if __name__=='__main__':
153 | global_variable.initialize_global_variable(DIM = 8)
154 | simp_solver = Simp(dim = 8)
155 | density = simp_solver.simp()
156 |
157 | # x = simp_solver.x_axis
158 | y = simp_solver.strain_energy
159 | # z = simp_solver.z_axis
160 | l = mlab.plot3d(x, y, z, representation='surface')
161 | # l = mlab.plot3d()
162 | mlab.show()
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
--------------------------------------------------------------------------------
/Python/postprocessor.py:
--------------------------------------------------------------------------------
1 | #standard import
2 | import numpy as np
3 | from numpy import *
4 |
5 | #user defined pakages import
6 | import global_variable
7 |
8 | #enthought import
9 | from tvtk.api import tvtk
10 | from mayavi import mlab
11 | from mayavi.sources.vtk_data_source import VTKDataSource
12 |
13 |
14 | class ResultData(object):
15 | #定义写入类
16 | #tvtk的写入类,与vtk的写入类很类似,在mayavi中,不能直接用vtk类,而要用由python 对vtk封装后的 tvtk,如果需要读数据,也可以用类似的API
17 | writer =tvtk.XMLUnstructuredGridWriter()
18 | def __init__(self):
19 | '''
20 | 初始化结果数据,为画图做准备
21 | 以unstrgrid开头的数据属于 vtk的非结构网格数据,这种数据可以保存到本地,但是不能直接用来绘图
22 | 需要通过VTKDataSource 作为接口,生成mayavi可以渲染的数据源
23 | 以vtkdatasource开头的数据属于经过了VTKDataSource函数处理过的数据,可以被mayavi渲染
24 | '''
25 | self.address = 'H:\GitHub\Topology-optimization-of-structure-via-simp-method'
26 | self.index = []
27 | self.U = np.zeros((global_variable.NODE_COUNTS,1))
28 | self.stress = np.zeros((global_variable.NODE_COUNTS,1))
29 | self.strain = np.zeros((global_variable.NODE_COUNTS,1))
30 | self.density = np.ones((global_variable.ELEMENT_COUNTS,1))
31 |
32 |
33 |
34 | #unstructuredgrid 类型数据
35 | self.unstrgrid_mesh = None
36 | self.unstrgrid_density = None
37 | self.unstrgrid_stress = None
38 | self.unstrgrid_strain = None
39 | self.unstrgrid_displacement = None
40 | #vtksource类型数据
41 | self.vtkdatasource_mesh = None
42 | self.vtkdatasource_displacement = None
43 | self.vtkdatasource_stress = None
44 | self.vtkdatasource_strain = None
45 | self.vtkdatasource_density = None
46 | #初始化unstructuredgrid 与 vtkdatasource
47 | self.initialize()
48 |
49 | #以用户自定义值 初始化#初始化unstructuredgrid 与 vtkdatasource
50 | def initialize(self):
51 | self.unstrgrid_mesh = self.generate_unstrgrid_mesh()
52 | self.unstrgrid_density = self.generate_unstrgrid_mesh()
53 | self.unstrgrid_stress = self.generate_unstrgrid_mesh()
54 | self.unstrgrid_strain = self.generate_unstrgrid_mesh()
55 | self.unstrgrid_displacement = self.generate_unstrgrid_mesh()
56 | self.write_unstructured_data()
57 | self.vtkdatasource()
58 |
59 | def undate_ansys_date(self, U, stress, strain, x):
60 | self.U = U
61 | self.stress= stress
62 | self.strain = strain
63 | self.density = x
64 |
65 | #生成非结构网格数据
66 | def write_unstructured_data(self, loop = -1):
67 | '''
68 | 设置if条件语句是为了第一次初始化的结果不写入本地文件
69 | '''
70 | # 存储位移数据
71 | self.update_unstrgrid_displacement(self.U, 0.05, 0)
72 | if loop!=-1:
73 | name = 'displacement_0'+str(loop+1)+'.vtu'
74 | #设置写入的文件
75 | self.writer.set_input_data(self.unstrgrid_displacement)
76 | #设置写入文件的名称
77 | self.writer.file_name = self.address+'\displacement\\'+name
78 | #开始写入
79 | self.writer.write()
80 |
81 | # 应力数据
82 | self.update_unstrgrid_stress(self.stress)
83 | if loop!=-1:
84 | name = 'stress_0'+str(loop)+'.vtu'
85 | self.writer.set_input_data(self.unstrgrid_stress)
86 | self.writer.file_name = self.address+'\stress\\'+name
87 | self.writer.write()
88 |
89 |
90 |
91 | # 应变数据
92 | self.update_unstrgrid_strain(self.strain)
93 | if loop!=-1:
94 | name = 'strain_0'+str(loop)+'.vtu'
95 | self.writer.set_input_data(self.unstrgrid_strain)
96 | self.writer.file_name = self.address+'\strain\\'+name
97 | self.writer.write()
98 |
99 |
100 | # 密度数据
101 | self.update_unstrgrid_density(self.density)
102 | if loop!=-1:
103 | name = 'density_0'+str(loop)+'.vtu'
104 | self.writer.set_input_data(self.unstrgrid_density)
105 | self.writer.file_name = self.address+'\\density\\'+name
106 | self.writer.write()
107 |
108 | #生成vtkdata类型数据
109 | def vtkdatasource(self):
110 | self.vtkdatasource_mesh = VTKDataSource(data=self.unstrgrid_mesh, name='Geometry')
111 | self.vtkdatasource_displacement = VTKDataSource(data=self.unstrgrid_displacement, name='DisplacementData')
112 | self.vtkdatasource_stress = VTKDataSource(data=self.unstrgrid_stress, name='StessData')
113 | self.vtkdatasource_strain = VTKDataSource(data=self.unstrgrid_strain, name='StrainData')
114 | self.vtkdatasource_density = VTKDataSource(data=self.unstrgrid_density, name='DensiytData')
115 |
116 | #生成网格数据,filter参数是为了过滤我们不想显示的密度单元,1表示全部显示,0表示全部不显示
117 | def generate_unstrgrid_mesh(self, filter = 0):
118 | points = global_variable.NODE_COORDINATES
119 | self.index = where(self.density>=(1.0-filter))[0].tolist()
120 | cells = (global_variable.ELEMENT_ATTRIBUTES[self.index,:]-1)
121 | if global_variable.GRID_TYPE =='Polygon':
122 | cell_tpye = tvtk.Polygon().cell_type
123 |
124 | if global_variable.GRID_TYPE =='Hexahedron':
125 | cell_tpye = tvtk.Hexahedron().cell_type
126 |
127 | grid = tvtk.UnstructuredGrid(points = points)
128 | grid.set_cells(cell_tpye,cells)
129 | return grid
130 |
131 | #生成密度数据
132 | def update_unstrgrid_density(self, density):
133 |
134 | self.unstrgrid_density.cell_data.scalars = density[self.index]
135 | self.unstrgrid_density.cell_data.scalars.name = 'density'
136 |
137 |
138 | #生成位移数据,scale1表示单纯位移图形的位移缩放因子,scale2用于应力、应变、密度图形的位移缩放
139 | def update_unstrgrid_displacement(self, U, scale1 = 0.05, scale2 = 0):
140 | if U.shape[1] == 2:
141 | U=np.column_stack((U,zeros(U.shape[0])))
142 | self.unstrgrid_displacement.points = global_variable.NODE_COORDINATES + scale1 * U
143 | self.unstrgrid_displacement.point_data.scalars = sqrt(sum(U ** 2, axis = 1))
144 | self.unstrgrid_displacement.point_data.scalars.name = 'USUM'
145 | self.unstrgrid_density.points = global_variable.NODE_COORDINATES + scale2 * U
146 | self.unstrgrid_strain.points = global_variable.NODE_COORDINATES + scale2 * U
147 | self.unstrgrid_stress.points = global_variable.NODE_COORDINATES + scale2 * U
148 |
149 |
150 | #生成应力数据
151 | def update_unstrgrid_stress(self, stress):
152 | self.unstrgrid_stress.point_data.scalars = stress
153 | self.unstrgrid_stress.point_data.scalars.name = 'stress'
154 |
155 |
156 | #生成应变数据
157 | def update_unstrgrid_strain(self, strain):
158 | self.unstrgrid_strain.point_data.scalars = strain
159 | self.unstrgrid_strain.point_data.scalars.name = 'strain'
160 |
161 |
162 |
163 |
164 | if __name__=='__main__':
165 | mymodel = ResultData()
166 |
167 |
--------------------------------------------------------------------------------
/Python/shorthaircat.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Standard imports.
4 | import numpy as np
5 | from numpy import e
6 | import threading
7 | import matplotlib.pyplot as plt
8 | from matplotlib.patches import Circle
9 | import matplotlib.ticker as ticker
10 |
11 | #user defined pakages import
12 | from optimization_simp import Simp
13 | from postprocessor import ResultData
14 | import global_variable
15 | from vtu2stl import *
16 |
17 | # Enthought imports.
18 | from traits.api import HasTraits, Instance, Property, Enum,Range,on_trait_change,Button,ToolbarButton#导入所需要的类型HasTraits:基类,Instance:实例类型 Property: Enum:枚举类型
19 | from traitsui.api import View, Item, Group,HSplit, VSplit, InstanceEditor,RangeEditor#导入traits属性的可视化api
20 | from tvtk.pyface.scene_editor import SceneEditor#导入场景配置api
21 | from mayavi.core.api import PipelineBase,Engine
22 | from mayavi.sources.vtk_data_source import VTKDataSource
23 | from mayavi.modules.api import Surface , Volume
24 | from mayavi.core.ui.engine_view import EngineView#导入引擎可视化模块
25 | from mayavi.core.ui.mayavi_scene import MayaviScene
26 | from mayavi.tools.mlab_scene_model import MlabSceneModel#导入mlab 绘图窗口的可视化模型
27 | from mayavi import mlab
28 |
29 | ######################################################################
30 | class ShorthairCat(HasTraits):
31 | '''
32 | 所有拥有traits属性的类都需要从HasTraits类继承
33 | '''
34 | density_filter = Range(0.0,1.0,1.0)
35 |
36 | calculate_button = ToolbarButton('Calculate')
37 |
38 | initial_button = Button('initialize')
39 | animate_button = Button('animate')
40 |
41 | # The scene model.
42 | scene = Instance(MlabSceneModel,())#此处进行了初始化
43 | scene0 = Instance(MlabSceneModel,())#位移场景
44 | scene1 = Instance(MlabSceneModel,())#应力场景
45 | scene2 = Instance(MlabSceneModel,())#应变场景
46 | scene3 = Instance(MlabSceneModel,())#密度场景
47 | scene4 = Instance(MlabSceneModel,())#动图场景
48 |
49 | plot = Instance(PipelineBase)#生成动画的实例
50 |
51 | # The mayavi engine view.
52 | engine_view = Instance(EngineView)
53 |
54 | # The current selection in the engine tree view.
55 | current_selection = Property
56 |
57 | ######################
58 | main_view = View(
59 | Group(
60 | Group(HSplit(HSplit(VSplit(
61 | Item(name='engine_view',
62 | style='custom',
63 | resizable=True,
64 | height =500,
65 | width = 200,
66 | show_label=False
67 |
68 | ),
69 | ),
70 | Item(name='current_selection',
71 | editor=InstanceEditor(),
72 | enabled_when='current_selection is not None',
73 | style='custom',
74 | resizable = True,
75 | height = 500,
76 | width = 200,
77 | springy=True,
78 | show_label=False),
79 | )),label = 'Settings',show_border = False),
80 | Group(
81 | Group(
82 |
83 | Item(name = 'density_filter',editor = RangeEditor()),
84 | '_',
85 | HSplit(
86 | Item('initial_button', show_label=False),
87 |
88 | Item('calculate_button', show_label=False),
89 | Item('animate_button', show_label=False))
90 |
91 | ),
92 | Group(
93 | Item(name='scene',
94 | editor=SceneEditor(),
95 | show_label=False,
96 | resizable=True,
97 | springy = True,
98 | height=600,
99 | width=600,
100 | label = 'mesh'
101 | ),
102 | Item(name='scene0',
103 | editor=SceneEditor(),
104 | show_label=False,
105 | resizable=True,
106 | springy=True,
107 | height=600,
108 | width=600,
109 | label='displacement'
110 | ),
111 | Item(name='scene1',
112 | editor=SceneEditor(),
113 | show_label=False,
114 | resizable=True,
115 | springy=True,
116 | height=600,
117 | width=600,
118 | label = 'stress'
119 | ),
120 | Item(name='scene2',
121 | editor=SceneEditor(),
122 | show_label=False,
123 | resizable=True,
124 | springy=True,
125 | height=600,
126 | width=600,
127 | label = 'strain'
128 | ),
129 | Item(name='scene3',
130 | editor=SceneEditor(),
131 | show_label=False,
132 | resizable=True,
133 | springy=True,
134 | height=600,
135 | width=600,
136 | label='density'
137 | ),
138 | Item(name='scene4',
139 | editor=SceneEditor(),
140 | show_label=False,
141 | resizable=True,
142 | springy=True,
143 | height=600,
144 | width=600,
145 | label='animating'
146 | ),
147 | layout = 'tabbed'),
148 |
149 | orientation = 'vertical'),
150 | orientation = 'horizontal'
151 | ),
152 | height = 600,
153 | width = 760,
154 | resizable=True,
155 | # scrollable=True,
156 | title = 'ShorthairCat',
157 | )
158 |
159 | #**traits 表示传入参数的个数不确定
160 | def __init__(self,type,r,penal,move,e,nu,volfac,**traits):
161 |
162 | HasTraits.__init__(self, **traits)
163 | self.scene.mayavi_scene.name = 'Geometry'
164 | self.scene.foreground = (1,170/255,0)
165 | self.scene0.mayavi_scene.name = 'Displacement'
166 | self.scene1.mayavi_scene.name = 'Stress'
167 | self.scene2.mayavi_scene.name = 'Strain'
168 | self.scene3.mayavi_scene.name = 'Density'
169 | self.scene4.mayavi_scene.name = 'Animate'
170 |
171 | #初始化enine_view
172 | self.engine_view = EngineView(engine=self.scene.engine)
173 |
174 | #对current_selection 进行动态监听,如果current_selection的值发生变化就调用 self._selection_change
175 |
176 | self.scene.engine.on_trait_change(self._selection_change,name = 'current_selection')
177 | self.simp_solver = None
178 | self.type = type
179 | self.r = r
180 | self.penal = penal
181 | self.move = move
182 | self.e = e
183 | self.nu = nu
184 | self.volfac = volfac
185 | self.address = 'H:\GitHub\Topology-optimization-of-structure-via-simp-method'
186 | self.i = 1
187 | def _initial_button_fired(self):
188 | self.initial_thread = threading.Thread(target = self._initial,args=(),name='Thread-1')
189 | self.initial_thread.daemon = True
190 | self.initial_thread.start()
191 |
192 | def _initial(self):
193 | global_variable.hyperparameter(r=self.r,move=self.move,e=self.e,penal=self.penal,nu=self.nu,volfac=self.volfac)
194 | global_variable.initialize_global_variable(type =self.type)
195 | self.simp_solver = Simp()
196 | self._mayavi()
197 | self.simp_solver.on_trait_change(self._update_vtkdatasource,name = 'loop')
198 | self.simp_solver.on_trait_change(self._save_fig, name='loop',dispatch = 'ui')
199 | def _save_fig(self):
200 | path = 'H:\GitHub\Topology-optimization-of-structure-via-simp-method\\fig\\'
201 | fname = path + 'density' + str(self.simp_solver.loop) + '.png'
202 | self.scene3.mayavi_scene.scene.save(fname)
203 |
204 |
205 |
206 |
207 |
208 | def _calculate_button_fired(self):
209 | #监听loop,一改变立刻更新曲线,同时建立background thread ,在后台进行有限元计算
210 | # self.simp_solver.on_trait_change(self._plot_convergence_curve, name='loop', dispatch='new')#TODO 发现如果用dispatch = 'ui' 有很大几率卡死,但是这个模式会报错,不过不影响使用
211 | #self.simp_solver.on_trait_change(self._plot,name = 'loop')
212 |
213 | self.computation_thread = threading.Thread(target=self.simp_solver.simp,args=(),name= 'Thread-2')
214 | self.computation_thread.daemon = True
215 | self.computation_thread.start()
216 |
217 | self.plot_thread = threading.Thread(target = self._plot_convergence_curve,args = (),name = 'Thread-3')
218 | self.plot_thread.daemon = True
219 | self.plot_thread.start()
220 |
221 |
222 |
223 |
224 | def _animate_button_fired(self):
225 | #创建一个background thread 不停的显示动画
226 | animate_thread = threading.Thread(target= self._animate(),args=())
227 | animate_thread.daemon = True
228 | animate_thread.start()
229 |
230 | # 静态监听密度过滤器
231 | def _density_filter_changed(self):
232 | print('the density is :',self.density_filter)
233 | self.simp_solver.resultdata.unstrgrid_density = self.simp_solver.resultdata.generate_unstrgrid_mesh(self.density_filter)
234 | self.simp_solver.resultdata.update_unstrgrid_density(self.simp_solver.resultdata.density)
235 | self.simp_solver.resultdata.vtkdatasource_density.data = self.simp_solver.resultdata.unstrgrid_density
236 | self.simp_solver.resultdata.vtkdatasource_density.update()
237 |
238 | self.simp_solver.resultdata.unstrgrid_stress = self.simp_solver.resultdata.generate_unstrgrid_mesh(self.density_filter)
239 | self.simp_solver.resultdata.update_unstrgrid_stress(self.simp_solver.resultdata.stress)
240 | self.simp_solver.resultdata.vtkdatasource_stress.data = self.simp_solver.resultdata.unstrgrid_stress
241 | self.simp_solver.resultdata.vtkdatasource_stress.update()
242 |
243 |
244 | #初始化场景
245 | def _mayavi(self):
246 | """Shows how you can generate data using mayavi instead of mlab."""
247 | print('updating mayavi')
248 |
249 | e = self.scene.engine
250 |
251 | #网格scene配置
252 | e.current_scene = self.scene.mayavi_scene
253 | e.add_source(self.simp_solver.resultdata.vtkdatasource_mesh)
254 | e.add_module(Surface(name = 'mesh_wireframe'))
255 | e.current_scene.children[0].children[0].children[0].actor.property.representation = 'wireframe'
256 | e.current_scene.children[0].children[0].children[0].actor.property.color = (0,0,0)
257 | e.current_scene.children[0].children[0].children[0].actor.property.line_width = 1.0
258 | e.add_module(Surface(name='mesh_solid'))
259 |
260 | #位移scene配置
261 | e.current_scene = self.scene0.mayavi_scene
262 | e.add_source(self.simp_solver.resultdata.vtkdatasource_displacement)
263 | e.add_module(Surface(name = 'displacement'))
264 | self.scene.engine.current_scene.children[0].children[0].children[0].enable_contours = True
265 | self.scene.engine.current_scene.children[0].children[0].children[0].contour.filled_contours = True
266 | self.scene.engine.current_scene.children[0].children[0].children[0].module_manager.scalar_lut_manager.show_legend = True
267 |
268 | #应力scene配置
269 | e.current_scene = self.scene1.mayavi_scene
270 | e.add_source(self.simp_solver.resultdata.vtkdatasource_stress)
271 | e.add_module(Surface(name = 'stress'))
272 | self.scene.engine.current_scene.children[0].children[0].children[0].enable_contours = True
273 | self.scene.engine.current_scene.children[0].children[0].children[0].contour.filled_contours = True
274 | self.scene.engine.current_scene.children[0].children[0].children[0].module_manager.scalar_lut_manager.show_legend = True
275 |
276 | #应变scene配置
277 | e.current_scene = self.scene2.mayavi_scene
278 | e.add_source(self.simp_solver.resultdata.vtkdatasource_strain)
279 | e.add_module(Surface(name = 'strain'))
280 | self.scene.engine.current_scene.children[0].children[0].children[0].enable_contours = True
281 | self.scene.engine.current_scene.children[0].children[0].children[0].contour.filled_contours = True
282 | self.scene.engine.current_scene.children[0].children[0].children[0].module_manager.scalar_lut_manager.show_legend = True
283 |
284 | #密度scene配置
285 | e.current_scene = self.scene3.mayavi_scene
286 | e.add_source(self.simp_solver.resultdata.vtkdatasource_density)
287 | e.add_module(Surface(name = 'density'))
288 | self.scene.engine.current_scene.children[0].children[0].children[0].module_manager.scalar_lut_manager.show_legend = True
289 |
290 |
291 | def _update_vtkdatasource(self,old,new):
292 | self.simp_solver.loop
293 |
294 | filter = 0
295 | print('updating vtkdatasource')
296 | if 0< self.simp_solver.loop < 10:
297 | filter = 0.85
298 | if self.simp_solver.loop >= 10:
299 | filter = 1/np.e**(self.i) + 0.5
300 | self.i = self.i+1
301 | self.simp_solver.resultdata.vtkdatasource_displacement.data = self.simp_solver.resultdata.unstrgrid_displacement
302 | self.simp_solver.resultdata.vtkdatasource_displacement.update()
303 |
304 | self.simp_solver.resultdata.vtkdatasource_stress.data = self.simp_solver.resultdata.unstrgrid_stress
305 | self.simp_solver.resultdata.vtkdatasource_stress.update()
306 |
307 | self.simp_solver.resultdata.vtkdatasource_strain.data = self.simp_solver.resultdata.unstrgrid_strain
308 | self.simp_solver.resultdata.vtkdatasource_strain.update()
309 | self.simp_solver.resultdata.unstrgrid_density = self.simp_solver.resultdata.generate_unstrgrid_mesh(filter=1)
310 | self.simp_solver.resultdata.update_unstrgrid_density(self.simp_solver.resultdata.density)
311 | self.simp_solver.resultdata.vtkdatasource_density.data = self.simp_solver.resultdata.unstrgrid_density
312 | self.simp_solver.resultdata.vtkdatasource_density.update()
313 |
314 | print('updating done')
315 | print("----------------------")
316 |
317 | #动态监听currentselection
318 | def _selection_change(self, old, new):
319 | self.trait_property_changed('current_selection', old, new)
320 |
321 | def _get_current_selection(self):
322 | return self.scene.engine.current_selection
323 |
324 | def _plot_convergence_curve(self):
325 |
326 | plt.close() # clf() # 清图 cla() # 清坐标轴 close() # 关窗口
327 |
328 | fig = plt.figure()
329 | fig.hold(False)
330 | ax = fig.add_subplot(1, 1, 1)
331 | ax.axis("auto")
332 | ax.set_ylabel('Strain_energy')
333 |
334 | ax.set_title('convergence curves of strain energy and volume rate')
335 |
336 | ax1 = ax.twinx()
337 | ax1.set_ylabel('volume_rate')
338 | ax1.set_ylim([0,1])
339 | # ax.xaxis()# 设置图像显示的时候XY轴比例
340 | plt.grid(True) # 添加网格
341 | plt.ion() # interactive mode on
342 | try:
343 | while 1:
344 | ax.set_xlabel('Iteration:' + str(self.simp_solver.loop))
345 | ax.plot(self.simp_solver.strain_energy,c='b')
346 | ax1.plot(self.simp_solver.volume_rate,c = 'g')
347 | plt.pause(0.5)
348 | if self.simp_solver.finished:
349 | break
350 | ax.plot(self.simp_solver.strain_energy,c = 'b')
351 | ax1.plot(self.simp_solver.volume_rate, c='g')
352 | plt.savefig('Convergence_curve.png')
353 | plt.pause(36000)
354 |
355 | except Exception as err:
356 | print(err)
357 | # plt.plot(self.simp_solver.strain_energy)H:\GitHub\Topology-optimization-of-structure-via-simp-method\Python
358 | # ylabel = 'strain_energy/iteration: '+str(self.simp_solver.loop)
359 | # plt.ylabel(ylabel)
360 | # plt.xlabel('steps')
361 | # plt.title('convergence curve of strain energy')
362 | # plt.show()
363 |
364 |
365 |
366 | def _plot(self):
367 | pass
368 | def _animate(self):
369 |
370 | self.scene.engine.current_scene = self.scene4.mayavi_scene
371 | src = mlab.pipeline.open((self.address+'\density\density_00.vtu'))
372 | src.play = False
373 | src.add_module(Surface(name='animate_density'))
374 | # self.scene.engine.current_scene.children[0].children[0].children[0].enable_contours=True
375 | # self.scene.engine.current_scene.children[0].children[0].children[0].contour.filled_contours=True
376 | self.scene.engine.current_scene.children[0].children[0].children[0].module_manager.scalar_lut_manager.show_legend=True
377 |
378 |
379 | if __name__ == '__main__':
380 | #for cantilever2D e = 1, nu = 0.3
381 | #for complex2D e = 2.1*50000 ,nu = 0.3
382 | #for MBB,L_shape,center_load,distributed_load:
383 | # e = 1, nu = 0.3
384 | #L_shape: r = 1.5
385 | #MBB,center_load,distributed_load: r = 1.2
386 |
387 | # m = ShorthairCat(type='top2d',e =1, nu=0.3, r = 1.2, penal = 3, move = 0.1,volfac = 0.4)
388 | #e = 20000
389 | m = ShorthairCat(type='top3d',e =1000, nu=0.2, r = 30, penal = 3, move = 0.1,volfac = 0.2)
390 | m.configure_traits()
391 | try:
392 | vtu2stl()
393 | except:
394 | print('an error occured,please check your vtu file named as :top3d ')
395 |
396 |
397 |
398 |
--------------------------------------------------------------------------------
/Python/top2d_minf.txt:
--------------------------------------------------------------------------------
1 | /CWD,'H:\GitHub\Topology-optimization-of-structure-via-simp-method\\ANSYS\results'
2 | RESUME,'cantilever2D','db','H:\GitHub\Topology-optimization-of-structure-via-simp-method\ANSYS\models',0,0
3 | /FILENAME,top2d,1
4 | /TITLE,top2d
5 |
6 | *GET,ELEMSUM,ELEMENT,0,COUNT
7 | *GET,NODESUM,NODE,0,COUNT
8 | *CFOPEN,elements_nodes_counts,txt
9 | *VWRITE,ELEMSUM,NODESUM
10 | %10I %10I
11 | *CFCLOS
12 |
13 |
14 |
15 | *DIM,ELEMENTS_NODES,ARRAY,ELEMSUM,4
16 | *VGET,ELEMENTS_NODES(1,1),ELEM,1,NODE,1
17 | *VGET,ELEMENTS_NODES(1,2),ELEM,1,NODE,2
18 | *VGET,ELEMENTS_NODES(1,3),ELEM,1,NODE,3
19 | *VGET,ELEMENTS_NODES(1,4),ELEM,1,NODE,4
20 | *CFOPEN,elements_nodes,txt
21 | *VWRITE,ELEMENTS_NODES(1,1), ELEMENTS_NODES(1,2),ELEMENTS_NODES(1,3),ELEMENTS_NODES(1,4)
22 | %10I %10I %10I %10I
23 | *CFCLOS
24 |
25 |
26 | *DIM,ELEMENTS_CENTERS,ARRAY,ELEMSUM,3
27 | *VGET,ELEMENTS_CENTERS(1,1),ELEM,1,CENT,X
28 | *VGET,ELEMENTS_CENTERS(1,2),ELEM,1,CENT,Y
29 | *VGET,ELEMENTS_CENTERS(1,3),ELEM,1,CENT,Z
30 | *CFOPEN,elements_centers,txt
31 | *VWRITE,SEQU,ELEMENTS_CENTERS(1,1), ELEMENTS_CENTERS(1,2),ELEMENTS_CENTERS(1,3)
32 | %10I %15.7E %15.7E %15.7E
33 | *CFCLOS
34 |
35 | *DIM,NODES_COORDINATE,ARRAY,NODESUM,3
36 | *VGET,NODES_COORDINATE(1,1),NODE,1,LOC,X
37 | *VGET,NODES_COORDINATE(1,2),NODE,1,LOC,Y
38 | *VGET,NODES_COORDINATE(1,3),NODE,1,LOC,Z
39 | *CFOPEN,node_coordinates,txt
40 | *VWRITE,NODES_COORDINATE(1,1),NODES_COORDINATE(1,2),NODES_COORDINATE(1,3)
41 | %15.7E %15.7E %15.7E
42 | *CFCLOS
43 |
44 | *DIM,ELEMENTS_VOLUMN,ARRAY,ELEMSUM,1
45 | *VGET,ELEMENTS_VOLUMN(1,1),ELEM,1,GEOM
46 | *CFOPEN,elements_volumn,txt
47 | *VWRITE,SEQU,ELEMENTS_VOLUMN(1,1)
48 | %10I %15.7E
49 | *CFCLOS
50 |
51 | *CFOPEN,elements_nodes_counts,txt
52 | *VWRITE,ELEMSUM,NODESUM
53 | %10I %10I
54 | *CFCLOS
55 |
56 | !for 2D project
57 | *DIM,connectedelem,ARRAY,100,ELEMSUM
58 | *DIM,neibors,ARRAY,ELEMSUM,100
59 | *DO,i,1,ELEMSUM
60 | ESEL,S, , ,i
61 | NSLE,S
62 | ESLN,S
63 | *VGET,connectedelem(1,i),ELEM,,ELIST
64 | *ENDDO
65 | ALLSEL,ALL
66 | *MFUN,neibors(1,1),TRAN,connectedelem(1,1)
67 | *MWRITE,neibors(1,1),neibors,txt,,JIK,100,ELEMSUM,1
68 | (100F10.0)
69 | *CFCLOS
70 |
71 | SAVE
72 | FINISH
73 | exit
--------------------------------------------------------------------------------
/Python/top2d_rinf.txt:
--------------------------------------------------------------------------------
1 | /CWD,'H:\GitHub\Topology-optimization-of-structure-via-simp-method\\ANSYS\results'
2 | RESUME,'cantilever2D','db','H:\GitHub\Topology-optimization-of-structure-via-simp-method\ANSYS\models',0,0
3 | /FILENAME,top2d,1
4 | /TITLE,top2d
5 |
6 | *GET,ELEMSUM,ELEMENT,0,COUNT
7 | *GET,NODESUM,NODE,0,COUNT
8 |
9 |
10 | *DIM,MATERIAL,ARRAY,ELEMSUM,2
11 | *VREAD,MATERIAL(1,1),material,txt,,JIK,2,ELEMSUM
12 | (E14.7,E15.7)
13 |
14 |
15 | /PREP7
16 | *DO,i,1,ELEMSUM
17 | MP,EX,i,MATERIAL(i,2)
18 | MP,PRXY,i,MATERIAL(i,1)
19 | *ENDDO
20 |
21 |
22 | *DO,i,1,ELEMSUM
23 | FLST,2,1,2,ORDE,1
24 | FITEM,2,i
25 | EMODIF,P51X,MAT,i,
26 | *ENDDO
27 |
28 |
29 | /SOL
30 | ANTYPE,0
31 |
32 |
33 | /STATUS,SOLU
34 | SOLVE
35 |
36 | /POST1
37 | ETABLE,strain_e,SENE,
38 | *VGET,strain_e,ELEM,1,ETAB,STRAIN_E, , ,2
39 | *CFOPEN,strain_energy,txt
40 | *VWRITE,strain_e(1,1)
41 | (E15.7)
42 | *CFCLOS
43 |
44 |
45 |
46 | *DIM,NODAL_SOLUTION_STRESS,ARRAY,NODESUM,1
47 | *VGET,NODAL_SOLUTION_STRESS(1,1),NODE,1,S,EQV
48 | *CFOPEN,nodal_solution_stress,txt
49 | *VWRITE,SEQU,NODAL_SOLUTION_STRESS(1,1)
50 | %10I %15.7E
51 | *CFCLOS
52 |
53 | *DIM,NODAL_SOLUTION_STRAIN,ARRAY,NODESUM,1
54 | *VGET,NODAL_SOLUTION_STRAIN(1,1),NODE,1,EPTO,INT
55 | *CFOPEN,nodal_solution_strain,txt
56 | *VWRITE,SEQU,NODAL_SOLUTION_STRAIN(1,1)
57 | %10I %15.7E
58 | *CFCLOS
59 |
60 |
61 | *DIM,NODAL_SOLUTION_U,ARRAY,NODESUM,2
62 | *VGET,NODAL_SOLUTION_U(1,1),NODE,1,U,X
63 | *VGET,NODAL_SOLUTION_U(1,2),NODE,1,U,Y
64 | *CFOPEN,nodal_solution_u,txt
65 | *VWRITE,NODAL_SOLUTION_U(1,1), NODAL_SOLUTION_U(1,2)
66 | (E15.7,' ',E15.7)
67 | *CFCLOS
68 |
69 | SAVE
70 | FINISH
71 | exit
--------------------------------------------------------------------------------
/Python/top3d_minf.txt:
--------------------------------------------------------------------------------
1 | /CWD,'H:\GitHub\Topology-optimization-of-structure-via-simp-method\\ANSYS\results'
2 | RESUME,'cheshen','db','H:\GitHub\Topology-optimization-of-structure-via-simp-method\ANSYS\models',0,0
3 | /FILENAME,top3d,1
4 | /TITLE,top3d
5 |
6 | !optional for user defined command
7 | ESEL,U,TYPE,,5,6
8 |
9 | *GET,ELEMSUM,ELEMENT,0,COUNT
10 | *GET,NODESUM,NODE,0,COUNT
11 | *CFOPEN,elements_nodes_counts,txt
12 | *VWRITE,ELEMSUM,NODESUM
13 | %10I %10I
14 | *CFCLOS
15 |
16 | /SOL
17 | ANTYPE,0
18 |
19 | !/OUTPUT,elements_stiffness,out,,
20 | !/DEBUG,-1,,,1
21 |
22 | !/STATUS,SOLU
23 | !SOLVE
24 |
25 | *DIM,ELEMENTS_NODES,ARRAY,ELEMSUM,8
26 | *VGET,ELEMENTS_NODES(1,1),ELEM,1,NODE,1
27 | *VGET,ELEMENTS_NODES(1,2),ELEM,1,NODE,2
28 | *VGET,ELEMENTS_NODES(1,3),ELEM,1,NODE,3
29 | *VGET,ELEMENTS_NODES(1,4),ELEM,1,NODE,4
30 | *VGET,ELEMENTS_NODES(1,5),ELEM,1,NODE,5
31 | *VGET,ELEMENTS_NODES(1,6),ELEM,1,NODE,6
32 | *VGET,ELEMENTS_NODES(1,7),ELEM,1,NODE,7
33 | *VGET,ELEMENTS_NODES(1,8),ELEM,1,NODE,8
34 | *CFOPEN,elements_nodes,txt
35 | *VWRITE,ELEMENTS_NODES(1,1), ELEMENTS_NODES(1,2),ELEMENTS_NODES(1,3),ELEMENTS_NODES(1,4),ELEMENTS_NODES(1,5),ELEMENTS_NODES(1,6),ELEMENTS_NODES(1,7),ELEMENTS_NODES(1,8)
36 | %10I %10I %10I %10I %10I %10I %10I %10I
37 | *CFCLOS
38 |
39 | *DIM,ELEMENTS_CENTERS,ARRAY,ELEMSUM,3
40 | *VGET,ELEMENTS_CENTERS(1,1),ELEM,1,CENT,X
41 | *VGET,ELEMENTS_CENTERS(1,2),ELEM,1,CENT,Y
42 | *VGET,ELEMENTS_CENTERS(1,3),ELEM,1,CENT,Z
43 | *CFOPEN,elements_centers,txt
44 | *VWRITE,SEQU,ELEMENTS_CENTERS(1,1), ELEMENTS_CENTERS(1,2),ELEMENTS_CENTERS(1,3)
45 | %10I %15.7E %15.7E %15.7E
46 | *CFCLOS
47 |
48 | *DIM,NODES_COORDINATE,ARRAY,NODESUM,3
49 | *VGET,NODES_COORDINATE(1,1),NODE,1,LOC,X
50 | *VGET,NODES_COORDINATE(1,2),NODE,1,LOC,Y
51 | *VGET,NODES_COORDINATE(1,3),NODE,1,LOC,Z
52 | *CFOPEN,node_coordinates,txt
53 | *VWRITE,NODES_COORDINATE(1,1),NODES_COORDINATE(1,2),NODES_COORDINATE(1,3)
54 | %15.7E %15.7E %15.7E
55 | *CFCLOS
56 |
57 | *DIM,ELEMENTS_VOLUMN,ARRAY,ELEMSUM,1
58 | *VGET,ELEMENTS_VOLUMN(1,1),ELEM,1,GEOM
59 | *CFOPEN,elements_volumn,txt
60 | *VWRITE,SEQU,ELEMENTS_VOLUMN(1,1)
61 | %10I %15.7E
62 | *CFCLOS
63 |
64 |
65 | !for 3D project
66 | *DIM,connectedelem,ARRAY,100,ELEMSUM
67 | *DIM,neibors,ARRAY,ELEMSUM,100
68 | *DO,i,1,ELEMSUM
69 | ESEL,S, , ,i
70 | NSLE,S
71 | ESLN,S
72 | ESEL,U,TYPE,,5,6
73 | *VGET,connectedelem(1,i),ELEM,,ELIST
74 | *ENDDO
75 |
76 | ALLSEL,ALL
77 | *MFUN,neibors(1,1),TRAN,connectedelem(1,1)
78 | *MWRITE,neibors(1,1),neibors,txt,,JIK,100,ELEMSUM,1
79 | (100F10.0)
80 | *CFCLOS
81 |
82 | ALLSEL,ALL
83 | SAVE
84 | FINISH
85 | exit
--------------------------------------------------------------------------------
/Python/top3d_rinf.txt:
--------------------------------------------------------------------------------
1 | /CWD,'H:\GitHub\Topology-optimization-of-structure-via-simp-method\\ANSYS\results'
2 | RESUME,'cheshen','db','H:\GitHub\Topology-optimization-of-structure-via-simp-method\ANSYS\models',0,0
3 | /FILENAME,top3d,1
4 | /TITLE,top3d
5 | ALLSEL,ALL
6 | ! optional for your project
7 | ESEL,U,TYPE,,5,6
8 |
9 | *GET,ELEMSUM,ELEMENT,0,COUNT
10 | *GET,NODESUM,NODE,0,COUNT
11 |
12 |
13 | *DIM,MATERIAL,ARRAY,ELEMSUM,2
14 | *VREAD,MATERIAL(1,1),material,txt,,JIK,2,ELEMSUM
15 | (E14.7,E15.7)
16 |
17 | /PREP7
18 | *DO,i,1,ELEMSUM
19 | MP,EX,i,MATERIAL(i,2)
20 | MP,PRXY,i,MATERIAL(i,1)
21 | *ENDDO
22 |
23 | *DO,i,1,ELEMSUM
24 | FLST,2,1,2,ORDE,1
25 | FITEM,2,i
26 | EMODIF,P51X,MAT,i,
27 | *ENDDO
28 |
29 | ALLSEL,ALL
30 |
31 | /SOL
32 | ANTYPE,0
33 | EQSLV,PCG,1E-4
34 | /STATUS,SOLU
35 | SOLVE
36 |
37 | ESEL,U,TYPE,,5,6
38 |
39 | /POST1
40 | ETABLE,strain_e,SENE,
41 | *VGET,strain_e,ELEM,1,ETAB,STRAIN_E, , ,2
42 | *CFOPEN,strain_energy,txt
43 | *VWRITE,strain_e(1,1)
44 | (E15.7)
45 | *CFCLOS
46 |
47 | *DIM,NODAL_SOLUTION_U,ARRAY,NODESUM,3
48 | *VGET,NODAL_SOLUTION_U(1,1),NODE,1,U,X
49 | *VGET,NODAL_SOLUTION_U(1,2),NODE,1,U,Y
50 | *VGET,NODAL_SOLUTION_U(1,3),NODE,1,U,Z
51 | *CFOPEN,nodal_solution_u,txt
52 | *VWRITE,NODAL_SOLUTION_U(1,1), NODAL_SOLUTION_U(1,2),NODAL_SOLUTION_U(1,3)
53 | (E15.7,' ',E15.7,' ',E15.7)
54 | *CFCLOS
55 |
56 |
57 | *DIM,NODAL_SOLUTION_STRESS,ARRAY,NODESUM,1
58 | *VGET,NODAL_SOLUTION_STRESS(1,1),NODE,1,S,EQV
59 | *CFOPEN,nodal_solution_stress,txt
60 | *VWRITE,SEQU,NODAL_SOLUTION_STRESS(1,1)
61 | %10I %15.7E
62 | *CFCLOS
63 |
64 | *DIM,NODAL_SOLUTION_STRAIN,ARRAY,NODESUM,1
65 | *VGET,NODAL_SOLUTION_STRAIN(1,1),NODE,1,EPTO,INT
66 | *CFOPEN,nodal_solution_strain,txt
67 | *VWRITE,SEQU,NODAL_SOLUTION_STRAIN(1,1)
68 | %10I %15.7E
69 | *CFCLOS
70 |
71 | ALLSEL,ALL
72 |
73 | SAVE
74 | FINISH
75 | exit
--------------------------------------------------------------------------------
/Python/vtu2stl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | """Convert UnstructuredGrid in .vtk files to STL files."""
4 | from tvtk.api import tvtk
5 |
6 |
7 | def vtu2stl():
8 | reader = tvtk.XMLUnstructuredGridReader()
9 | reader.file_name="top3d.vtu"
10 |
11 |
12 | surface_filter = tvtk.DataSetSurfaceFilter()
13 | surface_filter.set_input_connection(reader.output_port)
14 |
15 | triangle_filter = tvtk.TriangleFilter()
16 | triangle_filter.set_input_connection(surface_filter.output_port)
17 |
18 | writer = tvtk.STLWriter()
19 | writer.file_name='top3d.stl'
20 | writer.set_input_connection(triangle_filter.output_port)
21 | writer.write()
22 |
23 | # import sys
24 | # import vtk
25 |
26 | # reader = vtk.vtkUnstructuredGridReader()
27 | # reader.SetFileName('L_shape.vtu')
28 | #
29 | # surface_filter = vtk.vtkDataSetSurfaceFilter()
30 | # surface_filter.SetInputConnection(reader.GetOutputPort())
31 | #
32 | # triangle_filter = vtk.vtkTriangleFilter()
33 | # triangle_filter.SetInputConnection(surface_filter.GetOutputPort())
34 | #
35 | # writer = vtk.vtkSTLWriter()
36 | # writer.SetFileName('L_shape.stl')
37 | # writer.SetInputConnection(triangle_filter.GetOutputPort())
38 | # writer.Write()
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Topology-optimizer
2 |
3 | This project contains topology optimization of structure with SIMP ,the codes are written by Python and used the FEA solver api provided by ANSYS.
4 |
5 | ## python environment configuration
6 | This app is developed in python 3.6 and require packages as follows :
7 | * trait 4.6.0
8 | * traitsui 5.1.0
9 | * mayavi 4.5.0
10 | * vtk 7.1.0
11 | * PyQt 4.11 (this is very important ,if you use PyQt 4.12 ,the app will not work)
12 | ## usage
13 | * 1. you should get the right ANSYS MAPDL.exe path in your computer to substitude the `self.ansys_path` in `finite_element_analysis.py`
14 | and choose the result path to save ANSYS result files.
15 | * 2. you should edit APDL by your self,and there are some tips:
16 | `a` you should make sure that the path of .db file you resume from is right which should be the same as your ANSYS result files path you set in usage1
17 | `b`the application can only optimize the modal that consist following kind of element:
18 | `3 nodes triangular element` ,`4 nodes quadrangle element `,`4 nodes tetrahedron element `,`8 nodes hexahedron element`,if your model is generated by workbench you should be careful of this, expecially when your model contain surface load.
19 | `c`you need get model information once by ANSYS apdl ,so the first time you boot the app you need boot ansys ,but in the later computer ,such as change a new hyperparameter,you don't need computer model again to obtain model information ,so you can anotate the line `ANSYS_SOLVER.boot()` in global_variable.py
20 |
21 | ## version info
22 | All version information are discribed as follows:
23 |
24 | ### version 1.0.0
25 | * This is a basic optimization frame.
26 | * The benchmark is a 2D rectangular cantilever , contains two modules: optimization and finite element analysis.The next release will contain data visualization of stress, strain,and displacement.
27 |
28 | ### version 1.1.0
29 | This version contains postprocessor to visualize the optimization results.
30 | Mainly include:
31 | * Show the stress, strain, and displacement timely.
32 | * Show the convergence curve timely.
33 | * Animate the results and make movie.
34 | The SDK is Mayavi+traits+traitsUI+tvtk.
35 |
36 | ### version 1.2.0
37 | This version is versatile,it can optimize 2D and 3D structure no matter using what kind of grid to mesh the geometry.
38 | And,this vertion is more much more efficient as I replaced the for loop by vectorizing.
39 |
40 | ### version 1.3.0
41 | this version used a new algorithm to restrain checkerboard and mesh independence, And no longer computing strain energy in every iteration step ,instead we getting strain energy from ANSYS directly.This improvement can enhance the robust and efficiency of the optimizer.
42 |
43 | ### version 1.4.0
44 | * For robust ,use the same filter as mentioned in "top99"
45 | * save pictures and make movie by ffmpeg
46 | * transfer topology results formated by vtu to stl format which can be used to 3D printing
47 |
48 | ## GUI exhibition
49 | The app named as shorthaircat.
50 | Shorthaircat could show convergency curve and displacement, stress ,strain ,density in time.
51 | we choose some GUI figures exhibited here:
52 |
53 |
54 |
55 |
57 |
58 |
72 |
73 |
77 |
78 |
81 |
82 |
83 |
87 |
88 |
91 |
92 |
108 |
109 |
113 |
114 |
116 |
117 |
118 |
122 |
123 |
125 |
126 |
127 |
131 |
132 |
133 |
137 |
138 |
139 |
143 |
144 |
148 |
149 |
150 |
152 |
153 |