├── .gitattributes ├── .idea ├── .gitignore ├── FreeTO.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── modules.xml └── vcs.xml ├── FreeTO.m ├── HHs3D.m ├── HnHns3D.m ├── LICENSE ├── README.md ├── SEMDOT.m ├── SIMP.m ├── STLs ├── Fig1.png ├── Fig2.png ├── Fig3.png ├── Fig4.jpg ├── GE_domain.STL ├── GE_fixed.STL ├── GE_force.STL ├── L_domain.STL ├── L_fixed.STL ├── L_force.STL ├── air_brack_TO.stl ├── air_domain.STL ├── air_fixed.STL ├── air_force.STL ├── air_zfixed.STL ├── hand_TO.gif ├── hand_domain.stl ├── hand_fixed.stl ├── hand_force1.stl ├── hand_force2.stl ├── hand_force3.stl ├── hand_force4.stl ├── hand_force5.stl ├── lever_domain.STL ├── lever_fixed.STL ├── lever_force1.STL ├── lever_force2.STL ├── lever_zfixed.STL ├── quad_TO.stl ├── quad_domain.STL ├── quad_fixed.STL ├── quad_force1.STL ├── quad_force2.STL ├── quad_force3.STL ├── quad_xfixed.STL └── quad_yfixed.STL ├── display_3Dsmooth.m ├── domainprep.m ├── domainstokeep.m ├── forcevec.m ├── geomeshini.m ├── intriangulation.m ├── lk_H8.m ├── smoothedge3D.m ├── stlgen.m ├── supportDOFs.m └── symmetry.m /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/FreeTO.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FreeTO.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | %%%% FreeTO - 3D Topology Optimization of Freeform Structures %%% 3 | %%%% Author: Osezua Ibhadode, March 2024 %%%%%%%%% 4 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 5 | function S = FreeTO(domain,force1,MeshControl,volfrac,varargin) 6 | %% Examples 7 | %FreeTO('STLs\GE_domain.stl','STLs\GE_force.stl',70,0.3,'fixed','STLs\GE_fixed.stl','force2','STLs\GE_force.stl','Fmagy',[0 -2000],'Fmagz',[1500 0],'YoungsModulus',210e9) 8 | %FreeTO('STLs\air_domain.stl','STLs\air_force.stl',90,0.2,'fixed','STLs\air_fixed.stl','zfixed','STLs\air_zfixed.stl','force2','STLs\air_force.stl','force3','STLs\air_force.stl','Fmagx',[1000 1324 0],'Fmagy',[0 -1324 -2500],'YoungsModulus',210e9,'Symmetry1',"x-y",'direction1',"right",'optimization','SEMDOT','modelName','STLs\air_brack_TO.stl') 9 | %FreeTO('STLs\hand_domain.stl','STLs\hand_force1.stl',90,0.3,'fixed','STLs\hand_fixed.stl','force2','STLs\hand_force2.stl','force3','STLs\hand_force3.stl','force4','STLs\hand_force4.stl','force5','STLs\hand_force5.stl','Fmagz',2e3*ones(1,5),'YoungsModulus',210e9) 10 | %FreeTO('STLs\quad_domain.stl','STLs\quad_force1.stl',70,0.3,'fixed','STLs\quad_fixed.stl','xfixed','STLs\quad_xfixed.stl','yfixed','STLs\quad_yfixed.stl','force2','STLs\quad_force2.stl','force3','STLs\quad_force3.stl','Fmagz',[-1500 -1500 -1000],'YoungsModulus',2e9,'Symmetry1','y-z','Symmetry2','z-x','direction1','right','direction2','left','modelName','STLs\quad_TO.stl','keep_BCz','yes') 11 | 12 | %% Default values of parameters 13 | default_fix = []; default_xfix = []; default_yfix = []; default_zfix = []; 14 | default_for2 = []; default_for3 = []; default_for4 = []; default_for5 = []; 15 | default_for6 = []; default_for7 = []; default_for8 = []; default_for9 = []; 16 | default_for10 = []; default_keepdom = []; 17 | default_keep_BC = 'yes'; default_keep_BCx = 'no'; default_keep_BCy = 'no'; default_keep_BCz = 'no'; default_E0 = 1; 18 | default_penal = 3; default_rmin = 1.5; default_Fmagx = 0; default_Fmagy = 0; default_Fmagz = 0; 19 | default_text = []; default_load = 'distributed'; 20 | default_sym1 = []; default_dir1 = 'right'; default_sym2 = []; default_dir2 = 'right'; default_sym3 = []; default_dir3 = 'right'; 21 | default_v = 0.3; default_opt_type = 'SIMP'; 22 | 23 | %% Input Parser for required, optional and name-value parameters 24 | pp = inputParser; 25 | addRequired(pp,'domain'); addRequired(pp,'force1'); addRequired(pp,'MeshControl'); addRequired(pp,'volfrac'); 26 | addOptional(pp,'fixed',default_fix); addOptional(pp,'xfixed',default_xfix); 27 | addOptional(pp,'yfixed',default_yfix); addOptional(pp,'zfixed',default_zfix); 28 | addOptional(pp,'force2',default_for2); addOptional(pp,'force3',default_for3); 29 | addOptional(pp,'force4',default_for4); addOptional(pp,'force5',default_for5); 30 | addOptional(pp,'force6',default_for6); addOptional(pp,'force7',default_for7); 31 | addOptional(pp,'force8',default_for8); addOptional(pp,'force9',default_for9); 32 | addOptional(pp,'force10',default_for10); addOptional(pp,'keepdom',default_keepdom); 33 | addParameter(pp,'keep_BC',default_keep_BC); addParameter(pp,'keep_BCx',default_keep_BCx); 34 | addParameter(pp,'keep_BCy',default_keep_BCy); addParameter(pp,'keep_BCz',default_keep_BCz); 35 | addParameter(pp,'YoungsModulus',default_E0); addParameter(pp,'optimization',default_opt_type); 36 | addParameter(pp,'penaltySIMP',default_penal); addParameter(pp,'filterRadius',default_rmin); 37 | addParameter(pp,'Fmagx',default_Fmagx); addParameter(pp,'Fmagy',default_Fmagy); 38 | addParameter(pp,'Fmagz',default_Fmagz); addParameter(pp,'modelName',default_text); 39 | addParameter(pp,'loadtype',default_load); addParameter(pp,'PoissonRatio',default_v); 40 | addParameter(pp,'Symmetry1',default_sym1); addParameter(pp,'direction1',default_dir1); 41 | addParameter(pp,'Symmetry2',default_sym2); addParameter(pp,'direction2',default_dir2); 42 | addParameter(pp,'Symmetry3',default_sym3); addParameter(pp,'direction3',default_dir3); 43 | 44 | parse(pp,domain,force1,MeshControl,volfrac,varargin{:}); 45 | 46 | %% Domain Initialization 47 | domain = pp.Results.domain; MeshControl = pp.Results.MeshControl; 48 | 49 | %% Optimization parameters 50 | volfrac = pp.Results.volfrac; penal = pp.Results.penaltySIMP; 51 | rmin = pp.Results.filterRadius; E0 = pp.Results.YoungsModulus;v = pp.Results.PoissonRatio; 52 | opt_type = pp.Results.optimization; 53 | 54 | %% Load and boundary conditions 55 | fixed = pp.Results.fixed; xfixed = pp.Results.xfixed; 56 | yfixed = pp.Results.yfixed; zfixed = pp.Results.zfixed; 57 | F1 = pp.Results.force1; F2 = pp.Results.force2; F3 = pp.Results.force3; F4 = pp.Results.force4; 58 | F5 = pp.Results.force5; F6 = pp.Results.force6; F7 = pp.Results.force7; F8 = pp.Results.force8; 59 | F9 = pp.Results.force9; F10 = pp.Results.force10; keep_domain = pp.Results.keepdom; 60 | Fmagx = pp.Results.Fmagx; Fmagy = pp.Results.Fmagy; Fmagz = pp.Results.Fmagz; loadtype = pp.Results.loadtype; 61 | keep_BC = pp.Results.keep_BC; keep_BCxyz = cellstr([string(pp.Results.keep_BCx) string(pp.Results.keep_BCy) string(pp.Results.keep_BCz)]); 62 | 63 | %% Post-processing parameters 64 | nm = pp.Results.modelName; 65 | symm = cellstr([string(pp.Results.Symmetry1) string(pp.Results.Symmetry2) string(pp.Results.Symmetry3)]); 66 | dir = cellstr([string(pp.Results.direction1) string(pp.Results.direction2) string(pp.Results.direction3)]); 67 | 68 | %% Check input 69 | if nargin == 4 70 | error('Please check that you included all required inputs in the right order and at least one support condition') 71 | elseif isempty(fixed) && isempty(xfixed) && isempty(yfixed) && isempty(zfixed) 72 | error('Please include at least one support condition') 73 | elseif all([Fmagx Fmagy Fmagz]) 74 | error('Please include at least one non-zero load definition') 75 | end 76 | 77 | %% Run topology optimization 78 | if strcmpi(opt_type,'SIMP') 79 | % SIMP density-based 80 | [nelx,nely,nelz,ngrid,~,xg,top,lss,fnx,fny,fnz,dx,dy,dz,S] = SIMP(MeshControl,domain,fixed,xfixed,yfixed,... 81 | zfixed,F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,keep_domain,keep_BC,keep_BCxyz,... 82 | volfrac,E0,v,penal,rmin,Fmagx,Fmagy,Fmagz,loadtype); 83 | elseif strcmpi(opt_type,'SEMDOT') 84 | % SEMDOT density-based 85 | [nelx,nely,nelz,ngrid,~,xg,top,lss,fnx,fny,fnz,dx,dy,dz,S] = SEMDOT(MeshControl,domain,fixed,xfixed,yfixed,... 86 | zfixed,F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,keep_domain,keep_BC,keep_BCxyz,... 87 | volfrac,E0,v,rmin,Fmagx,Fmagy,Fmagz,loadtype); 88 | end 89 | 90 | %% Post-processing 91 | if ~isempty(symm) 92 | [xg,top,fnx,fny,fnz,nelx,nely,nelz,dx,dy,dz] = symmetry(xg,lss,nelx,nely,nelz,dx,dy,dz,ngrid,symm{1},dir{1}); 93 | if length(symm) >= 2 94 | [xg,top,fnx,fny,fnz,nelx,nely,nelz,dx,dy,dz] = symmetry(xg,lss,nelx,nely,nelz,dx,dy,dz,ngrid,symm{2},dir{2}); 95 | if length(symm) == 3 96 | [xg,top,fnx,fny,fnz,~,~,~,dx,dy,dz] = symmetry(xg,lss,nelx,nely,nelz,dx,dy,dz,ngrid,symm{3},dir{3}); 97 | end 98 | end 99 | figure (1) 100 | display_3Dsmooth(xg,top); 101 | end 102 | if ~isempty(nm) 103 | stlgen(top,fnx,fny,fnz,dx,dy,dz,nm) 104 | end 105 | end 106 | % This Matlab code was written by Osezua Ibhadode % % 107 | % Please sent your comments to: ibhadode@ualberta.ca % 108 | % % 109 | % The code is intended for educational purposes and theoretical details % 110 | % are discussed in a paper which is currently under review % 111 | % % 112 | % Disclaimer: % 113 | % The author reserves all rights but do not guarantee that the code is % 114 | % free from errors. Furthermore, the author shall not be liable in any % 115 | % event caused by the use of the program. % 116 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 117 | -------------------------------------------------------------------------------- /HHs3D.m: -------------------------------------------------------------------------------- 1 | function [H,Hs]=HHs3D(nelx,nely,nelz,rmin,ele,nele) 2 | iH = ones(nele*(2*(ceil(rmin)-1)+1)^2,1); 3 | jH = ones(size(iH)); 4 | sH = zeros(size(iH)); 5 | k = 0; 6 | for k1 = 1:nelz 7 | for i1 = 1:nelx 8 | for j1 = 1:nely 9 | e1 = (k1-1)*nelx*nely + (i1-1)*nely+j1; 10 | for k2 = max(k1-(ceil(rmin)-1),1):min(k1+(ceil(rmin)-1),nelz) 11 | for i2 = max(i1-(ceil(rmin)-1),1):min(i1+(ceil(rmin)-1),nelx) 12 | for j2 = max(j1-(ceil(rmin)-1),1):min(j1+(ceil(rmin)-1),nely) 13 | e2 = (k2-1)*nelx*nely + (i2-1)*nely+j2; 14 | k = k+1; iH(k) = e1; jH(k) = e2; 15 | sH(k) = max(0,rmin-sqrt((i1-i2)^2+(j1-j2)^2+(k1-k2)^2)); 16 | end 17 | end 18 | end 19 | end 20 | end 21 | end 22 | H = sparse(iH,jH,sH); H = H(ele,ele); Hs = sum(H,2); -------------------------------------------------------------------------------- /HnHns3D.m: -------------------------------------------------------------------------------- 1 | function [Hn,Hns]=HnHns3D(nelx,nely,nelz,rnmin) 2 | inH = ones((nelx+1)*(nely+1)*(nelz+1)*(2*(ceil(rnmin)+1))^2,1); 3 | jnH = ones(size(inH)); snH = zeros(size(inH)); kn = 0; 4 | [elex,eley,elez] = meshgrid(1.5:nelx+0.5,1.5:nely+0.5,1.5:nelz+0.5); 5 | for kn1 = 1:nelz+1 6 | for in1 = 1:nelx+1 7 | for jn1 = 1:nely+1 8 | en1 = (kn1-1)*(nelx+1)*(nely+1)+(in1-1)*(nely+1)+jn1; 9 | for kn2 = max(kn1-(ceil(rnmin)),1):min(kn1+(ceil(rnmin)-1),nelz) 10 | for in2 = max(in1-(ceil(rnmin)),1):min(in1+(ceil(rnmin)-1),nelx) 11 | for jn2 = max(jn1-(ceil(rnmin)),1):min(jn1+(ceil(rnmin)-1),nely) 12 | en2 = (kn2-1)*nelx*nely + (in2-1)*nely+jn2; 13 | kn = kn+1; inH(kn) = en1; jnH(kn) = en2; 14 | snH(kn) = max(0,rnmin-sqrt((in1-elex(jn2,in2,kn2))^2+(jn1-eley(jn2,in2,kn2))^2+(kn1-elez(jn2,in2,kn2))^2)); 15 | end 16 | end 17 | end 18 | end 19 | end 20 | end 21 | Hn = sparse(inH,jnH,snH); Hns = sum(Hn,2); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 CADmaniac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeTO 2 | 3 | **FreeTO** stands for Freeform Topology Optimization (3D topology optimization through a structured mesh with smooth boundaries) 4 | and is an open-source Matlab code for initializing, optimizing, and post-processing free-form 3D topology optimization problems. 5 | 6 | 7 | 8 | ## Syntax 9 | * **FreeTO(*file*, *force1*, *MeshControl*, *volfrac*, 'PropertyName', VALUE,...)** Performs topology optimization on the STL domain provided in *file* with the load-carrying STL subdomain provided in *force1*, *MeshControl* to create a structured hexahedral mesh, *volfrac* for the required volume fraction, property names and values (support conditions and force magnitudes are essential), and other default parameters. The property name-value inputs are given in Table 1. 10 | 11 | ### Table 1: Name-value inputs 12 | 13 | -------------------------------------------------------------------------------------------------------------------- 14 | | Name-value | Description | Default value | 15 | |-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|---------------| 16 | | fixed | Mesh file (.stl) of the regions in domain with a Dirichlet displacement boundary condition fixed in all three directions | - | 17 | | xfixed, yfixed, zfixed | Type of topology optimization approach | - | 18 | | force2 – force10 | Mesh files (.stl) of regions in domain that carry up to 9 additional load cases | - | 19 | | keepdom | Mesh file (.stl) of regions in domain that should remain unchanged throughout the optimization | - | 20 | | keep_BC | Specify if the load and support (fixed in all directions) regions should be included in the optimized structure | Yes | 21 | | keep_BCx, keep_BCy, keep_BCz | Specify if the support regions fixed in x, y, and z directions, respectively, should be included in the optimized structure | No | 22 | | YoungsModulus | Specify the Young’s Modulus of the material | 23 | | PoissonsRatio | Specifies the Poisson’s ratio of the material | 0.3 | 24 | | optimization | Specify the optimization method (SIMP or SEMDOT) | SIMP | 25 | | Fmagx, Fmagy, Fmagz | Indicates the x, y, and z components of the loads | 0, 0, 0 | 26 | | modelName | Indicates the name of the mesh file (.stl) generated from the optimized structure | - | 27 | | loadtype | Indicates if a load should be applied at a point (center node) or distributed across all nodes in a load case | distributed | 28 | | Symmetry1, Symmetry2, Symmetry3 | Indicates a symmetry plane to mirror the optimized topology. Symmetry can be indicated a maximum of 3 times. | - | 29 | | direction1, direction2, direction3 | Indicates whether the mirror should occur to the left or right of the symmetry plane. It can be invoked the same number of times as Symmetry | Right | 30 | 31 | ### Table 2: Outputs 32 | | Variable | Description | 33 | |----------|---------------------------------------------------------| 34 | | comp | Compliance of optimized topology | 35 | | finalvol | Volume fraction of optimized topology | 36 | | eleden | Element density array | 37 | | gridden | Grid density array | 38 | | elenum1 | Number of active elements in the optimization | 39 | | elenum2 | Total number of elements (active + passive) | 40 | | | Display of the optimized topology | 41 | | | Display of the compliance and volume fraction histories | 42 | | | STL file (optional) | 43 | 44 | ## Examples: 45 | 46 | The following examples show how **FreeTO** is used. 47 | Input STL files of the sample design problems have been prepared and are in the STLs folder in this repository. The input file preparation is shown in a video for Example 1 [here](https://1drv.ms/v/s!ArlEZ0UGGKnchvZ0yWGqPRQcVgRg1Q?e=mK8vHb). 48 | 49 | * Example 1: 50 | Optimize a GE bracket given the freebody diagram on the left of Figure 1 51 | 52 | - **FreeTO('STLs\GE_domain.stl','STLs\GE_force.stl',80,0.3,'fixed','STLs\GE_fixed.stl','force2','STLs\GE_force.stl','Fmagy',[0 -2000],'Fmagz',[1500 0],'YoungsModulus',210e9)** 53 | 54 | 55 | 56 | * Example 2: 57 | Optimize one half of an airplane bracket 58 | 59 | - **FreeTO('STLs\air_domain.stl','STLs\air_force.stl',90,0.2,'fixed','STLs\air_fixed.stl','zfixed','STLs\air_zfixed.stl','force2','STLs\air_force.stl','force3','STLs\air_force.stl','Fmagx',[1000 1324 0],'Fmagy',[0 -1324 -2500],'YoungsModulus',210e9,'Symmetry1',"x-y",'direction1',"right",'optimization','SEMDOT','modelName','STLs\air_brack_TO.stl')** 60 | 61 | 62 | 63 | * Example 3: 64 | Optimize a human hand model with loads on the fingertips 65 | 66 | - **FreeTO('STLs\hand_domain.stl','STLs\hand_force1.stl',90,0.3,'fixed','STLs\hand_fixed.stl','force2','STLs\hand_force2.stl','force3','STLs\hand_force3.stl','force4','STLs\hand_force4.stl','force5','STLs\hand_force5.stl','Fmagz',2e3*ones(1,5),'YoungsModulus',210e9)** 67 | 68 | 69 | 70 | * Example 4: 71 | Optimize the quarter of a quadcopter 72 | 73 | - **FreeTO('STLs\quad_domain.stl','STLs\quad_force1.stl',70,0.3,'fixed','STLs\quad_fixed.stl','xfixed','STLs\quad_xfixed.stl','yfixed','STLs\quad_yfixed.stl','force2','STLs\quad_force2.stl','force3','STLs\quad_force3.stl','Fmagz',[-1500 -1500 -1000],'YoungsModulus',2e9,'Symmetry1','y-z','Symmetry2','z-x','direction1','right','direction2','left','modelName','STLs\quad_TO.stl','keep_BCz','yes')** 74 | 75 | 76 | 77 | 78 | ## Supporting Open-Source Codes 79 | **FreeTO** utilizes other open-source codes such as [intriangulation.m](https://www.mathworks.com/matlabcentral/fileexchange/43381-intriangulation-vertices-faces-testp-heavytest) by Johannes Korsawe, and modifications to [top3D.m](https://www.top3d.app/) by Liu and Tovar, [SEMDOT.m](https://blogs.deakin.edu.au/dot/software/) by Fu et al., and [phi2stl.m](https://asmedigitalcollection.asme.org/computingengineering/article/17/4/041012/474348/An-Open-Source-Framework-for-Integrated-Additive) by Vogiatzis et al. FreeTO on this repository uses the optimality criteria method (OCM) but works much better with the Method of Moving Asymptotes (MMA) as optimizer. The implementation of MMA is given in SIMP.m and SEMDOT.m but the lines are commented out as the codes are not on this repository and the user should request them from [Prof. Krista Svanberg](https://people.kth.se/~krille/Welcome.html) by email. If using MMA, comment out lines 79 to 84, 135, 137 and uncomment lines 44 to 56, 86 to 99 in SEMDOT.m and SIMP.m. Furthermore, in SEMDOT.m and SIMP.m, tolx = tol_thresh = 1e-3, beta = ER = 0.5 are recommended values for MMA. 80 | 81 | ## To Cite 82 | If you find this code helpful in your work, please cite [this open access paper](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4876754) 83 | -------------------------------------------------------------------------------- /SEMDOT.m: -------------------------------------------------------------------------------- 1 | function [nelx,nely,nelz,ngrid,stp1,xg,top,lss,fnx,fny,fnz,del_x,del_y,del_z,S] = SEMDOT(MeshControl,domain,fixed,fixed_x,fixed_y,fixed_z,force_1,... 2 | force_2,force_3,force_4,force_5,force_6,force_7,force_8,force_9,force_10,keep_domain,... 3 | keep_BC,keep_BCxyz,vol,E00,nu,rmin,Fmagx,Fmagy,Fmagz,loadtype) 4 | %% Arbitrary Domains 5 | [~,oute,outeM,sup_all,sup_x,sup_y,sup_z,Fn,nelx,nely,nelz,nele,ndof,stp1,aa,del_x,del_y,del_z] = geomeshini(MeshControl,domain,fixed,fixed_x,... 6 | fixed_y,fixed_z,force_1,force_2,force_3,force_4,force_5,force_6,force_7,force_8,force_9,force_10,keep_domain,keep_BC,keep_BCxyz); 7 | % node and DOF connectivity matrix 8 | [~,~,~,~,edofMatn,nnele,ele,n_vec,~,MusD]... 9 | = domainprep(oute,outeM,vol,nelx,nely,nelz,nele); 10 | % force nodes 11 | [F,nf] = forcevec(Fn,ndof,Fmagx,Fmagy,Fmagz,loadtype); 12 | % fixed DOFs 13 | fixeddof = supportDOFs(sup_all,sup_x,sup_y,sup_z); 14 | %% USER-DEFINED LOOP PARAMETERS 15 | maxloop = 500; % Maximum number of iterations 16 | tolx = 0.003; % Terminarion criterion 17 | %% USER-DEFINED MATERIAL PROPERTIES 18 | E0 = 1*E00; % Young's modulus of solid material 19 | Emin = 0.001; % Young's modulus of void-like material 20 | tol = 1; tol_thresh = 3e-3; 21 | %% USER-DEFINED GRID POINTS 22 | ngrid=4; rnmin=1; 23 | %% INITIALIZE HEAVISIDE REGULARIZATION PARAMETER 24 | beta=0.1; ER=0.05; 25 | %% ELEMENTAL NODES AND COORDINATES 26 | [nodex,nodey,nodez] = meshgrid(0:nelx,0:nely,0:nelz); 27 | [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx,0:1/ngrid:nely,0:1/ngrid:nelz); 28 | %% USER-DEFINED LOAD DOFs 29 | %% Load domain 30 | U = zeros(ndof,nf); 31 | freedofs = setdiff(n_vec,fixeddof); % make a change here 32 | KE = (aa/0.5)*lk_H8(nu); 33 | iK = reshape(kron(edofMatn,ones(24,1))',24*24*nnele,1); 34 | jK = reshape(kron(edofMatn,ones(1,24))',24*24*nnele,1); 35 | %% PREPARE FILTER 36 | [H,Hs]=HHs3D(nelx,nely,nelz,rmin,ele,nele); 37 | %% PREPARE FILTER FOR NODELS 38 | [Hn,Hns]=HnHns3D(nelx,nely,nelz,rnmin); 39 | %% INITIALIZE ITERATION 40 | vx = repmat(vol,[nely,nelx,nelz]); vx = vx(:); vx = vx(ele); % make a change here 41 | vxPhys = vx; 42 | loop = 0; 43 | change = 1; 44 | % %% INITIALIZE MMA OPTIMIZER 45 | % m = 1; % The number of general constraints. 46 | % n = nnele; % The number of design variables x_j. % make a change here 47 | % xmin = zeros(n,1); % Column vector with the lower bounds for the variables x_j. 48 | % xmax = ones(n,1); % Column vector with the upper bounds for the variables x_j. 49 | % xold1 = vx(:); % xval, one iteration ago (provided that iter>1). 50 | % xold2 = vx(:); % xval, two iterations ago (provided that iter>2). 51 | % low = ones(n,1); % Column vector with the lower asymptotes from the previous iteration (provided that iter>1). 52 | % upp = ones(n,1); % Column vector with the upper asymptotes from the previous iteration (provided that iter>1). 53 | % a0 = 1; % The constants a_0 in the term a_0*z. 54 | % a = zeros(m,1); % Column vector with the constants a_i in the terms a_i*z. 55 | % c_MMA = 10000*ones(m,1); % Column vector with the constants c_i in the terms c_i*y_i. 56 | % d = zeros(m,1); % Column vector with the constants d_i in the terms 0.5*d_i*(y_i)^2. 57 | %% START ITERATION 58 | while (change > tolx && tol>tol_thresh) && loop < maxloop 59 | %% UPDATE ITERATION 60 | loop = loop+1; 61 | %% FE-ANALYSIS 62 | sK = KE(:)*(vxPhys(:)'*E0+(1-vxPhys(:))'*(Emin*E0)); % make a change here 63 | K = sparse(iK(:),jK(:),sK(:)); K = (K+K')/2; 64 | U(freedofs,:)=K(freedofs, freedofs)\F(freedofs,:); 65 | %% OBJECTIVE FUNCTION AND SENSITIVITY ANALYSIS 66 | ts_SA = tic; 67 | c = 0; dc = 0; 68 | for i = 1:size(F,2) 69 | Ui = U(:,i); 70 | ce = sum((Ui(edofMatn)*KE).*Ui(edofMatn),2); % make a change here 71 | c= c+sum((vxPhys.*E0+(1-vxPhys).*(Emin*E0)).*ce); % make a change here 72 | dc = dc-((1-vxPhys)*Emin+vxPhys).*E0.*ce; 73 | end 74 | dv = ones(nnele,1); cc(loop) = c; % make a change here 75 | %% FILTERING AND MODIFICATION OF SENSITIVITIES 76 | dc = H*(dc./Hs); % make a change here 77 | dv = H*(dv./Hs); % make a change here 78 | %% OPTIMALITY CRITERIA UPDATE 79 | l1 = 0; l2 = 1e9; move = 0.1; 80 | while (l2-l1)/(l1+l2) > 1e-3 81 | lmid = 0.5*(l2+l1); 82 | vxnew = max(0,max(vxPhys-move,min(1,min(vxPhys+move,vxPhys.*sqrt(-dc./dv/lmid))))); 83 | if sum(vxnew) > vol*nnele, l1 = lmid; else l2 = lmid; end 84 | end 85 | vxPhys = (H*vxnew)./Hs; 86 | % %% METHOD OF MOVING ASYMPTOTES 87 | % xval = vx; % make a change here 88 | % f0val = c; 89 | % df0dx = dc; % make a change here 90 | % fval = sum(vxPhys)/(vol*nnele) - 1; % make a change here 91 | % dfdx = dv' / (vol*nnele); % make a change here 92 | % [vxmma, ~, ~, ~, ~, ~, ~, ~, ~, low,upp] = ... 93 | % mmasub(m, n, loop, xval, xmin, xmax, xold1, xold2, ... 94 | % f0val,df0dx,fval,dfdx,low,upp,a0,a,c_MMA,d); 95 | % %% Update MMA Variables 96 | % vxnew = vxmma; % make a change here 97 | % vxPhys = (H*vxnew)./Hs; % make a change here 98 | % xold2 = xold1(:); 99 | % xold1 = vx(:); 100 | %% Change to total number of elements 101 | vxPhys1 = zeros(nely,nelx,nelz); vxPhys2 = vxPhys1(:); % make a change here 102 | vxPhys2(ele) = vxPhys; vxPhys = vxPhys2; vxPhys(MusD) = 1; % make a change here 103 | %% Apply smooth edge algorithm 104 | [vxPhys,xg,lss,top,tol] = smoothedge3D(vxPhys,Hn,Hns,nelx,nely,nelz,nele,nnele,nodex,nodey,nodez,fnx,fny,fnz,beta,ngrid); 105 | %% CHECK CONVERGENCE 106 | change = sum(abs(vxnew(:)-vx(:)))/(vol*nnele); % make a change here 107 | vx=vxnew; 108 | if (change <= tolx || tol<=tol_thresh) || loop >= maxloop 109 | vxPhys(MusD) = 1; 110 | [vxPhys,xg,lss,top,tol] = smoothedge3D(vxPhys,Hn,Hns,nelx,nely,nelz,nele,nnele,nodex,nodey,nodez,fnx,fny,fnz,beta,ngrid); 111 | end 112 | %% PRINT RESULTS 113 | fvol(loop) = sum(vxPhys(:))/(nnele); 114 | fprintf('It.:%5i Obj.:%11.3f Vol.:%7.3f ch.:%7.5f Topo.:%7.5f\n'... 115 | ,loop,cc(loop),fvol(loop),change,tol); % make a change here 116 | figure(1) 117 | clf; 118 | display_3Dsmooth(flip(xg,3),flip(top,3)); 119 | hold on; 120 | figure(2) 121 | clf; 122 | yyaxis left 123 | plot(cc,'b-','LineWidth',1.8); % make a change here 124 | set(gca,'FontName','Times New Roman','Box','On','LineWidth',2,'FontSize',15,'defaultAxesColorOrder',[0 0 1; 1 0 0]); 125 | %plot(loop,cc(loop),'b*','LineWidth',2,'MarkerSize',8) % make a change here 126 | xlabel('Iteration','Interpreter','Latex') 127 | ylabel('Compliance (Nm)', 'rotation', 90,'Interpreter','Latex') 128 | ax = gca; ax.YColor = 'b'; 129 | 130 | yyaxis right 131 | plot(fvol,'r-','LineWidth',1.8); 132 | ylabel('Volume fraction', 'rotation', 270,'Interpreter','Latex'); ax = gca; ax.YColor = 'r'; 133 | pause(1e-6); hold off 134 | %% UPDATE% HEAVISIDE REGULARIZATION PARAMETER 135 | if beta < 2 136 | beta=beta+ER; 137 | end 138 | fprintf('Parameter beta increased to %g.\n',beta); 139 | vxPhys_full = reshape(vxPhys,nely,nelx,nelz); 140 | vxPhys = vxPhys(ele); 141 | end 142 | cc_end = cc(end); S = struct('comp',cc_end,'finalvol',fvol(end),'eleden',vxPhys_full,'gridden',xg,'elenum1',nnele,'elenum2',nele); -------------------------------------------------------------------------------- /SIMP.m: -------------------------------------------------------------------------------- 1 | function [nelx,nely,nelz,ngrid,stp1,xg,top,lss,fnx,fny,fnz,del_x,del_y,del_z,S] = SIMP(MeshControl,domain,fixed,fixed_x,fixed_y,fixed_z,force_1,... 2 | force_2,force_3,force_4,force_5,force_6,force_7,force_8,force_9,force_10,keep_domain,... 3 | keep_BC,keep_BCxyz,vol,E00,nu,penal,rmin,Fmagx,Fmagy,Fmagz,loadtype) 4 | %% Arbitrary Domains 5 | [~,oute,outeM,sup_all,sup_x,sup_y,sup_z,Fn,nelx,nely,nelz,nele,ndof,stp1,aa,del_x,del_y,del_z] = geomeshini(MeshControl,domain,fixed,fixed_x,... 6 | fixed_y,fixed_z,force_1,force_2,force_3,force_4,force_5,force_6,force_7,force_8,force_9,force_10,keep_domain,keep_BC,keep_BCxyz); 7 | % node and DOF connectivity matrix 8 | [~,~,~,~,edofMatn,nnele,ele,alldofs,~,MusD]... 9 | = domainprep(oute,outeM,vol,nelx,nely,nelz,nele); 10 | % force nodes 11 | [F,nf] = forcevec(Fn,ndof,Fmagx,Fmagy,Fmagz,loadtype); 12 | % fixed DOFs 13 | fixeddof = supportDOFs(sup_all,sup_x,sup_y,sup_z); 14 | %% USER-DEFINED LOOP PARAMETERS 15 | maxloop = 500; % Maximum number of iterations 16 | tolx = 0.003; % Terminarion criterion 17 | %% USER-DEFINED MATERIAL PROPERTIES 18 | E0 = 1*E00; % Young's modulus of solid material 19 | Emin = 0.001; % Young's modulus of void-like material 20 | tol = 1; tol_thresh = 3e-3; 21 | %% USER-DEFINED GRID POINTS 22 | ngrid=4; rnmin=1; 23 | %% INITIALIZE HEAVISIDE REGULARIZATION PARAMETER 24 | beta=0.1; ER=0.05; 25 | %% ELEMENTAL NODES AND COORDINATES 26 | [nodex,nodey,nodez] = meshgrid(0:nelx,0:nely,0:nelz); 27 | [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx,0:1/ngrid:nely,0:1/ngrid:nelz); 28 | %% USER-DEFINED LOAD DOFs 29 | %% Load domain 30 | U = zeros(ndof,nf); 31 | freedofs = setdiff(alldofs,fixeddof); % make a change here 32 | KE = (aa/0.5)*lk_H8(nu); 33 | iK = reshape(kron(edofMatn,ones(24,1))',24*24*nnele,1); 34 | jK = reshape(kron(edofMatn,ones(1,24))',24*24*nnele,1); 35 | %% PREPARE FILTER 36 | [H,Hs]=HHs3D(nelx,nely,nelz,rmin,ele,nele); 37 | %% PREPARE FILTER FOR NODELS 38 | [Hn,Hns]=HnHns3D(nelx,nely,nelz,rnmin); 39 | %% INITIALIZE ITERATION 40 | vx = repmat(vol,[nely,nelx,nelz]); vx = vx(:); vx = vx(ele); % make a change here 41 | vxPhys = vx; 42 | loop = 0; 43 | change = 1; 44 | % %% INITIALIZE MMA OPTIMIZER 45 | % m = 1; % The number of general constraints. 46 | % n = nnele; % The number of design variables x_j. % make a change here 47 | % xmin = zeros(n,1); % Column vector with the lower bounds for the variables x_j. 48 | % xmax = ones(n,1); % Column vector with the upper bounds for the variables x_j. 49 | % xold1 = vx(:); % xval, one iteration ago (provided that iter>1). 50 | % xold2 = vx(:); % xval, two iterations ago (provided that iter>2). 51 | % low = ones(n,1); % Column vector with the lower asymptotes from the previous iteration (provided that iter>1). 52 | % upp = ones(n,1); % Column vector with the upper asymptotes from the previous iteration (provided that iter>1). 53 | % a0 = 1; % The constants a_0 in the term a_0*z. 54 | % a = zeros(m,1); % Column vector with the constants a_i in the terms a_i*z. 55 | % c_MMA = 10000*ones(m,1); % Column vector with the constants c_i in the terms c_i*y_i. 56 | % d = zeros(m,1); % Column vector with the constants d_i in the terms 0.5*d_i*(y_i)^2. 57 | %% START ITERATION 58 | while (change > tolx && tol>tol_thresh) && loop < maxloop 59 | %% UPDATE ITERATION 60 | loop = loop+1; 61 | %% FE-ANALYSIS 62 | Ee = Emin+vxPhys(:)'.^penal.*(E0-Emin); 63 | sK = KE(:)*Ee; % make a change here 64 | K = sparse(iK(:),jK(:),sK(:)); K = (K+K')/2; 65 | U(freedofs,:)=K(freedofs, freedofs)\F(freedofs,:); 66 | %% OBJECTIVE FUNCTION AND SENSITIVITY ANALYSIS 67 | c = 0; dc = 0; 68 | for i = 1:size(F,2) 69 | Ui = U(:,i); 70 | ce = sum((Ui(edofMatn)*KE).*Ui(edofMatn),2); % make a change here 71 | c= c+sum(Ee'.*ce); % make a change here 72 | dc = dc-penal.*vxPhys(:).^(penal-1).*(E0-Emin).*ce; 73 | end 74 | dv = ones(nnele,1); cc(loop) = c; % make a change here 75 | %% FILTERING AND MODIFICATION OF SENSITIVITIES 76 | dc = H*(dc./Hs); % make a change here 77 | dv = H*(dv./Hs); % make a change here 78 | %% OPTIMALITY CRITERIA UPDATE 79 | l1 = 0; l2 = 1e9; move = 0.1; 80 | while (l2-l1)/(l1+l2) > 1e-3 81 | lmid = 0.5*(l2+l1); 82 | vxnew = max(0,max(vxPhys-move,min(1,min(vxPhys+move,vxPhys.*sqrt(-dc./dv/lmid))))); 83 | if sum(vxnew) > vol*nnele, l1 = lmid; else l2 = lmid; end 84 | end 85 | vxPhys = (H*vxnew)./Hs; 86 | % %% METHOD OF MOVING ASYMPTOTES 87 | % xval = vx; % make a change here 88 | % f0val = c; 89 | % df0dx = dc; % make a change here 90 | % fval = sum(vxPhys)/(vol*nnele) - 1; % make a change here 91 | % dfdx = dv' / (vol*nnele); % make a change here 92 | % [vxmma, ~, ~, ~, ~, ~, ~, ~, ~, low,upp] = ... 93 | % mmasub(m, n, loop, xval, xmin, xmax, xold1, xold2, ... 94 | % f0val,df0dx,fval,dfdx,low,upp,a0,a,c_MMA,d); 95 | % %% Update MMA Variables 96 | % vxnew = vxmma; % make a change here 97 | % vxPhys = (H*vxnew)./Hs; % make a change here 98 | % xold2 = xold1(:); 99 | % xold1 = vx(:); 100 | %% Change to total number of elements 101 | vxPhys1 = zeros(nely,nelx,nelz); vxPhys2 = vxPhys1(:); % make a change here 102 | vxPhys2(ele) = vxPhys; vxPhys = vxPhys2; vxPhys(MusD) = 1; % make a change here 103 | %% Apply smooth edge algorithm 104 | [vxPhys,xg,lss,top,tol] = smoothedge3D(vxPhys,Hn,Hns,nelx,nely,nelz,nele,nnele,nodex,nodey,nodez,fnx,fny,fnz,beta,ngrid); 105 | %% CHECK CONVERGENCE 106 | change = sum(abs(vxnew(:)-vx(:)))/(vol*nnele); % make a change here 107 | vx=vxnew; 108 | if (change <= tolx || tol<=tol_thresh) || loop >= maxloop 109 | vxPhys(MusD) = 1; 110 | [vxPhys,xg,lss,top,tol] = smoothedge3D(vxPhys,Hn,Hns,nelx,nely,nelz,nele,nnele,nodex,nodey,nodez,fnx,fny,fnz,beta,ngrid); 111 | end 112 | %% PRINT RESULTS 113 | fvol(loop) = sum(vxPhys(:))/(nnele); 114 | fprintf('It.:%5i Obj.:%11.3f Vol.:%7.3f ch.:%7.5f Topo.:%7.5f\n'... 115 | ,loop,cc(loop),fvol(loop),change,tol); % make a change here 116 | figure(1); 117 | clf; 118 | display_3Dsmooth(flip(xg,3),flip(top,3)); 119 | hold on; 120 | figure(2) 121 | clf; 122 | yyaxis left 123 | plot(cc,'b-','LineWidth',1.8); % make a change here 124 | set(gca,'FontName','Times New Roman','Box','On','LineWidth',2,'FontSize',15,'defaultAxesColorOrder',[0 0 1; 1 0 0]); 125 | %plot(loop,cc(loop),'b*','LineWidth',2,'MarkerSize',8) % make a change here 126 | xlabel('Iteration','Interpreter','Latex') 127 | ylabel('Compliance (Nm)', 'rotation', 90,'Interpreter','Latex') 128 | ax = gca; ax.YColor = 'b'; 129 | 130 | yyaxis right 131 | plot(fvol,'r-','LineWidth',1.8); 132 | ylabel('Volume fraction', 'rotation', 270,'Interpreter','Latex'); ax = gca; ax.YColor = 'r'; 133 | pause(1e-6); hold off 134 | %% UPDATE% HEAVISIDE REGULARIZATION PARAMETER 135 | if beta < 2 136 | beta=beta+ER; 137 | end 138 | fprintf('Parameter beta increased to %g.\n',beta); 139 | vxPhys_full = reshape(vxPhys,nely,nelx,nelz); 140 | vxPhys = vxPhys(ele); 141 | end 142 | cc_end = cc(end); S = struct('comp',cc_end,'finalvol',fvol(end),'eleden',vxPhys_full,'gridden',xg,'elenum1',nnele,'elenum2',nele); -------------------------------------------------------------------------------- /STLs/Fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/Fig1.png -------------------------------------------------------------------------------- /STLs/Fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/Fig2.png -------------------------------------------------------------------------------- /STLs/Fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/Fig3.png -------------------------------------------------------------------------------- /STLs/Fig4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/Fig4.jpg -------------------------------------------------------------------------------- /STLs/GE_domain.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/GE_domain.STL -------------------------------------------------------------------------------- /STLs/GE_fixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/GE_fixed.STL -------------------------------------------------------------------------------- /STLs/GE_force.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/GE_force.STL -------------------------------------------------------------------------------- /STLs/L_domain.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/L_domain.STL -------------------------------------------------------------------------------- /STLs/L_fixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/L_fixed.STL -------------------------------------------------------------------------------- /STLs/L_force.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/L_force.STL -------------------------------------------------------------------------------- /STLs/air_brack_TO.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/air_brack_TO.stl -------------------------------------------------------------------------------- /STLs/air_domain.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/air_domain.STL -------------------------------------------------------------------------------- /STLs/air_fixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/air_fixed.STL -------------------------------------------------------------------------------- /STLs/air_force.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/air_force.STL -------------------------------------------------------------------------------- /STLs/air_zfixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/air_zfixed.STL -------------------------------------------------------------------------------- /STLs/hand_TO.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_TO.gif -------------------------------------------------------------------------------- /STLs/hand_domain.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_domain.stl -------------------------------------------------------------------------------- /STLs/hand_fixed.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_fixed.stl -------------------------------------------------------------------------------- /STLs/hand_force1.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_force1.stl -------------------------------------------------------------------------------- /STLs/hand_force2.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_force2.stl -------------------------------------------------------------------------------- /STLs/hand_force3.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_force3.stl -------------------------------------------------------------------------------- /STLs/hand_force4.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_force4.stl -------------------------------------------------------------------------------- /STLs/hand_force5.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/hand_force5.stl -------------------------------------------------------------------------------- /STLs/lever_domain.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/lever_domain.STL -------------------------------------------------------------------------------- /STLs/lever_fixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/lever_fixed.STL -------------------------------------------------------------------------------- /STLs/lever_force1.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/lever_force1.STL -------------------------------------------------------------------------------- /STLs/lever_force2.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/lever_force2.STL -------------------------------------------------------------------------------- /STLs/lever_zfixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/lever_zfixed.STL -------------------------------------------------------------------------------- /STLs/quad_TO.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_TO.stl -------------------------------------------------------------------------------- /STLs/quad_domain.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_domain.STL -------------------------------------------------------------------------------- /STLs/quad_fixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_fixed.STL -------------------------------------------------------------------------------- /STLs/quad_force1.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_force1.STL -------------------------------------------------------------------------------- /STLs/quad_force2.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_force2.STL -------------------------------------------------------------------------------- /STLs/quad_force3.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_force3.STL -------------------------------------------------------------------------------- /STLs/quad_xfixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_xfixed.STL -------------------------------------------------------------------------------- /STLs/quad_yfixed.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ooibhadode/FreeTO/96862eba84bbf4ddc04f6099ebfb548866d42c77/STLs/quad_yfixed.STL -------------------------------------------------------------------------------- /display_3Dsmooth.m: -------------------------------------------------------------------------------- 1 | function display_3Dsmooth(xg,top) 2 | [nely,nelx,nelz] = size(xg); 3 | cla, hold on, view(30,30), rotate3d on, axis equal, axis([0 nelx 0 nely 0 nelz]), box 4 | set(gca,'YDir','reverse','ZDir','reverse','ZtickLabel',flipud(get(gca,'Ztick')')); 5 | [X,Y,Z] = meshgrid(1:nelx,1:nely,1:nelz); 6 | fcolor=[0 1 1]; 7 | patch(isocaps(X,Y,Z,top,0),'FaceColor',fcolor,'EdgeColor','none'); 8 | p = patch(isosurface(X,Y,Z,top,0),'FaceColor',fcolor,'EdgeColor','none'); 9 | camlight('headlight') 10 | axis off; 11 | box off; 12 | set(gcf, 'color', [1 1 1]) %Background 13 | drawnow 14 | end -------------------------------------------------------------------------------- /domainprep.m: -------------------------------------------------------------------------------- 1 | function [edofMat1,edofMat,ndn,nd,edofMatn,nnele,ele,n_vec,vol1,MusD]... 2 | = domainprep(oute,outeM,vol,nelx,nely,nelz,nele) 3 | 4 | %% Node (edofMat1) and element (edofMat) connectivity matrices 5 | nodenr = reshape(1:(1+nelx)*(1+nely)*(1+nelz),(1+nely),(1+nelx),(1+nelz)); % node numbers in domain 6 | nodenrs1 = nodenr-1; nodenrs1(end,:,:) = []; nodenrs1(:,end,:) = []; nodenrs1(:,:,end) = []; 7 | edofMat1 = nodenrs1(:) + [2 2+(nely+1) 2+nely 1 (nelx+1)*(nely+1)+2 (nelx+1)*(nely+1)+2+(nely+1) ... 8 | (nelx+1)*(nely+1)+2+nely (nelx+1)*(nely+1)+1]; % connectivity matrix for node numbers 9 | nodenrs = 3*(nodenr-1); nodenrs(end,:,:) = []; nodenrs(:,end,:) = []; nodenrs(:,:,end) = []; 10 | % element connectivity matrix 11 | edofMat = nodenrs(:) + [4 5 6 4+3*(nely+1) 5+3*(nely+1) 6+3*(nely+1) 1+3*(nely+1) 2+3*(nely+1) 3+3*(nely+1) ... 12 | 1 2 3 4+3*(nelx+1)*(nely+1) 5+3*(nelx+1)*(nely+1) 6+3*(nelx+1)*(nely+1) 4+3*(nelx+1)*(nely+1)+3*(nely+1)... 13 | 5+3*(nelx+1)*(nely+1)+3*(nely+1) 6+3*(nelx+1)*(nely+1)+3*(nely+1) 1+3*(nelx+1)*(nely+1)+3*(nely+1)... 14 | 2+3*(nelx+1)*(nely+1)+3*(nely+1) 3+3*(nelx+1)*(nely+1)+3*(nely+1) 1+3*(nelx+1)*(nely+1)... 15 | 2+3*(nelx+1)*(nely+1) 3+3*(nelx+1)*(nely+1)]; 16 | 17 | %% List of important parameters needed for optimization 18 | ndn = find(oute(:) == 0); % vector of passive domain elements 19 | nd = length(ndn); % number of passive domain elements 20 | edofMatn = edofMat; edofMatn(ndn,:) = []; % connectivity matrix of active domain elements only 21 | nnele = length(edofMatn(:,1)); % number of active domain elements 22 | ele = setdiff(1:nele,ndn); % active domain element numbers from total domain element numbers 23 | n_vec = unique(edofMatn(:)); % degrees of freedom for active domain elements only 24 | vol1 = vol*(nnele/nele); % volume fraction based on active domains only 25 | outeM = outeM(:); MusD = find(outeM == 1); 26 | -------------------------------------------------------------------------------- /domainstokeep.m: -------------------------------------------------------------------------------- 1 | function outeM = domainstokeep(oute,nelx,nely,nelz,keep_BC,keep_BCxyz,stp2,stp3,stp4,stp5,... 2 | stp6_1,stp7,stf2,stf3,stf4,stf5,stf6_1,stf7,nnf1,nnf2,x1_cen,y1_cen,z1_cen) 3 | %% Collect the vertices and faces of the boundary condition regions to keep during optimization 4 | if strcmp(keep_BC,'yes') 5 | % keep_BCxyz{1} = xfixed, keep_BCxyz{2} = yfixed, keep_BCxyz{3} = zfixed 6 | if strcmp(keep_BCxyz{1},'no') && strcmp(keep_BCxyz{2},'no') && strcmp(keep_BCxyz{3},'no') 7 | stpM = [stp2;stp6_1;stp7]; stfM = [stf2;stf6_1;stf7]; 8 | nn1 = [size(stp2,1) nnf1 size(stp7,1)]; 9 | nn2 = [size(stf2,1) nnf2 size(stf7,1)]; 10 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 11 | elseif strcmp(keep_BCxyz{1},'yes') && strcmp(keep_BCxyz{2},'no') && strcmp(keep_BCxyz{3},'no') 12 | stpM = [stp2;stp3;stp6_1;stp7]; stfM = [stf2;stf3;stf6_1;stf7]; 13 | nn1 = [size(stp2,1) size(stp3,1) nnf1 size(stp7,1)]; 14 | nn2 = [size(stf2,1) size(stf3,1) nnf2 size(stf7,1)]; 15 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 16 | elseif strcmp(keep_BCxyz{1},'no') && strcmp(keep_BCxyz{2},'yes') && strcmp(keep_BCxyz{3},'no') 17 | stpM = [stp2;stp4;stp6_1;stp7]; stfM = [stf2;stf4;stf6_1;stf7]; 18 | nn1 = [size(stp2,1) size(stp4,1) nnf1 size(stp7,1)]; 19 | nn2 = [size(stf2,1) size(stf4,1) nnf2 size(stf7,1)]; 20 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 21 | elseif strcmp(keep_BCxyz{1},'no') && strcmp(keep_BCxyz{2},'no') && strcmp(keep_BCxyz{3},'yes') 22 | stpM = [stp2;stp5;stp6_1;stp7]; stfM = [stf2;stf5;stf6_1;stf7]; 23 | nn1 = [size(stp2,1) size(stp5,1) nnf1 size(stp7,1)]; 24 | nn2 = [size(stf2,1) size(stf5,1) nnf2 size(stf7,1)]; 25 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 26 | elseif strcmp(keep_BCxyz{1},'yes') && strcmp(keep_BCxyz{2},'yes') && strcmp(keep_BCxyz{3},'no') 27 | stpM = [stp2;stp3;stp4;stp6_1;stp7]; stfM = [stf2;stf3;stf4;stf6_1;stf7]; 28 | nn1 = [size(stp2,1) size(stp3,1) size(stp4,1) nnf1 size(stp7,1)]; 29 | nn2 = [size(stf2,1) size(stf3,1) size(stf4,1) nnf2 size(stf7,1)]; 30 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 31 | elseif strcmp(keep_BCxyz{1},'yes') && strcmp(keep_BCxyz{2},'no') && strcmp(keep_BCxyz{3},'yes') 32 | stpM = [stp2;stp3;stp5;stp6_1;stp7]; stfM = [stf2;stf3;stf5;stf6_1;stf7]; 33 | nn1 = [size(stp2,1) size(stp3,1) size(stp5,1) nnf1 size(stp7,1)]; 34 | nn2 = [size(stf2,1) size(stf3,1) size(stf5,1) nnf2 size(stf7,1)]; 35 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 36 | elseif strcmp(keep_BCxyz{1},'no') && strcmp(keep_BCxyz{2},'yes') && strcmp(keep_BCxyz{3},'yes') 37 | stpM = [stp2;stp4;stp5;stp6_1;stp7]; stfM = [stf2;stf4;stf5;stf6_1;stf7]; 38 | nn1 = [size(stp2,1) size(stp4,1) size(stp5,1) nnf1 size(stp7,1)]; 39 | nn2 = [size(stf2,1) size(stf4,1) size(stf5,1) nnf2 size(stf7,1)]; 40 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 41 | elseif strcmp(keep_BCxyz{1},'yes') && strcmp(keep_BCxyz{2},'yes') && strcmp(keep_BCxyz{3},'yes') 42 | stpM = [stp2;stp3;stp4;stp5;stp6_1;stp7]; stfM = [stf2;stf3;stf4;stf5;stf6_1;stf7]; 43 | nn1 = [size(stp2,1) size(stp3,1) size(stp4,1) size(stp5,1) nnf1 size(stp7,1)]; 44 | nn2 = [size(stf2,1) size(stf3,1) size(stf4,1) size(stf5,1) nnf2 size(stf7,1)]; 45 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 46 | else 47 | stpM = [stp2;stp6_1;stp7]; stfM = [stf2;stf6_1;stf7]; 48 | nn1 = [size(stp2,1) nnf1 size(stp7,1)]; 49 | nn2 = [size(stf2,1) nnf2 size(stf7,1)]; 50 | nn1(nn1 == 0) = []; nn2(nn2 == 0) = []; 51 | end 52 | else 53 | stpM = stp7; stfM = stf7; nn1 = size(stp7,1); nn2 = size(stf7,1); 54 | end 55 | % Identify elements according to the selected vertices and faces 56 | outeM = zeros(size(oute(:))); 57 | for i = 1:length(nn1) 58 | j1 = sum(nn1(1:i))-nn1(i)+1; k1 = j1+nn1(i)-1; 59 | j2 = sum(nn2(1:i))-nn2(i)+1; k2 = j2+nn2(i)-1; 60 | outeM = outeM+double(intriangulation(stpM(j1:k1,:),stfM(j2:k2,:),[x1_cen(:),y1_cen(:),z1_cen(:)])); 61 | outeM(outeM<0) = 0; outeM(outeM>1) = 1; 62 | end 63 | outeM = flip(reshape(outeM,nely,nelx,nelz),1); -------------------------------------------------------------------------------- /forcevec.m: -------------------------------------------------------------------------------- 1 | function [F,nf] = forcevec(Fn,ndof,Fmagx,Fmagy,Fmagz,loadtype) 2 | 3 | nf = max([length(Fmagx) length(Fmagy) length(Fmagz)]); 4 | Fx = zeros(ndof,nf); Fy = zeros(ndof,nf); Fz = zeros(ndof,nf); 5 | for i = 1:nf 6 | frn = full(Fn(:,i)); frn(frn == 0) = []; 7 | if strcmp(loadtype,'point') 8 | frn = sort(frn); frn = frn(round(length(frn)/2)); 9 | end 10 | frnm = [frn*3-2 frn*3-1 frn*3]; 11 | if length(Fmagx) > 1 12 | Fx(frnm(:,1),i) = Fmagx(i)/length(frnm(:,1)); 13 | else 14 | Fx(frnm(:,1),1) = Fmagx/length(frnm(:,1)); 15 | end 16 | if length(Fmagy) > 1 17 | Fy(frnm(:,2),i) = Fmagy(i)/length(frnm(:,2)); 18 | else 19 | Fy(frnm(:,2),1) = Fmagy/length(frnm(:,2)); 20 | end 21 | if length(Fmagz) > 1 22 | Fz(frnm(:,3),i) = Fmagz(i)/length(frnm(:,3)); 23 | else 24 | Fz(frnm(:,3),1) = Fmagz/length(frnm(:,3)); 25 | end 26 | end 27 | F = Fx+Fy+Fz; F(:,all(F == 0)) = []; F = sparse(F); -------------------------------------------------------------------------------- /geomeshini.m: -------------------------------------------------------------------------------- 1 | function [out_p,oute,outeM,sup_all,sup_x,sup_y,sup_z,Fn,nelx,nely,nelz,nele,ndof,stp1,a,... 2 | del_x,del_y,del_z] = geomeshini(MeshControl,inp1,inp2,inp3,... 3 | inp4,inp5,inp6,inp7,inp8,inp9,inp10,inp11,inp12,inp13,inp14,inp15,inp16,keep_BC,keep_BCxyz) 4 | inp_f = {inp6,inp7,inp8,inp9,inp10,inp11,inp12,inp13,inp14,inp15}; inp_f(~cellfun(@ischar,inp_f)) = []; 5 | % get stl vertices for inp1 6 | st1 = stlread(inp1); % read stl 7 | stp1 = st1.Points; stf1 = st1.ConnectivityList; % obtain vertices and faces 8 | % get bounding box 9 | stp_xmax = max(stp1(:,1)); stp_xmin = min(stp1(:,1)); % get the minimum and maximum x for domain 10 | del_x = stp_xmax-stp_xmin; 11 | stp_ymax = max(stp1(:,2)); stp_ymin = min(stp1(:,2)); % get the minimum and maximum y for domain 12 | del_y = stp_ymax-stp_ymin; 13 | stp_zmax = max(stp1(:,3)); stp_zmin = min(stp1(:,3)); % get the minimum and maximum z for domain 14 | del_z = stp_zmax-stp_zmin; 15 | stt = abs([stp_xmin stp_xmax stp_ymin stp_ymax stp_zmin stp_zmax]); % get the maximum of all maximums for domain 16 | stm = max(stt); stff = find(stt == stm(1)); % get the position of the max of all maxs 17 | 18 | if stff(1) == 1 || stff(1) == 2 % if the maximum is in position 1 19 | gpx = MeshControl; 20 | x_old = linspace(stp_xmin, stp_xmax, gpx); ssz_old = (x_old(2)-x_old(1))*1e-2; 21 | x = linspace(stp_xmin+ssz_old, stp_xmax-ssz_old, gpx); 22 | ssz = x(2)-x(1); gpx = length(x); a = ssz/2000; % the MeshControl should be defined from the x-axis 23 | y_old = stp_ymin:ssz:stp_ymax; diff_y = stp_ymax-y_old(end); % obtain y MeshControl 24 | y = stp_ymin+diff_y/2:ssz:stp_ymax; gpy = length(y); 25 | z_old = stp_zmin:ssz:stp_zmax; diff_z = stp_zmax-z_old(end); % obtain z MeshControl 26 | z = stp_zmin+diff_z/2:ssz:stp_zmax; gpz = length(z); 27 | x_cen = x+ssz/2; x_cen(end) = []; y_cen = y+ssz/2; y_cen(end) = []; z_cen = z+ssz/2; z_cen(end) = []; 28 | elseif stff(1) == 3 || stff(1) == 4 29 | gpy = MeshControl; 30 | y_old = linspace(stp_ymin, stp_ymax, gpy); ssz_old = (y_old(2)-y_old(1))*1e-2; 31 | y = linspace(stp_ymin+ssz_old, stp_ymax-ssz_old, gpy); 32 | ssz = y(2)-y(1); gpy = length(y); a = ssz/2000; 33 | x_old = stp_xmin:ssz:stp_xmax; diff_x = stp_xmax-x_old(end); 34 | x = stp_xmin+diff_x/2:ssz:stp_xmax; gpx = length(x); 35 | z_old = stp_zmin:ssz:stp_zmax; diff_z = stp_zmax-z_old(end); 36 | z = stp_zmin+diff_z/2:ssz:stp_zmax; gpz = length(z); 37 | x_cen = x+ssz/2; x_cen(end) = []; y_cen = y+ssz/2; y_cen(end) = []; z_cen = z+ssz/2; z_cen(end) = []; 38 | else 39 | gpz = MeshControl; 40 | z_old = linspace(stp_zmin, stp_zmax, gpz); ssz_old = (z_old(2)-z_old(1))*1e-2; 41 | z = linspace(stp_zmin+ssz_old,stp_zmax-ssz_old,gpz); 42 | ssz = z(2)-z(1); gpz = length(z); a = ssz/2000; 43 | x_old = stp_xmin:ssz:stp_xmax; diff_x = stp_xmax-x_old(end); 44 | x = stp_xmin+diff_x/2:ssz:stp_xmax; gpx = length(x); 45 | y_old = stp_ymin:ssz:stp_ymax; diff_y = stp_ymax-y_old(end); % obtain y MeshControl 46 | y = stp_ymin+diff_y/2:ssz:stp_ymax; gpy = length(y); 47 | x_cen = x+ssz/2; x_cen(end) = []; y_cen = y+ssz/2; y_cen(end) = []; z_cen = z+ssz/2; z_cen(end) = []; 48 | end 49 | 50 | %create meshgrid for x, y, and z 51 | [x1,y1,z1] = meshgrid(x,y,z); % create a meshgrid from x,y,z MeshControl 52 | [x1_cen,y1_cen,z1_cen] = meshgrid(x_cen,y_cen,z_cen); 53 | % for inp1 - Domain 54 | out = double(intriangulation(stp1,stf1,[x1(:),y1(:),z1(:)])); out(out<0) = 0; % check which points lie in and out of the domain 55 | % for inp2 - Fixed all DOFs support 56 | if ~isempty(inp2) 57 | st2 = stlread(inp2); 58 | stp2 = st2.Points; stf2 = st2.ConnectivityList; 59 | out2 = intriangulation(stp2,stf2,[x1(:),y1(:),z1(:)]); 60 | out2 = flip(reshape(out2,gpy,gpx,gpz),1); 61 | sup_all = find(out2(:)>0); % identify support nodes fixd in all directions in the domain 62 | else 63 | sup_all = []; stp2 = []; stf2 = []; 64 | end 65 | % for inp3 - Fixed in x 66 | if ~isempty(inp3) 67 | st3 = stlread(inp3); 68 | stp3 = st3.Points; stf3 = st3.ConnectivityList; 69 | out3 = intriangulation(stp3,stf3,[x1(:),y1(:),z1(:)]); 70 | out3 = flip(reshape(out3,gpy,gpx,gpz),1); 71 | sup_x = find(out3(:)>0); 72 | else 73 | sup_x = []; stp3 = []; stf3 = []; 74 | end 75 | % for inp4 - Fixed in y 76 | if ~isempty(inp4) 77 | st4 = stlread(inp4); 78 | stp4 = st4.Points; stf4 = st4.ConnectivityList; 79 | out4 = intriangulation(stp4,stf4,[x1(:),y1(:),z1(:)]); 80 | out4 = flip(reshape(out4,gpy,gpx,gpz),1); 81 | sup_y = find(out4(:)>0); 82 | else 83 | sup_y = []; stp4 = []; stf4 = []; 84 | end 85 | % for inp5 - Fixed in z 86 | if ~isempty(inp5) 87 | st5 = stlread(inp5); 88 | stp5 = st5.Points; stf5 = st5.ConnectivityList; 89 | out5 = intriangulation(stp5,stf5,[x1(:),y1(:),z1(:)]); 90 | out5 = flip(reshape(out5,gpy,gpx,gpz),1); 91 | sup_z = find(out5(:)>0); 92 | else 93 | sup_z = []; stp5 = []; stf5 = []; 94 | end 95 | % for inp6 - Force 96 | Fn = sparse(length(out),length(inp_f)); stp6_1 = []; stf6_1 = []; 97 | for i = 1:length(inp_f) 98 | st6 = stlread(inp_f{i}); 99 | stp6 = st6.Points; stp6_1 = [stp6_1;stp6]; nnf1(i) = size(stp6,1); 100 | stf6 = st6.ConnectivityList; stf6_1 = [stf6_1;stf6]; nnf2(i) = size(stf6,1); 101 | out6 = intriangulation(stp6,stf6,[x1(:),y1(:),z1(:)]); 102 | out6 = flip(reshape(out6,gpy,gpx,gpz),1); 103 | Fn(1:length(find(out6(:)>0)),i) = find(out6(:)>0); 104 | end 105 | % for domains to keep 106 | if ~isempty(inp16) 107 | st7 = stlread(inp16); 108 | stp7 = st7.Points; stf7 = st7.ConnectivityList; 109 | out7 = intriangulation(stp7,stf7,[x1(:),y1(:),z1(:)]); 110 | out7 = flip(reshape(out7,gpy,gpx,gpz),1); 111 | dom_ke = find(out7(:)>0); 112 | else 113 | dom_ke = []; stp7 = []; stf7 = []; 114 | end 115 | out_p = flip(reshape(out,gpy,gpx,gpz),1); % flip the identified points 116 | nelx = size(out_p,2)-1; nely = size(out_p,1)-1; nelz = size(out_p,3)-1; nele = nelx*nely*nelz; % obtain nelx, nely,nelz and nele 117 | ndof = 3*(nelx+1)*(nely+1)*(nelz+1); % obtain number of degrees of freedom 118 | %% get elements in the domain 119 | oute = double(intriangulation(stp1,stf1,[x1_cen(:),y1_cen(:),z1_cen(:)])); oute(oute<0) = 0; 120 | oute = flip(reshape(oute,nely,nelx,nelz),1); 121 | %% get elements to keep during optimization 122 | outeM = domainstokeep(oute,nelx,nely,nelz,keep_BC,keep_BCxyz,stp2,stp3,stp4,stp5,... 123 | stp6_1,stp7,stf2,stf3,stf4,stf5,stf6_1,stf7,nnf1,nnf2,x1_cen,y1_cen,z1_cen); -------------------------------------------------------------------------------- /intriangulation.m: -------------------------------------------------------------------------------- 1 | function in = intriangulation(vertices,faces,testp,heavytest) 2 | % intriangulation: Test points in 3d wether inside or outside a (closed) triangulation 3 | % usage: in = intriangulation(vertices,faces,testp,heavytest) 4 | % 5 | % arguments: (input) 6 | % vertices - points in 3d as matrix with three columns 7 | % 8 | % faces - description of triangles as matrix with three columns. 9 | % Each row contains three indices into the matrix of vertices 10 | % which gives the three cornerpoints of the triangle. 11 | % 12 | % testp - points in 3d as matrix with three columns 13 | % 14 | % heavytest - int n >= 0. Perform n additional randomized rotation tests. 15 | % 16 | % IMPORTANT: the set of vertices and faces has to form a watertight surface! 17 | % 18 | % arguments: (output) 19 | % in - a vector of length size(testp,1), containing 0 and 1. 20 | % in(nr) = 0: testp(nr,:) is outside the triangulation 21 | % in(nr) = 1: testp(nr,:) is inside the triangulation 22 | % in(nr) = -1: unable to decide for testp(nr,:) 23 | % 24 | % Thanks to Adam A for providing the FEX submission voxelise. The 25 | % algorithms of voxelise form the algorithmic kernel of intriangulation. 26 | % 27 | % Thanks to Sven to discussions about speed and avoiding problems in 28 | % special cases. 29 | % 30 | % Example usage: 31 | % 32 | % n = 10; 33 | % vertices = rand(n, 3)-0.5; % Generate random points 34 | % tetra = delaunayn(vertices); % Generate delaunay triangulization 35 | % faces = freeBoundary(TriRep(tetra,vertices)); % use free boundary as triangulation 36 | % n = 1000; 37 | % testp = 2*rand(n,3)-1; % Generate random testpoints 38 | % in = intriangulation(vertices,faces,testp); 39 | % % Plot results 40 | % h = trisurf(faces,vertices(:,1),vertices(:,2),vertices(:,3)); 41 | % set(h,'FaceColor','black','FaceAlpha',1/3,'EdgeColor','none'); 42 | % hold on; 43 | % plot3(testp(:,1),testp(:,2),testp(:,3),'b.'); 44 | % plot3(testp(in==1,1),testp(in==1,2),testp(in==1,3),'ro'); 45 | % 46 | % See also: intetrahedron, tsearchn, inpolygon 47 | % 48 | % Author: Johannes Korsawe, heavily based on voxelise from Adam A. 49 | % E-mail: johannes.korsawe@volkswagen.de 50 | % Release: 1.3 51 | % Release date: 25/09/2013 52 | 53 | % check number of inputs 54 | if nargin<3, 55 | fprintf('??? Error using ==> intriangulation\nThree input matrices are needed.\n');in=[];return; 56 | end 57 | if nargin==3, 58 | heavytest = 0; 59 | end 60 | % check size of inputs 61 | if size(vertices,2)~=3 || size(faces,2)~=3 || size(testp,2)~=3, 62 | fprintf('??? Error using ==> intriagulation\nAll input matrices must have three columns.\n');in=[];return; 63 | end 64 | ipmax = max(faces(:));zerofound = ~isempty(find(faces(:)==0, 1)); 65 | if ipmax>size(vertices,1) || zerofound, 66 | fprintf('??? Error using ==> intriangulation\nThe triangulation data is defect. use trisurf(faces,vertices(:,1),vertices(:,2),vertices(:,3)) for test of deficiency.\n');return; 67 | end 68 | 69 | % loop for heavytest 70 | inreturn = zeros(size(testp,1),1);VER = vertices;TESTP = testp; 71 | 72 | for n = 1:heavytest+1, 73 | 74 | % Randomize 75 | if n>1, 76 | v=rand(1,3);D=rotmatrix(v/norm(v),rand*180/pi);vertices=VER*D;testp = TESTP*D; 77 | else, 78 | vertices=VER; 79 | end 80 | 81 | % Preprocessing data 82 | meshXYZ = zeros(size(faces,1),3,3); 83 | for loop = 1:3, 84 | meshXYZ(:,:,loop) = vertices(faces(:,loop),:); 85 | end 86 | 87 | % Basic idea (ingenious from FeX-submission voxelise): 88 | % If point is inside, it will cross the triangulation an uneven number of times in each direction (x, -x, y, -y, z, -z). 89 | 90 | % The function VOXELISEinternal is about 98% identical to its version inside voxelise.m. 91 | % This includes the elaborate comments. Thanks to Adam A! 92 | 93 | % z-direction: 94 | % intialization of results and correction list 95 | [in,cl] = VOXELISEinternal(testp(:,1),testp(:,2),testp(:,3),meshXYZ); 96 | 97 | % x-direction: 98 | % has only to be done for those points, that were not determinable in the first step --> cl 99 | [in2,cl2] = VOXELISEinternal(testp(cl,2),testp(cl,3),testp(cl,1),meshXYZ(:,[2,3,1],:)); 100 | % Use results of x-direction that determined "inside" 101 | in(cl(in2==1)) = 1; 102 | % remaining indices with unclear result 103 | cl = cl(cl2); 104 | 105 | % y-direction: 106 | % has only to be done for those points, that were not determinable in the first and second step --> cl 107 | [in3,cl3] = VOXELISEinternal(testp(cl,3),testp(cl,1),testp(cl,2),meshXYZ(:,[3,1,2],:)); 108 | 109 | % Use results of y-direction that determined "inside" 110 | in(cl(in3==1)) = 1; 111 | % remaining indices with unclear result 112 | cl = cl(cl3); 113 | 114 | % mark those indices, where all three tests have failed 115 | in(cl) = -1; 116 | 117 | if n==1, 118 | inreturn = in; % Starting guess 119 | else, 120 | % if ALWAYS inside, use as inside! 121 | % I = find(inreturn ~= in); 122 | % inreturn(I(in(I)==0)) = 0; 123 | 124 | % if AT LEAST ONCE inside, use as inside! 125 | I = find(inreturn ~= in); 126 | inreturn(I(in(I)==1)) = 1; 127 | 128 | end 129 | 130 | end 131 | 132 | in = inreturn; 133 | 134 | end 135 | 136 | %========================================================================== 137 | function [OUTPUT,correctionLIST] = VOXELISEinternal(testx,testy,testz,meshXYZ) 138 | 139 | % Prepare logical array to hold the logical data: 140 | OUTPUT = false(size(testx,1),1); 141 | 142 | %Identify the min and max x,y coordinates of the mesh: 143 | meshZmin = min(min(meshXYZ(:,3,:)));meshZmax = max(max(meshXYZ(:,3,:))); 144 | 145 | %Identify the min and max x,y,z coordinates of each facet: 146 | meshXYZmin = min(meshXYZ,[],3);meshXYZmax = max(meshXYZ,[],3); 147 | 148 | %====================================================== 149 | % TURN OFF DIVIDE-BY-ZERO WARNINGS 150 | %====================================================== 151 | %This prevents the Y1predicted, Y2predicted, Y3predicted and YRpredicted 152 | %calculations creating divide-by-zero warnings. Suppressing these warnings 153 | %doesn't affect the code, because only the sign of the result is important. 154 | %That is, 'Inf' and '-Inf' results are ok. 155 | %The warning will be returned to its original state at the end of the code. 156 | warningrestorestate = warning('query', 'MATLAB:divideByZero'); 157 | %warning off MATLAB:divideByZero 158 | 159 | %====================================================== 160 | % START COMPUTATION 161 | %====================================================== 162 | 163 | correctionLIST = []; %Prepare to record all rays that fail the voxelisation. This array is built on-the-fly, but since 164 | %it ought to be relatively small should not incur too much of a speed penalty. 165 | 166 | % Loop through each testpoint. 167 | % The testpoint-array will be tested by passing rays in the z-direction through 168 | % each x,y coordinate of the testpoints, and finding the locations where the rays cross the mesh. 169 | facetCROSSLIST = zeros(1,1e3); % uses countindex: nf 170 | nm = size(meshXYZmin,1); 171 | for loop = 1:length(OUTPUT), 172 | 173 | nf = 0; 174 | % % - 1a - Find which mesh facets could possibly be crossed by the ray: 175 | % possibleCROSSLISTy = find( meshXYZmin(:,2)<=testy(loop) & meshXYZmax(:,2)>=testy(loop) ); 176 | 177 | % % - 1b - Find which mesh facets could possibly be crossed by the ray: 178 | % possibleCROSSLIST = possibleCROSSLISTy( meshXYZmin(possibleCROSSLISTy,1)<=testx(loop) & meshXYZmax(possibleCROSSLISTy,1)>=testx(loop) ); 179 | 180 | % Do - 1a - and - 1b - faster 181 | possibleCROSSLISTy = find((testy(loop)-meshXYZmin(:,2)).*(meshXYZmax(:,2)-testy(loop))>0); 182 | possibleCROSSLISTx = (testx(loop)-meshXYZmin(possibleCROSSLISTy,1)).*(meshXYZmax(possibleCROSSLISTy,1)-testx(loop))>0; 183 | possibleCROSSLIST = possibleCROSSLISTy(possibleCROSSLISTx); 184 | 185 | if isempty(possibleCROSSLIST)==0 %Only continue the analysis if some nearby facets were actually identified 186 | 187 | % - 2 - For each facet, check if the ray really does cross the facet rather than just passing it close-by: 188 | 189 | % GENERAL METHOD: 190 | % 1. Take each edge of the facet in turn. 191 | % 2. Find the position of the opposing vertex to that edge. 192 | % 3. Find the position of the ray relative to that edge. 193 | % 4. Check if ray is on the same side of the edge as the opposing vertex. 194 | % 5. If this is true for all three edges, then the ray definitely passes through the facet. 195 | % 196 | % NOTES: 197 | % 1. If the ray crosses exactly on an edge, this is counted as crossing the facet. 198 | % 2. If a ray crosses exactly on a vertex, this is also taken into account. 199 | 200 | for loopCHECKFACET = possibleCROSSLIST' 201 | 202 | %Check if ray crosses the facet. This method is much (>>10 times) faster than using the built-in function 'inpolygon'. 203 | %Taking each edge of the facet in turn, check if the ray is on the same side as the opposing vertex. If so, let testVn=1 204 | 205 | Y1predicted = meshXYZ(loopCHECKFACET,2,2) - ((meshXYZ(loopCHECKFACET,2,2)-meshXYZ(loopCHECKFACET,2,3)) * (meshXYZ(loopCHECKFACET,1,2)-meshXYZ(loopCHECKFACET,1,1))/(meshXYZ(loopCHECKFACET,1,2)-meshXYZ(loopCHECKFACET,1,3))); 206 | YRpredicted = meshXYZ(loopCHECKFACET,2,2) - ((meshXYZ(loopCHECKFACET,2,2)-meshXYZ(loopCHECKFACET,2,3)) * (meshXYZ(loopCHECKFACET,1,2)-testx(loop))/(meshXYZ(loopCHECKFACET,1,2)-meshXYZ(loopCHECKFACET,1,3))); 207 | 208 | if (Y1predicted > meshXYZ(loopCHECKFACET,2,1) && YRpredicted > testy(loop)) || (Y1predicted < meshXYZ(loopCHECKFACET,2,1) && YRpredicted < testy(loop)) || (meshXYZ(loopCHECKFACET,2,2)-meshXYZ(loopCHECKFACET,2,3)) * (meshXYZ(loopCHECKFACET,1,2)-testx(loop)) == 0 209 | % testV1 = 1; %The ray is on the same side of the 2-3 edge as the 1st vertex. 210 | else 211 | % testV1 = 0; %The ray is on the opposite side of the 2-3 edge to the 1st vertex. 212 | % As the check is for ALL three checks to be true, we can continue here, if only one check fails 213 | continue; 214 | end %if 215 | 216 | Y2predicted = meshXYZ(loopCHECKFACET,2,3) - ((meshXYZ(loopCHECKFACET,2,3)-meshXYZ(loopCHECKFACET,2,1)) * (meshXYZ(loopCHECKFACET,1,3)-meshXYZ(loopCHECKFACET,1,2))/(meshXYZ(loopCHECKFACET,1,3)-meshXYZ(loopCHECKFACET,1,1))); 217 | YRpredicted = meshXYZ(loopCHECKFACET,2,3) - ((meshXYZ(loopCHECKFACET,2,3)-meshXYZ(loopCHECKFACET,2,1)) * (meshXYZ(loopCHECKFACET,1,3)-testx(loop))/(meshXYZ(loopCHECKFACET,1,3)-meshXYZ(loopCHECKFACET,1,1))); 218 | if (Y2predicted > meshXYZ(loopCHECKFACET,2,2) && YRpredicted > testy(loop)) || (Y2predicted < meshXYZ(loopCHECKFACET,2,2) && YRpredicted < testy(loop)) || (meshXYZ(loopCHECKFACET,2,3)-meshXYZ(loopCHECKFACET,2,1)) * (meshXYZ(loopCHECKFACET,1,3)-testx(loop)) == 0 219 | % testV2 = 1; %The ray is on the same side of the 3-1 edge as the 2nd vertex. 220 | else 221 | % testV2 = 0; %The ray is on the opposite side of the 3-1 edge to the 2nd vertex. 222 | % As the check is for ALL three checks to be true, we can continue here, if only one check fails 223 | continue; 224 | end %if 225 | 226 | Y3predicted = meshXYZ(loopCHECKFACET,2,1) - ((meshXYZ(loopCHECKFACET,2,1)-meshXYZ(loopCHECKFACET,2,2)) * (meshXYZ(loopCHECKFACET,1,1)-meshXYZ(loopCHECKFACET,1,3))/(meshXYZ(loopCHECKFACET,1,1)-meshXYZ(loopCHECKFACET,1,2))); 227 | YRpredicted = meshXYZ(loopCHECKFACET,2,1) - ((meshXYZ(loopCHECKFACET,2,1)-meshXYZ(loopCHECKFACET,2,2)) * (meshXYZ(loopCHECKFACET,1,1)-testx(loop))/(meshXYZ(loopCHECKFACET,1,1)-meshXYZ(loopCHECKFACET,1,2))); 228 | if (Y3predicted > meshXYZ(loopCHECKFACET,2,3) && YRpredicted > testy(loop)) || (Y3predicted < meshXYZ(loopCHECKFACET,2,3) && YRpredicted < testy(loop)) || (meshXYZ(loopCHECKFACET,2,1)-meshXYZ(loopCHECKFACET,2,2)) * (meshXYZ(loopCHECKFACET,1,1)-testx(loop)) == 0 229 | % testV3 = 1; %The ray is on the same side of the 1-2 edge as the 3rd vertex. 230 | else 231 | % testV3 = 0; %The ray is on the opposite side of the 1-2 edge to the 3rd vertex. 232 | % As the check is for ALL three checks to be true, we can continue here, if only one check fails 233 | continue; 234 | end %if 235 | 236 | nf=nf+1;facetCROSSLIST(nf)=loopCHECKFACET; 237 | 238 | end %for 239 | 240 | % Use only values ~=0 241 | facetCROSSLIST = facetCROSSLIST(1:nf); 242 | 243 | % - 3 - Find the z coordinate of the locations where the ray crosses each facet: 244 | gridCOzCROSS = zeros(1,nf); 245 | for loopFINDZ = facetCROSSLIST 246 | 247 | % METHOD: 248 | % 1. Define the equation describing the plane of the facet. For a 249 | % more detailed outline of the maths, see: 250 | % http://local.wasp.uwa.edu.au/~pbourke/geometry/planeeq/ 251 | % Ax + By + Cz + D = 0 252 | % where A = y1 (z2 - z3) + y2 (z3 - z1) + y3 (z1 - z2) 253 | % B = z1 (x2 - x3) + z2 (x3 - x1) + z3 (x1 - x2) 254 | % C = x1 (y2 - y3) + x2 (y3 - y1) + x3 (y1 - y2) 255 | % D = - x1 (y2 z3 - y3 z2) - x2 (y3 z1 - y1 z3) - x3 (y1 z2 - y2 z1) 256 | % 2. For the x and y coordinates of the ray, solve these equations to find the z coordinate in this plane. 257 | 258 | planecoA = meshXYZ(loopFINDZ,2,1)*(meshXYZ(loopFINDZ,3,2)-meshXYZ(loopFINDZ,3,3)) + meshXYZ(loopFINDZ,2,2)*(meshXYZ(loopFINDZ,3,3)-meshXYZ(loopFINDZ,3,1)) + meshXYZ(loopFINDZ,2,3)*(meshXYZ(loopFINDZ,3,1)-meshXYZ(loopFINDZ,3,2)); 259 | planecoB = meshXYZ(loopFINDZ,3,1)*(meshXYZ(loopFINDZ,1,2)-meshXYZ(loopFINDZ,1,3)) + meshXYZ(loopFINDZ,3,2)*(meshXYZ(loopFINDZ,1,3)-meshXYZ(loopFINDZ,1,1)) + meshXYZ(loopFINDZ,3,3)*(meshXYZ(loopFINDZ,1,1)-meshXYZ(loopFINDZ,1,2)); 260 | planecoC = meshXYZ(loopFINDZ,1,1)*(meshXYZ(loopFINDZ,2,2)-meshXYZ(loopFINDZ,2,3)) + meshXYZ(loopFINDZ,1,2)*(meshXYZ(loopFINDZ,2,3)-meshXYZ(loopFINDZ,2,1)) + meshXYZ(loopFINDZ,1,3)*(meshXYZ(loopFINDZ,2,1)-meshXYZ(loopFINDZ,2,2)); 261 | planecoD = - meshXYZ(loopFINDZ,1,1)*(meshXYZ(loopFINDZ,2,2)*meshXYZ(loopFINDZ,3,3)-meshXYZ(loopFINDZ,2,3)*meshXYZ(loopFINDZ,3,2)) - meshXYZ(loopFINDZ,1,2)*(meshXYZ(loopFINDZ,2,3)*meshXYZ(loopFINDZ,3,1)-meshXYZ(loopFINDZ,2,1)*meshXYZ(loopFINDZ,3,3)) - meshXYZ(loopFINDZ,1,3)*(meshXYZ(loopFINDZ,2,1)*meshXYZ(loopFINDZ,3,2)-meshXYZ(loopFINDZ,2,2)*meshXYZ(loopFINDZ,3,1)); 262 | 263 | if abs(planecoC) < 1e-14 264 | planecoC=0; 265 | end 266 | 267 | gridCOzCROSS(facetCROSSLIST==loopFINDZ) = (- planecoD - planecoA*testx(loop) - planecoB*testy(loop)) / planecoC; 268 | 269 | end %for 270 | 271 | if isempty(gridCOzCROSS),continue;end 272 | 273 | %Remove values of gridCOzCROSS which are outside of the mesh limits (including a 1e-12 margin for error). 274 | gridCOzCROSS = gridCOzCROSS( gridCOzCROSS>=meshZmin-1e-12 & gridCOzCROSS<=meshZmax+1e-12 ); 275 | 276 | %Round gridCOzCROSS to remove any rounding errors, and take only the unique values: 277 | gridCOzCROSS = round(gridCOzCROSS*1e10)/1e10; 278 | 279 | % Replacement of the call to unique (gridCOzCROSS = unique(gridCOzCROSS);) by the following line: 280 | tmp = sort(gridCOzCROSS);I=[0,tmp(2:end)-tmp(1:end-1)]~=0;gridCOzCROSS = [tmp(1),tmp(I)]; 281 | 282 | % - 4 - Label as being inside the mesh all the voxels that the ray passes through after crossing one facet before crossing another facet: 283 | 284 | if rem(numel(gridCOzCROSS),2)==0 % Only rays which cross an even number of facets are voxelised 285 | 286 | for loopASSIGN = 1:(numel(gridCOzCROSS)/2) 287 | voxelsINSIDE = (testz(loop)>gridCOzCROSS(2*loopASSIGN-1) & testz(loop)0 321 | 322 | %If necessary, add a one-pixel border around the x and y edges of the 323 | %array. This prevents an error if the code tries to interpolate a ray at 324 | %the edge of the x,y grid. 325 | if min(correctionLIST(:,1))==1 || max(correctionLIST(:,1))==numel(gridCOx) || min(correctionLIST(:,2))==1 || max(correctionLIST(:,2))==numel(gridCOy) 326 | gridOUTPUT = [zeros(1,voxcountY+2,voxcountZ);zeros(voxcountX,1,voxcountZ),gridOUTPUT,zeros(voxcountX,1,voxcountZ);zeros(1,voxcountY+2,voxcountZ)]; 327 | correctionLIST = correctionLIST + 1; 328 | end 329 | 330 | for loopC = 1:countCORRECTIONLIST 331 | voxelsforcorrection = squeeze( sum( [ gridOUTPUT(correctionLIST(loopC,1)-1,correctionLIST(loopC,2)-1,:) ,... 332 | gridOUTPUT(correctionLIST(loopC,1)-1,correctionLIST(loopC,2),:) ,... 333 | gridOUTPUT(correctionLIST(loopC,1)-1,correctionLIST(loopC,2)+1,:) ,... 334 | gridOUTPUT(correctionLIST(loopC,1),correctionLIST(loopC,2)-1,:) ,... 335 | gridOUTPUT(correctionLIST(loopC,1),correctionLIST(loopC,2)+1,:) ,... 336 | gridOUTPUT(correctionLIST(loopC,1)+1,correctionLIST(loopC,2)-1,:) ,... 337 | gridOUTPUT(correctionLIST(loopC,1)+1,correctionLIST(loopC,2),:) ,... 338 | gridOUTPUT(correctionLIST(loopC,1)+1,correctionLIST(loopC,2)+1,:) ,... 339 | ] ) ); 340 | voxelsforcorrection = (voxelsforcorrection>=4); 341 | gridOUTPUT(correctionLIST(loopC,1),correctionLIST(loopC,2),voxelsforcorrection) = 1; 342 | end %for 343 | 344 | %Remove the one-pixel border surrounding the array, if this was added 345 | %previously. 346 | if size(gridOUTPUT,1)>numel(gridCOx) || size(gridOUTPUT,2)>numel(gridCOy) 347 | gridOUTPUT = gridOUTPUT(2:end-1,2:end-1,:); 348 | end 349 | 350 | end %if 351 | 352 | %disp([' Ray tracing result: ',num2str(countCORRECTIONLIST),' rays (',num2str(countCORRECTIONLIST/(voxcountX*voxcountY)*100,'%5.1f'),'% of all rays) exactly crossed a facet edge and had to be computed by interpolation.']) 353 | 354 | end %function 355 | %========================================================================== 356 | 357 | function D = rotmatrix(v,deg) 358 | % calculate the rotation matrix about v by deg degrees 359 | 360 | deg=deg/180*pi; 361 | if deg~=0, 362 | v=v/norm(v); 363 | v1=v(1);v2=v(2);v3=v(3);ca=cos(deg);sa=sin(deg); 364 | D=[ca+v1*v1*(1-ca),v1*v2*(1-ca)-v3*sa,v1*v3*(1-ca)+v2*sa; 365 | v2*v1*(1-ca)+v3*sa,ca+v2*v2*(1-ca),v2*v3*(1-ca)-v1*sa; 366 | v3*v1*(1-ca)-v2*sa,v3*v2*(1-ca)+v1*sa,ca+v3*v3*(1-ca)]; 367 | else, 368 | D=eye(3,3); 369 | end 370 | 371 | end 372 | -------------------------------------------------------------------------------- /lk_H8.m: -------------------------------------------------------------------------------- 1 | %% === GENERATE ELEMENT STIFFNESS MATRIX === 2 | function [KE] = lk_H8(nu) 3 | A = [32 6 -8 6 -6 4 3 -6 -10 3 -3 -3 -4 -8; 4 | -48 0 0 -24 24 0 0 0 12 -12 0 12 12 12]; 5 | k = 1/144*A'*[1; nu]; 6 | K1 = [k(1) k(2) k(2) k(3) k(5) k(5); 7 | k(2) k(1) k(2) k(4) k(6) k(7); 8 | k(2) k(2) k(1) k(4) k(7) k(6); 9 | k(3) k(4) k(4) k(1) k(8) k(8); 10 | k(5) k(6) k(7) k(8) k(1) k(2); 11 | k(5) k(7) k(6) k(8) k(2) k(1)]; 12 | K2 = [k(9) k(8) k(12) k(6) k(4) k(7); 13 | k(8) k(9) k(12) k(5) k(3) k(5); 14 | k(10) k(10) k(13) k(7) k(4) k(6); 15 | k(6) k(5) k(11) k(9) k(2) k(10); 16 | k(4) k(3) k(5) k(2) k(9) k(12) 17 | k(11) k(4) k(6) k(12) k(10) k(13)]; 18 | K3 = [k(6) k(7) k(4) k(9) k(12) k(8); 19 | k(7) k(6) k(4) k(10) k(13) k(10); 20 | k(5) k(5) k(3) k(8) k(12) k(9); 21 | k(9) k(10) k(2) k(6) k(11) k(5); 22 | k(12) k(13) k(10) k(11) k(6) k(4); 23 | k(2) k(12) k(9) k(4) k(5) k(3)]; 24 | K4 = [k(14) k(11) k(11) k(13) k(10) k(10); 25 | k(11) k(14) k(11) k(12) k(9) k(8); 26 | k(11) k(11) k(14) k(12) k(8) k(9); 27 | k(13) k(12) k(12) k(14) k(7) k(7); 28 | k(10) k(9) k(8) k(7) k(14) k(11); 29 | k(10) k(8) k(9) k(7) k(11) k(14)]; 30 | K5 = [k(1) k(2) k(8) k(3) k(5) k(4); 31 | k(2) k(1) k(8) k(4) k(6) k(11); 32 | k(8) k(8) k(1) k(5) k(11) k(6); 33 | k(3) k(4) k(5) k(1) k(8) k(2); 34 | k(5) k(6) k(11) k(8) k(1) k(8); 35 | k(4) k(11) k(6) k(2) k(8) k(1)]; 36 | K6 = [k(14) k(11) k(7) k(13) k(10) k(12); 37 | k(11) k(14) k(7) k(12) k(9) k(2); 38 | k(7) k(7) k(14) k(10) k(2) k(9); 39 | k(13) k(12) k(10) k(14) k(7) k(11); 40 | k(10) k(9) k(2) k(7) k(14) k(7); 41 | k(12) k(2) k(9) k(11) k(7) k(14)]; 42 | KE = 1/((nu+1)*(1-2*nu))*... 43 | [ K1 K2 K3 K4; 44 | K2' K5 K6 K3'; 45 | K3' K6 K5' K2'; 46 | K4 K3 K2 K1']; 47 | end -------------------------------------------------------------------------------- /smoothedge3D.m: -------------------------------------------------------------------------------- 1 | function [vxPhys,xg,ls,top,tol] = smoothedge3D(vxPhys,Hn,Hns,nelx,nely,nelz,nele,nnele,nodex,nodey,nodez,fnx,fny,fnz,beta,ngrid) 2 | xn = reshape((Hn*vxPhys(:)./Hns),nely+1,nelx+1,nelz+1); 3 | %% UPDATE POINT DESNIGY BY A HEAVISIDE SMOOTH FUNCTION/ HEAVISIDE STEP FUNCTION 4 | xg = interp3(nodex,nodey,nodez,xn,fnx,fny,fnz,'linear'); 5 | l1 =0; l2 = 1; 6 | while (l2-l1) > 1.0e-5 7 | ls = (l1+l2)/2.0; 8 | %% Heaviside smooth function 9 | xgnew = max(0.001,(tanh(beta*ls)+tanh(beta*(xg-ls)))/(tanh(beta*ls)+tanh(beta*(1-ls)))); 10 | if sum(sum(sum(xgnew)))/((ngrid*nelx+1)*(ngrid*nely+1)*(ngrid*nelz+1)) - sum(vxPhys(:))/(nele) > 0 11 | l1 = ls; 12 | else 13 | l2 = ls; 14 | end 15 | end 16 | %% CONVERTING TO ELEMENTS 17 | vxPhys(:) = 0; 18 | Terr = 0; 19 | Tm=[]; 20 | for nk = 1:nelz 21 | for ni = 1:nelx 22 | for nj = 1:nely 23 | ne = (nk-1)*nelx*nely+(ni-1)*nely+nj; 24 | for nk1 = ngrid*(nk-1)+1:ngrid*nk+1 25 | for ni1 = ngrid*(ni-1)+1:ngrid*ni+1 26 | for nj1 = ngrid*(nj-1)+1:ngrid*nj+1 27 | Tm=[Tm;xgnew(nj1,ni1,nk1)]; 28 | vxPhys(ne) = vxPhys(ne)+xgnew(nj1,ni1,nk1); 29 | end 30 | end 31 | end 32 | if min(Tm)>0.001 && max(Tm)<1 33 | Terr=Terr+1; 34 | end 35 | Tm=[]; 36 | end 37 | end 38 | end 39 | vxPhys = vxPhys/((ngrid+1)^3); 40 | 41 | %% Topology Error 42 | tol = Terr/(nnele); 43 | top = xg-ls; -------------------------------------------------------------------------------- /stlgen.m: -------------------------------------------------------------------------------- 1 | function stlgen(top,fnx,fny,fnz,dx,dy,dz,nm) 2 | %% check model name nm 3 | nm1 = split(nm,'.'); 4 | if length(nm1) > 1 5 | nm = strcat(string(nm1{1}),'.stl'); 6 | else 7 | nm = strcat(string(nm1),'.stl'); 8 | end 9 | % Dimensions of initial phi 10 | st_max = max([dx dy dz]); 11 | phi = top; phi = flip(phi,1); 12 | gmax = max([max(fnx(:)) max(fny(:)) max(fnz(:))]); 13 | gx = (fnx/gmax)*st_max; gy = (fny/gmax)*st_max; gz = (fnz/gmax)*st_max; 14 | 15 | phi_stl = phi; % if positive/negative defines material (+/-phi). 16 | limit = 0.00001; % the sign of the limit should match the sign of phi. 17 | phi_stl(phi_stl<=limit) = -1; 18 | phi_stl(phi_stl>limit) = 1; 19 | phi_stl = smooth3(phi); 20 | 21 | [faces1, vertices1] = isosurface(gx, gy, gz, phi_stl, 0); 22 | [faces2, vertices2] = isocaps(gx, gy, gz, phi_stl, 0); 23 | faces = [faces1; length(vertices1(:,1)) + faces2]; 24 | vertices = [vertices1; vertices2]; 25 | triangles = triangulation(faces, vertices); 26 | 27 | normals = faceNormal(triangles); 28 | vertices=vertices'; faces=faces'; normals=normals'; 29 | % Transform vertices into single precision. 30 | datav = single(vertices); 31 | % Put all vertices of the faces in a single 2D matrix (3 x m). 32 | datavxyz = datav(:,faces); 33 | % Transform data to [V1, V2, V3] for each face in a (3 x 3 x m/3) matrix. 34 | dataxyz = reshape(datavxyz,3,3,numel(datavxyz)/9); 35 | % Reshape normals from (3 x m) to (3 x 1 x m) matrix. 36 | normals = reshape(normals,3,1,numel(normals)/3); 37 | % Include normal vectors: [n, V1, V2, V3] in a (3 x 4 x m/3) matrix. 38 | datanxyz = [normals(:,:,:), dataxyz(:,:,:)]; 39 | % Transform datanxyz to 16-bit unsigned integer. 40 | datanxyz = typecast(datanxyz(:), 'uint16'); 41 | % Reshape data. Each column will contain the information of one face. 42 | data = reshape(datanxyz(:), 24, numel(datanxyz)/24); 43 | % The 25th is the attribute byte count. 44 | data(25,:) = 0; 45 | 46 | % Generating stl file in Binary mode 47 | fileID = fopen(nm, 'w'); % name of stl file can be changed. 48 | % Write a title up to 80 characters. (80 bytes) 49 | fprintf(fileID, '%-80s',... 50 | 'exported using phi2stl created by P. Vogiatzis (Advisor: S. Chen)'); 51 | % Write the number of faces in 32-bit unsigned integer. 52 | fwrite(fileID, numel(data)/25, 'uint32'); 53 | % Write data. 54 | fwrite(fileID, data, 'uint16'); 55 | fclose(fileID); 56 | end 57 | -------------------------------------------------------------------------------- /supportDOFs.m: -------------------------------------------------------------------------------- 1 | function fixeddof = supportDOFs(sup_all,sup_x,sup_y,sup_z) 2 | 3 | fn1 = sup_all; fixeddof1 = reshape([fn1*3-2 fn1*3-1 fn1*3]'... 4 | ,1,3*length(fn1)); %fixed dofs in all directions 5 | 6 | fn2 = sup_x; fixeddof2 = (fn2*3-2)'; %fixed dofs in x direction 7 | 8 | fn3 = sup_y; fixeddof3 = (fn3*3-1)'; %fixed dofs in y direction 9 | 10 | fn4 = sup_z; fixeddof4 = (fn4*3)'; %fixed dofs in z direction 11 | 12 | fixeddof = union(fixeddof1,union(fixeddof2,union(fixeddof3,fixeddof4))); -------------------------------------------------------------------------------- /symmetry.m: -------------------------------------------------------------------------------- 1 | function [xg,top,fnx,fny,fnz,nelx,nely,nelz,dx,dy,dz] = symmetry(xg,ls,nelx,nely,nelz,dx,dy,dz,ngrid,symm,dir) 2 | if strcmp(symm,'x-y') 3 | if strcmp(dir,'left') 4 | [sx,sy,sz] = size(xg); xg1 = zeros(sx,sy,2*sz); xg_mir = flip(xg,3); xg1(:,:,1:sz) = xg_mir; xg1(:,:,sz+1:end) = xg; 5 | xg = xg1; 6 | nelz = 2*nelz; dz = 2*dz; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 7 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 8 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 9 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 10 | else 11 | [sx,sy,sz] = size(xg); xg1 = zeros(sx,sy,2*sz); xg_mir = flip(xg,3); xg1(:,:,1:sz) = xg; xg1(:,:,sz+1:end) = xg_mir; 12 | xg = xg1; 13 | nelz = 2*nelz; dz = 2*dz; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 14 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 15 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 16 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 17 | end 18 | elseif strcmp(symm,'y-z') 19 | if strcmp(dir,'left') 20 | [sx,sy,sz] = size(xg); xg1 = zeros(sx,2*sy,sz); xg_mir = flip(xg,2); xg1(:,1:sy,:) = xg_mir; xg1(:,sy+1:end,:) = xg; 21 | xg = xg1; 22 | nelx = 2*nelx; dx = 2*dx; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 23 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 24 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 25 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 26 | else 27 | [sx,sy,sz] = size(xg); xg1 = zeros(sx,2*sy,sz); xg_mir = flip(xg,2); xg1(:,1:sy,:) = xg; xg1(:,sy+1:end,:) = xg_mir; 28 | xg = xg1; 29 | nelx = 2*nelx; dx = 2*dx; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 30 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 31 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 32 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 33 | end 34 | elseif strcmp(symm,'z-x') 35 | if strcmp(dir,'right') 36 | [sx,sy,sz] = size(xg); xg1 = zeros(2*sx,sy,sz); xg_mir = flip(xg,1); xg1(1:sx,:,:) = xg; xg1(sx+1:end,:,:) = xg_mir; 37 | xg = xg1; 38 | nely = 2*nely; dy = 2*dy; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 39 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 40 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 41 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 42 | else 43 | [sx,sy,sz] = size(xg); xg1 = zeros(2*sx,sy,sz); xg_mir = flip(xg,1); xg1(1:sx,:,:) = xg_mir; xg1(sx+1:end,:,:) = xg; 44 | xg = xg1; 45 | nely = 2*nely; dy = 2*dy; [fnx,fny,fnz] = meshgrid(0:1/ngrid:nelx+5,0:1/ngrid:nely+5,0:1/ngrid:nelz+5); 46 | fnx(:,:,size(xg,3)+1:end) = []; fny(:,:,size(xg,3)+1:end) = []; fnz(:,:,size(xg,3)+1:end) = []; 47 | fnx(:,size(xg,2)+1:end,:) = []; fny(:,size(xg,2)+1:end,:) = []; fnz(:,size(xg,2)+1:end,:) = []; 48 | fnx(size(xg,1)+1:end,:,:) = []; fny(size(xg,1)+1:end,:,:) = []; fnz(size(xg,1)+1:end,:,:) = []; 49 | end 50 | end 51 | top = xg-ls; --------------------------------------------------------------------------------