alaka commited on
Commit
0b9b583
Β·
1 Parent(s): 3a6d592
Files changed (8) hide show
  1. .gitignore +53 -0
  2. README.md +97 -0
  3. app.py +218 -0
  4. examples/clarky.dat +36 -0
  5. examples/naca4412.dat +44 -0
  6. examples/rae2822.dat +48 -0
  7. requirements.txt +5 -0
  8. run.sh +33 -0
.gitignore ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ pip-wheel-metadata/
20
+ share/python-wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+
25
+ # Virtual Environment
26
+ venv/
27
+ .venv/
28
+ env/
29
+ ENV/
30
+ env.bak/
31
+ venv.bak/
32
+
33
+ # IDE
34
+ .vscode/
35
+ .idea/
36
+ *.swp
37
+ *.swo
38
+ *~
39
+ .DS_Store
40
+
41
+ # Logs
42
+ *.log
43
+ nohup.out
44
+ app.log
45
+
46
+ # Gradio
47
+ flagged/
48
+ gradio_cached_examples/
49
+
50
+ # Model cache
51
+ .cache/
52
+ *.pkl
53
+ *.pth
README.md CHANGED
@@ -12,3 +12,100 @@ short_description: Web-app and APIs to get NeuralFoil predictions
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
15
+
16
+ # NeuralFoil Airfoil Predictor (Hugging Face Space)
17
+
18
+ This Space wraps [NeuralFoil](https://github.com/peterdsharpe/NeuralFoil) in a Gradio UI.
19
+
20
+ It runs a NeuralFoil surrogate model to estimate airfoil aerodynamics (CL, CD, CM, transition locations, and an analysis confidence metric) for a single operating point.
21
+
22
+ NeuralFoil is a neural-network-based surrogate for XFoil.
23
+ Given an airfoil and operating conditions, it predicts aerodynamic coefficients and related quantities much faster than traditional CFD or XFoil.
24
+
25
+ This Space only wraps that functionality and does **not** train any models itself.
26
+
27
+ ## Features
28
+
29
+ - **Airfoil definition**
30
+ - By **name** via AeroSandbox (UIUC / NACA-style names, e.g. `naca4412`, `rae2822`, `clarky`)
31
+ - By **`.dat` file upload** (XFoil-style coordinate file)
32
+ - numpy coordinates
33
+
34
+ - **Operating point inputs**
35
+ - Angle of attack **Ξ± [deg]**
36
+ - Reynolds number **Re [-]**
37
+ - NeuralFoil **model size** (`xxsmall` … `xxxlarge`)
38
+
39
+ - **Outputs**
40
+ - Lift coefficient **CL**
41
+ - Drag coefficient **CD**
42
+ - Moment coefficient **CM**
43
+ - Transition locations: **Top_Xtr**, **Bot_Xtr** (if available)
44
+ - **analysis_confidence** + a crude textual interpretation
45
+ - Full raw NeuralFoil output JSON (for power users / debugging)
46
+
47
+
48
+ ## Running locally
49
+
50
+ ### Quick Start (if already set up)
51
+
52
+ ```bash
53
+ ./run.sh
54
+ # or
55
+ source venv/bin/activate && python app.py
56
+ ```
57
+
58
+ ### First Time Setup
59
+
60
+ ```bash
61
+ # 1. Create virtual environment
62
+ python3 -m venv venv
63
+
64
+ # 2. Activate virtual environment
65
+ source venv/bin/activate # macOS/Linux
66
+ # or
67
+ venv\Scripts\activate # Windows
68
+
69
+ # 3. Install dependencies
70
+ pip install --upgrade pip
71
+ pip install -r requirements.txt
72
+
73
+ # 4. Run the app
74
+ python app.py
75
+ ```
76
+
77
+ The app will start on `http://127.0.0.1:7860` (or next available port).
78
+
79
+ See [SETUP.md](SETUP.md) for detailed setup instructions and troubleshooting.
80
+
81
+ ## License
82
+
83
+ - NeuralFoil itself is MIT-licensed (see its own repository for details).
84
+ - This Space is just a thin wrapper around NeuralFoil and AeroSandbox.
85
+
86
+ ## Citation
87
+
88
+ If you use NeuralFoil in your research, please cite:
89
+ Both the tool itself (this repository), which includes the pre-print publication:
90
+
91
+ ```bibtex
92
+ @misc{neuralfoil,
93
+ author = {Peter Sharpe},
94
+ title = {{NeuralFoil}: An airfoil aerodynamics analysis tool using physics-informed machine learning},
95
+ year = {2023},
96
+ publisher = {GitHub},
97
+ journal = {GitHub repository},
98
+ howpublished = {\url{https://github.com/peterdsharpe/NeuralFoil}},
99
+ }
100
+ ```
101
+
102
+ And the author's PhD thesis, which has an extended chapter that serves as the primary long-form documentation for the tool:
103
+
104
+ ```bibtex
105
+ @phdthesis{aerosandbox_phd_thesis,
106
+ title = {Accelerating Practical Engineering Design Optimization with Computational Graph Transformations},
107
+ author = {Sharpe, Peter D.},
108
+ school = {Massachusetts Institute of Technology},
109
+ year = {2024},
110
+ }
111
+ ```
app.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import neuralfoil as nf
3
+ import numpy as np
4
+ import json
5
+ from pathlib import Path
6
+
7
+ def load_airfoil_from_dat(filepath):
8
+ """Load airfoil coordinates from a .dat file"""
9
+ with open(filepath, 'r') as f:
10
+ lines = f.readlines()
11
+
12
+ # Skip the first line (airfoil name)
13
+ coords = []
14
+ for line in lines[1:]:
15
+ line = line.strip()
16
+ if line:
17
+ parts = line.split()
18
+ if len(parts) >= 2:
19
+ try:
20
+ x, y = float(parts[0]), float(parts[1])
21
+ coords.append([x, y])
22
+ except ValueError:
23
+ continue
24
+
25
+ return np.array(coords)
26
+
27
+ def format_coordinates(coords_array):
28
+ """Format coordinates array as a string for display"""
29
+ if coords_array is None or len(coords_array) == 0:
30
+ return ""
31
+
32
+ lines = []
33
+ for x, y in coords_array:
34
+ lines.append(f"{x:.6f} {y:.6f}")
35
+ return "\n".join(lines)
36
+
37
+ def parse_coordinates(coords_text):
38
+ """Parse coordinates from text input"""
39
+ lines = coords_text.strip().split('\n')
40
+ coords = []
41
+
42
+ for line in lines:
43
+ line = line.strip()
44
+ if line:
45
+ parts = line.split()
46
+ if len(parts) >= 2:
47
+ try:
48
+ x, y = float(parts[0]), float(parts[1])
49
+ coords.append([x, y])
50
+ except ValueError:
51
+ continue
52
+
53
+ return np.array(coords)
54
+
55
+ def run_neuralfoil_prediction(coords_text, alpha, reynolds, model_size):
56
+ """Run NeuralFoil prediction and return full JSON output"""
57
+ try:
58
+ # Parse coordinates
59
+ coords = parse_coordinates(coords_text)
60
+
61
+ if len(coords) < 3:
62
+ return json.dumps({"error": "Invalid coordinates. Please provide at least 3 coordinate pairs."}, indent=2)
63
+
64
+ # Run NeuralFoil analysis directly from coordinates
65
+ result = nf.get_aero_from_coordinates(
66
+ coordinates=coords,
67
+ alpha=alpha,
68
+ Re=reynolds,
69
+ model_size=model_size
70
+ )
71
+ print(result)
72
+
73
+ # Convert result to a serializable dictionary
74
+ output = {}
75
+
76
+ # Standard outputs
77
+ if hasattr(result, 'CL'):
78
+ output['CL'] = float(result.CL) if not np.isnan(result.CL) else None
79
+ if hasattr(result, 'CD'):
80
+ output['CD'] = float(result.CD) if not np.isnan(result.CD) else None
81
+ if hasattr(result, 'CM'):
82
+ output['CM'] = float(result.CM) if not np.isnan(result.CM) else None
83
+
84
+ # Transition locations (if available)
85
+ if hasattr(result, 'Top_Xtr'):
86
+ output['Top_Xtr'] = float(result.Top_Xtr) if not np.isnan(result.Top_Xtr) else None
87
+ if hasattr(result, 'Bot_Xtr'):
88
+ output['Bot_Xtr'] = float(result.Bot_Xtr) if not np.isnan(result.Bot_Xtr) else None
89
+
90
+ # Confidence metric
91
+ if hasattr(result, 'analysis_confidence'):
92
+ output['analysis_confidence'] = float(result.analysis_confidence) if not np.isnan(result.analysis_confidence) else None
93
+
94
+ # Include all other attributes
95
+ for attr in dir(result):
96
+ if not attr.startswith('_') and attr not in output:
97
+ val = getattr(result, attr)
98
+ if isinstance(val, (int, float, str, bool)):
99
+ output[attr] = val
100
+ elif isinstance(val, np.ndarray):
101
+ output[attr] = val.tolist()
102
+
103
+ # Add input parameters for reference
104
+ output['input_parameters'] = {
105
+ 'alpha_deg': alpha,
106
+ 'reynolds_number': reynolds,
107
+ 'model_size': model_size,
108
+ 'num_coordinates': len(coords)
109
+ }
110
+
111
+ return json.dumps(output, indent=2)
112
+
113
+ except Exception as e:
114
+ return json.dumps({"error": str(e)}, indent=2)
115
+
116
+ def load_example(example_name):
117
+ """Load an example airfoil"""
118
+ example_files = {
119
+ "NACA 4412": "examples/naca4412.dat",
120
+ "Clark Y": "examples/clarky.dat",
121
+ "RAE 2822": "examples/rae2822.dat"
122
+ }
123
+
124
+ filepath = example_files.get(example_name)
125
+ if filepath and Path(filepath).exists():
126
+ coords = load_airfoil_from_dat(filepath)
127
+ return format_coordinates(coords)
128
+ return ""
129
+
130
+ # Load default airfoil (NACA 4412)
131
+ default_coords = format_coordinates(load_airfoil_from_dat("examples/naca4412.dat"))
132
+
133
+ # Create Gradio interface
134
+ with gr.Blocks(title="NeuralFoil Airfoil Predictor") as demo:
135
+ gr.Markdown("# NeuralFoil Airfoil Predictor")
136
+ gr.Markdown("""
137
+ This app uses [NeuralFoil](https://github.com/peterdsharpe/NeuralFoil) to predict airfoil aerodynamics.
138
+
139
+ Provide airfoil coordinates (x, y pairs, one per line) and operating conditions to get predictions for CL, CD, CM, and more.
140
+ """)
141
+
142
+ with gr.Row():
143
+ with gr.Column():
144
+ gr.Markdown("### Airfoil Coordinates")
145
+ gr.Markdown("Enter x,y coordinate pairs (one per line). Coordinates should trace the airfoil from trailing edge, over the top, to leading edge, then back along the bottom.")
146
+
147
+ coords_input = gr.Textbox(
148
+ label="Airfoil Coordinates",
149
+ value=default_coords,
150
+ lines=15,
151
+ max_lines=30,
152
+ placeholder="x y\n1.0 0.0\n0.95 0.01\n..."
153
+ )
154
+
155
+ gr.Markdown("### Load Example")
156
+ example_buttons = gr.Radio(
157
+ choices=["NACA 4412", "Clark Y", "RAE 2822"],
158
+ label="Example Airfoils",
159
+ value="NACA 4412"
160
+ )
161
+
162
+ load_btn = gr.Button("Load Example")
163
+
164
+ gr.Markdown("### Operating Conditions")
165
+ alpha_input = gr.Slider(
166
+ minimum=-10,
167
+ maximum=20,
168
+ value=5.0,
169
+ step=0.5,
170
+ label="Angle of Attack Ξ± [deg]"
171
+ )
172
+
173
+ reynolds_input = gr.Number(
174
+ value=1e6,
175
+ label="Reynolds Number Re [-]"
176
+ )
177
+
178
+ model_size_input = gr.Dropdown(
179
+ choices=["xxsmall", "xsmall", "small", "medium", "large", "xlarge", "xxlarge", "xxxlarge"],
180
+ value="large",
181
+ label="Model Size"
182
+ )
183
+
184
+ predict_btn = gr.Button("Run Prediction", variant="primary")
185
+
186
+ with gr.Column():
187
+ gr.Markdown("### Full NeuralFoil Output (JSON)")
188
+ output_json = gr.Textbox(
189
+ label="Prediction Results",
190
+ lines=25,
191
+ max_lines=40,
192
+ placeholder="Results will appear here..."
193
+ )
194
+
195
+ # Event handlers
196
+ load_btn.click(
197
+ fn=load_example,
198
+ inputs=[example_buttons],
199
+ outputs=[coords_input]
200
+ )
201
+
202
+ predict_btn.click(
203
+ fn=run_neuralfoil_prediction,
204
+ inputs=[coords_input, alpha_input, reynolds_input, model_size_input],
205
+ outputs=[output_json]
206
+ )
207
+
208
+ gr.Markdown("""
209
+ ---
210
+ ### About
211
+
212
+ **NeuralFoil** is a neural-network-based surrogate for XFoil that predicts airfoil aerodynamics much faster than traditional CFD.
213
+
214
+ **Citation**: If you use NeuralFoil, please cite the [GitHub repository](https://github.com/peterdsharpe/NeuralFoil) and Peter Sharpe's PhD thesis.
215
+ """)
216
+
217
+ if __name__ == "__main__":
218
+ demo.launch()
examples/clarky.dat ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CLARK Y
2
+ 1.000000 0.000000
3
+ 0.950000 0.009300
4
+ 0.900000 0.017800
5
+ 0.800000 0.032500
6
+ 0.700000 0.044500
7
+ 0.600000 0.053500
8
+ 0.500000 0.059500
9
+ 0.400000 0.061500
10
+ 0.300000 0.059000
11
+ 0.250000 0.056000
12
+ 0.200000 0.051500
13
+ 0.150000 0.045500
14
+ 0.100000 0.037000
15
+ 0.075000 0.031000
16
+ 0.050000 0.024000
17
+ 0.025000 0.015500
18
+ 0.012500 0.010500
19
+ 0.000000 0.000000
20
+ 0.012500 -0.010500
21
+ 0.025000 -0.015000
22
+ 0.050000 -0.020000
23
+ 0.075000 -0.023000
24
+ 0.100000 -0.025000
25
+ 0.150000 -0.026000
26
+ 0.200000 -0.025500
27
+ 0.250000 -0.024000
28
+ 0.300000 -0.022000
29
+ 0.400000 -0.017500
30
+ 0.500000 -0.012500
31
+ 0.600000 -0.008000
32
+ 0.700000 -0.004500
33
+ 0.800000 -0.002000
34
+ 0.900000 -0.000500
35
+ 0.950000 -0.000250
36
+ 1.000000 0.000000
examples/naca4412.dat ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ NACA 4412
2
+ 1.000000 0.001260
3
+ 0.950070 0.010760
4
+ 0.900190 0.019440
5
+ 0.850360 0.027380
6
+ 0.800580 0.034640
7
+ 0.750860 0.041260
8
+ 0.701190 0.047300
9
+ 0.651580 0.052790
10
+ 0.602030 0.057770
11
+ 0.552550 0.062270
12
+ 0.503140 0.066310
13
+ 0.453810 0.069910
14
+ 0.404580 0.073080
15
+ 0.355450 0.075820
16
+ 0.306440 0.078120
17
+ 0.257570 0.079970
18
+ 0.208840 0.081330
19
+ 0.160280 0.082160
20
+ 0.111900 0.082400
21
+ 0.063730 0.081950
22
+ 0.015810 0.080630
23
+ 0.000000 0.000000
24
+ 0.034190 -0.059780
25
+ 0.082600 -0.056500
26
+ 0.131100 -0.052370
27
+ 0.179720 -0.047770
28
+ 0.228460 -0.042960
29
+ 0.277320 -0.038070
30
+ 0.326290 -0.033180
31
+ 0.375360 -0.028360
32
+ 0.424540 -0.023640
33
+ 0.473810 -0.019070
34
+ 0.523190 -0.014680
35
+ 0.572640 -0.010500
36
+ 0.622170 -0.006560
37
+ 0.671770 -0.002890
38
+ 0.721440 0.000510
39
+ 0.771170 0.003630
40
+ 0.820960 0.006480
41
+ 0.870790 0.009060
42
+ 0.920660 0.011360
43
+ 0.970570 0.013400
44
+ 1.000000 0.001260
examples/rae2822.dat ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ RAE 2822
2
+ 1.000000 0.000000
3
+ 0.950000 0.005800
4
+ 0.900000 0.011200
5
+ 0.850000 0.016100
6
+ 0.800000 0.020700
7
+ 0.750000 0.024900
8
+ 0.700000 0.028800
9
+ 0.650000 0.032300
10
+ 0.600000 0.035500
11
+ 0.550000 0.038400
12
+ 0.500000 0.040900
13
+ 0.450000 0.043000
14
+ 0.400000 0.044700
15
+ 0.350000 0.045900
16
+ 0.300000 0.046500
17
+ 0.250000 0.046400
18
+ 0.200000 0.045300
19
+ 0.150000 0.042700
20
+ 0.100000 0.037800
21
+ 0.075000 0.034200
22
+ 0.050000 0.029200
23
+ 0.025000 0.021500
24
+ 0.012500 0.015500
25
+ 0.000000 0.000000
26
+ 0.012500 -0.015500
27
+ 0.025000 -0.021000
28
+ 0.050000 -0.026500
29
+ 0.075000 -0.029500
30
+ 0.100000 -0.031500
31
+ 0.150000 -0.033500
32
+ 0.200000 -0.033500
33
+ 0.250000 -0.032000
34
+ 0.300000 -0.029500
35
+ 0.350000 -0.026500
36
+ 0.400000 -0.023000
37
+ 0.450000 -0.019500
38
+ 0.500000 -0.016000
39
+ 0.550000 -0.012500
40
+ 0.600000 -0.009500
41
+ 0.650000 -0.006800
42
+ 0.700000 -0.004500
43
+ 0.750000 -0.002700
44
+ 0.800000 -0.001300
45
+ 0.850000 -0.000500
46
+ 0.900000 -0.000100
47
+ 0.950000 0.000000
48
+ 1.000000 0.000000
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=4.0.0,<4.45.0
2
+ # huggingface-hub<1.0.0
3
+ neuralfoil>=0.2.0
4
+ aerosandbox>=4.0.0
5
+ numpy>=1.24.0
run.sh ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Simple script to run the NeuralFoil Gradio app
3
+
4
+ echo "πŸš€ Starting NeuralFoil Airfoil Predictor..."
5
+ echo ""
6
+
7
+ # Check if virtual environment exists
8
+ if [ ! -d "venv" ]; then
9
+ echo "❌ Virtual environment not found!"
10
+ echo "Please run: python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
11
+ exit 1
12
+ fi
13
+
14
+ # Activate virtual environment
15
+ source venv/bin/activate
16
+
17
+ # Check if dependencies are installed
18
+ python -c "import gradio, neuralfoil" 2>/dev/null
19
+ if [ $? -ne 0 ]; then
20
+ echo "❌ Dependencies not installed!"
21
+ echo "Please run: pip install -r requirements.txt"
22
+ exit 1
23
+ fi
24
+
25
+ echo "βœ… Virtual environment activated"
26
+ echo "βœ… Dependencies found"
27
+ echo ""
28
+ echo "πŸ“Š Launching Gradio app..."
29
+ echo "⏳ First startup may take 30-60 seconds..."
30
+ echo ""
31
+
32
+ # Run the app
33
+ python app.py