Skip to content
This repository has been archived by the owner on Aug 11, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1 from acrovato/ci_shippable
Browse files Browse the repository at this point in the history
Hotfixes
  • Loading branch information
acrovato authored Apr 3, 2019
2 parents 562f6d4 + 97d5148 commit a8300b0
Showing 9 changed files with 339 additions and 44 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -7,14 +7,55 @@ ULiege, 2018-2019
## Features and limitations

### What is GeoGen?
This is it!
GeoGen is a python code that generates an aerodynamic geometry which can then be meshed in gmsh and used in CFD solvers.

### What can GeoGen do?
GeoGen currently supports the following configurations:
- [x] Arbitrary isolated wing
- [x] Sharp trailing edge
- [x] Cutoff wingtip
- [ ] Rounded wingtip
- [ ] Generic fuselage
- [ ] Horizontal tail
- [ ] Multibody (e.g. several isolated wings)
GeoGen was primarly designed to be used with [SU2](https://github.com/su2code/SU2) and [waves](https://github.com/ulgltas/waves), but any solver interfaced with [gmsh](http://gmsh.info/) can be used!

### Cite us!
If you use this work, please acknowledge the authors:
```text
"GeoGen - a python/gmsh automated geometry generator for Flow and SU2 CFD solvers" by Adrien Crovato, University of Liege
"GeoGen - a python/gmsh automated geometry generator for CFD solvers" by Adrien Crovato, University of Liege
```

## Usage
The script is run through the command line:
```sh
python geoGen.py -m path/to/config/file <-o path/to/output/file>
```
Note that the extensions (.py and .geo) are automatically handled by the code and should to be provided in the path.
If no output file is provided, a workspace directory will be created and the geometry will be stored inside as grid.geo.

The geometry is generated from a python file containing a dictionary of parameters. Examples are given in [config](config/) and the main options are summurized hereunder.
**Parameters**
Wing definition:
- airfPath: relative path to the airfoils directory
- airfName: array (size: nP+1) of names of file containing airfoil (Selig formatted) coordinates
- span: array (size: nP) of span for each planform of the wing
- taper: array (size: nP)) of taper of each planform of the wing
- sweep: array (size: nP) of leading edge sweep of each planform of the wing
- dihedral: array (size: nP) of dihedral angle of each planform of the wing
- twist: array (size: nP+1) of twist angle of each airfoil of the wing
- rootChord: root chord (scalar) of the wing
- offset: array of x and z offset (size: 2) applied to the leading edge of the root section
- coWingtip: boolean, True for cutoof wingtip, Fasle for rounded wingtip (not supported yet)
Domain definition:
- domType: string, box for box-shaped domain or shpere for shperical-shaped domain
if domain type is shpere, typical for Euler equations:
- rSphere: radius of the sphere (scalar)
if domain type is a box (a wake will then be defined), typically for potential equations:
- xoBox: x-coordinate (scalar) of the origin of the box
- xfBox: x-coordinate (scalar) of the end of the box
- yfBox: y-coordinate (scalar) of the end of the box
- zoBox: z-coordinate (scalar) of the origin of the box
- zfBox: z-coordinate (scalar) of the end of the box
- nSlope: number (scalar) of airfoil geometrical points counted from TE used to compute wake slope

### Input
12 changes: 4 additions & 8 deletions config/onera.py
Original file line number Diff line number Diff line change
@@ -16,14 +16,10 @@ def getParams():
p['dihedral'] = [0] # dihedral angle of each planform (size: nP)
p['twist'] = [0, 0] # twist angle of each airfoil (size: nP+1)
p['rootChord'] = 0.8059 # root chord
p['offset'] = [0., 0.] # x and z offset at the leading edge root
p['coWingtip'] = True # cut-off wingtip (not supported yet)
# Box
p['xoBox'] = -3.5*p['rootChord']
p['xfBox'] = 4.5*p['rootChord']
p['yfBox'] = 2*sum(p['span'])
p['zoBox'] = -3.5*p['rootChord']
p['zfBox'] = 3.5*p['rootChord']
# Wake
p['nSlope'] = 10 # number of airfoil TE points to compute wake slope
# Sphere
p['domType'] = 'sphere' # domain type ('sphere' or 'box')
p['rSphere'] = 50*p['rootChord']

return p
2 changes: 2 additions & 0 deletions config/rae.py
Original file line number Diff line number Diff line change
@@ -16,8 +16,10 @@ def getParams():
p['dihedral'] = [2., 1.] # dihedral angle of each planform (size: nP)
p['twist'] = [1., 0, -1.] # twist angle of each airfoil (size: nP+1)
p['rootChord'] = 1.0 # root chord
p['offset'] = [0., -0.1] # x and z offset at the leading edge root
p['coWingtip'] = True # cut-off wingtip (not supported yet)
# Box
p['domType'] = 'box' # domain type ('sphere' or 'box')
p['xoBox'] = -3.5*p['rootChord']
p['xfBox'] = 4.5*p['rootChord']
p['yfBox'] = 2*sum(p['span'])
160 changes: 155 additions & 5 deletions box.py → domain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#

'''
Copyright 2019 University of Liege
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''

## @package GeoGen (CFD basic grid creator)
#
# Create an unstructured tetrahedral grid around a wing
@@ -9,13 +25,138 @@

import numpy as np

## Handle box data
## Generic domain class
#
# Adrien Crovato
class Box:
def __init__(self, xO, xF, yF, zO, zF, _wing, _tip, _wake):
class Domain:
def __init__(self, _wing, _tip):
self.wing = _wing
self.tip = _tip

## Handle sphere data
#
# Adrien Crovato
class Sphere(Domain):
def __init__(self, R, _wing, _tip):
Domain.__init__(self, _wing, _tip)
self.initData(R)

def initData(self, R):
"""Initialize data, define numbering
"""
self.pts = [np.array([[self.wing.chord[0], 0., 0.]]),
np.array([[R+self.wing.chord[0], 0., 0.],
[0., 0., R],
[-R+self.wing.chord[0], 0., 0.],
[0., 0., -R]]),
np.array([[self.wing.chord[0], R, 0.]])]
self.ptsN = [np.array([5001]), np.arange(5002, 5006), np.array([5007])]

# lines (2*4 lines: 191-194 and 195-198)
self.linN = [np.arange(191, 195), np.arange(195, 199)]

# surface (5 surfaces: 111-115)
self.surN = [np.arange(111, 116)]

def writeInfo(self, fname):
"""Write sphere geometrical parameters
"""
file = open(fname, 'a')
file.write('// --- Domain geometry ---\n')
file.write('// Sphere radius: {0:f}\n'.format(self.pts[0][0,0]-self.wing.pts[1][0,0]))
file.write('\n')
file.close()

def writeOpts(self, fname):
"""Write sphere gmsh options
"""
file = open(fname, 'a')
file.write('// --- Domain options ---\n')
file.write('DefineConstant[ msF = {{ {0:f}, Name "Farfield mesh size" }} ];\n'.format(10*self.wing.chord[0]))
file.write('\n')
file.close()

def writePoints(self, fname):
"""Write sphere points
"""
file = open(fname, 'a')
file.write('// --- Sphere points ---\n')
file.write('// --- Center\n')
file.write('Point({0:d}) = {{{1:f},{2:f},{3:f},msF}};\n'.format(self.ptsN[0][0], self.pts[0][0,0], self.pts[0][0,1], self.pts[0][0,2]))
file.write('// --- Farfield\n')
for j in range(0,4):
file.write('Point({0:d}) = {{{1:f},{2:f},{3:f},msF}};\n'.format(self.ptsN[1][j], self.pts[1][j,0], self.pts[1][j,1], self.pts[1][j,2]))
file.write('Point({0:d}) = {{{1:f},{2:f},{3:f},msF}};\n'.format(self.ptsN[2][0], self.pts[2][0,0], self.pts[2][0,1], self.pts[2][0,2]))
file.write('\n')
file.close()

def writeLines(self, fname):
"""Write sphere lines
"""
file = open(fname, 'a')
file.write('// --- Sphere lines ---\n')
for j in range(0, 4):
file.write('Circle({0:d}) = {{{1:d},{2:d},{3:d}}};\n'.format(self.linN[0][j], self.ptsN[1][j], self.ptsN[0][0], self.ptsN[1][np.mod(j+1,4)]))
for j in range(0, 4):
file.write('Circle({0:d}) = {{{1:d},{2:d},{3:d}}};\n'.format(self.linN[1][j], self.ptsN[1][j], self.ptsN[0][0], self.ptsN[2][0]))
file.write('\n')
file.close()

def writeSurfaces(self, fname):
"""Write sphere surfaces
"""
file = open(fname, 'a')
file.write('// --- Sphere surfaces ---\n')
# line loops
for j in range(0, 4):
file.write('Line Loop({0:d}) = {{{1:d},{2:d},{3:d}}};\n'.format(self.surN[0][j], self.linN[0][j], self.linN[1][np.mod(j+1,4)], -self.linN[1][j]))
file.write('Line Loop({0:d}) = {{{1:d},{2:d},{3:d},{4:d}}};\n'.format(self.surN[0][-1], self.linN[0][0], self.linN[0][1], self.linN[0][2], self.linN[0][3]))
file.write('Line Loop({0:d}) = {{{1:d},{2:d},{3:d},{4:d},{5:d},{6:d}}};\n'.format(self.surN[0][-1]+1, self.wing.linaN[0][0], self.wing.linaN[0][1], self.wing.linaN[0][2], self.wing.linaN[0][3], self.wing.linaN[0][4], self.wing.linaN[0][5]))
# surfaces
for j in range(0, 4):
file.write('Surface({0:d}) = {{{0:d}}};\n'.format(self.surN[0][j]))
file.write('Plane Surface({0:d}) = {{{0:d},{1:d}}};\n'.format(self.surN[0][-1], self.surN[0][-1]+1))
file.write('\n')
file.close()

def writeVolumes(self, fname):
"""Write computational volume
"""
file = open(fname, 'a')
file.write('// --- Computational volumes ---\n')
# surface loops
file.write('Surface Loop({0:d}) = {{'.format(1))
for i in range(0, self.wing.n-1):
for j in range(0, 6):
file.write('{0:d},'.format(self.wing.surN[i][j]))
for j in range(0, 6):
file.write('{0:d},'.format(self.tip.surN[0][j]))
for j in range(0, 4):
file.write('{0:d},'.format(self.surN[0][j]))
file.write('{0:d}}};\n'.format(self.surN[0][-1]))

# volumes
file.write('Volume({0:d}) = {{{0:d}}};\n'.format(1))
file.write('\n')
file.close()

def writePhysical(self, fname):
"""Write sphere physical groups
"""
file = open(fname, 'a')
file.write('// --- Box physical groups ---\n')
file.write('Physical Surface("symmetry") = {{{0:d}}};\n'.format(self.surN[0][-1]))
file.write('Physical Surface("farfield") = {{{0:d},{1:d},{2:d},{3:d}}};\n'.format(self.surN[0][0],self.surN[0][1],self.surN[0][2],self.surN[0][3]))
file.write('Physical Volume("field") = {{{0:d}}};\n'.format(1))
file.write('\n')
file.close()

## Handle box data
#
# Adrien Crovato
class Box(Domain):
def __init__(self, xO, xF, yF, zO, zF, _wing, _tip, _wake):
Domain.__init__(self, _wing, _tip)
self.wake = _wake

self.initData(xO, xF, yF, zO, zF)
@@ -31,7 +172,7 @@ def initData(self, xO, xF, yF, zO, zF):
[xO, yF, zF],
[xO, yF, zO],
[xF, yF, zO]])]
self.ptsN = [np.arange(5000, 5004), np.arange(5004, 5008)]
self.ptsN = [np.arange(5001, 5005), np.arange(5005, 5009)]

# line numbering (2*6 x lines: 191-203) AND (4 y lines: 205-209)
self.linxN = [np.arange(191, 197), np.arange(197, 204)]
@@ -51,6 +192,15 @@ def writeInfo(self, fname):
file.write('\n')
file.close()

def writeOpts(self, fname):
"""Write box gmsh options
"""
file = open(fname, 'a')
file.write('// --- Domain options ---\n')
file.write('DefineConstant[ msF = {{ {0:f}, Name "Farfield mesh size" }} ];\n'.format(0.5*self.wing.chord[0]))
file.write('\n')
file.close()

def writePoints(self, fname):
"""Write box points
"""
Loading

0 comments on commit a8300b0

Please sign in to comment.