├── .gitattributes ├── .gitignore ├── CreateFFD.glf ├── Grid.png ├── Groups.png ├── LICENSE ├── Layers.png └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /CreateFFD.glf: -------------------------------------------------------------------------------- 1 | ############################################################################# 2 | # 3 | # (C) 2021 Cadence Design Systems, Inc. All rights reserved worldwide. 4 | # 5 | # This sample script is not supported by Cadence Design Systems, Inc. 6 | # It is provided freely for demonstration purposes only. 7 | # SEE THE WARRANTY DISCLAIMER AT THE BOTTOM OF THIS FILE. 8 | # 9 | ############################################################################# 10 | 11 | # ============================================================================ 12 | # GENERATE FFD BOXES FOR SU2 13 | # ============================================================================ 14 | # Written by: Travis Carrigan, Senior Engineer, Pointwise Inc. 15 | # 16 | # This script will generate FFD boxes for use with Stanford's SU2 solver. The 17 | # boxes are constructed using the extents of grouped connectors (2D) or domains 18 | # (3D). The group names are parsed for the parameters necessary to generate the 19 | # box. Each box is assigned a layer to help the user declutter the display and 20 | # make it easier for the data to be written to the .su2 grid. 21 | # 22 | # To create a group, select the entities and go to Create, Group. The group 23 | # name should be of the following format: ffd-name-scale-dimension. 24 | # 25 | # ffd: Tag to let script know the group will be used for an FFD box 26 | # name: The name of the FFD box 27 | # scale: The x,y,z scale vector for the box scaling (default=1.2) 28 | # dimension: Polynomial degree in the i,j,k directions (>=1, default=1) 29 | # 30 | # Example group name (please note the format): ffd-wing-1.2,1.2,1.2-4,5,2 31 | # Short group name format (will use defaults): ffd-wing 32 | # 33 | # Grids created in 2D for SU2 must be in the z-plane. Therefore, when running 34 | # in 2D, only x,y compenents of the scale vector and dimension are used. Also, 35 | # to invoke 2D mode, ensure that the CAE solver dimension has been set to 2D. 36 | # 37 | # Example 2D group name (only x,y needed): ffd-wing-1.2,1.2-4,2 38 | # 39 | # The boxes are aligned with the global x,y,z axes. However, database points 40 | # are generated at nodes and all connector, domain, and block centroids so 41 | # the user can transform the box using the Edit, Transform menu. These points 42 | # can be used as anchor points during various transformations. 43 | # 44 | # When the script is run it will check to see if there are any "FFD Layers" 45 | # where an FFD layer is a layer with a description of the form: FFD - XXX. 46 | # The script will then ensure that all "FFD groups" have been used to create 47 | # their respective FFD boxes and layers. Lastly, the script will export all 48 | # FFD boxes to a file containing all necessary FFD information for SU2. The 49 | # file name will be ffd.su2, The script will only export the FFD boxes when 50 | # there is a one-to-one match between the groups and the layers. Therefore, 51 | # the script must be run at least twice to create all the FFD boxes and then 52 | # export them for SU2. 53 | # 54 | # Because the script looks to export all FFD layers whenever it is executed, 55 | # FFD layers you would not like to export should be tagged "FFDx" in the 56 | # layer description field. For example, 57 | # 58 | # Script will export the following layer: FFD - wing 59 | # Script will not export the following layer: FFDx - wing 60 | # 61 | # Hint: You can organize the layers by name by clicking the Description 62 | # column in the Layer panel. 63 | # 64 | # Typically, you'll run this script after you've completed the grid, setup 65 | # all the CAE boundary conditions, and exported the .su2 grid file. Once 66 | # you create the FFD boxes and export the data, simply concatenate the two 67 | # files together like the following example, 68 | # 69 | # cat grid.su2 ffd.su2 > aircraft.su2 70 | # 71 | 72 | package require PWI_Glyph 73 | 74 | # PROCS 75 | 76 | # Get the extents (min, max corner points) for a list of entities 77 | proc GetExtents {ents} { 78 | 79 | set entExtents [pwu::Extents empty] 80 | 81 | foreach ent $ents { 82 | set entExtents [pwu::Extents enclose $entExtents [$ent getExtents]] 83 | } 84 | 85 | return $entExtents 86 | 87 | } 88 | 89 | # Create a connector given two points and a dimension 90 | proc CreateCon {pt1 pt2 dim} { 91 | 92 | set seg [pw::SegmentSpline create] 93 | $seg addPoint $pt1 94 | $seg addPoint $pt2 95 | 96 | set con [pw::Connector create] 97 | $con addSegment $seg 98 | $con setDimension $dim 99 | 100 | CreateMidPoint $con 101 | CreateEndPoints $con 102 | 103 | $con setRenderAttribute ColorMode Entity 104 | $con setColor [list 1.0 1.0 1.0] 105 | 106 | return $con 107 | 108 | } 109 | 110 | # Create a structured domain from a list of connectors 111 | proc CreateDom {cons} { 112 | 113 | set dom [pw::DomainStructured createFromConnectors $cons] 114 | 115 | CreateMidPoint $dom 116 | 117 | $dom setRenderAttribute ColorMode Entity 118 | $dom setColor [list 1.0 1.0 1.0] 119 | 120 | return $dom 121 | 122 | } 123 | 124 | # Create a structured block given the block extents and dimensions 125 | proc CreateBlock {extents dims} { 126 | 127 | set dimension [GetDimension] 128 | 129 | if {$dimension == 2} { 130 | 131 | set dim(x) [expr {[lindex $dims 0] + 1}] 132 | set dim(y) [expr {[lindex $dims 1] + 1}] 133 | 134 | set pt(1) [lindex $extents 0] 135 | set pt(7) [lindex $extents 1] 136 | 137 | set pt1(x) [pwu::Vector3 x $pt(1)] 138 | set pt1(y) [pwu::Vector3 y $pt(1)] 139 | set pt1(z) [pwu::Vector3 z $pt(1)] 140 | 141 | set pt7(x) [pwu::Vector3 x $pt(7)] 142 | set pt7(y) [pwu::Vector3 y $pt(7)] 143 | set pt7(z) [pwu::Vector3 z $pt(7)] 144 | 145 | set pt(2) "$pt7(x) $pt1(y) $pt1(z)" 146 | set pt(3) "$pt7(x) $pt7(y) $pt1(z)" 147 | set pt(4) "$pt1(x) $pt7(y) $pt1(z)" 148 | 149 | set mode [pw::Application begin Create] 150 | 151 | set con(1) [CreateCon $pt(1) $pt(2) $dim(x)] 152 | set con(2) [CreateCon $pt(2) $pt(3) $dim(y)] 153 | set con(3) [CreateCon $pt(3) $pt(4) $dim(x)] 154 | set con(4) [CreateCon $pt(4) $pt(1) $dim(y)] 155 | 156 | set block [CreateDom [list $con(1) $con(2) $con(3) $con(4)]] 157 | 158 | $mode end 159 | 160 | } else { 161 | 162 | set dim(x) [expr {[lindex $dims 0] + 1}] 163 | set dim(y) [expr {[lindex $dims 1] + 1}] 164 | set dim(z) [expr {[lindex $dims 2] + 1}] 165 | 166 | set pt(1) [lindex $extents 0] 167 | set pt(7) [lindex $extents 1] 168 | 169 | set pt1(x) [pwu::Vector3 x $pt(1)] 170 | set pt1(y) [pwu::Vector3 y $pt(1)] 171 | set pt1(z) [pwu::Vector3 z $pt(1)] 172 | 173 | set pt7(x) [pwu::Vector3 x $pt(7)] 174 | set pt7(y) [pwu::Vector3 y $pt(7)] 175 | set pt7(z) [pwu::Vector3 z $pt(7)] 176 | 177 | set pt(2) "$pt7(x) $pt1(y) $pt1(z)" 178 | set pt(3) "$pt7(x) $pt7(y) $pt1(z)" 179 | set pt(4) "$pt1(x) $pt7(y) $pt1(z)" 180 | set pt(5) "$pt1(x) $pt1(y) $pt7(z)" 181 | set pt(6) "$pt7(x) $pt1(y) $pt7(z)" 182 | set pt(8) "$pt1(x) $pt7(y) $pt7(z)" 183 | 184 | set mode [pw::Application begin Create] 185 | 186 | set con(1) [CreateCon $pt(1) $pt(2) $dim(x)] 187 | set con(2) [CreateCon $pt(2) $pt(3) $dim(y)] 188 | set con(3) [CreateCon $pt(3) $pt(4) $dim(x)] 189 | set con(4) [CreateCon $pt(4) $pt(1) $dim(y)] 190 | set con(5) [CreateCon $pt(5) $pt(6) $dim(x)] 191 | set con(6) [CreateCon $pt(6) $pt(7) $dim(y)] 192 | set con(7) [CreateCon $pt(7) $pt(8) $dim(x)] 193 | set con(8) [CreateCon $pt(8) $pt(5) $dim(y)] 194 | set con(9) [CreateCon $pt(1) $pt(5) $dim(z)] 195 | set con(10) [CreateCon $pt(2) $pt(6) $dim(z)] 196 | set con(11) [CreateCon $pt(3) $pt(7) $dim(z)] 197 | set con(12) [CreateCon $pt(4) $pt(8) $dim(z)] 198 | 199 | set dom(1) [CreateDom [list $con(1) $con(2) $con(3) $con(4)]] 200 | set dom(2) [CreateDom [list $con(5) $con(8) $con(7) $con(6)]] 201 | set dom(3) [CreateDom [list $con(4) $con(12) $con(8) $con(9)]] 202 | set dom(4) [CreateDom [list $con(2) $con(10) $con(6) $con(11)]] 203 | set dom(5) [CreateDom [list $con(1) $con(9) $con(5) $con(10)]] 204 | set dom(6) [CreateDom [list $con(3) $con(11) $con(7) $con(12)]] 205 | 206 | set block [pw::BlockStructured createFromDomains \ 207 | [list $dom(1) $dom(2) $dom(3) $dom(4) $dom(5) $dom(6)]] 208 | 209 | CreateMidPoint $block 210 | 211 | $mode end 212 | 213 | } 214 | 215 | return $block 216 | 217 | } 218 | 219 | # Get block corner points and dimensions 220 | proc GetFFDBoxAttributes {box} { 221 | 222 | set dimension [GetDimension] 223 | set dims [$box getDimensions] 224 | 225 | if {$dimension == 2} { 226 | 227 | set i(dim) [lindex $dims 0] 228 | set j(dim) [lindex $dims 1] 229 | 230 | set i(deg) [expr {$i(dim) - 1}] 231 | set j(deg) [expr {$j(dim) - 1}] 232 | 233 | set i(min) 1 234 | set j(min) 1 235 | 236 | set i(max) $i(dim) 237 | set j(max) $j(dim) 238 | 239 | set pt(1) [lrange [$box getXYZ "$i(min) $j(min)"] 0 1] 240 | set pt(2) [lrange [$box getXYZ "$i(max) $j(min)"] 0 1] 241 | set pt(3) [lrange [$box getXYZ "$i(max) $j(max)"] 0 1] 242 | set pt(4) [lrange [$box getXYZ "$i(min) $j(max)"] 0 1] 243 | 244 | return [list [list $i(deg) $j(deg)] \ 245 | [list $pt(1) $pt(2) $pt(3) $pt(4)]] 246 | 247 | } else { 248 | 249 | set i(dim) [lindex $dims 0] 250 | set j(dim) [lindex $dims 1] 251 | set k(dim) [lindex $dims 2] 252 | 253 | set i(deg) [expr {$i(dim) - 1}] 254 | set j(deg) [expr {$j(dim) - 1}] 255 | set k(deg) [expr {$k(dim) - 1}] 256 | 257 | set i(min) 1 258 | set j(min) 1 259 | set k(min) 1 260 | 261 | set i(max) $i(dim) 262 | set j(max) $j(dim) 263 | set k(max) $k(dim) 264 | 265 | set pt(1) [$box getXYZ "$i(min) $j(min) $k(min)"] 266 | set pt(2) [$box getXYZ "$i(max) $j(min) $k(min)"] 267 | set pt(3) [$box getXYZ "$i(max) $j(max) $k(min)"] 268 | set pt(4) [$box getXYZ "$i(min) $j(max) $k(min)"] 269 | set pt(5) [$box getXYZ "$i(min) $j(min) $k(max)"] 270 | set pt(6) [$box getXYZ "$i(max) $j(min) $k(max)"] 271 | set pt(7) [$box getXYZ "$i(max) $j(max) $k(max)"] 272 | set pt(8) [$box getXYZ "$i(min) $j(max) $k(max)"] 273 | 274 | return [list [list $i(deg) $j(deg) $k(deg)] \ 275 | [list $pt(1) $pt(2) $pt(3) $pt(4) \ 276 | $pt(5) $pt(6) $pt(7) $pt(8)]] 277 | 278 | } 279 | 280 | } 281 | 282 | # Create a database point at an entity midpoint location 283 | proc CreateMidPoint {ent} { 284 | 285 | CreateDbPoint [GetEntityCenter $ent] 286 | 287 | } 288 | 289 | # Create a database point at connector nodes 290 | proc CreateEndPoints {con} { 291 | 292 | CreateDbPoint [$con getXYZ -arc 0.0] 293 | CreateDbPoint [$con getXYZ -arc 1.0] 294 | 295 | } 296 | 297 | # General procedure for creating a database point 298 | proc CreateDbPoint {pt} { 299 | 300 | set dbPt [pw::Point create] 301 | $dbPt setPoint $pt 302 | 303 | } 304 | 305 | # Get the center of an extents vector 306 | proc GetExtentsCenter {extents} { 307 | 308 | set minExtents [lindex $extents 0] 309 | set maxExtents [lindex $extents 1] 310 | 311 | set center [pwu::Vector3 divide [pwu::Vector3 add $minExtents $maxExtents] 2.0] 312 | 313 | return $center 314 | 315 | } 316 | 317 | # Return the center of an entity, useful for scaling operations 318 | proc GetEntityCenter {ent} { 319 | 320 | set extents [GetExtents $ent] 321 | set center [GetExtentsCenter $extents] 322 | 323 | return $center 324 | 325 | } 326 | 327 | # Scale the extents of an object by a scaling vector 328 | proc ScaleExtents {extents n} { 329 | 330 | set minExtents [lindex $extents 0] 331 | set maxExtents [lindex $extents 1] 332 | 333 | set center [GetExtentsCenter $extents] 334 | 335 | set transformMin [pwu::Transform apply [pwu::Transform scaling -anchor $center $n] $minExtents] 336 | set transformMax [pwu::Transform apply [pwu::Transform scaling -anchor $center $n] $maxExtents] 337 | 338 | return [list $transformMin $transformMax] 339 | 340 | } 341 | 342 | # Get the layer entity count 343 | proc GetLayerEntityCount {layer} { 344 | 345 | set entCnts [pw::Layer getLayerEntityCounts $layer] 346 | set totalEntCnt 0 347 | 348 | foreach entCnt $entCnts { 349 | set totalEntCnt [incr totalEntCnt $entCnt] 350 | } 351 | 352 | return $totalEntCnt 353 | 354 | } 355 | 356 | # Retrieve a free layer from the layer manager 357 | proc GetFreeLayer {} { 358 | 359 | set numLayers [pw::Layer getCount] 360 | set layer [expr {$numLayers - 1}] 361 | 362 | while {$layer >= 0} { 363 | 364 | set totalEntCnt [GetLayerEntityCount $layer] 365 | 366 | if {$totalEntCnt} { 367 | incr layer -1 368 | continue 369 | } else { 370 | break 371 | } 372 | 373 | } 374 | 375 | return $layer 376 | 377 | } 378 | 379 | # Get all the domain groups 380 | proc GetGroups {} { 381 | 382 | set dimension [GetDimension] 383 | 384 | if {$dimension == 2} { 385 | set groups [pw::Group getAll -type pw::Connector] 386 | } else { 387 | set groups [pw::Group getAll -type pw::Domain] 388 | } 389 | 390 | return $groups 391 | 392 | } 393 | 394 | # Return only the groups appended with "ffd" 395 | proc GetFFDGroups {} { 396 | 397 | set groups [GetGroups] 398 | set ffdGroups [list] 399 | 400 | foreach group $groups { 401 | 402 | set groupName [$group getName] 403 | set params [split $groupName "-"] 404 | 405 | if {[lindex $params 0] eq "ffd"} { 406 | lappend ffdGroups $group 407 | } 408 | 409 | } 410 | 411 | return $ffdGroups 412 | 413 | } 414 | 415 | # Return only layers prepended with "FFD" 416 | proc GetFFDLayers {} { 417 | 418 | set numLayers [pw::Layer getCount] 419 | set ffdLayers [list] 420 | 421 | for {set layer 0} {$layer < $numLayers} {incr layer} { 422 | 423 | set layerName [pw::Layer getDescription $layer] 424 | if {[string range $layerName 0 2] eq "FFD"} { 425 | lappend ffdLayers $layer 426 | } 427 | 428 | 429 | } 430 | 431 | return $ffdLayers 432 | 433 | } 434 | 435 | # Return FFD layers that contain entities 436 | proc GetFullFFDLayers {} { 437 | 438 | set ffdLayers [GetFFDLayers] 439 | set fullFFDLayers [list] 440 | 441 | foreach ffdLayer $ffdLayers { 442 | 443 | set entCnt [GetLayerEntityCount $ffdLayer] 444 | 445 | if {$entCnt} { 446 | lappend fullFFDLayers $ffdLayer 447 | } 448 | 449 | } 450 | 451 | return $fullFFDLayers 452 | 453 | } 454 | 455 | # Return FFD layers tagged "FFD" for export 456 | proc GetExportFFDLayers {} { 457 | 458 | set fullFFDLayers [GetFullFFDLayers] 459 | set exportFFDLayers [list] 460 | 461 | foreach layer $fullFFDLayers { 462 | 463 | set layerName [pw::Layer getDescription $layer] 464 | if {[string range $layerName 0 3] eq "FFD-"} { 465 | lappend exportFFDLayers $layer 466 | } 467 | 468 | } 469 | 470 | return $exportFFDLayers 471 | 472 | } 473 | 474 | # Get the FFD box name from the group name 475 | proc GetFFDNameFromGroup {group} { 476 | 477 | set fullName [$group getName] 478 | set params [split $fullName "-"] 479 | set ffdName [lindex $params 1] 480 | 481 | return $ffdName 482 | 483 | } 484 | 485 | # Get the FFD box name from the layer name 486 | proc GetFFDNameFromLayer {layer} { 487 | 488 | set fullName [pw::Layer getDescription $layer] 489 | set params [split $fullName "-"] 490 | set ffdName [lindex $params 1] 491 | 492 | return $ffdName 493 | 494 | } 495 | 496 | # Get the group from the FFD name 497 | proc GetFFDGroupFromName {name} { 498 | 499 | set ffdGroups [GetFFDGroups] 500 | 501 | foreach group $ffdGroups { 502 | 503 | set ffdName [GetFFDNameFromGroup $group] 504 | 505 | if {$name eq $ffdName} { 506 | return $group 507 | } 508 | 509 | } 510 | 511 | } 512 | 513 | # Return FFD boxes (blocks) from FFD layers tagged for export 514 | proc GetExportFFDBoxes {} { 515 | 516 | set dimension [GetDimension] 517 | 518 | set exportFFDLayers [GetExportFFDLayers] 519 | set boxes [list] 520 | 521 | foreach layer $exportFFDLayers { 522 | 523 | if {$dimension == 2} { 524 | lappend boxes [pw::Layer getLayerEntities -type pw::DomainStructured $layer] 525 | } else { 526 | lappend boxes [pw::Layer getLayerEntities -type pw::BlockStructured $layer] 527 | } 528 | } 529 | 530 | return $boxes 531 | 532 | } 533 | 534 | # Given a group, return the parameters required to build the FFD box 535 | proc GetParameters {group} { 536 | 537 | set dimension [GetDimension] 538 | set groupName [$group getName] 539 | set params [split $groupName "-"] 540 | 541 | set ffdName [lindex $params 1] 542 | 543 | if {[llength $params] > 2} { 544 | 545 | set ffdScale [lindex $params 2] 546 | set ffdDim [lindex $params 3] 547 | 548 | if {$dimension == 2} { 549 | 550 | set ffdScale "$ffdScale,0" 551 | set ffdDim "$ffdDim,0" 552 | 553 | } 554 | 555 | } else { 556 | 557 | if {$dimension == 2} { 558 | 559 | set ffdScale "1.2,1.2,0" 560 | set ffdDim "1,1,0" 561 | 562 | } else { 563 | 564 | set ffdScale "1.2,1.2,1.2" 565 | set ffdDim "1,1,1" 566 | 567 | } 568 | 569 | } 570 | 571 | set ffdScale [split $ffdScale ","] 572 | set ffdDim [split $ffdDim ","] 573 | 574 | set ffdDoms [$group getEntityList] 575 | 576 | return [list $ffdName $ffdScale $ffdDim $ffdDoms] 577 | 578 | } 579 | 580 | # Write the FFD data to a file named ffd.su2 581 | proc ExportFFD {} { 582 | 583 | set dimension [GetDimension] 584 | 585 | set cwd [file dirname [info script]] 586 | set filename "$cwd/ffd.su2" 587 | set f [open $filename w] 588 | set boxes [GetExportFFDBoxes] 589 | set id 0 590 | 591 | puts $f "FFD_NBOX= [llength $boxes]" 592 | puts $f "FFD_NLEVEL= 1" 593 | 594 | foreach box $boxes { 595 | 596 | set boxAttributes [GetFFDBoxAttributes $box] 597 | set ffdDegree [lindex $boxAttributes 0] 598 | set cornerPoints [lindex $boxAttributes 1] 599 | 600 | if {$dimension == 2} { 601 | 602 | set ideg [lindex $ffdDegree 0] 603 | set jdeg [lindex $ffdDegree 1] 604 | 605 | set pt1 [lindex $cornerPoints 0] 606 | set pt2 [lindex $cornerPoints 1] 607 | set pt3 [lindex $cornerPoints 2] 608 | set pt4 [lindex $cornerPoints 3] 609 | 610 | puts $f "FFD_TAG= $id" 611 | puts $f "FFD_LEVEL= 0" 612 | puts $f "FFD_DEGREE_I= $ideg" 613 | puts $f "FFD_DEGREE_J= $jdeg" 614 | puts $f "FFD_PARENTS= 0" 615 | puts $f "FFD_CHILDREN= 0" 616 | puts $f "FFD_CORNER_POINTS=4" 617 | puts $f "$pt1" 618 | puts $f "$pt2" 619 | puts $f "$pt3" 620 | puts $f "$pt4" 621 | puts $f "FFD_CONTROL_POINTS=0" 622 | puts $f "FFD_SURFACE_POINTS=0" 623 | incr id 624 | 625 | } else { 626 | 627 | set ideg [lindex $ffdDegree 0] 628 | set jdeg [lindex $ffdDegree 1] 629 | set kdeg [lindex $ffdDegree 2] 630 | 631 | set pt1 [lindex $cornerPoints 0] 632 | set pt2 [lindex $cornerPoints 1] 633 | set pt3 [lindex $cornerPoints 2] 634 | set pt4 [lindex $cornerPoints 3] 635 | set pt5 [lindex $cornerPoints 4] 636 | set pt6 [lindex $cornerPoints 5] 637 | set pt7 [lindex $cornerPoints 6] 638 | set pt8 [lindex $cornerPoints 7] 639 | 640 | puts $f "FFD_TAG= $id" 641 | puts $f "FFD_LEVEL= 0" 642 | puts $f "FFD_DEGREE_I= $ideg" 643 | puts $f "FFD_DEGREE_J= $jdeg" 644 | puts $f "FFD_DEGREE_K= $kdeg" 645 | puts $f "FFD_PARENTS= 0" 646 | puts $f "FFD_CHILDREN= 0" 647 | puts $f "FFD_CORNER_POINTS=8" 648 | puts $f "$pt1" 649 | puts $f "$pt2" 650 | puts $f "$pt3" 651 | puts $f "$pt4" 652 | puts $f "$pt5" 653 | puts $f "$pt6" 654 | puts $f "$pt7" 655 | puts $f "$pt8" 656 | puts $f "FFD_CONTROL_POINTS=0" 657 | puts $f "FFD_SURFACE_POINTS=0" 658 | incr id 659 | 660 | } 661 | 662 | } 663 | 664 | close $f 665 | 666 | } 667 | 668 | # Find the relative complement of list2 with respect to list1 669 | proc ListComplement { list1 list2 } { 670 | 671 | set common [list] 672 | 673 | foreach item $list1 { 674 | 675 | set found 0 676 | 677 | if {[lsearch -exact $list2 $item] < 0} { 678 | set found 1 679 | } 680 | 681 | if {$found} { 682 | lappend common $item 683 | } 684 | 685 | } 686 | 687 | return $common 688 | 689 | } 690 | 691 | # Return CAE solver dimension 692 | proc GetDimension {} { 693 | 694 | return [pw::Application getCAESolverDimension] 695 | 696 | } 697 | 698 | # MAIN 699 | 700 | set allFFDGroups [GetFFDGroups] 701 | set fullFFDLayers [GetFullFFDLayers] 702 | 703 | if {![llength $allFFDGroups] && ![llength $fullFFDLayers]} { 704 | 705 | puts "No FFD groups or FFD layers found." 706 | exit 707 | 708 | } else { 709 | 710 | set allFFDGroupNames [list] 711 | set allFFDLayerNames [list] 712 | set ffdGroups [list] 713 | 714 | foreach ffdGroup $allFFDGroups { 715 | lappend allFFDGroupNames [GetFFDNameFromGroup $ffdGroup] 716 | } 717 | foreach ffdLayer $fullFFDLayers { 718 | lappend allFFDLayerNames [GetFFDNameFromLayer $ffdLayer] 719 | } 720 | 721 | set ffdGroupNames [ListComplement $allFFDGroupNames $allFFDLayerNames] 722 | 723 | foreach ffdGroupName $ffdGroupNames { 724 | lappend ffdGroups [GetFFDGroupFromName $ffdGroupName] 725 | } 726 | 727 | } 728 | 729 | if {![llength $ffdGroups]} { 730 | ExportFFD 731 | exit 732 | } 733 | 734 | set originalLayer [pw::Display getCurrentLayer] 735 | 736 | # Loop through each FFD group, create the structured domain or block 737 | # and assign the domain or block to a free layer. 738 | foreach group $ffdGroups { 739 | 740 | set layer [GetFreeLayer] 741 | pw::Display setCurrentLayer $layer 742 | 743 | set ffdParams [GetParameters $group] 744 | 745 | set ffdName [lindex $ffdParams 0] 746 | set ffdScale [lindex $ffdParams 1] 747 | set ffdDim [lindex $ffdParams 2] 748 | set ffdDoms [lindex $ffdParams 3] 749 | 750 | set extents [ScaleExtents [GetExtents $ffdDoms] $ffdScale] 751 | set ffd [CreateBlock $extents $ffdDim] 752 | 753 | pw::Layer setDescription $layer "FFD-$ffdName" 754 | 755 | } 756 | 757 | pw::Display setCurrentLayer $originalLayer 758 | 759 | # END SCRIPT 760 | 761 | ############################################################################# 762 | # 763 | # This file is licensed under the Cadence Public License Version 1.0 (the 764 | # "License"), a copy of which is found in the included file named "LICENSE", 765 | # and is distributed "AS IS." TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE 766 | # LAW, CADENCE DISCLAIMS ALL WARRANTIES AND IN NO EVENT SHALL BE LIABLE TO 767 | # ANY PARTY FOR ANY DAMAGES ARISING OUT OF OR RELATING TO USE OF THIS FILE. 768 | # Please see the License for the full text of applicable terms. 769 | # 770 | ############################################################################# 771 | -------------------------------------------------------------------------------- /Grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pointwise/CreateFFD/348ac31a62855fce4c09be4b4e13454c49834678/Grid.png -------------------------------------------------------------------------------- /Groups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pointwise/CreateFFD/348ac31a62855fce4c09be4b4e13454c49834678/Groups.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Cadence Public License Version 1.0 2 | 3 | May 2021 4 | 5 | BY USING, REPRODUCING, DISTRIBUTING, AND/OR OTHERWISE ACCESSING THIS 6 | TECHNOLOGY, YOU AGREE TO THE TERMS OF THIS CADENCE PUBLIC LICENSE 7 | VERSION 1.0 (THE "AGREEMENT"). 8 | 9 | 1. License Grant. Subject to the terms and conditions specified herein, You 10 | are hereby granted a perpetual, non-exclusive, no-charge license to Use the 11 | Technology for any purpose, which includes without limitation, the right to 12 | access, copy, modify, merge, publish, redistribute, sublicense, and/or sell 13 | (collectively, to "Use") copies of the Technology. 14 | 15 | 2. Distributions. 16 | 17 | i. Copyright Notice and Agreement Text. The Copyright Notice and the full 18 | text of this Cadence Public License Version 1.0 shall be included in all 19 | copies of the Technology, or substantial portions thereof. (For clarity, if 20 | You are redistributing the Technology in source code form, then you 21 | must ensure that the Copyright Notice and Agreement are left intact; if 22 | You are redistributing the Technology in binary or non-source code 23 | form, then you must ensure that the Copyright Notice and Agreement are 24 | reproduced and included in an attribution file, such as a "NOTICES", 25 | "COPYRIGHT", or other equivalent file.) 26 | 27 | ii. Modifications. Any distribution of modified Technology must include a 28 | prominent notice in the corresponding file(s) indicating that the file(s) 29 | was modified by You. You may license Your modifications under different 30 | or additional license terms. 31 | 32 | 3. Endorsement. Neither the name of the copyright holder nor the names of its 33 | contributors may be used to endorse or promote products derived from the 34 | Technology without specific prior written permission. 35 | 36 | 4. Trademarks. Except for attribution purposes (as described in Sec. 2(i), 37 | above) and as may otherwise be agreed in writing, You do not have 38 | permission to use the trade names, trademarks, service marks, or product 39 | names of the copyright holder or contributor(s). 40 | 41 | 5. Termination. 42 | 43 | i. Patent Litigation. If You institute patent litigation against any entity 44 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 45 | Technology constitutes direct or contributory patent infringement, then any 46 | patent licenses granted to You under this Agreement shall terminate as of 47 | the date such litigation is filed. 48 | 49 | ii. Breach. This Agreement, and the license granted hereunder, shall 50 | immediately terminate if You (a) fail to comply with any material terms or 51 | conditions and (b) fail to cure your lack of compliance within a reasonable 52 | time of becoming aware. 53 | 54 | 6. Disclaimer or Warranty. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 55 | WRITING, THE TECHNOLOGY IS PROVIDED BY THE COPYRIGHT HOLDERS AND 56 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 57 | NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 58 | PARTICULAR PURPOSE ARE DISCLAIMED. 59 | 60 | 7. Limitation of Liability. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 61 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 62 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 63 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 64 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 65 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 66 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 67 | TECHNOLOGY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 68 | 69 | 8. Stewardship. While everyone is permitted to copy and distribute copies of 70 | this Agreement, it may only be modified by the Steward. The Steward may 71 | publish new versions (including revisions) of the Agreement. For clarity, 72 | the Technology may always be distributed subject to the version of the 73 | Agreement under which it was received. In addition, after a new version of 74 | the Agreement is published, Licensor may elect to distribute the Technology 75 | under the new version. 76 | 77 | 9. Definitions. 78 | 79 | i. "Copyright Notice" means a notice that includes the name of the 80 | copyright holder, a copyright symbol (or equivalent), a date, and, at the 81 | owner's election, a statement of rights. 82 | 83 | ii. "Licensee" means any person or entity that receives the Technology 84 | under this Agreement. 85 | 86 | iii. "Licensor" means any person or entity that distributes the Technology. 87 | 88 | iv. "Steward" means Cadence Design Systems, Inc., which may assign the 89 | responsibility to serve as Steward to a suitable separate entity. 90 | 91 | v. "Technology" means the work of authorship, whether in source code, 92 | binary, or other form, made available under this Agreement, as indicated by 93 | a Copyright Notice that is included in or attached to the Technology. 94 | 95 | vi. "You" (or "Your") means a person or entity exercising permissions 96 | granted by this Agreement. 97 | -------------------------------------------------------------------------------- /Layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pointwise/CreateFFD/348ac31a62855fce4c09be4b4e13454c49834678/Layers.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CreateFFD 2 | Copyright 2021 Cadence Design Systems, Inc. All rights reserved worldwide. 3 | 4 | This script will generate FFD boxes for use with Stanford's SU2 solver. The 5 | boxes are constructed using the extents of grouped connectors (2D) or domains 6 | (3D). The group names are parsed for the parameters necessary to generate the 7 | box. Each box is assigned a layer to help the user declutter the display and 8 | make it easier for the data to be written to the .su2 grid file. 9 | 10 | ![ScriptImage](https://raw.github.com/pointwise/CreateFFD/master/Grid.png) 11 | 12 | To create a group, select the entities and go to *Create, Group*. The group 13 | name should be of the following format: **ffd-name-scale-dimension**. 14 | 15 | |Parameter |Description | 16 | |:-----------|:-----------------------------------------------------------| 17 | |*ffd* |Tag to let script know the group will be used for an FFD box| 18 | |*name* |The name of the FFD box | 19 | |*scale* |The x,y,z scale vector for the box scaling (default=1.2) | 20 | |*dimension* |Polynomial degree in the i,j,k directions (>=1, default=1) | 21 | 22 | Example group name (please note the format): ffd-wing-1.2,1.2,1.2-4,5,2 23 | Short group name format (will use defaults): ffd-wing 24 | 25 | ![ScriptImage](https://raw.github.com/pointwise/CreateFFD/master/Groups.png) 26 | 27 | Grids created in 2D for SU2 must be in the z-plane. Therefore, when running 28 | in 2D, only x,y compenents of the scale vector and dimension are used. Also, 29 | to invoke 2D mode, ensure that the CAE solver dimension has been set to 2D. 30 | 31 | Example 2D group name (only x,y needed): ffd-wing-1.2,1.2-4,2 32 | 33 | The boxes are aligned with the global x,y,z axes. However, database points 34 | are generated at nodes and all connector, domain, and block centroids so 35 | the user can transform the box using the Edit, Transform menu. These points 36 | can be used as anchor points during various transformations. 37 | 38 | When the script is run it will check to see if there are any *FFD Layers* 39 | where an FFD layer is a layer with a description of the form: *FFD - XXX*. 40 | The script will then ensure that all *FFD groups* have been used to create 41 | their respective FFD boxes and layers. Lastly, the script will export all 42 | FFD boxes to a file containing all necessary FFD information for SU2. The 43 | file name will be ffd.su2, The script will only export the FFD boxes when 44 | there is a one-to-one match between the groups and the layers. Therefore, 45 | the script must be run at least twice to create all the FFD boxes and then 46 | export them for SU2. 47 | 48 | Because the script looks to export all FFD layers whenever it is executed, 49 | FFD layers you would not like to export should be tagged *FFDx* in the 50 | layer description field. For example, 51 | 52 | Script will export the following layer: FFD - wing 53 | Script will not export the following layer: FFDx - wing 54 | 55 | *Hint: You can organize the layers by name by clicking the Description 56 | column in the Layer panel.* 57 | 58 | ![ScriptImage](https://raw.github.com/pointwise/CreateFFD/master/Layers.png) 59 | 60 | Typically, you'll run this script after you've completed the grid, setup 61 | all the CAE boundary conditions, and exported the .su2 grid file. Once 62 | you create the FFD boxes and export the data, simply concatenate the two 63 | files together like the following example, 64 | 65 | cat grid.su2 ffd.su2 > aircraft.su2 66 | 67 | ## Disclaimer 68 | This file is licensed under the Cadence Public License Version 1.0 (the "License"), a copy of which is found in the LICENSE file, and is distributed "AS IS." 69 | TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, CADENCE DISCLAIMS ALL WARRANTIES AND IN NO EVENT SHALL BE LIABLE TO ANY PARTY FOR ANY DAMAGES ARISING OUT OF OR RELATING TO USE OF THIS FILE. 70 | Please see the License for the full text of applicable terms. 71 | --------------------------------------------------------------------------------