Skip to content

Commit 6b4cdef

Browse files
ns5678peterdsharpeRishikeshRanade
authored
Generalize the encode_parameter in DoMINO to any arbitrary no. of global parameters input (#903)
* changes based on updated main branch * update to model.py and end to end testing * changes to sharded parts of the code * Update README * Update inference_on_stl.py to comply with new method * minor refactor * update * Tested training * remove hardcoded stuff from inference_on_stl.py * Removed comments from model.py * Remove air_density and stream_velocity from domino_sharded * Remove comments from domino_datapipe * Removed names and make paths generic * make encode_parameters false * Update and remove comments * Update README * Update README, remove redundant text * Update model.py to remove air_density and stream_velocity * Update inference_on_stl.py to be consistent with main * Update README.md to be compliant with main * Update tests * changes based on CI * small cleaning config.yaml * Update changelog * fixing doctest issue --------- Co-authored-by: Peter Sharpe <peterdsharpe@gmail.com> Co-authored-by: Rishikesh Ranade <dr.rranade@gmail.com> Co-authored-by: RishikeshRanade <65631160+RishikeshRanade@users.noreply.github.com>
1 parent b625f73 commit 6b4cdef

File tree

15 files changed

+378
-132
lines changed

15 files changed

+378
-132
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ cython_debug/
153153

154154
# VsCode
155155
.vscode/
156+
.cursor/
156157

157158
# VIM
158159
*.swp

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- Safe API to override `__init__`'s arguments saved in checkpoint file with
1515
`Module.from_checkpoint("chkpt.mdlus", models_args)`.
1616
- PyTorch Geometric MeshGraphNet backend.
17+
- Functionality in DoMINO to take arbitrary number of `scalar` or `vector`
18+
global parameters and encode them using `class ParameterModel`
1719

1820
### Changed
1921

examples/cfd/external_aerodynamics/domino/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,52 @@ The DoMINO model can be evaluated directly on unknown STLs using the pre-trained
241241
4. The surface predictions are carried out on the STL surface. The drag and lift
242242
accuracy will depend on the resolution of the STL.
243243

244+
### Incorporating multiple global simulation parameters for training/inference
245+
246+
DoMINO supports incorporating multiple global simulation parameters (such as inlet
247+
velocity, air density, etc.) that can vary across different simulations.
248+
249+
1. Define global parameters in the `variables.global_parameters` section of
250+
`conf/config.yaml`. Each parameter must specify its type (`vector` or `scalar`)
251+
and reference values for non-dimensionalization.
252+
253+
2. For `vector` type parameters:
254+
- If values are single-direction vectors (e.g., [30, 0, 0]), define reference as [30]
255+
- If values are two-direction vectors (e.g., [30, 30, 0]), define reference as [30, 30]
256+
257+
3. Enable parameter encoding in the model configuration by setting
258+
`model.encode_parameters: true`. This will:
259+
- Create a dedicated parameter encoding network (`ParameterModel`)
260+
- Non-dimensionalize parameters using reference values from `config.yaml`
261+
- Integrate parameter encodings into both surface and volume predictions
262+
263+
4. Ensure your simulation data includes global parameter values. The DoMINO
264+
datapipe expects these parameters in the pre-processed `.npy`/`.npz` files:
265+
- Examine `openfoam_datapipe.py` and `process_data.py` for examples of how global
266+
parameter values are incorporated for external aerodynamics
267+
- For the automotive example, `air_density` and `inlet_velocity` remain constant
268+
across simulations
269+
- Adapt these files for your specific case to correctly calculate
270+
`global_params_values` and `global_params_reference` during data preprocessing
271+
272+
5. During training, the model automatically handles global parameter encoding when
273+
`model.encode_parameters: true` is set
274+
- You may need to adapt `train.py` if you plan to use global parameters in loss
275+
functions or de-non-dimensionalization
276+
277+
6. During testing with `test.py`, define `global_params_values` for each test sample:
278+
- Global parameters must match those defined in `config.yaml`
279+
- For each parameter (e.g., "inlet_velocity", "air_density"), provide appropriate
280+
values for each simulation
281+
- See the `main()` function in `test.py` for implementation examples
282+
- If using global parameters for de-non-dimensionalization, modify `test_step()`
283+
284+
7. When inferencing on unseen geometries with `inference_on_stl.py`:
285+
- Define `global_params_values` and `global_params_reference` in both
286+
`compute_solution_in_volume()` and `compute_solution_on_surface()` methods
287+
- Adjust these parameters based on your specific use case and parameters defined
288+
in `config.yaml`
289+
244290
## Extending DoMINO to a custom dataset
245291

246292
This repository includes examples of **DoMINO** training on the DrivAerML dataset.

examples/cfd/external_aerodynamics/domino/src/conf/config.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ variables:
5959
UMeanTrim: vector
6060
pMeanTrim: scalar
6161
nutMeanTrim: scalar
62+
global_parameters:
63+
inlet_velocity:
64+
type: vector
65+
reference: [30.0] # vector [30, 0, 0] should be specified as [30], while [30, 30, 0] should be [30, 30].
66+
air_density:
67+
type: scalar
68+
reference: 1.226
6269

6370
# ┌───────────────────────────────────────────┐
6471
# │ Training Data Configs │
@@ -146,7 +153,6 @@ model:
146153
base_layer: 512
147154
parameter_model:
148155
base_layer: 512
149-
scaling_params: [30.0, 1.226] # [inlet_velocity, air_density]
150156
fourier_features: false
151157
num_modes: 5
152158

@@ -163,7 +169,7 @@ train: # Training configurable parameters
163169
shuffle: true
164170
drop_last: false
165171
checkpoint_dir: /user/models/ # Use only for retraining
166-
172+
167173
# ┌───────────────────────────────────────────┐
168174
# │ Validation Configs │
169175
# └───────────────────────────────────────────┘

examples/cfd/external_aerodynamics/domino/src/inference_on_stl.py

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ def _bvh_query_distance(
151151
sdf_hit_point: wp.array(dtype=wp.vec3f),
152152
sdf_hit_point_id: wp.array(dtype=wp.int32),
153153
):
154-
155154
"""
156155
Computes the signed distance from each point in the given array `points`
157156
to the mesh represented by `mesh`,within the maximum distance `max_dist`,
@@ -690,7 +689,11 @@ def __init__(
690689
self.air_density = torch.full((1, 1), 1.205, dtype=torch.float32).to(
691690
self.device
692691
)
693-
self.num_vol_vars, self.num_surf_vars = self.get_num_variables()
692+
(
693+
self.num_vol_vars,
694+
self.num_surf_vars,
695+
self.num_global_features,
696+
) = self.get_num_variables()
694697
self.model = None
695698
self.grid_resolution = torch.tensor(self.cfg.model.interp_res).to(self.device)
696699
self.vol_factors = None
@@ -755,28 +758,25 @@ def load_bounding_box(self):
755758
self.bounding_box_surface_min_max = [c_min, c_max]
756759

757760
def load_volume_scaling_factors(self):
758-
vol_factors = np.array(
759-
[
760-
[2.2642279, 2.2397292, 1.8689916, 0.7547227],
761-
[-1.2899836, -2.2787743, -1.866153, -2.7116761],
762-
],
763-
dtype=np.float32,
761+
scaling_param_path = self.cfg.eval.scaling_param_path
762+
vol_factors_path = os.path.join(
763+
scaling_param_path, "volume_scaling_factors.npy"
764764
)
765765

766+
vol_factors = np.load(vol_factors_path, allow_pickle=True)
766767
vol_factors = torch.from_numpy(vol_factors).to(self.device)
767768

768769
return vol_factors
769770

770771
def load_surface_scaling_factors(self):
771-
surf_factors = np.array(
772-
[
773-
[0.8215038, 0.01063187, 0.01514608, 0.01327803],
774-
[-2.1505525, -0.01865184, -0.01514422, -0.0121509],
775-
],
776-
dtype=np.float32,
772+
scaling_param_path = self.cfg.eval.scaling_param_path
773+
surf_factors_path = os.path.join(
774+
scaling_param_path, "surface_scaling_factors.npy"
777775
)
778776

777+
surf_factors = np.load(surf_factors_path, allow_pickle=True)
779778
surf_factors = torch.from_numpy(surf_factors).to(self.device)
779+
780780
return surf_factors
781781

782782
def read_stl(self):
@@ -842,14 +842,28 @@ def get_num_variables(self):
842842
num_surf_vars += 3
843843
else:
844844
num_surf_vars += 1
845-
return num_vol_vars, num_surf_vars
845+
846+
num_global_features = 0
847+
global_params_names = list(cfg.variables.global_parameters.keys())
848+
for param in global_params_names:
849+
if cfg.variables.global_parameters[param].type == "vector":
850+
num_global_features += len(
851+
cfg.variables.global_parameters[param].reference
852+
)
853+
elif cfg.variables.global_parameters[param].type == "scalar":
854+
num_global_features += 1
855+
else:
856+
raise ValueError(f"Unknown global parameter type")
857+
858+
return num_vol_vars, num_surf_vars, num_global_features
846859

847860
def initialize_model(self, model_path):
848861
model = (
849862
DoMINO(
850863
input_features=3,
851864
output_features_vol=self.num_vol_vars,
852865
output_features_surf=self.num_surf_vars,
866+
global_features=self.num_global_features,
853867
model_parameters=self.cfg.model,
854868
)
855869
.to(self.device)
@@ -1358,6 +1372,21 @@ def compute_solution_on_surface(
13581372
inlet_velocity,
13591373
air_density,
13601374
):
1375+
"""
1376+
Global parameters: For this particular case, the model was trained on single velocity/density values
1377+
across all simulations. Hence, global_params_values and global_params_reference are the same.
1378+
"""
1379+
global_params_values = torch.cat(
1380+
(inlet_velocity, air_density), axis=1
1381+
) # (1, 2)
1382+
global_params_values = torch.unsqueeze(global_params_values, -1) # (1, 2, 1)
1383+
1384+
global_params_reference = torch.cat(
1385+
(inlet_velocity, air_density), axis=1
1386+
) # (1, 2)
1387+
global_params_reference = torch.unsqueeze(
1388+
global_params_reference, -1
1389+
) # (1, 2, 1)
13611390

13621391
if self.dist.world_size == 1:
13631392
geo_encoding_local = model.geo_encoding_local(
@@ -1383,8 +1412,8 @@ def compute_solution_on_surface(
13831412
surface_neighbors_normals,
13841413
surface_areas,
13851414
surface_neighbors_areas,
1386-
inlet_velocity,
1387-
air_density,
1415+
global_params_values,
1416+
global_params_reference,
13881417
)
13891418
else:
13901419
pos_encoding = model.module.position_encoder(
@@ -1399,8 +1428,8 @@ def compute_solution_on_surface(
13991428
surface_neighbors_normals,
14001429
surface_areas,
14011430
surface_neighbors_areas,
1402-
inlet_velocity,
1403-
air_density,
1431+
global_params_values,
1432+
global_params_reference,
14041433
)
14051434

14061435
return tpredictions_batch
@@ -1420,6 +1449,19 @@ def compute_solution_in_volume(
14201449
air_density,
14211450
):
14221451

1452+
## Global parameters
1453+
global_params_values = torch.cat(
1454+
(inlet_velocity, air_density), axis=1
1455+
) # (1, 2)
1456+
global_params_values = torch.unsqueeze(global_params_values, -1) # (1, 2, 1)
1457+
1458+
global_params_reference = torch.cat(
1459+
(inlet_velocity, air_density), axis=1
1460+
) # (1, 2)
1461+
global_params_reference = torch.unsqueeze(
1462+
global_params_reference, -1
1463+
) # (1, 2, 1)
1464+
14231465
if self.dist.world_size == 1:
14241466
geo_encoding_local = model.geo_encoding_local(
14251467
geo_encoding, volume_mesh_centers, p_grid, mode="volume"
@@ -1441,8 +1483,8 @@ def compute_solution_in_volume(
14411483
volume_mesh_centers,
14421484
geo_encoding_local,
14431485
pos_encoding,
1444-
inlet_velocity,
1445-
air_density,
1486+
global_params_values,
1487+
global_params_reference,
14461488
num_sample_points=self.stencil_size,
14471489
eval_mode="volume",
14481490
)
@@ -1454,8 +1496,8 @@ def compute_solution_in_volume(
14541496
volume_mesh_centers,
14551497
geo_encoding_local,
14561498
pos_encoding,
1457-
inlet_velocity,
1458-
air_density,
1499+
global_params_values,
1500+
global_params_reference,
14591501
num_sample_points=self.stencil_size,
14601502
eval_mode="volume",
14611503
)
@@ -1481,8 +1523,8 @@ def compute_solution_in_volume(
14811523

14821524
domino = dominoInference(cfg, dist, False)
14831525
domino.initialize_model(
1484-
model_path="/lustre/rranade/modulus_dev/modulus_forked/modulus/examples/cfd/external_aerodynamics/domino/outputs/AWS_Dataset/19/models/DoMINO.0.201.pt"
1485-
)
1526+
model_path="/lustre/models/DoMINO.0.7.pt"
1527+
) ## Replace the model path with location of the trained model
14861528

14871529
for count, dirname in enumerate(dirnames_per_gpu):
14881530
# print(f"Processing file {dirname}")
@@ -1525,9 +1567,8 @@ def compute_solution_in_volume(
15251567
"Lift:",
15261568
out_dict["lift_force"],
15271569
)
1528-
vtp_path = f"/lustre/rranade/modulus_dev/modulus_forked/modulus/examples/cfd/external_aerodynamics/domino/pred_{dirname}_4.vtp"
1570+
vtp_path = f"/lustre/snidhan/physicsnemo-work/domino-global-param-runs/stl-results/pred_{dirname}_4.vtp"
15291571
domino.mesh_stl.save(vtp_path)
1530-
# vtp_path = f"/lustre/rranade/modulus_dev/modulus_demo/modulus_rishi/modulus/examples/cfd/external_aerodynamics/domino_gtc_demo/sensitivity_pred_{dirname}.vtp"
15311572
reader = vtk.vtkXMLPolyDataReader()
15321573
reader.SetFileName(f"{vtp_path}")
15331574
reader.Update()

0 commit comments

Comments
 (0)