Official implementation of Geometry-Aware Neural Optimizer for Shape Optimization and Inversion
Guoze Sun*, Tianya Miao*, Haoyang Huang, Huaguan Chen,
Han Wan, Rui Zhangβ , Hao Sunβ
Gaoling School of Artificial Intelligence, Renmin University of China
ICML 2026
* Equal contribution.
β Corresponding authors.
Geometry is a central design variable in many PDE-governed systems, but conventional shape optimization and inversion require repeated numerical simulation, geometry modification, and remeshing. These operations are computationally expensive and often require substantial expert intervention.
GANO is an end-to-end differentiable framework that unifies:
- geometry representation with StableSDF;
- field-level physical prediction with GI-Transolver;
- latent-space geometry optimization and inversion;
- part-wise geometry control through null-space projection; and
- remeshing-free vehicle surface updates through SDF-based projection.
The framework is evaluated on three benchmarks:
- 2D Helmholtz: forward scattering prediction and shape inversion;
- 2D Airfoil: flow-field prediction and aerodynamic shape optimization;
- 3D Vehicle: surface-pressure prediction and drag-minimizing optimization.
- End-to-end differentiable geometry optimization. GANO propagates gradients from field-level objectives through the physical surrogate to a compact geometry latent code.
- Stable latent geometry updates. StableSDF supports Gaussian perturbations to latent codes during training, inducing an implicit latent-Jacobian regularization and reducing sensitivity to latent perturbations. The released vehicle benchmark enables this mechanism.
- Geometry-informed field prediction. GI-Transolver explicitly injects geometry codes into Transolver slice tokens, creating an effective gradient pathway from predicted physical fields to geometry.
- Part-wise control. Null-space projection suppresses first-order changes at selected constraint points, allowing designated components to remain fixed during optimization.
- Remeshing-free vehicle optimization. In the released vehicle workflow, boundary query points are projected onto the updated implicit surface without reconstructing a new mesh at every iteration.
- 2D and 3D validation. GANO supports inverse scattering, airfoil optimization, and vehicle aerodynamic optimization in one unified framework.
StableSDF represents a geometry with a signed distance decoder
where
This denoising-style training encourages locally smooth and controllable
geometry changes during latent-space optimization. In the released scripts,
latent noise is enabled for the 3D vehicle benchmark with
GI-Transolver extends Transolver by injecting the geometry code into the slice-token space through a gated residual mechanism. It predicts full physical fields rather than a fixed scalar objective, enabling flexible objectives defined over global or local physical quantities.
Given a trained StableSDF decoder and GI-Transolver surrogate, GANO freezes their model parameters and iteratively updates the geometry latent code:
For unconstrained optimization, the safe gradient is
For part-wise control, the gradient can be projected onto the null space of the constraint Jacobian.
For vehicle optimization, surface query points are moved back to the updated zero level set after each latent update using an SDF-based projection, avoiding repeated remeshing. The released Helmholtz inversion and airfoil optimization scripts do not apply this surface-point projection.
GANO/
βββ checkpoints/ # Checkpoints generated by training
βββ data/ # Dataset instructions and processed data
βββ scripts/
β βββ airfoil/
β β βββ train_stablesdf_airfoil.py
β β βββ train_gi_transolver.py
β β βββ optimize_airfoil.py
β βββ car/
β β βββ train_stablesdf_car.py
β β βββ train_gi_transolver_car.py
β β βββ optimize_vehicle.py
β βββ hh/
β β βββ train_stablesdf.py
β β βββ train_gi_transolver.py
β β βββ optimize_hh.py
β βββ reproduce/ # Stage-aware reproduction wrappers
β βββ helmholtz.sh
β βββ airfoil.sh
β βββ vehicle.sh
βββ src/
β βββ airfoil/ # Airfoil models and utilities
β βββ car/ # Vehicle models and utilities
β βββ hh/ # Helmholtz models and utilities
βββ LICENSE
βββ README.md
βββ requirements.txt
git clone https://github.com/intell-sci-comput/GANO.git
cd GANOconda create -n gano python=3.11 -y
conda activate ganoThe released code was tested with PyTorch 2.5.1 and CUDA 12.1. Install the corresponding official wheels with:
pip install torch==2.5.1 torchvision==0.20.1 \
--index-url https://download.pytorch.org/whl/cu121For a different CUDA platform or a CPU-only installation, select the matching command from the official PyTorch installation guide.
pip install -r requirements.txt| Component | Version |
|---|---|
| Operating system | Ubuntu 20.04 |
| Python | 3.11.14 |
| PyTorch | 2.5.1 |
| CUDA runtime | 12.1 |
| GPU | NVIDIA A100 / H800 |
| NumPy | 1.24.2 |
GANO uses locally generated data for the 2D Helmholtz benchmark and two public datasets for the aerodynamic benchmarks.
| Benchmark | Source | Data used by GANO |
|---|---|---|
| 2D Helmholtz | Generated by this repository | Random obstacles, SDF samples, and finite-difference scattering fields |
| 2D Airfoil | Airfoil CFD 9k (OEDI) | All 8,996 airfoils at |
| 3D Vehicle | DrivAerNet++ | STL geometry and surface-pressure VTK data for 8,129 valid designs |
See data/README.md for download instructions, data formats,
normalization conventions, split definitions, and environment-variable path
overrides. Run every command below from the repository root.
data/
βββ README.md
βββ hh/
β βββ scattering_shapes_256.npz
β βββ scattering_sdf_dataset_mixed.npz
β βββ scattering_dataset_scat_fields_k7.npz
β βββ scattering_dataset_normalized.npz
β βββ normalization_stats.pt
βββ airfoil/
β βββ raw/
β β βββ airfoil_9k_data.h5
β βββ airfoil_sdf_train.pt
β βββ airfoil_physics_train.pt
βββ car/
βββ raw/
β βββ 3DMeshesSTL/ # Recursive collection of .stl files
β βββ PressureVTK/ # Recursive collection of .vtk files
βββ sdf/ # One .npz file per vehicle
βββ pressure/
β βββ **/*.npz # One pressure file per vehicle
β βββ dataset_stats.json
βββ split/
βββ train.txt
βββ test.txt
python data/hh/genshape.py
python data/hh/gensdf.py
python data/hh/genpde.py
python data/hh/normalize_pde.pyThe default configuration generates 1,000 random obstacles on a
Download the 52.7 GB HDF5 file from OEDI, or use the anonymous public S3 endpoint:
mkdir -p data/airfoil/raw
aws s3 cp \
s3://nrel-pds-windai/aerodynamic_shapes/2D/9k_airfoils/v1.0.0/airfoil_9k_data.h5 \
data/airfoil/raw/airfoil_9k_data.h5 \
--no-sign-request
python data/airfoil/preprocess_sdf_airfoil.pyThis produces 4,096 SDF samples for each of the 8,996 airfoils. Because the physics preprocessing requires the learned geometry codes, first train StableSDF and then build the field dataset:
python scripts/airfoil/train_stablesdf_airfoil.py
python data/airfoil/preprocess_physics_airfoil.pyGANO extracts the
Download the 3D Meshes (STL) and Pressure (VTK) modalities from the DrivAerNet++ Harvard Dataverse, following the download instructions in the official repository. The full multimodal dataset is not required. Extract the two modalities into the layout shown above, then run:
python data/car/preprocess_sdf_car.py
python data/car/preprocess_pressure_car.pyBoth scripts search their input trees recursively, so the internal directories
created by the downloaded archives may be retained. Matching STL and VTK files
must have the same basename, which is used as the vehicle ID. GANO centers each
geometry at its bounding-box center and scales its bounding-box diagonal to
1.9. Each vehicle receives 100,000 SDF samples; pressure is standardized with
the fixed training-set statistics recorded in dataset_stats.json.
The provided split contains 7,316 training and 813 test vehicle IDs, covering 8,129 successfully processed DrivAerNet++ designs. The source dataset is licensed separately from this repository; review its CC BY-NC 4.0 terms before downloading or redistributing it.
Pretrained weights are not distributed with this repository. Running the training commands below creates checkpoints in the locations expected by the downstream scripts:
checkpoints/
βββ hh/
β βββ stablesdf/
β β βββ deepsdf_final.pth
β βββ transolver/
β βββ best_transolver.pth
βββ airfoil_stablesdf/
β βββ model_latest.pth
β βββ latents_latest.pth
βββ airfoil_transolver/
β βββ airfoil_transolver_best.pth
βββ car_training_h800_all/
β βββ model_latest.pth
β βββ latents_latest.pth
β βββ file_list.json
βββ car_transolver/
βββ best_model.pth
βββ transolver_sdf_normals_<timestamp>/
βββ best_model.pth
βββ train.log
Checkpoint files are ignored by Git. Geometry codes and their associated index or file-list metadata must be kept together because later stages rely on their ordering.
Run all commands from the repository root after completing Installation and Data Preparation. Each benchmark follows the same sequence:
processed geometry -> StableSDF + latent codes -> processed physical fields
-> GI-Transolver -> latent-space optimization or inversion
The scripts use their in-file CONFIG dictionaries and GANO_* environment
variables rather than positional command-line arguments. The commands below
use the default settings from the released experiments.
Helmholtz is the smallest self-contained workflow because it does not require an external dataset. In a fresh clone, the following reduced configuration checks data generation, both training stages, and inversion:
GANO_SMOKE_TEST=1 python data/hh/genshape.py
GANO_SMOKE_TEST=1 python data/hh/gensdf.py
GANO_SMOKE_TEST=1 python data/hh/genpde.py
GANO_SMOKE_TEST=1 python data/hh/normalize_pde.py
GANO_SMOKE_TEST=1 python scripts/hh/train_stablesdf.py
GANO_SMOKE_TEST=1 python scripts/hh/train_gi_transolver.py
GANO_SMOKE_TEST=1 python scripts/hh/optimize_hh.pySmoke-test mode uses the normal output filenames. Do not run these commands on top of a prepared full dataset unless those files have been backed up.
Generate the full dataset as described above, then run:
python scripts/hh/train_stablesdf.py
python scripts/hh/train_gi_transolver.py
python scripts/hh/optimize_hh.pyThe stages communicate through these files:
| Stage | Main input | Main output |
|---|---|---|
| StableSDF | data/hh/scattering_sdf_dataset_mixed.npz |
checkpoints/hh/stablesdf/deepsdf_final.pth |
| GI-Transolver | normalized fields and deepsdf_final.pth |
checkpoints/hh/transolver/best_transolver.pth |
| Inversion | both checkpoints and normalization_stats.pt |
output/hh/inverse_vis/inverse_onecycle_result.png |
For example, training length, batch size, worker count, and inversion steps can be changed from the shell:
GANO_HH_STABLESDF_EPOCHS=500 \
GANO_HH_STABLESDF_BATCH_SIZE=64 \
python scripts/hh/train_stablesdf.py
GANO_HH_TRANSOLVER_EPOCHS=100 \
GANO_HH_TRANSOLVER_BATCH_SIZE=16 \
python scripts/hh/train_gi_transolver.py
GANO_HH_OPT_STEPS=200 \
GANO_HH_OPT_NUM_SENSORS=100 \
python scripts/hh/optimize_hh.pyAirfoil field preprocessing depends on the StableSDF latent codes. The complete order is therefore:
python data/airfoil/preprocess_sdf_airfoil.py
python scripts/airfoil/train_stablesdf_airfoil.py
python data/airfoil/preprocess_physics_airfoil.py
python scripts/airfoil/train_gi_transolver.py
python scripts/airfoil/optimize_airfoil.pyThe final command starts from airfoil index 0 and writes:
output/airfoil_optimization/
βββ optimized_airfoil_comsol.txt
βββ optimized_fields.npz
βββ z_opt.pt
βββ airfoil_mask.png
βββ airfoil_contour.png
βββ optimized_cl_cd_result.png
Common resource and optimization settings can be overridden as follows:
GANO_AIRFOIL_STABLESDF_EPOCHS=500 \
GANO_AIRFOIL_STABLESDF_BATCH_SIZE=64 \
python scripts/airfoil/train_stablesdf_airfoil.py
GANO_AIRFOIL_TRANSOLVER_EPOCHS=100 \
GANO_AIRFOIL_TRANSOLVER_BATCH_SIZE=32 \
GANO_AIRFOIL_TRANSOLVER_NUM_WORKERS=4 \
python scripts/airfoil/train_gi_transolver.py
GANO_AIRFOIL_OPT_SAMPLE_IDX=100 \
GANO_AIRFOIL_OPT_STEPS=150 \
GANO_AIRFOIL_OPT_OUTPUT_DIR=output/airfoil_optimization_sample_100 \
python scripts/airfoil/optimize_airfoil.pyThe selected sample index refers to the row order stored in
data/airfoil/airfoil_physics_train.pt.
Vehicle StableSDF training explicitly loads the complete processed SDF set into GPU memory and therefore requires a CUDA GPU with substantial memory. To start a new run rather than resume an existing checkpoint:
GANO_CAR_STABLESDF_RESUME=0 \
GANO_CAR_STABLESDF_START_EPOCH=0 \
python scripts/car/train_stablesdf_car.py
python scripts/car/train_gi_transolver_car.pyGI-Transolver stores a timestamped experiment directory and also updates the
stable path checkpoints/car_transolver/best_model.pth, which is loaded by the
optimization script. After arranging component meshes under
data/car/parts/<vehicle-id>/ as described in data/README.md, run:
GANO_CAR_OPT_CAR_ID=E_S_WW_WM_395 \
python scripts/car/optimize_vehicle.pyThe vehicle ID must exist in the StableSDF file_list.json, and its component
directory must contain STL files. A run writes:
output/car_optimization/
βββ opt_drag_nullspace_transolver_lbfgs_<vehicle-id>_<timestamp>/
βββ objs/
β βββ shape_0000.obj
β βββ shape_0040.obj
βββ latest_opt.pth
βββ optimized_z.pt
βββ drag_history.npy
βββ loss_history.npy
For smaller GPUs, reduce the per-vehicle pressure samples and batch size during GI-Transolver training. This does not reduce the full-dataset GPU allocation in the current StableSDF implementation.
GANO_CAR_NUM_POINTS=20000 \
GANO_CAR_BATCH_SIZE=4 \
GANO_CAR_NUM_WORKERS=4 \
python scripts/car/train_gi_transolver_car.py
GANO_CAR_OPT_CAR_ID=E_S_WW_WM_395 \
GANO_CAR_OPT_STEPS=20 \
GANO_CAR_OPT_OUTPUT_DIR=output/car_optimization_short \
python scripts/car/optimize_vehicle.pyVehicle optimization applies null-space projection by default. It samples
constraint points from component files containing Mirrors in their names and
falls back to files containing wheels when no mirror mesh is found. Run a
specific vehicle with:
GANO_CAR_OPT_CAR_ID=E_S_WW_WM_395 \
GANO_CAR_OPT_PARTS_ROOT=data/car/parts \
python scripts/car/optimize_vehicle.pyThe component layout and case-sensitive naming rules are documented in
data/README.md.
The script centers and scales all parts together using the same bounding-box
diagonal normalization as training, samples 16 constraint points by default,
and projects each latent gradient with Mirrors, Underbody, or wheels are excluded from the
drag objective's optimizable surface.
The reproduction wrappers preserve the dependency order between data preparation, StableSDF training, physical-field preprocessing, GI-Transolver training, and optimization. Run them from any directory; each wrapper first changes to the repository root.
scripts/reproduce/
βββ helmholtz.sh
βββ airfoil.sh
βββ vehicle.sh
After preparing the external datasets described in
data/README.md, a complete benchmark can be run with:
bash scripts/reproduce/helmholtz.sh all
bash scripts/reproduce/airfoil.sh all
bash scripts/reproduce/vehicle.sh allEach wrapper also exposes individual stages:
| Benchmark | Available stages |
|---|---|
| Helmholtz | prepare, train-sdf, train-physics, invert, all |
| Airfoil | prepare-sdf, train-sdf, prepare-physics, train-physics, optimize, all |
| Vehicle | prepare, train-sdf, train-physics, optimize, all |
For example, the following commands retrain only the airfoil physical surrogate and then rerun shape optimization:
bash scripts/reproduce/airfoil.sh train-physics
bash scripts/reproduce/airfoil.sh optimizeAll GANO_* overrides are passed through to the underlying Python scripts:
GANO_CAR_EPOCHS=100 \
GANO_CAR_BATCH_SIZE=8 \
bash scripts/reproduce/vehicle.sh train-physics
GANO_CAR_OPT_CAR_ID=F_D_WM_WW_2689 \
bash scripts/reproduce/vehicle.sh optimizeFor a lightweight pipeline check that requires no external data, use:
GANO_SMOKE_TEST=1 bash scripts/reproduce/helmholtz.sh allThis smoke workflow has been tested from shape generation through inversion. It writes to the standard Helmholtz data and checkpoint paths, so use it only in a fresh clone or after backing up full-resolution artifacts. Full experiment runs remain stochastic where the preprocessing or training script samples points randomly.
The tables below summarize the executable defaults in the released scripts.
Values supplied through the GANO_* environment variables take precedence.
| Setting | Helmholtz | Airfoil | Vehicle |
|---|---|---|---|
| Coordinate dimension | 2 | 2 | 3 |
| Latent dimension | 64 | 64 | 256 |
| Hidden dimension | 256 | 256 | 512 |
| Decoder architecture | 4 hidden layers + output | 4 hidden layers + output | 8 linear layers |
| Positional-encoding frequencies | 6 | 6 | 4 |
| SDF points per shape | 10,000 | 4,096 | 100,000 |
| Batch definition | 128 shapes | 128 shapes | 500,000 points |
| Training epochs | 1,000 | 1,000 | 800 |
| Optimizer and learning rate | Adam, |
Adam, |
Adam, |
| SDF reconstruction loss | L1, clamped at 0.05 | L1 | Surface-weighted L1, clamped at 0.1 |
| Latent regularization weight | |||
| Latent noise | None | None |
|
For the vehicle loss, samples with
| Setting | Helmholtz | Airfoil | Vehicle |
|---|---|---|---|
| Input features |
|
|
|
| Predicted fields | Real/imaginary wave field | Pressure | |
| Geometry-code dimension | 64 | 64 | 256 |
| Hidden dimension | 256 | 256 | 256 |
| Transolver blocks | 4 | 5 | 5 |
| Attention heads | 8 | 8 | 8 |
| Physical slices | 32 | 32 | 32 |
| MLP ratio | 1 | 1 | 2 |
| Latent injection | Every block | Every block | Every block |
| Sampled points per item | 4,096 | 4,096 | 50,000 |
| Batch size | 32 | 64 | 16 |
| Training epochs | 200 | 200 | 200 |
| Optimizer | AdamW | AdamW | AdamW |
| Peak learning rate | |||
| Learning-rate schedule | OneCycle | OneCycle | 10-epoch warmup + cosine |
| Weight decay | 0 | 0 | |
| Gradient clipping | 1.0 | 2.0 | 1.0 |
All three models use zero dropout and seed 42. Vehicle training uses bfloat16
automatic mixed precision and a Huber loss with
| Setting | Helmholtz inversion | Airfoil optimization | Vehicle optimization |
|---|---|---|---|
| Objective | Sparse-sensor field L1 | Maximize |
Minimize pressure drag |
| Optimizer | Adam + OneCycle | Adam | L-BFGS |
| Outer steps | 100 | 100 | 40 |
| Learning rate | Max |
||
| Latent regularization | |||
| Field/context points | 100 sensors + 4,096 context points | Up to 100,000 context points | 50,000 surface points |
| Surface projection | None | None | 5 SDF projection steps |
| Part-wise constraints | None | None | 16 points, |
| Geometry export resolution | N/A |
Here
If you find this work useful, please cite:
@inproceedings{
guoze2026geometryaware,
title={Geometry-Aware Neural Optimizer for Shape Optimization and Inversion},
author={Sun Guoze and Tianya Miao and Haoyang Huang and Huaguan Chen and Han Wan and Rui Zhang and Hao Sun},
booktitle={Forty-third International Conference on Machine Learning},
year={2026},
url={https://openreview.net/forum?id=PTaUjEBHat}
}This work builds upon ideas and implementations from prior research on implicit neural representations, neural operators, and differentiable shape optimization.
In particular, please cite and acknowledge the upstream projects and datasets used by this repository, including:
- DeepSDF;
- Transolver;
- AirFoil 9k;
- DrivAerNet++.
This repository is released under the PolyForm Noncommercial License 1.0.0.
The license permits noncommercial use, including research, experimentation, and study, subject to its terms. Please read the full license before using or redistributing the code.
For commercial use, please contact the authors.
For questions about the code, please open a GitHub issue.
For research-related inquiries:
- Rui Zhang: rayzhang@ruc.edu.cn
- Hao Sun: haosun@ruc.edu.cn





