From 94305521f4f0676b7c75f14b2711a646de01a401 Mon Sep 17 00:00:00 2001 From: alesiani Date: Fri, 10 Jun 2022 17:34:42 +0200 Subject: [PATCH 001/137] update package related information --- README.md | 2 +- pdebench/__init__.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 pdebench/__init__.py diff --git a/README.md b/README.md index 1ed849b..fa1f7f5 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ PDEBench features a much wider range of PDEs than existing approaches including ## Installation - pip install pdebench + pip install . ## Requirements diff --git a/pdebench/__init__.py b/pdebench/__init__.py new file mode 100644 index 0000000..37574ba --- /dev/null +++ b/pdebench/__init__.py @@ -0,0 +1,25 @@ +""" +PDEBench: An Extensive Benchmark for Scientific Machine Learning + +In this work, we provide a diverse and comprehensive benchmark for scientific machine learning, including a variety of challenging and representative range of physical problems. +This repository consists of the codes used to generate the datasets, upload and download the datasets from the data repository, train and evaluate different machine learning models as baseline. +PDEBench features a much wider range of PDEs than existing approaches including realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial or boundary conditions and model parameters, and extensible source codes. + +Citations + +@online{PDEBenchDataset, + author = {Makoto Takamoto and Timothy Pradita and Raphael Leiteritz and Dan MacKinlay and Francesco Alesiani and Dirk Pflüger and Mathias Niepert}, + title = {{PDEBench}: A Diverse and Comprehensive Benchmark for Scientific Machine Learning}, + year = 2022, + doi = {doi:10.18419/darus-2986}, + urldoi = {http://dx.doi.org/10.18419/darus-2986}, + url = {https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01}, + urldate = {2022-06-06} +} + + +""" + +__version__ = "0.0.1" +__author__ = 'Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert' +__credits__ = 'NEC labs Europe, University of Stuttgart, CSIRO’s Data61' From e58e6115a26212688689a49662e3b0d1420389b4 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Sat, 11 Jun 2022 11:11:00 +0200 Subject: [PATCH 002/137] Fixed bug in fno and unet train scripts for concatenated training --- pdebench/models/fno/train.py | 6 ++++-- pdebench/models/unet/train.py | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 65a2846..fde4af1 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -228,7 +228,8 @@ def run_training(if_training, train_l2_step += loss.item() _batch = yy.size(0) - l2_full = loss_fn(pred.reshape(_batch, -1), yy.reshape(_batch, -1)) + _yy = yy[..., :t_train, :] # if t_train is not -1 + l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() optimizer.zero_grad() @@ -278,7 +279,8 @@ def run_training(if_training, val_l2_step += loss.item() _batch = yy.size(0) - val_l2_full += loss_fn(pred.reshape(_batch, -1), yy.reshape(_batch, -1)).item() + _yy = yy[..., :t_train, :] + val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if training_type in ['single']: x = xx[..., 0 , :] diff --git a/pdebench/models/unet/train.py b/pdebench/models/unet/train.py index cf9b57f..bd3ba50 100644 --- a/pdebench/models/unet/train.py +++ b/pdebench/models/unet/train.py @@ -273,7 +273,9 @@ def run_training(if_training, xx = torch.cat((xx[..., 1:, :], im), dim=-2) train_l2_step += loss.item() - l2_full = loss_fn(pred.reshape(batch_size, -1), yy.reshape(batch_size, -1)) + _batch = yy.size(0) + _yy = yy[..., :t_train, :] + l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() optimizer.zero_grad() @@ -327,7 +329,9 @@ def run_training(if_training, xx = torch.cat((xx[..., 1:, :], im), dim=-2) val_l2_step += loss.item() - val_l2_full += loss_fn(pred.reshape(batch_size, -1), yy.reshape(batch_size, -1)).item() + _batch = yy.size(0) + _yy = yy[..., :t_train, :] + val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if training_type in ['single']: x = xx[..., 0 , :] @@ -408,7 +412,9 @@ def run_training(if_training, # xx = torch.cat((xx[..., 1:, :], im), dim=-2) train_l2_step += loss.item() - l2_full = loss_fn(pred.reshape(batch_size, -1), yy.reshape(batch_size, -1)) + _batch = yy.size(0) + _yy = yy[..., :t_train, :] # if t_train is not -1 + l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() optimizer.zero_grad() @@ -444,7 +450,9 @@ def run_training(if_training, pred = torch.cat((pred, im), -2) val_l2_step += loss.item() - val_l2_full += loss_fn(pred.reshape(batch_size, -1), yy.reshape(batch_size, -1)).item() + _batch = yy.size(0) + _yy = yy[..., :t_train, :] # if t_train is not -1 + val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if val_l2_full < loss_val_min: loss_val_min = val_l2_full From e995765c66b784e9cdcfcc0487d6598e4949142f Mon Sep 17 00:00:00 2001 From: Francesco Alesiani <67260726+falesiani@users.noreply.github.com> Date: Mon, 13 Jun 2022 11:22:03 +0200 Subject: [PATCH 003/137] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fa1f7f5..95cbb69 100644 --- a/README.md +++ b/README.md @@ -200,4 +200,9 @@ author = {M. Raissi and P. Perdikaris and G.E. Karniadakis} * [Gefei Shan](https://github.com/davecatmeow) * [Yizhou Yang](https://github.com/verdantwynnd) * [Ran Zhang](https://github.com/maphyca) -* [Simon Brown](https://github.com/SimonSyBrown) \ No newline at end of file +* [Simon Brown](https://github.com/SimonSyBrown) + + +## License +MIT for solver code and baseline code +NLE Academic License for selected code From 48134849f2f1b4a4e8c5d4505432e82670f7d44f Mon Sep 17 00:00:00 2001 From: timothypraditia Date: Mon, 13 Jun 2022 18:25:03 +0200 Subject: [PATCH 004/137] Update README to include the instruction on how to use pre-trained models --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 95cbb69..22c5819 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ An example to run the forward model training can be found in [run_forward_1D.sh] ## Datasets and pretrained models We provide the benchmark datasets we used in the paper through our [data repository](https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01). The data generation configuration can be found in the paper. -Additionally, the pretrained models are also available to be downloaded [here](https://darus.uni-stuttgart.de/privateurl.xhtml?token=cd862f8c-8e1b-49d2-b4da-b35f8df5ac85). +Additionally, the pretrained models are also available to be downloaded [here](https://darus.uni-stuttgart.de/privateurl.xhtml?token=cd862f8c-8e1b-49d2-b4da-b35f8df5ac85). To use the pretrained models, users can specify the argument `continue_training: True` in the [config file](./pdebench/models/config/config.yaml). ## Citations @@ -204,5 +204,4 @@ author = {M. Raissi and P. Perdikaris and G.E. Karniadakis} ## License -MIT for solver code and baseline code -NLE Academic License for selected code +MIT for solver code and baseline code, and NLE Academic License for selected code From 9fe186159151b8382f9c24e6138bac8bd1f858a7 Mon Sep 17 00:00:00 2001 From: alesiani Date: Tue, 14 Jun 2022 09:14:48 +0200 Subject: [PATCH 005/137] init file --- pdebench/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/__init__.py b/pdebench/__init__.py index 37574ba..010919c 100644 --- a/pdebench/__init__.py +++ b/pdebench/__init__.py @@ -22,4 +22,4 @@ __version__ = "0.0.1" __author__ = 'Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert' -__credits__ = 'NEC labs Europe, University of Stuttgart, CSIRO’s Data61' +__credits__ = 'NEC labs Europe, University of Stuttgart, CSIRO''s Data61' From 22d6a709912ff8d0135ee6797be0e5d9ec48cdc3 Mon Sep 17 00:00:00 2001 From: Francesco Alesiani <67260726+falesiani@users.noreply.github.com> Date: Tue, 14 Jun 2022 18:09:05 +0200 Subject: [PATCH 006/137] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 22c5819..8a109de 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,28 @@ # PDEBench This repository contains the code for the paper: + PDEBench: An Extensive Benchmark for Scientific Machine Learning In this work, we provide a diverse and comprehensive benchmark for scientific machine learning, including a variety of challenging and representative range of physical problems. This repository consists of the codes used to generate the datasets, upload and download the datasets from the data repository, train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing approaches including realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial or boundary conditions and model parameters, and extensible source codes. +## Dataset + +Temporary url + +https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01 + + +Permanent url + +https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986 + +DOI + +[![DOI:10.18419/darus-2986](https://img.shields.io/badge/DOI-doi%3A10.18419%2Fdarus--2986-red)](https://doi.org/10.18419/darus-2986) + ## Installation pip install . From d31466c6fa36ebd36e85b532adab11c2f92e57a4 Mon Sep 17 00:00:00 2001 From: Francesco Alesiani <67260726+falesiani@users.noreply.github.com> Date: Wed, 15 Jun 2022 16:10:03 +0200 Subject: [PATCH 007/137] Update README.md Added DOIs --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a109de..cebb75e 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,25 @@ PDEBench features a much wider range of PDEs than existing approaches including Temporary url +PDEBench Dataset https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01 +PDEBench Pre-Trained Models +https://darus.uni-stuttgart.de/privateurl.xhtml?token=cd862f8c-8e1b-49d2-b4da-b35f8df5ac85 Permanent url +PDEBench Dataset https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986 -DOI +PDEBench Pre-Trained Models +https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987 + + +DOIs [![DOI:10.18419/darus-2986](https://img.shields.io/badge/DOI-doi%3A10.18419%2Fdarus--2986-red)](https://doi.org/10.18419/darus-2986) +[![DOI:10.18419/darus-2987](https://img.shields.io/badge/DOI-doi%3A10.18419%2Fdarus--2987-red)](https://doi.org/10.18419/darus-2987) ## Installation From 4da9b89b0addf7506ed0d79c664cdb2c75719bc2 Mon Sep 17 00:00:00 2001 From: Francesco Alesiani <67260726+falesiani@users.noreply.github.com> Date: Wed, 15 Jun 2022 16:45:38 +0200 Subject: [PATCH 008/137] Update README.md added the bib entires for the dataset and the pre-trained model. Once doi is active we need to change the main bib entry's url --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index cebb75e..54497b3 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,26 @@ Additionally, the pretrained models are also available to be downloaded [here](h urldate = {2022-06-06} } + +@data{darus-2986_2022, +author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, +publisher = {DaRUS}, +title = {{PDEBench Datasets}}, +year = {2022}, +doi = {10.18419/darus-2986}, +url = {https://doi.org/10.18419/darus-2986} +} + +@data{darus-2987_2022, +author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, +publisher = {DaRUS}, +title = {{PDEBench Pretrained Models}}, +year = {2022}, +doi = {10.18419/darus-2987}, +url = {https://doi.org/10.18419/darus-2987} +} + + @misc{li2020fourier, title={Fourier Neural Operator for Parametric Partial Differential Equations}, author={Zongyi Li and Nikola Kovachki and Kamyar Azizzadenesheli and Burigede Liu and Kaushik Bhattacharya and Andrew Stuart and Anima Anandkumar}, From baf74838b120993cd8f76e6288d5d3ddcc446780 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Tue, 21 Jun 2022 10:10:49 +0200 Subject: [PATCH 009/137] Added implementation for 3D scenario, modified README.md, added data generation from NLE --- README.md | 22 +- .../data_gen_NLE/.idea/deployment.xml | 42 + .../data_gen_NLE/.idea/fluid_sims.iml | 8 + .../inspectionProfiles/Project_Default.xml | 19 + .../inspectionProfiles/profiles_settings.xml | 6 + pdebench/data_gen/data_gen_NLE/.idea/misc.xml | 4 + .../data_gen/data_gen_NLE/.idea/modules.xml | 8 + pdebench/data_gen/data_gen_NLE/.idea/vcs.xml | 6 + .../data_gen/data_gen_NLE/.idea/workspace.xml | 43 + .../AdvectionEq/advection_exact_Hydra.py | 213 +++ .../advection_multi_solution_Hydra.py | 282 +++ .../AdvectionEq/config/args/beta1e-1.yaml | 10 + .../AdvectionEq/config/args/beta1e0.yaml | 10 + .../AdvectionEq/config/args/beta1e1.yaml | 10 + .../AdvectionEq/config/args/beta2e-1.yaml | 10 + .../AdvectionEq/config/args/beta2e0.yaml | 10 + .../AdvectionEq/config/args/beta4e-1.yaml | 10 + .../AdvectionEq/config/args/beta4e0.yaml | 10 + .../AdvectionEq/config/args/config.yaml | 10 + .../AdvectionEq/config/multi/beta1e-1.yaml | 15 + .../AdvectionEq/config/multi/beta1e0.yaml | 15 + .../AdvectionEq/config/multi/beta2e-1.yaml | 15 + .../AdvectionEq/config/multi/beta2e0.yaml | 15 + .../AdvectionEq/config/multi/beta4e-1.yaml | 15 + .../AdvectionEq/config/multi/beta4e0.yaml | 15 + .../AdvectionEq/config/multi/config.yaml | 15 + .../AdvectionEq/config/multi/config2D.yaml | 19 + .../data_gen_NLE/AdvectionEq/run_testset.sh | 7 + .../data_gen_NLE/AdvectionEq/run_trainset.sh | 6 + .../data_gen_NLE/BurgersEq/burgers_Hydra.py | 288 +++ .../BurgersEq/burgers_multi_solution_Hydra.py | 286 +++ .../BurgersEq/config/args/config.yaml | 17 + .../BurgersEq/config/args/possin_eps1e-1.yaml | 15 + .../BurgersEq/config/args/possin_eps1e-2.yaml | 15 + .../BurgersEq/config/args/possin_eps1e-3.yaml | 15 + .../BurgersEq/config/args/possin_eps1e0.yaml | 15 + .../BurgersEq/config/args/possin_eps1e1.yaml | 15 + .../BurgersEq/config/args/possin_eps1e2.yaml | 15 + .../BurgersEq/config/args/sin_eps1e-1.yaml | 15 + .../BurgersEq/config/args/sin_eps1e-2.yaml | 15 + .../config/args/sin_eps1e-2_u01e-1.yaml | 15 + .../config/args/sin_eps1e-2_u01e-2.yaml | 15 + .../config/args/sin_eps1e-2_u01e1.yaml | 15 + .../config/args/sin_eps1e-2_u01e2.yaml | 15 + .../BurgersEq/config/args/sin_eps1e-3.yaml | 15 + .../BurgersEq/config/args/sin_eps1e0.yaml | 15 + .../BurgersEq/config/args/sin_eps1e1.yaml | 15 + .../BurgersEq/config/args/sin_eps1e2.yaml | 15 + .../config/args/sinsin_eps1e-2_du01.yaml | 15 + .../config/args/sinsin_eps1e-2_du025.yaml | 15 + .../config/args/sinsin_eps1e-2_du05.yaml | 15 + .../config/args/sinsin_eps1e-2_du1.yaml | 15 + .../config/args/sinsin_eps1e-2_du2.yaml | 15 + .../config/args/sinsin_eps1e-2_du5.yaml | 15 + .../BurgersEq/config/multi/1e-1.yaml | 14 + .../BurgersEq/config/multi/1e-2.yaml | 14 + .../BurgersEq/config/multi/1e-3.yaml | 14 + .../BurgersEq/config/multi/1e0.yaml | 14 + .../BurgersEq/config/multi/2e-1.yaml | 14 + .../BurgersEq/config/multi/2e-2.yaml | 14 + .../BurgersEq/config/multi/2e-3.yaml | 14 + .../BurgersEq/config/multi/2e0.yaml | 14 + .../BurgersEq/config/multi/4e-1.yaml | 14 + .../BurgersEq/config/multi/4e-2.yaml | 14 + .../BurgersEq/config/multi/4e-3.yaml | 14 + .../BurgersEq/config/multi/4e0.yaml | 14 + .../BurgersEq/config/multi/config.yaml | 12 + .../data_gen_NLE/BurgersEq/run_testset.sh | 25 + .../data_gen_NLE/BurgersEq/run_trainset.sh | 12 + .../CompressibleFluid/CFD_Hydra.py | 519 ++++++ .../CompressibleFluid/CFD_multi_Hydra.py | 644 +++++++ .../config/args/1D_Multi.yaml | 27 + .../config/args/1D_Multi_shock.yaml | 27 + .../config/args/1D_Multi_trans.yaml | 27 + .../config/args/1D_ShockTube.yaml | 29 + .../config/args/1D_ShockTube2.yaml | 29 + .../config/args/1D_ShockTube3.yaml | 29 + .../config/args/1D_ShockTube4.yaml | 29 + .../config/args/1D_ShockTube5.yaml | 29 + .../config/args/1D_ShockTube6.yaml | 29 + .../config/args/1D_ShockTube7.yaml | 29 + .../config/args/2D_KH_M01_dk1.yaml | 29 + .../config/args/2D_KH_M01_dk10.yaml | 29 + .../config/args/2D_KH_M01_dk2.yaml | 29 + .../config/args/2D_KH_M01_dk5.yaml | 29 + .../config/args/2D_KH_M02_dk1.yaml | 29 + .../config/args/2D_KH_M04_dk1.yaml | 29 + .../config/args/2D_KH_M1_dk1.yaml | 29 + .../config/args/2D_KH_M2_dk1.yaml | 29 + .../config/args/2D_Multi_KH.yaml | 27 + .../config/args/2D_Multi_Rand.yaml | 28 + .../config/args/2D_Multi_Rand_HR.yaml | 28 + .../config/args/2D_Multi_Turb.yaml | 28 + .../config/args/2D_ShockTube.yaml | 29 + .../CompressibleFluid/config/args/2D_TOV.yaml | 29 + .../config/args/3D_BlastWave.yaml | 29 + .../config/args/3D_Multi_Rand.yaml | 28 + .../config/args/3D_Multi_TurbM1.yaml | 28 + .../config/args/3D_TurbM01.yaml | 29 + .../config/args/3D_TurbM05.yaml | 29 + .../config/args/3D_TurbM1.yaml | 29 + .../config/args/3D_TurbM2.yaml | 29 + .../config/args/3D_TurbM4.yaml | 29 + .../config/args/default.yaml | 26 + .../CompressibleFluid/config/config.yaml | 9 + .../CompressibleFluid/run_testset.sh | 7 + .../CompressibleFluid/run_testset_3DTurb.sh | 6 + .../CompressibleFluid/run_testset_KHI.sh | 8 + .../CompressibleFluid/run_trainset_1D.sh | 9 + .../CompressibleFluid/run_trainset_1DShock.sh | 9 + .../run_trainset_1D_trans.sh | 9 + .../CompressibleFluid/run_trainset_2D.sh | 10 + .../CompressibleFluid/run_trainset_2DTurb.sh | 10 + .../CompressibleFluid/run_trainset_3D.sh | 9 + .../CompressibleFluid/run_trainset_3DTurb.sh | 9 + pdebench/data_gen/data_gen_NLE/Data_Merge.py | 353 ++++ .../config/args/Rho1e0_Nu1e0.yaml | 13 + .../config/args/Rho1e0_Nu2e0.yaml | 13 + .../config/args/Rho1e0_Nu5e-1.yaml | 13 + .../config/args/Rho1e0_Nu5e0.yaml | 13 + .../config/args/Rho1e1_Nu1e0.yaml | 13 + .../config/args/Rho1e1_Nu2e0.yaml | 13 + .../config/args/Rho1e1_Nu5e-1.yaml | 13 + .../config/args/Rho1e1_Nu5e0.yaml | 13 + .../config/args/Rho2e0_Nu1e0.yaml | 13 + .../config/args/Rho2e0_Nu2e0.yaml | 13 + .../config/args/Rho2e0_Nu5e-1.yaml | 13 + .../config/args/Rho2e0_Nu5e0.yaml | 13 + .../config/args/Rho5e0_Nu1e0.yaml | 13 + .../config/args/Rho5e0_Nu2e0.yaml | 13 + .../config/args/Rho5e0_Nu5e-1.yaml | 13 + .../config/args/Rho5e0_Nu5e0.yaml | 13 + .../config/args/config.yaml | 13 + .../config/args/config_2D.yaml | 16 + .../config/multi/Rho1e0_Nu1e0.yaml | 16 + .../config/multi/Rho1e0_Nu1e1.yaml | 16 + .../config/multi/Rho1e0_Nu2e0.yaml | 16 + .../config/multi/Rho1e0_Nu5e-1.yaml | 16 + .../config/multi/Rho1e0_Nu5e0.yaml | 16 + .../config/multi/Rho1e1_Nu1e0.yaml | 16 + .../config/multi/Rho1e1_Nu1e1.yaml | 16 + .../config/multi/Rho1e1_Nu2e0.yaml | 16 + .../config/multi/Rho1e1_Nu5e-1.yaml | 16 + .../config/multi/Rho1e1_Nu5e0.yaml | 16 + .../config/multi/Rho2e0_Nu1e0.yaml | 16 + .../config/multi/Rho2e0_Nu1e1.yaml | 16 + .../config/multi/Rho2e0_Nu2e0.yaml | 16 + .../config/multi/Rho2e0_Nu5e-1.yaml | 16 + .../config/multi/Rho2e0_Nu5e0.yaml | 16 + .../config/multi/Rho5e0_Nu1e0.yaml | 16 + .../config/multi/Rho5e0_Nu1e1.yaml | 16 + .../config/multi/Rho5e0_Nu2e0.yaml | 16 + .../config/multi/Rho5e0_Nu5e-1.yaml | 16 + .../config/multi/Rho5e0_Nu5e0.yaml | 16 + .../config/multi/config.yaml | 14 + .../config/multi/config_2D.yaml | 17 + ...ction_diffusion_2D_multi_solution_Hydra.py | 307 ++++ .../reaction_diffusion_Hydra.py | 266 +++ ...reaction_diffusion_multi_solution_Hydra.py | 294 ++++ .../ReactionDiffusionEq/run_testset.sh | 20 + .../ReactionDiffusionEq/run_trainset.sh | 16 + .../ReactionDiffusionEq/run_trainset_2D.sh | 10 + .../data_gen/data_gen_NLE/config/config.yaml | 14 + .../data_gen/data_gen_NLE/save/CFD/.gitignore | 4 + .../data_gen_NLE/save/ReacDiff/.gitignore | 4 + .../data_gen_NLE/save/advection/.gitignore | 4 + .../data_gen_NLE/save/burgers/.gitignore | 4 + pdebench/data_gen/data_gen_NLE/utils.py | 1558 +++++++++++++++++ pdebench/models/analyse_result_forward.py | 28 +- pdebench/models/config/args/config_2DCFD.yaml | 25 + pdebench/models/config/args/config_3DCFD.yaml | 24 + .../models/config/args/config_ReacDiff.yaml | 25 + .../models/config/args/config_pinn_CFD1d.yaml | 15 + .../models/config/args/config_pinn_pde1d.yaml | 15 + pdebench/models/fno/utils.py | 49 + pdebench/models/metrics.py | 1 + pdebench/models/pinn/pde_definitions.py | 123 ++ pdebench/models/pinn/train.py | 165 ++ pdebench/models/pinn/utils.py | 384 ++++ pdebench/models/unet/utils.py | 38 + 180 files changed, 8534 insertions(+), 7 deletions(-) create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/deployment.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/misc.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/modules.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/vcs.xml create mode 100644 pdebench/data_gen/data_gen_NLE/.idea/workspace.xml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh create mode 100644 pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh create mode 100644 pdebench/data_gen/data_gen_NLE/Data_Merge.py create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh create mode 100644 pdebench/data_gen/data_gen_NLE/config/config.yaml create mode 100644 pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore create mode 100644 pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore create mode 100644 pdebench/data_gen/data_gen_NLE/save/advection/.gitignore create mode 100644 pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore create mode 100644 pdebench/data_gen/data_gen_NLE/utils.py create mode 100644 pdebench/models/config/args/config_2DCFD.yaml create mode 100644 pdebench/models/config/args/config_3DCFD.yaml create mode 100644 pdebench/models/config/args/config_ReacDiff.yaml create mode 100644 pdebench/models/config/args/config_pinn_CFD1d.yaml create mode 100644 pdebench/models/config/args/config_pinn_pde1d.yaml diff --git a/README.md b/README.md index 54497b3..f293805 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # PDEBench -This repository contains the code for the paper: +By : Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert +This repository contains the code for the paper: PDEBench: An Extensive Benchmark for Scientific Machine Learning In this work, we provide a diverse and comprehensive benchmark for scientific machine learning, including a variety of challenging and representative range of physical problems. @@ -74,6 +75,25 @@ The data generation codes are contained in [data_gen](./pdebench/data_gen): - `configs` directory contains the yaml files storing the configuration for the simulation. Arguments for the simulation are problem-specific and detailed explanation can be found in the simulation scripts. - `src` directory contains the simulation scripts for different problems: `sim_diff_react-py` for 2D diffusion-reaction, `sim_diff_sorp.py` for 1D diffusion-sorption, and `swe` for the shallow-water equation. +### Data Generation for 1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes Equations +The data generation codes are contained in [data_gen_NLE](./pdebench/data_gen/data_gen_NLE/): +- `utils.py` util file for data generation, mainly boundary conditions and initial conditions. +- `AdvectionEq` directory with the source codes to generate 1D Advection equation training samples +- `BurgersEq` directory with the source codes to generate 1D Burgers equation training samples +- `CompressibleFluid` directory with the source codes to generate compressible Navier-Stokes equations training samples +- `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples +- `save` directory saving the generated training samples + +A typical example to generate training samples (1D Advection Equation): +(in `data_gen/data_gen_NLE/AdvectionEq/`) +```bash +python3 advection_multi_solution_Hydra.py +multi=beta1e0.yaml +``` +which is assumed to be performed in each directory. + +Examples for generating other PDEs are provided in `run_trainset.sh` in each PDE's directories. +The config files for Hydra are stored in `config` directory in each PDE's directory. + ## Configuration You can sent the default values for data locations for this project by putting config vars like this in the `.env` file: diff --git a/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml b/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml new file mode 100644 index 0000000..1988f52 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml b/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6e5f9ee --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/misc.xml b/pdebench/data_gen/data_gen_NLE/.idea/misc.xml new file mode 100644 index 0000000..d1e22ec --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/modules.xml b/pdebench/data_gen/data_gen_NLE/.idea/modules.xml new file mode 100644 index 0000000..b404b77 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml b/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml b/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml new file mode 100644 index 0000000..383506f --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1648051406434 + + + + \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py new file mode 100644 index 0000000..1938425 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: advection_exact_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import time +import sys +from math import ceil + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +import jax.numpy as jnp +from jax import device_put + + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + print('advection velocity: {}'.format(cfg.args.beta)) + + # cell edge coordinate + xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * (xe[1] - xe[0]) + # t-coordinate + it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 + tc = jnp.arange(it_tot + 1) * cfg.args.dt_save + + def evolve(u): + t = cfg.args.ini_time + i_save = 0 + tm_ini = time.time() + + it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time)/cfg.args.dt_save) + 1 + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + while t < cfg.args.fin_time: + print('save data at t = {0:.3f}'.format(t)) + u = set_function(xc, t, cfg.args.beta) + uu = uu.at[i_save].set(u) + t += cfg.args.dt_save + i_save += 1 + + tm_fin = time.time() + print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + uu = uu.at[-1].set(u) + return uu, t + + @jax.jit + def set_function(x, t, beta): + return jnp.sin(2.*jnp.pi*(x - beta*t)) + + u = set_function(xc, t=0, beta=cfg.args.beta) + u = device_put(u) # putting variables in GPU (not necessary??) + uu, t = evolve(u) + print('final time is: {0:.3f}'.format(t)) + + + print('data saving...') + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd + cfg.args.save + '/Advection_beta' + str(cfg.args.beta), uu) + jnp.save(cwd + cfg.args.save + '/x_coordinate', xe) + jnp.save(cwd + cfg.args.save + '/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py new file mode 100644 index 0000000..5d3ee79 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: advection_multi_solution_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" +import sys +from math import ceil, log, exp +import random + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +from jax import vmap +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import init_multi, Courant, save_data, bc, limiting + + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + # basic parameters + dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx + dx_inv = 1. / dx + + # cell edge coordinate + xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + # t-coordinate + it_tot = ceil((cfg.multi.fin_time - cfg.multi.ini_time) / cfg.multi.dt_save) + 1 + tc = jnp.arange(it_tot + 1) * cfg.multi.dt_save + + show_steps = cfg.multi.show_steps + ini_time = cfg.multi.ini_time + fin_time = cfg.multi.fin_time + dt_save = cfg.multi.dt_save + CFL = cfg.multi.CFL + if cfg.multi.if_rand_param: + beta = exp(random.uniform(log(0.01), log(100))) # uniform number between 0.01 to 100 + else: + beta = cfg.multi.beta + + print('beta: ', beta) + + @jax.jit + def evolve(u): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _show(_carry): + u, tsave, i_save, uu = _carry + uu = uu.at[i_save].set(u) + tsave += dt_save + i_save += 1 + return (u, tsave, i_save, uu) + + t, tsave, steps, i_save, dt, u, uu = carry + + carry = (u, tsave, i_save, uu) + u, tsave, i_save, uu = lax.cond(t >= tsave, _show, _pass, carry) + + carry = (u, t, dt, steps, tsave) + u, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, u, uu) + + carry = t, tsave, steps, i_save, dt, u, uu + t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) + uu = uu.at[-1].set(u) + + return uu + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave = carry + dt = Courant(jnp.array([beta]), dx) * CFL + dt = jnp.min(jnp.array([dt, fin_time - t, tsave - t])) + + def _update(carry): + u, dt = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt) + return u, dt + + carry = u, dt + u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave + + @jax.jit + def update(u, u_tmp, dt): + f = flux(u_tmp) + u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + return u + + def flux(u): + _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u + uL, uR = limiting(_u, cfg.multi.nx, if_second_order=cfg.multi.if_second_order) + fL = uL * beta + fR = uR * beta + # upwind advection scheme + f_upwd = (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] + - jnp.abs(beta)*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) + return f_upwd + + u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) + u = device_put(u) # putting variables in GPU (not necessary??) + + #vm_evolve = vmap(evolve, 0, 0) + #uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + local_devices = jax.local_device_count() + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd+cfg.multi.save+'1D_Advection_Sols_beta'+str(beta)[:5], uu) + jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) + jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml new file mode 100644 index 0000000..481e65f --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e-1 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml new file mode 100644 index 0000000..c1a1492 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e0 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml new file mode 100644 index 0000000..a7164b1 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e1 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml new file mode 100644 index 0000000..3d324c5 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 2.e-1 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml new file mode 100644 index 0000000..fd69b3c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 2.e0 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml new file mode 100644 index 0000000..91a0ad9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 4.e-1 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml new file mode 100644 index 0000000..3788878 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 4.e0 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml new file mode 100644 index 0000000..c1a1492 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml @@ -0,0 +1,10 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e0 +if_show: 1 +init_mode: 'sin' diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml new file mode 100644 index 0000000..569c31a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e-1 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml new file mode 100644 index 0000000..93af1e3 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e0 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml new file mode 100644 index 0000000..25edbee --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 2.e-1 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml new file mode 100644 index 0000000..5171dc6 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 2.e0 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml new file mode 100644 index 0000000..2b5385d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 4.e-1 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml new file mode 100644 index 0000000..a9b2961 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 4.e0 +if_show: 1 +numbers: 10000 +CFL: 4.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml new file mode 100644 index 0000000..6e60508 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml @@ -0,0 +1,15 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: 0. +xR: 1. +beta : 1.e0 +if_show: 1 +numbers: 100 +CFL: 3.e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: 1 diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml new file mode 100644 index 0000000..06c0f34 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml @@ -0,0 +1,19 @@ +save: '../save/advection/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 32 +ny: 32 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +betaX : 1.e0 +betaY : 1.e0 +if_show: 1 +numbers: 4 +CFL: 2.5e-1 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh new file mode 100644 index 0000000..384989a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh @@ -0,0 +1,7 @@ +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh new file mode 100644 index 0000000..0d92fee --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh @@ -0,0 +1,6 @@ +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta1e0.yaml +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta1e-1.yaml +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta2e0.yaml +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta2e-1.yaml +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta4e0.yaml +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta4e-1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py new file mode 100644 index 0000000..50f438c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: burgers_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import time +import sys +from math import ceil + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import init, Courant, Courant_diff, save_data, bc, limiting + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + # basic parameters + pi_inv = 1. / jnp.pi + dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx + dx_inv = 1. / dx + + # cell edge coordinate + xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + + show_steps = cfg.args.show_steps + ini_time = cfg.args.ini_time + fin_time = cfg.args.fin_time + dt_save = cfg.args.dt_save + CFL = cfg.args.CFL + + # t-coordinate + it_tot = ceil((fin_time - ini_time) / dt_save) + 1 + tc = jnp.arange(it_tot + 1) * dt_save + + @jax.jit + def evolve(u): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + tm_ini = time.time() + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _save(_carry): + u, tsave, i_save, uu = _carry + uu = uu.at[i_save].set(u) + tsave += dt_save + i_save += 1 + return (u, tsave, i_save, uu) + + t, tsave, steps, i_save, dt, u, uu = carry + + # if save data + carry = (u, tsave, i_save, uu) + u, tsave, i_save, uu = lax.cond(t >= tsave, _save, _pass, carry) + + carry = (u, t, dt, steps, tsave) + u, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, u, uu) + + carry = t, tsave, steps, i_save, dt, u, uu + t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) + uu = uu.at[-1].set(u) + + tm_fin = time.time() + print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + return uu, t + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave = carry + dt_adv = Courant(u, dx) * CFL + dt_dif = Courant_diff(dx, cfg.args.epsilon*pi_inv) * CFL + dt = jnp.min(jnp.array([dt_adv, dt_dif, fin_time - t, tsave - t])) + + def _update(carry): + u, dt = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt) + return u, dt + + carry = u, dt + u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave + + @jax.jit + def update(u, u_tmp, dt): + f = flux(u_tmp) + u -= dt * dx_inv * (f[1:cfg.args.nx + 1] - f[0:cfg.args.nx]) + return u + + def flux(u): + _u = bc(u, dx, Ncell=cfg.args.nx) # index 2 for _U is equivalent with index 0 for u + uL, uR = limiting(_u, cfg.args.nx, if_second_order=1.) + fL = 0.5*uL**2 + fR = 0.5*uR**2 + # upwind advection scheme + f_upwd = (fR[1:cfg.args.nx+2] + fL[2:cfg.args.nx+3] + - 0.5*jnp.abs(uL[2:cfg.args.nx+3] + uR[1:cfg.args.nx+2])*(uL[2:cfg.args.nx+3] - uR[1:cfg.args.nx+2])) + # source term + f_upwd += - cfg.args.epsilon*pi_inv*(_u[2:cfg.args.nx+3] - _u[1:cfg.args.nx+2])*dx_inv + return f_upwd + + u = init(xc=xc, mode=cfg.args.init_mode, u0=cfg.args.u0, du=cfg.args.du) + u = device_put(u) # putting variables in GPU (not necessary??) + uu, t = evolve(u) + print('final time is: {0:.3f}'.format(t)) + + print('data saving...') + cwd = hydra.utils.get_original_cwd() + '/' + if cfg.args.init_mode=='sinsin': + jnp.save(cwd + cfg.args.save + '/Burgers_' + cfg.args.init_mode + '_u' + str(cfg.args.u0) + '_du' + str( + cfg.args.du) + '_Nu' + str(cfg.args.epsilon), uu) + else: + jnp.save(cwd + cfg.args.save + '/Burgers_' + cfg.args.init_mode + '_u' + str(cfg.args.u0) + '_Nu' + str( + cfg.args.epsilon), uu) + jnp.save(cwd + cfg.args.save+'/x_coordinate', xc) + jnp.save(cwd + cfg.args.save+'/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py new file mode 100644 index 0000000..79ce9e0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -0,0 +1,286 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: burgers_multi_solution_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" +import sys +import random +from math import ceil, exp, log + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +from jax import vmap +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import init_multi, Courant, Courant_diff, save_data, bc, limiting + + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + # basic parameters + pi_inv = 1. / jnp.pi + dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx + dx_inv = 1. / dx + + # cell edge coordinate + xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + + show_steps = cfg.multi.show_steps + ini_time = cfg.multi.ini_time + fin_time = cfg.multi.fin_time + dt_save = cfg.multi.dt_save + CFL = cfg.multi.CFL + if cfg.multi.if_rand_param: + epsilon = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + else: + epsilon = cfg.multi.epsilon + print('epsilon: ', epsilon) + # t-coordinate + it_tot = ceil((fin_time - ini_time) / dt_save) + 1 + tc = jnp.arange(it_tot + 1) * dt_save + + @jax.jit + def evolve(u): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _show(_carry): + u, tsave, i_save, uu = _carry + uu = uu.at[i_save].set(u) + tsave += dt_save + i_save += 1 + return (u, tsave, i_save, uu) + + t, tsave, steps, i_save, dt, u, uu = carry + + carry = (u, tsave, i_save, uu) + u, tsave, i_save, uu = lax.cond(t >= tsave, _show, _pass, carry) + + carry = (u, t, dt, steps, tsave) + u, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, u, uu) + + carry = t, tsave, steps, i_save, dt, u, uu + t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) + uu = uu.at[-1].set(u) + + return uu + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave = carry + dt_adv = Courant(u, dx) * CFL + dt_dif = Courant_diff(dx, epsilon*pi_inv) * CFL + dt = jnp.min(jnp.array([dt_adv, dt_dif, fin_time - t, tsave - t])) + + def _update(carry): + u, dt = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt) + return u, dt + + carry = u, dt + u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave + + @jax.jit + def update(u, u_tmp, dt): + f = flux(u_tmp) + u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + return u + + def flux(u): + _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u + uL, uR = limiting(_u, cfg.multi.nx, if_second_order=1.) + fL = 0.5*uL**2 + fR = 0.5*uR**2 + # upwind advection scheme + f_upwd = (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] + - 0.5*jnp.abs(uL[2:cfg.multi.nx+3] + uR[1:cfg.multi.nx+2])*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) + # source term + f_upwd += - epsilon*pi_inv*(_u[2:cfg.multi.nx+3] - _u[1:cfg.multi.nx+2])*dx_inv + return f_upwd + + u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) + u = device_put(u) # putting variables in GPU (not necessary??) + + #vm_evolve = vmap(evolve, 0, 0) + #uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + local_devices = jax.local_device_count() + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd+cfg.multi.save+'1D_Burgers_Sols_Nu'+str(epsilon)[:5], uu) + jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) + jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml new file mode 100644 index 0000000..1fa8247 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml @@ -0,0 +1,17 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' +init_key: 2022 +if_rand_param: None \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml new file mode 100644 index 0000000..62def9e --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-1 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml new file mode 100644 index 0000000..1f56fe3 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml new file mode 100644 index 0000000..f2ea891 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-3 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml new file mode 100644 index 0000000..88f0d44 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e0 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml new file mode 100644 index 0000000..2acd61c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e1 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml new file mode 100644 index 0000000..fb53b28 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'possin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml new file mode 100644 index 0000000..7c5b1f2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-1 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml new file mode 100644 index 0000000..942c834 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml new file mode 100644 index 0000000..99edd16 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1.e-1 +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml new file mode 100644 index 0000000..9309d59 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1.e-2 +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml new file mode 100644 index 0000000..814a1b8 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1.e1 +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml new file mode 100644 index 0000000..bf4118d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1.e2 +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml new file mode 100644 index 0000000..a190a58 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-3 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml new file mode 100644 index 0000000..b1e460d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e0 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml new file mode 100644 index 0000000..48cf919 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e1 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml new file mode 100644 index 0000000..0515b8c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml new file mode 100644 index 0000000..d1a644e --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml new file mode 100644 index 0000000..2376eb6 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.25 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml new file mode 100644 index 0000000..9274eef --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 0.5 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml new file mode 100644 index 0000000..a561103 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 1 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml new file mode 100644 index 0000000..9137a1a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 2 +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml new file mode 100644 index 0000000..cd789a5 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml @@ -0,0 +1,15 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +u0 : 1. +du : 5. +CFL: 4.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'sinsin' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml new file mode 100644 index 0000000..7df90c5 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-1 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml new file mode 100644 index 0000000..5055858 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml new file mode 100644 index 0000000..4aa10f9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-3 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml new file mode 100644 index 0000000..766644c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e0 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml new file mode 100644 index 0000000..548654d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 2.e-1 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml new file mode 100644 index 0000000..0ecdb41 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 2.e-2 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml new file mode 100644 index 0000000..770413e --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 2.e-3 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml new file mode 100644 index 0000000..89dd822 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 2.e0 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml new file mode 100644 index 0000000..0ca7ce9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 4.e-1 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml new file mode 100644 index 0000000..5d4db50 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 4.e-2 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml new file mode 100644 index 0000000..2ff3334 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 4.e-3 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml new file mode 100644 index 0000000..aac4585 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml @@ -0,0 +1,14 @@ +save: '../save/burgers/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 4.e0 +CFL: 2.5e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml new file mode 100644 index 0000000..39fb552 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml @@ -0,0 +1,12 @@ +save: '../save/burgers/' +dt_save: 0.05 +ini_time: 0. +fin_time: 2. +nx: 1024 +xL: -1. +xR: 1. +epsilon: 1.e-2 +CFL: 4.e-1 +if_second_order: 1. +numbers: 10000 +show_steps: 100 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh new file mode 100644 index 0000000..82b0310 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh @@ -0,0 +1,25 @@ +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e1.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e-2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e-3.yaml +# +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e1.yaml +#CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-3.yaml +# +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-2_u01e1.yaml +#CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-2_u01e2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-2_u01e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sin_eps1e-2_u01e-2.yaml +# +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du1.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du01.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du2.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du5.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du05.yaml +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du025.yaml \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh new file mode 100644 index 0000000..5899961 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh @@ -0,0 +1,12 @@ +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e0.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-1.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-2.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-3.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=2e0.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=2e-1.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=2e-2.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=2e-3.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e0.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-1.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-2.yaml +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-3.yaml \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py new file mode 100644 index 0000000..5888b89 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: CFD_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import time +import sys +from math import ceil +from functools import partial + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +from jax import jit +import jax.numpy as jnp +from jax import device_put, lax + +# if double precision +#from jax.config import config +#config.update("jax_enable_x64", True) + +sys.path.append('..') +from utils import init_HD, Courant_HD, Courant_vis_HD, save_data_HD, bc_HD, limiting_HD, bc_HD_vis + + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config", config_name="config") +def main(cfg: DictConfig) -> None: + # physical constants + gamma = cfg.args.gamma # 3D non-relativistic gas + gammi1 = gamma - 1. + gamminv1 = 1. / gammi1 + gamgamm1inv = gamma * gamminv1 + gammi1 = gamma - 1. + gampl1 = gamma + 1. + gammi3 = gamma - 3. + gampl3 = gamma + 3. + + visc = cfg.args.zeta + cfg.args.eta / 3. + + BCs = ['trans', 'periodic', 'KHI'] # reflect + assert cfg.args.bc in BCs, "bc should be in 'trans, reflect, periodic'" + + dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx + dx_inv = 1. / dx + # + dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny + dy_inv = 1. / dy + # + dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz + dz_inv = 1. / dz + + # cell edge coordinate + xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) + ye = jnp.linspace(cfg.args.yL, cfg.args.yR, cfg.args.ny + 1) + ze = jnp.linspace(cfg.args.zL, cfg.args.zR, cfg.args.nz + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + yc = ye[:-1] + 0.5 * dy + zc = ze[:-1] + 0.5 * dz + + # t-coordinate + it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 + tc = jnp.arange(it_tot + 1) * cfg.args.dt_save + + def evolve(Q): + t = cfg.args.ini_time + tsave = t + steps = 0 + i_save = 0 + tm_ini = time.time() + dt = 0. + + while t < cfg.args.fin_time: + if t >= tsave: + print('save data at t = {0:.3f}'.format(t)) + save_data_HD(Q[:,2:-2,2:-2,2:-2], xc, yc, zc, i_save, cfg.args.save) + tsave += cfg.args.dt_save + i_save += 1 + + if steps%cfg.args.show_steps==0 and cfg.args.if_show: + print('now {0:d}-steps, t = {1:.3f}, dt = {2:.3f}'.format(steps, t, dt)) + + carry = (Q, t, dt, steps, tsave) + Q, t, dt, steps, tsave = lax.fori_loop(0, cfg.args.show_steps, simulation_fn, carry) + + tm_fin = time.time() + print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + save_data_HD(Q[:,2:-2,2:-2,2:-2], xc, yc, zc, + i_save, cfg.args.save, cfg.args.dt_save, if_final=True) + return t + + @jit + def simulation_fn(i, carry): + Q, t, dt, steps, tsave = carry + dt = Courant_HD(Q[:,2:-2,2:-2,2:-2], dx, dy, dz, cfg.args.gamma) * cfg.args.CFL + dt = jnp.min(jnp.array([dt, cfg.args.fin_time - t, tsave - t])) + + def _update(carry): + Q, dt = carry + + # preditor step for calculating t+dt/2-th time step + Q_tmp = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = update(Q, Q_tmp, dt * 0.5) + # update using flux at t+dt/2-th time step + Q_tmp = bc_HD(Q_tmp, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = update(Q, Q_tmp, dt) + + # update via viscosity + dt_vis = Courant_vis_HD(dx, dy, dz, cfg.args.eta, cfg.args.zeta) * cfg.args.CFL + dt_vis = jnp.min(jnp.array([dt_vis, dt])) + t_vis = 0. + + carry = Q, dt, dt_vis, t_vis + Q, dt, dt_vis, t_vis = lax.while_loop(lambda x: x[1] - x[3] > 1.e-8, update_vis, carry) + return Q, dt + + carry = Q, dt + Q, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return Q, t, dt, steps, tsave + + @jit + def update(Q, Q_tmp, dt): + # calculate conservative variables + D0 = Q[0] + Mx = Q[1]*Q[0] + My = Q[2]*Q[0] + Mz = Q[3]*Q[0] + E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + # calculate flux + fx = flux_x(Q_tmp) + fy = flux_y(Q_tmp) + fz = flux_z(Q_tmp) + + # update conservative variables + dtdx, dtdy, dtdz = dt*dx_inv, dt*dy_inv, dt*dz_inv + D0 -= dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[0, 2:-2, 2:-2, 1:] - fz[0, 2:-2, 2:-2, :-1]) + + Mx -= dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[1, 2:-2, 2:-2, 1:] - fz[1, 2:-2, 2:-2, :-1]) + + My -= dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[2, 2:-2, 2:-2, 1:] - fz[2, 2:-2, 2:-2, :-1]) + + Mz -= dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[3, 2:-2, 2:-2, 1:] - fz[3, 2:-2, 2:-2, :-1]) + + E0 -= dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[4, 2:-2, 2:-2, 1:] - fz[4, 2:-2, 2:-2, :-1]) + + # reverse primitive variables + Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx/D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My/D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz/D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2)/D0)) # p + Q = Q.at[4].set(jnp.where(Q[4] > 1.e-8, Q[4], cfg.args.p_floor)) + + return Q + + @jit + def update_vis(carry): + Q, dt, dt_vis, t_vis = carry + Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = bc_HD_vis(Q) # index 2 for _U is equivalent with index 0 for u + dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) + t_vis += dt_ev + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + # update conservative variables + dtdxdx, dtdxdy, dtdxdz = dt_ev * dx_inv * dx_inv, dt_ev * dx_inv * dy_inv, dt_ev * dx_inv * dz_inv + dtdydy, dtdydz = dt_ev * dy_inv * dy_inv, dt_ev * dy_inv * dz_inv + dtdzdz = dt_ev * dz_inv * dz_inv + + Mx += (visc + cfg.args.eta) * D0 * dtdxdx * ( + Q[1, 3:-1, 2:-2, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 2:-2]) \ + + cfg.args.eta * D0 * dtdydy * ( + Q[1, 2:-2, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 1:-3, 2:-2]) \ + + cfg.args.eta * D0 * dtdzdz * ( + Q[1, 2:-2, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdxdy * (Q[2, 3:-1, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 1:-3, 2:-2]) \ + + dtdxdz * (Q[3, 3:-1, 2:-2, 3:-1] - 2. * Q[3, 1:-3, 2:-2, 1:-3] + Q[3, 1:-3, 2:-2, 1:-3])) + + My += cfg.args.eta * D0 * dtdxdx * ( + Q[2, 3:-1, 2:-2, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 2:-2, 2:-2]) \ + + (visc + cfg.args.eta) * D0 * dtdydy * ( + Q[2, 2:-2, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 2:-2]) \ + + cfg.args.eta * D0 * dtdzdz * ( + Q[2, 2:-2, 2:-2, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdydz * (Q[3, 2:-2, 3:-1, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 1:-3])\ + + dtdxdy * (Q[1, 3:-1, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 1:-3, 2:-2])) + + Mz += cfg.args.eta * D0 * dtdxdx * ( + Q[3, 3:-1, 2:-2, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 1:-3, 2:-2, 2:-2]) \ + + cfg.args.eta * D0 * dtdydy * ( + Q[3, 2:-2, 3:-1, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 2:-2]) \ + + (visc + cfg.args.eta) * D0 * dtdzdz * ( + Q[3, 2:-2, 2:-2, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdydz * (Q[2, 2:-2, 3:-1, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 1:-3])\ + + dtdxdz * (Q[1, 3:-1, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 1:-3])) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2) / D0)) # p + + return Q, dt, dt_vis, t_vis + + @jit + def flux_x(Q): + QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = HLL(QL, QR, direc=0) + f_Riemann = HLLC(QL, QR, direc=0) + return f_Riemann + + @jit + def flux_y(Q): + _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) + QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + f_Riemann = jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + return f_Riemann + + @jit + def flux_z(Q): + _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) + QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) + f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) + return f_Riemann + + @partial(jit, static_argnums=(2,)) + def HLL(QL, QR, direc): + # direc = 0, 1, 2: (X, Y, Z) + iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 + cfL = jnp.sqrt(gamma * QL[4] / QL[0]) + cfR = jnp.sqrt(gamma * QR[4] / QR[0]) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + dcfi = 1. / (Sfr - Sfl + 1.e-8) + + UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) + UL = UL.at[0].set(QL[0]) + UL = UL.at[iX].set(QL[0] * QL[iX]) + UL = UL.at[iY].set(QL[0] * QL[iY]) + UL = UL.at[iZ].set(QL[0] * QL[iZ]) + UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UR = UR.at[0].set(QR[0]) + UR = UR.at[iX].set(QR[0] * QR[iX]) + UR = UR.at[iY].set(QR[0] * QR[iY]) + UR = UR.at[iZ].set(QR[0] * QR[iZ]) + UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + + fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) + fL = fL.at[0].set(UL[iX]) + fL = fL.at[iX].set(UL[iX] * QL[iX] + QL[4]) + fL = fL.at[iY].set(UL[iX] * QL[iY]) + fL = fL.at[iZ].set(UL[iX] * QL[iZ]) + fL = fL.at[4].set((UL[4] + QL[4]) * QL[iX]) + fR = fR.at[0].set(UR[iX]) + fR = fR.at[iX].set(UR[iX] * QR[iX] + QR[4]) + fR = fR.at[iY].set(UR[iX] * QR[iY]) + fR = fR.at[iZ].set(UR[iX] * QR[iZ]) + fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) + # upwind advection scheme + fHLL = dcfi * (Sfr * fR[:, 1:-2] - Sfl * fL[:, 2:-1] + + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2])) + + # L: left of cell = right-going, R: right of cell: left-going + f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fHLL) + f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) + + return f_Riemann + + @partial(jit, static_argnums=(2,)) + def HLLC(QL, QR, direc): + """ full-Godunov method -- exact shock solution""" + + iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 + cfL = jnp.sqrt(gamma * QL[4] / QL[0]) + cfR = jnp.sqrt(gamma * QR[4] / QR[0]) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + + UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) + UL = UL.at[0].set(QL[0]) + UL = UL.at[iX].set(QL[0] * QL[iX]) + UL = UL.at[iY].set(QL[0] * QL[iY]) + UL = UL.at[iZ].set(QL[0] * QL[iZ]) + UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UR = UR.at[0].set(QR[0]) + UR = UR.at[iX].set(QR[0] * QR[iX]) + UR = UR.at[iY].set(QR[0] * QR[iY]) + UR = UR.at[iZ].set(QR[0] * QR[iZ]) + UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + + Va = (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2]- QL[4, 2:-1] + QR[4, 1:-2] + Va /= (Sfr - QL[iX, 2:-1]) * QL[0, 2:-1] - (Sfl - QR[iX, 1:-2]) * QR[0, 1:-2] + Pa = QR[4, 1:-2] + QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) * (Va - QR[iX, 1:-2]) + + # shock jump condition + Dal = QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) / (Sfl - Va) # right-hand density + Dar = QL[0, 2:-1] * (Sfr - QL[iX, 2:-1]) / (Sfr - Va) # left-hand density + + fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) + fL = fL.at[0].set(UL[iX]) + fL = fL.at[iX].set(UL[iX] * QL[iX] + QL[4]) + fL = fL.at[iY].set(UL[iX] * QL[iY]) + fL = fL.at[iZ].set(UL[iX] * QL[iZ]) + fL = fL.at[4].set((UL[4] + QL[4]) * QL[iX]) + fR = fR.at[0].set(UR[iX]) + fR = fR.at[iX].set(UR[iX] * QR[iX] + QR[4]) + fR = fR.at[iY].set(UR[iX] * QR[iY]) + fR = fR.at[iZ].set(UR[iX] * QR[iZ]) + fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) + # upwind advection scheme + far, fal = jnp.zeros_like(QL[:, 2:-1]), jnp.zeros_like(QR[:, 1:-2]) + far = far.at[0].set(Dar * Va) + far = far.at[iX].set(Dar * Va**2 + Pa) + far = far.at[iY].set(Dar * Va * QL[iY, 2:-1]) + far = far.at[iZ].set(Dar * Va * QL[iZ, 2:-1]) + far = far.at[4].set( (gamgamm1inv * Pa + 0.5 * Dar * (Va**2 + QL[iY, 2:-1]**2 + QL[iZ, 2:-1]**2)) * Va) + fal = fal.at[0].set(Dal * Va) + fal = fal.at[iX].set(Dal * Va**2 + Pa) + fal = fal.at[iY].set(Dal * Va * QR[iY, 1:-2]) + fal = fal.at[iZ].set(Dal * Va * QR[iZ, 1:-2]) + fal = fal.at[4].set( (gamgamm1inv * Pa + 0.5 * Dal * (Va**2 + QR[iY, 1:-2]**2 + QR[iZ, 1:-2]**2)) * Va) + + f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fL[:, 2:-1]) # Sf2 > 0 : supersonic + f_Riemann = jnp.where(Sfl*Va < 0., fal, f_Riemann) # SL < 0 and Va > 0 : sub-sonic + f_Riemann = jnp.where(Sfr*Va < 0., far, f_Riemann) # Va < 0 and SR > 0 : sub-sonic + #f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic + + return f_Riemann + + Q = jnp.zeros([5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4]) + Q = init_HD(Q, xc, yc, zc, mode=cfg.args.init_mode, direc='x', init_key=cfg.args.init_key, + M0=cfg.args.M0, dk=cfg.args.dk, gamma=cfg.args.gamma) + Q = device_put(Q) # putting variables in GPU (not necessary??) + t = evolve(Q) + print('final time is: {0:.3f}'.format(t)) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py new file mode 100644 index 0000000..69c22a7 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -0,0 +1,644 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: CFD_multi_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import time +import random +import sys, os +from functools import partial +from math import ceil, exp, log + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +from jax import jit, vmap +import jax.numpy as jnp +from jax import device_put, lax + +os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' +os.environ['XLA_PYTHON_CLIENT_MEM_FRACTION'] = '.9' + +sys.path.append('..') +from utils import Courant_HD, Courant_vis_HD, bc_HD, limiting_HD, bc_HD_vis +from utils import init_multi_HD, init_multi_HD_shock, init_multi_HD_KH +from utils import init_multi_HD_2DTurb, init_multi_HD_2DRand, init_multi_HD_3DTurb, init_multi_HD_3DRand + +# if double precision +#from jax.config import config +#config.update("jax_enable_x64", True) + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config", config_name="config") +def main(cfg: DictConfig) -> None: + # physical constants + gamma = cfg.args.gamma # 3D non-relativistic gas + gammi1 = gamma - 1. + gamminv1 = 1. / gammi1 + gamgamm1inv = gamma * gamminv1 + gammi1 = gamma - 1. + gampl1 = gamma + 1. + gammi3 = gamma - 3. + gampl3 = gamma + 3. + + BCs = ['trans', 'periodic', 'KHI'] # reflect + assert cfg.args.bc in BCs, "bc should be in 'trans, reflect, periodic'" + + dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx + dx_inv = 1. / dx + # + dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny + dy_inv = 1. / dy + # + dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz + dz_inv = 1. / dz + + # cell edge coordinate + xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) + ye = jnp.linspace(cfg.args.yL, cfg.args.yR, cfg.args.ny + 1) + ze = jnp.linspace(cfg.args.zL, cfg.args.zR, cfg.args.nz + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + yc = ye[:-1] + 0.5 * dy + zc = ze[:-1] + 0.5 * dz + + show_steps = cfg.args.show_steps + ini_time = cfg.args.ini_time + fin_time = cfg.args.fin_time + dt_save = cfg.args.dt_save + + # t-coordinate + it_tot = ceil((fin_time - ini_time) / dt_save) + 1 + tc = jnp.arange(it_tot + 1) * dt_save + + # set viscosity + if cfg.args.if_rand_param: + zeta = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + eta = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + else: + zeta = cfg.args.zeta + eta = cfg.args.eta + print('zeta: {0:>5f}, eta: {1:>5f}'.format(zeta, eta)) + visc = zeta + eta / 3. + + def evolve(Q): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + + tm_ini = time.time() + + DDD = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) + VVx = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) + VVy = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) + VVz = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) + PPP = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) + # initial time-step + DDD = DDD.at[0].set(Q[0,2:-2,2:-2,2:-2]) + VVx = VVx.at[0].set(Q[1,2:-2,2:-2,2:-2]) + VVy = VVy.at[0].set(Q[2,2:-2,2:-2,2:-2]) + VVz = VVz.at[0].set(Q[3,2:-2,2:-2,2:-2]) + PPP = PPP.at[0].set(Q[4,2:-2,2:-2,2:-2]) + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _save(_carry): + Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP = _carry + + DDD = DDD.at[i_save].set(Q[0,2:-2,2:-2,2:-2]) + VVx = VVx.at[i_save].set(Q[1,2:-2,2:-2,2:-2]) + VVy = VVy.at[i_save].set(Q[2,2:-2,2:-2,2:-2]) + VVz = VVz.at[i_save].set(Q[3,2:-2,2:-2,2:-2]) + PPP = PPP.at[i_save].set(Q[4,2:-2,2:-2,2:-2]) + + tsave += dt_save + i_save += 1 + return (Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP) + + t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP = carry + + # if save data + carry = (Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP) + Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP = lax.cond(t >= tsave, _save, _pass, carry) + + carry = (Q, t, dt, steps, tsave) + Q, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP) + + carry = t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP + t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP = lax.while_loop(cond_fun, _body_fun, carry) + + tm_fin = time.time() + print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + DDD = DDD.at[-1].set(Q[0, 2:-2, 2:-2, 2:-2]) + VVx = VVx.at[-1].set(Q[1, 2:-2, 2:-2, 2:-2]) + VVy = VVy.at[-1].set(Q[2, 2:-2, 2:-2, 2:-2]) + VVz = VVz.at[-1].set(Q[3, 2:-2, 2:-2, 2:-2]) + PPP = PPP.at[-1].set(Q[4, 2:-2, 2:-2, 2:-2]) + return t, DDD, VVx, VVy, VVz, PPP + + @jit + def simulation_fn(i, carry): + Q, t, dt, steps, tsave = carry + dt = Courant_HD(Q[:,2:-2,2:-2,2:-2], dx, dy, dz, cfg.args.gamma) * cfg.args.CFL + dt = jnp.min(jnp.array([dt, cfg.args.fin_time - t, tsave - t])) + + def _update(carry): + Q, dt = carry + + # preditor step for calculating t+dt/2-th time step + Q_tmp = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = update(Q, Q_tmp, dt * 0.5) + # update using flux at t+dt/2-th time step + Q_tmp = bc_HD(Q_tmp, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = update(Q, Q_tmp, dt) + + # update via viscosity + dt_vis = Courant_vis_HD(dx, dy, dz, eta, zeta) * cfg.args.CFL + dt_vis = jnp.min(jnp.array([dt_vis, dt])) + t_vis = 0. + + carry = Q, dt, dt_vis, t_vis + Q, dt, dt_vis, t_vis = lax.while_loop(lambda x: x[1] - x[3] > 1.e-8, update_vis, carry) + return Q, dt + + carry = Q, dt + Q, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return Q, t, dt, steps, tsave + + @jit + def update(Q, Q_tmp, dt): + # calculate conservative variables + D0 = Q[0] + Mx = Q[1]*Q[0] + My = Q[2]*Q[0] + Mz = Q[3]*Q[0] + E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + # calculate flux + fx = flux_x(Q_tmp) + fy = flux_y(Q_tmp) + fz = flux_z(Q_tmp) + + # update conservative variables + dtdx, dtdy, dtdz = dt*dx_inv, dt*dy_inv, dt*dz_inv + D0 -= dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[0, 2:-2, 2:-2, 1:] - fz[0, 2:-2, 2:-2, :-1]) + + Mx -= dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[1, 2:-2, 2:-2, 1:] - fz[1, 2:-2, 2:-2, :-1]) + + My -= dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[2, 2:-2, 2:-2, 1:] - fz[2, 2:-2, 2:-2, :-1]) + + Mz -= dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[3, 2:-2, 2:-2, 1:] - fz[3, 2:-2, 2:-2, :-1]) + + E0 -= dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2])\ + + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2])\ + + dtdz * (fz[4, 2:-2, 2:-2, 1:] - fz[4, 2:-2, 2:-2, :-1]) + + # reverse primitive variables + Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx/D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My/D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz/D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2)/D0)) # p + Q = Q.at[4].set(jnp.where(Q[4] > 1.e-8, Q[4], cfg.args.p_floor)) + + return Q + + @jit + def update_vis(carry): + Q, dt, dt_vis, t_vis = carry + Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = bc_HD_vis(Q) # index 2 for _U is equivalent with index 0 for u + dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) + t_vis += dt_ev + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + # update conservative variables + dtdxdx, dtdxdy, dtdxdz = dt_ev * dx_inv * dx_inv, dt_ev * dx_inv * dy_inv, dt_ev * dx_inv * dz_inv + dtdydy, dtdydz = dt_ev * dy_inv * dy_inv, dt_ev * dy_inv * dz_inv + dtdzdz = dt_ev * dz_inv * dz_inv + + Mx += (visc + eta) * D0 * dtdxdx * ( + Q[1, 3:-1, 2:-2, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 2:-2]) \ + + eta * D0 * dtdydy * ( + Q[1, 2:-2, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 1:-3, 2:-2]) \ + + eta * D0 * dtdzdz * ( + Q[1, 2:-2, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdxdy * (Q[2, 3:-1, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 1:-3, 2:-2])\ + + dtdxdz * (Q[3, 3:-1, 2:-2, 3:-1] - 2. * Q[3, 1:-3, 2:-2, 1:-3] + Q[3, 1:-3, 2:-2, 1:-3])) + + My += eta * D0 * dtdxdx * ( + Q[2, 3:-1, 2:-2, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 2:-2, 2:-2]) \ + + (visc + eta) * D0 * dtdydy * ( + Q[2, 2:-2, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 2:-2]) \ + + eta * D0 * dtdzdz * ( + Q[2, 2:-2, 2:-2, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdydz * (Q[3, 2:-2, 3:-1, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 1:-3])\ + + dtdxdy * (Q[1, 3:-1, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 1:-3, 2:-2])) + + Mz += eta * D0 * dtdxdx * ( + Q[3, 3:-1, 2:-2, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 1:-3, 2:-2, 2:-2]) \ + + eta * D0 * dtdydy * ( + Q[3, 2:-2, 3:-1, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 2:-2]) \ + + (visc + eta) * D0 * dtdzdz * ( + Q[3, 2:-2, 2:-2, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 2:-2, 1:-3]) \ + + visc * D0 * 0.5 * (dtdydz * (Q[2, 2:-2, 3:-1, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 1:-3])\ + + dtdxdz * (Q[1, 3:-1, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 1:-3])) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2) / D0)) # p + + return Q, dt, dt_vis, t_vis + + @jit + def flux_x(Q): + QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = HLL(QL, QR, direc=0) + f_Riemann = HLLC(QL, QR, direc=0) + return f_Riemann + + @jit + def flux_y(Q): + _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) + QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + f_Riemann = jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + return f_Riemann + + @jit + def flux_z(Q): + _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) + QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) + #f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) + f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) + return f_Riemann + + @partial(jit, static_argnums=(2,)) + def HLL(QL, QR, direc): + # direc = 0, 1, 2: (X, Y, Z) + iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 + cfL = jnp.sqrt(gamma * QL[4] / QL[0]) + cfR = jnp.sqrt(gamma * QR[4] / QR[0]) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + dcfi = 1. / (Sfr - Sfl + 1.e-8) + + UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) + UL = UL.at[0].set(QL[0]) + UL = UL.at[iX].set(QL[0] * QL[iX]) + UL = UL.at[iY].set(QL[0] * QL[iY]) + UL = UL.at[iZ].set(QL[0] * QL[iZ]) + UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UR = UR.at[0].set(QR[0]) + UR = UR.at[iX].set(QR[0] * QR[iX]) + UR = UR.at[iY].set(QR[0] * QR[iY]) + UR = UR.at[iZ].set(QR[0] * QR[iZ]) + UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + + fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) + fL = fL.at[0].set(UL[iX]) + fL = fL.at[iX].set(UL[iX] * QL[iX] + QL[4]) + fL = fL.at[iY].set(UL[iX] * QL[iY]) + fL = fL.at[iZ].set(UL[iX] * QL[iZ]) + fL = fL.at[4].set((UL[4] + QL[4]) * QL[iX]) + fR = fR.at[0].set(UR[iX]) + fR = fR.at[iX].set(UR[iX] * QR[iX] + QR[4]) + fR = fR.at[iY].set(UR[iX] * QR[iY]) + fR = fR.at[iZ].set(UR[iX] * QR[iZ]) + fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) + # upwind advection scheme + fHLL = dcfi * (Sfr * fR[:, 1:-2] - Sfl * fL[:, 2:-1] + + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2])) + + # L: left of cell = right-going, R: right of cell: left-going + f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fHLL) + f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) + + return f_Riemann + + @partial(jit, static_argnums=(2,)) + def HLLC(QL, QR, direc): + """ full-Godunov method -- exact shock solution""" + + iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 + cfL = jnp.sqrt(gamma * QL[4] / QL[0]) + cfR = jnp.sqrt(gamma * QR[4] / QR[0]) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + + UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) + UL = UL.at[0].set(QL[0]) + UL = UL.at[iX].set(QL[0] * QL[iX]) + UL = UL.at[iY].set(QL[0] * QL[iY]) + UL = UL.at[iZ].set(QL[0] * QL[iZ]) + UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UR = UR.at[0].set(QR[0]) + UR = UR.at[iX].set(QR[0] * QR[iX]) + UR = UR.at[iY].set(QR[0] * QR[iY]) + UR = UR.at[iZ].set(QR[0] * QR[iZ]) + UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + + Va = (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2]- QL[4, 2:-1] + QR[4, 1:-2] + Va /= (Sfr - QL[iX, 2:-1]) * QL[0, 2:-1] - (Sfl - QR[iX, 1:-2]) * QR[0, 1:-2] + Pa = QR[4, 1:-2] + QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) * (Va - QR[iX, 1:-2]) + + # shock jump condition + Dal = QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) / (Sfl - Va) # right-hand density + Dar = QL[0, 2:-1] * (Sfr - QL[iX, 2:-1]) / (Sfr - Va) # left-hand density + + fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) + fL = fL.at[0].set(UL[iX]) + fL = fL.at[iX].set(UL[iX] * QL[iX] + QL[4]) + fL = fL.at[iY].set(UL[iX] * QL[iY]) + fL = fL.at[iZ].set(UL[iX] * QL[iZ]) + fL = fL.at[4].set((UL[4] + QL[4]) * QL[iX]) + fR = fR.at[0].set(UR[iX]) + fR = fR.at[iX].set(UR[iX] * QR[iX] + QR[4]) + fR = fR.at[iY].set(UR[iX] * QR[iY]) + fR = fR.at[iZ].set(UR[iX] * QR[iZ]) + fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) + # upwind advection scheme + far, fal = jnp.zeros_like(QL[:, 2:-1]), jnp.zeros_like(QR[:, 1:-2]) + far = far.at[0].set(Dar * Va) + far = far.at[iX].set(Dar * Va**2 + Pa) + far = far.at[iY].set(Dar * Va * QL[iY, 2:-1]) + far = far.at[iZ].set(Dar * Va * QL[iZ, 2:-1]) + far = far.at[4].set( (gamgamm1inv * Pa + 0.5 * Dar * (Va**2 + QL[iY, 2:-1]**2 + QL[iZ, 2:-1]**2)) * Va) + fal = fal.at[0].set(Dal * Va) + fal = fal.at[iX].set(Dal * Va**2 + Pa) + fal = fal.at[iY].set(Dal * Va * QR[iY, 1:-2]) + fal = fal.at[iZ].set(Dal * Va * QR[iZ, 1:-2]) + fal = fal.at[4].set( (gamgamm1inv * Pa + 0.5 * Dal * (Va**2 + QR[iY, 1:-2]**2 + QR[iZ, 1:-2]**2)) * Va) + + f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fL[:, 2:-1]) # Sf2 > 0 : supersonic + f_Riemann = jnp.where(Sfl*Va < 0., fal, f_Riemann) # SL < 0 and Va > 0 : sub-sonic + f_Riemann = jnp.where(Sfr*Va < 0., far, f_Riemann) # Va < 0 and SR > 0 : sub-sonic + #f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic + + return f_Riemann + + Q = jnp.zeros([cfg.args.numbers, 5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4]) + if cfg.args.init_mode_Multi == '1D_rand': + Q = Q.at[:, 0, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, + k_tot=3, init_key=cfg.args.init_key, + num_choise_k=2, + umin=1.e0, umax=1.e1, if_renorm=True)) + Q = Q.at[:, 4, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, + k_tot=3, init_key=cfg.args.init_key+1, + num_choise_k=2, + umin=1.e1, umax=1.e2, if_renorm=True)) + Q = Q.at[:, 1, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, + k_tot=3, init_key=cfg.args.init_key+2, + num_choise_k=2, + if_renorm=False)) + elif cfg.args.init_mode_Multi == '1D_shocks': + Q = Q.at[:,0,2:-2,2:-2,2:-2].set(init_multi_HD_shock( + xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + umin=1.e0, umax=1.e1)) + Q = Q.at[:,4,2:-2,2:-2,2:-2].set(init_multi_HD_shock( + xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key+1, + umin=1.e1, umax=1.e2)) + Q = Q.at[:,1,2:-2,2:-2,2:-2].set(init_multi_HD_shock( + xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key+2, + umin=-0.5e0, umax=0.5e0)) + elif cfg.args.init_mode_Multi == 'KHs': + assert 2. * yc[0] - (yc[1] - yc[0]) == 0., 'yL is assumed 0!' + print('now we are coming into KHs...') + Q = init_multi_HD_KH(Q, xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, dkMx=cfg.args.dkMx, gamma = cfg.args.gamma) + elif cfg.args.init_mode_Multi == '2DTurbs': + print('now we are coming into 2DTurbs......') + Q = init_multi_HD_2DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) + elif cfg.args.init_mode_Multi == '2D_rand': + assert xe[0] == 0. and ye[0] == 0. and xe[-1] == 1. and ye[-1] == 1., 'xc, yc should be between 0 and 1!' + print('now we are coming into 2Drand......') + Q = init_multi_HD_2DRand(Q, xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) + elif cfg.args.init_mode_Multi == '3DTurbs': + print('now we are coming into 3DTurbs......') + Q = init_multi_HD_3DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) + elif cfg.args.init_mode_Multi == '3D_rand': + print('now we are coming into 3Drand......') + Q = init_multi_HD_3DRand(Q, xc, yc, zc, numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) + print('initial conditions were prepared!!') + Q = device_put(Q) # putting variables in GPU (not necessary??) + + local_device_count = jax.local_device_count() + pm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + t, DDD, VVx, VVy, VVz, PPP = pm_evolve(Q.reshape([local_device_count, + cfg.args.numbers//local_device_count, + 5, cfg.args.nx+4, cfg.args.ny+4, cfg.args.nz+4])) + + itot = DDD.shape[2] + DDD = DDD.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) + VVx = VVx.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) + VVy = VVy.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) + VVz = VVz.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) + PPP = PPP.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) + print('now data saving...') + jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_D', DDD) + jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vx', VVx) + jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vy', VVy) + jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vz', VVz) + jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_P', PPP) + jnp.save(cfg.args.save + '/x_coordinate', xc) + jnp.save(cfg.args.save + '/y_coordinate', yc) + jnp.save(cfg.args.save + '/z_coordinate', zc) + jnp.save(cfg.args.save + '/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml new file mode 100644 index 0000000..1e5092f --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml @@ -0,0 +1,27 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-2 +zeta: 1.e-2 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 1000 +init_mode_Multi: '1D_rand' +init_key: 2022 +if_rand_param: False +M0 : 0. \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml new file mode 100644 index 0000000..958b043 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml @@ -0,0 +1,27 @@ +save: '../save/CFD/' +dt_save: 0.005 +ini_time: 0. +fin_time: 0.4 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 1000 +init_mode_Multi: '1D_shocks' +init_key: 2020 +if_rand_param: False +M0 : 0 \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml new file mode 100644 index 0000000..b16c66d --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml @@ -0,0 +1,27 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 1000 +init_mode_Multi: '1D_rand' +init_key: 2022 +if_rand_param: False +M0 : 0 \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml new file mode 100644 index 0000000..993ba0c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 0.4 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube1' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml new file mode 100644 index 0000000..e3c8126 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 0.15 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube2' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml new file mode 100644 index 0000000..9d634cb --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.001 +ini_time: 0. +fin_time: 0.012 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube3' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml new file mode 100644 index 0000000..f474760 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.001 +ini_time: 0. +fin_time: 0.035 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube4' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml new file mode 100644 index 0000000..9ae2430 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.001 +ini_time: 0. +fin_time: 0.012 +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube5' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml new file mode 100644 index 0000000..692bd6f --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube6' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml new file mode 100644 index 0000000..fc59bbf --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 2. +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube7' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 +M0: 1. +dk: 1. +dkMx: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml new file mode 100644 index 0000000..19e03c0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/KH/KH_M01_dk1_Re1e3/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 0.1 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml new file mode 100644 index 0000000..a4718c1 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0 : 0.1 +dk: 10. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml new file mode 100644 index 0000000..91b9315 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/KH/KH_M01_dk2_Re1e3/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 0.1 +dk: 2. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml new file mode 100644 index 0000000..4f9164b --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 0.1 +dk: 5. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml new file mode 100644 index 0000000..1c90c61 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 0.2 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml new file mode 100644 index 0000000..dfe76cd --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 0.4 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml new file mode 100644 index 0000000..1ca5586 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/KH/KH_M1_dk1_Re1e3/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0: 1. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml new file mode 100644 index 0000000..50d9364 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 5. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-3 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: 'KHI' +M0 : 2. +dk : 1 +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml new file mode 100644 index 0000000..85ea371 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml @@ -0,0 +1,27 @@ +save: '../save/CFD/' +dt_save: 0.1 +ini_time: 0. +fin_time: 2. +nx: 32 +ny: 32 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'KHI' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 4 +init_mode_Multi: 'KHs' +M0 : 0.1 +dkMx: 2. +init_key: 2022 \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml new file mode 100644 index 0000000..3faa563 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml @@ -0,0 +1,28 @@ +save: '../save/CFD/' +dt_save: 0.05 +ini_time: 0. +fin_time: 1. +nx: 128 +ny: 128 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 100 +init_mode_Multi: '2D_rand' +M0 : 0.1 +k_tot: 4 +init_key: 2020 +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml new file mode 100644 index 0000000..0b1ecd0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml @@ -0,0 +1,28 @@ +save: '../save/CFD/' +dt_save: 0.05 +ini_time: 0. +fin_time: 1. +nx: 512 +ny: 512 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 20 +init_mode_Multi: '2D_rand' +M0 : 0.5 +k_tot: 4 +init_key: 2020 +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml new file mode 100644 index 0000000..c73faee --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml @@ -0,0 +1,28 @@ +save: '../save/CFD/' +dt_save: 0.05 +ini_time: 0. +fin_time: 1. +nx: 512 +ny: 512 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +p_floor: 1.e-4 +numbers: 20 +init_mode_Multi: '2DTurbs' +M0 : 0.1 +k_tot: 4 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml new file mode 100644 index 0000000..783f4ef --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 4000 +p_floor: 1.e-4 +init_mode: '2D-shock' +M0: 1. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml new file mode 100644 index 0000000..2774a16 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +ny: 1024 +nz: 1 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 5.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'OTVortex' +M0: 1. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml new file mode 100644 index 0000000..b0bfaa9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 0.5 +nx: 256 +ny: 256 +nz: 256 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'BlastWave' +M0: 1. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml new file mode 100644 index 0000000..e7a4edf --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml @@ -0,0 +1,28 @@ +save: '../save/CFD/' +dt_save: 0.05 +ini_time: 0. +fin_time: 1. +nx: 128 +ny: 128 +nz: 128 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '3D_rand' +M0 : 1. +k_tot: 4 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml new file mode 100644 index 0000000..756a95a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml @@ -0,0 +1,28 @@ +save: '../save/CFD/' +dt_save: 0.05 +ini_time: 0. +fin_time: 1. +nx: 64 +ny: 64 +nz: 64 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +numbers: 20 +init_mode_Multi: '3DTurbs' +M0 : 1. +k_tot: 4 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml new file mode 100644 index 0000000..cca4a94 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 1. +nx: 256 +ny: 256 +nz: 256 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'turbulence' +M0: 1.e-1 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml new file mode 100644 index 0000000..65ab227 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 1. +nx: 256 +ny: 256 +nz: 256 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'turbulence' +M0: 5.e-1 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml new file mode 100644 index 0000000..cca4a94 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 1. +nx: 256 +ny: 256 +nz: 256 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'turbulence' +M0: 1.e-1 +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml new file mode 100644 index 0000000..50d8f32 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 1. +nx: 256 +ny: 256 +nz: 256 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'turbulence' +M0: 2. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml new file mode 100644 index 0000000..b7058e6 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml @@ -0,0 +1,29 @@ +save: '../save/CFD/' +dt_save: 0.025 +ini_time: 0. +fin_time: 1. +nx: 256 +ny: 256 +nz: 256 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +zL: 0. +zR: 1. +bc: 'periodic' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 400 +p_floor: 1.e-4 +init_mode: 'turbulence' +M0: 4. +dk: 1. +dkMx: 1. +numbers: 4 +init_mode_Multi: 'KHs' +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml new file mode 100644 index 0000000..437e632 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml @@ -0,0 +1,26 @@ +save: '../save/CFD/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +ny: 1 +nz: 1 +xL: -1. +xR: 1. +yL: -1. +yR: 1. +zL: -1. +zR: 1. +bc: 'trans' +gamma: 1.6666666666666667 +eta: 1.e-8 +zeta: 1.e-8 +CFL: 3.e-1 +if_second_order: 1. +if_show: 1 +show_steps: 100 +init_mode: 'shocktube1' +p_floor: 1.e-4 +numbers: 10 +init_mode_Multi: '1D_shocks' +init_key: 2022 \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml new file mode 100644 index 0000000..3a4b989 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml @@ -0,0 +1,9 @@ +defaults: + - _self_ + - override hydra/hydra_logging: disabled + - override hydra/job_logging: disabled + +hydra: + output_subdir: null + run: + dir: . \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh new file mode 100644 index 0000000..384989a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh @@ -0,0 +1,7 @@ +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e0.yaml +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh new file mode 100644 index 0000000..770b5f7 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh @@ -0,0 +1,6 @@ +CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM01.yaml +CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM05.yaml +CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM1.yaml +CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM2.yaml +CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM4.yaml + diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh new file mode 100644 index 0000000..1bd7d44 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh @@ -0,0 +1,8 @@ +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M01_dk1.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M02_dk1.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M04_dk1.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M1_dk1.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M2_dk1.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M01_dk2.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M01_dk5.yaml +CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M01_dk10.yaml diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh new file mode 100644 index 0000000..777f36f --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh @@ -0,0 +1,9 @@ +nn=1 +key=2020 +while [ $nn -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh new file mode 100644 index 0000000..aa9676c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh @@ -0,0 +1,9 @@ +nn=1 +key=2031 +while [ $nn -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_shock.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh new file mode 100644 index 0000000..502b3d9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh @@ -0,0 +1,9 @@ +nn=1 +key=2020 +while [ $nn -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_trans.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh new file mode 100644 index 0000000..a1c7c06 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh @@ -0,0 +1,10 @@ +nn=1 +key=2031 +while [ $nn -le 100 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand.yaml ++args.init_key=$key + #CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand_HR.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh new file mode 100644 index 0000000..9dd556c --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh @@ -0,0 +1,10 @@ +nn=1 +key=2031 +#while [ $nn -le 100 ]; do +while [ $nn -le 55 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Turb.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh new file mode 100644 index 0000000..e6de413 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh @@ -0,0 +1,9 @@ +nn=1 +key=2031 +while [ $nn -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=3D_Multi_Rand.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh new file mode 100644 index 0000000..154a0fd --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh @@ -0,0 +1,9 @@ +nn=1 +key=2031 +while [ $nn -le 6 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=3D_Multi_TurbM1.yaml ++args.init_key=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py new file mode 100644 index 0000000..db34962 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -0,0 +1,353 @@ +""" + + + File: Data_Merge.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + + +import numpy as np +import h5py +import glob + +# Hydra +import hydra +from omegaconf import DictConfig + +def _mergeRD(var, DataND): + _vars = ['2D', 'nu'] + if var not in _vars: + print(var+' is not defined!') + return None + + idx = 0 + datas = glob.glob('save/ReacDiff/'+var+'*key*.npy') + datas.sort() + for data in datas: + print(idx, data) + test = np.load(data).squeeze() + batch = min(test.shape[0], DataND.shape[0] - idx) + if var == '2D': + DataND[idx:idx + batch] = test[:batch, -2] + else: + DataND[idx:idx + batch] = test[:batch] + idx += batch + + return DataND[:idx] + +def _merge(var, DataND, dim): + if dim == 1: + _vars = ['D', 'P', 'Vx'] + elif dim == 2: + _vars = ['D', 'P', 'Vx', 'Vy'] + elif dim == 3: + _vars = ['D', 'P', 'Vx', 'Vy', 'Vz'] + if var not in _vars: + print(var+' is not defined!') + return None + + idx = 0 + datas = glob.glob('save/CFD/HD*'+var+'.npy') + datas.sort() + for data in datas: + print(idx, data) + test = np.load(data).squeeze() + batch = min(test.shape[0], DataND.shape[0] - idx) + DataND[idx:idx+batch] = test[:batch] + idx += batch + + return DataND[:idx] + +def nan_check(data): + data = np.abs(data).reshape([data.shape[0], data.shape[1],-1]).sum(axis=-1) + return np.where(data[:,-2] < 1.e-6)[0], np.where(data[:,-2] > 1.e-6)[0] + +def merge(type, dim, bd): + if type=='CFD': + datas = glob.glob('save/CFD/HD*D.npy') + datas.sort() + test = np.load(datas[0]) + _nbatch, nt, nx, ny, nz = test.shape + nbatch = _nbatch * len(datas) + print('nb, nt, nx, ny, nz: ', nbatch, nt, nx, ny, nz) + + if dim == 1: + DataND = np.zeros([2*nbatch, nt, nx]) + vars = ['D', 'P', 'Vx'] + elif dim == 2: + DataND = np.zeros([2*nbatch, nt, nx, ny]) + vars = ['D', 'P', 'Vx', 'Vy'] + elif dim == 3: + DataND = np.zeros([2*nbatch, nt, nx, ny, nz]) + vars = ['D', 'P', 'Vx', 'Vy', 'Vz'] + + elif type=='ReacDiff': + datas = glob.glob('save/ReacDiff/nu*.npy') + datas.sort() + test = np.load(datas[0]) + _nbatch, nx, ny = test.shape + nbatch = _nbatch * len(datas) + print('nb, nx, ny: ', nbatch, nx, ny) + DataND = np.zeros([nbatch, nx, ny]) + vars = ['2D', 'nu'] + + for var in vars: + if type=='CFD': + DataND = _merge(var, DataND, dim) + if var=='D': + idx_neg, idx_pos = nan_check(DataND) + print('idx_neg: {0}, idx_pos: {1}'.format(len(idx_neg), len(idx_pos))) + if len(idx_pos) < nbatch: + print('too many ill-defined data...') + print('nbatch: {0}, idx_pos: {1}'.format(nbatch, len(idx_pos))) + DataND = DataND[idx_pos] + np.save('save/CFD/'+var+'.npy', DataND) + elif type == 'ReacDiff': + DataND = _mergeRD(var, DataND) + np.save('save/ReacDiff/'+var+'.npy', DataND) + + datas = glob.glob('save/'+type+'/*npy') + datas.sort() + + if type == 'CFD': + zcrd = np.load(datas[-1]) + del (datas[-1]) + ycrd = np.load(datas[-1]) + del (datas[-1]) + xcrd = np.load(datas[-1]) + del (datas[-1]) + tcrd = np.load(datas[-1]) + del (datas[-1]) + if type=='ReacDiff': + datas = glob.glob('save/' + type + '/nu*key*npy') + datas.sort() + _beta = datas[0].split('/')[-1].split('_')[3] + flnm = 'save/ReacDiff/2D_DecayFlow_'+_beta+'_Train.hdf5' + with h5py.File(flnm, 'w') as f: + f.create_dataset('tensor', data = np.load('save/ReacDiff/2D.npy')[:, None, :, :]) + f.create_dataset('nu', data = np.load('save/ReacDiff/nu.npy')) + f.create_dataset('x-coordinate', data=xcrd) + f.create_dataset('y-coordinate', data=ycrd) + f.attrs['beta'] = float(_beta[4:]) + return 0 + + mode = datas[1].split('/')[-1].split('_')[3] + _eta = datas[1].split('/')[-1].split('_')[4] + _zeta = datas[1].split('/')[-1].split('_')[5] + _M = datas[1].split('/')[-1].split('_')[6] + if dim == 1: + flnm = 'save/CFD/1D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + bd + '_Train.hdf5' + elif dim == 2: + flnm = 'save/CFD/2D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + elif dim == 3: + flnm = 'save/CFD/3D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + print(flnm) + + del(DataND) + + with h5py.File(flnm, 'w') as f: + f.create_dataset('density', data = np.load('save/CFD/D.npy')) + f.create_dataset('pressure', data = np.load('save/CFD/P.npy')) + f.create_dataset('Vx', data = np.load('save/CFD/Vx.npy')) + if dim > 1: + f.create_dataset('Vy', data=np.load('save/CFD/Vy.npy')) + f.create_dataset('y-coordinate', data=ycrd) + if dim == 3: + f.create_dataset('Vz', data=np.load('save/CFD/Vz.npy')) + f.create_dataset('z-coordinate', data=zcrd) + f.create_dataset('x-coordinate', data = xcrd) + f.create_dataset('t-coordinate', data = tcrd) + eta = float(_eta[3:]) + zeta = float(_zeta[4:]) + print('(eta, zeta) = ', eta, zeta) + f.attrs['eta'] = eta + f.attrs['zeta'] = zeta + if dim > 1: + M = float(_M[1:]) + f.attrs['M'] = M + print('M: ', M) + +def transform(type): + datas = glob.glob('save/'+type+'/*npy') + datas.sort() + xcrd = np.load(datas[-1]) + del (datas[-1]) + tcrd = np.load(datas[-1]) + del (datas[-1]) + + flnm = datas[0] + with h5py.File(flnm[:-3]+'hdf5', 'w') as f: + print(flnm) + _data = np.load(flnm) + f.create_dataset('tensor', data = _data.astype(np.float32)) + f.create_dataset('x-coordinate', data = xcrd) + f.create_dataset('t-coordinate', data = tcrd) + if type=='advection': + beta = float(flnm.split('/')[-1].split('_')[3][4:-4]) # advection train + print(beta) + f.attrs['beta'] = beta + + elif type=='burgers': + Nu = float(flnm.split('/')[-1].split('_')[-1][2:-4]) # Burgers test/train + print(Nu) + f.attrs['Nu'] = Nu + + elif type=='ReacDiff': + Rho = float(flnm.split('/')[-1].split('_')[-1][3:-4]) # reac-diff test + Nu = float(flnm.split('/')[-1].split('_')[-2][2:]) # reac-diff test + print(Nu, Rho) + f.attrs['Nu'] = Nu + f.attrs['rho'] = Rho + +# Init arguments with Hydra +@hydra.main(config_path="config", config_name="config") +def main(cfg: DictConfig) -> None: + pde1ds = ['advection', 'burgers', 'ReacDiff'] + if cfg.args.type in pde1ds and cfg.args.dim==1: + transform(type=cfg.args.type) + else: + bds = ['periodic', 'trans'] + assert cfg.args.bd in bds, 'bd should be either periodic or trans' + merge(type=cfg.args.type, dim=cfg.args.dim, bd=cfg.args.bd) + +if __name__=='__main__': + main() \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml new file mode 100644 index 0000000..30f3641 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 1.e0 +rho: 1.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml new file mode 100644 index 0000000..f551aa0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 2.e0 +rho: 1.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml new file mode 100644 index 0000000..cdd76db --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e-1 +rho: 1.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml new file mode 100644 index 0000000..bef66db --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e0 +rho: 1.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml new file mode 100644 index 0000000..1a996f7 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 1.e0 +rho: 1.e1 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml new file mode 100644 index 0000000..42228e0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 2.e0 +rho: 1.e1 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml new file mode 100644 index 0000000..d8f4513 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e-1 +rho: 1.e1 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml new file mode 100644 index 0000000..fe56c62 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e0 +rho: 1.e1 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml new file mode 100644 index 0000000..9804ca1 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 1.e0 +rho: 2.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml new file mode 100644 index 0000000..ce67bba --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 2.e0 +rho: 2.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml new file mode 100644 index 0000000..04abd46 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e-1 +rho: 2.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml new file mode 100644 index 0000000..755beaf --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e0 +rho: 2.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml new file mode 100644 index 0000000..33d3e25 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 1.e0 +rho: 5.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml new file mode 100644 index 0000000..d9b17ad --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 2.e0 +rho: 5.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml new file mode 100644 index 0000000..f1a7705 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e-1 +rho: 5.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml new file mode 100644 index 0000000..6039f11 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 5.e0 +rho: 5.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml new file mode 100644 index 0000000..30f3641 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml @@ -0,0 +1,13 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 6.28318530718 +nu : 1.e0 +rho: 1.e0 +CFL: 4.e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml new file mode 100644 index 0000000..23cc8c9 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 64 +nx: 64 +xL: 0. +xR: 6.28318530718 +yL: 0. +yR: 6.28318530718 +nu : 1.e0 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +show_steps: 100 +init_mode: 'react' diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml new file mode 100644 index 0000000..572d0ff --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 1.e0 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml new file mode 100644 index 0000000..f661d34 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 1.e1 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml new file mode 100644 index 0000000..12479a0 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 2.e0 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml new file mode 100644 index 0000000..e7c4bcd --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 5.e-1 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml new file mode 100644 index 0000000..cad5d73 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 256 +xL: 0. +xR: 1. +nu: 5.e0 +rho: 1.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml new file mode 100644 index 0000000..c9de644 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 1.e0 +rho: 1.e1 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml new file mode 100644 index 0000000..cccd76a --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 1.e1 +rho: 1.e1 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml new file mode 100644 index 0000000..cff657e --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 2.e0 +rho: 1.e1 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml new file mode 100644 index 0000000..512fcd1 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 5.e-1 +rho: 1.e1 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml new file mode 100644 index 0000000..7965ac3 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 256 +xL: 0. +xR: 1. +nu: 5.e0 +rho: 1.e1 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml new file mode 100644 index 0000000..e98c943 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 1.e0 +rho: 2.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml new file mode 100644 index 0000000..3b71699 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 1.e1 +rho: 2.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml new file mode 100644 index 0000000..2e2fc56 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 2.e0 +rho: 2.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml new file mode 100644 index 0000000..e201d68 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 5.e-1 +rho: 2.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml new file mode 100644 index 0000000..17996c7 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 256 +xL: 0. +xR: 1. +nu: 5.e0 +rho: 2.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml new file mode 100644 index 0000000..7cddd71 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 1.e0 +rho: 5.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml new file mode 100644 index 0000000..e2025e3 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 1.e1 +rho: 5.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 100 +if_second_order: 1. +show_steps: 10000 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml new file mode 100644 index 0000000..8580063 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 512 +xL: 0. +xR: 1. +nu: 2.e0 +rho: 5.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml new file mode 100644 index 0000000..aae3611 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 5.e-1 +rho: 5.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 10000 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml new file mode 100644 index 0000000..47f91c8 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml @@ -0,0 +1,16 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 256 +xL: 0. +xR: 1. +nu: 5.e0 +rho: 5.e0 +CFL: 2.5e-1 +if_show: 1 +numbers: 10000 +if_second_order: 1. +show_steps: 100 +init_key: 2022 +if_rand_param: False \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml new file mode 100644 index 0000000..60e27b4 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml @@ -0,0 +1,14 @@ +save: '../save/ReacDiff/' +dt_save: 0.01 +ini_time: 0. +fin_time: 1. +nx: 1024 +xL: 0. +xR: 1. +nu: 5.e0 +rho: 1.e1 +CFL: 4.e-1 +if_show: 1 +numbers: 100 +if_second_order: 1. +show_steps: 100 diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml new file mode 100644 index 0000000..00169c2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml @@ -0,0 +1,17 @@ +save: '../save/ReacDiff//' +dt_save: 0.25 +ini_time: 0. +fin_time: 2. +nx: 128 +ny: 128 +xL: 0. +xR: 1. +yL: 0. +yR: 1. +beta: 1. +CFL: 2.5e-1 +if_show: 1 +numbers: 200 +if_second_order: 1. +show_steps: 100 +init_key: 2022 \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py new file mode 100644 index 0000000..b3b6fca --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: reaction_diffusion_2D_multi_solution_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import sys +import random +from math import ceil, exp, log + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +from jax import vmap +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import Courant_diff_2D, bc_2D, init_multi_2DRand + + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + # basic parameters + dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx + dx_inv = 1. / dx + dy = (cfg.multi.yR - cfg.multi.yL)/cfg.multi.ny + dy_inv = 1./dy + + # cell edge coordinate + xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) + ye = jnp.linspace(cfg.multi.yL, cfg.multi.yR, cfg.multi.ny + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + yc = ye[:-1] + 0.5 * dy + + show_steps = cfg.multi.show_steps + ini_time = cfg.multi.ini_time + fin_time = cfg.multi.fin_time + dt_save = cfg.multi.dt_save + CFL = cfg.multi.CFL + beta = cfg.multi.beta + + # t-coordinate + it_tot = ceil((fin_time - ini_time) / dt_save) + 1 + tc = jnp.arange(it_tot + 1) * dt_save + + @jax.jit + def evolve(u, nu): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + uu = jnp.zeros([it_tot, u.shape[0], u.shape[1]]) + uu = uu.at[0].set(u) + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _show(_carry): + u, tsave, i_save, uu = _carry + uu = uu.at[i_save].set(u) + tsave += dt_save + i_save += 1 + return (u, tsave, i_save, uu) + + t, tsave, steps, i_save, dt, u, uu, nu = carry + + carry = (u, tsave, i_save, uu) + u, tsave, i_save, uu = lax.cond(t >= tsave, _show, _pass, carry) + + carry = (u, t, dt, steps, tsave, nu) + u, t, dt, steps, tsave, nu = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, u, uu, nu) + + carry = t, tsave, steps, i_save, dt, u, uu, nu + t, tsave, steps, i_save, dt, u, uu, nu = lax.while_loop(cond_fun, _body_fun, carry) + uu = uu.at[-1].set(u) + + return uu + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave, nu = carry + dt = Courant_diff_2D(dx, dy, nu) * CFL + dt = jnp.min(jnp.array([dt, fin_time - t, tsave - t])) + + def _update(carry): + u, dt, nu = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5, nu) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt, nu) + return u, dt, nu + + carry = u, dt, nu + u, dt, nu = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave, nu + + + @jax.jit + def update(u, u_tmp, dt, nu): + # boundary condition + _u = bc_2D(u_tmp, mode='Neumann') + # diffusion + dtdx = dt * dx_inv + dtdy = dt * dy_inv + fx = - 0.5 * (nu[2:-1, 2:-2] + nu[1:-2, 2:-2]) * dx_inv * (_u[2:-1, 2:-2] - _u[1:-2, 2:-2]) + fy = - 0.5 * (nu[2:-2, 2:-1] + nu[2:-2, 1:-2]) * dy_inv * (_u[2:-2, 2:-1] - _u[2:-2, 1:-2]) + u -= dtdx * (fx[1:, :] - fx[:-1, :])\ + + dtdy * (fy[:, 1:] - fy[:, :-1]) + # source term: f = 1 * beta + u += dt * beta + return u + + u = init_multi_2DRand(xc, yc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) + u = device_put(u) # putting variables in GPU (not necessary??) + + # generate random diffusion coefficient + key = jax.random.PRNGKey(cfg.multi.init_key) + xms = jax.random.uniform(key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.xL, maxval=cfg.multi.xR) + key, subkey = jax.random.split(key) + yms = jax.random.uniform(key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.yL, maxval=cfg.multi.yR) + + key, subkey = jax.random.split(key) + stds = 0.5*(cfg.multi.xR - cfg.multi.xL) * jax.random.uniform(key, shape=[cfg.multi.numbers, 5]) + nu = jnp.zeros_like(u) + for i in range(5): + nu += jnp.exp(-((xc[None, :, None] - xms[:, None, None, i]) ** 2 + + (yc[None, None, :] - yms[:, None, None, i]) ** 2) / stds[:, None, None, i]) + nu = jnp.where(nu > nu.mean(), 1, 0.1) + nu = vmap(bc_2D, axis_name='i')(nu) + + local_devices = jax.local_device_count() + if local_devices > 1: + nb, nx, ny = u.shape + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, nx, ny]),\ + nu.reshape([local_devices, cfg.multi.numbers//local_devices, nx+4, ny+4])) + uu = uu.reshape([nb, -1, nx, ny]) + else: + vm_evolve = vmap(evolve, 0, 0) + uu = vm_evolve(u, nu) + + print('data saving...') + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd + cfg.multi.save+'/2D_ReacDiff_Multi_beta'+str(beta)[:5]+'_key'+str(cfg.multi.init_key), uu) + jnp.save(cwd + cfg.multi.save+'/x_coordinate', xc) + jnp.save(cwd + cfg.multi.save+'/y_coordinate', yc) + jnp.save(cwd + cfg.multi.save+'/t_coordinate', tc) + jnp.save(cwd + cfg.multi.save+'/nu_diff_coef_beta'+str(beta)[:5]+'_key'+str(cfg.multi.init_key), nu[:,2:-2,2:-2]) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py new file mode 100644 index 0000000..638ffcc --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: reaction_diffusion_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import time +import sys +from math import ceil + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import init, Courant_diff, save_data, bc + + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + print('nu: {0:.3f}, rho: {1:.3f}'.format(cfg.args.nu, cfg.args.rho)) + + # basic parameters + dx = (cfg.args.xR - cfg.args.xL)/cfg.args.nx + dx_inv = 1./dx + + # cell edge coordinate + xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5*dx + # t-coordinate + it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 + tc = jnp.arange(it_tot + 1) * cfg.args.dt_save + + def evolve(u): + t = cfg.args.ini_time + tsave = t + steps = 0 + i_save = 0 + tm_ini = time.time() + dt = 0. + + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + while t < cfg.args.fin_time: + if t >= tsave: + uu = uu.at[i_save].set(u) + tsave += cfg.args.dt_save + i_save += 1 + + if steps%cfg.args.show_steps==0 and cfg.args.if_show: + print('now {0:d}-steps, t = {1:.3f}, dt = {2:.3f}'.format(steps, t, dt)) + + carry = (u, t, dt, steps, tsave) + u, t, dt, steps, tsave = lax.fori_loop(0, cfg.args.show_steps, simulation_fn, carry) + + tm_fin = time.time() + print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + return uu, t + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave = carry + dt = Courant_diff(dx, cfg.args.nu) * cfg.args.CFL + dt = jnp.min(jnp.array([dt, cfg.args.fin_time - t, tsave - t])) + + def _update(carry): + u, dt = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt) + return u, dt + def _pass(carry): + return carry + + carry = u, dt + u, dt = lax.cond(t > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave + + @jax.jit + def update(u, u_tmp, dt): + # stiff part + u = Piecewise_Exact_Solution(u, dt) + # diffusion + f = flux(u_tmp) + u -= dt * dx_inv * (f[1:cfg.args.nx + 1] - f[0:cfg.args.nx]) + return u + + @jax.jit + def flux(u): + _u = bc(u, dx, Ncell=cfg.args.nx) # index 2 for _U is equivalent with index 0 for u + # source term + f = - cfg.args.nu*(_u[2:cfg.args.nx+3] - _u[1:cfg.args.nx+2])*dx_inv + return f + + @jax.jit + def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method + # stiff equation + u = 1./(1. + jnp.exp(- cfg.args.rho*dt)*(1. - u)/u) + return u + + u = init(xc=xc, mode=cfg.args.init_mode) + u = device_put(u) # putting variables in GPU (not necessary??) + uu, t = evolve(u) + print('final time is: {0:.3f}'.format(t)) + + print('data saving...') + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd + cfg.args.save + '/ReacDiff_' + cfg.args.init_mode + '_Nu' + str(cfg.args.nu) + '_Rho' + str(cfg.args.rho), uu) + jnp.save(cwd + cfg.args.save + '/x_coordinate', xc) + jnp.save(cwd + cfg.args.save + '/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py new file mode 100644 index 0000000..f279eee --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: reaction_diffusion_multi_solution_Hydra.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import sys +import random +from math import ceil, exp, log + +# Hydra +from omegaconf import DictConfig, OmegaConf +import hydra + +import jax +from jax import vmap +import jax.numpy as jnp +from jax import device_put, lax + +sys.path.append('..') +from utils import init_multi, Courant, Courant_diff, save_data, bc, limiting + + +def _pass(carry): + return carry + +# Init arguments with Hydra +@hydra.main(config_path="config") +def main(cfg: DictConfig) -> None: + # basic parameters + dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx + dx_inv = 1. / dx + + # cell edge coordinate + xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) + # cell center coordinate + xc = xe[:-1] + 0.5 * dx + + show_steps = cfg.multi.show_steps + ini_time = cfg.multi.ini_time + fin_time = cfg.multi.fin_time + dt_save = cfg.multi.dt_save + CFL = cfg.multi.CFL + if cfg.multi.if_rand_param: + rho = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + nu = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + else: + rho = cfg.multi.rho + nu = cfg.multi.nu + print('rho: {0:>5f}, nu: {1:>5f}'.format(rho, nu)) + + # t-coordinate + it_tot = ceil((fin_time - ini_time) / dt_save) + 1 + tc = jnp.arange(it_tot + 1) * dt_save + + @jax.jit + def evolve(u): + t = ini_time + tsave = t + steps = 0 + i_save = 0 + dt = 0. + uu = jnp.zeros([it_tot, u.shape[0]]) + uu = uu.at[0].set(u) + + cond_fun = lambda x: x[0] < fin_time + + def _body_fun(carry): + def _show(_carry): + u, tsave, i_save, uu = _carry + uu = uu.at[i_save].set(u) + tsave += dt_save + i_save += 1 + return (u, tsave, i_save, uu) + + t, tsave, steps, i_save, dt, u, uu = carry + + carry = (u, tsave, i_save, uu) + u, tsave, i_save, uu = lax.cond(t >= tsave, _show, _pass, carry) + + carry = (u, t, dt, steps, tsave) + u, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) + + return (t, tsave, steps, i_save, dt, u, uu) + + carry = t, tsave, steps, i_save, dt, u, uu + t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) + uu = uu.at[-1].set(u) + + return uu + + @jax.jit + def simulation_fn(i, carry): + u, t, dt, steps, tsave = carry + dt = Courant_diff(dx, nu) * CFL + dt = jnp.min(jnp.array([dt, fin_time - t, tsave - t])) + + def _update(carry): + u, dt = carry + # preditor step for calculating t+dt/2-th time step + u_tmp = update(u, u, dt * 0.5) + # update using flux at t+dt/2-th time step + u = update(u, u_tmp, dt) + return u, dt + + carry = u, dt + u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + + t += dt + steps += 1 + return u, t, dt, steps, tsave + + + @jax.jit + def update(u, u_tmp, dt): + # stiff part + u = Piecewise_Exact_Solution(u, dt) + # diffusion + f = flux(u_tmp) + u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + return u + + @jax.jit + def flux(u): + _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u + # 2nd-order diffusion flux + f = - nu*(_u[2:cfg.multi.nx+3] - _u[1:cfg.multi.nx+2])*dx_inv + return f + + @jax.jit + def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method + # stiff equation + u = 1./(1. + jnp.exp(- rho*dt)*(1. - u)/u) + return u + + u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key, if_norm=True) + u = device_put(u) # putting variables in GPU (not necessary??) + + #vm_evolve = vmap(evolve, 0, 0) + #uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + local_devices = jax.local_device_count() + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + + print('data saving...') + cwd = hydra.utils.get_original_cwd() + '/' + jnp.save(cwd + cfg.multi.save+'/ReacDiff_Nu'+str(nu)[:5]+'_Rho'+str(rho)[:5], uu) + jnp.save(cwd + cfg.multi.save+'/x_coordinate', xc) + jnp.save(cwd + cfg.multi.save+'/t_coordinate', tc) + +if __name__=='__main__': + main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh new file mode 100644 index 0000000..7f6bd11 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh @@ -0,0 +1,20 @@ +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu1e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu2e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu5e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu1e1.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho2e0_Nu1e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho2e0_Nu2e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho2e0_Nu5e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho2e0_Nu1e1.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho5e0_Nu1e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho5e0_Nu2e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho5e0_Nu5e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho5e0_Nu1e1.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e1_Nu1e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e1_Nu2e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e1_Nu5e0.yaml +#CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e1_Nu1e1.yaml +CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e1_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho5e0_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho2e0_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu5e-1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh new file mode 100644 index 0000000..bfc78db --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh @@ -0,0 +1,16 @@ +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu1e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu2e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu5e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho2e0_Nu1e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho2e0_Nu2e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho2e0_Nu5e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho5e0_Nu1e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho5e0_Nu2e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho5e0_Nu5e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e1_Nu1e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e1_Nu2e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e1_Nu5e0.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho2e0_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho5e0_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu5e-1.yaml +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e1_Nu5e-1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh new file mode 100644 index 0000000..1ee0716 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh @@ -0,0 +1,10 @@ +nn=1 +key=2020 +while [ $nn -le 50 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_2D_multi_solution_Hydra.py +multi=config_2D.yaml ++multi.init_k\ +ey=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml new file mode 100644 index 0000000..d328852 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -0,0 +1,14 @@ +defaults: + - _self_ + - override hydra/hydra_logging: disabled + - override hydra/job_logging: disabled + +hydra: + output_subdir: null + run: + dir: . + +args: + type: 'advection' + dim: 1 + bd: 'periodic' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore b/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore b/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore b/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore b/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py new file mode 100644 index 0000000..94eec22 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -0,0 +1,1558 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + + + File: utils.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" + +import math as mt +import jax +import numpy as np +import jax.numpy as jnp +from jax import random, jit, nn, lax, vmap, scipy +from functools import partial + +# if double precision +#from jax.config import config +#config.update("jax_enable_x64", True) + + +def init(xc, mode='sin', u0=1., du=0.1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + modes = ['sin', 'sinsin', 'Gaussian', 'react', 'possin'] + assert mode in modes, 'mode is not defined!!' + if mode == 'sin': # sinusoidal wave + u = u0 * jnp.sin((xc + 1.) * jnp.pi) + elif mode == 'sinsin': # sinusoidal wave + u = jnp.sin((xc + 1.) * jnp.pi) + du * jnp.sin((xc + 1.) * jnp.pi*8.) + elif mode == 'Gaussian': # for diffusion check + t0 = 0.01 + u = jnp.exp(-xc**2*jnp.pi/(4.*t0))/jnp.sqrt(2.*t0) + elif mode == 'react': # for reaction-diffusion eq. + logu = - 0.5*(xc - jnp.pi)**2/(0.25*jnp.pi)**2 + u = jnp.exp(logu) + elif mode == 'possin': # sinusoidal wave + u = u0 * jnp.abs(jnp.sin((xc + 1.) * jnp.pi)) + return u + + +@partial(jit, static_argnums=(1, 2, 3, 4)) +def init_multi(xc, numbers=10000, k_tot=8, init_key=2022, num_choise_k=2, if_norm=False): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + + def _pass(carry): + return carry + + def select_A(carry): + def _func(carry): + carry = jnp.abs(carry) + return carry + + cond, value = carry + value = lax.cond(cond == 1, _func, _pass, value) + return cond, value + + def select_W(carry): + def _window(carry): + xx, val, xL, xR, trns = carry + val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + return xx, val, xL, xR, trns + + cond, value, xx, xL, xR, trns = carry + + carry = xx, value, xL, xR, trns + xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def normalize(carry): + def _norm(carry): + u = carry + u -= jnp.min(u, axis=1, keepdims=True) # positive value + u /= jnp.max(u, axis=1, keepdims=True) # normalize + return u + + cond, u = carry + u = lax.cond(cond==True, _norm, _pass, u) + return cond, u + + key = random.PRNGKey(init_key) + + selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) + kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + amp = random.uniform(key, shape=[numbers, k_tot, 1]) + + key, subkey = random.split(key) + + phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) + _u = jnp.sum(_u, axis=1) + + # perform absolute value function + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + carry = (cond, _u) + + cond, _u = vmap(select_A, 0, 0)(carry) + sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) + _u *= sgn # random flip of signature + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + mask = jnp.ones_like(_xc) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, xL, xR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + _u *= mask + + carry = if_norm, _u + _, _u = normalize(carry) # normalize value between [0, 1] for reaction-diffusion eq. + + return _u + +def init_multi_2DRand(xc, yc, numbers=10000, init_key=2022, k_tot=4, duMx = 1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, val, xL, xR, yL, yR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + val = x_win[:, None] * y_win[None, :] + return xx, yy, val, xL, xR, yL, yR, trns + + cond, value, xx, yy, xL, xR, yL, yR, trns = carry + + carry = xx, yy, value, xL, xR, yL, yR, trns + xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def __create_2DRand_init(u0, delu): + nx, ny = xc.shape[0], yc.shape[0] + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random field + u = jnp.zeros([nx, ny]) + + key = random.PRNGKey(init_key) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[1]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None] + ky * yc[None, :] + u += uk * jnp.sin(kdx + phs) + + # renormalize total velocity + u = u0 + delu * u / jnp.abs(u).mean() + + return u + + key = random.PRNGKey(init_key) + u0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=duMx) + key, subkey = random.split(key) + delu = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.5) + u = jax.vmap(__create_2DRand_init, axis_name='i')(u0, delu) + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + _yc = jnp.repeat(xc[None, :], numbers, axis=0) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + u = u * mask + u = u + u0[:,:,None] * (1. - mask) + + return u + +def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, + M0=0.1, dk=1, gamma=.1666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + print(mode) + modes = ['shocktube0','shocktube1','shocktube2','shocktube3','shocktube4','shocktube5','shocktube6','shocktube7', + '2D-shock', 'OTVortex', 'KHI', 'turbulence', 'sound_wave', 'c_discon', 'BlastWave'] + assert mode in modes, 'mode is not defined!!' + + _, nx, ny, nz = u.shape + + if mode[:-1] == 'shocktube': # shock tube + + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + Ncell = nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + Ncell = ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + Ncell = nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + if mode[-1] == '0': # test 0 for viscosity + nx0 = int(0.5*Ncell) + uL = [1., 0.75, 0.2, -0.3, 1.] + uR = [0.125, 0., 0.1, 0.9, 0.1] + elif mode[-1] == '1': # test 1 + nx0 = int(0.3*Ncell) + uL = [1., 0.75, 0., 0., 1.] + uR = [0.125, 0., 0., 0., 0.1] + elif mode[-1] == '2': # test 2 + nx0 = int(0.5*Ncell) + uL = [1., -2., 0., 0., 0.4] + uR = [1., 2., 0., 0., 0.4] + elif mode[-1] == '3': # test 3 + nx0 = int(0.5*Ncell) + uL = [1., 0., 0., 0., 1.e3] + uR = [1., 0., 0., 0., 0.01] + elif mode[-1] == '4': # test 4 + nx0 = int(0.4*Ncell) + uL = [5.99924, 19.5975, 0., 0., 460.894] + uR = [5.99242, -6.19633, 0., 0., 46.095] + elif mode[-1] == '5': # test 5 + nx0 = int(0.8 * Ncell) + uL = [1., -19.59745, 0., 0., 1.e3] + uR = [1., -19.59745, 0., 0., 0.01] + elif mode[-1] == '6': # test 6 + nx0 = int(0.5 * Ncell) + uL = [1.4, 0., 0., 0., 1.] + uR = [1., 0., 0., 0., 1.] + elif mode[-1] == '7': # test 7 + nx0 = int(0.5 * Ncell) + uL = [1.4, 0.1, 0., 0., 1.] + uR = [1., 0.1, 0., 0., 1.] + + # left + _u = _u.at[0, :nx0].set(uL[0]) + _u = _u.at[iX, :nx0].set(uL[1]) + _u = _u.at[iY, :nx0].set(uL[2]) + _u = _u.at[iZ, :nx0].set(uL[3]) + _u = _u.at[4, :nx0].set(uL[4]) + # right + _u = _u.at[0, nx0:].set(uR[0]) + _u = _u.at[iX, nx0:].set(uR[1]) + _u = _u.at[iY, nx0:].set(uR[2]) + _u = _u.at[iZ, nx0:].set(uR[3]) + _u = _u.at[4, nx0:].set(uR[4]) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + elif mode == '2D-shock': # shock tube + u1 = [0.5, 0., 0., 0., 0.1] + u2 = [0.1, 0., 1., 0., 1.] + u3 = [0.1, 1., 0., 0., 1.] + u4 = [0.1, 0., 0., 0., 0.01] + + # left-bottom + u = u.at[0, :nx//2, :ny//2].set(u1[0]) + u = u.at[1, :nx//2, :ny//2].set(u1[1]) + u = u.at[2, :nx//2, :ny//2].set(u1[2]) + u = u.at[3, :nx//2, :ny//2].set(u1[3]) + u = u.at[4, :nx//2, :ny//2].set(u1[4]) + # right-bottom + u = u.at[0, nx//2:, :ny//2].set(u2[0]) + u = u.at[1, nx//2:, :ny//2].set(u2[1]) + u = u.at[2, nx//2:, :ny//2].set(u2[2]) + u = u.at[3, nx//2:, :ny//2].set(u2[3]) + u = u.at[4, nx//2:, :ny//2].set(u2[4]) + # left-top + u = u.at[0, :nx//2, ny//2:].set(u3[0]) + u = u.at[1, :nx//2, ny//2:].set(u3[1]) + u = u.at[2, :nx//2, ny//2:].set(u3[2]) + u = u.at[3, :nx//2, ny//2:].set(u3[3]) + u = u.at[4, :nx//2, ny//2:].set(u3[4]) + # right-top + u = u.at[0, nx//2:, ny//2:].set(u4[0]) + u = u.at[1, nx//2:, ny//2:].set(u4[1]) + u = u.at[2, nx//2:, ny//2:].set(u4[2]) + u = u.at[3, nx//2:, ny//2:].set(u4[3]) + u = u.at[4, nx//2:, ny//2:].set(u4[4]) + + elif mode == 'OTVortex': # shock tube + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * xc.shape[0] + qLy = dy * yc.shape[0] + _xc = jnp.zeros([xc.shape[0] + 4]) + _yc = jnp.zeros([yc.shape[0] + 4]) + _xc = _xc.at[2:-2].set(xc) + _yc = _yc.at[2:-2].set(yc) + _xc = _xc.at[:2].set(jnp.array([-2 * dx, -dx])) + _yc = _yc.at[:2].set(jnp.array([-2 * dy, -dy])) + _xc = _xc.at[-2:].set(jnp.array([xc[-1] + dx, xc[-1] + 2. * dx])) + _yc = _yc.at[-2:].set(jnp.array([yc[-1] + dy, yc[-1] + 2. * dy])) + + u = u.at[0].add(gamma ** 2) + u = u.at[1].set(- jnp.sin(2. * jnp.pi * _yc[None, :, None] / qLy)) + u = u.at[2].set(jnp.sin(2. * jnp.pi * _xc[:, None, None] / qLx)) + u = u.at[3].add(0.) + u = u.at[4].add(gamma) + + elif mode == 'KHI': # Kelvin-Helmholtz instability + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + #gamma = 1.666666666666667 + #k = 1. # moved to the external input + d0_u = 2./(dk + 1.) + d0_d = dk * d0_u + d0 = 0.5 * (d0_u + d0_d) + #M0 = 0.1 # Mach number # moved to external input + ux = 1. + cs = ux/M0 + #ux = 0.1 * cs # << cs + p0 = cs**2 * d0/gamma + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * nx + qLy = dy * ny + kk = 4. # wave number + kx = kk * 2. * jnp.pi / qLx + dl = 5.e-3 * qLy + + bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 + + vx = jnp.zeros([nx, ny, nz]) + dd = jnp.zeros([nx, ny, nz]) + for i in range(nx): + _vx = jnp.where(yc > bound[i], ux, -ux) + _dd = jnp.where(yc > bound[i], d0_u, d0_d) + vx = vx.at[i, :, :].set(_vx[:, None]) + dd = dd.at[i, :, :].set(_dd[:, None]) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2].set(0.) + u = u.at[3].add(0.) + u = u.at[4].add(p0) + + elif mode == 'turbulence': # 3D decaying turbulence + + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + k_tot = 3 + vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + + del(kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz + fx = dfx * (np.arange(nx) - 1. - nx//2) + fy = dfy * (np.arange(ny) - 1. - ny//2) + fz = dfz * (np.arange(nz) - 1. - nz//2) + + vkx = np.fft.fftn(vx) * dx * dy * dz + vky = np.fft.fftn(vy) * dx * dy * dz + vkz = np.fft.fftn(vz) * dx * dy * dz + + # shift to kxi=0 is at the center + vkx = np.fft.fftshift(vkx) + vky = np.fft.fftshift(vky) + vkz = np.fft.fftshift(vkz) + + #for k in range(nz): + # for j in range(ny): + # for i in range(nx): + # ff = (fx[i]**2 + fy[j]**2 + fz[k]**2) + # fi = np.where(ff > 1.e-8, 1./ff, 0.) + # # subtract expansion k.vk + # fdv = fx[i] * vkx[i, j, k] + fy[j] * vky[i, j, k] + fz[k] * vkz[i, j, k] + # vkx -= fdv * fx[i] * fi + # vky -= fdv * fy[j] * fi + # vkz -= fdv * fz[k] * fi + + fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 + fi = np.where(fi > 1.e-8, 1./fi, 0.) + + fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi + vkx -= fdv * fx[:,None,None] + vky -= fdv * fy[None,:,None] + vkz -= fdv * fz[None,None,:] + del(fi, fdv) + + # shift back to original order + vkx = np.fft.ifftshift(vkx) + vky = np.fft.ifftshift(vky) + vkz = np.fft.ifftshift(vkz) + + # inverse FFT + vx = np.fft.ifftn(vkx).real * dfx * dfy * dfz + vy = np.fft.ifftn(vky).real * dfx * dfy * dfz + vz = np.fft.ifftn(vkz).real * dfx * dfy * dfz + + # renormalize total velocity + vtot = np.sqrt(vx**2 + vy**2 + vz**2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1,2:-2,2:-2,2:-2].set(jnp.array(vx)) + u = u.at[2,2:-2,2:-2,2:-2].set(jnp.array(vy)) + u = u.at[3,2:-2,2:-2,2:-2].add(jnp.array(vz)) + u = u.at[4].add(p0) + + elif mode == 'BlastWave': # Kelvin-Helmholtz instability + """ Stone Gardiner 2009 without B """ + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + db = 1. + pb = 0.1 + + pc = 1.e2 # central region + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + qL = (qLx + qLy + qLz)/3. + + #p0 = jnp.ones([nx, ny, nz]) * pb + RR = jnp.sqrt((xc[:,None,None] - xc[nx//2])**2 + + (yc[None,:,None] - yc[ny//2])**2 + + (zc[None,None,:] - zc[nz//2])**2) + p0 = jnp.where(RR > 0.05 * qL, pb, pc) + #for k in range(nz): + # for j in range(ny): + # for i in range(nx): + # RR = jnp.sqrt((xc[i] - 0.5 * qLx)**2 + (yc[j] - 0.5 * qLy)**2 + (zc[k] - 0.5 * qLz)**2) + # if RR < 0.1 * qL: + # p0 = p0.at[i,j,k].set(pc) + + u = u.at[0].set(db) + u = u.at[1].set(0.) + u = u.at[2].set(0.) + u = u.at[3].set(0.) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p0) + + elif mode == 'sound_wave': # sound wave + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + gamma = 1.666666666666667 + d0 = 1. + cs = 2. + p0 = cs**2 * d0/gamma + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + XC = xc + qL = (xc[1] - xc[0]) * nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + XC = yc + qL = (yc[1] - yc[0]) * ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + XC = zc + qL = (zc[1] - zc[0]) * nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + kk = 2. * jnp.pi / qL + _u = _u.at[0,2:-2].set(d0 * (1. + 1.e-3 * jnp.sin(kk * XC[:, None, None]))) + _u = _u.at[iX].set((_u[0] - d0) * cs /d0) + _u = _u.at[4].set(p0 + cs**2 * (_u[0] - d0) ) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + + elif mode == 'c_discon': # tangent discontinuity + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + p0 = 1. + vy0 = 0.1 + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + XC = xc + qL = (xc[1] - xc[0]) * nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + XC = yc + qL = (yc[1] - yc[0]) * ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + XC = zc + qL = (zc[1] - zc[0]) * nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + _u = _u.at[0].set(d0) + _u = _u.at[iY, 2:-2].set(vy0 * scipy.special.erf(0.5 * XC[:, None, None] / jnp.sqrt(0.1))) + _u = _u.at[4].set(p0) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + + return u + +@partial(jit, static_argnums=(3, 4, 5, 6, 7, 8, 9)) +def init_multi_HD(xc, yc, zc, numbers=10000, k_tot=10, init_key=2022, num_choise_k=2, + if_renorm=False, umax=1.e4, umin=1.e-8): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + + def _pass(carry): + return carry + + def select_A(carry): + def _func(carry): + carry = jnp.abs(carry) + return carry + + cond, value = carry + value = lax.cond(cond == 1, _func, _pass, value) + return cond, value + + def select_W(carry): + def _window(carry): + xx, val, xL, xR, trns = carry + val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + return xx, val, xL, xR, trns + + cond, value, xx, xL, xR, trns = carry + + carry = xx, value, xL, xR, trns + xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def renormalize(carry): + def _norm(carry): + u, key = carry + u -= jnp.min(u, axis=1, keepdims=True) # positive value + u /= jnp.max(u, axis=1, keepdims=True) # normalize + + key, subkey = random.split(key) + m_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + m_val = jnp.exp(m_val) + key, subkey = random.split(key) + b_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + b_val = jnp.exp(b_val) + return u * m_val[:, None] + b_val[:, None], key + + cond, u, key = carry + carry = u, key + u, key = lax.cond(cond==True, _norm, _pass, carry) + return cond, u, key + + assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + key = random.PRNGKey(init_key) + + selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) + kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + amp = random.uniform(key, shape=[numbers, k_tot, 1]) + + key, subkey = random.split(key) + + phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) + _u = jnp.sum(_u, axis=1) + + # perform absolute value function + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + carry = (cond, _u) + + cond, _u = vmap(select_A, 0, 0)(carry) + sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) + _u *= sgn # random flip of signature + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + mask = jnp.ones_like(_xc) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, xL, xR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + _u *= mask + + carry = if_renorm, _u, key + _, _u, _ = renormalize(carry) # renormalize value between a given values + + return _u[...,None,None] + +#@partial(jit, static_argnums=(3, 4, 5, 6)) +def init_multi_HD_shock(xc, yc, zc, numbers=10000, init_key=2022, umax=1.e4, umin=1.e-8): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def select_var(carry): + def _func(carry): + vmin, vmax = carry + return jnp.log(vmin), jnp.log(vmax) + + def _pass(carry): + return carry + + vmin, vmax = carry + vmin, vmax = lax.cond(vmin > 0., _func, _pass, carry) + return vmin, vmax + + nx = xc.shape[0] + + carry = umin, umax + u_min, u_max = select_var(carry) + + key = random.PRNGKey(init_key) + QLs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) + QLs = jnp.exp(QLs) + key, subkey = random.split(key) + QRs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) + QRs = jnp.exp(QRs) + + nx0s = nx * random.uniform(key, shape=([numbers, 1]), minval=0.25, maxval=0.75) + nx0s = nx0s.astype(int) + + u = jnp.arange(xc.shape[0]) + u = jnp.tile(u, (numbers, 1)) + + u = jax.vmap(jnp.where, axis_name='i')(u < nx0s, QLs, QRs) + return u[...,None,None] + +#@partial(jit, static_argnums=(4, 5, 6, 7, 8, 9)) +def init_multi_HD_KH(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, dkMx=2., kmax=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_KH_init(u, dk, kk): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0_u = 2./(dk + 1.) + d0_d = dk * d0_u + d0 = 0.5 * (d0_u + d0_d) + ux = 1. + cs = ux/M0 + p0 = cs**2 * d0/gamma + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * nx + qLy = dy * ny + kx = kk * 2. * jnp.pi / qLx + dl = 5.e-3 * qLy + # (numbers, nx) + bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 + + vx = jnp.zeros([nx, ny, nz]) + dd = jnp.zeros([nx, ny, nz]) + for i in range(nx): + _vx = jnp.where(yc > bound[i], ux, -ux) + _dd = jnp.where(yc > bound[i], d0_u, d0_d) + vx = vx.at[i, :, :].set(_vx[:, None]) + dd = dd.at[i, :, :].set(_dd[:, None]) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2].set(0.) + u = u.at[3].add(0.) + u = u.at[4].add(p0) + return u + + # create random density ratio + key = random.PRNGKey(init_key) + dk = random.uniform(key, shape=([numbers, 1]), minval=1. / dkMx, maxval=dkMx) + #create random wave-numbers + key, subkey = random.split(key) + kk = random.randint(key, shape=([numbers, 1]), minval=1, maxval=kmax) + print('vmap...') + u = jax.vmap(__create_KH_init, axis_name='i')(u, dk, kk) + + return u + +#@partial(jit, static_argnums=(4, 5, 6, 7, 8)) +def init_multi_HD_2DTurb(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_2DTurb_init(u): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random velocity field + vx, vy = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[2]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None, None] + ky * yc[None, :, None] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + + del (kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy = 1. / qLx, 1. / qLy + fx = dfx * (np.arange(nx) - 1. - nx // 2) + fy = dfy * (np.arange(ny) - 1. - ny // 2) + + vkx = np.fft.fftn(vx) * dx * dy + vky = np.fft.fftn(vy) * dx * dy + + # shift to kxi=0 is at the center + vkx = np.fft.fftshift(vkx) + vky = np.fft.fftshift(vky) + + fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 + fi = np.where(fi > 1.e-8, 1. / fi, 0.) + + fdv = (fx[:, None, None] * vkx + fy[None, :, None] * vky) * fi + vkx -= fdv * fx[:, None, None] + vky -= fdv * fy[None, :, None] + del (fi, fdv) + + # shift back to original order + vkx = np.fft.ifftshift(vkx) + vky = np.fft.ifftshift(vky) + + # inverse FFT + vx = np.fft.ifftn(vkx).real * dfx * dfy + vy = np.fft.ifftn(vky).real * dfx * dfy + + # renormalize total velocity + vtot = np.sqrt(vx ** 2 + vy ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(jnp.array(vx)) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(jnp.array(vy)) + u = u.at[4].add(p0) + return u + + u = jax.vmap(__create_2DTurb_init, axis_name='i')(u) + + return u + +def init_multi_HD_2DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, + dMx=1.e1, TMx=1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, val, xL, xR, yL, yR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + val = x_win[:, None] * y_win[None, :] + return xx, yy, val, xL, xR, yL, yR, trns + + cond, value, xx, yy, xL, xR, yL, yR, trns = carry + + carry = xx, yy, value, xL, xR, yL, yR, trns + xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, yy, xL, xR, yL, yR, trns + + def __create_2DRand_init(u, d0, T0, delD, delP): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + + p0 = d0 * T0 + cs = jnp.sqrt(T0 * gamma) + u0 = M0 * cs + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random velocity field + d, p, vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[4]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None, None] + ky * yc[None, :, None] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + p += uk * jnp.sin(kdx + phs[2]) + d += uk * jnp.sin(kdx + phs[3]) + + del (kdx, uk, phs) + + # renormalize total velocity + vtot = np.sqrt(vx ** 2 + vy ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + #d = d0 + delD * d / jnp.abs(d).mean() + #p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1. + delD * d / jnp.abs(d).mean()) + p = p0 * (1. + delP * p / jnp.abs(p).mean()) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + return u + + key = random.PRNGKey(init_key) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + key, subkey = random.split(key) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + key, subkey = random.split(key) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + key, subkey = random.split(key) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + u = jax.vmap(__create_2DRand_init, axis_name='i')(u, d0, T0, delD, delP) + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) # add batch + _yc = jnp.repeat(yc[None, :], numbers, axis=0) # add batch + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns + cond, mask, _xc, _yc, xL, xR, yL, yR, trns = vmap(select_W, 0, 0)(carry) + + u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, None]) + u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, None])) + u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, None])) + + return u + +def init_multi_HD_3DTurb(u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_3DTurb_init(u): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + + del(kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz + fx = dfx * (np.arange(nx) - 1. - nx//2) + fy = dfy * (np.arange(ny) - 1. - ny//2) + fz = dfz * (np.arange(nz) - 1. - nz//2) + + vkx = np.fft.fftn(vx) * dx * dy * dz + vky = np.fft.fftn(vy) * dx * dy * dz + vkz = np.fft.fftn(vz) * dx * dy * dz + + # shift to kxi=0 is at the center + vkx = np.fft.fftshift(vkx) + vky = np.fft.fftshift(vky) + vkz = np.fft.fftshift(vkz) + + fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 + fi = np.where(fi > 1.e-8, 1./fi, 0.) + + fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi + vkx -= fdv * fx[:,None,None] + vky -= fdv * fy[None,:,None] + vkz -= fdv * fz[None,None,:] + del(fi, fdv) + + # shift back to original order + vkx = np.fft.ifftshift(vkx) + vky = np.fft.ifftshift(vky) + vkz = np.fft.ifftshift(vkz) + + # inverse FFT + vx = np.fft.ifftn(vkx).real * dfx * dfy * dfz + vy = np.fft.ifftn(vky).real * dfx * dfy * dfz + vz = np.fft.ifftn(vkz).real * dfx * dfy * dfz + + # renormalize total velocity + vtot = np.sqrt(vx**2 + vy**2 + vz**2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(jnp.array(vx)) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(jnp.array(vy)) + u = u.at[3, 2:-2, 2:-2, 2:-2].set(jnp.array(vz)) + u = u.at[4].add(p0) + return u + + u = jax.vmap(__create_3DTurb_init, axis_name='i')(u) + + return u + +def init_multi_HD_3DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, + dMx=1.e1, TMx=1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + z_win = 0.5 * (jnp.tanh((zz - zL) / trns) - jnp.tanh((zz - zR) / trns)) + val = x_win[:, None, None] * y_win[None, :, None] * z_win[None, None, :] + return xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns + + cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns = carry + + carry = xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns + xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns + + def __create_3DRand_init(u, d0, T0, delD, delP): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + + p0 = d0 * T0 + cs = jnp.sqrt(T0 * gamma) + u0 = M0 * cs + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + d, p, vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), \ + jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + p += uk * jnp.sin(kdx + phs[3]) + d += uk * jnp.sin(kdx + phs[4]) + + del(kdx, uk, phs) + + # renormalize total velocity + vtot = np.sqrt(vx ** 2 + vy ** 2 + vz ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + #d = d0 + delD * d / jnp.abs(d).mean() + #p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1. + delD * d / jnp.abs(d).mean()) + p = p0 * (1. + delP * p / jnp.abs(p).mean()) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + return u + + key = random.PRNGKey(init_key) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + key, subkey = random.split(key) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + key, subkey = random.split(key) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + key, subkey = random.split(key) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + + u = jax.vmap(__create_3DRand_init, axis_name='i')(u, d0, T0, delD, delP) + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0], zc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + zL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + zR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + _yc = jnp.repeat(yc[None, :], numbers, axis=0) + _zc = jnp.repeat(zc[None, :], numbers, axis=0) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns + cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns = vmap(select_W, 0, 0)(carry) + + u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, :]) + u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, :])) + u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, :])) + + return u + +def bc(u, dx, Ncell, mode='periodic'): + _u = jnp.zeros(Ncell+4) # because of 2nd-order precision in space + _u = _u.at[2:Ncell+2].set(u) + if mode=='periodic': # periodic boundary condition + _u = _u.at[0:2].set(u[-2:]) # left hand side + _u = _u.at[Ncell + 2:Ncell + 4].set(u[0:2]) # right hand side + elif mode=='reflection': + _u = _u.at[0].set(- u[3]) # left hand side + _u = _u.at[1].set(- u[2]) # left hand side + _u = _u.at[-2].set(- u[-3]) # right hand side + _u = _u.at[-1].set(- u[-4]) # right hand side + elif mode=='copy': + _u = _u.at[0].set(u[3]) # left hand side + _u = _u.at[1].set(u[2]) # left hand side + _u = _u.at[-2].set(u[-3]) # right hand side + _u = _u.at[-1].set(u[-4]) # right hand side + + return _u + +def bc_2D(_u, mode='trans'): + Nx, Ny = _u.shape + u = jnp.zeros([Nx + 4, Ny + 4]) # because of 2nd-order precision in space + u = u.at[2:-2, 2:-2].set(_u) + Nx += 2 + Ny += 2 + + if mode=='periodic': # periodic boundary condition + # left hand side + u = u.at[0:2, 2:-2].set(u[Nx-2:Nx, 2:-2]) # x + u = u.at[2:-2, 0:2].set(u[2:-2, Ny-2:Ny]) # y + # right hand side + u = u.at[Nx:Nx+2, 2:-2].set(u[2:4, 2:-2]) + u = u.at[2:-2, Ny:Ny+2].set(u[2:-2, 2:4]) + elif mode=='trans': # periodic boundary condition + # left hand side + u = u.at[0, 2:-2].set(u[3, 2:-2]) # x + u = u.at[2:-2, 0].set(u[2:-2, 3]) # y + u = u.at[1, 2:-2].set(u[2, 2:-2]) # x + u = u.at[2:-2, 1].set(u[2:-2, 2]) # y + # right hand side + u = u.at[-2, 2:-2].set(u[-3, 2:-2]) + u = u.at[2:-2, -2].set(u[2:-2, -3]) + u = u.at[-1, 2:-2].set(u[-4, 2:-2]) + u = u.at[2:-2, -1].set(u[2:-2, -4]) + elif mode=='Neumann': # periodic boundary condition + # left hand side + u = u.at[0, 2:-2].set(0.) # x + u = u.at[2:-2, 0].set(0.) # y + u = u.at[1, 2:-2].set(0.) # x + u = u.at[2:-2, 1].set(0.) # y + # right hand side + u = u.at[-2, 2:-2].set(0.) + u = u.at[2:-2, -2].set(0.) + u = u.at[-1, 2:-2].set(0.) + u = u.at[2:-2, -1].set(0.) + return u + +def bc_HD(u, mode): + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + if mode=='periodic': # periodic boundary condition + # left hand side + u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx-2:Nx, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0:2, 2:-2].set(u[:, 2:-2, Ny-2:Ny, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0:2].set(u[:, 2:-2, 2:-2, Nz-2:Nz]) # z + # right hand side + u = u.at[:, Nx:Nx+2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, Ny:Ny+2, 2:-2].set(u[:, 2:-2, 2:4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, Nz:Nz+2].set(u[:, 2:-2, 2:-2, 2:4]) + elif mode=='trans': # periodic boundary condition + # left hand side + u = u.at[:, 0, 2:-2, 2:-2].set(u[:, 3, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.at[:, 1, 2:-2, 2:-2].set(u[:, 2, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + # right hand side + u = u.at[:, -2, 2:-2, 2:-2].set(u[:, -3, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.at[:, -1, 2:-2, 2:-2].set(u[:, -4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + elif mode=='KHI': # x: periodic, y, z : trans + # left hand side + u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2:Nx, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + # right hand side + u = u.at[:, Nx:Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + return u + +def bc_HD_vis(u): # for viscosity + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, 2:4]) # xBzB + u = u.at[:, 0:2, Ny:Ny+2, 2:-2].set(u[:, 2:4, Ny-2:Ny, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, Nz:Nz+2].set(u[:, 2:4, 2:-2, Nz-2:Nz]) # xBzT + u = u.at[:, Nx:Nx+2, 0:2, 2:-2].set(u[:, Nx-2:Nx, 2:4, 2:-2]) # xTyB + u = u.at[:, Nx:Nx+2, 2:-2, 0:2].set(u[:, Nx-2:Nx, 2:-2, 2:4]) # xTzB + u = u.at[:, Nx:Nx+2, Ny:Ny+2, 2:-2].set(u[:, Nx-2:Nx, Ny-2:Ny, 2:-2]) # xTyT + u = u.at[:, Nx:Nx+2, 2:-2, Nz:Nz+2].set(u[:, Nx-2:Nx, 2:-2, Nz-2:Nz]) # xTzT + + return u + +def VLlimiter(a, b, c, alpha=2.): + return jnp.sign(c)\ + *(0.5 + 0.5*jnp.sign(a*b))\ + *jnp.minimum(alpha*jnp.minimum(jnp.abs(a), jnp.abs(b)), jnp.abs(c)) + +def limiting(u, Ncell, if_second_order): + # under construction + duL = u[1:Ncell + 3] - u[0:Ncell + 2] + duR = u[2:Ncell + 4] - u[1:Ncell + 3] + duM = (u[2:Ncell + 4] - u[0:Ncell + 2])*0.5 + gradu = VLlimiter(duL, duR, duM) * if_second_order + # -1:Ncell + #uL, uR = jnp.zeros(Ncell+4), jnp.zeros(Ncell+4) + uL, uR = jnp.zeros_like(u), jnp.zeros_like(u) + uL = uL.at[1:Ncell+3].set(u[1:Ncell+3] - 0.5*gradu) # left of cell + uR = uR.at[1:Ncell+3].set(u[1:Ncell+3] + 0.5*gradu) # right of cell + return uL, uR + +def limiting_HD(u, if_second_order): + nd, nx, ny, nz = u.shape + uL, uR = u, u + nx -= 4 + + duL = u[:, 1:nx + 3, :, :] - u[:, 0:nx + 2, :, :] + duR = u[:, 2:nx + 4, :, :] - u[:, 1:nx + 3, :, :] + duM = (u[:, 2:nx + 4, :, :] - u[:, 0:nx + 2, :, :]) * 0.5 + gradu = VLlimiter(duL, duR, duM) * if_second_order + # -1:Ncell + uL = uL.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] - 0.5*gradu) # left of cell + uR = uR.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] + 0.5*gradu) # right of cell + + uL = jnp.where(uL[0] > 0., uL, u) + uL = jnp.where(uL[4] > 0., uL, u) + uR = jnp.where(uR[0] > 0., uR, u) + uR = jnp.where(uR[4] > 0., uR, u) + + return uL, uR + +def save_data(u, xc, i_save, save_dir, dt_save=None, if_final=False): + if if_final: + jnp.save(save_dir+'/x_coordinate', xc) + # + tc = jnp.arange(i_save+1)*dt_save + jnp.save(save_dir+'/t_coordinate', tc) + # + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + else: + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + +def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): + if if_final: + jnp.save(save_dir+'/x_coordinate', xc) + jnp.save(save_dir+'/y_coordinate', yc) + jnp.save(save_dir+'/z_coordinate', zc) + # + tc = jnp.arange(i_save+1)*dt_save + jnp.save(save_dir+'/t_coordinate', tc) + # + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + else: + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + +def Courant(u, dx): + stability_adv = dx/(jnp.max(jnp.abs(u)) + 1.e-8) + return stability_adv + +def Courant_diff(dx, epsilon=1.e-3): + stability_dif = 0.5*dx**2/(epsilon + 1.e-8) + return stability_dif + +def Courant_diff_2D(dx, dy, epsilon=1.e-3): + stability_dif_x = 0.5*dx**2/(epsilon + 1.e-8) + stability_dif_y = 0.5*dy**2/(epsilon + 1.e-8) + return jnp.min(jnp.array([stability_dif_x, stability_dif_y])) + +def Courant_HD(u, dx, dy, dz, gamma): + cs = jnp.sqrt(gamma*u[4]/u[0]) # sound velocity + stability_adv_x = dx/(jnp.max(cs + jnp.abs(u[1])) + 1.e-8) + stability_adv_y = dy/(jnp.max(cs + jnp.abs(u[2])) + 1.e-8) + stability_adv_z = dz/(jnp.max(cs + jnp.abs(u[3])) + 1.e-8) + stability_adv = jnp.min(jnp.array([stability_adv_x, stability_adv_y, stability_adv_z])) + return stability_adv + +def Courant_vis_HD(dx, dy, dz, eta, zeta): + #visc = jnp.max(jnp.array([eta, zeta])) + visc = 4. / 3. * eta + zeta # maximum + stability_dif_x = 0.5*dx**2/(visc + 1.e-8) + stability_dif_y = 0.5*dy**2/(visc + 1.e-8) + stability_dif_z = 0.5*dz**2/(visc + 1.e-8) + stability_dif = jnp.min(jnp.array([stability_dif_x, stability_dif_y, stability_dif_z])) + return stability_dif + diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 4d4373b..696b5a0 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -166,17 +166,33 @@ def main(): title = fl.split('\\')[-1][:-7].split('_') print(title) if title[0] == '1D': - index1.append(title[1]) - index2.append(title[3]) - index3.append(title[4]) + if title[1] == 'CFD': + index1.append(title[0] + title[1]) + index2.append(title[3] + title[4] + '_' + title[2] + '_' + title[5]) + index3.append(title[7]) + else: + index1.append(title[1]) + index2.append(title[3]) + index3.append(title[4]) elif title[0] == '2D': - index1.append(title[1]) - index2.append(title[2]) - index3.append(title[4]) + if title[1] == 'CFD': + index1.append(title[0] + title[1]) + index2.append(title[3] + title[3] + title[4] + '_' + title[2] + '_' + title[6]) + index3.append(title[9]) + else: + index1.append(title[1]) + index2.append(title[2]) + index3.append(title[4]) + elif title[0] == '3D': + index1.append(title[0] + title[1]) + index2.append(title[3] + title[4] + title[5] + '_' + title[2] + '_' + title[6]) + index3.append(title[8]) else: index1.append(title[0]) index2.append(title[1] + title[2]) index3.append(title[3]) + if index3[-1][0] == 'U': + index3[-1] = index3[-1][:4] indexes = [index1, index2, index3] # create dataframe diff --git a/pdebench/models/config/args/config_2DCFD.yaml b/pdebench/models/config/args/config_2DCFD.yaml new file mode 100644 index 0000000..0ae0ce7 --- /dev/null +++ b/pdebench/models/config/args/config_2DCFD.yaml @@ -0,0 +1,25 @@ +model_name: 'FNO' +if_training: True +continue_training: False +batch_size: 20 +unroll_step: 20 +num_workers: 2 +t_train: 21 +model_update: 2 +filename: '2D_CFD_M0.1_Eta0.01_Zeta0.01_periodic_128_Train.hdf5' +single_file: True +reduced_resolution: 2 +reduced_resolution_t: 1 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +#Unet +in_channels: 4 +out_channels: 4 +#FNO +num_channels: 4 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 \ No newline at end of file diff --git a/pdebench/models/config/args/config_3DCFD.yaml b/pdebench/models/config/args/config_3DCFD.yaml new file mode 100644 index 0000000..2bbcfc9 --- /dev/null +++ b/pdebench/models/config/args/config_3DCFD.yaml @@ -0,0 +1,24 @@ +model_name: 'Unet' +if_training: True +t_train: 21 +continue_training: False +batch_size: 5 +unroll_step: 20 +model_update: 1 +filename: '3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5' +single_file: True +reduced_resolution: 2 +reduced_resolution_t: 1 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +#Unet +in_channels: 5 +out_channels: 5 +#FNO +num_channels: 5 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 # should be the same value to unroll_step ?? \ No newline at end of file diff --git a/pdebench/models/config/args/config_ReacDiff.yaml b/pdebench/models/config/args/config_ReacDiff.yaml new file mode 100644 index 0000000..a4dbfbf --- /dev/null +++ b/pdebench/models/config/args/config_ReacDiff.yaml @@ -0,0 +1,25 @@ +model_name: 'Unet' +if_training: True +continue_training: False +batch_size: 50 +unroll_step: 10 +t_train: 30 +model_update: 1 +filename: 'ReacDiff_Nu0.5_Rho1.0.hdf5' +single_file: True +reduced_resolution: 4 +reduced_resolution_t: 1 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +num_workers: 0 +#Unet +in_channels: 1 +out_channels: 1 +#FNO +num_channels: 1 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 5 # should be the same value to unroll_step ?? \ No newline at end of file diff --git a/pdebench/models/config/args/config_pinn_CFD1d.yaml b/pdebench/models/config/args/config_pinn_CFD1d.yaml new file mode 100644 index 0000000..4efa594 --- /dev/null +++ b/pdebench/models/config/args/config_pinn_CFD1d.yaml @@ -0,0 +1,15 @@ +model_name: 'PINN' +scenario: 'pde1D' +model_update: 500 +filename: '1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5' +epochs: 15000 +input_ch: 2 +output_ch: 3 +learning_rate: 1.e-3 +root_path: '../data' +val_num: 10 +if_periodic_bc: True +period: 5000 +val_time: 1.0 +val_batch_idx: 10 +aux_params: [1.6666666667] \ No newline at end of file diff --git a/pdebench/models/config/args/config_pinn_pde1d.yaml b/pdebench/models/config/args/config_pinn_pde1d.yaml new file mode 100644 index 0000000..b0013ef --- /dev/null +++ b/pdebench/models/config/args/config_pinn_pde1d.yaml @@ -0,0 +1,15 @@ +model_name: 'PINN' +scenario: 'pde1D' +model_update: 500 +filename: '1D_Advection_Sols_beta0.1.hdf5' +epochs: 15000 +input_ch: 2 +output_ch: 1 +learning_rate: 1.e-3 +root_path: '../data' +val_num: 10 +if_periodic_bc: True +period: 5000 +val_time: 2.0 +val_batch_idx: 10 +aux_params: [0.1] \ No newline at end of file diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 6b52982..5a0c437 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -253,7 +253,56 @@ def __init__(self, filename, y = torch.tensor(y, dtype=torch.float) X, Y = torch.meshgrid(x, y) self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + if len(idx_cfd)==5: # 3D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + idx_cfd[4]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 5], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,3] = _data # batch, x, t, ch + # Vz + _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,4] = _data # batch, x, t, ch + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + z = np.array(f["z-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + z = torch.tensor(z, dtype=torch.float) + X, Y, Z = torch.meshgrid(x, y, z) + self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ + ::reduced_resolution,\ + ::reduced_resolution] + else: # scalar equations ## data dim = [t, x1, ..., xd, v] _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 2b19051..1c4b9fe 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -159,6 +159,7 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 code for calculate metrics discussed in the Brain-storming session MSE, normalized MSE, max error, MSE at the boundaries, conserved variables, MSE in Fourier space, temporal sensitivity """ + pred, target = pred.to(device), target.to(device) # (batch, nx^i..., timesteps, nc) idxs = target.size() if len(idxs) == 4: diff --git a/pdebench/models/pinn/pde_definitions.py b/pdebench/models/pinn/pde_definitions.py index f4740d7..9369cf3 100644 --- a/pdebench/models/pinn/pde_definitions.py +++ b/pdebench/models/pinn/pde_definitions.py @@ -84,3 +84,126 @@ def pde_swe2d(x, y): eq3 = v_t + u * v_x + v * v_y + g * h_y return eq1 + eq2 + eq3 + +def pde_adv1d(x, y, beta): + dy_x = dde.grad.jacobian(y, x, i=0, j=0) + dy_t = dde.grad.jacobian(y, x, i=0, j=1) + return dy_t + beta * dy_x + +def pde_diffusion_reaction_1d(x, y, nu, rho): + dy_t = dde.grad.jacobian(y, x, i=0, j=1) + dy_xx = dde.grad.hessian(y, x, i=0, j=0) + return dy_t - nu * dy_xx - rho * y * (1. - y) + +def pde_burgers1D(x, y, nu): + dy_x = dde.grad.jacobian(y, x, i=0, j=0) + dy_t = dde.grad.jacobian(y, x, i=0, j=1) + dy_xx = dde.grad.hessian(y, x, i=0, j=0) + return dy_t + y * dy_x - nu / np.pi * dy_xx + +def pde_CFD1d(x, y, gamma): + h = y[..., 0].unsqueeze(1) # rho + u = y[..., 1].unsqueeze(1) # v + p = y[..., 2].unsqueeze(1) # p + E = p/(gamma - 1.) + 0.5 * h * u**2 + E = E.unsqueeze(1) + Fx = u * (E + p) + Fx = Fx.unsqueeze(1) + + # non conservative form + hu_x = dde.grad.jacobian(h * u, x, i=0, j=0) + h_t = dde.grad.jacobian(y, x, i=0, j=1) + u_x = dde.grad.jacobian(y, x, i=1, j=0) + u_t = dde.grad.jacobian(y, x, i=1, j=1) + p_x = dde.grad.jacobian(y, x, i=2, j=0) + Fx_x = dde.grad.jacobian(Fx, x, i=0, j=0) + E_t = dde.grad.jacobian(E, x, i=0, j=1) + + eq1 = h_t + hu_x + eq2 = h * (u_t + u * u_x) - p_x + eq3 = E_t + Fx_x + + return eq1 + eq2 + eq3 + +def pde_CFD2d(x, y, gamma): + h = y[..., 0].unsqueeze(1) # rho + ux = y[..., 1].unsqueeze(1) # vx + uy = y[..., 2].unsqueeze(1) # vy + p = y[..., 3].unsqueeze(1) # p + E = p/(gamma - 1.) + 0.5 * h * (ux**2 + uy**2) + E = E.unsqueeze(1) + Fx = ux * (E + p) + Fx = Fx.unsqueeze(1) + Fy = uy * (E + p) + Fy = Fy.unsqueeze(1) + + # non conservative form + hu_x = dde.grad.jacobian(h * ux, x, i=0, j=0) + hu_y = dde.grad.jacobian(h * uy, x, i=0, j=1) + h_t = dde.grad.jacobian(y, x, i=0, j=2) + ux_x = dde.grad.jacobian(y, x, i=1, j=0) + ux_y = dde.grad.jacobian(y, x, i=1, j=1) + ux_t = dde.grad.jacobian(y, x, i=1, j=2) + uy_x = dde.grad.jacobian(y, x, i=2, j=0) + uy_y = dde.grad.jacobian(y, x, i=2, j=1) + uy_t = dde.grad.jacobian(y, x, i=2, j=2) + p_x = dde.grad.jacobian(y, x, i=3, j=0) + p_y = dde.grad.jacobian(y, x, i=3, j=1) + Fx_x = dde.grad.jacobian(Fx, x, i=0, j=0) + Fy_y = dde.grad.jacobian(Fy, x, i=0, j=1) + E_t = dde.grad.jacobian(E, x, i=0, j=2) + + eq1 = h_t + hu_x + hu_y + eq2 = h * (ux_t + ux * ux_x + uy * ux_y) - p_x + eq3 = h * (uy_t + ux * uy_x + uy * uy_y) - p_y + eq4 = E_t + Fx_x + Fy_y + + return eq1 + eq2 + eq3 + eq4 + +def pde_CFD3d(x, y, gamma): + h = y[..., 0].unsqueeze(1) # rho + ux = y[..., 1].unsqueeze(1) # vx + uy = y[..., 2].unsqueeze(1) # vy + uz = y[..., 3].unsqueeze(1) # vz + p = y[..., 4].unsqueeze(1) # p + E = p/(gamma - 1.) + 0.5 * h * (ux**2 + uy**2 + uz**2) + E = E.unsqueeze(1) + Fx = ux * (E + p) + Fx = Fx.unsqueeze(1) + Fy = uy * (E + p) + Fy = Fy.unsqueeze(1) + Fz = uz * (E + p) + Fz = Fz.unsqueeze(1) + + # non conservative form + hu_x = dde.grad.jacobian(h * ux, x, i=0, j=0) + hu_y = dde.grad.jacobian(h * uy, x, i=0, j=1) + hu_z = dde.grad.jacobian(h * uy, x, i=0, j=2) + h_t = dde.grad.jacobian(y, x, i=0, j=3) + ux_x = dde.grad.jacobian(y, x, i=1, j=0) + ux_y = dde.grad.jacobian(y, x, i=1, j=1) + ux_z = dde.grad.jacobian(y, x, i=1, j=2) + ux_t = dde.grad.jacobian(y, x, i=1, j=3) + uy_x = dde.grad.jacobian(y, x, i=2, j=0) + uy_y = dde.grad.jacobian(y, x, i=2, j=1) + uy_z = dde.grad.jacobian(y, x, i=2, j=2) + uy_t = dde.grad.jacobian(y, x, i=2, j=3) + uz_x = dde.grad.jacobian(y, x, i=3, j=0) + uz_y = dde.grad.jacobian(y, x, i=3, j=1) + uz_z = dde.grad.jacobian(y, x, i=3, j=2) + uz_t = dde.grad.jacobian(y, x, i=3, j=3) + p_x = dde.grad.jacobian(y, x, i=4, j=0) + p_y = dde.grad.jacobian(y, x, i=4, j=1) + p_z = dde.grad.jacobian(y, x, i=4, j=2) + Fx_x = dde.grad.jacobian(Fx, x, i=0, j=0) + Fy_y = dde.grad.jacobian(Fy, x, i=0, j=1) + Fz_z = dde.grad.jacobian(Fz, x, i=0, j=2) + E_t = dde.grad.jacobian(E, x, i=0, j=3) + + eq1 = h_t + hu_x + hu_y + hu_z + eq2 = h * (ux_t + ux * ux_x + uy * ux_y + uz * ux_z) - p_x + eq3 = h * (uy_t + ux * uy_x + uy * uy_y + uz * uy_z) - p_y + eq4 = h * (uz_t + ux * uz_x + uy * uz_y + uz * uz_z) - p_z + eq5 = E_t + Fx_x + Fy_y + Fz_z + + return eq1 + eq2 + eq3 + eq4 + eq5 \ No newline at end of file diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index 4708011..6c8a758 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -15,12 +15,21 @@ PINNDataset2D, PINNDatasetDiffSorption, PINNDatasetBump, + PINNDataset1Dpde, + PINNDataset2Dpde, + PINNDataset3Dpde, ) from .pde_definitions import ( pde_diffusion_reaction, pde_swe2d, pde_diffusion_sorption, pde_swe1d, + pde_adv1d, + pde_diffusion_reaction_1d, + pde_burgers1D, + pde_CFD1d, + pde_CFD2d, + pde_CFD3d, ) from metrics import metrics, metric_func @@ -175,6 +184,162 @@ def setup_swe_2d(filename, seed) -> Tuple[dde.Model, PINNDataset2D]: return model, dataset +def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", + root_path='data', + val_batch_idx=-1, + input_ch=2, + output_ch=1, + hidden_ch=40, + xL=0., + xR=1., + if_periodic_bc=True, + aux_params=[0.1]): + + # TODO: read from dataset config file + geom = dde.geometry.Interval(xL, xR) + if filename[0] == 'R': + timedomain = dde.geometry.TimeDomain(0, 1.0) + pde = lambda x, y : pde_diffusion_reaction_1d(x, y, aux_params[0], aux_params[1]) + else: + if filename.split('_')[1][0]=='A': + timedomain = dde.geometry.TimeDomain(0, 2.0) + pde = lambda x, y: pde_adv1d(x, y, aux_params[0]) + elif filename.split('_')[1][0] == 'B': + timedomain = dde.geometry.TimeDomain(0, 2.0) + pde = lambda x, y: pde_burgers1D(x, y, aux_params[0]) + elif filename.split('_')[1][0]=='C': + timedomain = dde.geometry.TimeDomain(0, 1.0) + pde = lambda x, y: pde_CFD1d(x, y, aux_params[0]) + geomtime = dde.geometry.GeometryXTime(geom, timedomain) + + dataset = PINNDataset1Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + # prepare initial condition + initial_input, initial_u = dataset.get_initial_condition() + if filename.split('_')[1][0] == 'C': + ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,0].unsqueeze(1), component=0) + ic_data_v = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,1].unsqueeze(1), component=1) + ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,2].unsqueeze(1), component=2) + else: + ic_data_u = dde.icbc.PointSetBC(initial_input.cpu(), initial_u, component=0) + # prepare boundary condition + if if_periodic_bc: + bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) + if filename.split('_')[1][0] == 'C': + data = dde.data.TimePDE( + geomtime, + pde, + [ic_data_d, ic_data_v, ic_data_p, bc], + num_domain=1000, + num_boundary=1000, + num_initial=5000, + ) + else: + data = dde.data.TimePDE( + geomtime, + pde, + [ic_data_u, bc], + num_domain=1000, + num_boundary=1000, + num_initial=5000, + ) + else: + ic = dde.icbc.IC( + geomtime, lambda x: -np.sin(np.pi * x[:, 0:1]), lambda _, on_initial: on_initial + ) + bd_input, bd_uL, bd_uR = dataset.get_boundary_condition() + bc_data_uL = dde.icbc.PointSetBC(bd_input.cpu(), bd_uL, component=0) + bc_data_uR = dde.icbc.PointSetBC(bd_input.cpu(), bd_uR, component=0) + + data = dde.data.TimePDE( + geomtime, + pde, + [ic, bc_data_uL, bc_data_uR], + num_domain=1000, + num_boundary=1000, + num_initial=5000, + ) + net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + model = dde.Model(data, net) + + return model, dataset + +def setup_CFD2D(filename="2D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", + root_path='data', + val_batch_idx=-1, + input_ch=2, + output_ch=4, + hidden_ch=40, + xL=0., + xR=1., + yL=0., + yR=1., + if_periodic_bc=True, + aux_params=[1.6667]): + + # TODO: read from dataset config file + geom = dde.geometry.Rectangle((-1, -1), (1, 1)) + timedomain = dde.geometry.TimeDomain(0., 1.0) + pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) + geomtime = dde.geometry.GeometryXTime(geom, timedomain) + + dataset = PINNDataset2Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + # prepare initial condition + initial_input, initial_u = dataset.get_initial_condition() + ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,0].unsqueeze(1), component=0) + ic_data_vx = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,1].unsqueeze(1), component=1) + ic_data_vy = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,2].unsqueeze(1), component=2) + ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,3].unsqueeze(1), component=3) + # prepare boundary condition + bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) + data = dde.data.TimePDE( + geomtime, + pde, + [ic_data_d, ic_data_vx, ic_data_vy, ic_data_p],#, bc], + num_domain=1000, + num_boundary=1000, + num_initial=5000, + ) + net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + model = dde.Model(data, net) + + return model, dataset + +def setup_CFD3D(filename="3D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", + root_path='data', + val_batch_idx=-1, + input_ch=2, + output_ch=4, + hidden_ch=40, + aux_params=[1.6667]): + + # TODO: read from dataset config file + geom = dde.geometry.Cuboid((0., 0., 0.), (1., 1., 1.)) + timedomain = dde.geometry.TimeDomain(0., 1.0) + pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) + geomtime = dde.geometry.GeometryXTime(geom, timedomain) + + dataset = PINNDataset3Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + # prepare initial condition + initial_input, initial_u = dataset.get_initial_condition() + ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,0].unsqueeze(1), component=0) + ic_data_vx = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,1].unsqueeze(1), component=1) + ic_data_vy = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,2].unsqueeze(1), component=2) + ic_data_vz = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,3].unsqueeze(1), component=3) + ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,4].unsqueeze(1), component=4) + # prepare boundary condition + bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) + data = dde.data.TimePDE( + geomtime, + pde, + [ic_data_d, ic_data_vx, ic_data_vy, ic_data_vz, ic_data_p, bc], + num_domain=1000, + num_boundary=1000, + num_initial=5000, + ) + net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + model = dde.Model(data, net) + + return model, dataset def run_training(scenario, epochs, learning_rate, model_update, flnm, seed): if scenario == "swe2d": diff --git a/pdebench/models/pinn/utils.py b/pdebench/models/pinn/utils.py index 92dba36..ba11980 100644 --- a/pdebench/models/pinn/utils.py +++ b/pdebench/models/pinn/utils.py @@ -318,3 +318,387 @@ def get_initial_condition(self): u0 = np.ones(Nx) * np.random.uniform(0, 0.2) return (self.data_input[:Nx, :], np.expand_dims(u0, 1)) + +class PINNDataset1Dpde(Dataset): + def __init__(self, filename, root_path='data', val_batch_idx=-1): + """ + :param filename: filename that contains the dataset + :type filename: STR + """ + + # load data file + data_path = os.path.join(root_path, filename) + h5_file = h5py.File(data_path, "r") + + # build input data from individual dimensions + # dim x = [x] + self.data_grid_x = torch.tensor(h5_file["x-coordinate"], dtype=torch.float) + self.dx = self.data_grid_x[1] - self.data_grid_x[0] + self.xL = self.data_grid_x[0] - 0.5 * self.dx + self.xR = self.data_grid_x[-1] + 0.5 * self.dx + self.xdim = self.data_grid_x.size(0) + # # dim t = [t] + self.data_grid_t = torch.tensor(h5_file["t-coordinate"], dtype=torch.float) + + # main data + keys = list(h5_file.keys()) + keys.sort() + if 'tensor' in keys: + self.data_output = torch.tensor(np.array(h5_file["tensor"][val_batch_idx]), + dtype=torch.float) + # permute from [t, x] -> [x, t] + self.data_output = self.data_output.T + + # for init/boundary conditions + self.init_data = self.data_output[..., 0, None] + self.bd_data_L = self.data_output[0, :, None] + self.bd_data_R = self.data_output[-1, :, None] + + else: + _data1 = np.array(h5_file["density"][val_batch_idx]) + _data2 = np.array(h5_file["Vx"][val_batch_idx]) + _data3 = np.array(h5_file["pressure"][val_batch_idx]) + _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None]], axis=-1) + # permute from [t, x] -> [x, t] + _data = np.transpose(_data, (1, 0, 2)) + + self.data_output = torch.tensor(_data, dtype=torch.float) + del(_data, _data1, _data2, _data3) + + # for init/boundary conditions + self.init_data = self.data_output[:, 0] + self.bd_data_L = self.data_output[0] + self.bd_data_R = self.data_output[-1] + + self.tdim = self.data_output.size(1) + self.data_grid_t = self.data_grid_t[:self.tdim] + + XX, TT = torch.meshgrid( + [self.data_grid_x, self.data_grid_t], + indexing="ij", + ) + + self.data_input = torch.vstack([XX.ravel(), TT.ravel()]).T + + h5_file.close() + if 'tensor' in keys: + self.data_output = self.data_output.reshape(-1, 1) + else: + self.data_output = self.data_output.reshape(-1, 3) + + def get_initial_condition(self): + # return (self.data_grid_x[:, None], self.init_data) + return (self.data_input[::self.tdim, :], self.init_data) + + def get_boundary_condition(self): + # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) + return (self.data_input[:self.xdim, :], self.bd_data_L, self.bd_data_R) + + def get_test_data(self, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + n_t = len(self.data_grid_t) + + # start_idx = n_x * n_y * (n_t - n_last_time_steps) + test_input_x = self.data_input[:, 0].reshape((n_x, n_t)) + test_input_t = self.data_input[:, 1].reshape((n_x, n_t)) + test_output = self.data_output.reshape((n_x, n_t, n_components)) + + # extract last n time steps + test_input_x = test_input_x[:, -n_last_time_steps:] + test_input_t = test_input_t[:, -n_last_time_steps:] + test_output = test_output[:, -n_last_time_steps:, :] + + test_input = torch.vstack([test_input_x.ravel(), test_input_t.ravel()]).T + + # stack depending on number of output components + test_output_stacked = test_output[..., 0].ravel() + if n_components > 1: + for i in range(1, n_components): + test_output_stacked = torch.vstack( + [test_output_stacked, test_output[..., i].ravel()] + ) + else: + test_output_stacked = test_output_stacked.unsqueeze(1) + + test_output = test_output_stacked.T + + return test_input, test_output + + def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + return raveled_tensor.reshape((1, n_x, n_last_time_steps, n_components)) + + def generate_plot_input(self, time=1.0): + x_space = np.linspace(self.xL, self.xR, self.xdim) + # xx, yy = np.meshgrid(x_space, y_space) + + tt = np.ones_like(x_space) * time + val_input = np.vstack((x_space, tt)).T + return val_input + + def __len__(self): + return len(self.data_output) + + def __getitem__(self, idx): + return self.data_input[idx, :], self.data_output[idx] + +class PINNDataset2Dpde(Dataset): + def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=9, rdc_y=9): + """ + :param filename: filename that contains the dataset + :type filename: STR + """ + + # load data file + data_path = os.path.join(root_path, filename) + h5_file = h5py.File(data_path, "r") + + # build input data from individual dimensions + # dim x = [x] + self.data_grid_x = torch.tensor(h5_file["x-coordinate"], dtype=torch.float) + self.data_grid_x = self.data_grid_x[::rdc_x] + self.dx = self.data_grid_x[1] - self.data_grid_x[0] + self.xL = self.data_grid_x[0] - 0.5 * self.dx + self.xR = self.data_grid_x[-1] + 0.5 * self.dx + self.xdim = self.data_grid_x.size(0) + # dim y = [y] + self.data_grid_y = torch.tensor(h5_file["y-coordinate"], dtype=torch.float) + self.data_grid_y = self.data_grid_y[::rdc_y] + self.dy = self.data_grid_y[1] - self.data_grid_y[0] + self.yL = self.data_grid_y[0] - 0.5 * self.dy + self.yR = self.data_grid_y[-1] + 0.5 * self.dy + self.ydim = self.data_grid_y.size(0) + # # dim t = [t] + self.data_grid_t = torch.tensor(h5_file["t-coordinate"], dtype=torch.float) + + # main data + _data1 = np.array(h5_file["density"][val_batch_idx]) + _data2 = np.array(h5_file["Vx"][val_batch_idx]) + _data3 = np.array(h5_file["Vy"][val_batch_idx]) + _data4 = np.array(h5_file["pressure"][val_batch_idx]) + _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None], _data4[...,None]], + axis=-1) + # permute from [t, x, y, v] -> [x, y, t, v] + _data = np.transpose(_data, (1, 2, 0, 3)) + _data = _data[::rdc_x, ::rdc_y] + + self.data_output = torch.tensor(_data, dtype=torch.float) + del(_data, _data1, _data2, _data3, _data4) + + # for init/boundary conditions + self.init_data = self.data_output[..., 0, :] + self.bd_data_xL = self.data_output[0] + self.bd_data_xR = self.data_output[-1] + self.bd_data_yL = self.data_output[:,0] + self.bd_data_yR = self.data_output[:,-1] + + self.tdim = self.data_output.size(2) + self.data_grid_t = self.data_grid_t[:self.tdim] + + XX, YY, TT = torch.meshgrid( + [self.data_grid_x, self.data_grid_y, self.data_grid_t], + indexing="ij", + ) + + self.data_input = torch.vstack([XX.ravel(), YY.ravel(), TT.ravel()]).T + + h5_file.close() + self.data_output = self.data_output.reshape(-1, 4) + + def get_initial_condition(self): + # return (self.data_grid_x[:, None], self.init_data) + return (self.data_input[::self.tdim, :], self.init_data) + + def get_boundary_condition(self): + # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) + return (self.data_input[:self.xdim*self.ydim, :], + self.bd_data_xL, self.bd_data_xR, + self.bd_data_yL, self.bd_data_yR + ) + + def get_test_data(self, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + n_y = len(self.data_grid_y) + n_t = len(self.data_grid_t) + + # start_idx = n_x * n_y * (n_t - n_last_time_steps) + test_input_x = self.data_input[:, 0].reshape((n_x, n_y, n_t)) + test_input_y = self.data_input[:, 1].reshape((n_x, n_y, n_t)) + test_input_t = self.data_input[:, 4].reshape((n_x, n_y, n_t)) + test_output = self.data_output.reshape((n_x, n_y, n_t, n_components)) + + # extract last n time steps + test_input_x = test_input_x[:, :, -n_last_time_steps:] + test_input_y = test_input_y[:, :, -n_last_time_steps:] + test_input_t = test_input_t[:, :, -n_last_time_steps:] + test_output = test_output[:, :, -n_last_time_steps:, :] + + test_input = torch.vstack( + [test_input_x.ravel(), test_input_y.ravel(), test_input_t.ravel()] + ).T + + # stack depending on number of output components + test_output_stacked = test_output[..., 0].ravel() + if n_components > 1: + for i in range(1, n_components): + test_output_stacked = torch.vstack( + [test_output_stacked, test_output[..., i].ravel()] + ) + else: + test_output_stacked = test_output_stacked.unsqueeze(1) + + test_output = test_output_stacked.T + + return test_input, test_output + + def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + n_y = len(self.data_grid_y) + return raveled_tensor.reshape((1, n_x, n_y, n_last_time_steps, n_components)) + + def generate_plot_input(self, time=1.0): + return None + + def __len__(self): + return len(self.data_output) + + def __getitem__(self, idx): + return self.data_input[idx, :], self.data_output[idx].unsqueeze(1) + +class PINNDataset3Dpde(Dataset): + def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=2, rdc_y=2, rdc_z=2): + """ + :param filename: filename that contains the dataset + :type filename: STR + """ + + # load data file + data_path = os.path.join(root_path, filename) + h5_file = h5py.File(data_path, "r") + + # build input data from individual dimensions + # dim x = [x] + self.data_grid_x = torch.tensor(h5_file["x-coordinate"], dtype=torch.float) + self.data_grid_x = self.data_grid_x[::rdc_x] + self.dx = self.data_grid_x[1] - self.data_grid_x[0] + self.xL = self.data_grid_x[0] - 0.5 * self.dx + self.xR = self.data_grid_x[-1] + 0.5 * self.dx + self.xdim = self.data_grid_x.size(0) + # dim y = [y] + self.data_grid_y = torch.tensor(h5_file["y-coordinate"], dtype=torch.float) + self.data_grid_y = self.data_grid_y[::rdc_y] + self.dy = self.data_grid_y[1] - self.data_grid_y[0] + self.yL = self.data_grid_y[0] - 0.5 * self.dy + self.yR = self.data_grid_y[-1] + 0.5 * self.dy + self.ydim = self.data_grid_y.size(0) + # dim z = [z] + self.data_grid_z = torch.tensor(h5_file["z-coordinate"], dtype=torch.float) + self.data_grid_z = self.data_grid_z[::rdc_z] + self.dz = self.data_grid_z[1] - self.data_grid_z[0] + self.zL = self.data_grid_z[0] - 0.5 * self.dz + self.zR = self.data_grid_z[-1] + 0.5 * self.dz + self.zdim = self.data_grid_z.size(0) + # # dim t = [t] + self.data_grid_t = torch.tensor(h5_file["t-coordinate"], dtype=torch.float) + + # main data + _data1 = np.array(h5_file["density"][val_batch_idx]) + _data2 = np.array(h5_file["Vx"][val_batch_idx]) + _data3 = np.array(h5_file["Vy"][val_batch_idx]) + _data4 = np.array(h5_file["Vz"][val_batch_idx]) + _data5 = np.array(h5_file["pressure"][val_batch_idx]) + _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None], _data4[...,None], _data5[...,None]], + axis=-1) + # permute from [t, x, y, z, v] -> [x, y, z, t, v] + _data = np.transpose(_data, (1, 2, 3, 0, 4)) + _data = _data[::rdc_x, ::rdc_y, ::rdc_z] + + self.data_output = torch.tensor(_data, dtype=torch.float) + del(_data, _data1, _data2, _data3, _data4, _data5) + + # for init/boundary conditions + self.init_data = self.data_output[..., 0, :] + self.bd_data_xL = self.data_output[0] + self.bd_data_xR = self.data_output[-1] + self.bd_data_yL = self.data_output[:,0] + self.bd_data_yR = self.data_output[:,-1] + self.bd_data_zL = self.data_output[:,:,0] + self.bd_data_zR = self.data_output[:,:,-1] + + self.tdim = self.data_output.size(3) + self.data_grid_t = self.data_grid_t[:self.tdim] + + XX, YY, ZZ, TT = torch.meshgrid( + [self.data_grid_x, self.data_grid_y, self.data_grid_z, self.data_grid_t], + indexing="ij", + ) + + self.data_input = torch.vstack([XX.ravel(), YY.ravel(), ZZ.ravel(), TT.ravel()]).T + + h5_file.close() + self.data_output = self.data_output.reshape(-1, 5) + + def get_initial_condition(self): + # return (self.data_grid_x[:, None], self.init_data) + return (self.data_input[::self.tdim, :], self.init_data) + + def get_boundary_condition(self): + # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) + return (self.data_input[:self.xdim*self.ydim*self.zdim, :], + self.bd_data_xL, self.bd_data_xR, + self.bd_data_yL, self.bd_data_yR, + self.bd_data_zL, self.bd_data_zR, + ) + + def get_test_data(self, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + n_y = len(self.data_grid_y) + n_z = len(self.data_grid_z) + n_t = len(self.data_grid_t) + + # start_idx = n_x * n_y * (n_t - n_last_time_steps) + test_input_x = self.data_input[:, 0].reshape((n_x, n_y, n_z, n_t)) + test_input_y = self.data_input[:, 1].reshape((n_x, n_y, n_z, n_t)) + test_input_z = self.data_input[:, 2].reshape((n_x, n_y, n_z, n_t)) + test_input_t = self.data_input[:, 3].reshape((n_x, n_y, n_z, n_t)) + test_output = self.data_output.reshape((n_x, n_y, n_z, n_t, n_components)) + + # extract last n time steps + test_input_x = test_input_x[:, :, :, -n_last_time_steps:] + test_input_y = test_input_y[:, :, :, -n_last_time_steps:] + test_input_z = test_input_z[:, :, :, -n_last_time_steps:] + test_input_t = test_input_t[:, :, :, -n_last_time_steps:] + test_output = test_output[:, :, :, -n_last_time_steps:, :] + + test_input = torch.vstack( + [test_input_x.ravel(), test_input_y.ravel(), test_input_z.ravel(), test_input_t.ravel()] + ).T + + # stack depending on number of output components + test_output_stacked = test_output[..., 0].ravel() + if n_components > 1: + for i in range(1, n_components): + test_output_stacked = torch.vstack( + [test_output_stacked, test_output[..., i].ravel()] + ) + else: + test_output_stacked = test_output_stacked.unsqueeze(1) + + test_output = test_output_stacked.T + + return test_input, test_output + + def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): + n_x = len(self.data_grid_x) + n_y = len(self.data_grid_y) + n_z = len(self.data_grid_z) + return raveled_tensor.reshape((1, n_x, n_y, n_z, n_last_time_steps, n_components)) + + def generate_plot_input(self, time=1.0): + return None + + def __len__(self): + return len(self.data_output) + + def __getitem__(self, idx): + return self.data_input[idx, :], self.data_output[idx].unsqueeze(1) \ No newline at end of file diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index 669035d..f622015 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -240,6 +240,44 @@ def __init__(self, filename, ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) self.data[...,3] = _data # batch, x, t, ch + + if len(idx_cfd)==5: # 3D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + idx_cfd[4]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 5], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,3] = _data # batch, x, t, ch + # Vz + _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,4] = _data # batch, x, t, ch else: # scalar equations ## data dim = [t, x1, ..., xd, v] From f97a0124af14869f77125072689f7cb92f32ee6a Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Wed, 29 Jun 2022 09:04:37 +0200 Subject: [PATCH 010/137] Implemented changes on PINN training and NLE data generation scripts --- README.md | 13 +- pdebench/data_gen/data_gen_NLE/Data_Merge.py | 92 +++++++----- .../data_gen/data_gen_NLE/config/config.yaml | 6 +- .../data_gen/data_gen_NLE/save/CFD/.gitignore | 4 - .../data_gen_NLE/save/ReacDiff/.gitignore | 4 - .../data_gen_NLE/save/advection/.gitignore | 4 - .../data_gen_NLE/save/burgers/.gitignore | 4 - pdebench/models/pinn/train.py | 139 ++++++++++++++---- 8 files changed, 175 insertions(+), 91 deletions(-) delete mode 100644 pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore delete mode 100644 pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore delete mode 100644 pdebench/data_gen/data_gen_NLE/save/advection/.gitignore delete mode 100644 pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore diff --git a/README.md b/README.md index f293805..46db0ff 100644 --- a/README.md +++ b/README.md @@ -94,9 +94,20 @@ which is assumed to be performed in each directory. Examples for generating other PDEs are provided in `run_trainset.sh` in each PDE's directories. The config files for Hydra are stored in `config` directory in each PDE's directory. +#### Data Transformaion and Merge into HDF5 format +1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes Equations save data as a numpy array. +So, to read those data via our dataloaders, the data transformation/merge should be performed. +This can be done using `data_gen_NLE/Data_Merge.py` whose config file is located at: `data_gen/data_gen_NLE/config/config.yaml`. +After properly set parameters in the config file (type: name of PDEs, dim: number of spatial-dimension, bd: boundary condition), +the corresponding HDF5 file could be obtained as: +```bash +python3 Data_Merge.py +``` + + ## Configuration -You can sent the default values for data locations for this project by putting config vars like this in the `.env` file: +You can set the default values for data locations for this project by putting config vars like this in the `.env` file: ``` WORKING_DIR=~/Data/Working diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py index db34962..6cfe4b4 100644 --- a/pdebench/data_gen/data_gen_NLE/Data_Merge.py +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -143,6 +143,11 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +''' +Data_Merge.py +This is a script creating HDF5 from the generated data (numpy array) by our data generation scripts. +A more detailed explanation how to use this script is provided in the README. +''' import numpy as np @@ -153,14 +158,14 @@ import hydra from omegaconf import DictConfig -def _mergeRD(var, DataND): +def _mergeRD(var, DataND, savedir): _vars = ['2D', 'nu'] if var not in _vars: print(var+' is not defined!') return None idx = 0 - datas = glob.glob('save/ReacDiff/'+var+'*key*.npy') + datas = glob.glob(savedir+'/' + var + '*key*.npy') datas.sort() for data in datas: print(idx, data) @@ -174,7 +179,7 @@ def _mergeRD(var, DataND): return DataND[:idx] -def _merge(var, DataND, dim): +def _merge(var, DataND, dim, savedir): if dim == 1: _vars = ['D', 'P', 'Vx'] elif dim == 2: @@ -186,7 +191,7 @@ def _merge(var, DataND, dim): return None idx = 0 - datas = glob.glob('save/CFD/HD*'+var+'.npy') + datas = glob.glob(savedir+'/HD*' + var + '.npy') datas.sort() for data in datas: print(idx, data) @@ -201,51 +206,57 @@ def nan_check(data): data = np.abs(data).reshape([data.shape[0], data.shape[1],-1]).sum(axis=-1) return np.where(data[:,-2] < 1.e-6)[0], np.where(data[:,-2] > 1.e-6)[0] -def merge(type, dim, bd): +def merge(type, dim, bd, nbatch, savedir): if type=='CFD': - datas = glob.glob('save/CFD/HD*D.npy') + datas = glob.glob(savedir+'/HD*D.npy') datas.sort() test = np.load(datas[0]) - _nbatch, nt, nx, ny, nz = test.shape - nbatch = _nbatch * len(datas) - print('nb, nt, nx, ny, nz: ', nbatch, nt, nx, ny, nz) + __nbatch, nt, nx, ny, nz = test.shape + _nbatch = __nbatch * len(datas) + print('nb, nt, nx, ny, nz: ', _nbatch, nt, nx, ny, nz) + print('nbatch: {0}, _nbatch: {1}'.format(nbatch, _nbatch)) + assert nbatch <= _nbatch, 'nbatch should be equal or less than the number of generated samples' + assert 2*nbatch > _nbatch, '2*nbatch should be larger than the number of generated samples' if dim == 1: - DataND = np.zeros([2*nbatch, nt, nx]) + DataND = np.zeros([2*nbatch, nt, nx], dtype=np.float32) vars = ['D', 'P', 'Vx'] elif dim == 2: - DataND = np.zeros([2*nbatch, nt, nx, ny]) + DataND = np.zeros([2*nbatch, nt, nx, ny], dtype=np.float32) vars = ['D', 'P', 'Vx', 'Vy'] elif dim == 3: - DataND = np.zeros([2*nbatch, nt, nx, ny, nz]) + DataND = np.zeros([2*nbatch, nt, nx, ny, nz], dtype=np.float32) vars = ['D', 'P', 'Vx', 'Vy', 'Vz'] elif type=='ReacDiff': - datas = glob.glob('save/ReacDiff/nu*.npy') + datas = glob.glob(savedir+'/nu*.npy') datas.sort() test = np.load(datas[0]) - _nbatch, nx, ny = test.shape - nbatch = _nbatch * len(datas) - print('nb, nx, ny: ', nbatch, nx, ny) - DataND = np.zeros([nbatch, nx, ny]) + __nbatch, nx, ny = test.shape + _nbatch = __nbatch * len(datas) + print('nbatch: {0}, _nbatch: {1}'.format(nbatch, _nbatch)) + assert nbatch == _nbatch, 'nbatch should be equal or less than the number of generated samples' + print('nb, nx, ny: ', _nbatch, nx, ny) + DataND = np.zeros([nbatch, nx, ny], dtype=np.float32) vars = ['2D', 'nu'] for var in vars: if type=='CFD': - DataND = _merge(var, DataND, dim) + _DataND = _merge(var, DataND, dim, savedir) if var=='D': - idx_neg, idx_pos = nan_check(DataND) + idx_neg, idx_pos = nan_check(_DataND) print('idx_neg: {0}, idx_pos: {1}'.format(len(idx_neg), len(idx_pos))) if len(idx_pos) < nbatch: print('too many ill-defined data...') print('nbatch: {0}, idx_pos: {1}'.format(nbatch, len(idx_pos))) - DataND = DataND[idx_pos] - np.save('save/CFD/'+var+'.npy', DataND) + _DataND = _DataND[idx_pos] + _DataND = _DataND[:nbatch] + np.save(savedir+'/' + var + '.npy', _DataND) elif type == 'ReacDiff': - DataND = _mergeRD(var, DataND) - np.save('save/ReacDiff/'+var+'.npy', DataND) + DataND = _mergeRD(var, DataND, savedir) + np.save(savedir+'/' + var + '.npy', DataND) - datas = glob.glob('save/'+type+'/*npy') + datas = glob.glob(savedir+'/*npy') datas.sort() if type == 'CFD': @@ -258,13 +269,14 @@ def merge(type, dim, bd): tcrd = np.load(datas[-1]) del (datas[-1]) if type=='ReacDiff': - datas = glob.glob('save/' + type + '/nu*key*npy') + #datas = glob.glob('save/' + type + '/nu*key*npy') + datas = glob.glob(savedir+'/nu*key*npy') datas.sort() _beta = datas[0].split('/')[-1].split('_')[3] - flnm = 'save/ReacDiff/2D_DecayFlow_'+_beta+'_Train.hdf5' + flnm = savedir+'/2D_DecayFlow_' + _beta + '_Train.hdf5' with h5py.File(flnm, 'w') as f: - f.create_dataset('tensor', data = np.load('save/ReacDiff/2D.npy')[:, None, :, :]) - f.create_dataset('nu', data = np.load('save/ReacDiff/nu.npy')) + f.create_dataset('tensor', data=np.load(savedir+'/2D.npy')[:, None, :, :]) + f.create_dataset('nu', data=np.load(savedir+'/nu.npy')) f.create_dataset('x-coordinate', data=xcrd) f.create_dataset('y-coordinate', data=ycrd) f.attrs['beta'] = float(_beta[4:]) @@ -275,24 +287,24 @@ def merge(type, dim, bd): _zeta = datas[1].split('/')[-1].split('_')[5] _M = datas[1].split('/')[-1].split('_')[6] if dim == 1: - flnm = 'save/CFD/1D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + bd + '_Train.hdf5' + flnm = savedir+'/1D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + bd + '_Train.hdf5' elif dim == 2: - flnm = 'save/CFD/2D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + flnm = savedir+'/2D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' elif dim == 3: - flnm = 'save/CFD/3D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + flnm = savedir+'/3D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' print(flnm) del(DataND) with h5py.File(flnm, 'w') as f: - f.create_dataset('density', data = np.load('save/CFD/D.npy')) - f.create_dataset('pressure', data = np.load('save/CFD/P.npy')) - f.create_dataset('Vx', data = np.load('save/CFD/Vx.npy')) + f.create_dataset('density', data=np.load(savedir+'/D.npy')) + f.create_dataset('pressure', data=np.load(savedir+'/P.npy')) + f.create_dataset('Vx', data=np.load(savedir+'/Vx.npy')) if dim > 1: - f.create_dataset('Vy', data=np.load('save/CFD/Vy.npy')) + f.create_dataset('Vy', data=np.load(savedir+'/Vy.npy')) f.create_dataset('y-coordinate', data=ycrd) if dim == 3: - f.create_dataset('Vz', data=np.load('save/CFD/Vz.npy')) + f.create_dataset('Vz', data=np.load(savedir+'/Vz.npy')) f.create_dataset('z-coordinate', data=zcrd) f.create_dataset('x-coordinate', data = xcrd) f.create_dataset('t-coordinate', data = tcrd) @@ -306,8 +318,8 @@ def merge(type, dim, bd): f.attrs['M'] = M print('M: ', M) -def transform(type): - datas = glob.glob('save/'+type+'/*npy') +def transform(type, savedir): + datas = glob.glob(savedir+'/*npy') datas.sort() xcrd = np.load(datas[-1]) del (datas[-1]) @@ -343,11 +355,11 @@ def transform(type): def main(cfg: DictConfig) -> None: pde1ds = ['advection', 'burgers', 'ReacDiff'] if cfg.args.type in pde1ds and cfg.args.dim==1: - transform(type=cfg.args.type) + transform(type=cfg.args.type, savedir=cfg.args.savedir) else: bds = ['periodic', 'trans'] assert cfg.args.bd in bds, 'bd should be either periodic or trans' - merge(type=cfg.args.type, dim=cfg.args.dim, bd=cfg.args.bd) + merge(type=cfg.args.type, dim=cfg.args.dim, bd=cfg.args.bd, nbatch=cfg.args.nbatch, savedir=cfg.args.savedir) if __name__=='__main__': main() \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index d328852..f43418b 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -9,6 +9,8 @@ hydra: dir: . args: - type: 'advection' + type: 'advection' # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 - bd: 'periodic' \ No newline at end of file + bd: 'periodic' + nbatch: 1000 + savedir: './save/CFD/' \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore b/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/pdebench/data_gen/data_gen_NLE/save/CFD/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore b/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/pdebench/data_gen/data_gen_NLE/save/ReacDiff/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore b/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/pdebench/data_gen/data_gen_NLE/save/advection/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore b/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/pdebench/data_gen/data_gen_NLE/save/burgers/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index 6c8a758..2741d93 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -3,12 +3,13 @@ import numpy as np import pickle import matplotlib.pyplot as plt -import sys +import os, sys import torch from typing import Tuple sys.path.append(".") + from .utils import ( PINNDatasetRadialDambreak, PINNDatasetDiffReact, @@ -94,7 +95,6 @@ def transform_output(x, y): return model, dataset - def setup_diffusion_reaction(filename, seed): # TODO: read from dataset config file geom = dde.geometry.Rectangle((-1, -1), (1, 1)) @@ -184,6 +184,9 @@ def setup_swe_2d(filename, seed) -> Tuple[dde.Model, PINNDataset2D]: return model, dataset +def _boundary_r(x, on_boundary, xL, xR): + return (on_boundary and np.isclose(x[0], xL)) or (on_boundary and np.isclose(x[0], xR)) + def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", root_path='data', val_batch_idx=-1, @@ -197,6 +200,7 @@ def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", # TODO: read from dataset config file geom = dde.geometry.Interval(xL, xR) + boundary_r = lambda x, on_boundary: _boundary_r(x, on_boundary, xL, xR) if filename[0] == 'R': timedomain = dde.geometry.TimeDomain(0, 1.0) pde = lambda x, y : pde_diffusion_reaction_1d(x, y, aux_params[0], aux_params[1]) @@ -223,17 +227,21 @@ def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", ic_data_u = dde.icbc.PointSetBC(initial_input.cpu(), initial_u, component=0) # prepare boundary condition if if_periodic_bc: - bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) if filename.split('_')[1][0] == 'C': + bc_D = dde.icbc.PeriodicBC(geomtime, 0, boundary_r) + bc_V = dde.icbc.PeriodicBC(geomtime, 1, boundary_r) + bc_P = dde.icbc.PeriodicBC(geomtime, 2, boundary_r) + data = dde.data.TimePDE( geomtime, pde, - [ic_data_d, ic_data_v, ic_data_p, bc], + [ic_data_d, ic_data_v, ic_data_p, bc_D, bc_V, bc_P], num_domain=1000, num_boundary=1000, num_initial=5000, ) else: + bc = dde.icbc.PeriodicBC(geomtime, 0, boundary_r) data = dde.data.TimePDE( geomtime, pde, @@ -341,7 +349,11 @@ def setup_CFD3D(filename="3D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", return model, dataset -def run_training(scenario, epochs, learning_rate, model_update, flnm, seed): +def _run_training(scenario, epochs, learning_rate, model_update, flnm, + input_ch, output_ch, + root_path, val_batch_idx, if_periodic_bc, aux_params, + if_single_run, + seed): if scenario == "swe2d": model, dataset = setup_swe_2d(filename=flnm, seed=seed) n_components = 1 @@ -351,11 +363,42 @@ def run_training(scenario, epochs, learning_rate, model_update, flnm, seed): elif scenario == "diff-sorp": model, dataset = setup_diffusion_sorption(filename=flnm, seed=seed) n_components = 1 + elif scenario == "pde1D": + model, dataset = setup_pde1D(filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + if_periodic_bc=if_periodic_bc, + aux_params=aux_params) + if flnm.split('_')[1][0] == 'C': + n_components = 3 + else: + n_components = 1 + elif scenario == "CFD2D": + model, dataset = setup_CFD2D(filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + aux_params=aux_params) + n_components = 4 + elif scenario == "CFD3D": + model, dataset = setup_CFD3D(filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + aux_params=aux_params) + n_components = 5 else: raise NotImplementedError(f"PINN training not implemented for {scenario}") # filename - model_name = flnm + "_PINN" + if if_single_run: + model_name = flnm + "_PINN" + else: + model_name = flnm[:-5] + "_PINN" checker = dde.callbacks.ModelCheckpoint( f"{model_name}.pt", save_better_only=True, period=5000 @@ -381,32 +424,64 @@ def run_training(scenario, epochs, learning_rate, model_update, flnm, seed): test_gt, n_last_time_steps=20, n_components=n_components ) - errs = metric_func(test_pred, test_gt) - errors = [np.array(err.cpu()) for err in errs] - print(errors) - pickle.dump(errors, open(model_name + ".pickle", "wb")) - - # plot sample - plot_input = dataset.generate_plot_input(time=1.0) - dim = dataset.config["plot"]["dim"] - xdim = dataset.config["sim"]["xdim"] - if dim == 2: - ydim = dataset.config["sim"]["ydim"] - y_pred = model.predict(plot_input)[:, 0] - if dim == 1: - plt.figure() - plt.plot(y_pred) - elif dim == 2: - im_data = y_pred.reshape(xdim, ydim) - plt.figure() - plt.imshow(im_data) - - plt.savefig(f"{model_name}.png") - - # TODO: implement function to get specific timestep from dataset - # y_true = dataset[:][1][-xdim * ydim :] - - # print("L2 relative error:", dde.metrics.l2_relative_error(y_true.cpu(), y_pred)) + if if_single_run: + errs = metric_func(test_pred, test_gt) + errors = [np.array(err.cpu()) for err in errs] + print(errors) + pickle.dump(errors, open(model_name + ".pickle", "wb")) + + # plot sample + plot_input = dataset.generate_plot_input(time=1.0) + if scenario == "pde1D": + xdim = dataset.xdim + dim = 1 + else: + dim = dataset.config["plot"]["dim"] + xdim = dataset.config["sim"]["xdim"] + if dim == 2: + ydim = dataset.config["sim"]["ydim"] + + y_pred = model.predict(plot_input)[:, 0] + if dim == 1: + plt.figure() + plt.plot(y_pred) + elif dim == 2: + im_data = y_pred.reshape(xdim, ydim) + plt.figure() + plt.imshow(im_data) + + plt.savefig(f"{model_name}.png") + + # TODO: implement function to get specific timestep from dataset + # y_true = dataset[:][1][-xdim * ydim :] + else: + return test_pred, test_gt, model_name + +def run_training(scenario, epochs, learning_rate, model_update, flnm, + input_ch=1, output_ch=1, + root_path='../data/', val_num=10, if_periodic_bc=True, aux_params=[None], seed='0000'): + + if val_num == 1: # single job + _run_training(scenario, epochs, learning_rate, model_update, flnm, + input_ch, output_ch, + root_path, -val_num, if_periodic_bc, aux_params, + if_single_run=True, seed=seed) + else: + for val_batch_idx in range(-1, -val_num, -1): + test_pred, test_gt, model_name = _run_training(scenario, epochs, learning_rate, model_update, flnm, + input_ch, output_ch, + root_path, val_batch_idx, if_periodic_bc, aux_params, + if_single_run=False, seed=seed) + if val_batch_idx == -1: + pred, target = test_pred.unsqueeze(0), test_gt.unsqueeze(0) + else: + pred = torch.cat([pred, test_pred.unsqueeze(0)], 0) + target = torch.cat([target, test_gt.unsqueeze(0)], 0) + + errs = metric_func(test_pred, test_gt) + errors = [np.array(err.cpu()) for err in errs] + print(errors) + pickle.dump(errors, open(model_name + ".pickle", "wb")) if __name__ == "__main__": From 6594ec20daf983c8b15fc81d281543e3a68a2d5b Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Fri, 15 Jul 2022 13:42:43 +0200 Subject: [PATCH 011/137] Debugged Makoto's data generation utils code --- pdebench/data_gen/data_gen_NLE/utils.py | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index 94eec22..c2830f7 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1444,6 +1444,37 @@ def bc_HD(u, mode): u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) return u +def bc_HD_vis(u, if_periodic=True): # for viscosity + """ + for the moment, assuming periodic/copy boundary + seemingly, copy boundary does not work well... + """ + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + + if if_periodic: + u = u.at[:, 0:2, 0:2 + 2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT + else: # trans + u = u.at[:, 0:2, 0:2 + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT + + return u + def bc_HD_vis(u): # for viscosity _, Nx, Ny, Nz = u.shape Nx -= 2 From 535d9f147ded7b42bb265daafc06852b67bc3283 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Thu, 4 Aug 2022 13:55:36 +0200 Subject: [PATCH 012/137] Added time measurement for data generation of 2d diffusion-reaction, diffusion-sorption, and radial dam break scenarios, fixed bugs in NLE data generation utils script --- pdebench/data_gen/data_gen_NLE/utils.py | 31 +++++++++++++ pdebench/data_gen/gen_diff_react.py | 55 ++++++++++++----------- pdebench/data_gen/gen_diff_sorp.py | 49 ++++++++++---------- pdebench/data_gen/gen_radial_dam_break.py | 50 ++++++++++----------- 4 files changed, 108 insertions(+), 77 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index c2830f7..7da204f 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1443,6 +1443,37 @@ def bc_HD(u, mode): u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) return u + +def bc_HD_vis(u, if_periodic=True): # for viscosity + """ + for the moment, assuming periodic/copy boundary + seemingly, copy boundary does not work well... + """ + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + + if if_periodic: + u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT + else: # trans + u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, 4:2, 4:2, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT + + return u def bc_HD_vis(u, if_periodic=True): # for viscosity """ diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index 5696b85..67d4ac3 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -8,6 +8,8 @@ # e.g. HPC versus local laptop dotenv.load_dotenv() +import time + # or if the environment variables will be fixed for all executions, we can hard-code the environment variables like this: num_threads = "4" @@ -32,37 +34,38 @@ log = logging.getLogger(__name__) -def simulator(config, i, data_f): +def simulator(config, i): from src import sim_diff_react config.sim.seed = i log.info(f"Starting seed {i}") + start_time = time.time() sim_obj = sim_diff_react.Simulator(**config.sim) data_sample = sim_obj.generate_sample() + duration = time.time() - start_time + log.info(f"Seed {config.sim.seed} took {duration} to finish") seed_str = str(i).zfill(4) - ## Chunking for compression and data access - ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage - ## should be by batch and less than 1MB - ## lzf compression for float32 is kind of pointless though. - data_f.create_dataset( - f"{seed_str}/data", - data=data_sample, - dtype="float32", - compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/y", data=sim_obj.y, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" - ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + with h5py.File(utils.expand_path(config.output_path), "a") as data_f: + ## Chunking for compression and data access + ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage + ## should be by batch and less than 1MB + ## lzf compression for float32 is kind of pointless though. + data_f.create_dataset( + f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/y", data=sim_obj.y, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + ) + data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) @hydra.main(config_path="configs/", config_name="diff-react") @@ -94,12 +97,10 @@ def main(config: DictConfig): num_samples_init = 0 num_samples_final = 1000 - with h5py.File(utils.expand_path(config.output_path), "w") as data_f: - - pool = mp.Pool(mp.cpu_count()) - seed = np.arange(num_samples_init, num_samples_final) - seed = seed.tolist() - pool.starmap(simulator, zip(repeat(config), seed, data_f)) + pool = mp.Pool(mp.cpu_count()) + seed = np.arange(num_samples_init, num_samples_final) + seed = seed.tolist() + pool.starmap(simulator, zip(repeat(config), seed)) if config.upload: dataverse_upload( diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index 09a3759..016ca1b 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -8,6 +8,8 @@ # e.g. HPC versus local laptop dotenv.load_dotenv() +import time + # or if the environment variables will be fixed for all executions, we can hard-code the environment variables like this: num_threads = "4" @@ -33,34 +35,35 @@ log = logging.getLogger(__name__) -def simulator(config, i, data_f): +def simulator(config, i): from src import sim_diff_sorp config.sim.seed = i log.info(f"Starting seed {i}") + start_time = time.time() sim_obj = sim_diff_sorp.Simulator(**config.sim) data_sample = sim_obj.generate_sample() + duration = time.time() - start_time + log.info(f"Seed {config.sim.seed} took {duration} to finish") seed_str = str(i).zfill(4) - ## Chunking for compression and data access - ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage - ## should be by batch and less than 1MB - ## lzf compression for float32 is kind of pointless though. - data_f.create_dataset( - f"{seed_str}/data", - data=data_sample, - dtype="float32", - compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" - ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + with h5py.File(utils.expand_path(config.output_path), "a") as data_f: + ## Chunking for compression and data access + ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage + ## should be by batch and less than 1MB + ## lzf compression for float32 is kind of pointless though. + data_f.create_dataset( + f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + ) + data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) @hydra.main(config_path="configs/", config_name="diff-sorp") @@ -92,12 +95,10 @@ def main(config: DictConfig): num_samples_init = 0 num_samples_final = 10000 - with h5py.File(utils.expand_path(config.output_path), "w") as data_f: - - pool = mp.Pool(mp.cpu_count()) - seed = np.arange(num_samples_init, num_samples_final) - seed = seed.tolist() - pool.starmap(simulator, zip(repeat(config), seed, data_f)) + pool = mp.Pool(mp.cpu_count()) + seed = np.arange(num_samples_init, num_samples_final) + seed = seed.tolist() + pool.starmap(simulator, zip(repeat(config), seed)) if config.upload: dataverse_upload( diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index 8586e3e..e9bc4b0 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -31,11 +31,12 @@ from src import utils import numpy as np from uploader import dataverse_upload +import time log = logging.getLogger(__name__) -def simulator(base_config, i, data_f): +def simulator(base_config, i): from src.sim_radial_dam_break.py import RadialDamBreak2D @@ -54,15 +55,26 @@ def simulator(base_config, i, data_f): ydim=config.sim.ydim, ) + start_time = time.time() scenario.run(T=config.sim.T_end, tsteps=config.sim.n_time_steps, plot=False) - scenario.save_state_to_disk(data_f, i) - - seed_str = str(i).zfill(4) - - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) - + duration = time.time() - start_time + log.info(f"Seed {config.sim.seed} took {duration} to finish") + config.output_path = config.output_path + "_" + str(i).zfill(4) + ".h5" + scenario.save_state_to_disk(filepath=config.output_path) + with h5py.File(config.output_path, "r+") as f: + f.attrs["config"] = OmegaConf.to_yaml(config) + if config.upload: + dataverse_upload( + file_path=config.output_path, + dataverse_url=os.getenv("DATAVERSE_URL", "https://darus.uni-stuttgart.de"), + dataverse_token=os.getenv("DATAVERSE_API_TOKEN", ""), + dataverse_dir=config.name, + dataverse_id=os.getenv("DATAVERSE_ID", ""), + log=log, + ) + @hydra.main(config_path="configs/", config_name="radial_dam_break") def main(config: DictConfig): @@ -86,29 +98,15 @@ def main(config: DictConfig): output_path = os.path.join(work_path, config.data_dir, config.output_path) if not os.path.isdir(output_path): os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + '.h5' + config.output_path = os.path.join(output_path, config.output_path) num_samples_init = 0 num_samples_final = 1000 - with h5py.File(utils.expand_path(config.output_path), "w") as data_f: - - pool = mp.Pool(mp.cpu_count()) - seed = np.arange(num_samples_init, num_samples_final) - seed = seed.tolist() - pool.starmap(simulator, zip(repeat(config), seed, data_f)) - - if config.upload: - dataverse_upload( - file_path=config.output_path, - dataverse_url=os.getenv( - 'DATAVERSE_URL', 'https://darus.uni-stuttgart.de'), - dataverse_token=os.getenv( - 'DATAVERSE_API_TOKEN', ''), - dataverse_dir=config.name, - dataverse_id=os.getenv( - 'DATAVERSE_ID', ''), - log=log) + pool = mp.Pool(mp.cpu_count()) + seed = np.arange(num_samples_init, num_samples_final) + seed = seed.tolist() + pool.starmap(simulator, zip(repeat(config), seed)) return From e628e87e7701e4d5a020c1d0f64ee6ba33e4335b Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Tue, 9 Aug 2022 09:04:51 +0200 Subject: [PATCH 013/137] Modified requirements file, separated the packages required for data generation, modified the README.md file accordingly --- README.md | 13 ++----------- requirements.txt | 19 +++++-------------- requirements_datagen.txt | 29 +++++++++++++++++++++++++++++ setup.py | 8 ++------ 4 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 requirements_datagen.txt diff --git a/README.md b/README.md index 46db0ff..90d8f44 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,6 @@ PDEBench features a much wider range of PDEs than existing approaches including ## Dataset -Temporary url - -PDEBench Dataset -https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01 - -PDEBench Pre-Trained Models -https://darus.uni-stuttgart.de/privateurl.xhtml?token=cd862f8c-8e1b-49d2-b4da-b35f8df5ac85 - -Permanent url - PDEBench Dataset https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986 @@ -46,7 +36,8 @@ pip install --upgrade pip wheel pip install -r requirements.txt ``` -The required packages are listed in [requirements.txt](./requirements.txt) +The minimum required packages to train and run the baseline ML models are listed in [requirements.txt](./requirements.txt) +To run the data generation scripts, the complete package requirements are listed in [requirements_datagen.txt](./requirements_datagen.txt) For GPU support there are additional platform-specific instructions: diff --git a/requirements.txt b/requirements.txt index 0ff5383..a000e01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -numpy~=1.22.3 # Basic mathematical library for array operations scipy~=1.8.0 # Scientific library matplotlib~=3.5.2 # Plotting library h5py~=3.6.0 # Data storage format @@ -7,25 +6,17 @@ python-dotenv~=0.20.0 # Configuration library hydra-core~=1.2.0 # Configuration library hydra-joblib-launcher~=1.2.0 # parallel jobs locally hydra-submitit-launcher~=1.1.6 # parallel jobs on HPC cluster -pytorch-lightning~=1.6.3 # only for LightningDataModule omegaconf~=2.2.1 # Configuration library -clawpack~=5.6.1 # Finite Volume library deepxde~=1.1.3 # Physics-informed ML library -torch~=1.11.0 # PyTorch library for ML applications -jax~=0.3.13 # Numerical library -jaxlib~=0.3.10 # Numerical library part 2 (CPU) -dash~=2.2.0 # dashboard visualisation for phiflow -phiflow~=2.0.3 # PDE simulator +pyro-ppl # Probabilistic programming library for the inverse problems +tqdm~=4.64.0 + +# Optional, for downloading and uploading dataset using API pyDaRUS~=1.0.5 # DaRUS API package to upload and download dataset pyDataverse@ git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be -imageio~=2.19.2 -tqdm~=4.64.0 -einops~=0.4.1 -tensorboard~=2.9.0 -tensorboard-plugin-profile~=2.8.0 # Optional, if you want to use jupyter jupyter~=1.0.0 ipykernel~=6.9.0 jupyter-dash~=0.4.1 -nbstripout~=0.5.0 # trim outputs from jupyter notebooks \ No newline at end of file +nbstripout~=0.5.0 # trim outputs from jupyter notebooks \ No newline at end of file diff --git a/requirements_datagen.txt b/requirements_datagen.txt new file mode 100644 index 0000000..ee93da9 --- /dev/null +++ b/requirements_datagen.txt @@ -0,0 +1,29 @@ +scipy~=1.8.0 # Scientific library +matplotlib~=3.5.2 # Plotting library +h5py~=3.6.0 # Data storage format +pandas~=1.4.2 # Dataframe library +python-dotenv~=0.20.0 # Configuration library +hydra-core~=1.2.0 # Configuration library +hydra-joblib-launcher~=1.2.0 # parallel jobs locally +hydra-submitit-launcher~=1.1.6 # parallel jobs on HPC cluster +omegaconf~=2.2.1 # Configuration library +deepxde~=1.1.3 # Physics-informed ML library +pyro-ppl # Probabilistic programming library for the inverse problems +tqdm~=4.64.0 + +# Optional, for downloading and uploading dataset using API +pyDaRUS~=1.0.5 # DaRUS API package to upload and download dataset +pyDataverse@ git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be + +# For data generation +git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93 # includes fix for setuptools > 61.0.0 +dash~=2.2.0 # dashboard visualisation for phiflow +phiflow~=2.0.3 # PDE simulator +imageio~=2.19.2 +einops~=0.4.1 + +# Optional, if you want to use jupyter +jupyter~=1.0.0 +ipykernel~=6.9.0 +jupyter-dash~=0.4.1 +nbstripout~=0.5.0 # trim outputs from jupyter notebooks \ No newline at end of file diff --git a/setup.py b/setup.py index cb2e6db..b98e17f 100644 --- a/setup.py +++ b/setup.py @@ -6,18 +6,14 @@ setup( name='pdebench', version='0.0.1', - install_requires=['numpy', - 'scipy', + install_requires=['scipy', 'matplotlib', 'h5py', 'pandas', 'python-dotenv', 'hydra-core', 'omegaconf', - 'clawpack', - 'deepxde', - 'torch', - 'pyDaRUS' + 'deepxde' ], packages=['pdebench'], author='Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert', From 853e30b49ccde6dde3e6d1f82c795eb4bf03ff6e Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 10 Aug 2022 19:10:01 +0200 Subject: [PATCH 014/137] simplify download utility script --- README.md | 3 +-- pdebench/data_download/config/config.yaml | 5 ++++- pdebench/data_download/download.py | 27 +++++------------------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 90d8f44..7dc9e43 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,7 @@ There is an example in `example.env`. ## Data Download The data download codes are contained in [data_download](./pdebench/data_download): - `download.py` to download the data. -- `.env` is the environment data to store Dataverse URL and API token to download the generated data. Note that the filename should be strictly `.env` (i.e. remove the `example` from the filename) -- `config` directory contains the yaml files storing the configuration for the data downloader. The argument 'filename' should match the filename in the data repository. +- `config` directory contains the yaml files storing the configuration for the data downloader. Anyfiles in the dataset matching `args.filename` will be downloaded into `args.data_folder`. ## Baseline Models diff --git a/pdebench/data_download/config/config.yaml b/pdebench/data_download/config/config.yaml index a2897f3..7cd39eb 100644 --- a/pdebench/data_download/config/config.yaml +++ b/pdebench/data_download/config/config.yaml @@ -9,4 +9,7 @@ hydra: dir: . args: - filename: '1D_diff-sorp_NA_NA' \ No newline at end of file + filename: 'Advection_beta' + dataverse_url: 'https://darus.uni-stuttgart.de' + dataset_id: 'doi:10.18419/darus-2986' + data_folder: 'data' diff --git a/pdebench/data_download/download.py b/pdebench/data_download/download.py index 4622202..8d15ed6 100644 --- a/pdebench/data_download/download.py +++ b/pdebench/data_download/download.py @@ -1,14 +1,10 @@ import os -import dotenv - -dotenv.load_dotenv() import hydra from hydra.utils import get_original_cwd from omegaconf import DictConfig import logging -import glob from pyDataverse.api import NativeApi, DataAccessApi from pyDaRUS import Dataset from easyDataverse.core.downloader import download_files @@ -19,7 +15,7 @@ @hydra.main(config_path="config/", config_name="config") def main(config: DictConfig): """ - use config specifications to generate dataset + use config specifications to download files from a dataset """ # Imports should be nested inside @hydra.main to optimize tab completion @@ -29,21 +25,14 @@ def main(config: DictConfig): os.chdir(get_original_cwd()) - path_to_data = os.path.join(os.path.abspath("../data"), config.args.filename) - - file_list = sorted(glob.glob(path_to_data + "/*.h5")) - doi = "doi:10.18419/darus-2986" - # Extract dataset from the given DOI dataset = Dataset() - setattr(dataset, "p_id", doi) + setattr(dataset, "p_id", config.args.dataset_id) # Extract file list contained in the dataset - api = NativeApi(os.getenv("DATAVERSE_URL"), os.getenv("DATAVERSE_API_TOKEN")) - data_api = DataAccessApi( - os.getenv("DATAVERSE_URL"), os.getenv("DATAVERSE_API_TOKEN") - ) - dv_dataset = api.get_dataset(doi) + api = NativeApi(config.args.dataverse_url) + data_api = DataAccessApi(config.args.dataverse_url) + dv_dataset = api.get_dataset(config.args.dataset_id) files_list = dv_dataset.json()["data"]["latestVersion"]["files"] # Compile list of files that matches the desired filename @@ -53,12 +42,8 @@ def main(config: DictConfig): files.append(file) # Download the files - download_files(data_api, dataset, files, os.path.abspath("../data/")) + download_files(data_api, dataset, files, os.path.abspath(config.args.data_folder)) - return - - -import os if __name__ == "__main__": main() From e930c328b876492686866bd5996ce3fd6e7c406a Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 10 Aug 2022 19:59:55 +0200 Subject: [PATCH 015/137] delete obsolete example env file --- pdebench/data_download/example.env | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pdebench/data_download/example.env diff --git a/pdebench/data_download/example.env b/pdebench/data_download/example.env deleted file mode 100644 index 399e3dc..0000000 --- a/pdebench/data_download/example.env +++ /dev/null @@ -1,3 +0,0 @@ -DATAVERSE_URL=https://darus.uni-stuttgart.de -DATAVERSE_API_TOKEN=abc_0123 -DATAVERSE_ID=doi:10.18419/darus-2986 \ No newline at end of file From dfae55e1ea2804d3f01bf7f984a2af0fc3d42f70 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 10 Aug 2022 20:27:35 +0200 Subject: [PATCH 016/137] update requirements --- requirements.txt | 12 +----------- requirements_datagen.txt | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/requirements.txt b/requirements.txt index a000e01..8270581 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,14 +9,4 @@ hydra-submitit-launcher~=1.1.6 # parallel jobs on HPC cluster omegaconf~=2.2.1 # Configuration library deepxde~=1.1.3 # Physics-informed ML library pyro-ppl # Probabilistic programming library for the inverse problems -tqdm~=4.64.0 - -# Optional, for downloading and uploading dataset using API -pyDaRUS~=1.0.5 # DaRUS API package to upload and download dataset -pyDataverse@ git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be - -# Optional, if you want to use jupyter -jupyter~=1.0.0 -ipykernel~=6.9.0 -jupyter-dash~=0.4.1 -nbstripout~=0.5.0 # trim outputs from jupyter notebooks \ No newline at end of file +tqdm~=4.64.0 \ No newline at end of file diff --git a/requirements_datagen.txt b/requirements_datagen.txt index ee93da9..7514eab 100644 --- a/requirements_datagen.txt +++ b/requirements_datagen.txt @@ -13,10 +13,10 @@ tqdm~=4.64.0 # Optional, for downloading and uploading dataset using API pyDaRUS~=1.0.5 # DaRUS API package to upload and download dataset -pyDataverse@ git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be +pyDataverse@git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be # For data generation -git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93 # includes fix for setuptools > 61.0.0 +clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93 # includes fix for setuptools > 61.0.0 dash~=2.2.0 # dashboard visualisation for phiflow phiflow~=2.0.3 # PDE simulator imageio~=2.19.2 From a6c8919d561286b9eff7b9c776663124701ccd80 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 10 Aug 2022 20:28:47 +0200 Subject: [PATCH 017/137] add conda instructions to readme --- README.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7dc9e43..8a80094 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ DOIs ## Requirements +### Using pip ```bash python3 -m venv ./venv --prompt pde_benchmark --system-site-packages . ./venv/bin/activate @@ -36,23 +37,53 @@ pip install --upgrade pip wheel pip install -r requirements.txt ``` -The minimum required packages to train and run the baseline ML models are listed in [requirements.txt](./requirements.txt) +The minimum required packages to train and run the baseline ML models are listed in [requirements.txt](./requirements.txt). + To run the data generation scripts, the complete package requirements are listed in [requirements_datagen.txt](./requirements_datagen.txt) For GPU support there are additional platform-specific instructions: -For pytorch, +For PyTorch, [see here](https://pytorch.org/get-started/locally/). -```bash -pip3 install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu113 +For JAX, which is approximately 6 times faster for simulations than PyTorch in our tests, [see here](https://github.com/google/jax#installation) + +### Using conda: + +If you like you can also install dependencies using anaconda. We suggest using [miniforge](https://github.com/conda-forge/miniforge) (and possibly mamba) as distribution. Otherwise you may have to __enable the conda-forge__ channel for the following commands. + +Starting from a fresh environment: + +``` +conda create -n myenv python=3.9 +conda activate myenv ``` -For jax, which is approximately 6 times faster for simulations than pytorch in our tests, +Install dependencies for model training: +``` +conda install deepxde hydra-core h5py +``` -```bash -pip install "jax[cuda11_cudnn82]>=0.3.0" -f https://storage.googleapis.com/jax-releases/jax_releases.html +[Install PyTorch](https://pytorch.org/get-started/locally/) according to your hardware requirements: + +E.g. +``` +conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch +``` + +Optional dependencies for data generation: +``` +conda install clawpack jax jaxlib python-dotenv ``` +Optional dependencies for data downloading: +``` +pip install pyDarus~=1.0.5 +``` + +## Configuring DeepXDE +In our tests we used PyTorch as backend for DeepXDE. Please [follow the documentation](https://deepxde.readthedocs.io/en/latest/user/installation.html#working-with-different-backends) to enable this. + + ## Data Generation The data generation codes are contained in [data_gen](./pdebench/data_gen): - `gen_diff_react.py` to generate the 2D diffusion-reaction data. From 0f9121da122f0aaaf46c3486b9751971364ef9d9 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Thu, 11 Aug 2022 10:01:46 +0200 Subject: [PATCH 018/137] Fixed error in the result plotting script --- pdebench/models/analyse_result_forward.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 696b5a0..091ea3a 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -191,8 +191,6 @@ def main(): index1.append(title[0]) index2.append(title[1] + title[2]) index3.append(title[3]) - if index3[-1][0] == 'U': - index3[-1] = index3[-1][:4] indexes = [index1, index2, index3] # create dataframe From fb4263f381158cbc0fc49ba53916378637e7c9ae Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Thu, 11 Aug 2022 11:29:01 +0200 Subject: [PATCH 019/137] Increased fontsize for plotting for better visibility --- pdebench/models/analyse_result_forward.py | 10 +++--- pdebench/models/analyse_result_inverse.py | 10 +++--- pdebench/models/metrics.py | 44 ++++++++++++++--------- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 091ea3a..8d5e575 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -221,12 +221,12 @@ def main(): ax.bar(pos, data[data.index.isin([models[i]],level=2)]['MSE'], width) ax.set_xticks(x) - ax.set_xticklabels(pdes,fontsize=16) - ax.tick_params(axis='y',labelsize=16) + ax.set_xticklabels(pdes,fontsize=30) + ax.tick_params(axis='y',labelsize=30) ax.set_yscale('log') - ax.set_xlabel('PDEs',fontsize=16) - ax.set_ylabel('MSE',fontsize=16) - fig.legend(models,loc=8,ncol=num_models,fontsize=16) + ax.set_xlabel('PDEs',fontsize=30) + ax.set_ylabel('MSE',fontsize=30) + fig.legend(models,loc=8,ncol=num_models,fontsize=20) plt.tight_layout(rect=[0,0.1,1,1]) plt.savefig('Results.pdf') diff --git a/pdebench/models/analyse_result_inverse.py b/pdebench/models/analyse_result_inverse.py index 5420232..9111613 100644 --- a/pdebench/models/analyse_result_inverse.py +++ b/pdebench/models/analyse_result_inverse.py @@ -168,12 +168,12 @@ def main(): print(width, pos) ax.set_xticks(x) - ax.set_xticklabels(pdes,rotation=45,fontsize=16) - ax.tick_params(axis='y',labelsize=16) + ax.set_xticklabels(pdes,rotation=45,fontsize=30) + ax.tick_params(axis='y',labelsize=30) ax.set_yscale('log') - ax.set_xlabel('PDEs',fontsize=16) - ax.set_ylabel('MSE',fontsize=16) - fig.legend(models,loc=1,ncol=num_models,fontsize=16) + ax.set_xlabel('PDEs',fontsize=30) + ax.set_ylabel('MSE',fontsize=30) + fig.legend(models,loc=1,ncol=num_models,fontsize=20) plt.tight_layout() plt.savefig('ResultsInverse.pdf') diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 1c4b9fe..1629a2a 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -397,9 +397,12 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) - ax.set_title("Prediction", fontsize=20) - ax.set_ylabel("$x$", fontsize=20) - ax.set_xlabel("$t$", fontsize=20) + cbar.ax.tick_params(labelsize=30) + ax.set_title("Prediction", fontsize=30) + ax.tick_params(axis='x',labelsize=30) + ax.tick_params(axis='y',labelsize=30) + ax.set_ylabel("$x$", fontsize=30) + ax.set_xlabel("$t$", fontsize=30) plt.tight_layout() filename = model_name + '_pred.pdf' plt.savefig(filename) @@ -411,9 +414,12 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) - ax.set_title("Data", fontsize=20) - ax.set_ylabel("$x$", fontsize=20) - ax.set_xlabel("$t$", fontsize=20) + cbar.ax.tick_params(labelsize=30) + ax.set_title("Data", fontsize=30) + ax.tick_params(axis='x',labelsize=30) + ax.tick_params(axis='y',labelsize=30) + ax.set_ylabel("$x$", fontsize=30) + ax.set_xlabel("$t$", fontsize=30) plt.tight_layout() filename = model_name + '_data.pdf' plt.savefig(filename) @@ -427,9 +433,12 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) - ax.set_title("Prediction", fontsize=20) - ax.set_ylabel("$y$", fontsize=20) - ax.set_xlabel("$x$", fontsize=20) + cbar.ax.tick_params(labelsize=30) + ax.set_title("Prediction", fontsize=30) + ax.tick_params(axis='x',labelsize=30) + ax.tick_params(axis='y',labelsize=30) + ax.set_ylabel("$y$", fontsize=30) + ax.set_xlabel("$x$", fontsize=30) plt.tight_layout() filename = model_name + '_pred.pdf' plt.savefig(filename) @@ -441,19 +450,22 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) - ax.set_title("Data", fontsize=20) - ax.set_ylabel("$y$", fontsize=20) - ax.set_xlabel("$x$", fontsize=20) + cbar.ax.tick_params(labelsize=30) + ax.set_title("Data", fontsize=30) + ax.tick_params(axis='x',labelsize=30) + ax.tick_params(axis='y',labelsize=30) + ax.set_ylabel("$y$", fontsize=30) + ax.set_xlabel("$x$", fontsize=30) plt.tight_layout() filename = model_name + '_data.pdf' plt.savefig(filename) - # plt.figure(figsize=(5,5)) + # plt.figure(figsize=(8,8)) # plt.semilogy(torch.arange(initial_step,yy.shape[-2]), # val_l2_time[initial_step:].detach().cpu()) - # plt.xlabel('$t$', fontsize=20) - # plt.ylabel('$MSE$', fontsize=20) - # plt.title('MSE vs unrolled time steps', fontsize=20) + # plt.xlabel('$t$', fontsize=30) + # plt.ylabel('$MSE$', fontsize=30) + # plt.title('MSE vs unrolled time steps', fontsize=30) # plt.tight_layout() # filename = model_name + '_mse_time.pdf' # plt.savefig(filename) From e3e86e6348ae1597162c3f12c013ae4b24f3f280 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Tue, 16 Aug 2022 16:54:03 +0200 Subject: [PATCH 020/137] debug for viscosity part by MT@16082022 --- .../CompressibleFluid/CFD_Hydra.py | 193 ++++++++++++----- .../CompressibleFluid/CFD_multi_Hydra.py | 194 +++++++++++++----- 2 files changed, 287 insertions(+), 100 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py index 5888b89..3ac7d98 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -254,6 +254,8 @@ def _update(carry): Q = update(Q, Q_tmp, dt) # update via viscosity + #d_min = jnp.min(Q[0]) + #dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity dt_vis = Courant_vis_HD(dx, dy, dz, cfg.args.eta, cfg.args.zeta) * cfg.args.CFL dt_vis = jnp.min(jnp.array([dt_vis, dt])) t_vis = 0. @@ -323,61 +325,152 @@ def update(Q, Q_tmp, dt): @jit def update_vis(carry): + def _update_vis_x(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdx = dt * dx_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-1, 2:-2, 2:-2] + D0[1:-2, 2:-2, 2:-2]) + + fMx = (eta + visc) * Dm * dx_inv * (\ + Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) + fMy = eta * Dm * dx_inv * (\ + Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) + fMz = eta * Dm * dx_inv * (\ + Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dx_inv * (\ + Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2)\ + + 0.5 * eta * Dm * dx_inv * (\ + (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2)\ + + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdx * (fMx[1:, :, :] - fMx[:-1, :, :]) + My += dtdx * (fMy[1:, :, :] - fMy[:-1, :, :]) + Mz += dtdx * (fMz[1:, :, :] - fMz[:-1, :, :]) + E0 += dtdx * (fE[1:, :, :] - fE[:-1, :, :]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + + def _update_vis_y(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdy = dt * dy_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-2, 2:-1, 2:-2] + D0[2:-2, 1:-2, 2:-2]) + + fMx = eta * Dm * dy_inv * (\ + Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) + fMy = (eta + visc) * Dm * dy_inv * (\ + Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) + fMz = eta * Dm * dy_inv * (\ + Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dy_inv * (\ + Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2)\ + + 0.5 * eta * Dm * dy_inv * ( \ + (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) \ + + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdy * (fMx[:, 1:, :] - fMx[:, :-1, :]) + My += dtdy * (fMy[:, 1:, :] - fMy[:, :-1, :]) + Mz += dtdy * (fMz[:, 1:, :] - fMz[:, :-1, :]) + E0 += dtdy * (fE[:, 1:, :] - fE[:, :-1, :]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + + def _update_vis_z(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdz = dt * dz_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-2, 2:-2, 2:-1] + D0[2:-2, 2:-2, 1:-2]) + + fMx = eta * Dm * dz_inv * (\ + Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) + fMy = eta * Dm * dz_inv * (\ + Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) + fMz = (eta + visc) * Dm * dz_inv * (\ + Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) + fE = 0.5 * (eta + visc) * Dm * dz_inv * (\ + Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2)\ + + 0.5 * eta * Dm * dz_inv * ( \ + (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) \ + + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdz * (fMx[:, :, 1:] - fMx[:, :, :-1]) + My += dtdz * (fMy[:, :, 1:] - fMy[:, :, :-1]) + Mz += dtdz * (fMz[:, :, 1:] - fMz[:, :, :-1]) + E0 += dtdz * (fE[:, :, 1:] - fE[:, :, :-1]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + Q, dt, dt_vis, t_vis = carry Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u - Q = bc_HD_vis(Q) # index 2 for _U is equivalent with index 0 for u dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) - t_vis += dt_ev - # calculate conservative variables - D0 = Q[0] - Mx = Q[1] * D0 - My = Q[2] * D0 - Mz = Q[3] * D0 - E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) - - D0 = D0[2:-2, 2:-2, 2:-2] - Mx = Mx[2:-2, 2:-2, 2:-2] - My = My[2:-2, 2:-2, 2:-2] - Mz = Mz[2:-2, 2:-2, 2:-2] - E0 = E0[2:-2, 2:-2, 2:-2] - # update conservative variables - dtdxdx, dtdxdy, dtdxdz = dt_ev * dx_inv * dx_inv, dt_ev * dx_inv * dy_inv, dt_ev * dx_inv * dz_inv - dtdydy, dtdydz = dt_ev * dy_inv * dy_inv, dt_ev * dy_inv * dz_inv - dtdzdz = dt_ev * dz_inv * dz_inv - - Mx += (visc + cfg.args.eta) * D0 * dtdxdx * ( - Q[1, 3:-1, 2:-2, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 2:-2]) \ - + cfg.args.eta * D0 * dtdydy * ( - Q[1, 2:-2, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 1:-3, 2:-2]) \ - + cfg.args.eta * D0 * dtdzdz * ( - Q[1, 2:-2, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdxdy * (Q[2, 3:-1, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 1:-3, 2:-2]) \ - + dtdxdz * (Q[3, 3:-1, 2:-2, 3:-1] - 2. * Q[3, 1:-3, 2:-2, 1:-3] + Q[3, 1:-3, 2:-2, 1:-3])) - - My += cfg.args.eta * D0 * dtdxdx * ( - Q[2, 3:-1, 2:-2, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 2:-2, 2:-2]) \ - + (visc + cfg.args.eta) * D0 * dtdydy * ( - Q[2, 2:-2, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 2:-2]) \ - + cfg.args.eta * D0 * dtdzdz * ( - Q[2, 2:-2, 2:-2, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdydz * (Q[3, 2:-2, 3:-1, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 1:-3])\ - + dtdxdy * (Q[1, 3:-1, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 1:-3, 2:-2])) - - Mz += cfg.args.eta * D0 * dtdxdx * ( - Q[3, 3:-1, 2:-2, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 1:-3, 2:-2, 2:-2]) \ - + cfg.args.eta * D0 * dtdydy * ( - Q[3, 2:-2, 3:-1, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 2:-2]) \ - + (visc + cfg.args.eta) * D0 * dtdzdz * ( - Q[3, 2:-2, 2:-2, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdydz * (Q[2, 2:-2, 3:-1, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 1:-3])\ - + dtdxdz * (Q[1, 3:-1, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 1:-3])) + carry = Q, dt_ev + # directional split + carry = _update_vis_x(carry) # x + carry = _update_vis_y(carry) # y + Q, d_ev = _update_vis_z(carry) # z - # reverse primitive variables - Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx - Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy - Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2) / D0)) # p + t_vis += dt_ev return Q, dt, dt_vis, t_vis diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py index 69c22a7..3d1fa4b 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -306,6 +306,9 @@ def _update(carry): Q = update(Q, Q_tmp, dt) # update via viscosity + #d_min = jnp.min(Q[0]) + #dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity + dt_vis = Courant_vis_HD(dx, dy, dz, eta, zeta) * cfg.args.CFL dt_vis = jnp.min(jnp.array([dt_vis, dt])) t_vis = 0. @@ -375,61 +378,152 @@ def update(Q, Q_tmp, dt): @jit def update_vis(carry): + def _update_vis_x(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdx = dt * dx_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-1, 2:-2, 2:-2] + D0[1:-2, 2:-2, 2:-2]) + + fMx = (eta + visc) * Dm * dx_inv * (\ + Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) + fMy = eta * Dm * dx_inv * (\ + Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) + fMz = eta * Dm * dx_inv * (\ + Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dx_inv * (\ + Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2)\ + + 0.5 * eta * Dm * dx_inv * (\ + (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2)\ + + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdx * (fMx[1:, :, :] - fMx[:-1, :, :]) + My += dtdx * (fMy[1:, :, :] - fMy[:-1, :, :]) + Mz += dtdx * (fMz[1:, :, :] - fMz[:-1, :, :]) + E0 += dtdx * (fE[1:, :, :] - fE[:-1, :, :]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + + def _update_vis_y(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdy = dt * dy_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-2, 2:-1, 2:-2] + D0[2:-2, 1:-2, 2:-2]) + + fMx = eta * Dm * dy_inv * (\ + Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) + fMy = (eta + visc) * Dm * dy_inv * (\ + Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) + fMz = eta * Dm * dy_inv * (\ + Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dy_inv * (\ + Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2)\ + + 0.5 * eta * Dm * dy_inv * ( \ + (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) \ + + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdy * (fMx[:, 1:, :] - fMx[:, :-1, :]) + My += dtdy * (fMy[:, 1:, :] - fMy[:, :-1, :]) + Mz += dtdy * (fMz[:, 1:, :] - fMz[:, :-1, :]) + E0 += dtdy * (fE[:, 1:, :] - fE[:, :-1, :]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + + def _update_vis_z(carry): + Q, dt = carry + # calculate conservative variables + D0 = Q[0] + Mx = Q[1] * D0 + My = Q[2] * D0 + Mz = Q[3] * D0 + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) + + # calculate flux + dtdz = dt * dz_inv + # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) + Dm = 0.5 * (D0[2:-2, 2:-2, 2:-1] + D0[2:-2, 2:-2, 1:-2]) + + fMx = eta * Dm * dz_inv * (\ + Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) + fMy = eta * Dm * dz_inv * (\ + Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) + fMz = (eta + visc) * Dm * dz_inv * (\ + Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) + fE = 0.5 * (eta + visc) * Dm * dz_inv * (\ + Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2)\ + + 0.5 * eta * Dm * dz_inv * ( \ + (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) \ + + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2)) + + D0 = D0[2:-2, 2:-2, 2:-2] + Mx = Mx[2:-2, 2:-2, 2:-2] + My = My[2:-2, 2:-2, 2:-2] + Mz = Mz[2:-2, 2:-2, 2:-2] + E0 = E0[2:-2, 2:-2, 2:-2] + + Mx += dtdz * (fMx[:, :, 1:] - fMx[:, :, :-1]) + My += dtdz * (fMy[:, :, 1:] - fMy[:, :, :-1]) + Mz += dtdz * (fMz[:, :, 1:] - fMz[:, :, :-1]) + E0 += dtdz * (fE[:, :, 1:] - fE[:, :, :-1]) + + # reverse primitive variables + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + + return Q, dt + Q, dt, dt_vis, t_vis = carry Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u - Q = bc_HD_vis(Q) # index 2 for _U is equivalent with index 0 for u dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) - t_vis += dt_ev - # calculate conservative variables - D0 = Q[0] - Mx = Q[1] * D0 - My = Q[2] * D0 - Mz = Q[3] * D0 - E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) - D0 = D0[2:-2, 2:-2, 2:-2] - Mx = Mx[2:-2, 2:-2, 2:-2] - My = My[2:-2, 2:-2, 2:-2] - Mz = Mz[2:-2, 2:-2, 2:-2] - E0 = E0[2:-2, 2:-2, 2:-2] - - # update conservative variables - dtdxdx, dtdxdy, dtdxdz = dt_ev * dx_inv * dx_inv, dt_ev * dx_inv * dy_inv, dt_ev * dx_inv * dz_inv - dtdydy, dtdydz = dt_ev * dy_inv * dy_inv, dt_ev * dy_inv * dz_inv - dtdzdz = dt_ev * dz_inv * dz_inv - - Mx += (visc + eta) * D0 * dtdxdx * ( - Q[1, 3:-1, 2:-2, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 2:-2]) \ - + eta * D0 * dtdydy * ( - Q[1, 2:-2, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 1:-3, 2:-2]) \ - + eta * D0 * dtdzdz * ( - Q[1, 2:-2, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdxdy * (Q[2, 3:-1, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 1:-3, 2:-2])\ - + dtdxdz * (Q[3, 3:-1, 2:-2, 3:-1] - 2. * Q[3, 1:-3, 2:-2, 1:-3] + Q[3, 1:-3, 2:-2, 1:-3])) - - My += eta * D0 * dtdxdx * ( - Q[2, 3:-1, 2:-2, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 1:-3, 2:-2, 2:-2]) \ - + (visc + eta) * D0 * dtdydy * ( - Q[2, 2:-2, 3:-1, 2:-2] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 2:-2]) \ - + eta * D0 * dtdzdz * ( - Q[2, 2:-2, 2:-2, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdydz * (Q[3, 2:-2, 3:-1, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 1:-3])\ - + dtdxdy * (Q[1, 3:-1, 3:-1, 2:-2] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 1:-3, 2:-2])) - - Mz += eta * D0 * dtdxdx * ( - Q[3, 3:-1, 2:-2, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 1:-3, 2:-2, 2:-2]) \ - + eta * D0 * dtdydy * ( - Q[3, 2:-2, 3:-1, 2:-2] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 1:-3, 2:-2]) \ - + (visc + eta) * D0 * dtdzdz * ( - Q[3, 2:-2, 2:-2, 3:-1] - 2. * Q[3, 2:-2, 2:-2, 2:-2] + Q[3, 2:-2, 2:-2, 1:-3]) \ - + visc * D0 * 0.5 * (dtdydz * (Q[2, 2:-2, 3:-1, 3:-1] - 2. * Q[2, 2:-2, 2:-2, 2:-2] + Q[2, 2:-2, 1:-3, 1:-3])\ - + dtdxdz * (Q[1, 3:-1, 2:-2, 3:-1] - 2. * Q[1, 2:-2, 2:-2, 2:-2] + Q[1, 1:-3, 2:-2, 1:-3])) + carry = Q, dt_ev + # directional split + carry = _update_vis_x(carry) # x + carry = _update_vis_y(carry) # y + Q, d_ev = _update_vis_z(carry) # z - # reverse primitive variables - Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx - Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy - Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2) / D0)) # p + t_vis += dt_ev return Q, dt, dt_vis, t_vis From 488b3f7de1ad1bdffa0010e46c956782b7140b5f Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Fri, 19 Aug 2022 16:51:43 +0200 Subject: [PATCH 021/137] Modified citation in the readme file --- README.md | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/README.md b/README.md index 8a80094..cb41b5b 100644 --- a/README.md +++ b/README.md @@ -225,17 +225,6 @@ Additionally, the pretrained models are also available to be downloaded [here](h ## Citations ``` -@online{PDEBenchDataset, - author = {Makoto Takamoto and Timothy Pradita and Raphael Leiteritz and Dan MacKinlay and Francesco Alesiani and Dirk Pflüger and Mathias Niepert}, - title = {{PDEBench}: A Diverse and Comprehensive Benchmark for Scientific Machine Learning}, - year = 2022, - doi = {doi:10.18419/darus-2986}, - urldoi = {http://dx.doi.org/10.18419/darus-2986}, - url = {https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01}, - urldate = {2022-06-06} -} - - @data{darus-2986_2022, author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, publisher = {DaRUS}, @@ -244,46 +233,6 @@ year = {2022}, doi = {10.18419/darus-2986}, url = {https://doi.org/10.18419/darus-2986} } - -@data{darus-2987_2022, -author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, -publisher = {DaRUS}, -title = {{PDEBench Pretrained Models}}, -year = {2022}, -doi = {10.18419/darus-2987}, -url = {https://doi.org/10.18419/darus-2987} -} - - -@misc{li2020fourier, - title={Fourier Neural Operator for Parametric Partial Differential Equations}, - author={Zongyi Li and Nikola Kovachki and Kamyar Azizzadenesheli and Burigede Liu and Kaushik Bhattacharya and Andrew Stuart and Anima Anandkumar}, - year={2020}, - eprint={2010.08895}, - archivePrefix={arXiv}, - primaryClass={cs.LG} -} - -@article{buda2019association, - title={Association of genomic subtypes of lower-grade gliomas with shape features automatically extracted by a deep learning algorithm}, - author={Buda, Mateusz and Saha, Ashirbani and Mazurowski, Maciej A}, - journal={Computers in Biology and Medicine}, - volume={109}, - year={2019}, - publisher={Elsevier}, - doi={10.1016/j.compbiomed.2019.05.002} -} - -@article{raissi2019pinn, -title = {Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear partial differential equations}, -journal = {Journal of Computational Physics}, -volume = {378}, -pages = {686-707}, -year = {2019}, -issn = {0021-9991}, -doi = {https://doi.org/10.1016/j.jcp.2018.10.045}, -author = {M. Raissi and P. Perdikaris and G.E. Karniadakis} -} ``` ## Code contributors From fe8f8cddeb35a19a4c2a645cec6ac6f39f79a86f Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 22 Aug 2022 10:52:03 +0200 Subject: [PATCH 022/137] debugged utils.py initial condition parts by MT@22082022 --- pdebench/data_gen/data_gen_NLE/utils.py | 3057 +++++++++++------------ 1 file changed, 1437 insertions(+), 1620 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index 7da204f..db8ffbe 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1,1620 +1,1437 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" - - - File: utils.py - Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) - -NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. - - THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. - - PROPRIETARY INFORMATION --- - -SOFTWARE LICENSE AGREEMENT - -ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY - -BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS -LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR -DOWNLOAD THE SOFTWARE. - -This is a license agreement ("Agreement") between your academic institution -or non-profit organization or self (called "Licensee" or "You" in this -Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this -Agreement). All rights not specifically granted to you in this Agreement -are reserved for Licensor. - -RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive -ownership of any copy of the Software (as defined below) licensed under this -Agreement and hereby grants to Licensee a personal, non-exclusive, -non-transferable license to use the Software for noncommercial research -purposes, without the right to sublicense, pursuant to the terms and -conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF -LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this -Agreement, the term "Software" means (i) the actual copy of all or any -portion of code for program routines made accessible to Licensee by Licensor -pursuant to this Agreement, inclusive of backups, updates, and/or merged -copies permitted hereunder or subsequently supplied by Licensor, including -all or any file structures, programming instructions, user interfaces and -screen formats and sequences as well as any and all documentation and -instructions related to it, and (ii) all or any derivatives and/or -modifications created or made by You to any of the items specified in (i). - -CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is -proprietary to Licensor, and as such, Licensee agrees to receive all such -materials and to use the Software only in accordance with the terms of this -Agreement. Licensee agrees to use reasonable effort to protect the Software -from unauthorized use, reproduction, distribution, or publication. All -publication materials mentioning features or use of this software must -explicitly include an acknowledgement the software was developed by NEC -Laboratories Europe GmbH. - -COPYRIGHT: The Software is owned by Licensor. - -PERMITTED USES: The Software may be used for your own noncommercial -internal research purposes. You understand and agree that Licensor is not -obligated to implement any suggestions and/or feedback you might provide -regarding the Software, but to the extent Licensor does so, you are not -entitled to any compensation related thereto. - -DERIVATIVES: You may create derivatives of or make modifications to the -Software, however, You agree that all and any such derivatives and -modifications will be owned by Licensor and become a part of the Software -licensed to You under this Agreement. You may only use such derivatives and -modifications for your own noncommercial internal research purposes, and you -may not otherwise use, distribute or copy such derivatives and modifications -in violation of this Agreement. - -BACKUPS: If Licensee is an organization, it may make that number of copies -of the Software necessary for internal noncommercial use at a single site -within its organization provided that all information appearing in or on the -original labels, including the copyright and trademark notices are copied -onto the labels of the copies. - -USES NOT PERMITTED: You may not distribute, copy or use the Software except -as explicitly permitted herein. Licensee has not been granted any trademark -license as part of this Agreement. Neither the name of NEC Laboratories -Europe GmbH nor the names of its contributors may be used to endorse or -promote products derived from this Software without specific prior written -permission. - -You may not sell, rent, lease, sublicense, lend, time-share or transfer, in -whole or in part, or provide third parties access to prior or present -versions (or any parts thereof) of the Software. - -ASSIGNMENT: You may not assign this Agreement or your rights hereunder -without the prior written consent of Licensor. Any attempted assignment -without such consent shall be null and void. - -TERM: The term of the license granted by this Agreement is from Licensee's -acceptance of this Agreement by downloading the Software or by using the -Software until terminated as provided below. - -The Agreement automatically terminates without notice if you fail to comply -with any provision of this Agreement. Licensee may terminate this Agreement -by ceasing using the Software. Upon any termination of this Agreement, -Licensee will delete any and all copies of the Software. You agree that all -provisions which operate to protect the proprietary rights of Licensor shall -remain in force should breach occur and that the obligation of -confidentiality described in this Agreement is binding in perpetuity and, as -such, survives the term of the Agreement. - -FEE: Provided Licensee abides completely by the terms and conditions of this -Agreement, there is no fee due to Licensor for Licensee's use of the -Software in accordance with this Agreement. - -DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY -OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR -FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE -BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND -RELATED MATERIALS. - -SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is -provided as part of this Agreement. - -EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent -permitted under applicable law, Licensor shall not be liable for direct, -indirect, special, incidental, or consequential damages or lost profits -related to Licensee's use of and/or inability to use the Software, even if -Licensor is advised of the possibility of such damage. - -EXPORT REGULATION: Licensee agrees to comply with any and all applicable -export control laws, regulations, and/or other laws related to embargoes and -sanction programs administered by law. - -SEVERABILITY: If any provision(s) of this Agreement shall be held to be -invalid, illegal, or unenforceable by a court or other tribunal of competent -jurisdiction, the validity, legality and enforceability of the remaining -provisions shall not in any way be affected or impaired thereby. - -NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right -or remedy under this Agreement shall be construed as a waiver of any future -or other exercise of such right or remedy by Licensor. - -GOVERNING LAW: This Agreement shall be construed and enforced in accordance -with the laws of Germany without reference to conflict of laws principles. -You consent to the personal jurisdiction of the courts of this country and -waive their rights to venue outside of Germany. - -ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and -entire agreement between Licensee and Licensor as to the matter set forth -herein and supersedes any previous agreements, understandings, and -arrangements between the parties relating hereto. - - THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. -""" - -import math as mt -import jax -import numpy as np -import jax.numpy as jnp -from jax import random, jit, nn, lax, vmap, scipy -from functools import partial - -# if double precision -#from jax.config import config -#config.update("jax_enable_x64", True) - - -def init(xc, mode='sin', u0=1., du=0.1): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - modes = ['sin', 'sinsin', 'Gaussian', 'react', 'possin'] - assert mode in modes, 'mode is not defined!!' - if mode == 'sin': # sinusoidal wave - u = u0 * jnp.sin((xc + 1.) * jnp.pi) - elif mode == 'sinsin': # sinusoidal wave - u = jnp.sin((xc + 1.) * jnp.pi) + du * jnp.sin((xc + 1.) * jnp.pi*8.) - elif mode == 'Gaussian': # for diffusion check - t0 = 0.01 - u = jnp.exp(-xc**2*jnp.pi/(4.*t0))/jnp.sqrt(2.*t0) - elif mode == 'react': # for reaction-diffusion eq. - logu = - 0.5*(xc - jnp.pi)**2/(0.25*jnp.pi)**2 - u = jnp.exp(logu) - elif mode == 'possin': # sinusoidal wave - u = u0 * jnp.abs(jnp.sin((xc + 1.) * jnp.pi)) - return u - - -@partial(jit, static_argnums=(1, 2, 3, 4)) -def init_multi(xc, numbers=10000, k_tot=8, init_key=2022, num_choise_k=2, if_norm=False): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - - def _pass(carry): - return carry - - def select_A(carry): - def _func(carry): - carry = jnp.abs(carry) - return carry - - cond, value = carry - value = lax.cond(cond == 1, _func, _pass, value) - return cond, value - - def select_W(carry): - def _window(carry): - xx, val, xL, xR, trns = carry - val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) - return xx, val, xL, xR, trns - - cond, value, xx, xL, xR, trns = carry - - carry = xx, value, xL, xR, trns - xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) - return cond, value, xx, xL, xR, trns - - def normalize(carry): - def _norm(carry): - u = carry - u -= jnp.min(u, axis=1, keepdims=True) # positive value - u /= jnp.max(u, axis=1, keepdims=True) # normalize - return u - - cond, u = carry - u = lax.cond(cond==True, _norm, _pass, u) - return cond, u - - key = random.PRNGKey(init_key) - - selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) - selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) - kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) - amp = random.uniform(key, shape=[numbers, k_tot, 1]) - - key, subkey = random.split(key) - - phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) - _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) - _u = jnp.sum(_u, axis=1) - - # perform absolute value function - cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) - carry = (cond, _u) - - cond, _u = vmap(select_A, 0, 0)(carry) - sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) - _u *= sgn # random flip of signature - - # perform window function - key, subkey = random.split(key) - cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) - _xc = jnp.repeat(xc[None, :], numbers, axis=0) - mask = jnp.ones_like(_xc) - xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - trns = 0.01 * jnp.ones_like(cond) - carry = cond, mask, _xc, xL, xR, trns - cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) - - _u *= mask - - carry = if_norm, _u - _, _u = normalize(carry) # normalize value between [0, 1] for reaction-diffusion eq. - - return _u - -def init_multi_2DRand(xc, yc, numbers=10000, init_key=2022, k_tot=4, duMx = 1.e1): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def _pass(carry): - return carry - - def select_W(carry): - def _window(carry): - xx, yy, val, xL, xR, yL, yR, trns = carry - x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) - y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) - val = x_win[:, None] * y_win[None, :] - return xx, yy, val, xL, xR, yL, yR, trns - - cond, value, xx, yy, xL, xR, yL, yR, trns = carry - - carry = xx, yy, value, xL, xR, yL, yR, trns - xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) - return cond, value, xx, xL, xR, trns - - def __create_2DRand_init(u0, delu): - nx, ny = xc.shape[0], yc.shape[0] - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - - qLx = dx * nx - qLy = dy * ny - - ## random field - u = jnp.zeros([nx, ny]) - - key = random.PRNGKey(init_key) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[1]) # (vi, k) - - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) - kdx = kx * xc[:, None] + ky * yc[None, :] - u += uk * jnp.sin(kdx + phs) - - # renormalize total velocity - u = u0 + delu * u / jnp.abs(u).mean() - - return u - - key = random.PRNGKey(init_key) - u0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=duMx) - key, subkey = random.split(key) - delu = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.5) - u = jax.vmap(__create_2DRand_init, axis_name='i')(u0, delu) - - # perform window function - key, subkey = random.split(key) - cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) - mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) - xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - key, subkey = random.split(key) - yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - _xc = jnp.repeat(xc[None, :], numbers, axis=0) - _yc = jnp.repeat(xc[None, :], numbers, axis=0) - trns = 0.01 * jnp.ones_like(cond) - carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns - cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) - - u = u * mask - u = u + u0[:,:,None] * (1. - mask) - - return u - -def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, - M0=0.1, dk=1, gamma=.1666666667): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - print(mode) - modes = ['shocktube0','shocktube1','shocktube2','shocktube3','shocktube4','shocktube5','shocktube6','shocktube7', - '2D-shock', 'OTVortex', 'KHI', 'turbulence', 'sound_wave', 'c_discon', 'BlastWave'] - assert mode in modes, 'mode is not defined!!' - - _, nx, ny, nz = u.shape - - if mode[:-1] == 'shocktube': # shock tube - - if direc == 'x': - iX, iY, iZ = 1, 2, 3 - Ncell = nx - _u = jnp.zeros_like(u) - elif direc == 'y': - iX, iY, iZ = 2, 3, 1 - Ncell = ny - _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': - iX, iY, iZ = 3, 1, 2 - Ncell = nz - _u = jnp.transpose(u, (0, 3, 1, 2)) - - if mode[-1] == '0': # test 0 for viscosity - nx0 = int(0.5*Ncell) - uL = [1., 0.75, 0.2, -0.3, 1.] - uR = [0.125, 0., 0.1, 0.9, 0.1] - elif mode[-1] == '1': # test 1 - nx0 = int(0.3*Ncell) - uL = [1., 0.75, 0., 0., 1.] - uR = [0.125, 0., 0., 0., 0.1] - elif mode[-1] == '2': # test 2 - nx0 = int(0.5*Ncell) - uL = [1., -2., 0., 0., 0.4] - uR = [1., 2., 0., 0., 0.4] - elif mode[-1] == '3': # test 3 - nx0 = int(0.5*Ncell) - uL = [1., 0., 0., 0., 1.e3] - uR = [1., 0., 0., 0., 0.01] - elif mode[-1] == '4': # test 4 - nx0 = int(0.4*Ncell) - uL = [5.99924, 19.5975, 0., 0., 460.894] - uR = [5.99242, -6.19633, 0., 0., 46.095] - elif mode[-1] == '5': # test 5 - nx0 = int(0.8 * Ncell) - uL = [1., -19.59745, 0., 0., 1.e3] - uR = [1., -19.59745, 0., 0., 0.01] - elif mode[-1] == '6': # test 6 - nx0 = int(0.5 * Ncell) - uL = [1.4, 0., 0., 0., 1.] - uR = [1., 0., 0., 0., 1.] - elif mode[-1] == '7': # test 7 - nx0 = int(0.5 * Ncell) - uL = [1.4, 0.1, 0., 0., 1.] - uR = [1., 0.1, 0., 0., 1.] - - # left - _u = _u.at[0, :nx0].set(uL[0]) - _u = _u.at[iX, :nx0].set(uL[1]) - _u = _u.at[iY, :nx0].set(uL[2]) - _u = _u.at[iZ, :nx0].set(uL[3]) - _u = _u.at[4, :nx0].set(uL[4]) - # right - _u = _u.at[0, nx0:].set(uR[0]) - _u = _u.at[iX, nx0:].set(uR[1]) - _u = _u.at[iY, nx0:].set(uR[2]) - _u = _u.at[iZ, nx0:].set(uR[3]) - _u = _u.at[4, nx0:].set(uR[4]) - - if direc == 'x': - u = _u - elif direc == 'y': - u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': - u = jnp.transpose(_u, (0, 2, 3, 1)) - elif mode == '2D-shock': # shock tube - u1 = [0.5, 0., 0., 0., 0.1] - u2 = [0.1, 0., 1., 0., 1.] - u3 = [0.1, 1., 0., 0., 1.] - u4 = [0.1, 0., 0., 0., 0.01] - - # left-bottom - u = u.at[0, :nx//2, :ny//2].set(u1[0]) - u = u.at[1, :nx//2, :ny//2].set(u1[1]) - u = u.at[2, :nx//2, :ny//2].set(u1[2]) - u = u.at[3, :nx//2, :ny//2].set(u1[3]) - u = u.at[4, :nx//2, :ny//2].set(u1[4]) - # right-bottom - u = u.at[0, nx//2:, :ny//2].set(u2[0]) - u = u.at[1, nx//2:, :ny//2].set(u2[1]) - u = u.at[2, nx//2:, :ny//2].set(u2[2]) - u = u.at[3, nx//2:, :ny//2].set(u2[3]) - u = u.at[4, nx//2:, :ny//2].set(u2[4]) - # left-top - u = u.at[0, :nx//2, ny//2:].set(u3[0]) - u = u.at[1, :nx//2, ny//2:].set(u3[1]) - u = u.at[2, :nx//2, ny//2:].set(u3[2]) - u = u.at[3, :nx//2, ny//2:].set(u3[3]) - u = u.at[4, :nx//2, ny//2:].set(u3[4]) - # right-top - u = u.at[0, nx//2:, ny//2:].set(u4[0]) - u = u.at[1, nx//2:, ny//2:].set(u4[1]) - u = u.at[2, nx//2:, ny//2:].set(u4[2]) - u = u.at[3, nx//2:, ny//2:].set(u4[3]) - u = u.at[4, nx//2:, ny//2:].set(u4[4]) - - elif mode == 'OTVortex': # shock tube - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - qLx = dx * xc.shape[0] - qLy = dy * yc.shape[0] - _xc = jnp.zeros([xc.shape[0] + 4]) - _yc = jnp.zeros([yc.shape[0] + 4]) - _xc = _xc.at[2:-2].set(xc) - _yc = _yc.at[2:-2].set(yc) - _xc = _xc.at[:2].set(jnp.array([-2 * dx, -dx])) - _yc = _yc.at[:2].set(jnp.array([-2 * dy, -dy])) - _xc = _xc.at[-2:].set(jnp.array([xc[-1] + dx, xc[-1] + 2. * dx])) - _yc = _yc.at[-2:].set(jnp.array([yc[-1] + dy, yc[-1] + 2. * dy])) - - u = u.at[0].add(gamma ** 2) - u = u.at[1].set(- jnp.sin(2. * jnp.pi * _yc[None, :, None] / qLy)) - u = u.at[2].set(jnp.sin(2. * jnp.pi * _xc[:, None, None] / qLx)) - u = u.at[3].add(0.) - u = u.at[4].add(gamma) - - elif mode == 'KHI': # Kelvin-Helmholtz instability - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - #gamma = 1.666666666666667 - #k = 1. # moved to the external input - d0_u = 2./(dk + 1.) - d0_d = dk * d0_u - d0 = 0.5 * (d0_u + d0_d) - #M0 = 0.1 # Mach number # moved to external input - ux = 1. - cs = ux/M0 - #ux = 0.1 * cs # << cs - p0 = cs**2 * d0/gamma - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - qLx = dx * nx - qLy = dy * ny - kk = 4. # wave number - kx = kk * 2. * jnp.pi / qLx - dl = 5.e-3 * qLy - - bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 - - vx = jnp.zeros([nx, ny, nz]) - dd = jnp.zeros([nx, ny, nz]) - for i in range(nx): - _vx = jnp.where(yc > bound[i], ux, -ux) - _dd = jnp.where(yc > bound[i], d0_u, d0_d) - vx = vx.at[i, :, :].set(_vx[:, None]) - dd = dd.at[i, :, :].set(_dd[:, None]) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2].set(0.) - u = u.at[3].add(0.) - u = u.at[4].add(p0) - - elif mode == 'turbulence': # 3D decaying turbulence - - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - dz = zc[1] - zc[0] - qLx = dx * nx - qLy = dy * ny - qLz = dz * nz - - ## random velocity field - k_tot = 3 - vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) - - key = random.PRNGKey(init_key) - - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz - - for k in range(-k_tot, k_tot + 1): - kz = kz0 * k # from 1 to k_tot - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j * k == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] - vx += uk * jnp.sin(kdx + phs[0]) - vy += uk * jnp.sin(kdx + phs[1]) - vz += uk * jnp.sin(kdx + phs[2]) - - del(kdx, uk, phs) - - # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz - fx = dfx * (np.arange(nx) - 1. - nx//2) - fy = dfy * (np.arange(ny) - 1. - ny//2) - fz = dfz * (np.arange(nz) - 1. - nz//2) - - vkx = np.fft.fftn(vx) * dx * dy * dz - vky = np.fft.fftn(vy) * dx * dy * dz - vkz = np.fft.fftn(vz) * dx * dy * dz - - # shift to kxi=0 is at the center - vkx = np.fft.fftshift(vkx) - vky = np.fft.fftshift(vky) - vkz = np.fft.fftshift(vkz) - - #for k in range(nz): - # for j in range(ny): - # for i in range(nx): - # ff = (fx[i]**2 + fy[j]**2 + fz[k]**2) - # fi = np.where(ff > 1.e-8, 1./ff, 0.) - # # subtract expansion k.vk - # fdv = fx[i] * vkx[i, j, k] + fy[j] * vky[i, j, k] + fz[k] * vkz[i, j, k] - # vkx -= fdv * fx[i] * fi - # vky -= fdv * fy[j] * fi - # vkz -= fdv * fz[k] * fi - - fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 - fi = np.where(fi > 1.e-8, 1./fi, 0.) - - fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi - vkx -= fdv * fx[:,None,None] - vky -= fdv * fy[None,:,None] - vkz -= fdv * fz[None,None,:] - del(fi, fdv) - - # shift back to original order - vkx = np.fft.ifftshift(vkx) - vky = np.fft.ifftshift(vky) - vkz = np.fft.ifftshift(vkz) - - # inverse FFT - vx = np.fft.ifftn(vkx).real * dfx * dfy * dfz - vy = np.fft.ifftn(vky).real * dfx * dfy * dfz - vz = np.fft.ifftn(vkz).real * dfx * dfy * dfz - - # renormalize total velocity - vtot = np.sqrt(vx**2 + vy**2 + vz**2).mean() - vx *= u0 / vtot - vy *= u0 / vtot - vz *= u0 / vtot - - u = u.at[0].set(d0) - u = u.at[1,2:-2,2:-2,2:-2].set(jnp.array(vx)) - u = u.at[2,2:-2,2:-2,2:-2].set(jnp.array(vy)) - u = u.at[3,2:-2,2:-2,2:-2].add(jnp.array(vz)) - u = u.at[4].add(p0) - - elif mode == 'BlastWave': # Kelvin-Helmholtz instability - """ Stone Gardiner 2009 without B """ - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - db = 1. - pb = 0.1 - - pc = 1.e2 # central region - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - dz = zc[1] - zc[0] - qLx = dx * nx - qLy = dy * ny - qLz = dz * nz - qL = (qLx + qLy + qLz)/3. - - #p0 = jnp.ones([nx, ny, nz]) * pb - RR = jnp.sqrt((xc[:,None,None] - xc[nx//2])**2 - + (yc[None,:,None] - yc[ny//2])**2 - + (zc[None,None,:] - zc[nz//2])**2) - p0 = jnp.where(RR > 0.05 * qL, pb, pc) - #for k in range(nz): - # for j in range(ny): - # for i in range(nx): - # RR = jnp.sqrt((xc[i] - 0.5 * qLx)**2 + (yc[j] - 0.5 * qLy)**2 + (zc[k] - 0.5 * qLz)**2) - # if RR < 0.1 * qL: - # p0 = p0.at[i,j,k].set(pc) - - u = u.at[0].set(db) - u = u.at[1].set(0.) - u = u.at[2].set(0.) - u = u.at[3].set(0.) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p0) - - elif mode == 'sound_wave': # sound wave - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - gamma = 1.666666666666667 - d0 = 1. - cs = 2. - p0 = cs**2 * d0/gamma - if direc == 'x': - iX, iY, iZ = 1, 2, 3 - XC = xc - qL = (xc[1] - xc[0]) * nx - _u = jnp.zeros_like(u) - elif direc == 'y': - iX, iY, iZ = 2, 3, 1 - XC = yc - qL = (yc[1] - yc[0]) * ny - _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': - iX, iY, iZ = 3, 1, 2 - XC = zc - qL = (zc[1] - zc[0]) * nz - _u = jnp.transpose(u, (0, 3, 1, 2)) - - kk = 2. * jnp.pi / qL - _u = _u.at[0,2:-2].set(d0 * (1. + 1.e-3 * jnp.sin(kk * XC[:, None, None]))) - _u = _u.at[iX].set((_u[0] - d0) * cs /d0) - _u = _u.at[4].set(p0 + cs**2 * (_u[0] - d0) ) - - if direc == 'x': - u = _u - elif direc == 'y': - u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': - u = jnp.transpose(_u, (0, 2, 3, 1)) - - elif mode == 'c_discon': # tangent discontinuity - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - p0 = 1. - vy0 = 0.1 - if direc == 'x': - iX, iY, iZ = 1, 2, 3 - XC = xc - qL = (xc[1] - xc[0]) * nx - _u = jnp.zeros_like(u) - elif direc == 'y': - iX, iY, iZ = 2, 3, 1 - XC = yc - qL = (yc[1] - yc[0]) * ny - _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': - iX, iY, iZ = 3, 1, 2 - XC = zc - qL = (zc[1] - zc[0]) * nz - _u = jnp.transpose(u, (0, 3, 1, 2)) - - _u = _u.at[0].set(d0) - _u = _u.at[iY, 2:-2].set(vy0 * scipy.special.erf(0.5 * XC[:, None, None] / jnp.sqrt(0.1))) - _u = _u.at[4].set(p0) - - if direc == 'x': - u = _u - elif direc == 'y': - u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': - u = jnp.transpose(_u, (0, 2, 3, 1)) - - return u - -@partial(jit, static_argnums=(3, 4, 5, 6, 7, 8, 9)) -def init_multi_HD(xc, yc, zc, numbers=10000, k_tot=10, init_key=2022, num_choise_k=2, - if_renorm=False, umax=1.e4, umin=1.e-8): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - - def _pass(carry): - return carry - - def select_A(carry): - def _func(carry): - carry = jnp.abs(carry) - return carry - - cond, value = carry - value = lax.cond(cond == 1, _func, _pass, value) - return cond, value - - def select_W(carry): - def _window(carry): - xx, val, xL, xR, trns = carry - val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) - return xx, val, xL, xR, trns - - cond, value, xx, xL, xR, trns = carry - - carry = xx, value, xL, xR, trns - xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) - return cond, value, xx, xL, xR, trns - - def renormalize(carry): - def _norm(carry): - u, key = carry - u -= jnp.min(u, axis=1, keepdims=True) # positive value - u /= jnp.max(u, axis=1, keepdims=True) # normalize - - key, subkey = random.split(key) - m_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) - m_val = jnp.exp(m_val) - key, subkey = random.split(key) - b_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) - b_val = jnp.exp(b_val) - return u * m_val[:, None] + b_val[:, None], key - - cond, u, key = carry - carry = u, key - u, key = lax.cond(cond==True, _norm, _pass, carry) - return cond, u, key - - assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - key = random.PRNGKey(init_key) - - selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) - selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) - kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) - amp = random.uniform(key, shape=[numbers, k_tot, 1]) - - key, subkey = random.split(key) - - phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) - _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) - _u = jnp.sum(_u, axis=1) - - # perform absolute value function - cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) - carry = (cond, _u) - - cond, _u = vmap(select_A, 0, 0)(carry) - sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) - _u *= sgn # random flip of signature - - # perform window function - key, subkey = random.split(key) - cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) - _xc = jnp.repeat(xc[None, :], numbers, axis=0) - mask = jnp.ones_like(_xc) - xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - trns = 0.01 * jnp.ones_like(cond) - carry = cond, mask, _xc, xL, xR, trns - cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) - - _u *= mask - - carry = if_renorm, _u, key - _, _u, _ = renormalize(carry) # renormalize value between a given values - - return _u[...,None,None] - -#@partial(jit, static_argnums=(3, 4, 5, 6)) -def init_multi_HD_shock(xc, yc, zc, numbers=10000, init_key=2022, umax=1.e4, umin=1.e-8): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def select_var(carry): - def _func(carry): - vmin, vmax = carry - return jnp.log(vmin), jnp.log(vmax) - - def _pass(carry): - return carry - - vmin, vmax = carry - vmin, vmax = lax.cond(vmin > 0., _func, _pass, carry) - return vmin, vmax - - nx = xc.shape[0] - - carry = umin, umax - u_min, u_max = select_var(carry) - - key = random.PRNGKey(init_key) - QLs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) - QLs = jnp.exp(QLs) - key, subkey = random.split(key) - QRs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) - QRs = jnp.exp(QRs) - - nx0s = nx * random.uniform(key, shape=([numbers, 1]), minval=0.25, maxval=0.75) - nx0s = nx0s.astype(int) - - u = jnp.arange(xc.shape[0]) - u = jnp.tile(u, (numbers, 1)) - - u = jax.vmap(jnp.where, axis_name='i')(u < nx0s, QLs, QRs) - return u[...,None,None] - -#@partial(jit, static_argnums=(4, 5, 6, 7, 8, 9)) -def init_multi_HD_KH(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, dkMx=2., kmax=4., gamma=1.666666667): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def __create_KH_init(u, dk, kk): - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0_u = 2./(dk + 1.) - d0_d = dk * d0_u - d0 = 0.5 * (d0_u + d0_d) - ux = 1. - cs = ux/M0 - p0 = cs**2 * d0/gamma - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - qLx = dx * nx - qLy = dy * ny - kx = kk * 2. * jnp.pi / qLx - dl = 5.e-3 * qLy - # (numbers, nx) - bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 - - vx = jnp.zeros([nx, ny, nz]) - dd = jnp.zeros([nx, ny, nz]) - for i in range(nx): - _vx = jnp.where(yc > bound[i], ux, -ux) - _dd = jnp.where(yc > bound[i], d0_u, d0_d) - vx = vx.at[i, :, :].set(_vx[:, None]) - dd = dd.at[i, :, :].set(_dd[:, None]) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2].set(0.) - u = u.at[3].add(0.) - u = u.at[4].add(p0) - return u - - # create random density ratio - key = random.PRNGKey(init_key) - dk = random.uniform(key, shape=([numbers, 1]), minval=1. / dkMx, maxval=dkMx) - #create random wave-numbers - key, subkey = random.split(key) - kk = random.randint(key, shape=([numbers, 1]), minval=1, maxval=kmax) - print('vmap...') - u = jax.vmap(__create_KH_init, axis_name='i')(u, dk, kk) - - return u - -#@partial(jit, static_argnums=(4, 5, 6, 7, 8)) -def init_multi_HD_2DTurb(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def __create_2DTurb_init(u): - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - - qLx = dx * nx - qLy = dy * ny - - ## random velocity field - vx, vy = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) - - key = random.PRNGKey(init_key) - - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[2]) # (vi, k) - - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) - kdx = kx * xc[:, None, None] + ky * yc[None, :, None] - vx += uk * jnp.sin(kdx + phs[0]) - vy += uk * jnp.sin(kdx + phs[1]) - - del (kdx, uk, phs) - - # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy = 1. / qLx, 1. / qLy - fx = dfx * (np.arange(nx) - 1. - nx // 2) - fy = dfy * (np.arange(ny) - 1. - ny // 2) - - vkx = np.fft.fftn(vx) * dx * dy - vky = np.fft.fftn(vy) * dx * dy - - # shift to kxi=0 is at the center - vkx = np.fft.fftshift(vkx) - vky = np.fft.fftshift(vky) - - fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 - fi = np.where(fi > 1.e-8, 1. / fi, 0.) - - fdv = (fx[:, None, None] * vkx + fy[None, :, None] * vky) * fi - vkx -= fdv * fx[:, None, None] - vky -= fdv * fy[None, :, None] - del (fi, fdv) - - # shift back to original order - vkx = np.fft.ifftshift(vkx) - vky = np.fft.ifftshift(vky) - - # inverse FFT - vx = np.fft.ifftn(vkx).real * dfx * dfy - vy = np.fft.ifftn(vky).real * dfx * dfy - - # renormalize total velocity - vtot = np.sqrt(vx ** 2 + vy ** 2).mean() - vx *= u0 / vtot - vy *= u0 / vtot - - u = u.at[0].set(d0) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(jnp.array(vx)) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(jnp.array(vy)) - u = u.at[4].add(p0) - return u - - u = jax.vmap(__create_2DTurb_init, axis_name='i')(u) - - return u - -def init_multi_HD_2DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, - dMx=1.e1, TMx=1.e1): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def _pass(carry): - return carry - - def select_W(carry): - def _window(carry): - xx, yy, val, xL, xR, yL, yR, trns = carry - x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) - y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) - val = x_win[:, None] * y_win[None, :] - return xx, yy, val, xL, xR, yL, yR, trns - - cond, value, xx, yy, xL, xR, yL, yR, trns = carry - - carry = xx, yy, value, xL, xR, yL, yR, trns - xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) - return cond, value, xx, yy, xL, xR, yL, yR, trns - - def __create_2DRand_init(u, d0, T0, delD, delP): - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - - p0 = d0 * T0 - cs = jnp.sqrt(T0 * gamma) - u0 = M0 * cs - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - - qLx = dx * nx - qLy = dy * ny - - ## random velocity field - d, p, vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) - - key = random.PRNGKey(init_key) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[4]) # (vi, k) - - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) - kdx = kx * xc[:, None, None] + ky * yc[None, :, None] - vx += uk * jnp.sin(kdx + phs[0]) - vy += uk * jnp.sin(kdx + phs[1]) - p += uk * jnp.sin(kdx + phs[2]) - d += uk * jnp.sin(kdx + phs[3]) - - del (kdx, uk, phs) - - # renormalize total velocity - vtot = np.sqrt(vx ** 2 + vy ** 2).mean() - vx *= u0 / vtot - vy *= u0 / vtot - #d = d0 + delD * d / jnp.abs(d).mean() - #p = p0 + delP * p / jnp.abs(p).mean() - d = d0 * (1. + delD * d / jnp.abs(d).mean()) - p = p0 * (1. + delP * p / jnp.abs(p).mean()) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) - return u - - key = random.PRNGKey(init_key) - d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) - key, subkey = random.split(key) - delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) - key, subkey = random.split(key) - T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) - key, subkey = random.split(key) - delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) - u = jax.vmap(__create_2DRand_init, axis_name='i')(u, d0, T0, delD, delP) - - # perform window function - key, subkey = random.split(key) - cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) - mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) - xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - key, subkey = random.split(key) - yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - _xc = jnp.repeat(xc[None, :], numbers, axis=0) # add batch - _yc = jnp.repeat(yc[None, :], numbers, axis=0) # add batch - trns = 0.01 * jnp.ones_like(cond) - carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns - cond, mask, _xc, _yc, xL, xR, yL, yR, trns = vmap(select_W, 0, 0)(carry) - - u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, None]) - u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, None])) - u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, None])) - - return u - -def init_multi_HD_3DTurb(u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def __create_3DTurb_init(u): - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - dz = zc[1] - zc[0] - - qLx = dx * nx - qLy = dy * ny - qLz = dz * nz - - ## random velocity field - vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) - - key = random.PRNGKey(init_key) - - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz - - for k in range(-k_tot, k_tot + 1): - kz = kz0 * k # from 1 to k_tot - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j * k == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] - vx += uk * jnp.sin(kdx + phs[0]) - vy += uk * jnp.sin(kdx + phs[1]) - vz += uk * jnp.sin(kdx + phs[2]) - - del(kdx, uk, phs) - - # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz - fx = dfx * (np.arange(nx) - 1. - nx//2) - fy = dfy * (np.arange(ny) - 1. - ny//2) - fz = dfz * (np.arange(nz) - 1. - nz//2) - - vkx = np.fft.fftn(vx) * dx * dy * dz - vky = np.fft.fftn(vy) * dx * dy * dz - vkz = np.fft.fftn(vz) * dx * dy * dz - - # shift to kxi=0 is at the center - vkx = np.fft.fftshift(vkx) - vky = np.fft.fftshift(vky) - vkz = np.fft.fftshift(vkz) - - fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 - fi = np.where(fi > 1.e-8, 1./fi, 0.) - - fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi - vkx -= fdv * fx[:,None,None] - vky -= fdv * fy[None,:,None] - vkz -= fdv * fz[None,None,:] - del(fi, fdv) - - # shift back to original order - vkx = np.fft.ifftshift(vkx) - vky = np.fft.ifftshift(vky) - vkz = np.fft.ifftshift(vkz) - - # inverse FFT - vx = np.fft.ifftn(vkx).real * dfx * dfy * dfz - vy = np.fft.ifftn(vky).real * dfx * dfy * dfz - vz = np.fft.ifftn(vkz).real * dfx * dfy * dfz - - # renormalize total velocity - vtot = np.sqrt(vx**2 + vy**2 + vz**2).mean() - vx *= u0 / vtot - vy *= u0 / vtot - vz *= u0 / vtot - - u = u.at[0].set(d0) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(jnp.array(vx)) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(jnp.array(vy)) - u = u.at[3, 2:-2, 2:-2, 2:-2].set(jnp.array(vz)) - u = u.at[4].add(p0) - return u - - u = jax.vmap(__create_3DTurb_init, axis_name='i')(u) - - return u - -def init_multi_HD_3DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, - dMx=1.e1, TMx=1.e1): - """ - :param xc: cell center coordinate - :param mode: initial condition - :return: 1D scalar function u at cell center - """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' - - def _pass(carry): - return carry - - def select_W(carry): - def _window(carry): - xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns = carry - x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) - y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) - z_win = 0.5 * (jnp.tanh((zz - zL) / trns) - jnp.tanh((zz - zR) / trns)) - val = x_win[:, None, None] * y_win[None, :, None] * z_win[None, None, :] - return xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns - - cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns = carry - - carry = xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns - xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns = lax.cond(cond == 1, _window, _pass, carry) - return cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns - - def __create_3DRand_init(u, d0, T0, delD, delP): - nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - - p0 = d0 * T0 - cs = jnp.sqrt(T0 * gamma) - u0 = M0 * cs - - dx = xc[1] - xc[0] - dy = yc[1] - yc[0] - dz = zc[1] - zc[0] - - qLx = dx * nx - qLy = dy * ny - qLz = dz * nz - - ## random velocity field - d, p, vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), \ - jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) - - key = random.PRNGKey(init_key) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz - - for k in range(-k_tot, k_tot + 1): - kz = kz0 * k # from 1 to k_tot - for j in range(-k_tot, k_tot + 1): - ky = ky0 * j # from 1 to k_tot - for i in range(-k_tot, k_tot + 1): - kx = kx0 * i # from 1 to k_tot - if i * j * k == 0: # avoiding uniform velocity - continue - # random phase - key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] - vx += uk * jnp.sin(kdx + phs[0]) - vy += uk * jnp.sin(kdx + phs[1]) - vz += uk * jnp.sin(kdx + phs[2]) - p += uk * jnp.sin(kdx + phs[3]) - d += uk * jnp.sin(kdx + phs[4]) - - del(kdx, uk, phs) - - # renormalize total velocity - vtot = np.sqrt(vx ** 2 + vy ** 2 + vz ** 2).mean() - vx *= u0 / vtot - vy *= u0 / vtot - vz *= u0 / vtot - #d = d0 + delD * d / jnp.abs(d).mean() - #p = p0 + delP * p / jnp.abs(p).mean() - d = d0 * (1. + delD * d / jnp.abs(d).mean()) - p = p0 * (1. + delP * p / jnp.abs(p).mean()) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) - return u - - key = random.PRNGKey(init_key) - d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) - key, subkey = random.split(key) - delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) - key, subkey = random.split(key) - T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) - key, subkey = random.split(key) - delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) - - u = jax.vmap(__create_3DRand_init, axis_name='i')(u, d0, T0, delD, delP) - - # perform window function - key, subkey = random.split(key) - cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) - mask = jnp.ones([numbers, xc.shape[0], yc.shape[0], zc.shape[0]]) - xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - key, subkey = random.split(key) - yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - key, subkey = random.split(key) - zL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) - zR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) - _xc = jnp.repeat(xc[None, :], numbers, axis=0) - _yc = jnp.repeat(yc[None, :], numbers, axis=0) - _zc = jnp.repeat(zc[None, :], numbers, axis=0) - trns = 0.01 * jnp.ones_like(cond) - carry = cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns - cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns = vmap(select_W, 0, 0)(carry) - - u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, :]) - u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, :])) - u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, :])) - - return u - -def bc(u, dx, Ncell, mode='periodic'): - _u = jnp.zeros(Ncell+4) # because of 2nd-order precision in space - _u = _u.at[2:Ncell+2].set(u) - if mode=='periodic': # periodic boundary condition - _u = _u.at[0:2].set(u[-2:]) # left hand side - _u = _u.at[Ncell + 2:Ncell + 4].set(u[0:2]) # right hand side - elif mode=='reflection': - _u = _u.at[0].set(- u[3]) # left hand side - _u = _u.at[1].set(- u[2]) # left hand side - _u = _u.at[-2].set(- u[-3]) # right hand side - _u = _u.at[-1].set(- u[-4]) # right hand side - elif mode=='copy': - _u = _u.at[0].set(u[3]) # left hand side - _u = _u.at[1].set(u[2]) # left hand side - _u = _u.at[-2].set(u[-3]) # right hand side - _u = _u.at[-1].set(u[-4]) # right hand side - - return _u - -def bc_2D(_u, mode='trans'): - Nx, Ny = _u.shape - u = jnp.zeros([Nx + 4, Ny + 4]) # because of 2nd-order precision in space - u = u.at[2:-2, 2:-2].set(_u) - Nx += 2 - Ny += 2 - - if mode=='periodic': # periodic boundary condition - # left hand side - u = u.at[0:2, 2:-2].set(u[Nx-2:Nx, 2:-2]) # x - u = u.at[2:-2, 0:2].set(u[2:-2, Ny-2:Ny]) # y - # right hand side - u = u.at[Nx:Nx+2, 2:-2].set(u[2:4, 2:-2]) - u = u.at[2:-2, Ny:Ny+2].set(u[2:-2, 2:4]) - elif mode=='trans': # periodic boundary condition - # left hand side - u = u.at[0, 2:-2].set(u[3, 2:-2]) # x - u = u.at[2:-2, 0].set(u[2:-2, 3]) # y - u = u.at[1, 2:-2].set(u[2, 2:-2]) # x - u = u.at[2:-2, 1].set(u[2:-2, 2]) # y - # right hand side - u = u.at[-2, 2:-2].set(u[-3, 2:-2]) - u = u.at[2:-2, -2].set(u[2:-2, -3]) - u = u.at[-1, 2:-2].set(u[-4, 2:-2]) - u = u.at[2:-2, -1].set(u[2:-2, -4]) - elif mode=='Neumann': # periodic boundary condition - # left hand side - u = u.at[0, 2:-2].set(0.) # x - u = u.at[2:-2, 0].set(0.) # y - u = u.at[1, 2:-2].set(0.) # x - u = u.at[2:-2, 1].set(0.) # y - # right hand side - u = u.at[-2, 2:-2].set(0.) - u = u.at[2:-2, -2].set(0.) - u = u.at[-1, 2:-2].set(0.) - u = u.at[2:-2, -1].set(0.) - return u - -def bc_HD(u, mode): - _, Nx, Ny, Nz = u.shape - Nx -= 2 - Ny -= 2 - Nz -= 2 - if mode=='periodic': # periodic boundary condition - # left hand side - u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx-2:Nx, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0:2, 2:-2].set(u[:, 2:-2, Ny-2:Ny, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0:2].set(u[:, 2:-2, 2:-2, Nz-2:Nz]) # z - # right hand side - u = u.at[:, Nx:Nx+2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, Ny:Ny+2, 2:-2].set(u[:, 2:-2, 2:4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, Nz:Nz+2].set(u[:, 2:-2, 2:-2, 2:4]) - elif mode=='trans': # periodic boundary condition - # left hand side - u = u.at[:, 0, 2:-2, 2:-2].set(u[:, 3, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z - u = u.at[:, 1, 2:-2, 2:-2].set(u[:, 2, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z - # right hand side - u = u.at[:, -2, 2:-2, 2:-2].set(u[:, -3, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) - u = u.at[:, -1, 2:-2, 2:-2].set(u[:, -4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) - elif mode=='KHI': # x: periodic, y, z : trans - # left hand side - u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2:Nx, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z - u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z - # right hand side - u = u.at[:, Nx:Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) - u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) - return u - -def bc_HD_vis(u, if_periodic=True): # for viscosity - """ - for the moment, assuming periodic/copy boundary - seemingly, copy boundary does not work well... - """ - _, Nx, Ny, Nz = u.shape - Nx -= 2 - Ny -= 2 - Nz -= 2 - - if if_periodic: - u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT - else: # trans - u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, 4:2, 4:2, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT - - return u - -def bc_HD_vis(u, if_periodic=True): # for viscosity - """ - for the moment, assuming periodic/copy boundary - seemingly, copy boundary does not work well... - """ - _, Nx, Ny, Nz = u.shape - Nx -= 2 - Ny -= 2 - Nz -= 2 - - if if_periodic: - u = u.at[:, 0:2, 0:2 + 2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT - else: # trans - u = u.at[:, 0:2, 0:2 + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT - - return u - -def bc_HD_vis(u): # for viscosity - _, Nx, Ny, Nz = u.shape - Nx -= 2 - Ny -= 2 - Nz -= 2 - - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, 2:4]) # xBzB - u = u.at[:, 0:2, Ny:Ny+2, 2:-2].set(u[:, 2:4, Ny-2:Ny, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, Nz:Nz+2].set(u[:, 2:4, 2:-2, Nz-2:Nz]) # xBzT - u = u.at[:, Nx:Nx+2, 0:2, 2:-2].set(u[:, Nx-2:Nx, 2:4, 2:-2]) # xTyB - u = u.at[:, Nx:Nx+2, 2:-2, 0:2].set(u[:, Nx-2:Nx, 2:-2, 2:4]) # xTzB - u = u.at[:, Nx:Nx+2, Ny:Ny+2, 2:-2].set(u[:, Nx-2:Nx, Ny-2:Ny, 2:-2]) # xTyT - u = u.at[:, Nx:Nx+2, 2:-2, Nz:Nz+2].set(u[:, Nx-2:Nx, 2:-2, Nz-2:Nz]) # xTzT - - return u - -def VLlimiter(a, b, c, alpha=2.): - return jnp.sign(c)\ - *(0.5 + 0.5*jnp.sign(a*b))\ - *jnp.minimum(alpha*jnp.minimum(jnp.abs(a), jnp.abs(b)), jnp.abs(c)) - -def limiting(u, Ncell, if_second_order): - # under construction - duL = u[1:Ncell + 3] - u[0:Ncell + 2] - duR = u[2:Ncell + 4] - u[1:Ncell + 3] - duM = (u[2:Ncell + 4] - u[0:Ncell + 2])*0.5 - gradu = VLlimiter(duL, duR, duM) * if_second_order - # -1:Ncell - #uL, uR = jnp.zeros(Ncell+4), jnp.zeros(Ncell+4) - uL, uR = jnp.zeros_like(u), jnp.zeros_like(u) - uL = uL.at[1:Ncell+3].set(u[1:Ncell+3] - 0.5*gradu) # left of cell - uR = uR.at[1:Ncell+3].set(u[1:Ncell+3] + 0.5*gradu) # right of cell - return uL, uR - -def limiting_HD(u, if_second_order): - nd, nx, ny, nz = u.shape - uL, uR = u, u - nx -= 4 - - duL = u[:, 1:nx + 3, :, :] - u[:, 0:nx + 2, :, :] - duR = u[:, 2:nx + 4, :, :] - u[:, 1:nx + 3, :, :] - duM = (u[:, 2:nx + 4, :, :] - u[:, 0:nx + 2, :, :]) * 0.5 - gradu = VLlimiter(duL, duR, duM) * if_second_order - # -1:Ncell - uL = uL.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] - 0.5*gradu) # left of cell - uR = uR.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] + 0.5*gradu) # right of cell - - uL = jnp.where(uL[0] > 0., uL, u) - uL = jnp.where(uL[4] > 0., uL, u) - uR = jnp.where(uR[0] > 0., uR, u) - uR = jnp.where(uR[4] > 0., uR, u) - - return uL, uR - -def save_data(u, xc, i_save, save_dir, dt_save=None, if_final=False): - if if_final: - jnp.save(save_dir+'/x_coordinate', xc) - # - tc = jnp.arange(i_save+1)*dt_save - jnp.save(save_dir+'/t_coordinate', tc) - # - flnm = save_dir+'/Data_'+str(i_save).zfill(4) - jnp.save(flnm, u) - else: - flnm = save_dir+'/Data_'+str(i_save).zfill(4) - jnp.save(flnm, u) - -def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): - if if_final: - jnp.save(save_dir+'/x_coordinate', xc) - jnp.save(save_dir+'/y_coordinate', yc) - jnp.save(save_dir+'/z_coordinate', zc) - # - tc = jnp.arange(i_save+1)*dt_save - jnp.save(save_dir+'/t_coordinate', tc) - # - flnm = save_dir+'/Data_'+str(i_save).zfill(4) - jnp.save(flnm, u) - else: - flnm = save_dir+'/Data_'+str(i_save).zfill(4) - jnp.save(flnm, u) - -def Courant(u, dx): - stability_adv = dx/(jnp.max(jnp.abs(u)) + 1.e-8) - return stability_adv - -def Courant_diff(dx, epsilon=1.e-3): - stability_dif = 0.5*dx**2/(epsilon + 1.e-8) - return stability_dif - -def Courant_diff_2D(dx, dy, epsilon=1.e-3): - stability_dif_x = 0.5*dx**2/(epsilon + 1.e-8) - stability_dif_y = 0.5*dy**2/(epsilon + 1.e-8) - return jnp.min(jnp.array([stability_dif_x, stability_dif_y])) - -def Courant_HD(u, dx, dy, dz, gamma): - cs = jnp.sqrt(gamma*u[4]/u[0]) # sound velocity - stability_adv_x = dx/(jnp.max(cs + jnp.abs(u[1])) + 1.e-8) - stability_adv_y = dy/(jnp.max(cs + jnp.abs(u[2])) + 1.e-8) - stability_adv_z = dz/(jnp.max(cs + jnp.abs(u[3])) + 1.e-8) - stability_adv = jnp.min(jnp.array([stability_adv_x, stability_adv_y, stability_adv_z])) - return stability_adv - -def Courant_vis_HD(dx, dy, dz, eta, zeta): - #visc = jnp.max(jnp.array([eta, zeta])) - visc = 4. / 3. * eta + zeta # maximum - stability_dif_x = 0.5*dx**2/(visc + 1.e-8) - stability_dif_y = 0.5*dy**2/(visc + 1.e-8) - stability_dif_z = 0.5*dz**2/(visc + 1.e-8) - stability_dif = jnp.min(jnp.array([stability_dif_x, stability_dif_y, stability_dif_z])) - return stability_dif - +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import math as mt +import jax +import numpy as np +import jax.numpy as jnp +from jax import random, jit, nn, lax, vmap, scipy +from functools import partial + +# if double precision +#from jax.config import config +#config.update("jax_enable_x64", True) + + +def init(xc, mode='sin', u0=1., du=0.1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + modes = ['sin', 'sinsin', 'Gaussian', 'react', 'possin'] + assert mode in modes, 'mode is not defined!!' + if mode == 'sin': # sinusoidal wave + u = u0 * jnp.sin((xc + 1.) * jnp.pi) + elif mode == 'sinsin': # sinusoidal wave + u = jnp.sin((xc + 1.) * jnp.pi) + du * jnp.sin((xc + 1.) * jnp.pi*8.) + elif mode == 'Gaussian': # for diffusion check + t0 = 0.01 + u = jnp.exp(-xc**2*jnp.pi/(4.*t0))/jnp.sqrt(2.*t0) + elif mode == 'react': # for reaction-diffusion eq. + logu = - 0.5*(xc - jnp.pi)**2/(0.25*jnp.pi)**2 + u = jnp.exp(logu) + elif mode == 'possin': # sinusoidal wave + u = u0 * jnp.abs(jnp.sin((xc + 1.) * jnp.pi)) + return u + + +@partial(jit, static_argnums=(1, 2, 3, 4)) +def init_multi(xc, numbers=10000, k_tot=8, init_key=2022, num_choise_k=2, if_norm=False): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + + def _pass(carry): + return carry + + def select_A(carry): + def _func(carry): + carry = jnp.abs(carry) + return carry + + cond, value = carry + value = lax.cond(cond == 1, _func, _pass, value) + return cond, value + + def select_W(carry): + def _window(carry): + xx, val, xL, xR, trns = carry + val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + return xx, val, xL, xR, trns + + cond, value, xx, xL, xR, trns = carry + + carry = xx, value, xL, xR, trns + xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def normalize(carry): + def _norm(carry): + u = carry + u -= jnp.min(u, axis=1, keepdims=True) # positive value + u /= jnp.max(u, axis=1, keepdims=True) # normalize + return u + + cond, u = carry + u = lax.cond(cond==True, _norm, _pass, u) + return cond, u + + key = random.PRNGKey(init_key) + + selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) + kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + amp = random.uniform(key, shape=[numbers, k_tot, 1]) + + key, subkey = random.split(key) + + phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) + _u = jnp.sum(_u, axis=1) + + # perform absolute value function + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + carry = (cond, _u) + + cond, _u = vmap(select_A, 0, 0)(carry) + sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) + _u *= sgn # random flip of signature + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + mask = jnp.ones_like(_xc) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, xL, xR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + _u *= mask + + carry = if_norm, _u + _, _u = normalize(carry) # normalize value between [0, 1] for reaction-diffusion eq. + + return _u + +def init_multi_2DRand(xc, yc, numbers=10000, init_key=2022, k_tot=4, duMx = 1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, val, xL, xR, yL, yR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + val = x_win[:, None] * y_win[None, :] + return xx, yy, val, xL, xR, yL, yR, trns + + cond, value, xx, yy, xL, xR, yL, yR, trns = carry + + carry = xx, yy, value, xL, xR, yL, yR, trns + xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def __create_2DRand_init(u0, delu): + nx, ny = xc.shape[0], yc.shape[0] + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random field + u = jnp.zeros([nx, ny]) + + key = random.PRNGKey(init_key) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[1]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None] + ky * yc[None, :] + u += uk * jnp.sin(kdx + phs) + + # renormalize total velocity + u = u0 + delu * u / jnp.abs(u).mean() + + return u + + key = random.PRNGKey(init_key) + u0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=duMx) + key, subkey = random.split(key) + delu = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.5) + u = jax.vmap(__create_2DRand_init, axis_name='i')(u0, delu) + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + _yc = jnp.repeat(xc[None, :], numbers, axis=0) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + u = u * mask + u = u + u0[:,:,None] * (1. - mask) + + return u + +def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, + M0=0.1, dk=1, gamma=.1666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + print(mode) + modes = ['shocktube0','shocktube1','shocktube2','shocktube3','shocktube4','shocktube5','shocktube6','shocktube7', + '2D-shock', 'OTVortex', 'KHI', 'turbulence', 'sound_wave', 'c_discon', 'BlastWave'] + assert mode in modes, 'mode is not defined!!' + + _, nx, ny, nz = u.shape + + if mode[:-1] == 'shocktube': # shock tube + + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + Ncell = nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + Ncell = ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + Ncell = nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + if mode[-1] == '0': # test 0 for viscosity + nx0 = int(0.5*Ncell) + uL = [1., 0.75, 0.2, -0.3, 1.] + uR = [0.125, 0., 0.1, 0.9, 0.1] + elif mode[-1] == '1': # test 1 + nx0 = int(0.3*Ncell) + uL = [1., 0.75, 0., 0., 1.] + uR = [0.125, 0., 0., 0., 0.1] + elif mode[-1] == '2': # test 2 + nx0 = int(0.5*Ncell) + uL = [1., -2., 0., 0., 0.4] + uR = [1., 2., 0., 0., 0.4] + elif mode[-1] == '3': # test 3 + nx0 = int(0.5*Ncell) + uL = [1., 0., 0., 0., 1.e3] + uR = [1., 0., 0., 0., 0.01] + elif mode[-1] == '4': # test 4 + nx0 = int(0.4*Ncell) + uL = [5.99924, 19.5975, 0., 0., 460.894] + uR = [5.99242, -6.19633, 0., 0., 46.095] + elif mode[-1] == '5': # test 5 + nx0 = int(0.8 * Ncell) + uL = [1., -19.59745, 0., 0., 1.e3] + uR = [1., -19.59745, 0., 0., 0.01] + elif mode[-1] == '6': # test 6 + nx0 = int(0.5 * Ncell) + uL = [1.4, 0., 0., 0., 1.] + uR = [1., 0., 0., 0., 1.] + elif mode[-1] == '7': # test 7 + nx0 = int(0.5 * Ncell) + uL = [1.4, 0.1, 0., 0., 1.] + uR = [1., 0.1, 0., 0., 1.] + + # left + _u = _u.at[0, :nx0].set(uL[0]) + _u = _u.at[iX, :nx0].set(uL[1]) + _u = _u.at[iY, :nx0].set(uL[2]) + _u = _u.at[iZ, :nx0].set(uL[3]) + _u = _u.at[4, :nx0].set(uL[4]) + # right + _u = _u.at[0, nx0:].set(uR[0]) + _u = _u.at[iX, nx0:].set(uR[1]) + _u = _u.at[iY, nx0:].set(uR[2]) + _u = _u.at[iZ, nx0:].set(uR[3]) + _u = _u.at[4, nx0:].set(uR[4]) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + elif mode == '2D-shock': # shock tube + u1 = [0.5, 0., 0., 0., 0.1] + u2 = [0.1, 0., 1., 0., 1.] + u3 = [0.1, 1., 0., 0., 1.] + u4 = [0.1, 0., 0., 0., 0.01] + + # left-bottom + u = u.at[0, :nx//2, :ny//2].set(u1[0]) + u = u.at[1, :nx//2, :ny//2].set(u1[1]) + u = u.at[2, :nx//2, :ny//2].set(u1[2]) + u = u.at[3, :nx//2, :ny//2].set(u1[3]) + u = u.at[4, :nx//2, :ny//2].set(u1[4]) + # right-bottom + u = u.at[0, nx//2:, :ny//2].set(u2[0]) + u = u.at[1, nx//2:, :ny//2].set(u2[1]) + u = u.at[2, nx//2:, :ny//2].set(u2[2]) + u = u.at[3, nx//2:, :ny//2].set(u2[3]) + u = u.at[4, nx//2:, :ny//2].set(u2[4]) + # left-top + u = u.at[0, :nx//2, ny//2:].set(u3[0]) + u = u.at[1, :nx//2, ny//2:].set(u3[1]) + u = u.at[2, :nx//2, ny//2:].set(u3[2]) + u = u.at[3, :nx//2, ny//2:].set(u3[3]) + u = u.at[4, :nx//2, ny//2:].set(u3[4]) + # right-top + u = u.at[0, nx//2:, ny//2:].set(u4[0]) + u = u.at[1, nx//2:, ny//2:].set(u4[1]) + u = u.at[2, nx//2:, ny//2:].set(u4[2]) + u = u.at[3, nx//2:, ny//2:].set(u4[3]) + u = u.at[4, nx//2:, ny//2:].set(u4[4]) + + elif mode == 'OTVortex': # shock tube + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * xc.shape[0] + qLy = dy * yc.shape[0] + _xc = jnp.zeros([xc.shape[0] + 4]) + _yc = jnp.zeros([yc.shape[0] + 4]) + _xc = _xc.at[2:-2].set(xc) + _yc = _yc.at[2:-2].set(yc) + _xc = _xc.at[:2].set(jnp.array([-2 * dx, -dx])) + _yc = _yc.at[:2].set(jnp.array([-2 * dy, -dy])) + _xc = _xc.at[-2:].set(jnp.array([xc[-1] + dx, xc[-1] + 2. * dx])) + _yc = _yc.at[-2:].set(jnp.array([yc[-1] + dy, yc[-1] + 2. * dy])) + + u = u.at[0].add(gamma ** 2) + u = u.at[1].set(- jnp.sin(2. * jnp.pi * _yc[None, :, None] / qLy)) + u = u.at[2].set(jnp.sin(2. * jnp.pi * _xc[:, None, None] / qLx)) + u = u.at[3].add(0.) + u = u.at[4].add(gamma) + + elif mode == 'KHI': # Kelvin-Helmholtz instability + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + #gamma = 1.666666666666667 + #k = 1. # moved to the external input + d0_u = 2./(dk + 1.) + d0_d = dk * d0_u + d0 = 0.5 * (d0_u + d0_d) + #M0 = 0.1 # Mach number # moved to external input + ux = 1. + cs = ux/M0 + #ux = 0.1 * cs # << cs + p0 = cs**2 * d0/gamma + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * nx + qLy = dy * ny + kk = 4. # wave number + kx = kk * 2. * jnp.pi / qLx + dl = 5.e-3 * qLy + + bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 + + vx = jnp.zeros([nx, ny, nz]) + dd = jnp.zeros([nx, ny, nz]) + for i in range(nx): + _vx = jnp.where(yc > bound[i], ux, -ux) + _dd = jnp.where(yc > bound[i], d0_u, d0_d) + vx = vx.at[i, :, :].set(_vx[:, None]) + dd = dd.at[i, :, :].set(_dd[:, None]) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2].set(0.) + u = u.at[3].add(0.) + u = u.at[4].add(p0) + + elif mode == 'turbulence': # 3D decaying turbulence + + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + k_tot = 3 + vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) + + key = random.PRNGKey(init_key) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + + del(kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz + fx = dfx * (np.arange(nx) - 1. - nx//2) + fy = dfy * (np.arange(ny) - 1. - ny//2) + fz = dfz * (np.arange(nz) - 1. - nz//2) + + vkx = np.fft.fftn(vx) * dx * dy * dz + vky = np.fft.fftn(vy) * dx * dy * dz + vkz = np.fft.fftn(vz) * dx * dy * dz + + # shift to kxi=0 is at the center + vkx = np.fft.fftshift(vkx) + vky = np.fft.fftshift(vky) + vkz = np.fft.fftshift(vkz) + + #for k in range(nz): + # for j in range(ny): + # for i in range(nx): + # ff = (fx[i]**2 + fy[j]**2 + fz[k]**2) + # fi = np.where(ff > 1.e-8, 1./ff, 0.) + # # subtract expansion k.vk + # fdv = fx[i] * vkx[i, j, k] + fy[j] * vky[i, j, k] + fz[k] * vkz[i, j, k] + # vkx -= fdv * fx[i] * fi + # vky -= fdv * fy[j] * fi + # vkz -= fdv * fz[k] * fi + + fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 + fi = np.where(fi > 1.e-8, 1./fi, 0.) + + fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi + vkx -= fdv * fx[:,None,None] + vky -= fdv * fy[None,:,None] + vkz -= fdv * fz[None,None,:] + del(fi, fdv) + + # shift back to original order + vkx = np.fft.ifftshift(vkx) + vky = np.fft.ifftshift(vky) + vkz = np.fft.ifftshift(vkz) + + # inverse FFT + vx = np.fft.ifftn(vkx).real * dfx * dfy * dfz + vy = np.fft.ifftn(vky).real * dfx * dfy * dfz + vz = np.fft.ifftn(vkz).real * dfx * dfy * dfz + + # renormalize total velocity + vtot = np.sqrt(vx**2 + vy**2 + vz**2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1,2:-2,2:-2,2:-2].set(jnp.array(vx)) + u = u.at[2,2:-2,2:-2,2:-2].set(jnp.array(vy)) + u = u.at[3,2:-2,2:-2,2:-2].add(jnp.array(vz)) + u = u.at[4].add(p0) + + elif mode == 'BlastWave': # Kelvin-Helmholtz instability + """ Stone Gardiner 2009 without B """ + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + db = 1. + pb = 0.1 + + pc = 1.e2 # central region + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + qL = (qLx + qLy + qLz)/3. + + #p0 = jnp.ones([nx, ny, nz]) * pb + RR = jnp.sqrt((xc[:,None,None] - xc[nx//2])**2 + + (yc[None,:,None] - yc[ny//2])**2 + + (zc[None,None,:] - zc[nz//2])**2) + p0 = jnp.where(RR > 0.05 * qL, pb, pc) + #for k in range(nz): + # for j in range(ny): + # for i in range(nx): + # RR = jnp.sqrt((xc[i] - 0.5 * qLx)**2 + (yc[j] - 0.5 * qLy)**2 + (zc[k] - 0.5 * qLz)**2) + # if RR < 0.1 * qL: + # p0 = p0.at[i,j,k].set(pc) + + u = u.at[0].set(db) + u = u.at[1].set(0.) + u = u.at[2].set(0.) + u = u.at[3].set(0.) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p0) + + elif mode == 'sound_wave': # sound wave + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + gamma = 1.666666666666667 + d0 = 1. + cs = 2. + p0 = cs**2 * d0/gamma + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + XC = xc + qL = (xc[1] - xc[0]) * nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + XC = yc + qL = (yc[1] - yc[0]) * ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + XC = zc + qL = (zc[1] - zc[0]) * nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + kk = 2. * jnp.pi / qL + _u = _u.at[0,2:-2].set(d0 * (1. + 1.e-3 * jnp.sin(kk * XC[:, None, None]))) + _u = _u.at[iX].set((_u[0] - d0) * cs /d0) + _u = _u.at[4].set(p0 + cs**2 * (_u[0] - d0) ) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + + elif mode == 'c_discon': # tangent discontinuity + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + p0 = 1. + vy0 = 0.1 + if direc == 'x': + iX, iY, iZ = 1, 2, 3 + XC = xc + qL = (xc[1] - xc[0]) * nx + _u = jnp.zeros_like(u) + elif direc == 'y': + iX, iY, iZ = 2, 3, 1 + XC = yc + qL = (yc[1] - yc[0]) * ny + _u = jnp.transpose(u, (0, 2, 3, 1)) + if direc == 'z': + iX, iY, iZ = 3, 1, 2 + XC = zc + qL = (zc[1] - zc[0]) * nz + _u = jnp.transpose(u, (0, 3, 1, 2)) + + _u = _u.at[0].set(d0) + _u = _u.at[iY, 2:-2].set(vy0 * scipy.special.erf(0.5 * XC[:, None, None] / jnp.sqrt(0.1))) + _u = _u.at[4].set(p0) + + if direc == 'x': + u = _u + elif direc == 'y': + u = jnp.transpose(_u, (0, 3, 1, 2)) + elif direc == 'z': + u = jnp.transpose(_u, (0, 2, 3, 1)) + + return u + +@partial(jit, static_argnums=(3, 4, 5, 6, 7, 8, 9)) +def init_multi_HD(xc, yc, zc, numbers=10000, k_tot=10, init_key=2022, num_choise_k=2, + if_renorm=False, umax=1.e4, umin=1.e-8): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + + def _pass(carry): + return carry + + def select_A(carry): + def _func(carry): + carry = jnp.abs(carry) + return carry + + cond, value = carry + value = lax.cond(cond == 1, _func, _pass, value) + return cond, value + + def select_W(carry): + def _window(carry): + xx, val, xL, xR, trns = carry + val = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + return xx, val, xL, xR, trns + + cond, value, xx, xL, xR, trns = carry + + carry = xx, value, xL, xR, trns + xx, value, xL, xR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, xL, xR, trns + + def renormalize(carry): + def _norm(carry): + u, key = carry + u -= jnp.min(u, axis=1, keepdims=True) # positive value + u /= jnp.max(u, axis=1, keepdims=True) # normalize + + key, subkey = random.split(key) + m_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + m_val = jnp.exp(m_val) + key, subkey = random.split(key) + b_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + b_val = jnp.exp(b_val) + return u * m_val[:, None] + b_val[:, None], key + + cond, u, key = carry + carry = u, key + u, key = lax.cond(cond==True, _norm, _pass, carry) + return cond, u, key + + assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + key = random.PRNGKey(init_key) + + selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) + kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + amp = random.uniform(key, shape=[numbers, k_tot, 1]) + + key, subkey = random.split(key) + + phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) + _u = jnp.sum(_u, axis=1) + + # perform absolute value function + cond = random.choice(key, 2, p=jnp.array([0.9, 0.1]), shape=([numbers])) + carry = (cond, _u) + + cond, _u = vmap(select_A, 0, 0)(carry) + sgn = random.choice(key, a=jnp.array([1, -1]), shape=([numbers, 1])) + _u *= sgn # random flip of signature + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + mask = jnp.ones_like(_xc) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, xL, xR, trns + cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) + + _u *= mask + + carry = if_renorm, _u, key + _, _u, _ = renormalize(carry) # renormalize value between a given values + + return _u[...,None,None] + +#@partial(jit, static_argnums=(3, 4, 5, 6)) +def init_multi_HD_shock(xc, yc, zc, numbers=10000, init_key=2022, umax=1.e4, umin=1.e-8): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def select_var(carry): + def _func(carry): + vmin, vmax = carry + return jnp.log(vmin), jnp.log(vmax) + + def _pass(carry): + return carry + + vmin, vmax = carry + vmin, vmax = lax.cond(vmin > 0., _func, _pass, carry) + return vmin, vmax + + nx = xc.shape[0] + + carry = umin, umax + u_min, u_max = select_var(carry) + + key = random.PRNGKey(init_key) + QLs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) + QLs = jnp.exp(QLs) + key, subkey = random.split(key) + QRs = random.uniform(key, shape=([numbers, 1]), minval=u_min, maxval=u_max) + QRs = jnp.exp(QRs) + + nx0s = nx * random.uniform(key, shape=([numbers, 1]), minval=0.25, maxval=0.75) + nx0s = nx0s.astype(int) + + u = jnp.arange(xc.shape[0]) + u = jnp.tile(u, (numbers, 1)) + + u = jax.vmap(jnp.where, axis_name='i')(u < nx0s, QLs, QRs) + return u[...,None,None] + +#@partial(jit, static_argnums=(4, 5, 6, 7, 8, 9)) +def init_multi_HD_KH(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, dkMx=2., kmax=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_KH_init(u, dk, kk): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0_u = 2./(dk + 1.) + d0_d = dk * d0_u + d0 = 0.5 * (d0_u + d0_d) + ux = 1. + cs = ux/M0 + p0 = cs**2 * d0/gamma + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + qLx = dx * nx + qLy = dy * ny + kx = kk * 2. * jnp.pi / qLx + dl = 5.e-3 * qLy + # (numbers, nx) + bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 + + vx = jnp.zeros([nx, ny, nz]) + dd = jnp.zeros([nx, ny, nz]) + for i in range(nx): + _vx = jnp.where(yc > bound[i], ux, -ux) + _dd = jnp.where(yc > bound[i], d0_u, d0_d) + vx = vx.at[i, :, :].set(_vx[:, None]) + dd = dd.at[i, :, :].set(_dd[:, None]) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2].set(0.) + u = u.at[3].add(0.) + u = u.at[4].add(p0) + return u + + # create random density ratio + key = random.PRNGKey(init_key) + dk = random.uniform(key, shape=([numbers, 1]), minval=1. / dkMx, maxval=dkMx) + #create random wave-numbers + key, subkey = random.split(key) + kk = random.randint(key, shape=([numbers, 1]), minval=1, maxval=kmax) + print('vmap...') + u = jax.vmap(__create_KH_init, axis_name='i')(u, dk, kk) + + return u + +#@partial(jit, static_argnums=(4, 5, 6, 7, 8)) +def init_multi_HD_2DTurb(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_2DTurb_init(u, keys): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random velocity field + vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(keys) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[2]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None, None] + ky * yc[None, :, None] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + + del (kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy = 1. / qLx, 1. / qLy + fx = dfx * (jnp.arange(nx) - 1. - nx // 2) + fy = dfy * (jnp.arange(ny) - 1. - ny // 2) + + vkx = jnp.fft.fftn(vx) * dx * dy + vky = jnp.fft.fftn(vy) * dx * dy + + # shift to kxi=0 is at the center + vkx = jnp.fft.fftshift(vkx) + vky = jnp.fft.fftshift(vky) + + fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 + fi = jnp.where(fi > 1.e-8, 1. / fi, 0.) + + fdv = (fx[:, None, None] * vkx + fy[None, :, None] * vky) * fi + vkx -= fdv * fx[:, None, None] + vky -= fdv * fy[None, :, None] + del (fi, fdv) + + # shift back to original order + vkx = jnp.fft.ifftshift(vkx) + vky = jnp.fft.ifftshift(vky) + + # inverse FFT + vx = jnp.fft.ifftn(vkx).real * dfx * dfy + vy = jnp.fft.ifftn(vky).real * dfx * dfy + + # renormalize total velocity + vtot = jnp.sqrt(vx ** 2 + vy ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[4].add(p0) + return u + + key = random.PRNGKey(init_key) + keys = random.randint(key, [numbers,], minval=0, maxval=10000000) + u = jax.vmap(__create_2DTurb_init, axis_name='i')(u, keys) + + return u + +def init_multi_HD_2DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, + dMx=1.e1, TMx=1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert zc.shape[0] == 1, 'nz is assumed to be 1!!' + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, val, xL, xR, yL, yR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + val = x_win[:, None] * y_win[None, :] + return xx, yy, val, xL, xR, yL, yR, trns + + cond, value, xx, yy, xL, xR, yL, yR, trns = carry + + carry = xx, yy, value, xL, xR, yL, yR, trns + xx, yy, value, xL, xR, yL, yR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, yy, xL, xR, yL, yR, trns + + def __create_2DRand_init(u, d0, T0, delD, delP, keys): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + + p0 = d0 * T0 + cs = jnp.sqrt(T0 * gamma) + u0 = M0 * cs + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + + qLx = dx * nx + qLy = dy * ny + + ## random velocity field + d, p, vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(keys) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[4]) # (vi, k) + + uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + kdx = kx * xc[:, None, None] + ky * yc[None, :, None] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + p += uk * jnp.sin(kdx + phs[2]) + d += uk * jnp.sin(kdx + phs[3]) + + del (kdx, uk, phs) + + # renormalize total velocity + vtot = jnp.sqrt(vx ** 2 + vy ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + #d = d0 + delD * d / jnp.abs(d).mean() + #p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1. + delD * d / jnp.abs(d).mean()) + p = p0 * (1. + delP * p / jnp.abs(p).mean()) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + return u + + key = random.PRNGKey(init_key) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + key, subkey = random.split(key) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + key, subkey = random.split(key) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + key, subkey = random.split(key) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + + key, subkey = random.split(key) + keys = random.randint(key, shape=([numbers, ]), minval=0, maxval=10000000) + u = jax.vmap(__create_2DRand_init, axis_name='i')(u, d0, T0, delD, delP, keys) + + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) # add batch + _yc = jnp.repeat(yc[None, :], numbers, axis=0) # add batch + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns + cond, mask, _xc, _yc, xL, xR, yL, yR, trns = vmap(select_W, 0, 0)(carry) + + u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, None]) + u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, None])) + u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, None])) + + return u + +def init_multi_HD_3DTurb(u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def __create_3DTurb_init(u, keys): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + d0 = 1. + cs = 1./M0 + u0 = 1. # fixed + p0 = cs ** 2 * d0 / gamma + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(keys) + + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + + del(kdx, uk, phs) + + # Helmholtz decomposition to subtract expansion: k.vk + dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz + fx = dfx * (jnp.arange(nx) - 1. - nx//2) + fy = dfy * (jnp.arange(ny) - 1. - ny//2) + fz = dfz * (jnp.arange(nz) - 1. - nz//2) + + vkx = jnp.fft.fftn(vx) * dx * dy * dz + vky = jnp.fft.fftn(vy) * dx * dy * dz + vkz = jnp.fft.fftn(vz) * dx * dy * dz + + # shift to kxi=0 is at the center + vkx = jnp.fft.fftshift(vkx) + vky = jnp.fft.fftshift(vky) + vkz = jnp.fft.fftshift(vkz) + + fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 + fi = jnp.where(fi > 1.e-8, 1./fi, 0.) + + fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi + vkx -= fdv * fx[:,None,None] + vky -= fdv * fy[None,:,None] + vkz -= fdv * fz[None,None,:] + del(fi, fdv) + + # shift back to original order + vkx = jnp.fft.ifftshift(vkx) + vky = jnp.fft.ifftshift(vky) + vkz = jnp.fft.ifftshift(vkz) + + # inverse FFT + vx = jnp.fft.ifftn(vkx).real * dfx * dfy * dfz + vy = jnp.fft.ifftn(vky).real * dfx * dfy * dfz + vz = jnp.fft.ifftn(vkz).real * dfx * dfy * dfz + + # renormalize total velocity + vtot = jnp.sqrt(vx**2 + vy**2 + vz**2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + + u = u.at[0].set(d0) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) + u = u.at[4].add(p0) + return u + + key = random.PRNGKey(init_key) + keys = random.randint(key, [numbers,], minval=0, maxval=10000000) + u = jax.vmap(__create_3DTurb_init, axis_name='i')(u, keys) + + return u + +def init_multi_HD_3DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, + dMx=1.e1, TMx=1.e1): + """ + :param xc: cell center coordinate + :param mode: initial condition + :return: 1D scalar function u at cell center + """ + assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + + def _pass(carry): + return carry + + def select_W(carry): + def _window(carry): + xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns = carry + x_win = 0.5 * (jnp.tanh((xx - xL) / trns) - jnp.tanh((xx - xR) / trns)) + y_win = 0.5 * (jnp.tanh((yy - yL) / trns) - jnp.tanh((yy - yR) / trns)) + z_win = 0.5 * (jnp.tanh((zz - zL) / trns) - jnp.tanh((zz - zR) / trns)) + val = x_win[:, None, None] * y_win[None, :, None] * z_win[None, None, :] + return xx, yy, zz, val, xL, xR, yL, yR, zL, zR, trns + + cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns = carry + + carry = xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns + xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns = lax.cond(cond == 1, _window, _pass, carry) + return cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns + + def __create_3DRand_init(u, d0, T0, delD, delP, keys): + nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] + + p0 = d0 * T0 + cs = jnp.sqrt(T0 * gamma) + u0 = M0 * cs + + dx = xc[1] - xc[0] + dy = yc[1] - yc[0] + dz = zc[1] - zc[0] + + qLx = dx * nx + qLy = dy * ny + qLz = dz * nz + + ## random velocity field + d, p, vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), \ + jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + + key = random.PRNGKey(keys) + kx0 = jnp.pi * 2. / qLx + ky0 = jnp.pi * 2. / qLy + kz0 = jnp.pi * 2. / qLz + + for k in range(-k_tot, k_tot + 1): + kz = kz0 * k # from 1 to k_tot + for j in range(-k_tot, k_tot + 1): + ky = ky0 * j # from 1 to k_tot + for i in range(-k_tot, k_tot + 1): + kx = kx0 * i # from 1 to k_tot + if i * j * k == 0: # avoiding uniform velocity + continue + # random phase + key, subkey = random.split(key) + phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + vx += uk * jnp.sin(kdx + phs[0]) + vy += uk * jnp.sin(kdx + phs[1]) + vz += uk * jnp.sin(kdx + phs[2]) + p += uk * jnp.sin(kdx + phs[3]) + d += uk * jnp.sin(kdx + phs[4]) + + del(kdx, uk, phs) + + # renormalize total velocity + vtot = jnp.sqrt(vx ** 2 + vy ** 2 + vz ** 2).mean() + vx *= u0 / vtot + vy *= u0 / vtot + vz *= u0 / vtot + #d = d0 + delD * d / jnp.abs(d).mean() + #p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1. + delD * d / jnp.abs(d).mean()) + p = p0 * (1. + delP * p / jnp.abs(p).mean()) + + u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) + u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + return u + + key = random.PRNGKey(init_key) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + key, subkey = random.split(key) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + key, subkey = random.split(key) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + key, subkey = random.split(key) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + + key, subkey = random.split(key) + keys = random.randint(key, [numbers,], minval=0, maxval=10000000) + u = jax.vmap(__create_3DRand_init, axis_name='i')(u, d0, T0, delD, delP, keys) + + # perform window function + key, subkey = random.split(key) + cond = random.choice(key, 2, p=jnp.array([0.5, 0.5]), shape=([numbers])) + mask = jnp.ones([numbers, xc.shape[0], yc.shape[0], zc.shape[0]]) + xL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + xR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + yL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + yR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + key, subkey = random.split(key) + zL = random.uniform(key, shape=([numbers]), minval=0.1, maxval=0.45) + zR = random.uniform(key, shape=([numbers]), minval=0.55, maxval=0.9) + _xc = jnp.repeat(xc[None, :], numbers, axis=0) + _yc = jnp.repeat(yc[None, :], numbers, axis=0) + _zc = jnp.repeat(zc[None, :], numbers, axis=0) + trns = 0.01 * jnp.ones_like(cond) + carry = cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns + cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns = vmap(select_W, 0, 0)(carry) + + u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, :]) + u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, :])) + u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, :])) + + return u + +def bc(u, dx, Ncell, mode='periodic'): + _u = jnp.zeros(Ncell+4) # because of 2nd-order precision in space + _u = _u.at[2:Ncell+2].set(u) + if mode=='periodic': # periodic boundary condition + _u = _u.at[0:2].set(u[-2:]) # left hand side + _u = _u.at[Ncell + 2:Ncell + 4].set(u[0:2]) # right hand side + elif mode=='reflection': + _u = _u.at[0].set(- u[3]) # left hand side + _u = _u.at[1].set(- u[2]) # left hand side + _u = _u.at[-2].set(- u[-3]) # right hand side + _u = _u.at[-1].set(- u[-4]) # right hand side + elif mode=='copy': + _u = _u.at[0].set(u[3]) # left hand side + _u = _u.at[1].set(u[2]) # left hand side + _u = _u.at[-2].set(u[-3]) # right hand side + _u = _u.at[-1].set(u[-4]) # right hand side + + return _u + +def bc_2D(_u, mode='trans'): + Nx, Ny = _u.shape + u = jnp.zeros([Nx + 4, Ny + 4]) # because of 2nd-order precision in space + u = u.at[2:-2, 2:-2].set(_u) + Nx += 2 + Ny += 2 + + if mode=='periodic': # periodic boundary condition + # left hand side + u = u.at[0:2, 2:-2].set(u[Nx-2:Nx, 2:-2]) # x + u = u.at[2:-2, 0:2].set(u[2:-2, Ny-2:Ny]) # y + # right hand side + u = u.at[Nx:Nx+2, 2:-2].set(u[2:4, 2:-2]) + u = u.at[2:-2, Ny:Ny+2].set(u[2:-2, 2:4]) + elif mode=='trans': # periodic boundary condition + # left hand side + u = u.at[0, 2:-2].set(u[3, 2:-2]) # x + u = u.at[2:-2, 0].set(u[2:-2, 3]) # y + u = u.at[1, 2:-2].set(u[2, 2:-2]) # x + u = u.at[2:-2, 1].set(u[2:-2, 2]) # y + # right hand side + u = u.at[-2, 2:-2].set(u[-3, 2:-2]) + u = u.at[2:-2, -2].set(u[2:-2, -3]) + u = u.at[-1, 2:-2].set(u[-4, 2:-2]) + u = u.at[2:-2, -1].set(u[2:-2, -4]) + elif mode=='Neumann': # periodic boundary condition + # left hand side + u = u.at[0, 2:-2].set(0.) # x + u = u.at[2:-2, 0].set(0.) # y + u = u.at[1, 2:-2].set(0.) # x + u = u.at[2:-2, 1].set(0.) # y + # right hand side + u = u.at[-2, 2:-2].set(0.) + u = u.at[2:-2, -2].set(0.) + u = u.at[-1, 2:-2].set(0.) + u = u.at[2:-2, -1].set(0.) + return u + +def bc_HD(u, mode): + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + if mode=='periodic': # periodic boundary condition + # left hand side + u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx-2:Nx, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0:2, 2:-2].set(u[:, 2:-2, Ny-2:Ny, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0:2].set(u[:, 2:-2, 2:-2, Nz-2:Nz]) # z + # right hand side + u = u.at[:, Nx:Nx+2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, Ny:Ny+2, 2:-2].set(u[:, 2:-2, 2:4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, Nz:Nz+2].set(u[:, 2:-2, 2:-2, 2:4]) + elif mode=='trans': # periodic boundary condition + # left hand side + u = u.at[:, 0, 2:-2, 2:-2].set(u[:, 3, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.at[:, 1, 2:-2, 2:-2].set(u[:, 2, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + # right hand side + u = u.at[:, -2, 2:-2, 2:-2].set(u[:, -3, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.at[:, -1, 2:-2, 2:-2].set(u[:, -4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + elif mode=='KHI': # x: periodic, y, z : trans + # left hand side + u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2:Nx, 2:-2, 2:-2]) # x + u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + # right hand side + u = u.at[:, Nx:Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + return u + +def bc_HD_vis(u, if_periodic=True): # for viscosity + """ + for the moment, assuming periodic/copy boundary + seemingly, copy boundary does not work well... + """ + _, Nx, Ny, Nz = u.shape + Nx -= 2 + Ny -= 2 + Nz -= 2 + + if if_periodic: + u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT + else: # trans + u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, 4:2, 4:2, 2:-2]) # xByT + u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB + u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB + u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT + u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB + u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB + u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT + u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT + + return u + +def VLlimiter(a, b, c, alpha=2.): + return jnp.sign(c)\ + *(0.5 + 0.5*jnp.sign(a*b))\ + *jnp.minimum(alpha*jnp.minimum(jnp.abs(a), jnp.abs(b)), jnp.abs(c)) + +def limiting(u, Ncell, if_second_order): + # under construction + duL = u[1:Ncell + 3] - u[0:Ncell + 2] + duR = u[2:Ncell + 4] - u[1:Ncell + 3] + duM = (u[2:Ncell + 4] - u[0:Ncell + 2])*0.5 + gradu = VLlimiter(duL, duR, duM) * if_second_order + # -1:Ncell + #uL, uR = jnp.zeros(Ncell+4), jnp.zeros(Ncell+4) + uL, uR = jnp.zeros_like(u), jnp.zeros_like(u) + uL = uL.at[1:Ncell+3].set(u[1:Ncell+3] - 0.5*gradu) # left of cell + uR = uR.at[1:Ncell+3].set(u[1:Ncell+3] + 0.5*gradu) # right of cell + return uL, uR + +def limiting_HD(u, if_second_order): + nd, nx, ny, nz = u.shape + uL, uR = u, u + nx -= 4 + + duL = u[:, 1:nx + 3, :, :] - u[:, 0:nx + 2, :, :] + duR = u[:, 2:nx + 4, :, :] - u[:, 1:nx + 3, :, :] + duM = (u[:, 2:nx + 4, :, :] - u[:, 0:nx + 2, :, :]) * 0.5 + gradu = VLlimiter(duL, duR, duM) * if_second_order + # -1:Ncell + uL = uL.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] - 0.5*gradu) # left of cell + uR = uR.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] + 0.5*gradu) # right of cell + + uL = jnp.where(uL[0] > 0., uL, u) + uL = jnp.where(uL[4] > 0., uL, u) + uR = jnp.where(uR[0] > 0., uR, u) + uR = jnp.where(uR[4] > 0., uR, u) + + return uL, uR + +def save_data(u, xc, i_save, save_dir, dt_save=None, if_final=False): + if if_final: + jnp.save(save_dir+'/x_coordinate', xc) + # + tc = jnp.arange(i_save+1)*dt_save + jnp.save(save_dir+'/t_coordinate', tc) + # + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + else: + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + +def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): + if if_final: + jnp.save(save_dir+'/x_coordinate', xc) + jnp.save(save_dir+'/y_coordinate', yc) + jnp.save(save_dir+'/z_coordinate', zc) + # + tc = jnp.arange(i_save+1)*dt_save + jnp.save(save_dir+'/t_coordinate', tc) + # + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + else: + flnm = save_dir+'/Data_'+str(i_save).zfill(4) + jnp.save(flnm, u) + +def Courant(u, dx): + stability_adv = dx/(jnp.max(jnp.abs(u)) + 1.e-8) + return stability_adv + +def Courant_diff(dx, epsilon=1.e-3): + stability_dif = 0.5*dx**2/(epsilon + 1.e-8) + return stability_dif + +def Courant_diff_2D(dx, dy, epsilon=1.e-3): + stability_dif_x = 0.5*dx**2/(epsilon + 1.e-8) + stability_dif_y = 0.5*dy**2/(epsilon + 1.e-8) + return jnp.min(jnp.array([stability_dif_x, stability_dif_y])) + +def Courant_HD(u, dx, dy, dz, gamma): + cs = jnp.sqrt(gamma*u[4]/u[0]) # sound velocity + stability_adv_x = dx/(jnp.max(cs + jnp.abs(u[1])) + 1.e-8) + stability_adv_y = dy/(jnp.max(cs + jnp.abs(u[2])) + 1.e-8) + stability_adv_z = dz/(jnp.max(cs + jnp.abs(u[3])) + 1.e-8) + stability_adv = jnp.min(jnp.array([stability_adv_x, stability_adv_y, stability_adv_z])) + return stability_adv + +def Courant_vis_HD(dx, dy, dz, eta, zeta): + #visc = jnp.max(jnp.array([eta, zeta])) + visc = 4. / 3. * eta + zeta # maximum + stability_dif_x = 0.5*dx**2/(visc + 1.e-8) + stability_dif_y = 0.5*dy**2/(visc + 1.e-8) + stability_dif_z = 0.5*dz**2/(visc + 1.e-8) + stability_dif = jnp.min(jnp.array([stability_dif_x, stability_dif_y, stability_dif_z])) + return stability_dif + From 528779eeffa37e2aade020dcca4aa5bc1bd95bc9 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Wed, 24 Aug 2022 15:51:19 +0200 Subject: [PATCH 023/137] mode name consistency by MT@24082022 --- .../data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py | 4 ++-- .../CompressibleFluid/config/args/3D_Multi_TurbM1.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py index 3d1fa4b..a734e54 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -687,7 +687,7 @@ def HLLC(QL, QR, direc): Q = init_multi_HD_KH(Q, xc, yc, zc, numbers=cfg.args.numbers, init_key=cfg.args.init_key, M0=cfg.args.M0, dkMx=cfg.args.dkMx, gamma = cfg.args.gamma) - elif cfg.args.init_mode_Multi == '2DTurbs': + elif cfg.args.init_mode_Multi == '2D_Turbs': print('now we are coming into 2DTurbs......') Q = init_multi_HD_2DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, init_key=cfg.args.init_key, @@ -698,7 +698,7 @@ def HLLC(QL, QR, direc): Q = init_multi_HD_2DRand(Q, xc, yc, zc, numbers=cfg.args.numbers, init_key=cfg.args.init_key, M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) - elif cfg.args.init_mode_Multi == '3DTurbs': + elif cfg.args.init_mode_Multi == '3D_Turbs': print('now we are coming into 3DTurbs......') Q = init_multi_HD_3DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, init_key=cfg.args.init_key, diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml index 756a95a..a284aac 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml @@ -21,7 +21,7 @@ if_show: 1 show_steps: 400 p_floor: 1.e-4 numbers: 20 -init_mode_Multi: '3DTurbs' +init_mode_Multi: '3D_Turbs' M0 : 1. k_tot: 4 init_key: 2022 From bbe86bea0e0f92ee29850407807813d95b61e4d2 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Tue, 30 Aug 2022 10:50:59 +0200 Subject: [PATCH 024/137] debug metrics.py of savez part by adding cpu() by MT@30082022 --- pdebench/models/metrics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 1629a2a..82d04d0 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -471,7 +471,7 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min # plt.savefig(filename) filename = model_name + 'mse_time.npz' - np.savez(filename, t=torch.arange(initial_step,yy.shape[-2]), + np.savez(filename, t=torch.arange(initial_step,yy.shape[-2]).cpu(), mse=val_l2_time[initial_step:].detach().cpu()) return err_MSE, err_nMSE, err_CSV, err_Max, err_BD, err_F @@ -699,4 +699,4 @@ def inverse_metrics(u0,x,pred_u0,y): ,'fftl3loss_hi_pred_u0': fftl3loss_hi_pred_u0 } - return metric \ No newline at end of file + return metric From fbf93a96bb98c1edba2b82dd695b4e771d59a044 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Wed, 28 Sep 2022 08:26:31 +0200 Subject: [PATCH 025/137] Modified README to add temporary URL to the paper and the bibtex citation --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cb41b5b..031f167 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ By : Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert This repository contains the code for the paper: -PDEBench: An Extensive Benchmark for Scientific Machine Learning +[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://openreview.net/pdf?id=dh_MkX0QfrK) In this work, we provide a diverse and comprehensive benchmark for scientific machine learning, including a variety of challenging and representative range of physical problems. This repository consists of the codes used to generate the datasets, upload and download the datasets from the data repository, train and evaluate different machine learning models as baseline. @@ -225,6 +225,15 @@ Additionally, the pretrained models are also available to be downloaded [here](h ## Citations ``` +@inproceedings{PDEBench2022, +author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, +title = {{PDEBench: An Extensive Benchmark for Scientific Machine Learning}}, +year = {2022}, +booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2022) Track on Datasets and Benchmarks], +url = {https://doi.org/10.18419/darus-2986} +} + + @data{darus-2986_2022, author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, publisher = {DaRUS}, From 29df54b549fab454b39dcfd5eea80e60a882b3c9 Mon Sep 17 00:00:00 2001 From: Mathias Niepert Date: Fri, 14 Oct 2022 10:45:09 +0200 Subject: [PATCH 026/137] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 031f167..eb3a0a0 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # PDEBench -By : Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert +Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert. -This repository contains the code for the paper: +This repository contains the code for the PDEBench paper [PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://openreview.net/pdf?id=dh_MkX0QfrK) -In this work, we provide a diverse and comprehensive benchmark for scientific machine learning, including a variety of challenging and representative range of physical problems. -This repository consists of the codes used to generate the datasets, upload and download the datasets from the data repository, train and evaluate different machine learning models as baseline. -PDEBench features a much wider range of PDEs than existing approaches including realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial or boundary conditions and model parameters, and extensible source codes. +PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including a variety of challenging and representative range of physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing approaches including realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial or boundary conditions and model parameters, and extensible source codes. ## Dataset -PDEBench Dataset +We also provide datasets and pretrained machine learning models. + +PDEBench Datasets: https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986 -PDEBench Pre-Trained Models +PDEBench Pre-Trained Models: https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987 From 9d981438b2700870503a9310feac8f0f91980423 Mon Sep 17 00:00:00 2001 From: Mathias Niepert Date: Fri, 14 Oct 2022 10:45:47 +0200 Subject: [PATCH 027/137] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb3a0a0..dbb0a03 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repository contains the code for the PDEBench paper PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including a variety of challenging and representative range of physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing approaches including realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial or boundary conditions and model parameters, and extensible source codes. -## Dataset +## Datasets and Pretrained Models We also provide datasets and pretrained machine learning models. From 6b178f7c43b59636578bb09a8fcb4d46189c1521 Mon Sep 17 00:00:00 2001 From: Mathias Niepert Date: Fri, 14 Oct 2022 10:48:48 +0200 Subject: [PATCH 028/137] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dbb0a03..1ee6e56 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Created and maintained by Makoto Takamoto ` Date: Fri, 14 Oct 2022 10:49:24 +0200 Subject: [PATCH 029/137] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ee6e56..8c589ad 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Created and maintained by Makoto Takamoto ` Date: Fri, 14 Oct 2022 14:09:03 +0200 Subject: [PATCH 030/137] Add files via upload --- pdebench_examples.PNG | Bin 0 -> 353327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pdebench_examples.PNG diff --git a/pdebench_examples.PNG b/pdebench_examples.PNG new file mode 100644 index 0000000000000000000000000000000000000000..85f659e2467776f0b277d496370597678b6774b1 GIT binary patch literal 353327 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%0A{bdig! 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%0AR62q>SXMly>-uh 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

mlN{! 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 yA6J3@IYi~uy#DuZ5Y7L87U(O{|KkMg Date: Fri, 14 Oct 2022 14:10:17 +0200 Subject: [PATCH 031/137] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8c589ad..ccc419f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ This repository contains the code for the PDEBench paper PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing benchmarks and included realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was crated to make the source code extensible and we invite active participation to improve and extent the benchmark. + +![Visualizations of some PDE problems covered by the benchmark.](https://github.com/pdebench/PDEBench/blob/main/pdebench_examples.PNG) + + ## Datasets and Pretrained Models We also provide datasets and pretrained machine learning models. From 5e7b83bbcff1e5175869ca02ea1064e266aec72a Mon Sep 17 00:00:00 2001 From: Mathias Niepert Date: Fri, 14 Oct 2022 18:18:20 +0200 Subject: [PATCH 032/137] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ccc419f..51c514f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ # PDEBench -Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert. - This repository contains the code for the PDEBench paper [PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://openreview.net/pdf?id=dh_MkX0QfrK) PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing benchmarks and included realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was crated to make the source code extensible and we invite active participation to improve and extent the benchmark. - ![Visualizations of some PDE problems covered by the benchmark.](https://github.com/pdebench/PDEBench/blob/main/pdebench_examples.PNG) +Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert. + ## Datasets and Pretrained Models We also provide datasets and pretrained machine learning models. From 45e490c3c7a3659d16d24731f0ba7d3850836914 Mon Sep 17 00:00:00 2001 From: Mathias Niepert Date: Fri, 14 Oct 2022 18:19:29 +0200 Subject: [PATCH 033/137] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51c514f..ead854d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PDEBench -This repository contains the code for the PDEBench paper +The code repository for the NeurIPS 2022 paper [PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://openreview.net/pdf?id=dh_MkX0QfrK) PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing benchmarks and included realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was crated to make the source code extensible and we invite active participation to improve and extent the benchmark. From 99fbb7ae7ae33fc927ac1c3df8ee97e9c7601dd3 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Tue, 18 Oct 2022 16:18:29 +0200 Subject: [PATCH 034/137] Updated the downloader script according to the updated easyDataverse library --- pdebench/data_download/download.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/pdebench/data_download/download.py b/pdebench/data_download/download.py index 8d15ed6..5e3976f 100644 --- a/pdebench/data_download/download.py +++ b/pdebench/data_download/download.py @@ -5,13 +5,11 @@ from omegaconf import DictConfig import logging -from pyDataverse.api import NativeApi, DataAccessApi -from pyDaRUS import Dataset -from easyDataverse.core.downloader import download_files +from pyDataverse.api import NativeApi +from easyDataverse import Dataset log = logging.getLogger(__name__) - @hydra.main(config_path="config/", config_name="config") def main(config: DictConfig): """ @@ -24,6 +22,7 @@ def main(config: DictConfig): # Change to original working directory os.chdir(get_original_cwd()) + os.environ["DATAVERSE_URL"] = config.args.dataverse_url # Extract dataset from the given DOI dataset = Dataset() @@ -31,7 +30,6 @@ def main(config: DictConfig): # Extract file list contained in the dataset api = NativeApi(config.args.dataverse_url) - data_api = DataAccessApi(config.args.dataverse_url) dv_dataset = api.get_dataset(config.args.dataset_id) files_list = dv_dataset.json()["data"]["latestVersion"]["files"] @@ -39,10 +37,17 @@ def main(config: DictConfig): files = [] for i, file in enumerate(files_list): if config.args.filename in file["dataFile"]["filename"]: - files.append(file) + files.append(file["dataFile"]["filename"]) # Download the files - download_files(data_api, dataset, files, os.path.abspath(config.args.data_folder)) + + dataset = Dataset.from_dataverse_doi( + doi=config.args.dataset_id, + dataverse_url=config.args.dataverse_url, + filenames=files, + filedir=config.args.data_folder, + ) + if __name__ == "__main__": From 044a823d48d5d2d13fbc4928c6bb5e12ddff045e Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Fri, 28 Oct 2022 14:25:45 +0200 Subject: [PATCH 035/137] Changed target time step for 'single' mode prediction for consistency, changed validation target to exclude initial time steps --- pdebench/models/config/args/config_rdb.yaml | 2 +- pdebench/models/fno/train.py | 9 +++++---- pdebench/models/unet/train.py | 14 ++++++++------ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/pdebench/models/config/args/config_rdb.yaml b/pdebench/models/config/args/config_rdb.yaml index 1048098..d58fd4d 100644 --- a/pdebench/models/config/args/config_rdb.yaml +++ b/pdebench/models/config/args/config_rdb.yaml @@ -4,7 +4,7 @@ continue_training: False num_workers: 2 batch_size: 5 initial_step: 10 -t_train: 100 +t_train: 101 model_update: 10 filename: '2D_rdb_NA_NA' single_file: False diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index fde4af1..8ca4729 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -238,7 +238,7 @@ def run_training(if_training, if training_type in ['single']: x = xx[..., 0 , :] - y = yy[..., t_train:t_train+1 , :] + y = yy[..., t_train-1:t_train, :] pred = model(x, grid) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) @@ -279,12 +279,13 @@ def run_training(if_training, val_l2_step += loss.item() _batch = yy.size(0) - _yy = yy[..., :t_train, :] - val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() + _pred = pred[..., initial_step:t_train, :] + _yy = yy[..., initial_step:t_train, :] + val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if training_type in ['single']: x = xx[..., 0 , :] - y = yy[..., t_train:t_train+1 , :] + y = yy[..., t_train-1:t_train, :] pred = model(x, grid) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) diff --git a/pdebench/models/unet/train.py b/pdebench/models/unet/train.py index bd3ba50..9c37ec3 100644 --- a/pdebench/models/unet/train.py +++ b/pdebench/models/unet/train.py @@ -284,7 +284,7 @@ def run_training(if_training, if training_type in ['single']: x = xx[..., 0 , :] - y = yy[..., t_train:t_train+1 , :] + y = yy[..., t_train-1:t_train, :] pred = model(x.permute([0, 2, 1])).permute([0, 2, 1]) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) @@ -330,12 +330,13 @@ def run_training(if_training, val_l2_step += loss.item() _batch = yy.size(0) - _yy = yy[..., :t_train, :] - val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() + _pred = pred[..., initial_step:t_train, :] + _yy = yy[..., initial_step:t_train, :] + val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if training_type in ['single']: x = xx[..., 0 , :] - y = yy[..., t_train:t_train+1 , :] + y = yy[..., t_train-1:t_train, :] pred = model(x.permute([0, 2, 1])).permute([0, 2, 1]) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) @@ -451,8 +452,9 @@ def run_training(if_training, val_l2_step += loss.item() _batch = yy.size(0) - _yy = yy[..., :t_train, :] # if t_train is not -1 - val_l2_full += loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() + _pred = pred[..., initial_step:t_train, :] + _yy = yy[..., initial_step:t_train, :] # if t_train is not -1 + val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() if val_l2_full < loss_val_min: loss_val_min = val_l2_full From e6c74d702813b3ea1d9b24f3116cbe50767ef72d Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Fri, 28 Oct 2022 20:11:16 +0200 Subject: [PATCH 036/137] Fixed indentation in utils.py --- pdebench/models/fno/utils.py | 2 +- pdebench/models/unet/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 5a0c437..3e24d4a 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -254,7 +254,7 @@ def __init__(self, filename, X, Y = torch.meshgrid(x, y) self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - if len(idx_cfd)==5: # 3D + if len(idx_cfd)==5: # 3D self.data = np.zeros([idx_cfd[0]//reduced_batch, idx_cfd[2]//reduced_resolution, idx_cfd[3]//reduced_resolution, diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index f622015..d38c242 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -241,7 +241,7 @@ def __init__(self, filename, _data = np.transpose(_data, (0, 2, 3, 1)) self.data[...,3] = _data # batch, x, t, ch - if len(idx_cfd)==5: # 3D + if len(idx_cfd)==5: # 3D self.data = np.zeros([idx_cfd[0]//reduced_batch, idx_cfd[2]//reduced_resolution, idx_cfd[3]//reduced_resolution, From 17c138276c9c9505feddd69f671489d1266846e1 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Wed, 2 Nov 2022 10:00:17 +0100 Subject: [PATCH 037/137] Fixed the typo in the bib citation in the README.md file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ead854d..8cdb232 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ Additionally, the pretrained models are also available to be downloaded [here](h author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, title = {{PDEBench: An Extensive Benchmark for Scientific Machine Learning}}, year = {2022}, -booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2022) Track on Datasets and Benchmarks], +booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2022) Track on Datasets and Benchmarks}, url = {https://doi.org/10.18419/darus-2986} } From f8c8493692e3290f1435b99e4eae66f224938414 Mon Sep 17 00:00:00 2001 From: Raphael Leiteritz <48442682+leiterrl@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:57:23 +0100 Subject: [PATCH 038/137] remove duplicate entry from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8cdb232..2fa5a4f 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ The data generation codes are contained in [data_gen](./pdebench/data_gen): - `gen_diff_react.py` to generate the 2D diffusion-reaction data. - `gen_diff_sorp.py` to generate the 1D diffusion-sorption data. - `gen_radial_dam_break.py` to generate the 2D shallow-water data. -- `gen_radial_dam_break.py` to generate the 2D shallow-water data. - `gen_ns_incomp.py` to generate the 2D incompressible inhomogenous Navier-Stokes data. - `plot.py` to plot the generated data. - `uploader.py` to upload the generated data to the data repository. From 04f566dc4e0bfbe14368c923c7e423f57eba7efb Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Mon, 14 Nov 2022 11:23:53 +0100 Subject: [PATCH 039/137] Added initial draft of config documentation --- pdebench/models/config/config_docs.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 pdebench/models/config/config_docs.md diff --git a/pdebench/models/config/config_docs.md b/pdebench/models/config/config_docs.md new file mode 100644 index 0000000..f6b444a --- /dev/null +++ b/pdebench/models/config/config_docs.md @@ -0,0 +1,23 @@ +# Config Documentation + +This is the documentation of the config files that were used to generate the provided [pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). +Since the default config files for all problems are already provided, this file only provides the values for the arguments that need to be changed. +N/A values mean that the default values can be used. +The complete explanation of the arguments can be found in the [README file](./README.md) + +## 2D Shallow Water Equation + +| Pre-trained model | Config filename | ar_mode | pushforward | unroll_step | modes | width | +| :--- | :---- | :--- | :--- | ---: | ---: | ---: | +| 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | N/A | N/A | N/A | 16 | 64 | +| 1D_diff-sorp_NA_NA_Unet-1-step.pt | config_diff-sorp.yaml | False | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | True | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | True | True | 20 | N/A | N/A | +| 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | N/A | N/A | N/A | 12 | 20 | +| 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | False | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | True | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-PF-20.pt | config_diff-react.yaml | True | True | 20 | N/A | N/A | +| 2D_rdb_NA_NA_FNO.pt | config_rdb.yaml | N/A | N/A | N/A | 12 | 20 | +| 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | False | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | True | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | True | True | 20 | N/A | N/A | From 6b6421a91bef0065f84481f97f73ccd88c74ec34 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Mon, 14 Nov 2022 11:25:18 +0100 Subject: [PATCH 040/137] Fixed broken link in the config doc file --- pdebench/models/config/config_docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/models/config/config_docs.md b/pdebench/models/config/config_docs.md index f6b444a..21fd40b 100644 --- a/pdebench/models/config/config_docs.md +++ b/pdebench/models/config/config_docs.md @@ -3,7 +3,7 @@ This is the documentation of the config files that were used to generate the provided [pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). Since the default config files for all problems are already provided, this file only provides the values for the arguments that need to be changed. N/A values mean that the default values can be used. -The complete explanation of the arguments can be found in the [README file](./README.md) +The complete explanation of the arguments can be found in the [README file](/README.md) ## 2D Shallow Water Equation From d8d5850abdc833f3bbe1325537e707c1d43ddc1d Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Mon, 14 Nov 2022 11:30:21 +0100 Subject: [PATCH 041/137] Removed heading in the config file documentation --- pdebench/models/config/config_docs.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/pdebench/models/config/config_docs.md b/pdebench/models/config/config_docs.md index 21fd40b..4437064 100644 --- a/pdebench/models/config/config_docs.md +++ b/pdebench/models/config/config_docs.md @@ -5,8 +5,6 @@ Since the default config files for all problems are already provided, this file N/A values mean that the default values can be used. The complete explanation of the arguments can be found in the [README file](/README.md) -## 2D Shallow Water Equation - | Pre-trained model | Config filename | ar_mode | pushforward | unroll_step | modes | width | | :--- | :---- | :--- | :--- | ---: | ---: | ---: | | 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | N/A | N/A | N/A | 16 | 64 | From cbdeeeda3dec3886bdabaf5bb881f1521fe846b0 Mon Sep 17 00:00:00 2001 From: Raphael Leiteritz <48442682+leiterrl@users.noreply.github.com> Date: Mon, 14 Nov 2022 12:07:41 +0100 Subject: [PATCH 042/137] add pinn info to config documentation --- pdebench/models/config/config_docs.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdebench/models/config/config_docs.md b/pdebench/models/config/config_docs.md index 4437064..f0868a4 100644 --- a/pdebench/models/config/config_docs.md +++ b/pdebench/models/config/config_docs.md @@ -19,3 +19,6 @@ The complete explanation of the arguments can be found in the [README file](/REA | 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | False | False | N/A | N/A | N/A | | 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | True | False | N/A | N/A | N/A | | 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | True | True | 20 | N/A | N/A | +| 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | N/A | N/A | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_diff-react.yaml | N/A | N/A | N/A | N/A | N/A | +| 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | N/A | N/A | N/A | N/A | N/A | From 92a8e2394adad88cfd4cdd3e616a941a1c3229c3 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Wed, 23 Nov 2022 16:49:07 +0100 Subject: [PATCH 043/137] add config files for 1D Adv/Bgs training --- pdebench/models/config/args/config_Adv.yaml | 25 ++++++ pdebench/models/config/args/config_Bgs.yaml | 25 ++++++ pdebench/models/run_forward_1D.sh | 96 ++++++++++----------- 3 files changed, 98 insertions(+), 48 deletions(-) create mode 100644 pdebench/models/config/args/config_Adv.yaml create mode 100644 pdebench/models/config/args/config_Bgs.yaml diff --git a/pdebench/models/config/args/config_Adv.yaml b/pdebench/models/config/args/config_Adv.yaml new file mode 100644 index 0000000..e491d23 --- /dev/null +++ b/pdebench/models/config/args/config_Adv.yaml @@ -0,0 +1,25 @@ +model_name: 'Unet' +if_training: True +continue_training: False +batch_size: 50 +unroll_step: 20 +t_train: 200 +model_update: 1 +filename: '1D_Advection_Sols_beta0.1.hdf5' +single_file: True +reduced_resolution: 4 +reduced_resolution_t: 5 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +num_workers: 0 +#Unet +in_channels: 1 +out_channels: 1 +#FNO +num_channels: 1 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 \ No newline at end of file diff --git a/pdebench/models/config/args/config_Bgs.yaml b/pdebench/models/config/args/config_Bgs.yaml new file mode 100644 index 0000000..2fd1618 --- /dev/null +++ b/pdebench/models/config/args/config_Bgs.yaml @@ -0,0 +1,25 @@ +model_name: 'Unet' +if_training: True +continue_training: False +batch_size: 50 +unroll_step: 20 +t_train: 200 +model_update: 1 +filename: '1D_Burgers_Sols_Nu0.001.hdf5' +single_file: True +reduced_resolution: 4 +reduced_resolution_t: 5 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +num_workers: 0 +#Unet +in_channels: 1 +out_channels: 1 +#FNO +num_channels: 1 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 \ No newline at end of file diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index 19d6916..f69bda8 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -1,56 +1,56 @@ ## 'FNO' # Advection -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' ++args.if_training=False # Reaction Diffusion -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='FNO' ++args.reduced_resolution_t=1 ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='FNO' ++args.if_training=False # Burgers Eq. -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='FNO' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='FNO' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='FNO' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False ## Unet # Advection -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' ++args.if_training=False # Reaction Diffusion -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='Unet' ++args.reduced_resolution_t=1 ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_ReacDiff.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.model_name='Unet' ++args.if_training=False # Burgers Eq. -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='Unet' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='Unet' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='Unet' ++args.if_training=False -CUDA_VISIBLE_DEVICES='2' python3 train_models_forward.py +args=config.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False From dfb88008bd9400b1e015b9ea72f81c98bd4fe67e Mon Sep 17 00:00:00 2001 From: leiterrl Date: Thu, 24 Nov 2022 13:24:15 +0100 Subject: [PATCH 044/137] remove .idea folder --- .../data_gen_NLE/.idea/deployment.xml | 42 ------------------ .../data_gen_NLE/.idea/fluid_sims.iml | 8 ---- .../inspectionProfiles/Project_Default.xml | 19 -------- .../inspectionProfiles/profiles_settings.xml | 6 --- pdebench/data_gen/data_gen_NLE/.idea/misc.xml | 4 -- .../data_gen/data_gen_NLE/.idea/modules.xml | 8 ---- pdebench/data_gen/data_gen_NLE/.idea/vcs.xml | 6 --- .../data_gen/data_gen_NLE/.idea/workspace.xml | 43 ------------------- 8 files changed, 136 deletions(-) delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/deployment.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/misc.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/modules.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/vcs.xml delete mode 100644 pdebench/data_gen/data_gen_NLE/.idea/workspace.xml diff --git a/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml b/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml deleted file mode 100644 index 1988f52..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/deployment.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml b/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml deleted file mode 100644 index d0876a7..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/fluid_sims.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 6e5f9ee..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml b/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/misc.xml b/pdebench/data_gen/data_gen_NLE/.idea/misc.xml deleted file mode 100644 index d1e22ec..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/modules.xml b/pdebench/data_gen/data_gen_NLE/.idea/modules.xml deleted file mode 100644 index b404b77..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml b/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml b/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml deleted file mode 100644 index 383506f..0000000 --- a/pdebench/data_gen/data_gen_NLE/.idea/workspace.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1648051406434 - - - - \ No newline at end of file From 0f78dd866b151dc82641d3fca334b48681ed624c Mon Sep 17 00:00:00 2001 From: leiterrl Date: Thu, 24 Nov 2022 13:25:06 +0100 Subject: [PATCH 045/137] add .idea to gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 109c47f..4267a68 100644 --- a/.gitignore +++ b/.gitignore @@ -158,7 +158,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ #linters and ... .vscode/ From a03648df03b04b0cbe3679ae2f60a17e5c174717 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Thu, 24 Nov 2022 17:50:53 +0100 Subject: [PATCH 046/137] debugged for PINN pde1d byMT@24112022 --- pdebench/models/run_forward_1D.sh | 16 ++++++++++++++++ pdebench/models/train_models_forward.py | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index f69bda8..ce04e70 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -54,3 +54,19 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml + CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False +# 'PINN' +# Advection +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.aux_params=[0.1] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.aux_params=[0.4] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.aux_params=[1.] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.aux_params=[4.] +# Reaction Diffusion +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.aux_params=[0.5,1.] ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.aux_params=[0.5,10.] ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.aux_params=[2.,1.] ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.aux_params=[2.,10.] ++args.val_time=0.5 +# Burgers Eq. +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.aux_params=[0.001] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.aux_params=[0.01] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.aux_params=[0.1] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.aux_params=[1.] diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 98b1276..57acff3 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -236,6 +236,12 @@ def main(cfg: DictConfig): model_update=cfg.args.model_update, flnm=cfg.args.filename, seed=cfg.args.seed, + input_ch=cfg.args.input_ch, + output_ch=cfg.args.output_ch, + root_path=cfg.args.root_path, + val_num=cfg.args.val_num, + if_periodic_bc=cfg.args.if_periodic_bc, + aux_params=cfg.args.aux_params ) From fa0d8b897b6e2309fe533e31f09242f39c365c37 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Fri, 25 Nov 2022 14:19:38 +0100 Subject: [PATCH 047/137] Add new download script for easier data access Co-authored-by: Mario Co-authored-by: Timothy Praditia --- pdebench/data_download/download_direct.py | 103 +++++ ...{download.py => download_easydataverse.py} | 0 pdebench/data_download/download_metadata.csv | 366 ++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 pdebench/data_download/download_direct.py rename pdebench/data_download/{download.py => download_easydataverse.py} (100%) create mode 100644 pdebench/data_download/download_metadata.csv diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py new file mode 100644 index 0000000..0f2b04f --- /dev/null +++ b/pdebench/data_download/download_direct.py @@ -0,0 +1,103 @@ +import argparse +import os + +import pandas as pd +from torchvision.datasets.utils import download_url + + +def parse_metadata(pde_names): + """ + This function parses the argument to filter the metadata of files that need to be downloaded. + + Args: + pde_names: List containing the name of the PDE to be downloaded + df : The provided dataframe loaded from the csv file + + Options for pde_names: + - Advection + - Burgers + - 1D_CFD + - Diff-Sorp + - 1D_ReacDiff + - 2D_CFD + - Darcy + - 2D_ReacDiff + - NS_Incom + - SWE + - 3D_CFD + + Returns: + pde_df : Filtered dataframe containing metadata of files to be downloaded + """ + + meta_df = pd.read_csv("download_metadata.csv") + + # Ensure the pde_name is defined + pde_list = [ + "advection", + "burgers", + "1d_cfd", + "diff-sorp", + "1d_reacdiff", + "2d_cfd", + "darcy", + "2d_reacdiff", + "ns_incom", + "swe", + "3d_cfd", + ] + + assert all( + [name.lower() in pde_list for name in pde_names] + ), "PDE name not defined." + + # Filter the files to be downloaded + meta_df["PDE"] = meta_df["PDE"].str.lower() + pde_df = meta_df[meta_df["PDE"].isin(pde_names)] + + return pde_df + + +def download_data(root_folder, pde_name): + """ " + Download data splits specific to a given PDE. + + Args: + root_folder: The root folder where the data will be downloaded + pde_name : The name of the PDE for which the data to be downloaded + """ + + print(f"Downloading data for {pde_name} ...") + + # Load and parse metadata csv file + pde_df = parse_metadata(pde_name) + + # Iterate filtered dataframe and download the files + for index, row in pde_df.iterrows(): + file_path = os.path.join(root_folder, row["Path"]) + download_url(row["URL"], file_path, row["Filename"], md5=row["MD5"]) + + +if __name__ == "__main__": + arg_parser = argparse.ArgumentParser( + prog="Download Script", + description="Helper script to download the PDEBench datasets", + epilog="", + ) + + arg_parser.add_argument( + "--root_folder", + type=str, + default="./data", + required=False, + help="Root folder where the data will be downloaded", + ) + arg_parser.add_argument( + "--pde_name", + action="append", + help="Name of the PDE dataset to download. You can use this flag multiple times to download multiple datasets", + ) + + args = arg_parser.parse_args() + + download_data(args.root_folder, args.pde_name) diff --git a/pdebench/data_download/download.py b/pdebench/data_download/download_easydataverse.py similarity index 100% rename from pdebench/data_download/download.py rename to pdebench/data_download/download_easydataverse.py diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv new file mode 100644 index 0000000..1755737 --- /dev/null +++ b/pdebench/data_download/download_metadata.csv @@ -0,0 +1,366 @@ +PDE,Filename,URL,Path,MD5 +Advection,1D_Advection_Sols_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133100,1D/Advection/Train/,183cd9b7a6c3c5caf0fd89c87f2c4a80 +Advection,1D_Advection_Sols_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133109,1D/Advection/Train/,e6b6997e00bc0310308ce14df12fecd4 +Advection,1D_Advection_Sols_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133110,1D/Advection/Train/,99d57a5a8d660ba2cb824f41e09c6543 +Advection,1D_Advection_Sols_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133111,1D/Advection/Train/,91a599ed8c11447dec8200b0915571a3 +Advection,1D_Advection_Sols_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133112,1D/Advection/Train/,24bb9795a558bcc3bc9103b1cb791965 +Advection,1D_Advection_Sols_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133113,1D/Advection/Train/,73ec4f7980462a13629234ec2f849e78 +Advection,Advection_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133025,1D/Advection/Test/,77023c3b3b47e3d57ebca03a020d5a66 +Advection,Advection_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133028,1D/Advection/Test/,bb37c9a0faf199e51a66b82d7ca25122 +Advection,Advection_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133026,1D/Advection/Test/,79040b74bce5469374b59e3b7246a4d4 +Advection,Advection_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133027,1D/Advection/Test/,500bb617627770febe6f3caadd2ba68a +Advection,Advection_beta10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133023,1D/Advection/Test/,dbc70127e36f037bdb7d43e88e6fa409 +Advection,Advection_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133024,1D/Advection/Test/,36876a5d0e720e12685e5bc2e37a2885 +Advection,Advection_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133022,1D/Advection/Test/,68faad750411f2b3d30caae880558d39 +Burgers,1D_Burgers_Sols_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133133,1D/Burgers/Train/,5f7242c820efb37bae3d82e983c04e43 +Burgers,1D_Burgers_Sols_Nu0.002.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133134,1D/Burgers/Train/,eb7cc49d7d4d346d7abe9af9eee3f86b +Burgers,1D_Burgers_Sols_Nu0.004.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133135,1D/Burgers/Train/,e1c40dd4c0d8b858231408009e8a8def +Burgers,1D_Burgers_Sols_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133136,1D/Burgers/Train/,0a49257a0e5cd644459d3984185bb033 +Burgers,1D_Burgers_Sols_Nu0.02.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133137,1D/Burgers/Train/,79047cf2f24cb1fac309900f7ca0e8b3 +Burgers,1D_Burgers_Sols_Nu0.04.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133138,1D/Burgers/Train/,4aea33401b9d8dcca6830116717d54fe +Burgers,1D_Burgers_Sols_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133139,1D/Burgers/Train/,f50828c7d253d4b9cdb15b21c175b506 +Burgers,1D_Burgers_Sols_Nu0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133140,1D/Burgers/Train/,e9791092deac0d2f54dcaad6e8d047c2 +Burgers,1D_Burgers_Sols_Nu0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133141,1D/Burgers/Train/,3389b8dd9ed15873463878506a0a87e8 +Burgers,1D_Burgers_Sols_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133142,1D/Burgers/Train/,69c1890c49fc7ba36aac7107c41d5f41 +Burgers,1D_Burgers_Sols_Nu2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133143,1D/Burgers/Train/,fe6a312a9f4058e26dd69b999312ddd1 +Burgers,1D_Burgers_Sols_Nu4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133144,1D/Burgers/Train/,f9f848275533aaebd0aa4683411629af +Burgers,Burgers_possin_u1.0_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133114,1D/Burgers/Test/,f00667db13f14cbb6a10e2ae9493b74f +Burgers,Burgers_possin_u1.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133115,1D/Burgers/Test/,e0cce7b0af6f3d11d3179cf3d6602ef5 +Burgers,Burgers_possin_u1.0_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133116,1D/Burgers/Test/,8f320060f3799e6a3c5522347dcaa75c +Burgers,Burgers_possin_u1.0_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133117,1D/Burgers/Test/,ae4a0330ce6be1a6d86965d97a6f5a5c +Burgers,Burgers_possin_u1.0_Nu10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133118,1D/Burgers/Test/,953d39c68a8a22e984f2eb960a218b19 +Burgers,Burgers_sinsin_u1.0_du0.1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133127,1D/Burgers/Test/,1944dc91e9e743fc154a23b6203b0e06 +Burgers,Burgers_sinsin_u1.0_du0.25_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133128,1D/Burgers/Test/,6b67166a52fef4fcd68573dbca722a22 +Burgers,Burgers_sinsin_u1.0_du0.5_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133129,1D/Burgers/Test/,7dc1aa478e5a16a51b0c1a5a323eb66b +Burgers,Burgers_sinsin_u1.0_du1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133130,1D/Burgers/Test/,ba129e02864a16fff2171c91528bd807 +Burgers,Burgers_sinsin_u1.0_du2_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133131,1D/Burgers/Test/,88151c84329d1f304d160e5012f97fa3 +Burgers,Burgers_sinsin_u1.0_du5.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133132,1D/Burgers/Test/,f8adc4e9367068d9812d45011d11ffc5 +Burgers,Burgers_sin_u0.01_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133119,1D/Burgers/Test/,cc9dfc22b7e90d9a771a4bace1114bdc +Burgers,Burgers_sin_u0.1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133120,1D/Burgers/Test/,4487509cb38c1a3a57bb684081ffbb31 +Burgers,Burgers_sin_u1.0_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133121,1D/Burgers/Test/,8207ae134f2c2ecb13d03ead7601d6a5 +Burgers,Burgers_sin_u1.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133122,1D/Burgers/Test/,ba29e8728396e24fcd7aa4b8abf377b9 +Burgers,Burgers_sin_u1.0_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133123,1D/Burgers/Test/,caba5dff211bd9f5b88a5785ebb85ac7 +Burgers,Burgers_sin_u1.0_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133124,1D/Burgers/Test/,06a992d195c80405d4492b622c41f0ba +Burgers,Burgers_sin_u1.0_Nu10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133125,1D/Burgers/Test/,f6a2e39eb9f44061fd8bb236fca5abc7 +Burgers,Burgers_sin_u10.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133126,1D/Burgers/Test/,dd40cb0f808b68e7f1a75ec5a7917527 +1D_CFD,1D_CFD_Rand_Eta0.01_Zeta0.01_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164672,1D/CFD/Train/,2fdd00138f1d7abc794fb953021a9f43 +1D_CFD,1D_CFD_Rand_Eta0.1_Zeta0.1_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164668,1D/CFD/Train/,45655bd77d006ab539c52b7fbcf099b9 +1D_CFD,1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135485,1D/CFD/Train/,ad22fe08b8abc721179dbb34f2cc8b2a +1D_CFD,1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133155,1D/CFD/Train/,79857a11ad9a69d77b8cf249a1c72d06 +1D_CFD,1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133156,1D/CFD/Train/,08dffc3d84033c7574a70614703dd753 +1D_CFD,Sod1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133145,1D/CFD/Test/ShockTube/,ef8271ce332b986e961a7aaedcae24e7 +1D_CFD,Sod2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133146,1D/CFD/Test/ShockTube/,10e47c5c6ee0d1028f912a52bbf613d0 +1D_CFD,Sod3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133147,1D/CFD/Test/ShockTube/,495f2d2e0cee9b4ae28d7fef29bb7c68 +1D_CFD,Sod4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133148,1D/CFD/Test/ShockTube/,544399b3d05ae514d66fc96982ee9db8 +1D_CFD,Sod5.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133149,1D/CFD/Test/ShockTube/,ca7fa6631201e8bd506914d3b0b9fe50 +1D_CFD,Sod6.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133150,1D/CFD/Train/ShockTube/,adb2d95bf0d48e03bc0d8f4a2cbcd1c6 +1D_CFD,Sod7.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133151,1D/CFD/Test/ShockTube/,b8d73e1e3ef862ef732a5fa43c64612e +Diff_Sorp,1D_diff-sorp_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133020,1D/diffusion-sorption/,9d466d1213065619d087319e16d9a938 +1D_ReacDiff,ReacDiff_Nu0.5_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133177,1D/ReactionDiffusion/Train/,69a429239778d529cd419ed5888ea835 +1D_ReacDiff,ReacDiff_Nu0.5_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133178,1D/ReactionDiffusion/Train/,ff7c724b18e7ebe02e19c179852f48ee +1D_ReacDiff,ReacDiff_Nu0.5_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133179,1D/ReactionDiffusion/Train/,ac907daa7e483d203a5c77567cdea561 +1D_ReacDiff,ReacDiff_Nu0.5_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133180,1D/ReactionDiffusion/Train/,fb149e7540d8977af158bb8fec1048a3 +1D_ReacDiff,ReacDiff_Nu1.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133181,1D/ReactionDiffusion/Train/,bd73c2f3448d03e95e98c3831fc8fa70 +1D_ReacDiff,ReacDiff_Nu1.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133182,1D/ReactionDiffusion/Train/,a94e65631881a27ddae3ef74caf53093 +1D_ReacDiff,ReacDiff_Nu1.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133183,1D/ReactionDiffusion/Train/,112c01a76447162bd67c8c1073f58ca2 +1D_ReacDiff,ReacDiff_Nu1.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133184,1D/ReactionDiffusion/Train/,fa224c9d143de37ac6914d391e70f425 +1D_ReacDiff,ReacDiff_Nu2.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133185,1D/ReactionDiffusion/Train/,925a7e2c9b9e40ad2dd44b7002ec882b +1D_ReacDiff,ReacDiff_Nu2.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133186,1D/ReactionDiffusion/Train/,a3e25a9fb8a99f010352ad3dd1afd596 +1D_ReacDiff,ReacDiff_Nu2.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133187,1D/ReactionDiffusion/Train/,426353b241acfbc64067acb1bdc80ade +1D_ReacDiff,ReacDiff_Nu2.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133188,1D/ReactionDiffusion/Train/,f2890bdac5103a3b78fac5f80e57b760 +1D_ReacDiff,ReacDiff_Nu5.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133189,1D/ReactionDiffusion/Train/,0ed75b55f61bec11c47d379e34959e54 +1D_ReacDiff,ReacDiff_Nu5.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133190,1D/ReactionDiffusion/Train/,264c80af0e2a6cc1f70e87275e0f6ac4 +1D_ReacDiff,ReacDiff_Nu5.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133191,1D/ReactionDiffusion/Train/,f927f5d85f2e9bd97dff31b4dab051ae +1D_ReacDiff,ReacDiff_Nu5.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133192,1D/ReactionDiffusion/Train/,c9c26e2b5f2d4bf5bcbbd4ba34fefd5e +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133157,1D/ReactionDiffusion/Test/,6dc95d36e7126c104c5316485670f2c0 +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133158,1D/ReactionDiffusion/Test/,5bf21fbc428ac4346c0e7a04a5899f9b +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133159,1D/ReactionDiffusion/Test/,5198a1572c201657dc19ad8592e24ac1 +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133160,1D/ReactionDiffusion/Test/,9968701e89a8b406f18ffbc2b74045d8 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133161,1D/ReactionDiffusion/Test/,a258ff5be23d17d89cb10ac94f512165 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133162,1D/ReactionDiffusion/Test/,09279a597860dcf48ef6f6539c46920f +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133163,1D/ReactionDiffusion/Test/,27113922509f6336f5c8be8d99109828 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133164,1D/ReactionDiffusion/Test/,24ab563c27bf07d653b881d3c652bfa5 +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133165,1D/ReactionDiffusion/Test/,5a0f4d1ff304b11b9b22c3b00b1f63d4 +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133166,1D/ReactionDiffusion/Test/,4cf67490b01e54f80d45c8e7936b804d +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133167,1D/ReactionDiffusion/Test/,3c54c1cd3710b1f5b02a3de3782201ed +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133168,1D/ReactionDiffusion/Test/,580f08d5755ee393e7092eafedb70e46 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133169,1D/ReactionDiffusion/Test/,208882dcf594d52f8e5017904efd84c9 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133170,1D/ReactionDiffusion/Test/,891f8b47e49348b5411882ecebbc1bfd +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133171,1D/ReactionDiffusion/Test/,56e8e1e55ac92acf21b253511c370642 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133172,1D/ReactionDiffusion/Test/,4498954ef4cd5c63ac530ee46c351790 +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133173,1D/ReactionDiffusion/Test/,6b2c45748711bea8779dea3b48a53a7e +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133174,1D/ReactionDiffusion/Test/,b04c22629bf86d85929386aaf4a5f95d +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133175,1D/ReactionDiffusion/Test/,1738e4374456af974e4d0058d3426a69 +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133176,1D/ReactionDiffusion/Test/,4e3cf0ac66e85dbb77c79861076db8e8 +2D_CFD,2D_shock.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133193,2D/CFD/Test/2DShock/,b4098c20e2e0a25cd8ee229c9294b899 +2D_CFD,KH_M01_dk10_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133209,2D/CFD/Test/KH/,54e278b3a7419107a5cd6a8a019225d5 +2D_CFD,KH_M01_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133210,2D/CFD/Test/KH/,29ce31d3ce61de3b90ac0b42716c061c +2D_CFD,KH_M01_dk2_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133211,2D/CFD/Test/KH/,de0b2a3343de028e4c162723ec571713 +2D_CFD,KH_M01_dk5_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133212,2D/CFD/Test/KH/,5bcaed9eb7db5fd55d51f7347a938413 +2D_CFD,KH_M02_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133213,2D/CFD/Test/KH/,fbc392f83affc2224a5703dad7bda28a +2D_CFD,KH_M04_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133214,2D/CFD/Test/KH/,66626c935ab070a1e5df52bcf6aeccc7 +2D_CFD,KH_M1_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133215,2D/CFD/Test/KH/,338393c118196048a38d1df731a33b0a +2D_CFD,OTVortex.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133216,2D/CFD/Test/TOV/,3b68cb29e3d1906a19ce2cb4ce71faa4 +Darcy,2D_DarcyFlow_beta0.01_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133217,2D/DarcyFlow/,d05c287d4c0b7d3178b0097084238251 +Darcy,2D_DarcyFlow_beta0.1_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133218,2D/DarcyFlow/,294f9a03a4aa16b0e386469ca8b471be +Darcy,2D_DarcyFlow_beta1.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133219,2D/DarcyFlow/,81694ed31306ff2e5f6b76349b0b4389 +Darcy,2D_DarcyFlow_beta10.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133220,2D/DarcyFlow/,a7f23cf8011fc211b180828af39b7d1a +Darcy,2D_DarcyFlow_beta100.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133221,2D/DarcyFlow/,7c09f3b1bb097737d3fd52ffb0c6f1c8 +2D_ReacDiff,2D_diff-react_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133017,2D/diffusion-reaction/,b8d0b86064193195ddc30c33be5dc949 +NS_Incom,ns_incom_inhom_2d_512-0.h5,https://darus.uni-stuttgart.de/api/access/datafile/133280,2D/NS_incom/,54109d46f9c957317bd670ddb2068ac0 +NS_Incom,ns_incom_inhom_2d_512-1.h5,https://darus.uni-stuttgart.de/api/access/datafile/136439,2D/NS_incom/,e280fd3208fccb8ad5c1ff46c4796864 +NS_Incom,ns_incom_inhom_2d_512-10.h5,https://darus.uni-stuttgart.de/api/access/datafile/133309,2D/NS_incom/,f497297295ce3f2ebfefd81d00de31da +NS_Incom,ns_incom_inhom_2d_512-100.h5,https://darus.uni-stuttgart.de/api/access/datafile/133267,2D/NS_incom/,fcf3b743fc82ac8706cfa85a7b259dcd +NS_Incom,ns_incom_inhom_2d_512-101.h5,https://darus.uni-stuttgart.de/api/access/datafile/133289,2D/NS_incom/,9d1cd827c95f80975d09eae0b4e6c3da +NS_Incom,ns_incom_inhom_2d_512-102.h5,https://darus.uni-stuttgart.de/api/access/datafile/133291,2D/NS_incom/,ce1f0a7a5e52bf49db43084eef367639 +NS_Incom,ns_incom_inhom_2d_512-103.h5,https://darus.uni-stuttgart.de/api/access/datafile/133294,2D/NS_incom/,4e0a5f39d14745adf96655f220329550 +NS_Incom,ns_incom_inhom_2d_512-104.h5,https://darus.uni-stuttgart.de/api/access/datafile/133298,2D/NS_incom/,52f59ffdef04338f2630e98e32448086 +NS_Incom,ns_incom_inhom_2d_512-105.h5,https://darus.uni-stuttgart.de/api/access/datafile/133290,2D/NS_incom/,1010c2d0c807e5e355e336fe8d905067 +NS_Incom,ns_incom_inhom_2d_512-106.h5,https://darus.uni-stuttgart.de/api/access/datafile/133374,2D/NS_incom/,1127b6e69bd83b128a9420ae58187729 +NS_Incom,ns_incom_inhom_2d_512-107.h5,https://darus.uni-stuttgart.de/api/access/datafile/133375,2D/NS_incom/,38e676bfd97cc946cd6db2af48c487bf +NS_Incom,ns_incom_inhom_2d_512-108.h5,https://darus.uni-stuttgart.de/api/access/datafile/133376,2D/NS_incom/,0355430cc9cee8e921378eda7c1c6b84 +NS_Incom,ns_incom_inhom_2d_512-109.h5,https://darus.uni-stuttgart.de/api/access/datafile/133305,2D/NS_incom/,eff2dd6e71649bb3f9f37f951bd54d63 +NS_Incom,ns_incom_inhom_2d_512-11.h5,https://darus.uni-stuttgart.de/api/access/datafile/133336,2D/NS_incom/,3342616df260d3ec60fbf24dfaabf9ae +NS_Incom,ns_incom_inhom_2d_512-110.h5,https://darus.uni-stuttgart.de/api/access/datafile/133313,2D/NS_incom/,d9739419a2100b390742acf88e4548b3 +NS_Incom,ns_incom_inhom_2d_512-111.h5,https://darus.uni-stuttgart.de/api/access/datafile/133318,2D/NS_incom/,ea5239ab7204d53c476f48739a4e0d78 +NS_Incom,ns_incom_inhom_2d_512-112.h5,https://darus.uni-stuttgart.de/api/access/datafile/133324,2D/NS_incom/,cdaf99ff4ef69ee79ad60648a06259d7 +NS_Incom,ns_incom_inhom_2d_512-113.h5,https://darus.uni-stuttgart.de/api/access/datafile/133377,2D/NS_incom/,aee8074da1cf6e19ee421a7b86caeaa3 +NS_Incom,ns_incom_inhom_2d_512-114.h5,https://darus.uni-stuttgart.de/api/access/datafile/133378,2D/NS_incom/,f1b4550b00a887771e79a81c7336a4b0 +NS_Incom,ns_incom_inhom_2d_512-115.h5,https://darus.uni-stuttgart.de/api/access/datafile/133379,2D/NS_incom/,0756d685a754b84337d1de7280572c85 +NS_Incom,ns_incom_inhom_2d_512-116.h5,https://darus.uni-stuttgart.de/api/access/datafile/133380,2D/NS_incom/,f7c55c110dcf0aeaed03699bc0942f0d +NS_Incom,ns_incom_inhom_2d_512-117.h5,https://darus.uni-stuttgart.de/api/access/datafile/133381,2D/NS_incom/,375331e30111457028de38b89e03c404 +NS_Incom,ns_incom_inhom_2d_512-118.h5,https://darus.uni-stuttgart.de/api/access/datafile/133383,2D/NS_incom/,6d2fbe4a0e32e1934d349616ca6b85d8 +NS_Incom,ns_incom_inhom_2d_512-119.h5,https://darus.uni-stuttgart.de/api/access/datafile/133385,2D/NS_incom/,993112f4fe3910230005cc35099c7d59 +NS_Incom,ns_incom_inhom_2d_512-12.h5,https://darus.uni-stuttgart.de/api/access/datafile/133574,2D/NS_incom/,71f407084513d7c0e93377c1b46e4bb4 +NS_Incom,ns_incom_inhom_2d_512-120.h5,https://darus.uni-stuttgart.de/api/access/datafile/133386,2D/NS_incom/,f7c0b147e5b3f217a1d534f303ab52f4 +NS_Incom,ns_incom_inhom_2d_512-121.h5,https://darus.uni-stuttgart.de/api/access/datafile/133441,2D/NS_incom/,785d733406eeec2694eb89769f72971b +NS_Incom,ns_incom_inhom_2d_512-122.h5,https://darus.uni-stuttgart.de/api/access/datafile/133346,2D/NS_incom/,eeb2488aa0a5d1f14069dcfaaa0da71d +NS_Incom,ns_incom_inhom_2d_512-123.h5,https://darus.uni-stuttgart.de/api/access/datafile/133442,2D/NS_incom/,57e341bea82fd33ec095e7070fd1ae38 +NS_Incom,ns_incom_inhom_2d_512-124.h5,https://darus.uni-stuttgart.de/api/access/datafile/133443,2D/NS_incom/,6aba12029ab0ddb165c1c71f3b1c8f27 +NS_Incom,ns_incom_inhom_2d_512-125.h5,https://darus.uni-stuttgart.de/api/access/datafile/133444,2D/NS_incom/,2b15572d1dda8048ab9713c2318d9322 +NS_Incom,ns_incom_inhom_2d_512-126.h5,https://darus.uni-stuttgart.de/api/access/datafile/133303,2D/NS_incom/,56bcc085c7c0d303dedf147e6c608730 +NS_Incom,ns_incom_inhom_2d_512-127.h5,https://darus.uni-stuttgart.de/api/access/datafile/133292,2D/NS_incom/,71b5191ed81e86ec68f0a7bcd6a07759 +NS_Incom,ns_incom_inhom_2d_512-128.h5,https://darus.uni-stuttgart.de/api/access/datafile/133454,2D/NS_incom/,72b8110ec99098530494bbf0b64f51f7 +NS_Incom,ns_incom_inhom_2d_512-129.h5,https://darus.uni-stuttgart.de/api/access/datafile/133293,2D/NS_incom/,0e2cda441dec2c19ffaefd8ad035220e +NS_Incom,ns_incom_inhom_2d_512-13.h5,https://darus.uni-stuttgart.de/api/access/datafile/133304,2D/NS_incom/,424f6d2ab607612599645442a45234cf +NS_Incom,ns_incom_inhom_2d_512-130.h5,https://darus.uni-stuttgart.de/api/access/datafile/133295,2D/NS_incom/,8a6f6b785ce64aecc9f46a31650662d0 +NS_Incom,ns_incom_inhom_2d_512-131.h5,https://darus.uni-stuttgart.de/api/access/datafile/133275,2D/NS_incom/,3c4541c0ed411fdc9641b27978318ad6 +NS_Incom,ns_incom_inhom_2d_512-132.h5,https://darus.uni-stuttgart.de/api/access/datafile/133296,2D/NS_incom/,3c4541c0ed411fdc9641b27978318ad6 +NS_Incom,ns_incom_inhom_2d_512-133.h5,https://darus.uni-stuttgart.de/api/access/datafile/133297,2D/NS_incom/,ba2bb63092d5d0cb8fae29a0c18ff851 +NS_Incom,ns_incom_inhom_2d_512-134.h5,https://darus.uni-stuttgart.de/api/access/datafile/133269,2D/NS_incom/,4b0b6e0c3d22772ea20dc4c3e07c5e7d +NS_Incom,ns_incom_inhom_2d_512-135.h5,https://darus.uni-stuttgart.de/api/access/datafile/133274,2D/NS_incom/,e48583df141230425ea36d093b0b46f6 +NS_Incom,ns_incom_inhom_2d_512-136.h5,https://darus.uni-stuttgart.de/api/access/datafile/133299,2D/NS_incom/,4b0b6e0c3d22772ea20dc4c3e07c5e7d +NS_Incom,ns_incom_inhom_2d_512-137.h5,https://darus.uni-stuttgart.de/api/access/datafile/133300,2D/NS_incom/,e48583df141230425ea36d093b0b46f6 +NS_Incom,ns_incom_inhom_2d_512-138.h5,https://darus.uni-stuttgart.de/api/access/datafile/133302,2D/NS_incom/,294b49a65b2fc861e23dfc40098d6e91 +NS_Incom,ns_incom_inhom_2d_512-139.h5,https://darus.uni-stuttgart.de/api/access/datafile/133575,2D/NS_incom/,14c9705c20d630e08f4387cc4880a646 +NS_Incom,ns_incom_inhom_2d_512-14.h5,https://darus.uni-stuttgart.de/api/access/datafile/133281,2D/NS_incom/,f8099cba29f6374b6454424ef521b86d +NS_Incom,ns_incom_inhom_2d_512-140.h5,https://darus.uni-stuttgart.de/api/access/datafile/133698,2D/NS_incom/,f8c12c7ee3973eab55a531a24689ce95 +NS_Incom,ns_incom_inhom_2d_512-141.h5,https://darus.uni-stuttgart.de/api/access/datafile/133307,2D/NS_incom/,95a897a07713b654ce497b513ada21df +NS_Incom,ns_incom_inhom_2d_512-142.h5,https://darus.uni-stuttgart.de/api/access/datafile/133306,2D/NS_incom/,b0c964793163e49d46ddfa783b7f2a87 +NS_Incom,ns_incom_inhom_2d_512-143.h5,https://darus.uni-stuttgart.de/api/access/datafile/133590,2D/NS_incom/,b35dd28ee302e41dd9500e65e681bd93 +NS_Incom,ns_incom_inhom_2d_512-144.h5,https://darus.uni-stuttgart.de/api/access/datafile/133591,2D/NS_incom/,d7b000376bcb6cf4949e2dec4232a926 +NS_Incom,ns_incom_inhom_2d_512-145.h5,https://darus.uni-stuttgart.de/api/access/datafile/133592,2D/NS_incom/,e90fad8268cf369136cebba09f3b06e8 +NS_Incom,ns_incom_inhom_2d_512-146.h5,https://darus.uni-stuttgart.de/api/access/datafile/133593,2D/NS_incom/,568fac9aab2f599c20da783e9c7f3f0a +NS_Incom,ns_incom_inhom_2d_512-147.h5,https://darus.uni-stuttgart.de/api/access/datafile/133599,2D/NS_incom/,43194bdefada7b59cac975cc08e6d12d +NS_Incom,ns_incom_inhom_2d_512-148.h5,https://darus.uni-stuttgart.de/api/access/datafile/133600,2D/NS_incom/,41a78815f65ff7b607d69dd508b3ffea +NS_Incom,ns_incom_inhom_2d_512-149.h5,https://darus.uni-stuttgart.de/api/access/datafile/133601,2D/NS_incom/,3ebb3a014d7c33787cda458a689f7728 +NS_Incom,ns_incom_inhom_2d_512-15.h5,https://darus.uni-stuttgart.de/api/access/datafile/133609,2D/NS_incom/,74dd42aaf8b809925ff6dce345be11fd +NS_Incom,ns_incom_inhom_2d_512-150.h5,https://darus.uni-stuttgart.de/api/access/datafile/133602,2D/NS_incom/,562bd9a0d25ce189a9635d385e3d6a3c +NS_Incom,ns_incom_inhom_2d_512-151.h5,https://darus.uni-stuttgart.de/api/access/datafile/133271,2D/NS_incom/,1acb06d08a4ae3a17ddbe04a2e3fd5cf +NS_Incom,ns_incom_inhom_2d_512-152.h5,https://darus.uni-stuttgart.de/api/access/datafile/133603,2D/NS_incom/,0023490cb7c00a4a75f9a252af9be71c +NS_Incom,ns_incom_inhom_2d_512-153.h5,https://darus.uni-stuttgart.de/api/access/datafile/133594,2D/NS_incom/,669788ec079a3e695b68821c17d79a26 +NS_Incom,ns_incom_inhom_2d_512-154.h5,https://darus.uni-stuttgart.de/api/access/datafile/133604,2D/NS_incom/,547cfa5abca50bcd927630b8264d6876 +NS_Incom,ns_incom_inhom_2d_512-155.h5,https://darus.uni-stuttgart.de/api/access/datafile/133605,2D/NS_incom/,f28fcf6d99c0d1f1bb08a82be94114eb +NS_Incom,ns_incom_inhom_2d_512-156.h5,https://darus.uni-stuttgart.de/api/access/datafile/133606,2D/NS_incom/,8c52c6258bdbbc7c4fe1cb7892b5bd23 +NS_Incom,ns_incom_inhom_2d_512-157.h5,https://darus.uni-stuttgart.de/api/access/datafile/133607,2D/NS_incom/,91f4ab6bd4eef91ab43908290539bdab +NS_Incom,ns_incom_inhom_2d_512-158.h5,https://darus.uni-stuttgart.de/api/access/datafile/133321,2D/NS_incom/,299521efb9f871919b003e86d0e8d628 +NS_Incom,ns_incom_inhom_2d_512-159.h5,https://darus.uni-stuttgart.de/api/access/datafile/133608,2D/NS_incom/,54b54c80708366c74d4aee82688be26e +NS_Incom,ns_incom_inhom_2d_512-16.h5,https://darus.uni-stuttgart.de/api/access/datafile/133311,2D/NS_incom/,b83a1465fee7f5dcb9826d1bbb4b6f71 +NS_Incom,ns_incom_inhom_2d_512-160.h5,https://darus.uni-stuttgart.de/api/access/datafile/133610,2D/NS_incom/,e90d31aff2172f508846473126c78947 +NS_Incom,ns_incom_inhom_2d_512-161.h5,https://darus.uni-stuttgart.de/api/access/datafile/133611,2D/NS_incom/,24b4b52f0a03dc52f7159ef10652c40a +NS_Incom,ns_incom_inhom_2d_512-162.h5,https://darus.uni-stuttgart.de/api/access/datafile/133683,2D/NS_incom/,87d592f2be6a76751b2b36e8bf0d0a88 +NS_Incom,ns_incom_inhom_2d_512-163.h5,https://darus.uni-stuttgart.de/api/access/datafile/133612,2D/NS_incom/,2acf733c2833fb815487271bbb5ce66f +NS_Incom,ns_incom_inhom_2d_512-164.h5,https://darus.uni-stuttgart.de/api/access/datafile/136435,2D/NS_incom/,8470816b05ecbabd2ec6036f346d4d79 +NS_Incom,ns_incom_inhom_2d_512-165.h5,https://darus.uni-stuttgart.de/api/access/datafile/133685,2D/NS_incom/,61d677b1edd03987c26648d7d136672d +NS_Incom,ns_incom_inhom_2d_512-166.h5,https://darus.uni-stuttgart.de/api/access/datafile/133308,2D/NS_incom/,4f1dd0e87f4ad925dc82444da4df7d3a +NS_Incom,ns_incom_inhom_2d_512-167.h5,https://darus.uni-stuttgart.de/api/access/datafile/133684,2D/NS_incom/,8e90f2ab307a6c5f8a2e1a3befa02d8f +NS_Incom,ns_incom_inhom_2d_512-168.h5,https://darus.uni-stuttgart.de/api/access/datafile/133701,2D/NS_incom/,1f8ef01efadb38eb638c383e242d9511 +NS_Incom,ns_incom_inhom_2d_512-169.h5,https://darus.uni-stuttgart.de/api/access/datafile/133310,2D/NS_incom/,d2ae69de21cb38a24dbc174f34afeb70 +NS_Incom,ns_incom_inhom_2d_512-17.h5,https://darus.uni-stuttgart.de/api/access/datafile/133325,2D/NS_incom/,9c206312c353aa6dcaa3144d53753075 +NS_Incom,ns_incom_inhom_2d_512-170.h5,https://darus.uni-stuttgart.de/api/access/datafile/133312,2D/NS_incom/,d7f9e313387dfb3efc356c1d5606ebc3 +NS_Incom,ns_incom_inhom_2d_512-171.h5,https://darus.uni-stuttgart.de/api/access/datafile/133679,2D/NS_incom/,f6ade3872e3e0db0d6b8af4cc9fd7ed6 +NS_Incom,ns_incom_inhom_2d_512-172.h5,https://darus.uni-stuttgart.de/api/access/datafile/133314,2D/NS_incom/,986851271af43e55264a8db4229b2049 +NS_Incom,ns_incom_inhom_2d_512-173.h5,https://darus.uni-stuttgart.de/api/access/datafile/133315,2D/NS_incom/,e064a3b043136572de413744f470e795 +NS_Incom,ns_incom_inhom_2d_512-174.h5,https://darus.uni-stuttgart.de/api/access/datafile/133316,2D/NS_incom/,d4906a07e5e94b6e147570613205d19d +NS_Incom,ns_incom_inhom_2d_512-175.h5,https://darus.uni-stuttgart.de/api/access/datafile/133317,2D/NS_incom/,7d7230752c444c2570396927e9f9b4bf +NS_Incom,ns_incom_inhom_2d_512-176.h5,https://darus.uni-stuttgart.de/api/access/datafile/133319,2D/NS_incom/,47a59095761d69cecc886ddb3027b556 +NS_Incom,ns_incom_inhom_2d_512-177.h5,https://darus.uni-stuttgart.de/api/access/datafile/133320,2D/NS_incom/,979de8be5c402f6dc50060e92ca6245a +NS_Incom,ns_incom_inhom_2d_512-178.h5,https://darus.uni-stuttgart.de/api/access/datafile/133342,2D/NS_incom/,a72fc652b38b2dc0b0aa406bb35cd676 +NS_Incom,ns_incom_inhom_2d_512-179.h5,https://darus.uni-stuttgart.de/api/access/datafile/133697,2D/NS_incom/,6dd8a78a85ad7c0e03daaabcb3826fdb +NS_Incom,ns_incom_inhom_2d_512-18.h5,https://darus.uni-stuttgart.de/api/access/datafile/136437,2D/NS_incom/,272c1cdf65a90dc3e4308df8a15ef7dc +NS_Incom,ns_incom_inhom_2d_512-180.h5,https://darus.uni-stuttgart.de/api/access/datafile/133326,2D/NS_incom/,f9ffc612a7438dc17524818b48acb7ac +NS_Incom,ns_incom_inhom_2d_512-181.h5,https://darus.uni-stuttgart.de/api/access/datafile/133327,2D/NS_incom/,78ee160778aab06d0e88507944f661b4 +NS_Incom,ns_incom_inhom_2d_512-182.h5,https://darus.uni-stuttgart.de/api/access/datafile/133328,2D/NS_incom/,e9a36056e3e9fe23937fbf65e0dfc16a +NS_Incom,ns_incom_inhom_2d_512-183.h5,https://darus.uni-stuttgart.de/api/access/datafile/133329,2D/NS_incom/,5107343596d93f8739b4f09926daeef7 +NS_Incom,ns_incom_inhom_2d_512-184.h5,https://darus.uni-stuttgart.de/api/access/datafile/133330,2D/NS_incom/,f4a7aff84a03fff6e1958617abf373c4 +NS_Incom,ns_incom_inhom_2d_512-185.h5,https://darus.uni-stuttgart.de/api/access/datafile/133331,2D/NS_incom/,f59cb88424414a28aad308499935619b +NS_Incom,ns_incom_inhom_2d_512-186.h5,https://darus.uni-stuttgart.de/api/access/datafile/133666,2D/NS_incom/,d9bc339d2d2047f8f6a369ad2e4e9357 +NS_Incom,ns_incom_inhom_2d_512-187.h5,https://darus.uni-stuttgart.de/api/access/datafile/136436,2D/NS_incom/,4d0461fc34683816d5906d4809c84432 +NS_Incom,ns_incom_inhom_2d_512-188.h5,https://darus.uni-stuttgart.de/api/access/datafile/133332,2D/NS_incom/,3565b7f3ab620c3716a25865670b98fb +NS_Incom,ns_incom_inhom_2d_512-189.h5,https://darus.uni-stuttgart.de/api/access/datafile/133668,2D/NS_incom/,d385ef198ae29ef5eff9aad6d129621a +NS_Incom,ns_incom_inhom_2d_512-19.h5,https://darus.uni-stuttgart.de/api/access/datafile/133335,2D/NS_incom/,f67fec4521fc9a113cd16de7f7c283ff +NS_Incom,ns_incom_inhom_2d_512-190.h5,https://darus.uni-stuttgart.de/api/access/datafile/136438,2D/NS_incom/,2dce11f971c3561083d580f7299f3e02 +NS_Incom,ns_incom_inhom_2d_512-191.h5,https://darus.uni-stuttgart.de/api/access/datafile/133663,2D/NS_incom/,fda967e4ca21881b00d5c67b984d4c78 +NS_Incom,ns_incom_inhom_2d_512-192.h5,https://darus.uni-stuttgart.de/api/access/datafile/133664,2D/NS_incom/,85542754064fcab3675930ac607b68e0 +NS_Incom,ns_incom_inhom_2d_512-193.h5,https://darus.uni-stuttgart.de/api/access/datafile/133371,2D/NS_incom/,2f67289963f5d9d3041acf67f45a38b7 +NS_Incom,ns_incom_inhom_2d_512-194.h5,https://darus.uni-stuttgart.de/api/access/datafile/133669,2D/NS_incom/,609d2d5dbd19ccdb6cee8ab67df66c38 +NS_Incom,ns_incom_inhom_2d_512-195.h5,https://darus.uni-stuttgart.de/api/access/datafile/133699,2D/NS_incom/,b39dbe9518dbc19da27345ce569489e4 +NS_Incom,ns_incom_inhom_2d_512-196.h5,https://darus.uni-stuttgart.de/api/access/datafile/133665,2D/NS_incom/,fc636092ce4175b3d908d156db66deb8 +NS_Incom,ns_incom_inhom_2d_512-197.h5,https://darus.uni-stuttgart.de/api/access/datafile/133702,2D/NS_incom/,0b3bd457d75ccb3120f649bcdb0089f2 +NS_Incom,ns_incom_inhom_2d_512-198.h5,https://darus.uni-stuttgart.de/api/access/datafile/133368,2D/NS_incom/,526cce686110114720bc9d84eb8d16ee +NS_Incom,ns_incom_inhom_2d_512-199.h5,https://darus.uni-stuttgart.de/api/access/datafile/133667,2D/NS_incom/,123272aaaf52747c39e8c32b4955390b +NS_Incom,ns_incom_inhom_2d_512-2.h5,https://darus.uni-stuttgart.de/api/access/datafile/133721,2D/NS_incom/,1d7a2aac41a410bea6c887f624273d20 +NS_Incom,ns_incom_inhom_2d_512-20.h5,https://darus.uni-stuttgart.de/api/access/datafile/133283,2D/NS_incom/,859c8c8cede4fd611548af291acfa362 +NS_Incom,ns_incom_inhom_2d_512-200.h5,https://darus.uni-stuttgart.de/api/access/datafile/136440,2D/NS_incom/,621c5bdf211ab8aa6d586ede4b51c5a2 +NS_Incom,ns_incom_inhom_2d_512-201.h5,https://darus.uni-stuttgart.de/api/access/datafile/133367,2D/NS_incom/,89b6667026a2f289b81e48b6d75ef4d6 +NS_Incom,ns_incom_inhom_2d_512-202.h5,https://darus.uni-stuttgart.de/api/access/datafile/133661,2D/NS_incom/,a70f3e38089caec832075b7c3ce86744 +NS_Incom,ns_incom_inhom_2d_512-203.h5,https://darus.uni-stuttgart.de/api/access/datafile/133660,2D/NS_incom/,f0bc2659a6f671813e88409bdb08531a +NS_Incom,ns_incom_inhom_2d_512-204.h5,https://darus.uni-stuttgart.de/api/access/datafile/133671,2D/NS_incom/,c109d2aa5cf0688c5f0565b03d1e3ce0 +NS_Incom,ns_incom_inhom_2d_512-205.h5,https://darus.uni-stuttgart.de/api/access/datafile/133688,2D/NS_incom/,2fa87303d667e4d50935fe26f04437b4 +NS_Incom,ns_incom_inhom_2d_512-206.h5,https://darus.uni-stuttgart.de/api/access/datafile/136441,2D/NS_incom/,28a557b57ffce43cb9de4cc48642fc05 +NS_Incom,ns_incom_inhom_2d_512-207.h5,https://darus.uni-stuttgart.de/api/access/datafile/133282,2D/NS_incom/,01a63c9ad30ac4d140f86bb2fa929274 +NS_Incom,ns_incom_inhom_2d_512-208.h5,https://darus.uni-stuttgart.de/api/access/datafile/133340,2D/NS_incom/,d2aa05820caf6a38be4711ef797d3bf4 +NS_Incom,ns_incom_inhom_2d_512-209.h5,https://darus.uni-stuttgart.de/api/access/datafile/133333,2D/NS_incom/,9e17d624a2eea451f0956f9184ce63d8 +NS_Incom,ns_incom_inhom_2d_512-21.h5,https://darus.uni-stuttgart.de/api/access/datafile/133334,2D/NS_incom/,859c8c8cede4fd611548af291acfa362 +NS_Incom,ns_incom_inhom_2d_512-210.h5,https://darus.uni-stuttgart.de/api/access/datafile/133703,2D/NS_incom/,fb5de2b943891ed1ec1bbb682520bc78 +NS_Incom,ns_incom_inhom_2d_512-211.h5,https://darus.uni-stuttgart.de/api/access/datafile/133286,2D/NS_incom/,24f8fe56ffe24baaa03f6bf12762ef95 +NS_Incom,ns_incom_inhom_2d_512-212.h5,https://darus.uni-stuttgart.de/api/access/datafile/133284,2D/NS_incom/,8e4561fe4ae69d62000df136f980fae3 +NS_Incom,ns_incom_inhom_2d_512-213.h5,https://darus.uni-stuttgart.de/api/access/datafile/133285,2D/NS_incom/,d1101482e0d766334b92c4d0e11d73f3 +NS_Incom,ns_incom_inhom_2d_512-214.h5,https://darus.uni-stuttgart.de/api/access/datafile/133337,2D/NS_incom/,d1101482e0d766334b92c4d0e11d73f3 +NS_Incom,ns_incom_inhom_2d_512-215.h5,https://darus.uni-stuttgart.de/api/access/datafile/133287,2D/NS_incom/,f615b96dd71864673bba274240acfb12 +NS_Incom,ns_incom_inhom_2d_512-216.h5,https://darus.uni-stuttgart.de/api/access/datafile/133288,2D/NS_incom/,892986d49b49b431f3f172d7baa9977b +NS_Incom,ns_incom_inhom_2d_512-217.h5,https://darus.uni-stuttgart.de/api/access/datafile/133338,2D/NS_incom/,a471e758505de149dba27c9fa2d29198 +NS_Incom,ns_incom_inhom_2d_512-218.h5,https://darus.uni-stuttgart.de/api/access/datafile/133339,2D/NS_incom/,f615b96dd71864673bba274240acfb12 +NS_Incom,ns_incom_inhom_2d_512-219.h5,https://darus.uni-stuttgart.de/api/access/datafile/133341,2D/NS_incom/,1965889c070e802965ebe19b01cdf2db +NS_Incom,ns_incom_inhom_2d_512-22.h5,https://darus.uni-stuttgart.de/api/access/datafile/133344,2D/NS_incom/,2235dd8e60d0d43b49aba99819ddb8d3 +NS_Incom,ns_incom_inhom_2d_512-220.h5,https://darus.uni-stuttgart.de/api/access/datafile/133343,2D/NS_incom/,588ce18cf51ca3517527f3dd0b0ee443 +NS_Incom,ns_incom_inhom_2d_512-221.h5,https://darus.uni-stuttgart.de/api/access/datafile/133345,2D/NS_incom/,e57ad44f1ae4fa9f31ddff57578507ce +NS_Incom,ns_incom_inhom_2d_512-222.h5,https://darus.uni-stuttgart.de/api/access/datafile/133347,2D/NS_incom/,892986d49b49b431f3f172d7baa9977b +NS_Incom,ns_incom_inhom_2d_512-223.h5,https://darus.uni-stuttgart.de/api/access/datafile/133369,2D/NS_incom/,9da97766e3b311af6d79a946f8db5184 +NS_Incom,ns_incom_inhom_2d_512-224.h5,https://darus.uni-stuttgart.de/api/access/datafile/133370,2D/NS_incom/,44a864a879590427273a3dbf1aa2c190 +NS_Incom,ns_incom_inhom_2d_512-225.h5,https://darus.uni-stuttgart.de/api/access/datafile/133372,2D/NS_incom/,8e4561fe4ae69d62000df136f980fae3 +NS_Incom,ns_incom_inhom_2d_512-226.h5,https://darus.uni-stuttgart.de/api/access/datafile/133373,2D/NS_incom/,adf37d1dd066c2dc8fb3886369b5434f +NS_Incom,ns_incom_inhom_2d_512-227.h5,https://darus.uni-stuttgart.de/api/access/datafile/136442,2D/NS_incom/,de46866be488bb59d0d1c8ddc13a4dcb +NS_Incom,ns_incom_inhom_2d_512-228.h5,https://darus.uni-stuttgart.de/api/access/datafile/133723,2D/NS_incom/,43975b57db7c922f94703d1c63fdaebe +NS_Incom,ns_incom_inhom_2d_512-229.h5,https://darus.uni-stuttgart.de/api/access/datafile/136443,2D/NS_incom/,805f1a7336301b2bcc4a9f0fcce43864 +NS_Incom,ns_incom_inhom_2d_512-23.h5,https://darus.uni-stuttgart.de/api/access/datafile/133617,2D/NS_incom/,d026b809986795e73f13368a763e56d2 +NS_Incom,ns_incom_inhom_2d_512-230.h5,https://darus.uni-stuttgart.de/api/access/datafile/136445,2D/NS_incom/,e43acdfb560a45181111256e4ce45cc1 +NS_Incom,ns_incom_inhom_2d_512-231.h5,https://darus.uni-stuttgart.de/api/access/datafile/133613,2D/NS_incom/,f56497f0ba427fdf9e215c885c6c1d3a +NS_Incom,ns_incom_inhom_2d_512-232.h5,https://darus.uni-stuttgart.de/api/access/datafile/133692,2D/NS_incom/,599ff6b1ea10572a829a13e5fc9c2523 +NS_Incom,ns_incom_inhom_2d_512-233.h5,https://darus.uni-stuttgart.de/api/access/datafile/133614,2D/NS_incom/,b0cbda1f7435ccbe04bc6a09840624df +NS_Incom,ns_incom_inhom_2d_512-234.h5,https://darus.uni-stuttgart.de/api/access/datafile/133700,2D/NS_incom/,6331197878327890af4da21d5c4065bf +NS_Incom,ns_incom_inhom_2d_512-235.h5,https://darus.uni-stuttgart.de/api/access/datafile/133693,2D/NS_incom/,dd937dace08671e5b61a8f960d845561 +NS_Incom,ns_incom_inhom_2d_512-236.h5,https://darus.uni-stuttgart.de/api/access/datafile/133615,2D/NS_incom/,07a4596a86c3099cf7f6e7e5c47b71b6 +NS_Incom,ns_incom_inhom_2d_512-237.h5,https://darus.uni-stuttgart.de/api/access/datafile/133616,2D/NS_incom/,4c977f28e59fd8cc877f6d95d96011e5 +NS_Incom,ns_incom_inhom_2d_512-238.h5,https://darus.uni-stuttgart.de/api/access/datafile/133695,2D/NS_incom/,9d3bad375edf84cd86a648caca49b6b5 +NS_Incom,ns_incom_inhom_2d_512-239.h5,https://darus.uni-stuttgart.de/api/access/datafile/136446,2D/NS_incom/,bae42dea9f17c4ca5ec33997e3eb966f +NS_Incom,ns_incom_inhom_2d_512-24.h5,https://darus.uni-stuttgart.de/api/access/datafile/133618,2D/NS_incom/,c7e0bdab4f18748914263534cf21422d +NS_Incom,ns_incom_inhom_2d_512-240.h5,https://darus.uni-stuttgart.de/api/access/datafile/133268,2D/NS_incom/,10b14e2a9eb6fcf5a88a7a5447481aa7 +NS_Incom,ns_incom_inhom_2d_512-241.h5,https://darus.uni-stuttgart.de/api/access/datafile/136447,2D/NS_incom/,0aa745b2377c49379f91f7f000d8f23e +NS_Incom,ns_incom_inhom_2d_512-242.h5,https://darus.uni-stuttgart.de/api/access/datafile/136448,2D/NS_incom/,79033da1fbd3167b560769c8292c15f4 +NS_Incom,ns_incom_inhom_2d_512-243.h5,https://darus.uni-stuttgart.de/api/access/datafile/136450,2D/NS_incom/,896aa796fbf2052da573c30694744794 +NS_Incom,ns_incom_inhom_2d_512-244.h5,https://darus.uni-stuttgart.de/api/access/datafile/136451,2D/NS_incom/,3063dee5e25740331b4c606939c3960e +NS_Incom,ns_incom_inhom_2d_512-245.h5,https://darus.uni-stuttgart.de/api/access/datafile/136457,2D/NS_incom/,daf18bcc864d5db453f7321de3c199bf +NS_Incom,ns_incom_inhom_2d_512-246.h5,https://darus.uni-stuttgart.de/api/access/datafile/136458,2D/NS_incom/,8f97135545fdea8eb504c6f76878b77c +NS_Incom,ns_incom_inhom_2d_512-247.h5,https://darus.uni-stuttgart.de/api/access/datafile/136459,2D/NS_incom/,2e1f4cf38135b2f07a72462e0f995c2d +NS_Incom,ns_incom_inhom_2d_512-248.h5,https://darus.uni-stuttgart.de/api/access/datafile/136460,2D/NS_incom/,edaec61956c5af7be9f8996b8498c8fd +NS_Incom,ns_incom_inhom_2d_512-249.h5,https://darus.uni-stuttgart.de/api/access/datafile/136461,2D/NS_incom/,b05738fc7cdf18fecca88361ba58875f +NS_Incom,ns_incom_inhom_2d_512-25.h5,https://darus.uni-stuttgart.de/api/access/datafile/133619,2D/NS_incom/,d5e37c12c61353533ab82c6c9e48e93f +NS_Incom,ns_incom_inhom_2d_512-26.h5,https://darus.uni-stuttgart.de/api/access/datafile/133689,2D/NS_incom/,563b6261312a32460d848e5b9ca82f79 +NS_Incom,ns_incom_inhom_2d_512-27.h5,https://darus.uni-stuttgart.de/api/access/datafile/133620,2D/NS_incom/,3536aace9614abf8868d9c03bbccf9d4 +NS_Incom,ns_incom_inhom_2d_512-28.h5,https://darus.uni-stuttgart.de/api/access/datafile/136462,2D/NS_incom/,aef3e1e349adc36e3d738d7d5534548b +NS_Incom,ns_incom_inhom_2d_512-29.h5,https://darus.uni-stuttgart.de/api/access/datafile/136463,2D/NS_incom/,b5f7a9f28aab2baf06d6129a436dca79 +NS_Incom,ns_incom_inhom_2d_512-3.h5,https://darus.uni-stuttgart.de/api/access/datafile/136466,2D/NS_incom/,e9c92a19854e96c918d3a46d39994be4 +NS_Incom,ns_incom_inhom_2d_512-30.h5,https://darus.uni-stuttgart.de/api/access/datafile/136464,2D/NS_incom/,fa2275aaf761334ec2d80affb10d324f +NS_Incom,ns_incom_inhom_2d_512-31.h5,https://darus.uni-stuttgart.de/api/access/datafile/133621,2D/NS_incom/,a30bc0bb2120dcb42a8335c6d264ac7d +NS_Incom,ns_incom_inhom_2d_512-32.h5,https://darus.uni-stuttgart.de/api/access/datafile/133626,2D/NS_incom/,6579fe51ea46095aa1cd336be4701b34 +NS_Incom,ns_incom_inhom_2d_512-33.h5,https://darus.uni-stuttgart.de/api/access/datafile/133622,2D/NS_incom/,63ee81664764659f3b6436c055981c95 +NS_Incom,ns_incom_inhom_2d_512-34.h5,https://darus.uni-stuttgart.de/api/access/datafile/133623,2D/NS_incom/,e7c32d512bf95b97699c36381fea07ca +NS_Incom,ns_incom_inhom_2d_512-35.h5,https://darus.uni-stuttgart.de/api/access/datafile/133624,2D/NS_incom/,c2715bd899018326e9679775d57e1dfd +NS_Incom,ns_incom_inhom_2d_512-36.h5,https://darus.uni-stuttgart.de/api/access/datafile/136465,2D/NS_incom/,b085ae82d91f9baa2a7f9c8720f5ca48 +NS_Incom,ns_incom_inhom_2d_512-37.h5,https://darus.uni-stuttgart.de/api/access/datafile/133625,2D/NS_incom/,80c39f8aaf4168974f8bde94df177ab6 +NS_Incom,ns_incom_inhom_2d_512-38.h5,https://darus.uni-stuttgart.de/api/access/datafile/133645,2D/NS_incom/,d01ee492fcdb64cd52b7d0ae7b606370 +NS_Incom,ns_incom_inhom_2d_512-39.h5,https://darus.uni-stuttgart.de/api/access/datafile/133630,2D/NS_incom/,3f4f083589ab6d314584fa7ec795669e +NS_Incom,ns_incom_inhom_2d_512-40.h5,https://darus.uni-stuttgart.de/api/access/datafile/133627,2D/NS_incom/,c906a76f920bb63d575e347e76e47caf +NS_Incom,ns_incom_inhom_2d_512-41.h5,https://darus.uni-stuttgart.de/api/access/datafile/133696,2D/NS_incom/,b55a275050a22b9c515d9d896dba6da0 +NS_Incom,ns_incom_inhom_2d_512-42.h5,https://darus.uni-stuttgart.de/api/access/datafile/133642,2D/NS_incom/,5aa8b626b64ba979905797b9e56dd34c +NS_Incom,ns_incom_inhom_2d_512-43.h5,https://darus.uni-stuttgart.de/api/access/datafile/133650,2D/NS_incom/,d6ef4cf80d79f696dfc34d4947b14a77 +NS_Incom,ns_incom_inhom_2d_512-44.h5,https://darus.uni-stuttgart.de/api/access/datafile/133686,2D/NS_incom/,b8527daec6934026f5128a6d1f5db1e5 +NS_Incom,ns_incom_inhom_2d_512-45.h5,https://darus.uni-stuttgart.de/api/access/datafile/136467,2D/NS_incom/,7899ab1897fbd4665e4d7daea7c90bc2 +NS_Incom,ns_incom_inhom_2d_512-46.h5,https://darus.uni-stuttgart.de/api/access/datafile/136468,2D/NS_incom/,6853e9a78b6a2b2b756c4e6f8bcdac3e +NS_Incom,ns_incom_inhom_2d_512-47.h5,https://darus.uni-stuttgart.de/api/access/datafile/136469,2D/NS_incom/,4737e1e1283748baae9617ceff1e777a +NS_Incom,ns_incom_inhom_2d_512-48.h5,https://darus.uni-stuttgart.de/api/access/datafile/136470,2D/NS_incom/,5d3abc3ecbddeacb5270030954cc8064 +NS_Incom,ns_incom_inhom_2d_512-5.h5,https://darus.uni-stuttgart.de/api/access/datafile/136471,2D/NS_incom/,6a939d7418593d70faf7f90a191f841d +NS_Incom,ns_incom_inhom_2d_512-50.h5,https://darus.uni-stuttgart.de/api/access/datafile/133678,2D/NS_incom/,f30303f48d036e9aeac07fd70e61c97b +NS_Incom,ns_incom_inhom_2d_512-51.h5,https://darus.uni-stuttgart.de/api/access/datafile/133659,2D/NS_incom/,e82c2adbbc22b59168b7b587012d296d +NS_Incom,ns_incom_inhom_2d_512-52.h5,https://darus.uni-stuttgart.de/api/access/datafile/133657,2D/NS_incom/,1a072500b92f728f24526182a8e22d71 +NS_Incom,ns_incom_inhom_2d_512-53.h5,https://darus.uni-stuttgart.de/api/access/datafile/133670,2D/NS_incom/,e82c2adbbc22b59168b7b587012d296d +NS_Incom,ns_incom_inhom_2d_512-54.h5,https://darus.uni-stuttgart.de/api/access/datafile/133672,2D/NS_incom/,3516776e1d47d573ee91815ca70360b6 +NS_Incom,ns_incom_inhom_2d_512-55.h5,https://darus.uni-stuttgart.de/api/access/datafile/133662,2D/NS_incom/,49732762c231a410904cf8bddb0444cd +NS_Incom,ns_incom_inhom_2d_512-56.h5,https://darus.uni-stuttgart.de/api/access/datafile/133673,2D/NS_incom/,f50c0e5f4ad659c1b5d6f7343e072bdf +NS_Incom,ns_incom_inhom_2d_512-57.h5,https://darus.uni-stuttgart.de/api/access/datafile/133674,2D/NS_incom/,1a072500b92f728f24526182a8e22d71 +NS_Incom,ns_incom_inhom_2d_512-58.h5,https://darus.uni-stuttgart.de/api/access/datafile/133658,2D/NS_incom/,ae1e262d7f1a56369b13b6d5de80e03d +NS_Incom,ns_incom_inhom_2d_512-59.h5,https://darus.uni-stuttgart.de/api/access/datafile/133656,2D/NS_incom/,b99c5b112d4a7dfca053cb2c78ebe581 +NS_Incom,ns_incom_inhom_2d_512-6.h5,https://darus.uni-stuttgart.de/api/access/datafile/136472,2D/NS_incom/,36156ac1f8cecb47d076a1ee0707dec5 +NS_Incom,ns_incom_inhom_2d_512-60.h5,https://darus.uni-stuttgart.de/api/access/datafile/133675,2D/NS_incom/,85a8bd3bd539acb7685393393294e916 +NS_Incom,ns_incom_inhom_2d_512-61.h5,https://darus.uni-stuttgart.de/api/access/datafile/133676,2D/NS_incom/,3b43956b04c4e0e619ba671cdfbbc834 +NS_Incom,ns_incom_inhom_2d_512-62.h5,https://darus.uni-stuttgart.de/api/access/datafile/133677,2D/NS_incom/,8bf9ac2f7dc47e3617ba9de2ac10d582 +NS_Incom,ns_incom_inhom_2d_512-63.h5,https://darus.uni-stuttgart.de/api/access/datafile/133680,2D/NS_incom/,49732762c231a410904cf8bddb0444cd +NS_Incom,ns_incom_inhom_2d_512-64.h5,https://darus.uni-stuttgart.de/api/access/datafile/133628,2D/NS_incom/,c0e54ed459c84470986818d6d89be0c8 +NS_Incom,ns_incom_inhom_2d_512-65.h5,https://darus.uni-stuttgart.de/api/access/datafile/133635,2D/NS_incom/,69f1b9013bb76fba1006272951e12845 +NS_Incom,ns_incom_inhom_2d_512-66.h5,https://darus.uni-stuttgart.de/api/access/datafile/133629,2D/NS_incom/,c79245b195ec850a40c0957056bffad7 +NS_Incom,ns_incom_inhom_2d_512-67.h5,https://darus.uni-stuttgart.de/api/access/datafile/133277,2D/NS_incom/,f8465b71d9afe577ba30cbe5d0c490e6 +NS_Incom,ns_incom_inhom_2d_512-68.h5,https://darus.uni-stuttgart.de/api/access/datafile/133681,2D/NS_incom/,ae1e262d7f1a56369b13b6d5de80e03d +NS_Incom,ns_incom_inhom_2d_512-69.h5,https://darus.uni-stuttgart.de/api/access/datafile/133631,2D/NS_incom/,b5159c70d9b14e480bb520961bafcae4 +NS_Incom,ns_incom_inhom_2d_512-7.h5,https://darus.uni-stuttgart.de/api/access/datafile/136473,2D/NS_incom/,e1b06fd07d09227a0767baa260f361e2 +NS_Incom,ns_incom_inhom_2d_512-70.h5,https://darus.uni-stuttgart.de/api/access/datafile/133632,2D/NS_incom/,3aee2aa9b1a14c7d04ce2770e95a354a +NS_Incom,ns_incom_inhom_2d_512-71.h5,https://darus.uni-stuttgart.de/api/access/datafile/133634,2D/NS_incom/,2f5b2bf56e72d61c41d4b056ac540fb0 +NS_Incom,ns_incom_inhom_2d_512-72.h5,https://darus.uni-stuttgart.de/api/access/datafile/133682,2D/NS_incom/,b99c5b112d4a7dfca053cb2c78ebe581 +NS_Incom,ns_incom_inhom_2d_512-73.h5,https://darus.uni-stuttgart.de/api/access/datafile/133273,2D/NS_incom/,9d1c5e98ee4fe97c7498d2f574c165ae +NS_Incom,ns_incom_inhom_2d_512-74.h5,https://darus.uni-stuttgart.de/api/access/datafile/133633,2D/NS_incom/,475427b7337f1fd7114b7e6c32a2f709 +NS_Incom,ns_incom_inhom_2d_512-75.h5,https://darus.uni-stuttgart.de/api/access/datafile/133694,2D/NS_incom/,170b32d051ed72716aab0f40b13f359e +NS_Incom,ns_incom_inhom_2d_512-76.h5,https://darus.uni-stuttgart.de/api/access/datafile/133636,2D/NS_incom/,2666826a3944f5999a1053242e9588a9 +NS_Incom,ns_incom_inhom_2d_512-77.h5,https://darus.uni-stuttgart.de/api/access/datafile/133638,2D/NS_incom/,c58cc6dd09fb89c1b5fb95081f1220c9 +NS_Incom,ns_incom_inhom_2d_512-78.h5,https://darus.uni-stuttgart.de/api/access/datafile/133637,2D/NS_incom/,b183a32a3e2230968d5b7f20d9dbf818 +NS_Incom,ns_incom_inhom_2d_512-79.h5,https://darus.uni-stuttgart.de/api/access/datafile/133651,2D/NS_incom/,def3145be356b7d020ea09e5232bb60f +NS_Incom,ns_incom_inhom_2d_512-8.h5,https://darus.uni-stuttgart.de/api/access/datafile/136449,2D/NS_incom/,2e6a62550680a255d4dfec24a761d5cf +NS_Incom,ns_incom_inhom_2d_512-80.h5,https://darus.uni-stuttgart.de/api/access/datafile/133639,2D/NS_incom/,07a1edc16fb7d981155ad4173c42abf1 +NS_Incom,ns_incom_inhom_2d_512-81.h5,https://darus.uni-stuttgart.de/api/access/datafile/133276,2D/NS_incom/,3a58395f316b2158ea61844eadd4109f +NS_Incom,ns_incom_inhom_2d_512-82.h5,https://darus.uni-stuttgart.de/api/access/datafile/133640,2D/NS_incom/,a0f4505418beb4da8b7f84a83efc3904 +NS_Incom,ns_incom_inhom_2d_512-83.h5,https://darus.uni-stuttgart.de/api/access/datafile/133272,2D/NS_incom/,89993b4f3ac2653f6377941e640e2233 +NS_Incom,ns_incom_inhom_2d_512-84.h5,https://darus.uni-stuttgart.de/api/access/datafile/133643,2D/NS_incom/,c5269398ec4edb0f5891d4efdb2fc89d +NS_Incom,ns_incom_inhom_2d_512-85.h5,https://darus.uni-stuttgart.de/api/access/datafile/133641,2D/NS_incom/,090862c29ecbc2c4232ddbacca3fb230 +NS_Incom,ns_incom_inhom_2d_512-86.h5,https://darus.uni-stuttgart.de/api/access/datafile/133647,2D/NS_incom/,a712449bef87318a620d1663c4ffde27 +NS_Incom,ns_incom_inhom_2d_512-87.h5,https://darus.uni-stuttgart.de/api/access/datafile/133644,2D/NS_incom/,920a3dcb3b216f0d9a4ab0b9b4c8745a +NS_Incom,ns_incom_inhom_2d_512-88.h5,https://darus.uni-stuttgart.de/api/access/datafile/133655,2D/NS_incom/,cc96b1e357f264f12dcfbb41736a53bf +NS_Incom,ns_incom_inhom_2d_512-89.h5,https://darus.uni-stuttgart.de/api/access/datafile/133646,2D/NS_incom/,7fd484851410b7139bfea4166074fe00 +NS_Incom,ns_incom_inhom_2d_512-9.h5,https://darus.uni-stuttgart.de/api/access/datafile/136475,2D/NS_incom/,254bb44710b625f716b61d51d5d1e58e +NS_Incom,ns_incom_inhom_2d_512-90.h5,https://darus.uni-stuttgart.de/api/access/datafile/133652,2D/NS_incom/,c6c4e27d6ba19b4ccb5f27e67dd9cdf1 +NS_Incom,ns_incom_inhom_2d_512-91.h5,https://darus.uni-stuttgart.de/api/access/datafile/136474,2D/NS_incom/,527b23b7e4ca4d18c5c036c41e15e4fe +NS_Incom,ns_incom_inhom_2d_512-92.h5,https://darus.uni-stuttgart.de/api/access/datafile/133648,2D/NS_incom/,84fd5f64db474f8e20b670d01929d57a +NS_Incom,ns_incom_inhom_2d_512-93.h5,https://darus.uni-stuttgart.de/api/access/datafile/133649,2D/NS_incom/,20583d7edf7da9a80f16a26c8388a65e +NS_Incom,ns_incom_inhom_2d_512-94.h5,https://darus.uni-stuttgart.de/api/access/datafile/133691,2D/NS_incom/,8540e7f1cc6a9e2bc64726a325ddacfb +NS_Incom,ns_incom_inhom_2d_512-95.h5,https://darus.uni-stuttgart.de/api/access/datafile/133690,2D/NS_incom/,25d4c08d5534d0f10c877220e258640c +NS_Incom,ns_incom_inhom_2d_512-96.h5,https://darus.uni-stuttgart.de/api/access/datafile/133270,2D/NS_incom/,93e772cbfd36d434e97a079d701d5382 +NS_Incom,ns_incom_inhom_2d_512-97.h5,https://darus.uni-stuttgart.de/api/access/datafile/133653,2D/NS_incom/,ffa1d46e58f67aa1932fc4ffd2e7716a +NS_Incom,ns_incom_inhom_2d_512-98.h5,https://darus.uni-stuttgart.de/api/access/datafile/133687,2D/NS_incom/,c28112e1339cda43f67dce03536e3455 +NS_Incom,ns_incom_inhom_2d_512-99.h5,https://darus.uni-stuttgart.de/api/access/datafile/133654,2D/NS_incom/,abfe923a224cb4515dd545d5858b2124 +SWE,2D_rdb_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133021,2D/shallow-water/,75d838c47aa410694bdc912ea7f22282 +3D_CFD,3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164693,3D/Train/,45892a12d1066d54af74badae55c438e +3D_CFD,3D_CFD_Turb_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164694,3D/Train/,4f0eacaec5ff000bbd9a31026ea207b8 +3D_CFD,BlastWave.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133264,3D/Test/BlastWave/,f901a1ec75925c1d861ac99a825878f3 +3D_CFD,Turb_M01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133224,3D/Test/Turbulence/,b27495031f434d01762bb0b819ac71e2 +3D_CFD,Turb_M05.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133225,3D/Test/Turbulence/,f9407dff1a75a1d14d93e7dd570af728 +3D_CFD,Turb_M1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135833,3D/Test/Turbulence/,3758f23f71684ac666e0b1e91da0a1c4 +3D_CFD,Turb_M2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133227,3D/Test/Turbulence/,12e528dc8ab800f69474600ec58b24d3 +3D_CFD,Turb_M4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133228,3D/Test/Turbulence/,8db384feba75903a8c5b21ebeba40083 From c09984b61059d928a9ee6b362a0546b049d6bd1a Mon Sep 17 00:00:00 2001 From: leiterrl Date: Fri, 25 Nov 2022 14:48:41 +0100 Subject: [PATCH 048/137] Require users to provide download path explicitly Co-authored-by: Timothy Praditia Co-authored-by: Mario --- pdebench/data_download/download_direct.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 0f2b04f..dfe02cd 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -88,8 +88,7 @@ def download_data(root_folder, pde_name): arg_parser.add_argument( "--root_folder", type=str, - default="./data", - required=False, + required=True, help="Root folder where the data will be downloaded", ) arg_parser.add_argument( From 52e33307080de3cd50bf17d748fdfc02daf6dcf5 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Fri, 25 Nov 2022 15:12:17 +0100 Subject: [PATCH 049/137] Add data download readme --- pdebench/data_download/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pdebench/data_download/README.md diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md new file mode 100644 index 0000000..82174dd --- /dev/null +++ b/pdebench/data_download/README.md @@ -0,0 +1,21 @@ + +# Downloading PDEBench Datasets + +Here we enumerate the list of available PDEs in PDEBench. + + + +| PDEs | Download | Dataset Size | +| ----------- | :----------------------------------------------------------- | ------------ | +| advection | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name advection` | | +| burgers | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name burgers` | | +| 1d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 1d_cfd` | | +| diff_sorp | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name diff_sorp` | | +| 1d_reacdiff | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 1d_reacdiff` | | +| 2d_reacdiff | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 2d_reacdiff` | | +| 2d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 2d_cfd` | | +| 3d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 3d_cfd` | | +| darcy | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name darcy` | | +| ns_incom | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name ns_incom` | | +| swe | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name swe` | | + From 911d7f2f45dbb6f71ddb7142354cdf3371652392 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 25 Nov 2022 16:24:56 +0100 Subject: [PATCH 050/137] update readme with data download details, fix private url issue --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2fa5a4f..0b07792 100644 --- a/README.md +++ b/README.md @@ -142,9 +142,12 @@ There is an example in `example.env`. ## Data Download -The data download codes are contained in [data_download](./pdebench/data_download): -- `download.py` to download the data. -- `config` directory contains the yaml files storing the configuration for the data downloader. Anyfiles in the dataset matching `args.filename` will be downloaded into `args.data_folder`. +The download scripts are provided in [data_download](./pdebench/data_download). There are two options to download data. + +1) Using `download_direct.py` (**recommended**) + - Retrieves data shards directly using URLs. Sample command for each PDE is given the README file in the [data_download](./pdebench/data_download) directory. +2) Using `download_easydataverse.py` (might encounter errors/issues, hence not recommended) + - Use the config files from the `config` directory that contains the yaml files storing the configuration. Any files in the dataset matching `args.filename` will be downloaded into `args.data_folder`. ## Baseline Models @@ -219,9 +222,9 @@ An example to run the forward model training can be found in [run_forward_1D.sh] - t_max: float, end of temporal domain. ## Datasets and pretrained models -We provide the benchmark datasets we used in the paper through our [data repository](https://darus.uni-stuttgart.de/privateurl.xhtml?token=1be27526-348a-40ed-9fd0-c62f588efc01). +We provide the benchmark datasets we used in the paper through our [DaRUS data repository](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986). The data generation configuration can be found in the paper. -Additionally, the pretrained models are also available to be downloaded [here](https://darus.uni-stuttgart.de/privateurl.xhtml?token=cd862f8c-8e1b-49d2-b4da-b35f8df5ac85). To use the pretrained models, users can specify the argument `continue_training: True` in the [config file](./pdebench/models/config/config.yaml). +Additionally, the pretrained models are also available to be downloaded from [PDEBench Pretrained Models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987) DaRus repository. To use the pretrained models, users can specify the argument `continue_training: True` in the [config file](./pdebench/models/config/config.yaml). ## Citations From 6e82da913539908eef6f9e16979ae7707653409c Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 25 Nov 2022 17:11:35 +0100 Subject: [PATCH 051/137] add missing urls/metadata for 2d cfd train --- pdebench/data_download/download_metadata.csv | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv index 1755737..2f1f601 100644 --- a/pdebench/data_download/download_metadata.csv +++ b/pdebench/data_download/download_metadata.csv @@ -92,6 +92,14 @@ Diff_Sorp,1D_diff-sorp_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafi 1D_ReacDiff,ReacDiff_react_Nu5.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133174,1D/ReactionDiffusion/Test/,b04c22629bf86d85929386aaf4a5f95d 1D_ReacDiff,ReacDiff_react_Nu5.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133175,1D/ReactionDiffusion/Test/,1738e4374456af974e4d0058d3426a69 1D_ReacDiff,ReacDiff_react_Nu5.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133176,1D/ReactionDiffusion/Test/,4e3cf0ac66e85dbb77c79861076db8e8 +2D_CFD,2D_CFD_Rand_M0.1_Eta0.01_Zeta0.01_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164687,2D/CFD/2D_Train_Rand/,5b21dcccaef4d2145ca579a71153c580 +2D_CFD,2D_CFD_Rand_M0.1_Eta0.1_Zeta0.1_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164688,2D/CFD/2D_Train_Rand/,b2733888745a1d64e36df813e979910b +2D_CFD,2D_CFD_Rand_M0.1_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164689,2D/CFD/2D_Train_Rand/,ae2552864d0856f58dc74132a2f95d20 +2D_CFD,2D_CFD_Rand_M1.0_Eta0.01_Zeta0.01_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164690,2D/CFD/2D_Train_Rand/,21969082d0e9524bcc4708e216148e60 +2D_CFD,2D_CFD_Rand_M1.0_Eta0.1_Zeta0.1_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164691,2D/CFD/2D_Train_Rand/,75a34f34a3dcd9bc8ae4b7adf8bf372c +2D_CFD,2D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164692,2D/CFD/2D_Train_Rand/,04fcc44c39acea69e2869b84cd653d4a +2D_CFD,2D_CFD_Turb_M0.1_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164685,2D/CFD/2D_Train_Turb/,844555000d342d2947162c6cf46798e7 +2D_CFD,2D_CFD_Turb_M1.0_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164686,2D/CFD/2D_Train_Turb/,3f2c7376cde5fb072db0f9814f1c6992 2D_CFD,2D_shock.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133193,2D/CFD/Test/2DShock/,b4098c20e2e0a25cd8ee229c9294b899 2D_CFD,KH_M01_dk10_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133209,2D/CFD/Test/KH/,54e278b3a7419107a5cd6a8a019225d5 2D_CFD,KH_M01_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133210,2D/CFD/Test/KH/,29ce31d3ce61de3b90ac0b42716c061c From 2f1671099d19e75c21986722f04102096ce3680e Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 25 Nov 2022 19:23:33 +0100 Subject: [PATCH 052/137] update data download readme --- pdebench/data_download/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 82174dd..6f503ce 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -1,21 +1,21 @@ # Downloading PDEBench Datasets -Here we enumerate the list of available PDEs in PDEBench. +Here we enumerate the list of all available PDEs in PDEBench and the commands to download them. | PDEs | Download | Dataset Size | | ----------- | :----------------------------------------------------------- | ------------ | -| advection | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name advection` | | -| burgers | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name burgers` | | -| 1d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 1d_cfd` | | -| diff_sorp | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name diff_sorp` | | -| 1d_reacdiff | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 1d_reacdiff` | | -| 2d_reacdiff | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 2d_reacdiff` | | -| 2d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 2d_cfd` | | -| 3d_cfd | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name 3d_cfd` | | -| darcy | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name darcy` | | -| ns_incom | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name ns_incom` | | -| swe | `python download_direct.py --root_folder $proj_home/pdebench/data --pde_name swe` | | +| advection | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name advection` | | +| burgers | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name burgers` | | +| 1d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 1d_cfd` | | +| diff_sorp | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name diff_sorp` | | +| 1d_reacdiff | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 1d_reacdiff` | | +| 2d_reacdiff | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 2d_reacdiff` | | +| 2d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 2d_cfd` | | +| 3d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 3d_cfd` | | +| darcy | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name darcy` | | +| ns_incom | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name ns_incom` | | +| swe | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name swe` | | From fe4eaa7bb6d32ac4a84e3171b127e3c7da5bf4d0 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 25 Nov 2022 19:24:12 +0100 Subject: [PATCH 053/137] add tqdm progress bar for data downloads --- pdebench/data_download/download_direct.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index dfe02cd..7bc082e 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -1,6 +1,7 @@ -import argparse import os +import argparse +from tqdm import tqdm import pandas as pd from torchvision.datasets.utils import download_url @@ -73,7 +74,7 @@ def download_data(root_folder, pde_name): pde_df = parse_metadata(pde_name) # Iterate filtered dataframe and download the files - for index, row in pde_df.iterrows(): + for index, row in tqdm(pde_df.iterrows(), total=pde_df.shape[0]): file_path = os.path.join(root_folder, row["Path"]) download_url(row["URL"], file_path, row["Filename"], md5=row["MD5"]) From 5d2bb95718dc9ab20bd003c3c5e977f7d3a29b46 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 26 Nov 2022 18:45:11 +0100 Subject: [PATCH 054/137] update readme with partial dataset size --- pdebench/data_download/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 6f503ce..635903d 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -5,17 +5,17 @@ Here we enumerate the list of all available PDEs in PDEBench and the commands to -| PDEs | Download | Dataset Size | +| PDEs | Dataset Download | Dataset Size | | ----------- | :----------------------------------------------------------- | ------------ | -| advection | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name advection` | | -| burgers | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name burgers` | | -| 1d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 1d_cfd` | | -| diff_sorp | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name diff_sorp` | | -| 1d_reacdiff | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 1d_reacdiff` | | -| 2d_reacdiff | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 2d_reacdiff` | | -| 2d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 2d_cfd` | | -| 3d_cfd | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name 3d_cfd` | | -| darcy | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name darcy` | | -| ns_incom | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name ns_incom` | | -| swe | `python download_direct.py --root_folder $proj_home/PDEBench/pdebench/data --pde_name swe` | | +| advection | ```python download_direct.py --root_folder $proj_home/data --pde_name advection``` | 47 GB | +| burgers | ```python download_direct.py --root_folder $proj_home/data --pde_name burgers``` | 93 GB | +| 1d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_cfd``` | 88 GB | +| diff_sorp | ```python download_direct.py --root_folder $proj_home/data --pde_name diff_sorp``` | 4 GB | +| 1d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_reacdiff``` | TBD | +| 2d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_reacdiff``` | TBD | +| 2d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd``` | TBD | +| 3d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd``` | TBD | +| darcy | ```python download_direct.py --root_folder $proj_home/data --pde_name darcy``` | TBD | +| ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | TBD | +| swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | TBD | From f748c954b670f1d81be533bb385d70a38d1ae853 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 26 Nov 2022 18:46:47 +0100 Subject: [PATCH 055/137] fix bug for diffusion-sorption dataset download --- pdebench/data_download/download_direct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 7bc082e..404437b 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -38,7 +38,7 @@ def parse_metadata(pde_names): "advection", "burgers", "1d_cfd", - "diff-sorp", + "diff_sorp", "1d_reacdiff", "2d_cfd", "darcy", From 8fdf71056a396947180f504a0281e8213138a781 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Mon, 28 Nov 2022 22:28:50 +0100 Subject: [PATCH 056/137] Fixed division by zero bug in the analyze result forward plotting --- pdebench/models/analyse_result_forward.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 8d5e575..6397843 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -213,7 +213,11 @@ def main(): models = index.get_level_values(2).drop_duplicates() num_models = len(models) x = np.arange(num_pdes) - width = 0.5/(num_models-1) + + if num_models == 1: + width = 0.5 + else: + width = 0.5/(num_models-1) fig, ax = plt.subplots(figsize=(8,6)) for i in range(num_models): From e4817b5606ecdde0e66dd17c947afd4e90b27b0a Mon Sep 17 00:00:00 2001 From: leiterrl Date: Mon, 28 Nov 2022 22:42:07 +0100 Subject: [PATCH 057/137] Add initial draft of visualization script Co-authored-by: Timothy Praditia --- pdebench/data_download/visualize_pdes.py | 217 +++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 pdebench/data_download/visualize_pdes.py diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py new file mode 100644 index 0000000..08d2ce2 --- /dev/null +++ b/pdebench/data_download/visualize_pdes.py @@ -0,0 +1,217 @@ +import argparse +import os +import h5py +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + + +pdes = ( + "advection", + "burgers", + "1d_cfd", + "diff_sorp", + "1d_reacdiff", + "2d_cfd", + "darcy", + "2d_reacdiff", + "ns_incom", + "swe", + "3d_cfd", +) + + +def visualize_diff_sorp(path, seed=None): + """ + This function animates the diffusion sorption data. + + Args: + path : path to the desired file + seed : seed to select a specific sample/batch, ranging from 0-9999 + """ + + # Read the h5 file and store the data + h5_file = h5py.File(os.path.join(path, "1D_diff-sorp_NA_NA.h5"), "r") + + if not seed: + seed = np.random.randint(0, len(h5_file.keys())) + + # Ensure the seed number is defined + assert seed < data.shape[0], "Seed number too high!" + + seed = str(seed).zfill(4) + data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 1024, 1] + + h5_file.close() + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + for i in range(data.shape[0]): + im = ax.plot(data[i].squeeze(), animated=True) + if i == 0: + ax.plot(data[0].squeeze()) # show an initial one first + ax.plot + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_2d_reacdiff(path, seed=None): + """ + This function animates the 2D reaction-diffusion data. + + Args: + path : path to the desired file + seed : seed to select a specific sample/batch, ranging from 0-9999 + """ + + # Read the h5 file and store the data + h5_file = h5py.File(os.path.join(path, "2D_diff-react_NA_NA.h5"), "r") + + if not seed: + seed = np.random.randint(0, len(h5_file.keys())) + + # Ensure the seed number is defined + assert seed < data.shape[0], "Seed number too high!" + + seed = str(seed).zfill(4) + data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 2] + + h5_file.close() + + # Initialize plot + fig, ax = plt.subplots(1, 2) + + # Store the plot handle at each time step in the 'ims' list + ims = [] + for i in range(data.shape[0]): + im1 = ax[0].imshow(data[i, ..., 0].squeeze(), animated=True) + im2 = ax[1].imshow(data[i, ..., 1].squeeze(), animated=True) + if i == 0: + ax[0].imshow(data[0, ..., 0].squeeze()) # show an initial one first + ax[1].imshow(data[0, ..., 1].squeeze()) # show an initial one first + ims.append([im1, im2]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_swe(path, seed=None): + """ + This function animates the shallow water equation data. + + Args: + path : path to the desired file + seed : seed to select a specific sample/batch, ranging from 0-9999 + """ + + # Read the h5 file and store the data + h5_file = h5py.File(os.path.join(path, "2D_rdb_NA_NA.h5"), "r") + + if not seed: + seed = np.random.randint(0, len(h5_file.keys())) + + # Ensure the seed number is defined + assert seed < data.shape[0], "Seed number too high!" + + seed = str(seed).zfill(4) + data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 1] + + h5_file.close() + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + for i in range(data.shape[0]): + im = ax.imshow(data[i].squeeze(), animated=True) + if i == 0: + ax.imshow(data[0].squeeze()) # show an initial one first + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_burgers(): + pass + + +def visualize_advection(): + pass + + +def visualize_1d_cfd(): + pass + + +def visualize_2d_cfd(): + pass + + +def visualize_3d_cfd(): + pass + + +def visualize_ns_incom(): + pass + + +def visualize_darcy(): + pass + + +def visualize_1d_reacdiff(): + pass + + +if __name__ == "__main__": + arg_parser = argparse.ArgumentParser( + prog="Visualize PDEs", + description="Helper script to visualize PDEs in the PDEBench dataset", + epilog="", + ) + + arg_parser.add_argument( + "--data_path", + type=str, + default=".", + help="Path to the hdf5 data where the downloaded data reside", + ) + arg_parser.add_argument( + "--pde_name", type=str, help="Name of the PDE dataset to download" + ) + arg_parser.add_argument( + "--seed_number", + type=int, + default=None, + help="Seed number to define which sample/batch to be plotted", + ) + + args = arg_parser.parse_args() + + if args.pde_name == "diff_sorp": + visualize_diff_sorp(args.data_path, args.seed_number) + elif args.pde_name == "2d_reacdiff": + visualize_2d_reacdiff(args.data_path, args.seed_number) + elif args.pde_name == "swe": + visualize_swe(args.data_path, args.seed_number) + else: + raise ValueError("PDE name not recognized!") From 1652ac4cfa7399f3817aefba5a56e15d3dfbf979 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 29 Nov 2022 20:28:14 +0100 Subject: [PATCH 058/137] update readme with partial dataset size --- pdebench/data_download/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 635903d..4c7a0e3 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -11,11 +11,11 @@ Here we enumerate the list of all available PDEs in PDEBench and the commands to | burgers | ```python download_direct.py --root_folder $proj_home/data --pde_name burgers``` | 93 GB | | 1d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_cfd``` | 88 GB | | diff_sorp | ```python download_direct.py --root_folder $proj_home/data --pde_name diff_sorp``` | 4 GB | -| 1d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_reacdiff``` | TBD | -| 2d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_reacdiff``` | TBD | -| 2d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd``` | TBD | -| 3d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd``` | TBD | -| darcy | ```python download_direct.py --root_folder $proj_home/data --pde_name darcy``` | TBD | +| 1d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_reacdiff``` | 62 GB | +| 2d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_reacdiff``` | 13 GB | +| 2d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd``` | 551 GB | +| 3d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd``` | 285 GB | +| darcy | ```python download_direct.py --root_folder $proj_home/data --pde_name darcy``` | 6.2 GB | | ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | TBD | -| swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | TBD | +| swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | 6.2 GB | From 8a8083fa93c1cf2b0be22674f6b07b23fecbbbeb Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 1 Dec 2022 14:31:01 +0100 Subject: [PATCH 059/137] update readme with full dataset size --- pdebench/data_download/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 4c7a0e3..703ff0c 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -16,6 +16,6 @@ Here we enumerate the list of all available PDEs in PDEBench and the commands to | 2d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd``` | 551 GB | | 3d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd``` | 285 GB | | darcy | ```python download_direct.py --root_folder $proj_home/data --pde_name darcy``` | 6.2 GB | -| ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | TBD | +| ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | 2.3 TB | | swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | 6.2 GB | From 4d2ddd1269812f03461e5b890439591f6a1f4117 Mon Sep 17 00:00:00 2001 From: dan mackinlay Date: Mon, 12 Dec 2022 14:28:20 +1100 Subject: [PATCH 060/137] Explicit licensing under MIT license with exceptions --- LICENSE.txt | 9 +++++++++ README.md | 6 ++++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..b36c10f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,9 @@ +Except where otherwise stated this code is released under the MIT license. + + Copyright 2022 NEC Labs Europe GmbH, Stuttgart University, CSIRO and PDEbench contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 0b07792..7ac3f90 100644 --- a/README.md +++ b/README.md @@ -263,5 +263,7 @@ url = {https://doi.org/10.18419/darus-2986} * [Simon Brown](https://github.com/SimonSyBrown) -## License -MIT for solver code and baseline code, and NLE Academic License for selected code +## License + +MIT licensed, except where otherwise stated. +See `LICENSE.txt` file. \ No newline at end of file From a02d12a762902cd695175502fd1b6a381500b793 Mon Sep 17 00:00:00 2001 From: dan mackinlay Date: Mon, 12 Dec 2022 15:01:16 +1100 Subject: [PATCH 061/137] Organisational affiliation for code developers --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0b07792..e89c2c3 100644 --- a/README.md +++ b/README.md @@ -251,16 +251,18 @@ url = {https://doi.org/10.18419/darus-2986} ## Code contributors -* [Makato Takamoto](https://github.com/mtakamoto-D) -* [Timothy Praditia](https://github.com/timothypraditia) -* [Raphael Leiteritz](https://github.com/leiterrl) -* [Francesco Alesiani](https://github.com/falesiani) -* [Dan MacKinlay](https://danmackinlay.name/) -* [John Kim](https://github.com/johnmjkim) -* [Gefei Shan](https://github.com/davecatmeow) -* [Yizhou Yang](https://github.com/verdantwynnd) -* [Ran Zhang](https://github.com/maphyca) -* [Simon Brown](https://github.com/SimonSyBrown) + +* [Makato Takamoto](https://github.com/mtakamoto-D) ([NEC laboratories Europe](https://www.neclab.eu/)) +* [Timothy Praditia](https://github.com/timothypraditia) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +* [Raphael Leiteritz](https://github.com/leiterrl) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +* [Francesco Alesiani](https://github.com/falesiani) ([NEC laboratories Europe](https://www.neclab.eu/)) +* [Dan MacKinlay](https://danmackinlay.name/) ([CSIRO’s Data61](https://data61.csiro.au/)) +* [Mario Kalimuthu](https://github.com/kmario23) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +* [John Kim](https://github.com/johnmjkim) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +* [Gefei Shan](https://github.com/davecatmeow) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +* [Yizhou Yang](https://github.com/verdantwynnd) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +* [Ran Zhang](https://github.com/maphyca) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +* [Simon Brown](https://github.com/SimonSyBrown) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) ## License From cb7c868e3310d5bd22d2544d1358013fbd24503a Mon Sep 17 00:00:00 2001 From: dan mackinlay Date: Mon, 12 Dec 2022 18:29:34 +1100 Subject: [PATCH 062/137] minor typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b07792..22f9b3b 100644 --- a/README.md +++ b/README.md @@ -206,9 +206,9 @@ An example to run the forward model training can be found in [run_forward_1D.sh] - mcmc_num_chains: 1 - num_samples_max: 1000 - in_channels_hid: 64 - - inverse_model_type: striung, type of inverse inference model, ProbRasterLatent, InitialConditionInterp - - inverse_epochs: int, number of epochs for the gradint based method - - inverse_learning_rate: float, learning rate for the gradint based method + - inverse_model_type: string, type of inverse inference model, ProbRasterLatent, InitialConditionInterp + - inverse_epochs: int, number of epochs for the gradient based method + - inverse_learning_rate: float, learning rate for the gradient based method - inverse_verbose_flag: bool, some printing #### Plotting specific args: From 4e68931ef953ff1609710e7e1501ad41aeb69b82 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 13 Dec 2022 20:26:31 +0100 Subject: [PATCH 063/137] update bibtex URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ec7fb8..3aa4b12 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacK title = {{PDEBench: An Extensive Benchmark for Scientific Machine Learning}}, year = {2022}, booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2022) Track on Datasets and Benchmarks}, -url = {https://doi.org/10.18419/darus-2986} +url = {https://arxiv.org/abs/2210.07182} } From 067e07f2abced84fac9cf1a65e7c940a35df0a0a Mon Sep 17 00:00:00 2001 From: Francesco Alesiani <67260726+falesiani@users.noreply.github.com> Date: Thu, 15 Dec 2022 09:45:03 +0100 Subject: [PATCH 064/137] based on comment from nevoliu Fixed a call to FNO3d instead of FNO2d --- pdebench/models/inverse/train.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/models/inverse/train.py b/pdebench/models/inverse/train.py index d8cfcf1..30a2f0f 100644 --- a/pdebench/models/inverse/train.py +++ b/pdebench/models/inverse/train.py @@ -260,7 +260,7 @@ def main(cfg: DictConfig): initial_step=cfg.args.initial_step).to(device) if dimensions == 6: - model = FNO2d(num_channels=cfg.args.num_channels, + model = FNO3d(num_channels=cfg.args.num_channels, width=cfg.args.width, modes1=cfg.args.modes, modes2=cfg.args.modes, @@ -407,4 +407,4 @@ def main(cfg: DictConfig): df_metric.to_csv(inverse_metric_filename) if __name__ == '__main__': - main() \ No newline at end of file + main() From c99e711798483fb351b90bd22c5b24f92328855f Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 15 Dec 2022 20:58:03 +0100 Subject: [PATCH 065/137] add directory tour --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/README.md b/README.md index 3aa4b12..42b7b5f 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,65 @@ We provide the benchmark datasets we used in the paper through our [DaRUS data r The data generation configuration can be found in the paper. Additionally, the pretrained models are also available to be downloaded from [PDEBench Pretrained Models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987) DaRus repository. To use the pretrained models, users can specify the argument `continue_training: True` in the [config file](./pdebench/models/config/config.yaml). +------- + +## Directory Tour + +Below is an illustration of the directory structure of PDEBench. + +``` +📂 pdebench +|_📁 models + |_📁 pinn # Model: Physics-Informed Neural Network + |_📄 train.py + |_📄 utils.py + |_📄 pde_definitions.py + |_📁 fno # Model: Fourier Neural Operator + |_📄 train.py + |_📄 utils.py + |_📄 fno.py + |_📁 unet # Model: U-Net + |_📄 train.py + |_📄 utils.py + |_📄 unet.py + |_📁 inverse # Model: Gradient-Based Inverse Method + |_📄 train.py + |_📄 utils.py + |_📄 inverse.py + |_📁 config # Config: All config files reside here + |_📄 train_models_inverse.py + |_📄 run_forward_1D.sh + |_📄 analyse_result_inverse.py + |_📄 train_models_forward.py + |_📄 run_inverse.sh + |_📄 metrics.py + |_📄 analyse_result_forward.py +|_📁 data_download # Data: Scripts to download data from DaRUS + |_📁 config + |_📄 download_direct.py + |_📄 download_easydataverse.py + |_📄 visualize_pdes.py + |_📄 README.md + |_📄 download_metadata.csv +|_📁 data_gen # Data: Scripts to generate data + |_📁 configs + |_📁 data_gen_NLE + |_📁 src + |_📁 notebooks + |_📄 gen_diff_sorp.py + |_📄 plot.py + |_📄 example.env + |_📄 gen_ns_incomp.py + |_📄 gen_diff_react.py + |_📄 uploader.py + |_📄 gen_radial_dam_break.py +|_📄 __init__.py +``` + + + +------ + ## Citations From 7bfa5ea60ab0fe188c4b964c0ab6811ca7eb0381 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Fri, 16 Dec 2022 12:44:25 +0100 Subject: [PATCH 066/137] Add workaround for parallel HDF5 writing: - catch FileIO exceptions and wait until resources are not busy anymore - remove some verbose output --- pdebench/data_gen/gen_diff_react.py | 45 +++++++++++-------- pdebench/data_gen/gen_diff_sorp.py | 39 +++++++++------- pdebench/data_gen/gen_radial_dam_break.py | 21 ++++++--- pdebench/data_gen/src/sim_diff_react.py | 2 +- pdebench/data_gen/src/sim_diff_sorp.py | 2 +- pdebench/data_gen/src/sim_radial_dam_break.py | 8 ++-- 6 files changed, 70 insertions(+), 47 deletions(-) diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index 67d4ac3..39abed8 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -48,24 +48,33 @@ def simulator(config, i): seed_str = str(i).zfill(4) - with h5py.File(utils.expand_path(config.output_path), "a") as data_f: - ## Chunking for compression and data access - ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage - ## should be by batch and less than 1MB - ## lzf compression for float32 is kind of pointless though. - data_f.create_dataset( - f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/y", data=sim_obj.y, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" - ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + while True: + try: + with h5py.File(utils.expand_path(config.output_path), "a") as data_f: + ## Chunking for compression and data access + ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage + ## should be by batch and less than 1MB + ## lzf compression for float32 is kind of pointless though. + data_f.create_dataset( + f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/y", data=sim_obj.y, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + ) + data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + except IOError: + time.sleep(0.1) + continue + else: + break + + @hydra.main(config_path="configs/", config_name="diff-react") diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index 016ca1b..8199d7a 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -48,22 +48,31 @@ def simulator(config, i): log.info(f"Seed {config.sim.seed} took {duration} to finish") seed_str = str(i).zfill(4) + + while True: + try: + with h5py.File(utils.expand_path(config.output_path), "a") as data_f: + ## Chunking for compression and data access + ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage + ## should be by batch and less than 1MB + ## lzf compression for float32 is kind of pointless though. + data_f.create_dataset( + f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + ) + data_f.create_dataset( + f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + ) + data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + except IOError: + time.sleep(0.1) + continue + else: + break + - with h5py.File(utils.expand_path(config.output_path), "a") as data_f: - ## Chunking for compression and data access - ## https://docs.h5py.org/en/stable/high/dataset.html#chunked-storage - ## should be by batch and less than 1MB - ## lzf compression for float32 is kind of pointless though. - data_f.create_dataset( - f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" - ) - data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" - ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) @hydra.main(config_path="configs/", config_name="diff-sorp") diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index e9bc4b0..a5db228 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -32,13 +32,13 @@ import numpy as np from uploader import dataverse_upload import time +from src.sim_radial_dam_break import RadialDamBreak2D log = logging.getLogger(__name__) def simulator(base_config, i): - from src.sim_radial_dam_break.py import RadialDamBreak2D config = deepcopy(base_config) config.sim.seed = i @@ -58,12 +58,19 @@ def simulator(base_config, i): start_time = time.time() scenario.run(T=config.sim.T_end, tsteps=config.sim.n_time_steps, plot=False) duration = time.time() - start_time - log.info(f"Seed {config.sim.seed} took {duration} to finish") - config.output_path = config.output_path + "_" + str(i).zfill(4) + ".h5" - scenario.save_state_to_disk(filepath=config.output_path) + seed_str = str(i).zfill(4) + log.info(f"Seed {seed_str} took {duration} to finish") - with h5py.File(config.output_path, "r+") as f: - f.attrs["config"] = OmegaConf.to_yaml(config) + while True: + try: + with h5py.File(utils.expand_path(config.output_path), "a") as h5_file: + scenario.save_state_to_disk(h5_file, seed_str) + h5_file.attrs["config"] = OmegaConf.to_yaml(config) + except IOError: + time.sleep(0.1) + continue + else: + break if config.upload: dataverse_upload( @@ -98,7 +105,7 @@ def main(config: DictConfig): output_path = os.path.join(work_path, config.data_dir, config.output_path) if not os.path.isdir(output_path): os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + config.output_path = os.path.join(output_path, config.output_path) + '.h5' num_samples_init = 0 num_samples_final = 1000 diff --git a/pdebench/data_gen/src/sim_diff_react.py b/pdebench/data_gen/src/sim_diff_react.py index 2d16185..821e7cb 100644 --- a/pdebench/data_gen/src/sim_diff_react.py +++ b/pdebench/data_gen/src/sim_diff_react.py @@ -149,6 +149,6 @@ def rc_ode(self, t, y): y_t = np.concatenate((u_t,v_t)) # Log the simulation progress - self.log.info('t = ' + str(t)) + # self.log.info('t = ' + str(t)) return y_t diff --git a/pdebench/data_gen/src/sim_diff_sorp.py b/pdebench/data_gen/src/sim_diff_sorp.py index 5a5f0eb..7e84aaa 100644 --- a/pdebench/data_gen/src/sim_diff_sorp.py +++ b/pdebench/data_gen/src/sim_diff_sorp.py @@ -124,6 +124,6 @@ def rc_ode(self, t, y): y_t = c_t # Log the simulation progress - self.log.info('t = ' + str(t)) + # self.log.info('t = ' + str(t)) return y_t diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index d0127d8..d212472 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -90,13 +90,11 @@ def init_save_state(self, T, tsteps): for key, getter in self.state_getters.items(): self.save_state[key] = [getter()] - def save_state_to_disk(self, data_f, seed): + def save_state_to_disk(self, data_f, seed_str): T = np.asarray(self.save_state["t"]) X = np.asarray(self.save_state["x"]) Y = np.asarray(self.save_state["y"]) H = np.expand_dims(np.asarray(self.save_state["h"]), -1) - - seed_str = str(seed).zfill(4) data_f.create_dataset(f"{seed_str}/data", data=H, dtype="f") data_f.create_dataset(f"{seed_str}/grid/x", data=X, dtype="f") @@ -116,10 +114,10 @@ def run(self, T=1.0, tsteps=20, plot=False): start = time.time() for tstep in range(1, tsteps + 1): t = tstep * dt - print("Simulating timestep {}/{} at t={:f}".format(tstep, tsteps, t)) + # print("Simulating timestep {}/{} at t={:f}".format(tstep, tsteps, t)) self.simulate(t) self.add_save_state() - print("Simulation took: {}".format(time.time() - start)) + # print("Simulation took: {}".format(time.time() - start)) class RadialDamBreak2D(Basic2DScenario): From 24d4aa31082a210c16e58b4aa440b966546c0a26 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 19 Dec 2022 16:10:59 +0100 Subject: [PATCH 067/137] modify visualization_pdes.py by MT@19122022 --- pdebench/data_download/visualize_pdes.py | 289 +++++++++++++++++++++-- 1 file changed, 275 insertions(+), 14 deletions(-) diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index 08d2ce2..2de70b1 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -150,36 +150,270 @@ def visualize_swe(path, seed=None): print("saved") -def visualize_burgers(): - pass +def visualize_burgers(path, param=None): + """ + This function animates the Burgers equation + Args: + path : path to the desired file + param: PDE parameter to be visualized + """ -def visualize_advection(): - pass + # Read the h5 file and store the data + if param is not None: + flnm = "1D_Burgers_Sols_Nu" + str(param) + ".hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+path + flnm + else: + flnm = "1D_Burgers_Sols_Nu0.01.hdf5" + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) + data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) -def visualize_1d_cfd(): - pass + # Initialize plot + fig, ax = plt.subplots() + # Store the plot handle at each time step in the 'ims' list + ims = [] -def visualize_2d_cfd(): - pass + for i in range(data.shape[0]): + im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + if i == 0: + ax.plot(xcrd, data[i].squeeze()) # show an initial one first + ax.plot + ims.append([im]) + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) -def visualize_3d_cfd(): - pass + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_advection(path, param=None): + """ + This function animates the Burgers equation + + Args: + path : path to the desired file + param: PDE parameter to be visualized + """ + + # Read the h5 file and store the data + if param is not None: + flnm = "1D_Advection_Sols_beta" + str(param) + ".hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "1D_Advection_Sols_beta0.4.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) + data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + for i in range(data.shape[0]): + im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + if i == 0: + ax.plot(xcrd, data[i].squeeze()) # show an initial one first + ax.plot + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_1d_cfd(path, param=None): + """ + This function animates the Burgers equation + + Args: + path : path to the desired file + param: PDE parameter to be visualized + """ + + # Read the h5 file and store the data + if param is not None: + assert len(param) == 4, 'param should include type,eta,zeta,boundary as list' + flnm = "1D_CFD_" + str(param[0]) + "_Eta" + str(param[1]) + '_Zeta' + str(param[2]) +"_" + str(param[3]) + "_Train.hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) + dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + ax.set_title('density') + for i in range(dd.shape[0]): + im, = ax.plot(xcrd, dd[i].squeeze(), animated=True) + if i == 0: + ax.plot(xcrd, dd[i].squeeze()) # show an initial one first + ax.plot + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_2d_cfd(path, param=None): + # Read the h5 file and store the data + if param is not None: + assert len(param) == 6, 'param should include type,M,eta,zeta,boundary, resolution as list' + flnm = "2D_CFD_" + str(param[0]) + "_M" + str(param[1]) + "_Eta" + str(param[2]) + '_Zeta' + str(param[3]) + "_" + str(param[4]) + "_" + str(param[5]) + "_Train.hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "2D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_512_Train.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + ax.set_title('density') + for i in range(dd.shape[0]): + im = ax.imshow(dd[i].squeeze(), animated=True) + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") + + +def visualize_3d_cfd(path, param=None): + # Read the h5 file and store the data + if param is not None: + assert len(param) == 5, 'param should include type,M,eta,zeta,boundary as list' + flnm = "3D_CFD_" + str(param[0]) + "_M" + str(param[1]) + "_Eta" + str(param[2]) + '_Zeta' + str(param[3]) + "_" + str(param[4]) + "_Train.hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "3D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + ax.set_title('density') + for i in range(dd.shape[0]): + im = ax.imshow(dd[i, :, :, 32].squeeze(), animated=True) + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") def visualize_ns_incom(): pass -def visualize_darcy(): - pass +def visualize_darcy(path, param=None): + """ + This function animates the Burgers equation + Args: + path : path to the desired file + param: PDE parameter to be visualized + """ -def visualize_1d_reacdiff(): - pass + # Read the h5 file and store the data + if param is not None: + flnm = "2D_DarcyFlow_beta" + str(param) + "_Train.hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "2D_DarcyFlow_beta1.0_Train.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + nu = np.array(h5_file["nu"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + + # Initialize plot + fig, ax = plt.subplots(1, 2, figsize=(16, 8)) + + ax[0].imshow(data.squeeze()) + ax[1].imshow(nu.squeeze()) + ax[0].set_title('Data u') + ax[1].set_title('diffusion coefficient nu') + plt.savefig('2D_DarcyFlow.pdf') + print("saved") + + +def visualize_1d_reacdiff(path, param=None): + """ + This function animates the Burgers equation + + Args: + path : path to the desired file + param: PDE parameter to be visualized + """ + + # Read the h5 file and store the data + if param is not None: + assert len(param) == 2, 'param should include Nu and Rho as list' + flnm = "ReacDiff_Nu" + str(param[0]) + '_Rho' + str(param[1]) +".hdf5" + assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + else: + flnm = "ReacDiff_Nu1.0_Rho1.0.hdf5" + + nb = 0 + with h5py.File(os.path.join(path, flnm), "r") as h5_file: + xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) + data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + + # Initialize plot + fig, ax = plt.subplots() + + # Store the plot handle at each time step in the 'ims' list + ims = [] + for i in range(data.shape[0]): + im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + if i == 0: + ax.plot(xcrd, data[i].squeeze()) # show an initial one first + ax.plot + ims.append([im]) + + # Animate the plot + ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) + + writer = animation.PillowWriter(fps=15, bitrate=1800) + ani.save("movie.gif", writer=writer) + print("saved") if __name__ == "__main__": @@ -204,6 +438,18 @@ def visualize_1d_reacdiff(): default=None, help="Seed number to define which sample/batch to be plotted", ) + arg_parser.add_argument( + "--param", + type=float, + default=None, + help="PDE parameter to be plotted", + ) + arg_parser.add_argument( + "--params", + nargs='+', + default=None, + help="PDE parameters to be plotted", + ) args = arg_parser.parse_args() @@ -213,5 +459,20 @@ def visualize_1d_reacdiff(): visualize_2d_reacdiff(args.data_path, args.seed_number) elif args.pde_name == "swe": visualize_swe(args.data_path, args.seed_number) + elif args.pde_name == "burgers": + visualize_burgers(args.data_path, args.param) + elif args.pde_name == "advection": + visualize_advection(args.data_path, args.param) + elif args.pde_name == "1d-cfd": + visualize_1d_cfd(args.data_path, args.params) + elif args.pde_name == "2d-cfd": + visualize_2d_cfd(args.data_path, args.params) + elif args.pde_name == "3d-cfd": + visualize_3d_cfd(args.data_path, args.params) + elif args.pde_name == "darcy": + visualize_darcy(args.data_path, args.param) + elif args.pde_name == "1d-reacdiff": + visualize_1d_reacdiff(args.data_path, args.params) + else: raise ValueError("PDE name not recognized!") From 7b3f21fd2cf1020dbd97c944e9132901fa8bba1a Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 19 Dec 2022 16:16:09 +0100 Subject: [PATCH 068/137] modify visualization_pdes.py by MT@19122022 --- pdebench/data_download/visualize_pdes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index 2de70b1..dddb666 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -463,15 +463,15 @@ def visualize_1d_reacdiff(path, param=None): visualize_burgers(args.data_path, args.param) elif args.pde_name == "advection": visualize_advection(args.data_path, args.param) - elif args.pde_name == "1d-cfd": + elif args.pde_name == "1d_cfd": visualize_1d_cfd(args.data_path, args.params) - elif args.pde_name == "2d-cfd": + elif args.pde_name == "2d_cfd": visualize_2d_cfd(args.data_path, args.params) - elif args.pde_name == "3d-cfd": + elif args.pde_name == "3d_cfd": visualize_3d_cfd(args.data_path, args.params) elif args.pde_name == "darcy": visualize_darcy(args.data_path, args.param) - elif args.pde_name == "1d-reacdiff": + elif args.pde_name == "1d_reacdiff": visualize_1d_reacdiff(args.data_path, args.params) else: From 14cb398a489826744f9c412b25a4149318ebba17 Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Tue, 20 Dec 2022 11:13:16 +0100 Subject: [PATCH 069/137] Updated the config files for model training and the corresponding README file --- pdebench/models/config/README.md | 37 +++++++++++++++++++ pdebench/models/config/args/config_Adv.yaml | 4 +- pdebench/models/config/args/config_Bgs.yaml | 6 ++- .../models/config/args/config_ReacDiff.yaml | 6 ++- pdebench/models/config/config_docs.md | 24 ------------ 5 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 pdebench/models/config/README.md delete mode 100644 pdebench/models/config/config_docs.md diff --git a/pdebench/models/config/README.md b/pdebench/models/config/README.md new file mode 100644 index 0000000..c18e309 --- /dev/null +++ b/pdebench/models/config/README.md @@ -0,0 +1,37 @@ +# Config Documentation + +This is the documentation of the config files that were used to generate the provided [pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). +Since the default config files for all problems are already provided, this file only provides the values for the arguments that need to be changed. +N/A values mean that the default values can be used. +The complete explanation of the arguments can be found in the [README file](/README.md) + +| Pre-trained model | Config filename | model_name| filename (data) | ar_mode | pushforward | unroll_step | modes | width | +| :--- | :---- | :--- | :--- | :--- | :--- | ---: | ---: | ---: | +| 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | FNO | 1D_diff-sorp_NA_NA | N/A | N/A | N/A | 16 | 64 | +| 1D_diff-sorp_NA_NA_Unet-1-step.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | False | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | True | 20 | N/A | N/A | +| 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | PINN | 1D_diff-sorp_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | FNO | 2D_diff-react_NA_NA | N/A | N/A | N/A | 12 | 20 | +| 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | False | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-PF-20.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | True | 20 | N/A | N/A | +| 2D_diff-react_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_diff-react.yaml | PINN | 2D_diff-react_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 2D_rdb_NA_NA_FNO.pt | config_rdb.yaml | FNO | 2D_rdb_NA_NA | N/A | N/A | N/A | 12 | 20 | +| 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | False | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | True | 20 | N/A | N/A | +| 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | PINN | 2D_rdb_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 1D_CFD_Shock_trans_Train_FNO.pt | config_1DCFD.yaml | FNO | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_CFD_Shock_trans_Train_Unet.pt | config_1DCFD.yaml | Unet | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | True | True | 20 | N/A | N/A | +| ReacDiff_Nu1.0_Rho2.0_FNO.pt | config_ReacDiff.yaml | FNO | ReacDiff_Nu1.0_Rho2.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| ReacDiff_Nu1.0_Rho2.0_Unet.pt | config_ReacDiff.yaml | Unet | ReacDiff_Nu1.0_Rho2.0.hdf5 | True | True | 10 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_FNO.pt | config_Adv.yaml | FNO | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Advection_Sols_beta4.0_Unet.pt | config_Adv.yaml | Unet | 1D_Advection_Sols_beta4.0.hdf5 | True | True | 20 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_PINN.pt-15000.pt | config_pinn_pde1d.yaml | PINN | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | N/A | N/A | +| 1D_Burgers_Sols_Nu1.0_FNO.pt | config_Bgs.yaml | FNO | 1D_Burgers_Sols_Nu1.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Burgers_Sols_Nu1.0_Unet-PF-20.pt | config_Bgs.yaml | Unet | 1D_Burgers_Sols_Nu1.0.hdf5 | True | True | 20 | N/A | N/A | +| 2D_DarcyFlow_beta0.01_Train_FNO.pt | config_Darcy.yaml | FNO | 2D_DarcyFlow_beta0.01_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 2D_DarcyFlow_beta0.01_Train_Unet_PF_1.pt | config_Darcy.yaml | Unet | 2D_DarcyFlow_beta0.01_Train.hdf5 | False | False | N/A | N/A | N/A | +| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_FNO.pt | config_3DCFD.yaml | FNO | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_Unet-PF-20.pt | config_3DCFD.yaml | Unet | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | True | True | 20 | N/A | N/A | \ No newline at end of file diff --git a/pdebench/models/config/args/config_Adv.yaml b/pdebench/models/config/args/config_Adv.yaml index e491d23..37d8f5f 100644 --- a/pdebench/models/config/args/config_Adv.yaml +++ b/pdebench/models/config/args/config_Adv.yaml @@ -5,7 +5,7 @@ batch_size: 50 unroll_step: 20 t_train: 200 model_update: 1 -filename: '1D_Advection_Sols_beta0.1.hdf5' +filename: '1D_Advection_Sols_beta4.0.hdf5' single_file: True reduced_resolution: 4 reduced_resolution_t: 5 @@ -16,6 +16,8 @@ num_workers: 0 #Unet in_channels: 1 out_channels: 1 +ar_mode: True +pushforward: True #FNO num_channels: 1 modes: 12 diff --git a/pdebench/models/config/args/config_Bgs.yaml b/pdebench/models/config/args/config_Bgs.yaml index 2fd1618..43e6917 100644 --- a/pdebench/models/config/args/config_Bgs.yaml +++ b/pdebench/models/config/args/config_Bgs.yaml @@ -2,10 +2,9 @@ model_name: 'Unet' if_training: True continue_training: False batch_size: 50 -unroll_step: 20 t_train: 200 model_update: 1 -filename: '1D_Burgers_Sols_Nu0.001.hdf5' +filename: '1D_Burgers_Sols_Nu1.0.hdf5' single_file: True reduced_resolution: 4 reduced_resolution_t: 5 @@ -16,6 +15,9 @@ num_workers: 0 #Unet in_channels: 1 out_channels: 1 +ar_mode: True +pushforward: True +unroll_step: 20 #FNO num_channels: 1 modes: 12 diff --git a/pdebench/models/config/args/config_ReacDiff.yaml b/pdebench/models/config/args/config_ReacDiff.yaml index a4dbfbf..7b8f045 100644 --- a/pdebench/models/config/args/config_ReacDiff.yaml +++ b/pdebench/models/config/args/config_ReacDiff.yaml @@ -2,7 +2,6 @@ model_name: 'Unet' if_training: True continue_training: False batch_size: 50 -unroll_step: 10 t_train: 30 model_update: 1 filename: 'ReacDiff_Nu0.5_Rho1.0.hdf5' @@ -16,10 +15,13 @@ num_workers: 0 #Unet in_channels: 1 out_channels: 1 +ar_mode: True +pushforward: True +unroll_step: 10 #FNO num_channels: 1 modes: 12 width: 20 scheduler_step: 100 scheduler_gamma: 0.5 -initial_step: 5 # should be the same value to unroll_step ?? \ No newline at end of file +initial_step: 5 \ No newline at end of file diff --git a/pdebench/models/config/config_docs.md b/pdebench/models/config/config_docs.md deleted file mode 100644 index f0868a4..0000000 --- a/pdebench/models/config/config_docs.md +++ /dev/null @@ -1,24 +0,0 @@ -# Config Documentation - -This is the documentation of the config files that were used to generate the provided [pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). -Since the default config files for all problems are already provided, this file only provides the values for the arguments that need to be changed. -N/A values mean that the default values can be used. -The complete explanation of the arguments can be found in the [README file](/README.md) - -| Pre-trained model | Config filename | ar_mode | pushforward | unroll_step | modes | width | -| :--- | :---- | :--- | :--- | ---: | ---: | ---: | -| 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | N/A | N/A | N/A | 16 | 64 | -| 1D_diff-sorp_NA_NA_Unet-1-step.pt | config_diff-sorp.yaml | False | False | N/A | N/A | N/A | -| 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | True | False | N/A | N/A | N/A | -| 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | True | True | 20 | N/A | N/A | -| 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | N/A | N/A | N/A | 12 | 20 | -| 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | False | False | N/A | N/A | N/A | -| 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | True | False | N/A | N/A | N/A | -| 2D_diff-react_NA_NA_Unet-PF-20.pt | config_diff-react.yaml | True | True | 20 | N/A | N/A | -| 2D_rdb_NA_NA_FNO.pt | config_rdb.yaml | N/A | N/A | N/A | 12 | 20 | -| 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | False | False | N/A | N/A | N/A | -| 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | True | False | N/A | N/A | N/A | -| 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | True | True | 20 | N/A | N/A | -| 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | N/A | N/A | N/A | N/A | N/A | -| 2D_diff-react_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_diff-react.yaml | N/A | N/A | N/A | N/A | N/A | -| 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | N/A | N/A | N/A | N/A | N/A | From 499487063c932ca2e544dafe668abb3e2fa81eae Mon Sep 17 00:00:00 2001 From: Timothy Praditia Date: Tue, 20 Dec 2022 11:14:40 +0100 Subject: [PATCH 070/137] Sorted the config README based on problem dimension --- pdebench/models/config/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pdebench/models/config/README.md b/pdebench/models/config/README.md index c18e309..c3d2b2d 100644 --- a/pdebench/models/config/README.md +++ b/pdebench/models/config/README.md @@ -12,6 +12,15 @@ The complete explanation of the arguments can be found in the [README file](/REA | 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | False | N/A | N/A | N/A | | 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | True | 20 | N/A | N/A | | 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | PINN | 1D_diff-sorp_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 1D_CFD_Shock_trans_Train_FNO.pt | config_1DCFD.yaml | FNO | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_CFD_Shock_trans_Train_Unet.pt | config_1DCFD.yaml | Unet | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | True | True | 20 | N/A | N/A | +| ReacDiff_Nu1.0_Rho2.0_FNO.pt | config_ReacDiff.yaml | FNO | ReacDiff_Nu1.0_Rho2.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| ReacDiff_Nu1.0_Rho2.0_Unet.pt | config_ReacDiff.yaml | Unet | ReacDiff_Nu1.0_Rho2.0.hdf5 | True | True | 10 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_FNO.pt | config_Adv.yaml | FNO | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Advection_Sols_beta4.0_Unet.pt | config_Adv.yaml | Unet | 1D_Advection_Sols_beta4.0.hdf5 | True | True | 20 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_PINN.pt-15000.pt | config_pinn_pde1d.yaml | PINN | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | N/A | N/A | +| 1D_Burgers_Sols_Nu1.0_FNO.pt | config_Bgs.yaml | FNO | 1D_Burgers_Sols_Nu1.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Burgers_Sols_Nu1.0_Unet-PF-20.pt | config_Bgs.yaml | Unet | 1D_Burgers_Sols_Nu1.0.hdf5 | True | True | 20 | N/A | N/A | | 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | FNO | 2D_diff-react_NA_NA | N/A | N/A | N/A | 12 | 20 | | 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | False | False | N/A | N/A | N/A | | 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | False | N/A | N/A | N/A | @@ -22,15 +31,6 @@ The complete explanation of the arguments can be found in the [README file](/REA | 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | False | N/A | N/A | N/A | | 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | True | 20 | N/A | N/A | | 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | PINN | 2D_rdb_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | -| 1D_CFD_Shock_trans_Train_FNO.pt | config_1DCFD.yaml | FNO | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_CFD_Shock_trans_Train_Unet.pt | config_1DCFD.yaml | Unet | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | True | True | 20 | N/A | N/A | -| ReacDiff_Nu1.0_Rho2.0_FNO.pt | config_ReacDiff.yaml | FNO | ReacDiff_Nu1.0_Rho2.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| ReacDiff_Nu1.0_Rho2.0_Unet.pt | config_ReacDiff.yaml | Unet | ReacDiff_Nu1.0_Rho2.0.hdf5 | True | True | 10 | N/A | N/A | -| 1D_Advection_Sols_beta4.0_FNO.pt | config_Adv.yaml | FNO | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_Advection_Sols_beta4.0_Unet.pt | config_Adv.yaml | Unet | 1D_Advection_Sols_beta4.0.hdf5 | True | True | 20 | N/A | N/A | -| 1D_Advection_Sols_beta4.0_PINN.pt-15000.pt | config_pinn_pde1d.yaml | PINN | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | N/A | N/A | -| 1D_Burgers_Sols_Nu1.0_FNO.pt | config_Bgs.yaml | FNO | 1D_Burgers_Sols_Nu1.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_Burgers_Sols_Nu1.0_Unet-PF-20.pt | config_Bgs.yaml | Unet | 1D_Burgers_Sols_Nu1.0.hdf5 | True | True | 20 | N/A | N/A | | 2D_DarcyFlow_beta0.01_Train_FNO.pt | config_Darcy.yaml | FNO | 2D_DarcyFlow_beta0.01_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | | 2D_DarcyFlow_beta0.01_Train_Unet_PF_1.pt | config_Darcy.yaml | Unet | 2D_DarcyFlow_beta0.01_Train.hdf5 | False | False | N/A | N/A | N/A | | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_FNO.pt | config_3DCFD.yaml | FNO | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | From 7acbb2d0b277c2414a43ff40a7cbbe5c16cde2f6 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Tue, 20 Dec 2022 11:31:13 +0100 Subject: [PATCH 071/137] Fix pinn training problems: - fixed saving of config data to dataset files - adjusted config parameters to reflect code changes --- pdebench/data_gen/gen_diff_react.py | 3 ++- pdebench/data_gen/gen_diff_sorp.py | 3 ++- pdebench/data_gen/gen_radial_dam_break.py | 5 +++-- .../models/config/args/config_pinn_diff-react.yaml | 11 +++++++++-- .../models/config/args/config_pinn_diff-sorp.yaml | 11 +++++++++-- pdebench/models/config/args/config_pinn_swe2d.yaml | 11 +++++++++-- 6 files changed, 34 insertions(+), 10 deletions(-) diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index 39abed8..196eef6 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -67,7 +67,8 @@ def simulator(config, i): data_f.create_dataset( f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + seed_group = data_f[seed_str] + seed_group.attrs["config"] = OmegaConf.to_yaml(config) except IOError: time.sleep(0.1) continue diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index 8199d7a..c142949 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -65,7 +65,8 @@ def simulator(config, i): data_f.create_dataset( f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" ) - data_f.attrs[f"{seed_str}/config"] = OmegaConf.to_yaml(config) + seed_group = data_f[seed_str] + seed_group.attrs["config"] = OmegaConf.to_yaml(config) except IOError: time.sleep(0.1) continue diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index a5db228..3b1d8fe 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -65,7 +65,8 @@ def simulator(base_config, i): try: with h5py.File(utils.expand_path(config.output_path), "a") as h5_file: scenario.save_state_to_disk(h5_file, seed_str) - h5_file.attrs["config"] = OmegaConf.to_yaml(config) + seed_group = h5_file[seed_str] + seed_group.attrs["config"] = OmegaConf.to_yaml(config) except IOError: time.sleep(0.1) continue @@ -108,7 +109,7 @@ def main(config: DictConfig): config.output_path = os.path.join(output_path, config.output_path) + '.h5' num_samples_init = 0 - num_samples_final = 1000 + num_samples_final = 10000 pool = mp.Pool(mp.cpu_count()) seed = np.arange(num_samples_init, num_samples_final) diff --git a/pdebench/models/config/args/config_pinn_diff-react.yaml b/pdebench/models/config/args/config_pinn_diff-react.yaml index 28ffa96..60f71cd 100644 --- a/pdebench/models/config/args/config_pinn_diff-react.yaml +++ b/pdebench/models/config/args/config_pinn_diff-react.yaml @@ -2,7 +2,14 @@ model_name: 'PINN' scenario: 'diff-react' model_update: 500 -filename: '2D_diff-react_NA_NA_0000.h5' +filename: '2D_diff-react_NA_NA/2D_diff-react_NA_NA.h5' epochs: 100 learning_rate: 1.e-3 -seed: '0000' \ No newline at end of file +seed: '0000' +# unused arguments +input_ch: 0 +output_ch: 1 +root_path: '.' +val_num: 1 +if_periodic_bc: False +aux_params: 0 \ No newline at end of file diff --git a/pdebench/models/config/args/config_pinn_diff-sorp.yaml b/pdebench/models/config/args/config_pinn_diff-sorp.yaml index e1dab70..8bc0701 100644 --- a/pdebench/models/config/args/config_pinn_diff-sorp.yaml +++ b/pdebench/models/config/args/config_pinn_diff-sorp.yaml @@ -2,7 +2,14 @@ model_name: 'PINN' scenario: 'diff-sorp' model_update: 500 -filename: '2D_diff-react_NA_NA_0000.h5' +filename: '1D_diff-sorp_NA_NA/1D_diff-sorp_NA_NA.h5' epochs: 15000 learning_rate: 1.e-3 -seed: '0000' \ No newline at end of file +seed: '0000' +# unused arguments +input_ch: 0 +output_ch: 1 +root_path: '.' +val_num: 1 +if_periodic_bc: False +aux_params: 0 \ No newline at end of file diff --git a/pdebench/models/config/args/config_pinn_swe2d.yaml b/pdebench/models/config/args/config_pinn_swe2d.yaml index d739f9a..b4fe9b0 100644 --- a/pdebench/models/config/args/config_pinn_swe2d.yaml +++ b/pdebench/models/config/args/config_pinn_swe2d.yaml @@ -2,7 +2,14 @@ model_name: 'PINN' scenario: 'swe2d' model_update: 500 -filename: '2D_rdb_NA_NA_0000.h5' +filename: '2D_rdb_NA_NA/2D_rdb_NA_NA.h5' epochs: 15000 learning_rate: 1.e-3 -seed: '0000' \ No newline at end of file +seed: '0000' +# unused arguments +input_ch: 0 +output_ch: 1 +root_path: '.' +val_num: 1 +if_periodic_bc: False +aux_params: 0 From 245610307ed024d043638d6de0f506bec4b26267 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Tue, 20 Dec 2022 11:41:02 +0100 Subject: [PATCH 072/137] convert to absolute imports --- pdebench/data_gen/gen_diff_react.py | 6 +++--- pdebench/data_gen/gen_diff_sorp.py | 6 +++--- pdebench/data_gen/gen_radial_dam_break.py | 6 +++--- pdebench/data_gen/plot.py | 2 +- pdebench/data_gen/src/sim_ns_incomp_2d.py | 4 ++-- pdebench/data_gen/src/sim_radial_dam_break.py | 3 --- pdebench/models/fno/train.py | 7 +++---- pdebench/models/pinn/train.py | 10 ++++------ pdebench/models/train_models_forward.py | 7 +++---- pdebench/models/train_models_inverse.py | 7 +++---- pdebench/models/unet/train.py | 7 +++---- 11 files changed, 28 insertions(+), 37 deletions(-) diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index 196eef6..1d710df 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -28,15 +28,15 @@ from itertools import repeat import numpy as np -from src import utils +from pdebench.data_gen.src import utils import h5py -from uploader import dataverse_upload +from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) def simulator(config, i): - from src import sim_diff_react + from pdebench.data_gen.src import sim_diff_react config.sim.seed = i log.info(f"Starting seed {i}") diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index c142949..dfa2017 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -28,16 +28,16 @@ from itertools import repeat import numpy as np -from src import utils +from pdebench.data_gen.src import utils import h5py -from uploader import dataverse_upload +from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) def simulator(config, i): - from src import sim_diff_sorp + from pdebench.data_gen.src import sim_diff_sorp config.sim.seed = i log.info(f"Starting seed {i}") diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index 3b1d8fe..89b0e8a 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -28,11 +28,11 @@ import logging import multiprocessing as mp from itertools import repeat -from src import utils +from pdebench.data_gen.src import utils import numpy as np -from uploader import dataverse_upload +from pdebench.data_gen.uploader import dataverse_upload import time -from src.sim_radial_dam_break import RadialDamBreak2D +from pdebench.data_gen.src.sim_radial_dam_break import RadialDamBreak2D log = logging.getLogger(__name__) diff --git a/pdebench/data_gen/plot.py b/pdebench/data_gen/plot.py index 5453f0a..02a3c6f 100644 --- a/pdebench/data_gen/plot.py +++ b/pdebench/data_gen/plot.py @@ -12,7 +12,7 @@ import h5py import matplotlib.pyplot as plt from hydra.utils import get_original_cwd -from src.plots import plot_data +from pdebench.data_gen.src.plots import plot_data @hydra.main(config_path="configs/", config_name="diff-sorp") def main(config: DictConfig): diff --git a/pdebench/data_gen/src/sim_ns_incomp_2d.py b/pdebench/data_gen/src/sim_ns_incomp_2d.py index 12b8c58..0b3f82c 100644 --- a/pdebench/data_gen/src/sim_ns_incomp_2d.py +++ b/pdebench/data_gen/src/sim_ns_incomp_2d.py @@ -11,8 +11,8 @@ import numpy as np from tqdm import tqdm import hydra -from src import data_io -from src import image_processor +from pdebench.data_gen.src import data_io +from pdebench.data_gen.src import image_processor log = logging.getLogger(__name__) diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index d212472..7d92550 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -12,9 +12,6 @@ from clawpack import pyclaw -sys.path.append(os.path.join(sys.path[0], "..", "utils")) - - class Basic2DScenario(ABC): name = "" diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 8ca4729..bbc0ecf 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -16,10 +16,9 @@ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') -sys.path.append('.') -from .fno import FNO1d, FNO2d, FNO3d -from .utils import FNODatasetSingle, FNODatasetMult -from metrics import metrics +from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d +from pdebench.models.fno.utils import FNODatasetSingle, FNODatasetMult +from pdebench.models.metrics import metrics def run_training(if_training, continue_training, diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index 2741d93..4d3ec2f 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -8,9 +8,7 @@ from typing import Tuple -sys.path.append(".") - -from .utils import ( +from pdebench.models.pinn.utils import ( PINNDatasetRadialDambreak, PINNDatasetDiffReact, PINNDataset2D, @@ -20,7 +18,7 @@ PINNDataset2Dpde, PINNDataset3Dpde, ) -from .pde_definitions import ( +from pdebench.models.pinn.pde_definitions import ( pde_diffusion_reaction, pde_swe2d, pde_diffusion_sorption, @@ -33,7 +31,7 @@ pde_CFD3d, ) -from metrics import metrics, metric_func +from pdebench.models.metrics import metrics, metric_func def setup_diffusion_sorption(filename, seed): @@ -498,7 +496,7 @@ def run_training(scenario, epochs, learning_rate, model_update, flnm, epochs=100, learning_rate=1e-3, model_update=500, - flnm="2D_diff-react_NA_NA_0000.h5", + flnm="2D_diff-react_NA_NA.h5", seed="0000", ) # run_training( diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 57acff3..89ae45a 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -155,10 +155,9 @@ from timeit import default_timer -sys.path.append(".") -from fno.train import run_training as run_training_FNO -from pinn.train import run_training as run_training_PINN -from unet.train import run_training as run_training_Unet +from pdebench.models.fno.train import run_training as run_training_FNO +from pdebench.models.pinn.train import run_training as run_training_PINN +from pdebench.models.unet.train import run_training as run_training_Unet @hydra.main(config_path="config", config_name="config") diff --git a/pdebench/models/train_models_inverse.py b/pdebench/models/train_models_inverse.py index 8ecb621..c5099be 100644 --- a/pdebench/models/train_models_inverse.py +++ b/pdebench/models/train_models_inverse.py @@ -154,10 +154,9 @@ from timeit import default_timer -sys.path.append('.') -from fno.train import run_training as run_training_FNO -from pinn.train import run_training as run_training_PINN -from unet.train import run_training as run_training_Unet +from pdebench.models.fno.train import run_training as run_training_FNO +from pdebench.models.pinn.train import run_training as run_training_PINN +from pdebench.models.unet.train import run_training as run_training_Unet @hydra.main(config_path='config', config_name='config') diff --git a/pdebench/models/unet/train.py b/pdebench/models/unet/train.py index 9c37ec3..7b301c9 100644 --- a/pdebench/models/unet/train.py +++ b/pdebench/models/unet/train.py @@ -18,10 +18,9 @@ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') -sys.path.append('.') -from .unet import UNet1d, UNet2d, UNet3d -from .utils import UNetDatasetSingle, UNetDatasetMult -from metrics import metrics +from pdebench.models.unet.unet import UNet1d, UNet2d, UNet3d +from pdebench.models.unet.utils import UNetDatasetSingle, UNetDatasetMult +from pdebench.models.metrics import metrics def run_training(if_training, continue_training, From fcbaff1333754fe156c9f5b2cf8204e717cd4c1a Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 20 Dec 2022 21:21:42 +0100 Subject: [PATCH 073/137] improve visualization functionalities --- pdebench/data_download/visualize_pdes.py | 122 +++++++++++++---------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index dddb666..8f163dc 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -1,5 +1,7 @@ -import argparse import os +import argparse + +from tqdm import tqdm import h5py import numpy as np import matplotlib.pyplot as plt @@ -32,12 +34,14 @@ def visualize_diff_sorp(path, seed=None): # Read the h5 file and store the data h5_file = h5py.File(os.path.join(path, "1D_diff-sorp_NA_NA.h5"), "r") - + num_samples = len(h5_file.keys()) + + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, len(h5_file.keys())) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined - assert seed < data.shape[0], "Seed number too high!" + assert seed < num_samples, "Seed number too high!" seed = str(seed).zfill(4) data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 1024, 1] @@ -49,19 +53,20 @@ def visualize_diff_sorp(path, seed=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): - im = ax.plot(data[i].squeeze(), animated=True) + for i in tqdm(range(data.shape[0])): if i == 0: - ax.plot(data[0].squeeze()) # show an initial one first + im = ax.plot(data[0].squeeze(), animated=True, color="blue") # show an initial one first + else: + im = ax.plot(data[i].squeeze(), animated=True, color="blue") ax.plot - ims.append([im]) + ims.append([im[0]]) # Animate the plot ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_diff_sorp.gif", writer=writer) + print("Animation saved") def visualize_2d_reacdiff(path, seed=None): @@ -75,12 +80,14 @@ def visualize_2d_reacdiff(path, seed=None): # Read the h5 file and store the data h5_file = h5py.File(os.path.join(path, "2D_diff-react_NA_NA.h5"), "r") + num_samples = len(h5_file.keys()) + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, len(h5_file.keys())) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined - assert seed < data.shape[0], "Seed number too high!" + assert seed < num_samples, "Seed number too high!" seed = str(seed).zfill(4) data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 2] @@ -92,7 +99,7 @@ def visualize_2d_reacdiff(path, seed=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): + for i in tqdm(range(data.shape[0])): im1 = ax[0].imshow(data[i, ..., 0].squeeze(), animated=True) im2 = ax[1].imshow(data[i, ..., 1].squeeze(), animated=True) if i == 0: @@ -104,8 +111,8 @@ def visualize_2d_reacdiff(path, seed=None): ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_2d_reacdiff.gif", writer=writer) + print("Animation saved") def visualize_swe(path, seed=None): @@ -119,12 +126,14 @@ def visualize_swe(path, seed=None): # Read the h5 file and store the data h5_file = h5py.File(os.path.join(path, "2D_rdb_NA_NA.h5"), "r") - + num_samples = len(h5_file.keys()) + + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, len(h5_file.keys())) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined - assert seed < data.shape[0], "Seed number too high!" + assert seed < num_samples, "Seed number too high!" seed = str(seed).zfill(4) data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 1] @@ -136,7 +145,7 @@ def visualize_swe(path, seed=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): + for i in tqdm(range(data.shape[0])): im = ax.imshow(data[i].squeeze(), animated=True) if i == 0: ax.imshow(data[0].squeeze()) # show an initial one first @@ -146,8 +155,8 @@ def visualize_swe(path, seed=None): ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_swe.gif", writer=writer) + print("Animation saved") def visualize_burgers(path, param=None): @@ -156,7 +165,7 @@ def visualize_burgers(path, param=None): Args: path : path to the desired file - param: PDE parameter to be visualized + param: PDE parameter of the data shard to be visualized """ # Read the h5 file and store the data @@ -177,28 +186,29 @@ def visualize_burgers(path, param=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): - im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + for i in tqdm(range(data.shape[0])): if i == 0: - ax.plot(xcrd, data[i].squeeze()) # show an initial one first + im = ax.plot(xcrd, data[i].squeeze(), animated=True, color="blue") + else: + im = ax.plot(xcrd, data[i].squeeze(), animated=True, color="blue") # show an initial one first ax.plot - ims.append([im]) + ims.append([im[0]]) # Animate the plot ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_burgers.gif", writer=writer) + print("Animation saved") def visualize_advection(path, param=None): """ - This function animates the Burgers equation + This function animates the Advection equation Args: path : path to the desired file - param: PDE parameter to be visualized + param: PDE parameter of the data shard to be visualized """ # Read the h5 file and store the data @@ -218,19 +228,19 @@ def visualize_advection(path, param=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): - im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + for i in tqdm(range(data.shape[0])): + im = ax.plot(xcrd, data[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, data[i].squeeze()) # show an initial one first ax.plot - ims.append([im]) + ims.append([im[0]]) # Animate the plot ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_advection.gif", writer=writer) + print("Animation saved") def visualize_1d_cfd(path, param=None): @@ -239,7 +249,7 @@ def visualize_1d_cfd(path, param=None): Args: path : path to the desired file - param: PDE parameter to be visualized + param: PDE parameter of the data shard to be visualized """ # Read the h5 file and store the data @@ -261,22 +271,30 @@ def visualize_1d_cfd(path, param=None): # Store the plot handle at each time step in the 'ims' list ims = [] ax.set_title('density') - for i in range(dd.shape[0]): - im, = ax.plot(xcrd, dd[i].squeeze(), animated=True) + for i in tqdm(range(dd.shape[0])): + im = ax.plot(xcrd, dd[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, dd[i].squeeze()) # show an initial one first ax.plot - ims.append([im]) + ims.append([im[0]]) # Animate the plot ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_1d_cfd.gif", writer=writer) + print("Animation saved") def visualize_2d_cfd(path, param=None): + """ + This function animates the Burgers equation + + Args: + path : path to the desired file + param: PDE parameter of the data shard to be visualized + """ + # Read the h5 file and store the data if param is not None: assert len(param) == 6, 'param should include type,M,eta,zeta,boundary, resolution as list' @@ -344,11 +362,11 @@ def visualize_ns_incom(): def visualize_darcy(path, param=None): """ - This function animates the Burgers equation + This function animates Darcy Flow equation Args: path : path to the desired file - param: PDE parameter to be visualized + param: PDE parameter of the data shard to be visualized """ # Read the h5 file and store the data @@ -371,16 +389,16 @@ def visualize_darcy(path, param=None): ax[0].set_title('Data u') ax[1].set_title('diffusion coefficient nu') plt.savefig('2D_DarcyFlow.pdf') - print("saved") + print("plot saved") def visualize_1d_reacdiff(path, param=None): """ - This function animates the Burgers equation + This function animates 1D Reaction Diffusion equation Args: path : path to the desired file - param: PDE parameter to be visualized + param: PDE parameter of the data shard to be visualized """ # Read the h5 file and store the data @@ -401,19 +419,19 @@ def visualize_1d_reacdiff(path, param=None): # Store the plot handle at each time step in the 'ims' list ims = [] - for i in range(data.shape[0]): - im, = ax.plot(xcrd, data[i].squeeze(), animated=True) + for i in tqdm(range(data.shape[0])): + im = ax.plot(xcrd, data[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, data[i].squeeze()) # show an initial one first ax.plot - ims.append([im]) + ims.append([im[0]]) # Animate the plot ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, repeat_delay=1000) writer = animation.PillowWriter(fps=15, bitrate=1800) - ani.save("movie.gif", writer=writer) - print("saved") + ani.save("movie_1d_reacdiff.gif", writer=writer) + print("Animation saved") if __name__ == "__main__": @@ -473,6 +491,6 @@ def visualize_1d_reacdiff(path, param=None): visualize_darcy(args.data_path, args.param) elif args.pde_name == "1d_reacdiff": visualize_1d_reacdiff(args.data_path, args.params) - else: raise ValueError("PDE name not recognized!") + From c1312eb66445c0241e9ce0ae41bd3c2087188b91 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 20 Dec 2022 21:22:22 +0100 Subject: [PATCH 074/137] update viz readme --- pdebench/data_download/README.md | 104 ++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 703ff0c..d86d5f6 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -1,10 +1,8 @@ -# Downloading PDEBench Datasets +# Downloading PDEBench Datasets :earth_asia: Here we enumerate the list of all available PDEs in PDEBench and the commands to download them. - - | PDEs | Dataset Download | Dataset Size | | ----------- | :----------------------------------------------------------- | ------------ | | advection | ```python download_direct.py --root_folder $proj_home/data --pde_name advection``` | 47 GB | @@ -19,3 +17,103 @@ Here we enumerate the list of all available PDEs in PDEBench and the commands to | ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | 2.3 TB | | swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | 6.2 GB | +-------- + +# Visualizing PDEs :ocean: + +Below are some illustrations for how to visualize a certain PDE. It is assumed that you first download the data shard you'd like to visualize for a desired PDE. Then you can use the `visualize_pde.py` script to generate an animation (i.e., `.gif`). + +###### 1D Diffusion Sorption Eqn + +``` +# get data: 1D_diff-sorp_NA_NA.h5 +https://darus.uni-stuttgart.de/api/access/datafile/133020 + +# visualize +python visualize_pdes.py --pde_name "diff_sorp" --data_path "./" +``` + +---------- + +###### 1D Diffusion Reaction Eqn + +``` +# get data: ReacDiff_Nu1.0_Rho1.0.hdf5 +https://darus.uni-stuttgart.de/api/access/datafile/133181 + +# visualize +python visualize_pdes.py --pde_name "1d_reacdiff" +``` + +---------- + +###### 1D Advection Eqn + +``` +# get data: 1D_Advection_Sols_beta0.4.hdf5 +https://darus.uni-stuttgart.de/api/access/datafile/133110 + +# visualize +python visualize_pdes.py --pde_name "advection" +``` + +----------- + +###### 1D Burgers Eqn + +``` +# get data: 1D_Burgers_Sols_Nu0.01.hdf5 +https://darus.uni-stuttgart.de/api/access/datafile/133136 + +# visualize +python visualize_pdes.py --pde_name "burgers" +``` + +-------------------- + +###### 1D CFD Eqn + +``` +# get data: 1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5 +https://darus.uni-stuttgart.de/api/access/datafile/135485 + +# visualize +python visualize_pdes.py --pde_name "1d_cfd" +``` + +------------- + +###### 2D Diffusion Reaction Eqn + +``` +# get data: 2D_diff-react_NA_NA.h5 +https://darus.uni-stuttgart.de/api/access/datafile/133017 + +# visualize +python visualize_pdes.py --pde_name "2d_reacdiff" +``` + +------------- + +###### 2D Darcy Flow Eqn + +``` +# get data: 2D_DarcyFlow_beta1.0_Train.hdf5 +https://darus.uni-stuttgart.de/api/access/datafile/133219 + +# visualize +python visualize_pdes.py --pde_name "darcy" +``` + +------------------ + +###### 2D Shallow Water Eqn + +``` +# get data: 2D_rdb_NA_NA.h5 +https://darus.uni-stuttgart.de/api/access/datafile/133021 + +# visualize +python visualize_pdes.py --pde_name "swe" --data_path "./" +``` + From 1bdaa4b739b18d99e62ea0d70ea25b7dd118f428 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Wed, 21 Dec 2022 11:09:52 +0100 Subject: [PATCH 075/137] modify default file of 3d-cfd in visualize_pdes.py bt MT@21122022 --- pdebench/data_download/visualize_pdes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index dddb666..f22e728 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -314,7 +314,7 @@ def visualize_3d_cfd(path, param=None): flnm = "3D_CFD_" + str(param[0]) + "_M" + str(param[1]) + "_Eta" + str(param[2]) + '_Zeta' + str(param[3]) + "_" + str(param[4]) + "_Train.hdf5" assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm else: - flnm = "3D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" + flnm = "3D_CFD_Rand_M1.0_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: From 3e1ac16ed563dc6f39d146e6597d18d268212665 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 30 Jan 2023 10:19:27 +0100 Subject: [PATCH 076/137] add explanation for generating DarcyFlow data by MT@30012023 --- README.md | 2 +- .../ReactionDiffusionEq/run_DarcyFlow2D.sh | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh diff --git a/README.md b/README.md index 9e599c9..d6cec44 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ The data generation codes are contained in [data_gen_NLE](./pdebench/data_gen/da - `AdvectionEq` directory with the source codes to generate 1D Advection equation training samples - `BurgersEq` directory with the source codes to generate 1D Burgers equation training samples - `CompressibleFluid` directory with the source codes to generate compressible Navier-Stokes equations training samples -- `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples +- `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples (**Note: DarcyFlow data can be generated by run_DarcyFlow2D.sh in this folder.**) - `save` directory saving the generated training samples A typical example to generate training samples (1D Advection Equation): diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh new file mode 100644 index 0000000..1ee0716 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh @@ -0,0 +1,10 @@ +nn=1 +key=2020 +while [ $nn -le 50 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_2D_multi_solution_Hydra.py +multi=config_2D.yaml ++multi.init_k\ +ey=$key + nn=$(expr $nn + 1) + key=$(expr $key + 1) + echo "$nn" + echo "$key" +done \ No newline at end of file From d004501665fd928542e41d0694c1939502ac0106 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 30 Jan 2023 10:27:31 +0100 Subject: [PATCH 077/137] erasing run_trainset_2D.sh in ReactionDiffusionEq by MT@30012023 --- .../ReactionDiffusionEq/run_trainset_2D.sh | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh deleted file mode 100644 index 1ee0716..0000000 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset_2D.sh +++ /dev/null @@ -1,10 +0,0 @@ -nn=1 -key=2020 -while [ $nn -le 50 ]; do - CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_2D_multi_solution_Hydra.py +multi=config_2D.yaml ++multi.init_k\ -ey=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) - echo "$nn" - echo "$key" -done \ No newline at end of file From bb36a36fad1f4d946a2033fdbfe0eed583abe571 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 24 Feb 2023 17:26:04 +0100 Subject: [PATCH 078/137] add readme for darcy data generation --- pdebench/data_gen/data_gen_NLE/README.md | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 pdebench/data_gen/data_gen_NLE/README.md diff --git a/pdebench/data_gen/data_gen_NLE/README.md b/pdebench/data_gen/data_gen_NLE/README.md new file mode 100644 index 0000000..7b5ff70 --- /dev/null +++ b/pdebench/data_gen/data_gen_NLE/README.md @@ -0,0 +1,25 @@ +## Data Generation + +#### Data generation for DarcyFlow Equation: + +- Run the shell script: + +```bash +bash data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh +``` + +which will in turn run the python script `data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_soluion_Hydra.py` + +- Update `data_gen/data_gen_NLE/config/config.yaml` to: + +```yaml +type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' +dim: 2 +``` + +- Finally, run the data merge script: + +```bash +python data_gen/data_gen_NLE/Data_Merge.py +``` + From 1b78258ea09752df8a333aaa25e463182ab610ab Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 24 Feb 2023 17:48:10 +0100 Subject: [PATCH 079/137] update readme with data gen info, add arXiv link --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d6cec44..83374cc 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # PDEBench The code repository for the NeurIPS 2022 paper -[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://openreview.net/pdf?id=dh_MkX0QfrK) +[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) -PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. The repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baseline. PDEBench features a much wider range of PDEs than existing benchmarks and included realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was crated to make the source code extensible and we invite active participation to improve and extent the benchmark. +PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. This repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baselines. PDEBench features a much wider range of PDEs than existing benchmarks and includes realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was created to make the source code extensible and we invite active participation from the SciML community to improve and extend the benchmark. ![Visualizations of some PDE problems covered by the benchmark.](https://github.com/pdebench/PDEBench/blob/main/pdebench_examples.PNG) -Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger and Mathias Niepert. +Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, and Mathias Niepert. ## Datasets and Pretrained Models @@ -105,7 +105,8 @@ The data generation codes are contained in [data_gen_NLE](./pdebench/data_gen/da - `AdvectionEq` directory with the source codes to generate 1D Advection equation training samples - `BurgersEq` directory with the source codes to generate 1D Burgers equation training samples - `CompressibleFluid` directory with the source codes to generate compressible Navier-Stokes equations training samples -- `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples (**Note: DarcyFlow data can be generated by run_DarcyFlow2D.sh in this folder.**) + - `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples (**Note: [DarcyFlow data can be generated by run_DarcyFlow2D.sh](pdebench/data_gen/data_gen_NLE/README.md) in this folder.**) + - `save` directory saving the generated training samples A typical example to generate training samples (1D Advection Equation): @@ -122,8 +123,9 @@ The config files for Hydra are stored in `config` directory in each PDE's direct 1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes Equations save data as a numpy array. So, to read those data via our dataloaders, the data transformation/merge should be performed. This can be done using `data_gen_NLE/Data_Merge.py` whose config file is located at: `data_gen/data_gen_NLE/config/config.yaml`. -After properly set parameters in the config file (type: name of PDEs, dim: number of spatial-dimension, bd: boundary condition), +After properly setting the parameters in the config file (type: name of PDEs, dim: number of spatial-dimension, bd: boundary condition), the corresponding HDF5 file could be obtained as: + ```bash python3 Data_Merge.py ``` @@ -145,14 +147,15 @@ There is an example in `example.env`. The download scripts are provided in [data_download](./pdebench/data_download). There are two options to download data. 1) Using `download_direct.py` (**recommended**) - - Retrieves data shards directly using URLs. Sample command for each PDE is given the README file in the [data_download](./pdebench/data_download) directory. -2) Using `download_easydataverse.py` (might encounter errors/issues, hence not recommended) + - Retrieves data shards directly using URLs. Sample command for each PDE is given in the README file in the [data_download](./pdebench/data_download) directory. +2) Using `download_easydataverse.py` (might be slow and you could encounter errors/issues; hence, not recommended!) - Use the config files from the `config` directory that contains the yaml files storing the configuration. Any files in the dataset matching `args.filename` will be downloaded into `args.data_folder`. ## Baseline Models In this work, we provide three different ML models to be trained and evaluated against the benchmark datasets, namely [FNO](https://arxiv.org/pdf/2010.08895.pdf), [U-Net](https://www.sciencedirect.com/science/article/abs/pii/S0010482519301520?via%3Dihub), and [PINN](https://www.sciencedirect.com/science/article/pii/S0021999118307125). The codes for the baseline model implementations are contained in [models](./pdebench/models): + - `train_models_forward.py` is the main script to train and evaluate the model. It will call on model-specific script based on the input argument. - `train_models_inverse.py` is the main script to train and evaluate the model for inverse problems. It will call on model-specific script based on the input argument. - `metrics.py` is the script to evaluate the trained models based on various evaluation metrics described in our paper. Additionally, it also plots the prediction and target data. From f5a4f0ac76e5241f9699de8bc3c4499a3432eed2 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Thu, 23 Mar 2023 16:55:45 +0100 Subject: [PATCH 080/137] modifying:MSE->RMSE in metric.py by MT@23032023 --- pdebench/models/metrics.py | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 82d04d0..f67942d 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -157,7 +157,7 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=12): """ code for calculate metrics discussed in the Brain-storming session - MSE, normalized MSE, max error, MSE at the boundaries, conserved variables, MSE in Fourier space, temporal sensitivity + RMSE, normalized RMSE, max error, RMSE at the boundaries, conserved variables, RMSE in Fourier space, temporal sensitivity """ pred, target = pred.to(device), target.to(device) # (batch, nx^i..., timesteps, nc) @@ -174,11 +174,11 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 idxs = target.size() nb, nc, nt = idxs[0], idxs[1], idxs[-1] - # MSE + # RMSE err_mean = torch.sqrt(torch.mean((pred.view([nb, nc, -1, nt]) - target.view([nb, nc, -1, nt])) ** 2, dim=2)) - err_MSE = torch.mean(err_mean, axis=0) + err_RMSE = torch.mean(err_mean, axis=0) nrm = torch.sqrt(torch.mean(target.view([nb, nc, -1, nt]) ** 2, dim=2)) - err_nMSE = torch.mean(err_mean / nrm, dim=0) + err_nRMSE = torch.mean(err_mean / nrm, dim=0) err_CSV = torch.sqrt(torch.mean( (torch.sum(pred.view([nb, nc, -1, nt]), dim=2) - torch.sum(target.view([nb, nc, -1, nt]), dim=2)) ** 2, @@ -261,14 +261,14 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 err_F[:,2] += torch.mean(_err_F[:,iHigh:], dim=1) # high freq if if_mean: - return torch.mean(err_MSE, dim=[0, -1]), \ - torch.mean(err_nMSE, dim=[0, -1]), \ + return torch.mean(err_RMSE, dim=[0, -1]), \ + torch.mean(err_nRMSE, dim=[0, -1]), \ torch.mean(err_CSV, dim=[0, -1]), \ torch.mean(err_Max, dim=[0, -1]), \ torch.mean(err_BD, dim=[0, -1]), \ torch.mean(err_F, dim=[0, -1]) else: - return err_MSE, err_nMSE, err_CSV, err_Max, err_BD, err_F + return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min, x_max, y_min, y_max, t_min, t_max, mode='FNO', initial_step=None, ): @@ -299,18 +299,18 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min pred = torch.cat((pred, im), -2) xx = torch.cat((xx[..., 1:, :], im), dim=-2) - _err_MSE, _err_nMSE, _err_CSV, _err_Max, _err_BD, _err_F \ + _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F \ = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) if itot == 0: - err_MSE, err_nMSE, err_CSV, err_Max, err_BD, err_F \ - = _err_MSE, _err_nMSE, _err_CSV, _err_Max, _err_BD, _err_F + err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F \ + = _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F pred_plot = pred[:1] target_plot = yy[:1] val_l2_time = torch.zeros(yy.shape[-2]).to(device) else: - err_MSE += _err_MSE - err_nMSE += _err_nMSE + err_RMSE += _err_RMSE + err_nRMSE += _err_nRMSE err_CSV += _err_CSV err_Max += _err_Max err_BD += _err_BD @@ -343,17 +343,17 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min pred = torch.cat((pred, im), -2) xx = torch.cat((xx[..., 1:, :], im), dim=-2) - _err_MSE, _err_nMSE, _err_CSV, _err_Max, _err_BD, _err_F \ + _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F \ = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) if itot == 0: - err_MSE, err_nMSE, err_CSV, err_Max, err_BD, err_F \ - = _err_MSE, _err_nMSE, _err_CSV, _err_Max, _err_BD, _err_F + err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F \ + = _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F pred_plot = pred[:1] target_plot = yy[:1] val_l2_time = torch.zeros(yy.shape[-2]).to(device) else: - err_MSE += _err_MSE - err_nMSE += _err_nMSE + err_RMSE += _err_RMSE + err_nRMSE += _err_nRMSE err_CSV += _err_CSV err_Max += _err_Max err_BD += _err_BD @@ -370,18 +370,18 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min raise NotImplementedError - err_MSE = np.array(err_MSE.data.cpu()/itot) - err_nMSE = np.array(err_nMSE.data.cpu()/itot) + err_RMSE = np.array(err_RMSE.data.cpu()/itot) + err_nRMSE = np.array(err_nRMSE.data.cpu()/itot) err_CSV = np.array(err_CSV.data.cpu()/itot) err_Max = np.array(err_Max.data.cpu()/itot) err_BD = np.array(err_BD.data.cpu()/itot) err_F = np.array(err_F.data.cpu()/itot) - print('MSE: {0:.5f}'.format(err_MSE)) - print('normalized MSE: {0:.5f}'.format(err_nMSE)) - print('MSE of conserved variables: {0:.5f}'.format(err_CSV)) + print('RMSE: {0:.5f}'.format(err_RMSE)) + print('normalized RMSE: {0:.5f}'.format(err_nRMSE)) + print('RMSE of conserved variables: {0:.5f}'.format(err_CSV)) print('Maximum value of rms error: {0:.5f}'.format(err_Max)) - print('MSE at boundaries: {0:.5f}'.format(err_BD)) - print('MSE in Fourier space: {0}'.format(err_F)) + print('RMSE at boundaries: {0:.5f}'.format(err_BD)) + print('RMSE in Fourier space: {0}'.format(err_F)) val_l2_time = val_l2_time/itot From aa8d79cc3e62d45a29cbc95603bf8ce8846bde12 Mon Sep 17 00:00:00 2001 From: nle18370 Date: Thu, 20 Apr 2023 13:42:19 +0200 Subject: [PATCH 081/137] debug in metric.py by MT@20042023 --- pdebench/models/metrics.py | 2 +- pdebench/models/run_forward_1D.sh | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index f67942d..11697c8 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -474,7 +474,7 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min np.savez(filename, t=torch.arange(initial_step,yy.shape[-2]).cpu(), mse=val_l2_time[initial_step:].detach().cpu()) - return err_MSE, err_nMSE, err_CSV, err_Max, err_BD, err_F + return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F # LpLoss Function diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index ce04e70..c2861fa 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -1,10 +1,10 @@ ## 'FNO' # Advection -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='FNO' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='FNO' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' ++args.if_training=False @@ -28,11 +28,11 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml + CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Bgs.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False ## Unet # Advection -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='Unet' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' ++args.if_training=False From 7313816ab15b35df6bb917db278382ba6a6c67bc Mon Sep 17 00:00:00 2001 From: nle18370 Date: Mon, 24 Apr 2023 13:49:37 +0200 Subject: [PATCH 082/137] edit run_forward_1D.sh by MT@24042023 --- pdebench/models/run_forward_1D.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index c2861fa..9128b3e 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -4,7 +4,7 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml + CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='FNO' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='FNO'++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='FNO' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='FNO' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' ++args.if_training=False @@ -32,7 +32,7 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml + CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='Unet' ++args.if_training=False +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='Unet'++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.4.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.model_name='Unet' ++args.if_training=False CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' ++args.if_training=False From c0a622c73ff1a0066ccecad5c5b2ed9dbc6563eb Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 28 May 2023 09:28:53 +0200 Subject: [PATCH 083/137] update config & training script (addressing #34) --- pdebench/models/config/config_darcy.yaml | 61 +++++ pdebench/models/fno/train.py | 5 +- pdebench/models/train_models_forward.py | 4 +- pdebench/models/train_models_forward_darcy.py | 251 ++++++++++++++++++ 4 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 pdebench/models/config/config_darcy.yaml create mode 100644 pdebench/models/train_models_forward_darcy.py diff --git a/pdebench/models/config/config_darcy.yaml b/pdebench/models/config/config_darcy.yaml new file mode 100644 index 0000000..02bc9dc --- /dev/null +++ b/pdebench/models/config/config_darcy.yaml @@ -0,0 +1,61 @@ +defaults: + - _self_ + - override hydra/hydra_logging: disabled + - override hydra/job_logging: disabled + +hydra: + output_subdir: null + run: + dir: . + +args: + model_name: 'FNO' + if_training: True + continue_training: False + num_workers: 2 + batch_size: 50 + initial_step: 1 + t_train: 1 # steady-state + model_update: 2 + filename: '2D_DarcyFlow_beta0.01_Train.hdf5' + single_file: True + reduced_resolution: 1 + reduced_resolution_t: 1 + reduced_batch: 1 + epochs: 500 + learning_rate: 1.e-3 + scheduler_step: 100 + scheduler_gamma: 0.5 + #Unet + in_channels: 1 + out_channels: 1 + ar_mode: False + pushforward: False + unroll_step: 1 + #FNO + num_channels: 1 + modes: 12 + width: 20 + #Inverse + data_path: '../data/2D/DarcyFlow/Train/' + training_type: 'single' #autoregressive + #Inverse MCMC + mcmc_num_samples: 20 + mcmc_warmup_steps: 10 + mcmc_num_chains: 1 + num_samples_max: 1000 + in_channels_hid: 64 + inverse_model_type: InitialConditionInterp + #Inverse grad + inverse_epochs: 100 + inverse_learning_rate: 0.2 + inverse_verbose_flag: False + #plotting + plot: False + channel_plot: 0 # Which channel/variable to be plotted + x_min: -1 + x_max: 1 # spatial dimension x: [-1, 1] + y_min: -1 + y_max: 1 # spatial dimension y: [-1, 1] + t_min: 0 + t_max: 5 # time dimension t: [0, 5] diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index bbc0ecf..a3f06c1 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -191,7 +191,7 @@ def run_training(if_training, xx = xx.to(device) yy = yy.to(device) grid = grid.to(device) - + # Initialize the prediction tensor pred = yy[..., :initial_step, :] # Extract shape of the input tensor for reshaping (i.e. stacking the @@ -310,4 +310,5 @@ def run_training(if_training, if __name__ == "__main__": run_training() - print("Done.") \ No newline at end of file + print("Done.") + diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 89ae45a..1200abd 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -160,7 +160,7 @@ from pdebench.models.unet.train import run_training as run_training_Unet -@hydra.main(config_path="config", config_name="config") +@hydra.main(version_base="1.2", config_path="config", config_name="config_darcy") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": print("FNO") @@ -172,6 +172,7 @@ def main(cfg: DictConfig): width=cfg.args.width, initial_step=cfg.args.initial_step, t_train=cfg.args.t_train, + training_type=cfg.args.training_type, num_channels=cfg.args.num_channels, batch_size=cfg.args.batch_size, epochs=cfg.args.epochs, @@ -181,6 +182,7 @@ def main(cfg: DictConfig): model_update=cfg.args.model_update, flnm=cfg.args.filename, single_file=cfg.args.single_file, + base_path=cfg.args.data_path, reduced_resolution=cfg.args.reduced_resolution, reduced_resolution_t=cfg.args.reduced_resolution_t, reduced_batch=cfg.args.reduced_batch, diff --git a/pdebench/models/train_models_forward_darcy.py b/pdebench/models/train_models_forward_darcy.py new file mode 100644 index 0000000..fc9c98d --- /dev/null +++ b/pdebench/models/train_models_forward_darcy.py @@ -0,0 +1,251 @@ +""" + + + File: train_models_forward.py + Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) + Timothy Praditia (timothy.praditia@iws.uni-stuttgart.de) + Raphael Leiteritz (raphael.leiteritz@ipvs.uni-stuttgart.de) + +NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. + + PROPRIETARY INFORMATION --- + +SOFTWARE LICENSE AGREEMENT + +ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY + +BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS +LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR +DOWNLOAD THE SOFTWARE. + +This is a license agreement ("Agreement") between your academic institution +or non-profit organization or self (called "Licensee" or "You" in this +Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this +Agreement). All rights not specifically granted to you in this Agreement +are reserved for Licensor. + +RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive +ownership of any copy of the Software (as defined below) licensed under this +Agreement and hereby grants to Licensee a personal, non-exclusive, +non-transferable license to use the Software for noncommercial research +purposes, without the right to sublicense, pursuant to the terms and +conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF +LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this +Agreement, the term "Software" means (i) the actual copy of all or any +portion of code for program routines made accessible to Licensee by Licensor +pursuant to this Agreement, inclusive of backups, updates, and/or merged +copies permitted hereunder or subsequently supplied by Licensor, including +all or any file structures, programming instructions, user interfaces and +screen formats and sequences as well as any and all documentation and +instructions related to it, and (ii) all or any derivatives and/or +modifications created or made by You to any of the items specified in (i). + +CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is +proprietary to Licensor, and as such, Licensee agrees to receive all such +materials and to use the Software only in accordance with the terms of this +Agreement. Licensee agrees to use reasonable effort to protect the Software +from unauthorized use, reproduction, distribution, or publication. All +publication materials mentioning features or use of this software must +explicitly include an acknowledgement the software was developed by NEC +Laboratories Europe GmbH. + +COPYRIGHT: The Software is owned by Licensor. + +PERMITTED USES: The Software may be used for your own noncommercial +internal research purposes. You understand and agree that Licensor is not +obligated to implement any suggestions and/or feedback you might provide +regarding the Software, but to the extent Licensor does so, you are not +entitled to any compensation related thereto. + +DERIVATIVES: You may create derivatives of or make modifications to the +Software, however, You agree that all and any such derivatives and +modifications will be owned by Licensor and become a part of the Software +licensed to You under this Agreement. You may only use such derivatives and +modifications for your own noncommercial internal research purposes, and you +may not otherwise use, distribute or copy such derivatives and modifications +in violation of this Agreement. + +BACKUPS: If Licensee is an organization, it may make that number of copies +of the Software necessary for internal noncommercial use at a single site +within its organization provided that all information appearing in or on the +original labels, including the copyright and trademark notices are copied +onto the labels of the copies. + +USES NOT PERMITTED: You may not distribute, copy or use the Software except +as explicitly permitted herein. Licensee has not been granted any trademark +license as part of this Agreement. Neither the name of NEC Laboratories +Europe GmbH nor the names of its contributors may be used to endorse or +promote products derived from this Software without specific prior written +permission. + +You may not sell, rent, lease, sublicense, lend, time-share or transfer, in +whole or in part, or provide third parties access to prior or present +versions (or any parts thereof) of the Software. + +ASSIGNMENT: You may not assign this Agreement or your rights hereunder +without the prior written consent of Licensor. Any attempted assignment +without such consent shall be null and void. + +TERM: The term of the license granted by this Agreement is from Licensee's +acceptance of this Agreement by downloading the Software or by using the +Software until terminated as provided below. + +The Agreement automatically terminates without notice if you fail to comply +with any provision of this Agreement. Licensee may terminate this Agreement +by ceasing using the Software. Upon any termination of this Agreement, +Licensee will delete any and all copies of the Software. You agree that all +provisions which operate to protect the proprietary rights of Licensor shall +remain in force should breach occur and that the obligation of +confidentiality described in this Agreement is binding in perpetuity and, as +such, survives the term of the Agreement. + +FEE: Provided Licensee abides completely by the terms and conditions of this +Agreement, there is no fee due to Licensor for Licensee's use of the +Software in accordance with this Agreement. + +DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY +OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR +FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE +BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND +RELATED MATERIALS. + +SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is +provided as part of this Agreement. + +EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent +permitted under applicable law, Licensor shall not be liable for direct, +indirect, special, incidental, or consequential damages or lost profits +related to Licensee's use of and/or inability to use the Software, even if +Licensor is advised of the possibility of such damage. + +EXPORT REGULATION: Licensee agrees to comply with any and all applicable +export control laws, regulations, and/or other laws related to embargoes and +sanction programs administered by law. + +SEVERABILITY: If any provision(s) of this Agreement shall be held to be +invalid, illegal, or unenforceable by a court or other tribunal of competent +jurisdiction, the validity, legality and enforceability of the remaining +provisions shall not in any way be affected or impaired thereby. + +NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right +or remedy under this Agreement shall be construed as a waiver of any future +or other exercise of such right or remedy by Licensor. + +GOVERNING LAW: This Agreement shall be construed and enforced in accordance +with the laws of Germany without reference to conflict of laws principles. +You consent to the personal jurisdiction of the courts of this country and +waive their rights to venue outside of Germany. + +ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and +entire agreement between Licensee and Licensor as to the matter set forth +herein and supersedes any previous agreements, understandings, and +arrangements between the parties relating hereto. + + THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. +""" +import sys, os +import hydra +from omegaconf import DictConfig + +import operator +from functools import reduce +from functools import partial + +from timeit import default_timer + +from pdebench.models.fno.train import run_training as run_training_FNO +#from pdebench.models.pinn.train import run_training as run_training_PINN +#from pdebench.models.unet.train import run_training as run_training_Unet + + +@hydra.main(config_path="config", config_name="config_darcy") +def main(cfg: DictConfig): + if cfg.args.model_name == "FNO": + print("FNO Arch") + run_training_FNO( + if_training=cfg.args.if_training, + training_type=cfg.args.training_type, + continue_training=cfg.args.continue_training, + num_workers=cfg.args.num_workers, + modes=cfg.args.modes, + width=cfg.args.width, + initial_step=cfg.args.initial_step, + t_train=cfg.args.t_train, + num_channels=cfg.args.num_channels, + batch_size=cfg.args.batch_size, + epochs=cfg.args.epochs, + learning_rate=cfg.args.learning_rate, + scheduler_step=cfg.args.scheduler_step, + scheduler_gamma=cfg.args.scheduler_gamma, + model_update=cfg.args.model_update, + flnm=cfg.args.filename, + single_file=cfg.args.single_file, + base_path=cfg.args.data_path, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + # plot=cfg.args.plot, + # channel_plot=cfg.args.channel_plot, + # x_min=cfg.args.x_min, + # x_max=cfg.args.x_max, + # y_min=cfg.args.y_min, + # y_max=cfg.args.y_max, + # t_min=cfg.args.t_min, + # t_max=cfg.args.t_max, + ) + elif cfg.args.model_name == "Unet": + print("Unet") + run_training_Unet( + if_training=cfg.args.if_training, + continue_training=cfg.args.continue_training, + num_workers=cfg.args.num_workers, + initial_step=cfg.args.initial_step, + t_train=cfg.args.t_train, + in_channels=cfg.args.in_channels, + out_channels=cfg.args.out_channels, + epochs=cfg.args.epochs, + learning_rate=cfg.args.learning_rate, + batch_size=cfg.args.batch_size, + unroll_step=cfg.args.unroll_step, + ar_mode=cfg.args.ar_mode, + pushforward=cfg.args.pushforward, + scheduler_step=cfg.args.scheduler_step, + scheduler_gamma=cfg.args.scheduler_gamma, + model_update=cfg.args.model_update, + flnm=cfg.args.filename, + single_file=cfg.args.single_file, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + plot=cfg.args.plot, + channel_plot=cfg.args.channel_plot, + x_min=cfg.args.x_min, + x_max=cfg.args.x_max, + y_min=cfg.args.y_min, + y_max=cfg.args.y_max, + t_min=cfg.args.t_min, + t_max=cfg.args.t_max, + ) + elif cfg.args.model_name == "PINN": + print("PINN") + run_training_PINN( + scenario=cfg.args.scenario, + epochs=cfg.args.epochs, + learning_rate=cfg.args.learning_rate, + model_update=cfg.args.model_update, + flnm=cfg.args.filename, + seed=cfg.args.seed, + input_ch=cfg.args.input_ch, + output_ch=cfg.args.output_ch, + root_path=cfg.args.root_path, + val_num=cfg.args.val_num, + if_periodic_bc=cfg.args.if_periodic_bc, + aux_params=cfg.args.aux_params + ) + + +if __name__ == "__main__": + main() + print("Done.") From 3c21d4045e8cea579927e0839929b346f2fd557e Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 28 May 2023 09:44:58 +0200 Subject: [PATCH 084/137] update darcy config & training script (addressing #34) --- pdebench/models/train_models_forward_darcy.py | 251 ------------------ 1 file changed, 251 deletions(-) delete mode 100644 pdebench/models/train_models_forward_darcy.py diff --git a/pdebench/models/train_models_forward_darcy.py b/pdebench/models/train_models_forward_darcy.py deleted file mode 100644 index fc9c98d..0000000 --- a/pdebench/models/train_models_forward_darcy.py +++ /dev/null @@ -1,251 +0,0 @@ -""" - - - File: train_models_forward.py - Authors: Makoto Takamoto (makoto.takamoto@neclab.eu) - Timothy Praditia (timothy.praditia@iws.uni-stuttgart.de) - Raphael Leiteritz (raphael.leiteritz@ipvs.uni-stuttgart.de) - -NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. - - THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. - - PROPRIETARY INFORMATION --- - -SOFTWARE LICENSE AGREEMENT - -ACADEMIC OR NON-PROFIT ORGANIZATION NONCOMMERCIAL RESEARCH USE ONLY - -BY USING OR DOWNLOADING THE SOFTWARE, YOU ARE AGREEING TO THE TERMS OF THIS -LICENSE AGREEMENT. IF YOU DO NOT AGREE WITH THESE TERMS, YOU MAY NOT USE OR -DOWNLOAD THE SOFTWARE. - -This is a license agreement ("Agreement") between your academic institution -or non-profit organization or self (called "Licensee" or "You" in this -Agreement) and NEC Laboratories Europe GmbH (called "Licensor" in this -Agreement). All rights not specifically granted to you in this Agreement -are reserved for Licensor. - -RESERVATION OF OWNERSHIP AND GRANT OF LICENSE: Licensor retains exclusive -ownership of any copy of the Software (as defined below) licensed under this -Agreement and hereby grants to Licensee a personal, non-exclusive, -non-transferable license to use the Software for noncommercial research -purposes, without the right to sublicense, pursuant to the terms and -conditions of this Agreement. NO EXPRESS OR IMPLIED LICENSES TO ANY OF -LICENSOR'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. As used in this -Agreement, the term "Software" means (i) the actual copy of all or any -portion of code for program routines made accessible to Licensee by Licensor -pursuant to this Agreement, inclusive of backups, updates, and/or merged -copies permitted hereunder or subsequently supplied by Licensor, including -all or any file structures, programming instructions, user interfaces and -screen formats and sequences as well as any and all documentation and -instructions related to it, and (ii) all or any derivatives and/or -modifications created or made by You to any of the items specified in (i). - -CONFIDENTIALITY/PUBLICATIONS: Licensee acknowledges that the Software is -proprietary to Licensor, and as such, Licensee agrees to receive all such -materials and to use the Software only in accordance with the terms of this -Agreement. Licensee agrees to use reasonable effort to protect the Software -from unauthorized use, reproduction, distribution, or publication. All -publication materials mentioning features or use of this software must -explicitly include an acknowledgement the software was developed by NEC -Laboratories Europe GmbH. - -COPYRIGHT: The Software is owned by Licensor. - -PERMITTED USES: The Software may be used for your own noncommercial -internal research purposes. You understand and agree that Licensor is not -obligated to implement any suggestions and/or feedback you might provide -regarding the Software, but to the extent Licensor does so, you are not -entitled to any compensation related thereto. - -DERIVATIVES: You may create derivatives of or make modifications to the -Software, however, You agree that all and any such derivatives and -modifications will be owned by Licensor and become a part of the Software -licensed to You under this Agreement. You may only use such derivatives and -modifications for your own noncommercial internal research purposes, and you -may not otherwise use, distribute or copy such derivatives and modifications -in violation of this Agreement. - -BACKUPS: If Licensee is an organization, it may make that number of copies -of the Software necessary for internal noncommercial use at a single site -within its organization provided that all information appearing in or on the -original labels, including the copyright and trademark notices are copied -onto the labels of the copies. - -USES NOT PERMITTED: You may not distribute, copy or use the Software except -as explicitly permitted herein. Licensee has not been granted any trademark -license as part of this Agreement. Neither the name of NEC Laboratories -Europe GmbH nor the names of its contributors may be used to endorse or -promote products derived from this Software without specific prior written -permission. - -You may not sell, rent, lease, sublicense, lend, time-share or transfer, in -whole or in part, or provide third parties access to prior or present -versions (or any parts thereof) of the Software. - -ASSIGNMENT: You may not assign this Agreement or your rights hereunder -without the prior written consent of Licensor. Any attempted assignment -without such consent shall be null and void. - -TERM: The term of the license granted by this Agreement is from Licensee's -acceptance of this Agreement by downloading the Software or by using the -Software until terminated as provided below. - -The Agreement automatically terminates without notice if you fail to comply -with any provision of this Agreement. Licensee may terminate this Agreement -by ceasing using the Software. Upon any termination of this Agreement, -Licensee will delete any and all copies of the Software. You agree that all -provisions which operate to protect the proprietary rights of Licensor shall -remain in force should breach occur and that the obligation of -confidentiality described in this Agreement is binding in perpetuity and, as -such, survives the term of the Agreement. - -FEE: Provided Licensee abides completely by the terms and conditions of this -Agreement, there is no fee due to Licensor for Licensee's use of the -Software in accordance with this Agreement. - -DISCLAIMER OF WARRANTIES: THE SOFTWARE IS PROVIDED "AS-IS" WITHOUT WARRANTY -OF ANY KIND INCLUDING ANY WARRANTIES OF PERFORMANCE OR MERCHANTABILITY OR -FITNESS FOR A PARTICULAR USE OR PURPOSE OR OF NON- INFRINGEMENT. LICENSEE -BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE SOFTWARE AND -RELATED MATERIALS. - -SUPPORT AND MAINTENANCE: No Software support or training by the Licensor is -provided as part of this Agreement. - -EXCLUSIVE REMEDY AND LIMITATION OF LIABILITY: To the maximum extent -permitted under applicable law, Licensor shall not be liable for direct, -indirect, special, incidental, or consequential damages or lost profits -related to Licensee's use of and/or inability to use the Software, even if -Licensor is advised of the possibility of such damage. - -EXPORT REGULATION: Licensee agrees to comply with any and all applicable -export control laws, regulations, and/or other laws related to embargoes and -sanction programs administered by law. - -SEVERABILITY: If any provision(s) of this Agreement shall be held to be -invalid, illegal, or unenforceable by a court or other tribunal of competent -jurisdiction, the validity, legality and enforceability of the remaining -provisions shall not in any way be affected or impaired thereby. - -NO IMPLIED WAIVERS: No failure or delay by Licensor in enforcing any right -or remedy under this Agreement shall be construed as a waiver of any future -or other exercise of such right or remedy by Licensor. - -GOVERNING LAW: This Agreement shall be construed and enforced in accordance -with the laws of Germany without reference to conflict of laws principles. -You consent to the personal jurisdiction of the courts of this country and -waive their rights to venue outside of Germany. - -ENTIRE AGREEMENT AND AMENDMENTS: This Agreement constitutes the sole and -entire agreement between Licensee and Licensor as to the matter set forth -herein and supersedes any previous agreements, understandings, and -arrangements between the parties relating hereto. - - THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. -""" -import sys, os -import hydra -from omegaconf import DictConfig - -import operator -from functools import reduce -from functools import partial - -from timeit import default_timer - -from pdebench.models.fno.train import run_training as run_training_FNO -#from pdebench.models.pinn.train import run_training as run_training_PINN -#from pdebench.models.unet.train import run_training as run_training_Unet - - -@hydra.main(config_path="config", config_name="config_darcy") -def main(cfg: DictConfig): - if cfg.args.model_name == "FNO": - print("FNO Arch") - run_training_FNO( - if_training=cfg.args.if_training, - training_type=cfg.args.training_type, - continue_training=cfg.args.continue_training, - num_workers=cfg.args.num_workers, - modes=cfg.args.modes, - width=cfg.args.width, - initial_step=cfg.args.initial_step, - t_train=cfg.args.t_train, - num_channels=cfg.args.num_channels, - batch_size=cfg.args.batch_size, - epochs=cfg.args.epochs, - learning_rate=cfg.args.learning_rate, - scheduler_step=cfg.args.scheduler_step, - scheduler_gamma=cfg.args.scheduler_gamma, - model_update=cfg.args.model_update, - flnm=cfg.args.filename, - single_file=cfg.args.single_file, - base_path=cfg.args.data_path, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - # plot=cfg.args.plot, - # channel_plot=cfg.args.channel_plot, - # x_min=cfg.args.x_min, - # x_max=cfg.args.x_max, - # y_min=cfg.args.y_min, - # y_max=cfg.args.y_max, - # t_min=cfg.args.t_min, - # t_max=cfg.args.t_max, - ) - elif cfg.args.model_name == "Unet": - print("Unet") - run_training_Unet( - if_training=cfg.args.if_training, - continue_training=cfg.args.continue_training, - num_workers=cfg.args.num_workers, - initial_step=cfg.args.initial_step, - t_train=cfg.args.t_train, - in_channels=cfg.args.in_channels, - out_channels=cfg.args.out_channels, - epochs=cfg.args.epochs, - learning_rate=cfg.args.learning_rate, - batch_size=cfg.args.batch_size, - unroll_step=cfg.args.unroll_step, - ar_mode=cfg.args.ar_mode, - pushforward=cfg.args.pushforward, - scheduler_step=cfg.args.scheduler_step, - scheduler_gamma=cfg.args.scheduler_gamma, - model_update=cfg.args.model_update, - flnm=cfg.args.filename, - single_file=cfg.args.single_file, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - plot=cfg.args.plot, - channel_plot=cfg.args.channel_plot, - x_min=cfg.args.x_min, - x_max=cfg.args.x_max, - y_min=cfg.args.y_min, - y_max=cfg.args.y_max, - t_min=cfg.args.t_min, - t_max=cfg.args.t_max, - ) - elif cfg.args.model_name == "PINN": - print("PINN") - run_training_PINN( - scenario=cfg.args.scenario, - epochs=cfg.args.epochs, - learning_rate=cfg.args.learning_rate, - model_update=cfg.args.model_update, - flnm=cfg.args.filename, - seed=cfg.args.seed, - input_ch=cfg.args.input_ch, - output_ch=cfg.args.output_ch, - root_path=cfg.args.root_path, - val_num=cfg.args.val_num, - if_periodic_bc=cfg.args.if_periodic_bc, - aux_params=cfg.args.aux_params - ) - - -if __name__ == "__main__": - main() - print("Done.") From f50c1bfdfda780bdaea55422be2bebeb99d30a37 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 28 May 2023 10:15:08 +0200 Subject: [PATCH 085/137] update gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4267a68..4780cfa 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,7 @@ debug/ # macos .DS_Store + +# neural net, sciml artefacts +.pt +.hdf5 From 79f7a6db8570529b3fe4fb5cf61b8bff04e5ca05 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 31 May 2023 15:08:05 +0200 Subject: [PATCH 086/137] Update README.md Add recent citations --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 83374cc..eb38091 100644 --- a/README.md +++ b/README.md @@ -285,12 +285,18 @@ Below is an illustration of the directory structure of PDEBench. ``` - ------ - ## Citations +Please cite the following papers if you use PDEBench datasets and/or source code in your research. + +

+ + PDEBench: An Extensive Benchmark for Scientific Machine Learning - NeurIPS'2022 + +
+ ``` @inproceedings{PDEBench2022, author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, @@ -299,8 +305,18 @@ year = {2022}, booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2022) Track on Datasets and Benchmarks}, url = {https://arxiv.org/abs/2210.07182} } +``` +
+ +
+ + PDEBench Datasets - NeurIPS'2022 + +
+ +``` @data{darus-2986_2022, author = {Takamoto, Makoto and Praditia, Timothy and Leiteritz, Raphael and MacKinlay, Dan and Alesiani, Francesco and Pflüger, Dirk and Niepert, Mathias}, publisher = {DaRUS}, @@ -310,6 +326,33 @@ doi = {10.18419/darus-2986}, url = {https://doi.org/10.18419/darus-2986} } ``` +
+ +
+ + Learning Neural PDE Solvers with Parameter-Guided Channel Attention - ICML'2023 + +
+ + ``` + @article{cape-takamoto:2023, + author = {Makoto Takamoto and + Francesco Alesiani and + Mathias Niepert}, + title = {Learning Neural {PDE} Solvers with Parameter-Guided Channel Attention}, + journal = {CoRR}, + volume = {abs/2304.14118}, + year = {2023}, + url = {https://doi.org/10.48550/arXiv.2304.14118}, + doi = {10.48550/arXiv.2304.14118}, + eprinttype = {arXiv}, + eprint = {2304.14118}, + } + ``` + +
+ +------ ## Code contributors @@ -330,4 +373,4 @@ url = {https://doi.org/10.18419/darus-2986} ## License MIT licensed, except where otherwise stated. -See `LICENSE.txt` file. \ No newline at end of file +See `LICENSE.txt` file. From 331b96a0abf7dced3b02666707309b237c8959bb Mon Sep 17 00:00:00 2001 From: leiterrl Date: Tue, 20 Jun 2023 15:07:31 +0200 Subject: [PATCH 087/137] fix deepxde interfering with other training routines --- pdebench/models/train_models_forward.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 1200abd..7a8ed2b 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -155,14 +155,12 @@ from timeit import default_timer -from pdebench.models.fno.train import run_training as run_training_FNO -from pdebench.models.pinn.train import run_training as run_training_PINN -from pdebench.models.unet.train import run_training as run_training_Unet @hydra.main(version_base="1.2", config_path="config", config_name="config_darcy") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": + from pdebench.models.fno.train import run_training as run_training_FNO print("FNO") run_training_FNO( if_training=cfg.args.if_training, @@ -196,6 +194,7 @@ def main(cfg: DictConfig): t_max=cfg.args.t_max, ) elif cfg.args.model_name == "Unet": + from pdebench.models.unet.train import run_training as run_training_Unet print("Unet") run_training_Unet( if_training=cfg.args.if_training, @@ -229,6 +228,8 @@ def main(cfg: DictConfig): t_max=cfg.args.t_max, ) elif cfg.args.model_name == "PINN": + # not importing globally as DeepXDE changes some global PyTorch settings + from pdebench.models.pinn.train import run_training as run_training_PINN print("PINN") run_training_PINN( scenario=cfg.args.scenario, From 5ad4c0f54d21ce49fba2e68b11cbe484931f7283 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 22 Jul 2023 17:39:24 +0200 Subject: [PATCH 088/137] add award info --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eb38091..8a98f5e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # PDEBench The code repository for the NeurIPS 2022 paper -[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) +[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) :tada: **SimTech Best Paper Award 2023** :confetti_ball: PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. This repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baselines. PDEBench features a much wider range of PDEs than existing benchmarks and includes realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was created to make the source code extensible and we invite active participation from the SciML community to improve and extend the benchmark. ![Visualizations of some PDE problems covered by the benchmark.](https://github.com/pdebench/PDEBench/blob/main/pdebench_examples.PNG) - Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, and Mathias Niepert. +--------------- + ## Datasets and Pretrained Models We also provide datasets and pretrained machine learning models. @@ -333,7 +334,7 @@ url = {https://doi.org/10.18419/darus-2986} Learning Neural PDE Solvers with Parameter-Guided Channel Attention - ICML'2023
- + ``` @article{cape-takamoto:2023, author = {Makoto Takamoto and @@ -349,7 +350,7 @@ url = {https://doi.org/10.18419/darus-2986} eprint = {2304.14118}, } ``` - + ------ From a3eb903582accdd2aa5d62d7f11c58228fa2fa46 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 22 Jul 2023 19:26:33 +0200 Subject: [PATCH 089/137] update data download urls --- pdebench/data_download/download_metadata.csv | 26 -------------------- 1 file changed, 26 deletions(-) diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv index 2f1f601..c820240 100644 --- a/pdebench/data_download/download_metadata.csv +++ b/pdebench/data_download/download_metadata.csv @@ -5,13 +5,6 @@ Advection,1D_Advection_Sols_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/acce Advection,1D_Advection_Sols_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133111,1D/Advection/Train/,91a599ed8c11447dec8200b0915571a3 Advection,1D_Advection_Sols_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133112,1D/Advection/Train/,24bb9795a558bcc3bc9103b1cb791965 Advection,1D_Advection_Sols_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133113,1D/Advection/Train/,73ec4f7980462a13629234ec2f849e78 -Advection,Advection_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133025,1D/Advection/Test/,77023c3b3b47e3d57ebca03a020d5a66 -Advection,Advection_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133028,1D/Advection/Test/,bb37c9a0faf199e51a66b82d7ca25122 -Advection,Advection_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133026,1D/Advection/Test/,79040b74bce5469374b59e3b7246a4d4 -Advection,Advection_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133027,1D/Advection/Test/,500bb617627770febe6f3caadd2ba68a -Advection,Advection_beta10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133023,1D/Advection/Test/,dbc70127e36f037bdb7d43e88e6fa409 -Advection,Advection_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133024,1D/Advection/Test/,36876a5d0e720e12685e5bc2e37a2885 -Advection,Advection_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133022,1D/Advection/Test/,68faad750411f2b3d30caae880558d39 Burgers,1D_Burgers_Sols_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133133,1D/Burgers/Train/,5f7242c820efb37bae3d82e983c04e43 Burgers,1D_Burgers_Sols_Nu0.002.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133134,1D/Burgers/Train/,eb7cc49d7d4d346d7abe9af9eee3f86b Burgers,1D_Burgers_Sols_Nu0.004.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133135,1D/Burgers/Train/,e1c40dd4c0d8b858231408009e8a8def @@ -24,25 +17,6 @@ Burgers,1D_Burgers_Sols_Nu0.4.hdf5,https://darus.uni-stuttgart.de/api/access/dat Burgers,1D_Burgers_Sols_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133142,1D/Burgers/Train/,69c1890c49fc7ba36aac7107c41d5f41 Burgers,1D_Burgers_Sols_Nu2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133143,1D/Burgers/Train/,fe6a312a9f4058e26dd69b999312ddd1 Burgers,1D_Burgers_Sols_Nu4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133144,1D/Burgers/Train/,f9f848275533aaebd0aa4683411629af -Burgers,Burgers_possin_u1.0_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133114,1D/Burgers/Test/,f00667db13f14cbb6a10e2ae9493b74f -Burgers,Burgers_possin_u1.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133115,1D/Burgers/Test/,e0cce7b0af6f3d11d3179cf3d6602ef5 -Burgers,Burgers_possin_u1.0_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133116,1D/Burgers/Test/,8f320060f3799e6a3c5522347dcaa75c -Burgers,Burgers_possin_u1.0_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133117,1D/Burgers/Test/,ae4a0330ce6be1a6d86965d97a6f5a5c -Burgers,Burgers_possin_u1.0_Nu10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133118,1D/Burgers/Test/,953d39c68a8a22e984f2eb960a218b19 -Burgers,Burgers_sinsin_u1.0_du0.1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133127,1D/Burgers/Test/,1944dc91e9e743fc154a23b6203b0e06 -Burgers,Burgers_sinsin_u1.0_du0.25_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133128,1D/Burgers/Test/,6b67166a52fef4fcd68573dbca722a22 -Burgers,Burgers_sinsin_u1.0_du0.5_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133129,1D/Burgers/Test/,7dc1aa478e5a16a51b0c1a5a323eb66b -Burgers,Burgers_sinsin_u1.0_du1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133130,1D/Burgers/Test/,ba129e02864a16fff2171c91528bd807 -Burgers,Burgers_sinsin_u1.0_du2_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133131,1D/Burgers/Test/,88151c84329d1f304d160e5012f97fa3 -Burgers,Burgers_sinsin_u1.0_du5.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133132,1D/Burgers/Test/,f8adc4e9367068d9812d45011d11ffc5 -Burgers,Burgers_sin_u0.01_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133119,1D/Burgers/Test/,cc9dfc22b7e90d9a771a4bace1114bdc -Burgers,Burgers_sin_u0.1_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133120,1D/Burgers/Test/,4487509cb38c1a3a57bb684081ffbb31 -Burgers,Burgers_sin_u1.0_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133121,1D/Burgers/Test/,8207ae134f2c2ecb13d03ead7601d6a5 -Burgers,Burgers_sin_u1.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133122,1D/Burgers/Test/,ba29e8728396e24fcd7aa4b8abf377b9 -Burgers,Burgers_sin_u1.0_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133123,1D/Burgers/Test/,caba5dff211bd9f5b88a5785ebb85ac7 -Burgers,Burgers_sin_u1.0_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133124,1D/Burgers/Test/,06a992d195c80405d4492b622c41f0ba -Burgers,Burgers_sin_u1.0_Nu10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133125,1D/Burgers/Test/,f6a2e39eb9f44061fd8bb236fca5abc7 -Burgers,Burgers_sin_u10.0_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133126,1D/Burgers/Test/,dd40cb0f808b68e7f1a75ec5a7917527 1D_CFD,1D_CFD_Rand_Eta0.01_Zeta0.01_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164672,1D/CFD/Train/,2fdd00138f1d7abc794fb953021a9f43 1D_CFD,1D_CFD_Rand_Eta0.1_Zeta0.1_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164668,1D/CFD/Train/,45655bd77d006ab539c52b7fbcf099b9 1D_CFD,1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135485,1D/CFD/Train/,ad22fe08b8abc721179dbb34f2cc8b2a From cacd7fd821ec35871d9bc2870ebaae95de0a6e5f Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 21 Aug 2023 12:28:04 +0200 Subject: [PATCH 090/137] fix minor typo in shape --- pdebench/data_gen/data_gen_NLE/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index db8ffbe..d8ff81f 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1140,7 +1140,7 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + phs = 2. * jnp.pi * random.uniform(key, shape=[5]) # (vi, k) uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] From b76c9f1d4d702156c0afe43560f1b688e2a7019f Mon Sep 17 00:00:00 2001 From: Raphael Leiteritz <48442682+leiterrl@users.noreply.github.com> Date: Thu, 24 Aug 2023 09:51:07 +0200 Subject: [PATCH 091/137] Add proper configuration for building (#47) * create pyproject.toml from setup and requirements * exclude python >3.10 * update readme * pin specific jax version * add python 3.9 * 3.10 variant to datagen extra * pin torch versions for datagen extra (for compatibility reasons with JAX) --- README.md | 36 ++++++++++++---------- pyproject.toml | 65 ++++++++++++++++++++++++++++++++++++++++ requirements.txt | 12 -------- requirements_datagen.txt | 29 ------------------ setup.py | 29 ------------------ 5 files changed, 86 insertions(+), 85 deletions(-) create mode 100644 pyproject.toml delete mode 100644 requirements.txt delete mode 100644 requirements_datagen.txt delete mode 100644 setup.py diff --git a/README.md b/README.md index 8a98f5e..4483cd3 100644 --- a/README.md +++ b/README.md @@ -29,21 +29,31 @@ DOIs ## Installation - pip install . - -## Requirements - ### Using pip + +Locally: ```bash -python3 -m venv ./venv --prompt pde_benchmark --system-site-packages -. ./venv/bin/activate pip install --upgrade pip wheel -pip install -r requirements.txt +pip install . ``` -The minimum required packages to train and run the baseline ML models are listed in [requirements.txt](./requirements.txt). +From PyPI: +```bash +pip install pdebench +``` + +To include dependencies for data generation: +```bash +pip install "pdebench[datagen310]" +pip install ".[datagen310]" # locally +``` +or +```bash +pip install "pdebench[datagen39]" +pip install ".[datagen39]" # locally +``` -To run the data generation scripts, the complete package requirements are listed in [requirements_datagen.txt](./requirements_datagen.txt) +### GPU Support For GPU support there are additional platform-specific instructions: @@ -51,7 +61,8 @@ For PyTorch, [see here](https://pytorch.org/get-started/locally/). For JAX, which is approximately 6 times faster for simulations than PyTorch in our tests, [see here](https://github.com/google/jax#installation) -### Using conda: + +## Installation using conda: If you like you can also install dependencies using anaconda. We suggest using [miniforge](https://github.com/conda-forge/miniforge) (and possibly mamba) as distribution. Otherwise you may have to __enable the conda-forge__ channel for the following commands. @@ -79,11 +90,6 @@ Optional dependencies for data generation: conda install clawpack jax jaxlib python-dotenv ``` -Optional dependencies for data downloading: -``` -pip install pyDarus~=1.0.5 -``` - ## Configuring DeepXDE In our tests we used PyTorch as backend for DeepXDE. Please [follow the documentation](https://deepxde.readthedocs.io/en/latest/user/installation.html#working-with-different-backends) to enable this. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3e1d3f1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,65 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +requires-python = ">=3.9,<3.11" +dynamic = ["readme"] +name = "pdebench" +version = "0.1.0" +description = "PDEBench: An Extensive Benchmark for Scientific Machine Learning" +authors = [ + {name = "Makoto Takamoto", email = "Makoto.Takamoto@neclab.eu"}, + {name = "Timothy Praditia", email = "timothy.praditia@iws.uni-stuttgart.de"}, + {name = "Raphael Leiteritz", email = "raphael.leiteritz@ipvs.uni-stuttgart.de"}, + {name = "Dan MacKinlay"}, + {name = "Francesco Alesiani"}, + {name = "Dirk Pflüger"}, + {name = "Mathias Niepert"}, +] +license = {file = "LICENSE.txt"} +dependencies = [ + "scipy", + "matplotlib", + "h5py", + "pandas", + "python-dotenv", + "hydra-core", + "torch~=1.13.0", + "torchvision~=0.14.1", + "deepxde~=1.1.3", + "pyro-ppl", + "tqdm", +] + +[project.urls] +Homepage = "https://github.com/pdebench/PDEBenchm" +Documentation = "https://github.com/pdebench/PDEBench" +Repository = "https://github.com/pdebench/PDEBench" + +[project.optional-dependencies] +datagen310 = [ + "clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93", + "dash", + "phiflow", + "imageio", + "einops", + "torch @ https://download.pytorch.org/whl/cu117/torch-1.13.1%2Bcu117-cp310-cp310-linux_x86_64.whl", + "torchvision @ https://download.pytorch.org/whl/cu117/torchvision-0.14.1%2Bcu117-cp310-cp310-linux_x86_64.whl", + "jax==0.4.11", + "jaxlib @ https://storage.googleapis.com/jax-releases/cuda11/jaxlib-0.4.11+cuda11.cudnn86-cp310-cp310-manylinux2014_x86_64.whl", +] +datagen39 = [ + "clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93", + "dash", + "phiflow", + "imageio", + "einops", + "torch @ https://download.pytorch.org/whl/cu117/torch-1.13.1%2Bcu117-cp39-cp39-linux_x86_64.whl", + "torchvision @ https://download.pytorch.org/whl/cu117/torchvision-0.14.1%2Bcu117-cp39-cp39-linux_x86_64.whl", + "jax==0.4.11", + "jaxlib @ https://storage.googleapis.com/jax-releases/cuda11/jaxlib-0.4.11+cuda11.cudnn86-cp39-cp39-manylinux2014_x86_64.whl" +] + +[tool.setuptools.dynamic] +readme = {file = ["README.md"], content-type = "text/markdown"} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 8270581..0000000 --- a/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -scipy~=1.8.0 # Scientific library -matplotlib~=3.5.2 # Plotting library -h5py~=3.6.0 # Data storage format -pandas~=1.4.2 # Dataframe library -python-dotenv~=0.20.0 # Configuration library -hydra-core~=1.2.0 # Configuration library -hydra-joblib-launcher~=1.2.0 # parallel jobs locally -hydra-submitit-launcher~=1.1.6 # parallel jobs on HPC cluster -omegaconf~=2.2.1 # Configuration library -deepxde~=1.1.3 # Physics-informed ML library -pyro-ppl # Probabilistic programming library for the inverse problems -tqdm~=4.64.0 \ No newline at end of file diff --git a/requirements_datagen.txt b/requirements_datagen.txt deleted file mode 100644 index 7514eab..0000000 --- a/requirements_datagen.txt +++ /dev/null @@ -1,29 +0,0 @@ -scipy~=1.8.0 # Scientific library -matplotlib~=3.5.2 # Plotting library -h5py~=3.6.0 # Data storage format -pandas~=1.4.2 # Dataframe library -python-dotenv~=0.20.0 # Configuration library -hydra-core~=1.2.0 # Configuration library -hydra-joblib-launcher~=1.2.0 # parallel jobs locally -hydra-submitit-launcher~=1.1.6 # parallel jobs on HPC cluster -omegaconf~=2.2.1 # Configuration library -deepxde~=1.1.3 # Physics-informed ML library -pyro-ppl # Probabilistic programming library for the inverse problems -tqdm~=4.64.0 - -# Optional, for downloading and uploading dataset using API -pyDaRUS~=1.0.5 # DaRUS API package to upload and download dataset -pyDataverse@git+https://github.com/JR-1991/pyDataverse.git@0fcfcd3fbc6bf1aec869899f715a51dca25e91be - -# For data generation -clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93 # includes fix for setuptools > 61.0.0 -dash~=2.2.0 # dashboard visualisation for phiflow -phiflow~=2.0.3 # PDE simulator -imageio~=2.19.2 -einops~=0.4.1 - -# Optional, if you want to use jupyter -jupyter~=1.0.0 -ipykernel~=6.9.0 -jupyter-dash~=0.4.1 -nbstripout~=0.5.0 # trim outputs from jupyter notebooks \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index b98e17f..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -from setuptools import setup - -with open("README.md", "r", encoding="utf-8") as fh: - long_description = fh.read() - -setup( - name='pdebench', - version='0.0.1', - install_requires=['scipy', - 'matplotlib', - 'h5py', - 'pandas', - 'python-dotenv', - 'hydra-core', - 'omegaconf', - 'deepxde' - ], - packages=['pdebench'], - author='Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert', - author_email='Makoto.Takamoto@neclab.eu, timothy.praditia@iws.uni-stuttgart.de', - url='https://github.com/pdebench/PDEBench', - zip_safe=False, - description='PDEBench: An Extensive Benchmark for Scientific Machine Learning', - long_description=long_description, - long_description_content_type="text/markdown", - classifiers=[ - "License :: see licence file", - ], -) \ No newline at end of file From f9fd131b57d715e7e4dc3716107a0b9bd85f6870 Mon Sep 17 00:00:00 2001 From: Marimuthu Kalimuthu Date: Thu, 24 Aug 2023 10:15:05 +0200 Subject: [PATCH 092/137] minor revamp of readme --- README.md | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4483cd3..ed5f962 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # PDEBench The code repository for the NeurIPS 2022 paper -[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) :tada: **SimTech Best Paper Award 2023** :confetti_ball: +[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) + +:tada: [**SimTech Best Paper Award 2023**](https://www.simtech.uni-stuttgart.de/press/SimTech-Best-Paper-Award-2023-Benchmark-for-ML-for-scientific-simulations) :confetti_ball: PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. This repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baselines. PDEBench features a much wider range of PDEs than existing benchmarks and includes realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was created to make the source code extensible and we invite active participation from the SciML community to improve and extend the benchmark. @@ -57,14 +59,14 @@ pip install ".[datagen39]" # locally For GPU support there are additional platform-specific instructions: -For PyTorch, [see here](https://pytorch.org/get-started/locally/). +For PyTorch, the latest version we support is v1.13.1 [see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). -For JAX, which is approximately 6 times faster for simulations than PyTorch in our tests, [see here](https://github.com/google/jax#installation) +For JAX, which is approximately 6 times faster for simulations than PyTorch in our tests, [see jax#pip-installation-gpu-cuda-installed-via-pip](https://github.com/google/jax#pip-installation-gpu-cuda-installed-via-pip-easier) ## Installation using conda: -If you like you can also install dependencies using anaconda. We suggest using [miniforge](https://github.com/conda-forge/miniforge) (and possibly mamba) as distribution. Otherwise you may have to __enable the conda-forge__ channel for the following commands. +If you like you can also install dependencies using anaconda, we suggest to use [mambaforge](https://github.com/conda-forge/miniforge#mambaforge) as a distribution. Otherwise you may have to __enable the conda-forge__ channel for the following commands. Starting from a fresh environment: @@ -75,16 +77,24 @@ conda activate myenv Install dependencies for model training: ``` -conda install deepxde hydra-core h5py +conda install deepxde hydra-core h5py -c conda-forge ``` -[Install PyTorch](https://pytorch.org/get-started/locally/) according to your hardware requirements: +According to your hardware availability, either install PyTorch with CUDA support: + + - [see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). -E.g. ``` -conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch +conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.7 -c pytorch -c nvidia ``` + - [or CPU only binaries](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). + +``` +conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 cpuonly -c pytorch +``` + + Optional dependencies for data generation: ``` conda install clawpack jax jaxlib python-dotenv From 524848f3b6ebbf4eb82a90d05112d4c7a1ffe3d1 Mon Sep 17 00:00:00 2001 From: Marimuthu Kalimuthu Date: Thu, 24 Aug 2023 10:24:31 +0200 Subject: [PATCH 093/137] fix scaling issue for 1D advection (#46) --- .../data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py index 5d3ee79..db45e96 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -261,7 +261,7 @@ def flux(u): fL = uL * beta fR = uR * beta # upwind advection scheme - f_upwd = (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] + f_upwd = 0.5 * (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] - jnp.abs(beta)*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) return f_upwd From 1bd5efc2c243cc3fa9775bc6f86c684bbf32e498 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 27 Sep 2023 13:01:25 -0700 Subject: [PATCH 094/137] Add missing base_path to unet (#49) --- pdebench/models/train_models_forward.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 7a8ed2b..f9b4c31 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -215,6 +215,7 @@ def main(cfg: DictConfig): model_update=cfg.args.model_update, flnm=cfg.args.filename, single_file=cfg.args.single_file, + base_path=cfg.args.data_path, reduced_resolution=cfg.args.reduced_resolution, reduced_resolution_t=cfg.args.reduced_resolution_t, reduced_batch=cfg.args.reduced_batch, From 4361430bd3704914ff6ed4fd737aaeb272354f19 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 10 Oct 2023 17:04:23 +0200 Subject: [PATCH 095/137] update 1d adv pde dataset urls --- pdebench/data_download/download_metadata.csv | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv index c820240..72f849d 100644 --- a/pdebench/data_download/download_metadata.csv +++ b/pdebench/data_download/download_metadata.csv @@ -1,10 +1,12 @@ PDE,Filename,URL,Path,MD5 -Advection,1D_Advection_Sols_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133100,1D/Advection/Train/,183cd9b7a6c3c5caf0fd89c87f2c4a80 -Advection,1D_Advection_Sols_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133109,1D/Advection/Train/,e6b6997e00bc0310308ce14df12fecd4 -Advection,1D_Advection_Sols_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133110,1D/Advection/Train/,99d57a5a8d660ba2cb824f41e09c6543 -Advection,1D_Advection_Sols_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133111,1D/Advection/Train/,91a599ed8c11447dec8200b0915571a3 -Advection,1D_Advection_Sols_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133112,1D/Advection/Train/,24bb9795a558bcc3bc9103b1cb791965 -Advection,1D_Advection_Sols_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133113,1D/Advection/Train/,73ec4f7980462a13629234ec2f849e78 +Advection,1D_Advection_Sols_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255672,1D/Advection/Train/,b4be2fc3383f737c76033073e6d2ccfb +Advection,1D_Advection_Sols_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255671,1D/Advection/Train/,d47b52c1be4b6bcaa8748a8df2f5fbb8 +Advection,1D_Advection_Sols_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255674,1D/Advection/Train/,d595bbfd2c659df995a93cd40d6ea568 +Advection,1D_Advection_Sols_beta0.7.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255666,1D/Advection/Train/,f89030c240a1e90b55649776854a84c2 +Advection,1D_Advection_Sols_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255675,1D/Advection/Train/,1fe41923a4123db55bf4e89bea32e142 +Advection,1D_Advection_Sols_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255677,1D/Advection/Train/,0e9beff98693089c38e213a1f2b9ac43 +Advection,1D_Advection_Sols_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255676,1D/Advection/Train/,a87b03ff425496360e2732921805f47d +Advection,1D_Advection_Sols_beta7.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255664,1D/Advection/Train/,72a286ad2fcba574bd20bc045ba82d58 Burgers,1D_Burgers_Sols_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133133,1D/Burgers/Train/,5f7242c820efb37bae3d82e983c04e43 Burgers,1D_Burgers_Sols_Nu0.002.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133134,1D/Burgers/Train/,eb7cc49d7d4d346d7abe9af9eee3f86b Burgers,1D_Burgers_Sols_Nu0.004.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133135,1D/Burgers/Train/,e1c40dd4c0d8b858231408009e8a8def From 5f199875d7581e8a4fc2ccc768ade71798237d8f Mon Sep 17 00:00:00 2001 From: nle18370 Date: Tue, 31 Oct 2023 16:07:59 +0100 Subject: [PATCH 096/137] debugged BurgersEq with changing xlim:(-1,1)-->(0,1) by MT@31102023 --- pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py | 2 +- .../data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml | 2 +- pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py index 50f438c..4adc063 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py @@ -262,7 +262,7 @@ def flux(u): fL = 0.5*uL**2 fR = 0.5*uR**2 # upwind advection scheme - f_upwd = (fR[1:cfg.args.nx+2] + fL[2:cfg.args.nx+3] + f_upwd = 0.5 * (fR[1:cfg.args.nx+2] + fL[2:cfg.args.nx+3] - 0.5*jnp.abs(uL[2:cfg.args.nx+3] + uR[1:cfg.args.nx+2])*(uL[2:cfg.args.nx+3] - uR[1:cfg.args.nx+2])) # source term f_upwd += - cfg.args.epsilon*pi_inv*(_u[2:cfg.args.nx+3] - _u[1:cfg.args.nx+2])*dx_inv diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py index 79ce9e0..cf84034 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -262,7 +262,7 @@ def flux(u): fL = 0.5*uL**2 fR = 0.5*uR**2 # upwind advection scheme - f_upwd = (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] + f_upwd = 0.5 * (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] - 0.5*jnp.abs(uL[2:cfg.multi.nx+3] + uR[1:cfg.multi.nx+2])*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) # source term f_upwd += - epsilon*pi_inv*(_u[2:cfg.multi.nx+3] - _u[1:cfg.multi.nx+2])*dx_inv diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml index 7df90c5..db263b5 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 1.e-1 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml index 5055858..315f7f7 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 1.e-2 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml index 4aa10f9..9a981a8 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 1.e-3 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml index 766644c..7b17380 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 1.e0 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml index 548654d..3f4d4d0 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 2.e-1 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml index 0ecdb41..13c9935 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 2.e-2 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml index 770413e..52ad377 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 2.e-3 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml index 89dd822..fc30c15 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 2.e0 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml index 0ca7ce9..eaf7974 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 4.e-1 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml index 5d4db50..07ba0c2 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 4.e-2 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml index 2ff3334..6ff09f2 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 4.e-3 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml index aac4585..0e8a8d2 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 -xL: -1. +xL: 0. xR: 1. epsilon: 4.e0 CFL: 2.5e-1 From bde54cb27830ff853167650e714b1df9ab4187b1 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:08:20 +0100 Subject: [PATCH 097/137] add support for SWE 2D PDE --- pdebench/models/config/config_rdb.yaml | 61 +++++ pdebench/models/fno/utils.py | 335 +++++++++++++----------- pdebench/models/train_models_forward.py | 2 +- 3 files changed, 240 insertions(+), 158 deletions(-) create mode 100644 pdebench/models/config/config_rdb.yaml diff --git a/pdebench/models/config/config_rdb.yaml b/pdebench/models/config/config_rdb.yaml new file mode 100644 index 0000000..0a49a74 --- /dev/null +++ b/pdebench/models/config/config_rdb.yaml @@ -0,0 +1,61 @@ +defaults: + - _self_ + - override hydra/hydra_logging: disabled + - override hydra/job_logging: disabled + +hydra: + output_subdir: null + run: + dir: . + +args: + model_name: 'FNO' + if_training: True + continue_training: False + num_workers: 2 + batch_size: 5 + initial_step: 10 + t_train: 101 + model_update: 2 + filename: '2D_rdb_NA_NA.h5' + single_file: True + data_path: '/data/scratch/ac141923/pdebench_data/2D/shallow-water/' + reduced_resolution: 1 + reduced_resolution_t: 2 + reduced_batch: 1 + epochs: 500 + learning_rate: 1.e-3 + scheduler_step: 100 + scheduler_gamma: 0.5 + #Unet + in_channels: 1 + out_channels: 1 + ar_mode: True + pushforward: True + unroll_step: 20 + #FNO + num_channels: 1 + modes: 12 + width: 20 + #Inverse + training_type: 'autoregressive' + #Inverse MCMC + mcmc_num_samples: 20 + mcmc_warmup_steps: 10 + mcmc_num_chains: 1 + num_samples_max: 1000 + in_channels_hid: 64 + inverse_model_type: InitialConditionInterp + #Inverse grad + inverse_epochs: 100 + inverse_learning_rate: 0.2 + inverse_verbose_flag: False + #Plotting + plot: False + channel_plot: 0 # Which channel/variable to be plotted + x_min: -2.5 + x_max: 2.5 + y_min: -2.5 + y_max: 2.5 + t_min: 0 + t_max: 1 diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 3e24d4a..221eef3 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -181,164 +181,185 @@ def __init__(self, filename, # Define path to files root_path = os.path.abspath(saved_folder + filename) - assert filename[-2:] != 'h5', 'HDF5 data is assumed!!' + if filename[-2:] != 'h5': + print(f".HDF5 file extension is assumed hereafter") - with h5py.File(root_path, 'r') as f: - keys = list(f.keys()) - keys.sort() - if 'tensor' not in keys: - _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... - idx_cfd = _data.shape - if len(idx_cfd)==3: # 1D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 3], - dtype=np.float32) - #density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,2] = _data # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - print(self.data.shape) - if len(idx_cfd)==4: # 2D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 4], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,3] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - - if len(idx_cfd)==5: # 3D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - idx_cfd[4]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 5], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,3] = _data # batch, x, t, ch - # Vz - _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,4] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - z = np.array(f["z-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z) - self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ - ::reduced_resolution,\ - ::reduced_resolution] - - else: # scalar equations - ## data dim = [t, x1, ..., xd, v] - _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... - if len(_data.shape) == 3: # 1D - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data = _data[:, :, :, None] # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - if len(_data.shape) == 4: # 2D Darcy flow - # u: label - _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - #if _data.shape[-1]==1: # if nt==1 - # _data = np.tile(_data, (1, 1, 1, 2)) - self.data = _data - # nu: input - _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - self.data = np.concatenate([_data, self.data], axis=-1) - self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + with h5py.File(root_path, 'r') as f: + keys = list(f.keys()) + keys.sort() + if 'tensor' not in keys: + _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... + idx_cfd = _data.shape + if len(idx_cfd)==3: # 1D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 3], + dtype=np.float32) + #density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,2] = _data # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + print(self.data.shape) + if len(idx_cfd)==4: # 2D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 4], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,3] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + if len(idx_cfd)==5: # 3D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + idx_cfd[4]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 5], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,3] = _data # batch, x, t, ch + # Vz + _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,4] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + z = np.array(f["z-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + z = torch.tensor(z, dtype=torch.float) + X, Y, Z = torch.meshgrid(x, y, z) + self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ + ::reduced_resolution,\ + ::reduced_resolution] + + else: # scalar equations + ## data dim = [t, x1, ..., xd, v] + _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... + if len(_data.shape) == 3: # 1D + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data = _data[:, :, :, None] # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + if len(_data.shape) == 4: # 2D Darcy flow + # u: label + _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + #if _data.shape[-1]==1: # if nt==1 + # _data = np.tile(_data, (1, 1, 1, 2)) + self.data = _data + # nu: input + _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + self.data = np.concatenate([_data, self.data], axis=-1) + self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + elif filename[-2:] == 'h5': + print(f".H5 file extension is assumed hereafter") + + with h5py.File(root_path, 'r') as f: + keys = list(f.keys()) + keys.sort() + data_arrays = [np.array(f[key]['data'], dtype=np.float32) for key in keys] + _data = torch.from_numpy(np.stack(data_arrays, axis=0)) # [batch, nt, nx, ny, nc] + _data = _data[::reduced_batch, ::reduced_resolution_t, ::reduced_resolution, ::reduced_resolution, ...] + _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] + gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) + mgridX, mgridY = np.meshgrid(gridx, gridy) + _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) + grid = _grid[::reduced_resolution, ::reduced_resolution, ...] + _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) + tsteps_t = _tsteps_t[::reduced_resolution_t] + self.data = _data + self.grid = _grid + self.tsteps_t = tsteps_t if num_samples_max>0: - num_samples_max = min(num_samples_max,self.data.shape[0]) + num_samples_max = min(num_samples_max, self.data.shape[0]) else: num_samples_max = self.data.shape[0] @@ -351,7 +372,7 @@ def __init__(self, filename, # Time steps used as initial conditions self.initial_step = initial_step - self.data = torch.tensor(self.data) + self.data = self.data if torch.is_tensor(self.data) else torch.tensor(self.data) def __len__(self): return len(self.data) @@ -440,4 +461,4 @@ def __getitem__(self, idx): X, Y, Z = torch.meshgrid(x, y, z) grid = torch.stack((X,Y,Z),axis=-1) - return data[...,:self.initial_step,:], data, grid \ No newline at end of file + return data[...,:self.initial_step,:], data, grid diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index f9b4c31..5ebd40a 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -157,7 +157,7 @@ -@hydra.main(version_base="1.2", config_path="config", config_name="config_darcy") +@hydra.main(version_base="1.2", config_path="config", config_name="config_rdb") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": from pdebench.models.fno.train import run_training as run_training_FNO From 107bc02a1ba2768a058ff12663888ed037d74ab4 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:12:03 +0100 Subject: [PATCH 098/137] Revert "add support for SWE 2D PDE" This reverts commit bde54cb27830ff853167650e714b1df9ab4187b1. --- pdebench/models/config/config_rdb.yaml | 61 ----- pdebench/models/fno/utils.py | 335 +++++++++++------------- pdebench/models/train_models_forward.py | 2 +- 3 files changed, 158 insertions(+), 240 deletions(-) delete mode 100644 pdebench/models/config/config_rdb.yaml diff --git a/pdebench/models/config/config_rdb.yaml b/pdebench/models/config/config_rdb.yaml deleted file mode 100644 index 0a49a74..0000000 --- a/pdebench/models/config/config_rdb.yaml +++ /dev/null @@ -1,61 +0,0 @@ -defaults: - - _self_ - - override hydra/hydra_logging: disabled - - override hydra/job_logging: disabled - -hydra: - output_subdir: null - run: - dir: . - -args: - model_name: 'FNO' - if_training: True - continue_training: False - num_workers: 2 - batch_size: 5 - initial_step: 10 - t_train: 101 - model_update: 2 - filename: '2D_rdb_NA_NA.h5' - single_file: True - data_path: '/data/scratch/ac141923/pdebench_data/2D/shallow-water/' - reduced_resolution: 1 - reduced_resolution_t: 2 - reduced_batch: 1 - epochs: 500 - learning_rate: 1.e-3 - scheduler_step: 100 - scheduler_gamma: 0.5 - #Unet - in_channels: 1 - out_channels: 1 - ar_mode: True - pushforward: True - unroll_step: 20 - #FNO - num_channels: 1 - modes: 12 - width: 20 - #Inverse - training_type: 'autoregressive' - #Inverse MCMC - mcmc_num_samples: 20 - mcmc_warmup_steps: 10 - mcmc_num_chains: 1 - num_samples_max: 1000 - in_channels_hid: 64 - inverse_model_type: InitialConditionInterp - #Inverse grad - inverse_epochs: 100 - inverse_learning_rate: 0.2 - inverse_verbose_flag: False - #Plotting - plot: False - channel_plot: 0 # Which channel/variable to be plotted - x_min: -2.5 - x_max: 2.5 - y_min: -2.5 - y_max: 2.5 - t_min: 0 - t_max: 1 diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 221eef3..3e24d4a 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -181,185 +181,164 @@ def __init__(self, filename, # Define path to files root_path = os.path.abspath(saved_folder + filename) - if filename[-2:] != 'h5': - print(f".HDF5 file extension is assumed hereafter") + assert filename[-2:] != 'h5', 'HDF5 data is assumed!!' - with h5py.File(root_path, 'r') as f: - keys = list(f.keys()) - keys.sort() - if 'tensor' not in keys: - _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... - idx_cfd = _data.shape - if len(idx_cfd)==3: # 1D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 3], - dtype=np.float32) - #density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,2] = _data # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - print(self.data.shape) - if len(idx_cfd)==4: # 2D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 4], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,3] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - - if len(idx_cfd)==5: # 3D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - idx_cfd[4]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 5], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,3] = _data # batch, x, t, ch - # Vz - _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,4] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - z = np.array(f["z-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z) - self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ - ::reduced_resolution,\ - ::reduced_resolution] - - else: # scalar equations - ## data dim = [t, x1, ..., xd, v] - _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... - if len(_data.shape) == 3: # 1D - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data = _data[:, :, :, None] # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - if len(_data.shape) == 4: # 2D Darcy flow - # u: label - _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - #if _data.shape[-1]==1: # if nt==1 - # _data = np.tile(_data, (1, 1, 1, 2)) - self.data = _data - # nu: input - _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - self.data = np.concatenate([_data, self.data], axis=-1) - self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - - elif filename[-2:] == 'h5': - print(f".H5 file extension is assumed hereafter") - - with h5py.File(root_path, 'r') as f: - keys = list(f.keys()) - keys.sort() - data_arrays = [np.array(f[key]['data'], dtype=np.float32) for key in keys] - _data = torch.from_numpy(np.stack(data_arrays, axis=0)) # [batch, nt, nx, ny, nc] - _data = _data[::reduced_batch, ::reduced_resolution_t, ::reduced_resolution, ::reduced_resolution, ...] - _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] - gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) - mgridX, mgridY = np.meshgrid(gridx, gridy) - _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) - grid = _grid[::reduced_resolution, ::reduced_resolution, ...] - _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) - tsteps_t = _tsteps_t[::reduced_resolution_t] - self.data = _data - self.grid = _grid - self.tsteps_t = tsteps_t + with h5py.File(root_path, 'r') as f: + keys = list(f.keys()) + keys.sort() + if 'tensor' not in keys: + _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... + idx_cfd = _data.shape + if len(idx_cfd)==3: # 1D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 3], + dtype=np.float32) + #density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,2] = _data # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + print(self.data.shape) + if len(idx_cfd)==4: # 2D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 4], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,3] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + if len(idx_cfd)==5: # 3D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + idx_cfd[4]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 5], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,3] = _data # batch, x, t, ch + # Vz + _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,4] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + z = np.array(f["z-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + z = torch.tensor(z, dtype=torch.float) + X, Y, Z = torch.meshgrid(x, y, z) + self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ + ::reduced_resolution,\ + ::reduced_resolution] + + else: # scalar equations + ## data dim = [t, x1, ..., xd, v] + _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... + if len(_data.shape) == 3: # 1D + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data = _data[:, :, :, None] # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + if len(_data.shape) == 4: # 2D Darcy flow + # u: label + _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + #if _data.shape[-1]==1: # if nt==1 + # _data = np.tile(_data, (1, 1, 1, 2)) + self.data = _data + # nu: input + _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + self.data = np.concatenate([_data, self.data], axis=-1) + self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] if num_samples_max>0: - num_samples_max = min(num_samples_max, self.data.shape[0]) + num_samples_max = min(num_samples_max,self.data.shape[0]) else: num_samples_max = self.data.shape[0] @@ -372,7 +351,7 @@ def __init__(self, filename, # Time steps used as initial conditions self.initial_step = initial_step - self.data = self.data if torch.is_tensor(self.data) else torch.tensor(self.data) + self.data = torch.tensor(self.data) def __len__(self): return len(self.data) @@ -461,4 +440,4 @@ def __getitem__(self, idx): X, Y, Z = torch.meshgrid(x, y, z) grid = torch.stack((X,Y,Z),axis=-1) - return data[...,:self.initial_step,:], data, grid + return data[...,:self.initial_step,:], data, grid \ No newline at end of file diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 5ebd40a..f9b4c31 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -157,7 +157,7 @@ -@hydra.main(version_base="1.2", config_path="config", config_name="config_rdb") +@hydra.main(version_base="1.2", config_path="config", config_name="config_darcy") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": from pdebench.models.fno.train import run_training as run_training_FNO From 8ab2e0c96866105767267f3a877e88a3a82de388 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:23:23 +0100 Subject: [PATCH 099/137] update SWE-2D config --- pdebench/models/config/config_rdb.yaml | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 pdebench/models/config/config_rdb.yaml diff --git a/pdebench/models/config/config_rdb.yaml b/pdebench/models/config/config_rdb.yaml new file mode 100644 index 0000000..ffa9ea0 --- /dev/null +++ b/pdebench/models/config/config_rdb.yaml @@ -0,0 +1,61 @@ +defaults: + - _self_ + - override hydra/hydra_logging: disabled + - override hydra/job_logging: disabled + +hydra: + output_subdir: null + run: + dir: . + +args: + model_name: 'FNO' + if_training: False + continue_training: False + num_workers: 2 + batch_size: 5 + initial_step: 10 + t_train: 101 + model_update: 2 + filename: '2D_rdb_NA_NA.h5' + single_file: True + data_path: '/path/to/swe2d/h5' + reduced_resolution: 1 + reduced_resolution_t: 1 + reduced_batch: 1 + epochs: 500 + learning_rate: 1.e-3 + scheduler_step: 100 + scheduler_gamma: 0.5 + #Unet + in_channels: 1 + out_channels: 1 + ar_mode: True + pushforward: True + unroll_step: 20 + #FNO + num_channels: 1 + modes: 12 + width: 20 + #Inverse + training_type: 'autoregressive' + #Inverse MCMC + mcmc_num_samples: 20 + mcmc_warmup_steps: 10 + mcmc_num_chains: 1 + num_samples_max: 1000 + in_channels_hid: 64 + inverse_model_type: InitialConditionInterp + #Inverse grad + inverse_epochs: 100 + inverse_learning_rate: 0.2 + inverse_verbose_flag: False + #Plotting + plot: False + channel_plot: 0 # Which channel/variable to be plotted + x_min: -2.5 + x_max: 2.5 + y_min: -2.5 + y_max: 2.5 + t_min: 0 + t_max: 1 From 8ddd2d0b8cea1581b9288acf17a8df4412320563 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:24:00 +0100 Subject: [PATCH 100/137] add dataloading support for SWE-2D --- pdebench/models/fno/utils.py | 337 +++++++++++++++++++---------------- 1 file changed, 179 insertions(+), 158 deletions(-) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 3e24d4a..68d4a01 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -180,165 +180,186 @@ def __init__(self, filename, """ # Define path to files - root_path = os.path.abspath(saved_folder + filename) - assert filename[-2:] != 'h5', 'HDF5 data is assumed!!' + root_path = os.path.join(os.path.abspath(saved_folder), filename) + if filename[-2:] != 'h5': + print(f".HDF5 file extension is assumed hereafter") - with h5py.File(root_path, 'r') as f: - keys = list(f.keys()) - keys.sort() - if 'tensor' not in keys: - _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... - idx_cfd = _data.shape - if len(idx_cfd)==3: # 1D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 3], - dtype=np.float32) - #density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,2] = _data # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - print(self.data.shape) - if len(idx_cfd)==4: # 2D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 4], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,3] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - - if len(idx_cfd)==5: # 3D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - idx_cfd[4]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 5], - dtype=np.float32) - # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,0] = _data # batch, x, t, ch - # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,1] = _data # batch, x, t, ch - # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,2] = _data # batch, x, t, ch - # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,3] = _data # batch, x, t, ch - # Vz - _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,4] = _data # batch, x, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - z = np.array(f["z-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z) - self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ - ::reduced_resolution,\ - ::reduced_resolution] - - else: # scalar equations - ## data dim = [t, x1, ..., xd, v] - _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... - if len(_data.shape) == 3: # 1D - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data = _data[:, :, :, None] # batch, x, t, ch - - self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) - if len(_data.shape) == 4: # 2D Darcy flow - # u: label - _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - #if _data.shape[-1]==1: # if nt==1 - # _data = np.tile(_data, (1, 1, 1, 2)) - self.data = _data - # nu: input - _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] - ## convert to [x1, ..., xd, t, v] - _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - self.data = np.concatenate([_data, self.data], axis=-1) - self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch - - x = np.array(f["x-coordinate"], dtype=np.float32) - y = np.array(f["y-coordinate"], dtype=np.float32) - x = torch.tensor(x, dtype=torch.float) - y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + with h5py.File(root_path, 'r') as f: + keys = list(f.keys()) + keys.sort() + if 'tensor' not in keys: + _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... + idx_cfd = _data.shape + if len(idx_cfd)==3: # 1D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 3], + dtype=np.float32) + #density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data[...,2] = _data # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + print(self.data.shape) + if len(idx_cfd)==4: # 2D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 4], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 1)) + self.data[...,3] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + if len(idx_cfd)==5: # 3D + self.data = np.zeros([idx_cfd[0]//reduced_batch, + idx_cfd[2]//reduced_resolution, + idx_cfd[3]//reduced_resolution, + idx_cfd[4]//reduced_resolution, + mt.ceil(idx_cfd[1]/reduced_resolution_t), + 5], + dtype=np.float32) + # density + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,0] = _data # batch, x, t, ch + # pressure + _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,1] = _data # batch, x, t, ch + # Vx + _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,2] = _data # batch, x, t, ch + # Vy + _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,3] = _data # batch, x, t, ch + # Vz + _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data, (0, 2, 3, 4, 1)) + self.data[...,4] = _data # batch, x, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + z = np.array(f["z-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + z = torch.tensor(z, dtype=torch.float) + X, Y, Z = torch.meshgrid(x, y, z) + self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ + ::reduced_resolution,\ + ::reduced_resolution] + + else: # scalar equations + ## data dim = [t, x1, ..., xd, v] + _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... + if len(_data.shape) == 3: # 1D + _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :], (0, 2, 1)) + self.data = _data[:, :, :, None] # batch, x, t, ch + + self.grid = np.array(f["x-coordinate"], dtype=np.float32) + self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + if len(_data.shape) == 4: # 2D Darcy flow + # u: label + _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + #if _data.shape[-1]==1: # if nt==1 + # _data = np.tile(_data, (1, 1, 1, 2)) + self.data = _data + # nu: input + _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... + _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] + ## convert to [x1, ..., xd, t, v] + _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) + self.data = np.concatenate([_data, self.data], axis=-1) + self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch + + x = np.array(f["x-coordinate"], dtype=np.float32) + y = np.array(f["y-coordinate"], dtype=np.float32) + x = torch.tensor(x, dtype=torch.float) + y = torch.tensor(y, dtype=torch.float) + X, Y = torch.meshgrid(x, y) + self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + + elif filename[-2:] == 'h5': + print(f".H5 file extension is assumed hereafter") + + with h5py.File(root_path, 'r') as f: + keys = list(f.keys()) + keys.sort() + data_arrays = [np.array(f[key]['data'], dtype=np.float32) for key in keys] + _data = torch.from_numpy(np.stack(data_arrays, axis=0)) # [batch, nt, nx, ny, nc] + _data = _data[::reduced_batch, ::reduced_resolution_t, ::reduced_resolution, ::reduced_resolution, ...] + _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] + gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) + mgridX, mgridY = np.meshgrid(gridx, gridy) + _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) + grid = _grid[::reduced_resolution, ::reduced_resolution, ...] + _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) + tsteps_t = _tsteps_t[::reduced_resolution_t] + self.data = _data + self.grid = _grid + self.tsteps_t = tsteps_t if num_samples_max>0: - num_samples_max = min(num_samples_max,self.data.shape[0]) + num_samples_max = min(num_samples_max, self.data.shape[0]) else: num_samples_max = self.data.shape[0] @@ -351,7 +372,7 @@ def __init__(self, filename, # Time steps used as initial conditions self.initial_step = initial_step - self.data = torch.tensor(self.data) + self.data = self.data if torch.is_tensor(self.data) else torch.tensor(self.data) def __len__(self): return len(self.data) @@ -440,4 +461,4 @@ def __getitem__(self, idx): X, Y, Z = torch.meshgrid(x, y, z) grid = torch.stack((X,Y,Z),axis=-1) - return data[...,:self.initial_step,:], data, grid \ No newline at end of file + return data[...,:self.initial_step,:], data, grid From 931bea2b7f752fde28f7055286d49027d14a2c17 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:24:36 +0100 Subject: [PATCH 101/137] test run for SWE-2D --- pdebench/models/train_models_forward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index f9b4c31..5ebd40a 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -157,7 +157,7 @@ -@hydra.main(version_base="1.2", config_path="config", config_name="config_darcy") +@hydra.main(version_base="1.2", config_path="config", config_name="config_rdb") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": from pdebench.models.fno.train import run_training as run_training_FNO From feba8fb359cc067043dbfe18f00f9b565894b190 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 7 Nov 2023 13:30:16 +0100 Subject: [PATCH 102/137] addresses #52 --- pdebench/models/fno/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 68d4a01..cc666a7 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -338,7 +338,7 @@ def __init__(self, filename, X, Y = torch.meshgrid(x, y) self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - elif filename[-2:] == 'h5': + elif filename[-2:] == 'h5': # SWE-2D (RDB) print(f".H5 file extension is assumed hereafter") with h5py.File(root_path, 'r') as f: From 320641b6d035f48e0ec1cb585225b1aed8d93acf Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 13 Nov 2023 13:01:19 +0100 Subject: [PATCH 103/137] add explicit indexing scheme for future torch upgrades (https://github.com/pytorch/pytorch/issues/50276) --- pdebench/models/fno/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index cc666a7..6ad16af 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -252,7 +252,7 @@ def __init__(self, filename, y = np.array(f["y-coordinate"], dtype=np.float32) x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) + X, Y = torch.meshgrid(x, y, indexing='ij') self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] if len(idx_cfd)==5: # 3D @@ -299,7 +299,7 @@ def __init__(self, filename, x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z) + X, Y, Z = torch.meshgrid(x, y, z, indexing='ij') self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ ::reduced_resolution,\ ::reduced_resolution] @@ -335,7 +335,7 @@ def __init__(self, filename, y = np.array(f["y-coordinate"], dtype=np.float32) x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) + X, Y = torch.meshgrid(x, y, indexing='ij') self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] elif filename[-2:] == 'h5': # SWE-2D (RDB) @@ -349,7 +349,7 @@ def __init__(self, filename, _data = _data[::reduced_batch, ::reduced_resolution_t, ::reduced_resolution, ::reduced_resolution, ...] _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) - mgridX, mgridY = np.meshgrid(gridx, gridy) + mgridX, mgridY = np.meshgrid(gridx, gridy, indexing='ij') _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) grid = _grid[::reduced_resolution, ::reduced_resolution, ...] _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) @@ -449,7 +449,7 @@ def __getitem__(self, idx): y = np.array(seed_group["grid"]["y"], dtype='f') x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y) + X, Y = torch.meshgrid(x, y, indexing='ij') grid = torch.stack((X,Y),axis=-1) elif dim == 3: x = np.array(seed_group["grid"]["x"], dtype='f') @@ -458,7 +458,7 @@ def __getitem__(self, idx): x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z) + X, Y, Z = torch.meshgrid(x, y, z, indexing='ij') grid = torch.stack((X,Y,Z),axis=-1) return data[...,:self.initial_step,:], data, grid From bf6a33b17cabe78a0c44df93d826b4cdee11740b Mon Sep 17 00:00:00 2001 From: Marimuthu Kalimuthu Date: Mon, 12 Feb 2024 13:45:04 +0100 Subject: [PATCH 104/137] update DARUS dataset urls of missing & new files --- pdebench/data_download/download_metadata.csv | Bin 50513 -> 25093 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv index 72f849de4e7577381cd60374d3527fb66f7b404d..404c0938c1cd9cd1a690a03089f023c1b88434f4 100644 GIT binary patch literal 25093 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 J1XTdw{{YJ~esll; literal 50513 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 From dcc94b86c46bb2f1a7881c104c17fc423464df6c Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 2 Apr 2024 02:44:44 +0200 Subject: [PATCH 105/137] fix data download urls csv --- pdebench/data_download/download_direct.py | 2 +- pdebench/data_download/download_metadata.csv | Bin 25093 -> 0 bytes pdebench/data_download/pdebench_data_urls.csv | 376 ++++++++++++++++++ 3 files changed, 377 insertions(+), 1 deletion(-) delete mode 100644 pdebench/data_download/download_metadata.csv create mode 100644 pdebench/data_download/pdebench_data_urls.csv diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 404437b..35ee0e0 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -31,7 +31,7 @@ def parse_metadata(pde_names): pde_df : Filtered dataframe containing metadata of files to be downloaded """ - meta_df = pd.read_csv("download_metadata.csv") + meta_df = pd.read_csv("pdebench_data_urls.csv") # Ensure the pde_name is defined pde_list = [ diff --git a/pdebench/data_download/download_metadata.csv b/pdebench/data_download/download_metadata.csv deleted file mode 100644 index 404c0938c1cd9cd1a690a03089f023c1b88434f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25093 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 J1XTdw{{YJ~esll; diff --git a/pdebench/data_download/pdebench_data_urls.csv b/pdebench/data_download/pdebench_data_urls.csv new file mode 100644 index 0000000..ba845ea --- /dev/null +++ b/pdebench/data_download/pdebench_data_urls.csv @@ -0,0 +1,376 @@ +PDE,Filename,URL,Path,MD5 +Advection,1D_Advection_Sols_beta0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255672,1D/Advection/Train/,b4be2fc3383f737c76033073e6d2ccfb +Advection,1D_Advection_Sols_beta0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255671,1D/Advection/Train/,d47b52c1be4b6bcaa8748a8df2f5fbb8 +Advection,1D_Advection_Sols_beta0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255674,1D/Advection/Train/,d595bbfd2c659df995a93cd40d6ea568 +Advection,1D_Advection_Sols_beta0.7.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255666,1D/Advection/Train/,f89030c240a1e90b55649776854a84c2 +Advection,1D_Advection_Sols_beta1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255675,1D/Advection/Train/,1fe41923a4123db55bf4e89bea32e142 +Advection,1D_Advection_Sols_beta2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255677,1D/Advection/Train/,0e9beff98693089c38e213a1f2b9ac43 +Advection,1D_Advection_Sols_beta4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255676,1D/Advection/Train/,a87b03ff425496360e2732921805f47d +Advection,1D_Advection_Sols_beta7.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/255664,1D/Advection/Train/,72a286ad2fcba574bd20bc045ba82d58 +Burgers,1D_Burgers_Sols_Nu0.001.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268190,1D/Burgers/Train/,44cb784d5a07aa2b1c864cabdcf625f9 +Burgers,1D_Burgers_Sols_Nu0.002.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268193,1D/Burgers/Train/,edf1cd13622d151dfde3e4e5af7a95b4 +Burgers,1D_Burgers_Sols_Nu0.004.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268191,1D/Burgers/Train/,435e1fecb8a64a0b4563bb8d09f81c33 +Burgers,1D_Burgers_Sols_Nu0.01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/281363,1D/Burgers/Train/,e6d9a4f62baf9a29121a816b919e2770 +Burgers,1D_Burgers_Sols_Nu0.02.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268189,1D/Burgers/Train/,7c8c717a3a7818145877baa57106b090 +Burgers,1D_Burgers_Sols_Nu0.04.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/281362,1D/Burgers/Train/,16f4c7afaf8c16238be157e54c9297c7 +Burgers,1D_Burgers_Sols_Nu0.1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268185,1D/Burgers/Train/,660ba1008d3843bf4e28d2895eb607ce +Burgers,1D_Burgers_Sols_Nu0.2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268187,1D/Burgers/Train/,01d9333254dff2f3cad090b61f5cf695 +Burgers,1D_Burgers_Sols_Nu0.4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268192,1D/Burgers/Train/,58327a6107e8f9defb5075677cc42533 +Burgers,1D_Burgers_Sols_Nu1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/281365,1D/Burgers/Train/,9021eba35332d127306f11ef84c1a60f +Burgers,1D_Burgers_Sols_Nu2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/281364,1D/Burgers/Train/,70fe0c24d9313e70f6059e5212bdb3d7 +Burgers,1D_Burgers_Sols_Nu4.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/268188,1D/Burgers/Train/,8af7f70166c00ffad377c80fa477d81f +1D_CFD,1D_CFD_Rand_Eta0.01_Zeta0.01_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164672,1D/CFD/Train/,2fdd00138f1d7abc794fb953021a9f43 +1D_CFD,1D_CFD_Rand_Eta0.1_Zeta0.1_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164668,1D/CFD/Train/,45655bd77d006ab539c52b7fbcf099b9 +1D_CFD,1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135485,1D/CFD/Train/,ad22fe08b8abc721179dbb34f2cc8b2a +1D_CFD,1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133155,1D/CFD/Train/,79857a11ad9a69d77b8cf249a1c72d06 +1D_CFD,1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133156,1D/CFD/Train/,08dffc3d84033c7574a70614703dd753 +1D_CFD,Sod1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133145,1D/CFD/Test/ShockTube/,ef8271ce332b986e961a7aaedcae24e7 +1D_CFD,Sod2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133146,1D/CFD/Test/ShockTube/,10e47c5c6ee0d1028f912a52bbf613d0 +1D_CFD,Sod3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133147,1D/CFD/Test/ShockTube/,495f2d2e0cee9b4ae28d7fef29bb7c68 +1D_CFD,Sod4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133148,1D/CFD/Test/ShockTube/,544399b3d05ae514d66fc96982ee9db8 +1D_CFD,Sod5.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133149,1D/CFD/Test/ShockTube/,ca7fa6631201e8bd506914d3b0b9fe50 +1D_CFD,Sod6.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133150,1D/CFD/Train/ShockTube/,adb2d95bf0d48e03bc0d8f4a2cbcd1c6 +1D_CFD,Sod7.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133151,1D/CFD/Test/ShockTube/,b8d73e1e3ef862ef732a5fa43c64612e +Diff_Sorp,1D_diff-sorp_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133020,1D/diffusion-sorption/,9d466d1213065619d087319e16d9a938 +1D_ReacDiff,ReacDiff_Nu0.5_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133177,1D/ReactionDiffusion/Train/,69a429239778d529cd419ed5888ea835 +1D_ReacDiff,ReacDiff_Nu0.5_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133178,1D/ReactionDiffusion/Train/,ff7c724b18e7ebe02e19c179852f48ee +1D_ReacDiff,ReacDiff_Nu0.5_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133179,1D/ReactionDiffusion/Train/,ac907daa7e483d203a5c77567cdea561 +1D_ReacDiff,ReacDiff_Nu0.5_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133180,1D/ReactionDiffusion/Train/,fb149e7540d8977af158bb8fec1048a3 +1D_ReacDiff,ReacDiff_Nu1.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133181,1D/ReactionDiffusion/Train/,bd73c2f3448d03e95e98c3831fc8fa70 +1D_ReacDiff,ReacDiff_Nu1.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133182,1D/ReactionDiffusion/Train/,a94e65631881a27ddae3ef74caf53093 +1D_ReacDiff,ReacDiff_Nu1.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133183,1D/ReactionDiffusion/Train/,112c01a76447162bd67c8c1073f58ca2 +1D_ReacDiff,ReacDiff_Nu1.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133184,1D/ReactionDiffusion/Train/,fa224c9d143de37ac6914d391e70f425 +1D_ReacDiff,ReacDiff_Nu2.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133185,1D/ReactionDiffusion/Train/,925a7e2c9b9e40ad2dd44b7002ec882b +1D_ReacDiff,ReacDiff_Nu2.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133186,1D/ReactionDiffusion/Train/,a3e25a9fb8a99f010352ad3dd1afd596 +1D_ReacDiff,ReacDiff_Nu2.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133187,1D/ReactionDiffusion/Train/,426353b241acfbc64067acb1bdc80ade +1D_ReacDiff,ReacDiff_Nu2.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133188,1D/ReactionDiffusion/Train/,f2890bdac5103a3b78fac5f80e57b760 +1D_ReacDiff,ReacDiff_Nu5.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133189,1D/ReactionDiffusion/Train/,0ed75b55f61bec11c47d379e34959e54 +1D_ReacDiff,ReacDiff_Nu5.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133190,1D/ReactionDiffusion/Train/,264c80af0e2a6cc1f70e87275e0f6ac4 +1D_ReacDiff,ReacDiff_Nu5.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133191,1D/ReactionDiffusion/Train/,f927f5d85f2e9bd97dff31b4dab051ae +1D_ReacDiff,ReacDiff_Nu5.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133192,1D/ReactionDiffusion/Train/,c9c26e2b5f2d4bf5bcbbd4ba34fefd5e +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133157,1D/ReactionDiffusion/Test/,6dc95d36e7126c104c5316485670f2c0 +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133158,1D/ReactionDiffusion/Test/,5bf21fbc428ac4346c0e7a04a5899f9b +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133159,1D/ReactionDiffusion/Test/,5198a1572c201657dc19ad8592e24ac1 +1D_ReacDiff,ReacDiff_react_Nu0.5_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133160,1D/ReactionDiffusion/Test/,9968701e89a8b406f18ffbc2b74045d8 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133161,1D/ReactionDiffusion/Test/,a258ff5be23d17d89cb10ac94f512165 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133162,1D/ReactionDiffusion/Test/,09279a597860dcf48ef6f6539c46920f +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133163,1D/ReactionDiffusion/Test/,27113922509f6336f5c8be8d99109828 +1D_ReacDiff,ReacDiff_react_Nu1.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133164,1D/ReactionDiffusion/Test/,24ab563c27bf07d653b881d3c652bfa5 +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133165,1D/ReactionDiffusion/Test/,5a0f4d1ff304b11b9b22c3b00b1f63d4 +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133166,1D/ReactionDiffusion/Test/,4cf67490b01e54f80d45c8e7936b804d +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133167,1D/ReactionDiffusion/Test/,3c54c1cd3710b1f5b02a3de3782201ed +1D_ReacDiff,ReacDiff_react_Nu10.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133168,1D/ReactionDiffusion/Test/,580f08d5755ee393e7092eafedb70e46 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133169,1D/ReactionDiffusion/Test/,208882dcf594d52f8e5017904efd84c9 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133170,1D/ReactionDiffusion/Test/,891f8b47e49348b5411882ecebbc1bfd +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133171,1D/ReactionDiffusion/Test/,56e8e1e55ac92acf21b253511c370642 +1D_ReacDiff,ReacDiff_react_Nu2.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133172,1D/ReactionDiffusion/Test/,4498954ef4cd5c63ac530ee46c351790 +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho1.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133173,1D/ReactionDiffusion/Test/,6b2c45748711bea8779dea3b48a53a7e +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho10.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133174,1D/ReactionDiffusion/Test/,b04c22629bf86d85929386aaf4a5f95d +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho2.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133175,1D/ReactionDiffusion/Test/,1738e4374456af974e4d0058d3426a69 +1D_ReacDiff,ReacDiff_react_Nu5.0_Rho5.0.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133176,1D/ReactionDiffusion/Test/,4e3cf0ac66e85dbb77c79861076db8e8 +2D_CFD,2D_CFD_Rand_M0.1_Eta0.01_Zeta0.01_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164687,2D/CFD/2D_Train_Rand/,5b21dcccaef4d2145ca579a71153c580 +2D_CFD,2D_CFD_Rand_M0.1_Eta0.1_Zeta0.1_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164688,2D/CFD/2D_Train_Rand/,b2733888745a1d64e36df813e979910b +2D_CFD,2D_CFD_Rand_M0.1_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164689,2D/CFD/2D_Train_Rand/,ae2552864d0856f58dc74132a2f95d20 +2D_CFD,2D_CFD_Rand_M1.0_Eta0.01_Zeta0.01_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164690,2D/CFD/2D_Train_Rand/,21969082d0e9524bcc4708e216148e60 +2D_CFD,2D_CFD_Rand_M1.0_Eta0.1_Zeta0.1_periodic_128_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164691,2D/CFD/2D_Train_Rand/,75a34f34a3dcd9bc8ae4b7adf8bf372c +2D_CFD,2D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164692,2D/CFD/2D_Train_Rand/,04fcc44c39acea69e2869b84cd653d4a +2D_CFD,2D_CFD_Turb_M0.1_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164685,2D/CFD/2D_Train_Turb/,844555000d342d2947162c6cf46798e7 +2D_CFD,2D_CFD_Turb_M1.0_Eta1e-08_Zeta1e-08_periodic_512_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164686,2D/CFD/2D_Train_Turb/,3f2c7376cde5fb072db0f9814f1c6992 +2D_CFD,2D_shock.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133193,2D/CFD/Test/2DShock/,b4098c20e2e0a25cd8ee229c9294b899 +2D_CFD,KH_M01_dk10_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133209,2D/CFD/Test/KH/,54e278b3a7419107a5cd6a8a019225d5 +2D_CFD,KH_M01_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133210,2D/CFD/Test/KH/,29ce31d3ce61de3b90ac0b42716c061c +2D_CFD,KH_M01_dk2_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133211,2D/CFD/Test/KH/,de0b2a3343de028e4c162723ec571713 +2D_CFD,KH_M01_dk5_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133212,2D/CFD/Test/KH/,5bcaed9eb7db5fd55d51f7347a938413 +2D_CFD,KH_M02_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133213,2D/CFD/Test/KH/,fbc392f83affc2224a5703dad7bda28a +2D_CFD,KH_M04_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133214,2D/CFD/Test/KH/,66626c935ab070a1e5df52bcf6aeccc7 +2D_CFD,KH_M1_dk1_Re1e3.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133215,2D/CFD/Test/KH/,338393c118196048a38d1df731a33b0a +2D_CFD,OTVortex.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133216,2D/CFD/Test/TOV/,3b68cb29e3d1906a19ce2cb4ce71faa4 +Darcy,2D_DarcyFlow_beta0.01_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133217,2D/DarcyFlow/,d05c287d4c0b7d3178b0097084238251 +Darcy,2D_DarcyFlow_beta0.1_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133218,2D/DarcyFlow/,294f9a03a4aa16b0e386469ca8b471be +Darcy,2D_DarcyFlow_beta1.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133219,2D/DarcyFlow/,81694ed31306ff2e5f6b76349b0b4389 +Darcy,2D_DarcyFlow_beta10.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133220,2D/DarcyFlow/,a7f23cf8011fc211b180828af39b7d1a +Darcy,2D_DarcyFlow_beta100.0_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133221,2D/DarcyFlow/,7c09f3b1bb097737d3fd52ffb0c6f1c8 +2D_ReacDiff,2D_diff-react_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133017,2D/diffusion-reaction/,b8d0b86064193195ddc30c33be5dc949 +NS_Incom,ns_incom_inhom_2d_512-0.h5,https://darus.uni-stuttgart.de/api/access/datafile/133280,2D/NS_incom/,54109d46f9c957317bd670ddb2068ac0 +NS_Incom,ns_incom_inhom_2d_512-1.h5,https://darus.uni-stuttgart.de/api/access/datafile/136439,2D/NS_incom/,e280fd3208fccb8ad5c1ff46c4796864 +NS_Incom,ns_incom_inhom_2d_512-10.h5,https://darus.uni-stuttgart.de/api/access/datafile/133309,2D/NS_incom/,f497297295ce3f2ebfefd81d00de31da +NS_Incom,ns_incom_inhom_2d_512-100.h5,https://darus.uni-stuttgart.de/api/access/datafile/133267,2D/NS_incom/,fcf3b743fc82ac8706cfa85a7b259dcd +NS_Incom,ns_incom_inhom_2d_512-101.h5,https://darus.uni-stuttgart.de/api/access/datafile/133289,2D/NS_incom/,9d1cd827c95f80975d09eae0b4e6c3da +NS_Incom,ns_incom_inhom_2d_512-102.h5,https://darus.uni-stuttgart.de/api/access/datafile/133291,2D/NS_incom/,ce1f0a7a5e52bf49db43084eef367639 +NS_Incom,ns_incom_inhom_2d_512-103.h5,https://darus.uni-stuttgart.de/api/access/datafile/133294,2D/NS_incom/,4e0a5f39d14745adf96655f220329550 +NS_Incom,ns_incom_inhom_2d_512-104.h5,https://darus.uni-stuttgart.de/api/access/datafile/133298,2D/NS_incom/,52f59ffdef04338f2630e98e32448086 +NS_Incom,ns_incom_inhom_2d_512-105.h5,https://darus.uni-stuttgart.de/api/access/datafile/133290,2D/NS_incom/,1010c2d0c807e5e355e336fe8d905067 +NS_Incom,ns_incom_inhom_2d_512-106.h5,https://darus.uni-stuttgart.de/api/access/datafile/133374,2D/NS_incom/,1127b6e69bd83b128a9420ae58187729 +NS_Incom,ns_incom_inhom_2d_512-107.h5,https://darus.uni-stuttgart.de/api/access/datafile/133375,2D/NS_incom/,38e676bfd97cc946cd6db2af48c487bf +NS_Incom,ns_incom_inhom_2d_512-108.h5,https://darus.uni-stuttgart.de/api/access/datafile/133376,2D/NS_incom/,0355430cc9cee8e921378eda7c1c6b84 +NS_Incom,ns_incom_inhom_2d_512-109.h5,https://darus.uni-stuttgart.de/api/access/datafile/133305,2D/NS_incom/,eff2dd6e71649bb3f9f37f951bd54d63 +NS_Incom,ns_incom_inhom_2d_512-11.h5,https://darus.uni-stuttgart.de/api/access/datafile/133336,2D/NS_incom/,3342616df260d3ec60fbf24dfaabf9ae +NS_Incom,ns_incom_inhom_2d_512-110.h5,https://darus.uni-stuttgart.de/api/access/datafile/133313,2D/NS_incom/,d9739419a2100b390742acf88e4548b3 +NS_Incom,ns_incom_inhom_2d_512-111.h5,https://darus.uni-stuttgart.de/api/access/datafile/133318,2D/NS_incom/,ea5239ab7204d53c476f48739a4e0d78 +NS_Incom,ns_incom_inhom_2d_512-112.h5,https://darus.uni-stuttgart.de/api/access/datafile/133324,2D/NS_incom/,cdaf99ff4ef69ee79ad60648a06259d7 +NS_Incom,ns_incom_inhom_2d_512-113.h5,https://darus.uni-stuttgart.de/api/access/datafile/133377,2D/NS_incom/,aee8074da1cf6e19ee421a7b86caeaa3 +NS_Incom,ns_incom_inhom_2d_512-114.h5,https://darus.uni-stuttgart.de/api/access/datafile/133378,2D/NS_incom/,f1b4550b00a887771e79a81c7336a4b0 +NS_Incom,ns_incom_inhom_2d_512-115.h5,https://darus.uni-stuttgart.de/api/access/datafile/133379,2D/NS_incom/,0756d685a754b84337d1de7280572c85 +NS_Incom,ns_incom_inhom_2d_512-116.h5,https://darus.uni-stuttgart.de/api/access/datafile/133380,2D/NS_incom/,f7c55c110dcf0aeaed03699bc0942f0d +NS_Incom,ns_incom_inhom_2d_512-117.h5,https://darus.uni-stuttgart.de/api/access/datafile/133381,2D/NS_incom/,375331e30111457028de38b89e03c404 +NS_Incom,ns_incom_inhom_2d_512-118.h5,https://darus.uni-stuttgart.de/api/access/datafile/133383,2D/NS_incom/,6d2fbe4a0e32e1934d349616ca6b85d8 +NS_Incom,ns_incom_inhom_2d_512-119.h5,https://darus.uni-stuttgart.de/api/access/datafile/133385,2D/NS_incom/,993112f4fe3910230005cc35099c7d59 +NS_Incom,ns_incom_inhom_2d_512-12.h5,https://darus.uni-stuttgart.de/api/access/datafile/133574,2D/NS_incom/,71f407084513d7c0e93377c1b46e4bb4 +NS_Incom,ns_incom_inhom_2d_512-120.h5,https://darus.uni-stuttgart.de/api/access/datafile/133386,2D/NS_incom/,f7c0b147e5b3f217a1d534f303ab52f4 +NS_Incom,ns_incom_inhom_2d_512-121.h5,https://darus.uni-stuttgart.de/api/access/datafile/133441,2D/NS_incom/,785d733406eeec2694eb89769f72971b +NS_Incom,ns_incom_inhom_2d_512-122.h5,https://darus.uni-stuttgart.de/api/access/datafile/133346,2D/NS_incom/,eeb2488aa0a5d1f14069dcfaaa0da71d +NS_Incom,ns_incom_inhom_2d_512-123.h5,https://darus.uni-stuttgart.de/api/access/datafile/133442,2D/NS_incom/,57e341bea82fd33ec095e7070fd1ae38 +NS_Incom,ns_incom_inhom_2d_512-124.h5,https://darus.uni-stuttgart.de/api/access/datafile/133443,2D/NS_incom/,6aba12029ab0ddb165c1c71f3b1c8f27 +NS_Incom,ns_incom_inhom_2d_512-125.h5,https://darus.uni-stuttgart.de/api/access/datafile/133444,2D/NS_incom/,2b15572d1dda8048ab9713c2318d9322 +NS_Incom,ns_incom_inhom_2d_512-126.h5,https://darus.uni-stuttgart.de/api/access/datafile/133303,2D/NS_incom/,56bcc085c7c0d303dedf147e6c608730 +NS_Incom,ns_incom_inhom_2d_512-127.h5,https://darus.uni-stuttgart.de/api/access/datafile/133292,2D/NS_incom/,71b5191ed81e86ec68f0a7bcd6a07759 +NS_Incom,ns_incom_inhom_2d_512-128.h5,https://darus.uni-stuttgart.de/api/access/datafile/133454,2D/NS_incom/,72b8110ec99098530494bbf0b64f51f7 +NS_Incom,ns_incom_inhom_2d_512-129.h5,https://darus.uni-stuttgart.de/api/access/datafile/133293,2D/NS_incom/,0e2cda441dec2c19ffaefd8ad035220e +NS_Incom,ns_incom_inhom_2d_512-13.h5,https://darus.uni-stuttgart.de/api/access/datafile/133304,2D/NS_incom/,424f6d2ab607612599645442a45234cf +NS_Incom,ns_incom_inhom_2d_512-130.h5,https://darus.uni-stuttgart.de/api/access/datafile/133295,2D/NS_incom/,8a6f6b785ce64aecc9f46a31650662d0 +NS_Incom,ns_incom_inhom_2d_512-131.h5,https://darus.uni-stuttgart.de/api/access/datafile/133275,2D/NS_incom/,3c4541c0ed411fdc9641b27978318ad6 +NS_Incom,ns_incom_inhom_2d_512-132.h5,https://darus.uni-stuttgart.de/api/access/datafile/133296,2D/NS_incom/,3c4541c0ed411fdc9641b27978318ad6 +NS_Incom,ns_incom_inhom_2d_512-133.h5,https://darus.uni-stuttgart.de/api/access/datafile/133297,2D/NS_incom/,ba2bb63092d5d0cb8fae29a0c18ff851 +NS_Incom,ns_incom_inhom_2d_512-134.h5,https://darus.uni-stuttgart.de/api/access/datafile/133269,2D/NS_incom/,4b0b6e0c3d22772ea20dc4c3e07c5e7d +NS_Incom,ns_incom_inhom_2d_512-135.h5,https://darus.uni-stuttgart.de/api/access/datafile/133274,2D/NS_incom/,e48583df141230425ea36d093b0b46f6 +NS_Incom,ns_incom_inhom_2d_512-136.h5,https://darus.uni-stuttgart.de/api/access/datafile/133299,2D/NS_incom/,4b0b6e0c3d22772ea20dc4c3e07c5e7d +NS_Incom,ns_incom_inhom_2d_512-137.h5,https://darus.uni-stuttgart.de/api/access/datafile/133300,2D/NS_incom/,e48583df141230425ea36d093b0b46f6 +NS_Incom,ns_incom_inhom_2d_512-138.h5,https://darus.uni-stuttgart.de/api/access/datafile/133302,2D/NS_incom/,294b49a65b2fc861e23dfc40098d6e91 +NS_Incom,ns_incom_inhom_2d_512-139.h5,https://darus.uni-stuttgart.de/api/access/datafile/133575,2D/NS_incom/,14c9705c20d630e08f4387cc4880a646 +NS_Incom,ns_incom_inhom_2d_512-14.h5,https://darus.uni-stuttgart.de/api/access/datafile/133281,2D/NS_incom/,f8099cba29f6374b6454424ef521b86d +NS_Incom,ns_incom_inhom_2d_512-140.h5,https://darus.uni-stuttgart.de/api/access/datafile/133698,2D/NS_incom/,f8c12c7ee3973eab55a531a24689ce95 +NS_Incom,ns_incom_inhom_2d_512-141.h5,https://darus.uni-stuttgart.de/api/access/datafile/133307,2D/NS_incom/,95a897a07713b654ce497b513ada21df +NS_Incom,ns_incom_inhom_2d_512-142.h5,https://darus.uni-stuttgart.de/api/access/datafile/133306,2D/NS_incom/,b0c964793163e49d46ddfa783b7f2a87 +NS_Incom,ns_incom_inhom_2d_512-143.h5,https://darus.uni-stuttgart.de/api/access/datafile/133590,2D/NS_incom/,b35dd28ee302e41dd9500e65e681bd93 +NS_Incom,ns_incom_inhom_2d_512-144.h5,https://darus.uni-stuttgart.de/api/access/datafile/133591,2D/NS_incom/,d7b000376bcb6cf4949e2dec4232a926 +NS_Incom,ns_incom_inhom_2d_512-145.h5,https://darus.uni-stuttgart.de/api/access/datafile/133592,2D/NS_incom/,e90fad8268cf369136cebba09f3b06e8 +NS_Incom,ns_incom_inhom_2d_512-146.h5,https://darus.uni-stuttgart.de/api/access/datafile/133593,2D/NS_incom/,568fac9aab2f599c20da783e9c7f3f0a +NS_Incom,ns_incom_inhom_2d_512-147.h5,https://darus.uni-stuttgart.de/api/access/datafile/133599,2D/NS_incom/,43194bdefada7b59cac975cc08e6d12d +NS_Incom,ns_incom_inhom_2d_512-148.h5,https://darus.uni-stuttgart.de/api/access/datafile/133600,2D/NS_incom/,41a78815f65ff7b607d69dd508b3ffea +NS_Incom,ns_incom_inhom_2d_512-149.h5,https://darus.uni-stuttgart.de/api/access/datafile/133601,2D/NS_incom/,3ebb3a014d7c33787cda458a689f7728 +NS_Incom,ns_incom_inhom_2d_512-15.h5,https://darus.uni-stuttgart.de/api/access/datafile/133609,2D/NS_incom/,74dd42aaf8b809925ff6dce345be11fd +NS_Incom,ns_incom_inhom_2d_512-150.h5,https://darus.uni-stuttgart.de/api/access/datafile/133602,2D/NS_incom/,562bd9a0d25ce189a9635d385e3d6a3c +NS_Incom,ns_incom_inhom_2d_512-151.h5,https://darus.uni-stuttgart.de/api/access/datafile/133271,2D/NS_incom/,1acb06d08a4ae3a17ddbe04a2e3fd5cf +NS_Incom,ns_incom_inhom_2d_512-152.h5,https://darus.uni-stuttgart.de/api/access/datafile/133603,2D/NS_incom/,0023490cb7c00a4a75f9a252af9be71c +NS_Incom,ns_incom_inhom_2d_512-153.h5,https://darus.uni-stuttgart.de/api/access/datafile/133594,2D/NS_incom/,669788ec079a3e695b68821c17d79a26 +NS_Incom,ns_incom_inhom_2d_512-154.h5,https://darus.uni-stuttgart.de/api/access/datafile/133604,2D/NS_incom/,547cfa5abca50bcd927630b8264d6876 +NS_Incom,ns_incom_inhom_2d_512-155.h5,https://darus.uni-stuttgart.de/api/access/datafile/133605,2D/NS_incom/,f28fcf6d99c0d1f1bb08a82be94114eb +NS_Incom,ns_incom_inhom_2d_512-156.h5,https://darus.uni-stuttgart.de/api/access/datafile/133606,2D/NS_incom/,8c52c6258bdbbc7c4fe1cb7892b5bd23 +NS_Incom,ns_incom_inhom_2d_512-157.h5,https://darus.uni-stuttgart.de/api/access/datafile/133607,2D/NS_incom/,91f4ab6bd4eef91ab43908290539bdab +NS_Incom,ns_incom_inhom_2d_512-158.h5,https://darus.uni-stuttgart.de/api/access/datafile/133321,2D/NS_incom/,299521efb9f871919b003e86d0e8d628 +NS_Incom,ns_incom_inhom_2d_512-159.h5,https://darus.uni-stuttgart.de/api/access/datafile/133608,2D/NS_incom/,54b54c80708366c74d4aee82688be26e +NS_Incom,ns_incom_inhom_2d_512-16.h5,https://darus.uni-stuttgart.de/api/access/datafile/133311,2D/NS_incom/,b83a1465fee7f5dcb9826d1bbb4b6f71 +NS_Incom,ns_incom_inhom_2d_512-160.h5,https://darus.uni-stuttgart.de/api/access/datafile/133610,2D/NS_incom/,e90d31aff2172f508846473126c78947 +NS_Incom,ns_incom_inhom_2d_512-161.h5,https://darus.uni-stuttgart.de/api/access/datafile/133611,2D/NS_incom/,24b4b52f0a03dc52f7159ef10652c40a +NS_Incom,ns_incom_inhom_2d_512-162.h5,https://darus.uni-stuttgart.de/api/access/datafile/133683,2D/NS_incom/,87d592f2be6a76751b2b36e8bf0d0a88 +NS_Incom,ns_incom_inhom_2d_512-163.h5,https://darus.uni-stuttgart.de/api/access/datafile/133612,2D/NS_incom/,2acf733c2833fb815487271bbb5ce66f +NS_Incom,ns_incom_inhom_2d_512-164.h5,https://darus.uni-stuttgart.de/api/access/datafile/136435,2D/NS_incom/,8470816b05ecbabd2ec6036f346d4d79 +NS_Incom,ns_incom_inhom_2d_512-165.h5,https://darus.uni-stuttgart.de/api/access/datafile/133685,2D/NS_incom/,61d677b1edd03987c26648d7d136672d +NS_Incom,ns_incom_inhom_2d_512-166.h5,https://darus.uni-stuttgart.de/api/access/datafile/133308,2D/NS_incom/,4f1dd0e87f4ad925dc82444da4df7d3a +NS_Incom,ns_incom_inhom_2d_512-167.h5,https://darus.uni-stuttgart.de/api/access/datafile/133684,2D/NS_incom/,8e90f2ab307a6c5f8a2e1a3befa02d8f +NS_Incom,ns_incom_inhom_2d_512-168.h5,https://darus.uni-stuttgart.de/api/access/datafile/133701,2D/NS_incom/,1f8ef01efadb38eb638c383e242d9511 +NS_Incom,ns_incom_inhom_2d_512-169.h5,https://darus.uni-stuttgart.de/api/access/datafile/133310,2D/NS_incom/,d2ae69de21cb38a24dbc174f34afeb70 +NS_Incom,ns_incom_inhom_2d_512-17.h5,https://darus.uni-stuttgart.de/api/access/datafile/133325,2D/NS_incom/,9c206312c353aa6dcaa3144d53753075 +NS_Incom,ns_incom_inhom_2d_512-170.h5,https://darus.uni-stuttgart.de/api/access/datafile/133312,2D/NS_incom/,d7f9e313387dfb3efc356c1d5606ebc3 +NS_Incom,ns_incom_inhom_2d_512-171.h5,https://darus.uni-stuttgart.de/api/access/datafile/133679,2D/NS_incom/,f6ade3872e3e0db0d6b8af4cc9fd7ed6 +NS_Incom,ns_incom_inhom_2d_512-172.h5,https://darus.uni-stuttgart.de/api/access/datafile/133314,2D/NS_incom/,986851271af43e55264a8db4229b2049 +NS_Incom,ns_incom_inhom_2d_512-173.h5,https://darus.uni-stuttgart.de/api/access/datafile/133315,2D/NS_incom/,e064a3b043136572de413744f470e795 +NS_Incom,ns_incom_inhom_2d_512-174.h5,https://darus.uni-stuttgart.de/api/access/datafile/133316,2D/NS_incom/,d4906a07e5e94b6e147570613205d19d +NS_Incom,ns_incom_inhom_2d_512-175.h5,https://darus.uni-stuttgart.de/api/access/datafile/133317,2D/NS_incom/,7d7230752c444c2570396927e9f9b4bf +NS_Incom,ns_incom_inhom_2d_512-176.h5,https://darus.uni-stuttgart.de/api/access/datafile/133319,2D/NS_incom/,47a59095761d69cecc886ddb3027b556 +NS_Incom,ns_incom_inhom_2d_512-177.h5,https://darus.uni-stuttgart.de/api/access/datafile/133320,2D/NS_incom/,979de8be5c402f6dc50060e92ca6245a +NS_Incom,ns_incom_inhom_2d_512-178.h5,https://darus.uni-stuttgart.de/api/access/datafile/133342,2D/NS_incom/,a72fc652b38b2dc0b0aa406bb35cd676 +NS_Incom,ns_incom_inhom_2d_512-179.h5,https://darus.uni-stuttgart.de/api/access/datafile/133697,2D/NS_incom/,6dd8a78a85ad7c0e03daaabcb3826fdb +NS_Incom,ns_incom_inhom_2d_512-18.h5,https://darus.uni-stuttgart.de/api/access/datafile/136437,2D/NS_incom/,272c1cdf65a90dc3e4308df8a15ef7dc +NS_Incom,ns_incom_inhom_2d_512-180.h5,https://darus.uni-stuttgart.de/api/access/datafile/133326,2D/NS_incom/,f9ffc612a7438dc17524818b48acb7ac +NS_Incom,ns_incom_inhom_2d_512-181.h5,https://darus.uni-stuttgart.de/api/access/datafile/133327,2D/NS_incom/,78ee160778aab06d0e88507944f661b4 +NS_Incom,ns_incom_inhom_2d_512-182.h5,https://darus.uni-stuttgart.de/api/access/datafile/133328,2D/NS_incom/,e9a36056e3e9fe23937fbf65e0dfc16a +NS_Incom,ns_incom_inhom_2d_512-183.h5,https://darus.uni-stuttgart.de/api/access/datafile/133329,2D/NS_incom/,5107343596d93f8739b4f09926daeef7 +NS_Incom,ns_incom_inhom_2d_512-184.h5,https://darus.uni-stuttgart.de/api/access/datafile/133330,2D/NS_incom/,f4a7aff84a03fff6e1958617abf373c4 +NS_Incom,ns_incom_inhom_2d_512-185.h5,https://darus.uni-stuttgart.de/api/access/datafile/133331,2D/NS_incom/,f59cb88424414a28aad308499935619b +NS_Incom,ns_incom_inhom_2d_512-186.h5,https://darus.uni-stuttgart.de/api/access/datafile/133666,2D/NS_incom/,d9bc339d2d2047f8f6a369ad2e4e9357 +NS_Incom,ns_incom_inhom_2d_512-187.h5,https://darus.uni-stuttgart.de/api/access/datafile/136436,2D/NS_incom/,4d0461fc34683816d5906d4809c84432 +NS_Incom,ns_incom_inhom_2d_512-188.h5,https://darus.uni-stuttgart.de/api/access/datafile/133332,2D/NS_incom/,3565b7f3ab620c3716a25865670b98fb +NS_Incom,ns_incom_inhom_2d_512-189.h5,https://darus.uni-stuttgart.de/api/access/datafile/133668,2D/NS_incom/,d385ef198ae29ef5eff9aad6d129621a +NS_Incom,ns_incom_inhom_2d_512-19.h5,https://darus.uni-stuttgart.de/api/access/datafile/133335,2D/NS_incom/,f67fec4521fc9a113cd16de7f7c283ff +NS_Incom,ns_incom_inhom_2d_512-190.h5,https://darus.uni-stuttgart.de/api/access/datafile/136438,2D/NS_incom/,2dce11f971c3561083d580f7299f3e02 +NS_Incom,ns_incom_inhom_2d_512-191.h5,https://darus.uni-stuttgart.de/api/access/datafile/133663,2D/NS_incom/,fda967e4ca21881b00d5c67b984d4c78 +NS_Incom,ns_incom_inhom_2d_512-192.h5,https://darus.uni-stuttgart.de/api/access/datafile/133664,2D/NS_incom/,85542754064fcab3675930ac607b68e0 +NS_Incom,ns_incom_inhom_2d_512-193.h5,https://darus.uni-stuttgart.de/api/access/datafile/133371,2D/NS_incom/,2f67289963f5d9d3041acf67f45a38b7 +NS_Incom,ns_incom_inhom_2d_512-194.h5,https://darus.uni-stuttgart.de/api/access/datafile/133669,2D/NS_incom/,609d2d5dbd19ccdb6cee8ab67df66c38 +NS_Incom,ns_incom_inhom_2d_512-195.h5,https://darus.uni-stuttgart.de/api/access/datafile/133699,2D/NS_incom/,b39dbe9518dbc19da27345ce569489e4 +NS_Incom,ns_incom_inhom_2d_512-196.h5,https://darus.uni-stuttgart.de/api/access/datafile/133665,2D/NS_incom/,fc636092ce4175b3d908d156db66deb8 +NS_Incom,ns_incom_inhom_2d_512-197.h5,https://darus.uni-stuttgart.de/api/access/datafile/133702,2D/NS_incom/,0b3bd457d75ccb3120f649bcdb0089f2 +NS_Incom,ns_incom_inhom_2d_512-198.h5,https://darus.uni-stuttgart.de/api/access/datafile/133368,2D/NS_incom/,526cce686110114720bc9d84eb8d16ee +NS_Incom,ns_incom_inhom_2d_512-199.h5,https://darus.uni-stuttgart.de/api/access/datafile/133667,2D/NS_incom/,123272aaaf52747c39e8c32b4955390b +NS_Incom,ns_incom_inhom_2d_512-2.h5,https://darus.uni-stuttgart.de/api/access/datafile/133721,2D/NS_incom/,1d7a2aac41a410bea6c887f624273d20 +NS_Incom,ns_incom_inhom_2d_512-20.h5,https://darus.uni-stuttgart.de/api/access/datafile/133283,2D/NS_incom/,859c8c8cede4fd611548af291acfa362 +NS_Incom,ns_incom_inhom_2d_512-200.h5,https://darus.uni-stuttgart.de/api/access/datafile/136440,2D/NS_incom/,621c5bdf211ab8aa6d586ede4b51c5a2 +NS_Incom,ns_incom_inhom_2d_512-201.h5,https://darus.uni-stuttgart.de/api/access/datafile/133367,2D/NS_incom/,89b6667026a2f289b81e48b6d75ef4d6 +NS_Incom,ns_incom_inhom_2d_512-202.h5,https://darus.uni-stuttgart.de/api/access/datafile/133661,2D/NS_incom/,a70f3e38089caec832075b7c3ce86744 +NS_Incom,ns_incom_inhom_2d_512-203.h5,https://darus.uni-stuttgart.de/api/access/datafile/133660,2D/NS_incom/,f0bc2659a6f671813e88409bdb08531a +NS_Incom,ns_incom_inhom_2d_512-204.h5,https://darus.uni-stuttgart.de/api/access/datafile/133671,2D/NS_incom/,c109d2aa5cf0688c5f0565b03d1e3ce0 +NS_Incom,ns_incom_inhom_2d_512-205.h5,https://darus.uni-stuttgart.de/api/access/datafile/133688,2D/NS_incom/,2fa87303d667e4d50935fe26f04437b4 +NS_Incom,ns_incom_inhom_2d_512-206.h5,https://darus.uni-stuttgart.de/api/access/datafile/136441,2D/NS_incom/,28a557b57ffce43cb9de4cc48642fc05 +NS_Incom,ns_incom_inhom_2d_512-207.h5,https://darus.uni-stuttgart.de/api/access/datafile/133282,2D/NS_incom/,01a63c9ad30ac4d140f86bb2fa929274 +NS_Incom,ns_incom_inhom_2d_512-208.h5,https://darus.uni-stuttgart.de/api/access/datafile/133340,2D/NS_incom/,d2aa05820caf6a38be4711ef797d3bf4 +NS_Incom,ns_incom_inhom_2d_512-209.h5,https://darus.uni-stuttgart.de/api/access/datafile/133333,2D/NS_incom/,9e17d624a2eea451f0956f9184ce63d8 +NS_Incom,ns_incom_inhom_2d_512-21.h5,https://darus.uni-stuttgart.de/api/access/datafile/133334,2D/NS_incom/,859c8c8cede4fd611548af291acfa362 +NS_Incom,ns_incom_inhom_2d_512-210.h5,https://darus.uni-stuttgart.de/api/access/datafile/133703,2D/NS_incom/,fb5de2b943891ed1ec1bbb682520bc78 +NS_Incom,ns_incom_inhom_2d_512-211.h5,https://darus.uni-stuttgart.de/api/access/datafile/133286,2D/NS_incom/,24f8fe56ffe24baaa03f6bf12762ef95 +NS_Incom,ns_incom_inhom_2d_512-212.h5,https://darus.uni-stuttgart.de/api/access/datafile/133284,2D/NS_incom/,8e4561fe4ae69d62000df136f980fae3 +NS_Incom,ns_incom_inhom_2d_512-213.h5,https://darus.uni-stuttgart.de/api/access/datafile/133285,2D/NS_incom/,d1101482e0d766334b92c4d0e11d73f3 +NS_Incom,ns_incom_inhom_2d_512-214.h5,https://darus.uni-stuttgart.de/api/access/datafile/133337,2D/NS_incom/,d1101482e0d766334b92c4d0e11d73f3 +NS_Incom,ns_incom_inhom_2d_512-215.h5,https://darus.uni-stuttgart.de/api/access/datafile/133287,2D/NS_incom/,f615b96dd71864673bba274240acfb12 +NS_Incom,ns_incom_inhom_2d_512-216.h5,https://darus.uni-stuttgart.de/api/access/datafile/133288,2D/NS_incom/,892986d49b49b431f3f172d7baa9977b +NS_Incom,ns_incom_inhom_2d_512-217.h5,https://darus.uni-stuttgart.de/api/access/datafile/133338,2D/NS_incom/,a471e758505de149dba27c9fa2d29198 +NS_Incom,ns_incom_inhom_2d_512-218.h5,https://darus.uni-stuttgart.de/api/access/datafile/133339,2D/NS_incom/,f615b96dd71864673bba274240acfb12 +NS_Incom,ns_incom_inhom_2d_512-219.h5,https://darus.uni-stuttgart.de/api/access/datafile/133341,2D/NS_incom/,1965889c070e802965ebe19b01cdf2db +NS_Incom,ns_incom_inhom_2d_512-22.h5,https://darus.uni-stuttgart.de/api/access/datafile/133344,2D/NS_incom/,2235dd8e60d0d43b49aba99819ddb8d3 +NS_Incom,ns_incom_inhom_2d_512-220.h5,https://darus.uni-stuttgart.de/api/access/datafile/133343,2D/NS_incom/,588ce18cf51ca3517527f3dd0b0ee443 +NS_Incom,ns_incom_inhom_2d_512-221.h5,https://darus.uni-stuttgart.de/api/access/datafile/133345,2D/NS_incom/,e57ad44f1ae4fa9f31ddff57578507ce +NS_Incom,ns_incom_inhom_2d_512-222.h5,https://darus.uni-stuttgart.de/api/access/datafile/133347,2D/NS_incom/,892986d49b49b431f3f172d7baa9977b +NS_Incom,ns_incom_inhom_2d_512-223.h5,https://darus.uni-stuttgart.de/api/access/datafile/133369,2D/NS_incom/,9da97766e3b311af6d79a946f8db5184 +NS_Incom,ns_incom_inhom_2d_512-224.h5,https://darus.uni-stuttgart.de/api/access/datafile/133370,2D/NS_incom/,44a864a879590427273a3dbf1aa2c190 +NS_Incom,ns_incom_inhom_2d_512-225.h5,https://darus.uni-stuttgart.de/api/access/datafile/133372,2D/NS_incom/,8e4561fe4ae69d62000df136f980fae3 +NS_Incom,ns_incom_inhom_2d_512-226.h5,https://darus.uni-stuttgart.de/api/access/datafile/133373,2D/NS_incom/,adf37d1dd066c2dc8fb3886369b5434f +NS_Incom,ns_incom_inhom_2d_512-227.h5,https://darus.uni-stuttgart.de/api/access/datafile/136442,2D/NS_incom/,de46866be488bb59d0d1c8ddc13a4dcb +NS_Incom,ns_incom_inhom_2d_512-228.h5,https://darus.uni-stuttgart.de/api/access/datafile/133723,2D/NS_incom/,43975b57db7c922f94703d1c63fdaebe +NS_Incom,ns_incom_inhom_2d_512-229.h5,https://darus.uni-stuttgart.de/api/access/datafile/136443,2D/NS_incom/,805f1a7336301b2bcc4a9f0fcce43864 +NS_Incom,ns_incom_inhom_2d_512-23.h5,https://darus.uni-stuttgart.de/api/access/datafile/133617,2D/NS_incom/,d026b809986795e73f13368a763e56d2 +NS_Incom,ns_incom_inhom_2d_512-230.h5,https://darus.uni-stuttgart.de/api/access/datafile/136445,2D/NS_incom/,e43acdfb560a45181111256e4ce45cc1 +NS_Incom,ns_incom_inhom_2d_512-231.h5,https://darus.uni-stuttgart.de/api/access/datafile/133613,2D/NS_incom/,f56497f0ba427fdf9e215c885c6c1d3a +NS_Incom,ns_incom_inhom_2d_512-232.h5,https://darus.uni-stuttgart.de/api/access/datafile/133692,2D/NS_incom/,599ff6b1ea10572a829a13e5fc9c2523 +NS_Incom,ns_incom_inhom_2d_512-233.h5,https://darus.uni-stuttgart.de/api/access/datafile/133614,2D/NS_incom/,b0cbda1f7435ccbe04bc6a09840624df +NS_Incom,ns_incom_inhom_2d_512-234.h5,https://darus.uni-stuttgart.de/api/access/datafile/133700,2D/NS_incom/,6331197878327890af4da21d5c4065bf +NS_Incom,ns_incom_inhom_2d_512-235.h5,https://darus.uni-stuttgart.de/api/access/datafile/133693,2D/NS_incom/,dd937dace08671e5b61a8f960d845561 +NS_Incom,ns_incom_inhom_2d_512-236.h5,https://darus.uni-stuttgart.de/api/access/datafile/133615,2D/NS_incom/,07a4596a86c3099cf7f6e7e5c47b71b6 +NS_Incom,ns_incom_inhom_2d_512-237.h5,https://darus.uni-stuttgart.de/api/access/datafile/133616,2D/NS_incom/,4c977f28e59fd8cc877f6d95d96011e5 +NS_Incom,ns_incom_inhom_2d_512-238.h5,https://darus.uni-stuttgart.de/api/access/datafile/133695,2D/NS_incom/,9d3bad375edf84cd86a648caca49b6b5 +NS_Incom,ns_incom_inhom_2d_512-239.h5,https://darus.uni-stuttgart.de/api/access/datafile/136446,2D/NS_incom/,bae42dea9f17c4ca5ec33997e3eb966f +NS_Incom,ns_incom_inhom_2d_512-24.h5,https://darus.uni-stuttgart.de/api/access/datafile/133618,2D/NS_incom/,c7e0bdab4f18748914263534cf21422d +NS_Incom,ns_incom_inhom_2d_512-240.h5,https://darus.uni-stuttgart.de/api/access/datafile/133268,2D/NS_incom/,10b14e2a9eb6fcf5a88a7a5447481aa7 +NS_Incom,ns_incom_inhom_2d_512-241.h5,https://darus.uni-stuttgart.de/api/access/datafile/136447,2D/NS_incom/,0aa745b2377c49379f91f7f000d8f23e +NS_Incom,ns_incom_inhom_2d_512-242.h5,https://darus.uni-stuttgart.de/api/access/datafile/136448,2D/NS_incom/,79033da1fbd3167b560769c8292c15f4 +NS_Incom,ns_incom_inhom_2d_512-243.h5,https://darus.uni-stuttgart.de/api/access/datafile/136450,2D/NS_incom/,896aa796fbf2052da573c30694744794 +NS_Incom,ns_incom_inhom_2d_512-244.h5,https://darus.uni-stuttgart.de/api/access/datafile/136451,2D/NS_incom/,3063dee5e25740331b4c606939c3960e +NS_Incom,ns_incom_inhom_2d_512-245.h5,https://darus.uni-stuttgart.de/api/access/datafile/136457,2D/NS_incom/,daf18bcc864d5db453f7321de3c199bf +NS_Incom,ns_incom_inhom_2d_512-246.h5,https://darus.uni-stuttgart.de/api/access/datafile/136458,2D/NS_incom/,8f97135545fdea8eb504c6f76878b77c +NS_Incom,ns_incom_inhom_2d_512-247.h5,https://darus.uni-stuttgart.de/api/access/datafile/136459,2D/NS_incom/,2e1f4cf38135b2f07a72462e0f995c2d +NS_Incom,ns_incom_inhom_2d_512-248.h5,https://darus.uni-stuttgart.de/api/access/datafile/136460,2D/NS_incom/,edaec61956c5af7be9f8996b8498c8fd +NS_Incom,ns_incom_inhom_2d_512-249.h5,https://darus.uni-stuttgart.de/api/access/datafile/136461,2D/NS_incom/,b05738fc7cdf18fecca88361ba58875f +NS_Incom,ns_incom_inhom_2d_512-25.h5,https://darus.uni-stuttgart.de/api/access/datafile/133619,2D/NS_incom/,d5e37c12c61353533ab82c6c9e48e93f +NS_Incom,ns_incom_inhom_2d_512-250.h5,https://darus.uni-stuttgart.de/api/access/datafile/166273,2D/NS_incom/,c9731467c80b809ce0f3f337ce3ed71e +NS_Incom,ns_incom_inhom_2d_512-251.h5,https://darus.uni-stuttgart.de/api/access/datafile/166274,2D/NS_incom/,3a515e95b641360734702248b3bfcb6d +NS_Incom,ns_incom_inhom_2d_512-252.h5,https://darus.uni-stuttgart.de/api/access/datafile/166275,2D/NS_incom/,283244af2adc86953dd6021bf6722ba4 +NS_Incom,ns_incom_inhom_2d_512-253.h5,https://darus.uni-stuttgart.de/api/access/datafile/166276,2D/NS_incom/,a471e758505de149dba27c9fa2d29198 +NS_Incom,ns_incom_inhom_2d_512-254.h5,https://darus.uni-stuttgart.de/api/access/datafile/166277,2D/NS_incom/,9da97766e3b311af6d79a946f8db5184 +NS_Incom,ns_incom_inhom_2d_512-255.h5,https://darus.uni-stuttgart.de/api/access/datafile/166278,2D/NS_incom/,1965889c070e802965ebe19b01cdf2db +NS_Incom,ns_incom_inhom_2d_512-256.h5,https://darus.uni-stuttgart.de/api/access/datafile/166279,2D/NS_incom/,588ce18cf51ca3517527f3dd0b0ee443 +NS_Incom,ns_incom_inhom_2d_512-257.h5,https://darus.uni-stuttgart.de/api/access/datafile/166281,2D/NS_incom/,44a864a879590427273a3dbf1aa2c190 +NS_Incom,ns_incom_inhom_2d_512-258.h5,https://darus.uni-stuttgart.de/api/access/datafile/166282,2D/NS_incom/,adf37d1dd066c2dc8fb3886369b5434f +NS_Incom,ns_incom_inhom_2d_512-259.h5,https://darus.uni-stuttgart.de/api/access/datafile/166283,2D/NS_incom/,588200460d5168ce1ba582101aa3547e +NS_Incom,ns_incom_inhom_2d_512-26.h5,https://darus.uni-stuttgart.de/api/access/datafile/133689,2D/NS_incom/,563b6261312a32460d848e5b9ca82f79 +NS_Incom,ns_incom_inhom_2d_512-260.h5,https://darus.uni-stuttgart.de/api/access/datafile/166284,2D/NS_incom/,2e1e5883bec2e8e50048738266785bae +NS_Incom,ns_incom_inhom_2d_512-261.h5,https://darus.uni-stuttgart.de/api/access/datafile/166285,2D/NS_incom/,b5f6b09ad77bbcafbc7d52710388669c +NS_Incom,ns_incom_inhom_2d_512-262.h5,https://darus.uni-stuttgart.de/api/access/datafile/166286,2D/NS_incom/,8a8026b2cd2e59a7b1d796dca4404e3a +NS_Incom,ns_incom_inhom_2d_512-263.h5,https://darus.uni-stuttgart.de/api/access/datafile/166287,2D/NS_incom/,8958869c63e7a496816d5c551d4d8115 +NS_Incom,ns_incom_inhom_2d_512-264.h5,https://darus.uni-stuttgart.de/api/access/datafile/166288,2D/NS_incom/,69e4911decfab00ff945884915084104 +NS_Incom,ns_incom_inhom_2d_512-265.h5,https://darus.uni-stuttgart.de/api/access/datafile/166290,2D/NS_incom/,3516776e1d47d573ee91815ca70360b6 +NS_Incom,ns_incom_inhom_2d_512-266.h5,https://darus.uni-stuttgart.de/api/access/datafile/166291,2D/NS_incom/,f50c0e5f4ad659c1b5d6f7343e072bdf +NS_Incom,ns_incom_inhom_2d_512-267.h5,https://darus.uni-stuttgart.de/api/access/datafile/166292,2D/NS_incom/,85a8bd3bd539acb7685393393294e916 +NS_Incom,ns_incom_inhom_2d_512-268.h5,https://darus.uni-stuttgart.de/api/access/datafile/166293,2D/NS_incom/,3b43956b04c4e0e619ba671cdfbbc834 +NS_Incom,ns_incom_inhom_2d_512-269.h5,https://darus.uni-stuttgart.de/api/access/datafile/166294,2D/NS_incom/,1ca4a6528b2d73b55042eb6dbe487ff1 +NS_Incom,ns_incom_inhom_2d_512-27.h5,https://darus.uni-stuttgart.de/api/access/datafile/133620,2D/NS_incom/,3536aace9614abf8868d9c03bbccf9d4 +NS_Incom,ns_incom_inhom_2d_512-270.h5,https://darus.uni-stuttgart.de/api/access/datafile/166295,2D/NS_incom/,8bf9ac2f7dc47e3617ba9de2ac10d582 +NS_Incom,ns_incom_inhom_2d_512-271.h5,https://darus.uni-stuttgart.de/api/access/datafile/166296,2D/NS_incom/,907d7cd2c84312f52f94b27197128d00 +NS_Incom,ns_incom_inhom_2d_512-272.h5,https://darus.uni-stuttgart.de/api/access/datafile/166297,2D/NS_incom/,7d1dc19e1b08c09d86bed9a0c7448ceb +NS_Incom,ns_incom_inhom_2d_512-273.h5,https://darus.uni-stuttgart.de/api/access/datafile/166298,2D/NS_incom/,e5e51712cc8921fcc4e89b9fe4690c7f +NS_Incom,ns_incom_inhom_2d_512-274.h5,https://darus.uni-stuttgart.de/api/access/datafile/166299,2D/NS_incom/,5a3ea4a7cdeade672038ffdbf3a7886a +NS_Incom,ns_incom_inhom_2d_512-28.h5,https://darus.uni-stuttgart.de/api/access/datafile/136462,2D/NS_incom/,aef3e1e349adc36e3d738d7d5534548b +NS_Incom,ns_incom_inhom_2d_512-29.h5,https://darus.uni-stuttgart.de/api/access/datafile/136463,2D/NS_incom/,b5f7a9f28aab2baf06d6129a436dca79 +NS_Incom,ns_incom_inhom_2d_512-3.h5,https://darus.uni-stuttgart.de/api/access/datafile/136466,2D/NS_incom/,e9c92a19854e96c918d3a46d39994be4 +NS_Incom,ns_incom_inhom_2d_512-30.h5,https://darus.uni-stuttgart.de/api/access/datafile/136464,2D/NS_incom/,fa2275aaf761334ec2d80affb10d324f +NS_Incom,ns_incom_inhom_2d_512-31.h5,https://darus.uni-stuttgart.de/api/access/datafile/133621,2D/NS_incom/,a30bc0bb2120dcb42a8335c6d264ac7d +NS_Incom,ns_incom_inhom_2d_512-32.h5,https://darus.uni-stuttgart.de/api/access/datafile/133626,2D/NS_incom/,6579fe51ea46095aa1cd336be4701b34 +NS_Incom,ns_incom_inhom_2d_512-33.h5,https://darus.uni-stuttgart.de/api/access/datafile/133622,2D/NS_incom/,63ee81664764659f3b6436c055981c95 +NS_Incom,ns_incom_inhom_2d_512-34.h5,https://darus.uni-stuttgart.de/api/access/datafile/133623,2D/NS_incom/,e7c32d512bf95b97699c36381fea07ca +NS_Incom,ns_incom_inhom_2d_512-35.h5,https://darus.uni-stuttgart.de/api/access/datafile/133624,2D/NS_incom/,c2715bd899018326e9679775d57e1dfd +NS_Incom,ns_incom_inhom_2d_512-36.h5,https://darus.uni-stuttgart.de/api/access/datafile/136465,2D/NS_incom/,b085ae82d91f9baa2a7f9c8720f5ca48 +NS_Incom,ns_incom_inhom_2d_512-37.h5,https://darus.uni-stuttgart.de/api/access/datafile/133625,2D/NS_incom/,80c39f8aaf4168974f8bde94df177ab6 +NS_Incom,ns_incom_inhom_2d_512-38.h5,https://darus.uni-stuttgart.de/api/access/datafile/133645,2D/NS_incom/,d01ee492fcdb64cd52b7d0ae7b606370 +NS_Incom,ns_incom_inhom_2d_512-39.h5,https://darus.uni-stuttgart.de/api/access/datafile/133630,2D/NS_incom/,3f4f083589ab6d314584fa7ec795669e +NS_Incom,ns_incom_inhom_2d_512-4.h5,https://darus.uni-stuttgart.de/api/access/datafile/166289,2D/NS_incom/,306a56ac14ea5686921cbb3f09c7dcb6 +NS_Incom,ns_incom_inhom_2d_512-40.h5,https://darus.uni-stuttgart.de/api/access/datafile/133627,2D/NS_incom/,c906a76f920bb63d575e347e76e47caf +NS_Incom,ns_incom_inhom_2d_512-41.h5,https://darus.uni-stuttgart.de/api/access/datafile/133696,2D/NS_incom/,b55a275050a22b9c515d9d896dba6da0 +NS_Incom,ns_incom_inhom_2d_512-42.h5,https://darus.uni-stuttgart.de/api/access/datafile/133642,2D/NS_incom/,5aa8b626b64ba979905797b9e56dd34c +NS_Incom,ns_incom_inhom_2d_512-43.h5,https://darus.uni-stuttgart.de/api/access/datafile/133650,2D/NS_incom/,d6ef4cf80d79f696dfc34d4947b14a77 +NS_Incom,ns_incom_inhom_2d_512-44.h5,https://darus.uni-stuttgart.de/api/access/datafile/133686,2D/NS_incom/,b8527daec6934026f5128a6d1f5db1e5 +NS_Incom,ns_incom_inhom_2d_512-45.h5,https://darus.uni-stuttgart.de/api/access/datafile/136467,2D/NS_incom/,7899ab1897fbd4665e4d7daea7c90bc2 +NS_Incom,ns_incom_inhom_2d_512-46.h5,https://darus.uni-stuttgart.de/api/access/datafile/136468,2D/NS_incom/,6853e9a78b6a2b2b756c4e6f8bcdac3e +NS_Incom,ns_incom_inhom_2d_512-47.h5,https://darus.uni-stuttgart.de/api/access/datafile/136469,2D/NS_incom/,4737e1e1283748baae9617ceff1e777a +NS_Incom,ns_incom_inhom_2d_512-48.h5,https://darus.uni-stuttgart.de/api/access/datafile/136470,2D/NS_incom/,5d3abc3ecbddeacb5270030954cc8064 +NS_Incom,ns_incom_inhom_2d_512-5.h5,https://darus.uni-stuttgart.de/api/access/datafile/136471,2D/NS_incom/,6a939d7418593d70faf7f90a191f841d +NS_Incom,ns_incom_inhom_2d_512-50.h5,https://darus.uni-stuttgart.de/api/access/datafile/133678,2D/NS_incom/,f30303f48d036e9aeac07fd70e61c97b +NS_Incom,ns_incom_inhom_2d_512-51.h5,https://darus.uni-stuttgart.de/api/access/datafile/133659,2D/NS_incom/,e82c2adbbc22b59168b7b587012d296d +NS_Incom,ns_incom_inhom_2d_512-52.h5,https://darus.uni-stuttgart.de/api/access/datafile/133657,2D/NS_incom/,1a072500b92f728f24526182a8e22d71 +NS_Incom,ns_incom_inhom_2d_512-53.h5,https://darus.uni-stuttgart.de/api/access/datafile/133670,2D/NS_incom/,e82c2adbbc22b59168b7b587012d296d +NS_Incom,ns_incom_inhom_2d_512-54.h5,https://darus.uni-stuttgart.de/api/access/datafile/133672,2D/NS_incom/,3516776e1d47d573ee91815ca70360b6 +NS_Incom,ns_incom_inhom_2d_512-55.h5,https://darus.uni-stuttgart.de/api/access/datafile/133662,2D/NS_incom/,49732762c231a410904cf8bddb0444cd +NS_Incom,ns_incom_inhom_2d_512-56.h5,https://darus.uni-stuttgart.de/api/access/datafile/133673,2D/NS_incom/,f50c0e5f4ad659c1b5d6f7343e072bdf +NS_Incom,ns_incom_inhom_2d_512-57.h5,https://darus.uni-stuttgart.de/api/access/datafile/133674,2D/NS_incom/,1a072500b92f728f24526182a8e22d71 +NS_Incom,ns_incom_inhom_2d_512-58.h5,https://darus.uni-stuttgart.de/api/access/datafile/133658,2D/NS_incom/,ae1e262d7f1a56369b13b6d5de80e03d +NS_Incom,ns_incom_inhom_2d_512-59.h5,https://darus.uni-stuttgart.de/api/access/datafile/133656,2D/NS_incom/,b99c5b112d4a7dfca053cb2c78ebe581 +NS_Incom,ns_incom_inhom_2d_512-6.h5,https://darus.uni-stuttgart.de/api/access/datafile/136472,2D/NS_incom/,36156ac1f8cecb47d076a1ee0707dec5 +NS_Incom,ns_incom_inhom_2d_512-60.h5,https://darus.uni-stuttgart.de/api/access/datafile/133675,2D/NS_incom/,85a8bd3bd539acb7685393393294e916 +NS_Incom,ns_incom_inhom_2d_512-61.h5,https://darus.uni-stuttgart.de/api/access/datafile/133676,2D/NS_incom/,3b43956b04c4e0e619ba671cdfbbc834 +NS_Incom,ns_incom_inhom_2d_512-62.h5,https://darus.uni-stuttgart.de/api/access/datafile/133677,2D/NS_incom/,8bf9ac2f7dc47e3617ba9de2ac10d582 +NS_Incom,ns_incom_inhom_2d_512-63.h5,https://darus.uni-stuttgart.de/api/access/datafile/133680,2D/NS_incom/,49732762c231a410904cf8bddb0444cd +NS_Incom,ns_incom_inhom_2d_512-64.h5,https://darus.uni-stuttgart.de/api/access/datafile/133628,2D/NS_incom/,c0e54ed459c84470986818d6d89be0c8 +NS_Incom,ns_incom_inhom_2d_512-65.h5,https://darus.uni-stuttgart.de/api/access/datafile/133635,2D/NS_incom/,69f1b9013bb76fba1006272951e12845 +NS_Incom,ns_incom_inhom_2d_512-66.h5,https://darus.uni-stuttgart.de/api/access/datafile/133629,2D/NS_incom/,c79245b195ec850a40c0957056bffad7 +NS_Incom,ns_incom_inhom_2d_512-67.h5,https://darus.uni-stuttgart.de/api/access/datafile/133277,2D/NS_incom/,f8465b71d9afe577ba30cbe5d0c490e6 +NS_Incom,ns_incom_inhom_2d_512-68.h5,https://darus.uni-stuttgart.de/api/access/datafile/133681,2D/NS_incom/,ae1e262d7f1a56369b13b6d5de80e03d +NS_Incom,ns_incom_inhom_2d_512-69.h5,https://darus.uni-stuttgart.de/api/access/datafile/133631,2D/NS_incom/,b5159c70d9b14e480bb520961bafcae4 +NS_Incom,ns_incom_inhom_2d_512-7.h5,https://darus.uni-stuttgart.de/api/access/datafile/136473,2D/NS_incom/,e1b06fd07d09227a0767baa260f361e2 +NS_Incom,ns_incom_inhom_2d_512-70.h5,https://darus.uni-stuttgart.de/api/access/datafile/133632,2D/NS_incom/,3aee2aa9b1a14c7d04ce2770e95a354a +NS_Incom,ns_incom_inhom_2d_512-71.h5,https://darus.uni-stuttgart.de/api/access/datafile/133634,2D/NS_incom/,2f5b2bf56e72d61c41d4b056ac540fb0 +NS_Incom,ns_incom_inhom_2d_512-72.h5,https://darus.uni-stuttgart.de/api/access/datafile/133682,2D/NS_incom/,b99c5b112d4a7dfca053cb2c78ebe581 +NS_Incom,ns_incom_inhom_2d_512-73.h5,https://darus.uni-stuttgart.de/api/access/datafile/133273,2D/NS_incom/,9d1c5e98ee4fe97c7498d2f574c165ae +NS_Incom,ns_incom_inhom_2d_512-74.h5,https://darus.uni-stuttgart.de/api/access/datafile/133633,2D/NS_incom/,475427b7337f1fd7114b7e6c32a2f709 +NS_Incom,ns_incom_inhom_2d_512-75.h5,https://darus.uni-stuttgart.de/api/access/datafile/133694,2D/NS_incom/,170b32d051ed72716aab0f40b13f359e +NS_Incom,ns_incom_inhom_2d_512-76.h5,https://darus.uni-stuttgart.de/api/access/datafile/133636,2D/NS_incom/,2666826a3944f5999a1053242e9588a9 +NS_Incom,ns_incom_inhom_2d_512-77.h5,https://darus.uni-stuttgart.de/api/access/datafile/133638,2D/NS_incom/,c58cc6dd09fb89c1b5fb95081f1220c9 +NS_Incom,ns_incom_inhom_2d_512-78.h5,https://darus.uni-stuttgart.de/api/access/datafile/133637,2D/NS_incom/,b183a32a3e2230968d5b7f20d9dbf818 +NS_Incom,ns_incom_inhom_2d_512-79.h5,https://darus.uni-stuttgart.de/api/access/datafile/133651,2D/NS_incom/,def3145be356b7d020ea09e5232bb60f +NS_Incom,ns_incom_inhom_2d_512-8.h5,https://darus.uni-stuttgart.de/api/access/datafile/136449,2D/NS_incom/,2e6a62550680a255d4dfec24a761d5cf +NS_Incom,ns_incom_inhom_2d_512-80.h5,https://darus.uni-stuttgart.de/api/access/datafile/133639,2D/NS_incom/,07a1edc16fb7d981155ad4173c42abf1 +NS_Incom,ns_incom_inhom_2d_512-81.h5,https://darus.uni-stuttgart.de/api/access/datafile/133276,2D/NS_incom/,3a58395f316b2158ea61844eadd4109f +NS_Incom,ns_incom_inhom_2d_512-82.h5,https://darus.uni-stuttgart.de/api/access/datafile/133640,2D/NS_incom/,a0f4505418beb4da8b7f84a83efc3904 +NS_Incom,ns_incom_inhom_2d_512-83.h5,https://darus.uni-stuttgart.de/api/access/datafile/133272,2D/NS_incom/,89993b4f3ac2653f6377941e640e2233 +NS_Incom,ns_incom_inhom_2d_512-84.h5,https://darus.uni-stuttgart.de/api/access/datafile/133643,2D/NS_incom/,c5269398ec4edb0f5891d4efdb2fc89d +NS_Incom,ns_incom_inhom_2d_512-85.h5,https://darus.uni-stuttgart.de/api/access/datafile/133641,2D/NS_incom/,090862c29ecbc2c4232ddbacca3fb230 +NS_Incom,ns_incom_inhom_2d_512-86.h5,https://darus.uni-stuttgart.de/api/access/datafile/133647,2D/NS_incom/,a712449bef87318a620d1663c4ffde27 +NS_Incom,ns_incom_inhom_2d_512-87.h5,https://darus.uni-stuttgart.de/api/access/datafile/133644,2D/NS_incom/,920a3dcb3b216f0d9a4ab0b9b4c8745a +NS_Incom,ns_incom_inhom_2d_512-88.h5,https://darus.uni-stuttgart.de/api/access/datafile/133655,2D/NS_incom/,cc96b1e357f264f12dcfbb41736a53bf +NS_Incom,ns_incom_inhom_2d_512-89.h5,https://darus.uni-stuttgart.de/api/access/datafile/133646,2D/NS_incom/,7fd484851410b7139bfea4166074fe00 +NS_Incom,ns_incom_inhom_2d_512-9.h5,https://darus.uni-stuttgart.de/api/access/datafile/136475,2D/NS_incom/,254bb44710b625f716b61d51d5d1e58e +NS_Incom,ns_incom_inhom_2d_512-90.h5,https://darus.uni-stuttgart.de/api/access/datafile/133652,2D/NS_incom/,c6c4e27d6ba19b4ccb5f27e67dd9cdf1 +NS_Incom,ns_incom_inhom_2d_512-91.h5,https://darus.uni-stuttgart.de/api/access/datafile/136474,2D/NS_incom/,527b23b7e4ca4d18c5c036c41e15e4fe +NS_Incom,ns_incom_inhom_2d_512-92.h5,https://darus.uni-stuttgart.de/api/access/datafile/133648,2D/NS_incom/,84fd5f64db474f8e20b670d01929d57a +NS_Incom,ns_incom_inhom_2d_512-93.h5,https://darus.uni-stuttgart.de/api/access/datafile/133649,2D/NS_incom/,20583d7edf7da9a80f16a26c8388a65e +NS_Incom,ns_incom_inhom_2d_512-94.h5,https://darus.uni-stuttgart.de/api/access/datafile/133691,2D/NS_incom/,8540e7f1cc6a9e2bc64726a325ddacfb +NS_Incom,ns_incom_inhom_2d_512-95.h5,https://darus.uni-stuttgart.de/api/access/datafile/133690,2D/NS_incom/,25d4c08d5534d0f10c877220e258640c +NS_Incom,ns_incom_inhom_2d_512-96.h5,https://darus.uni-stuttgart.de/api/access/datafile/133270,2D/NS_incom/,93e772cbfd36d434e97a079d701d5382 +NS_Incom,ns_incom_inhom_2d_512-97.h5,https://darus.uni-stuttgart.de/api/access/datafile/133653,2D/NS_incom/,ffa1d46e58f67aa1932fc4ffd2e7716a +NS_Incom,ns_incom_inhom_2d_512-98.h5,https://darus.uni-stuttgart.de/api/access/datafile/133687,2D/NS_incom/,c28112e1339cda43f67dce03536e3455 +NS_Incom,ns_incom_inhom_2d_512-99.h5,https://darus.uni-stuttgart.de/api/access/datafile/133654,2D/NS_incom/,abfe923a224cb4515dd545d5858b2124 +SWE,2D_rdb_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133021,2D/shallow-water/,75d838c47aa410694bdc912ea7f22282 +3D_CFD,3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164693,3D/Train/,45892a12d1066d54af74badae55c438e +3D_CFD,3D_CFD_Turb_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/164694,3D/Train/,4f0eacaec5ff000bbd9a31026ea207b8 +3D_CFD,BlastWave.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133264,3D/Test/BlastWave/,f901a1ec75925c1d861ac99a825878f3 +3D_CFD,Turb_M01.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133224,3D/Test/Turbulence/,b27495031f434d01762bb0b819ac71e2 +3D_CFD,Turb_M05.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133225,3D/Test/Turbulence/,f9407dff1a75a1d14d93e7dd570af728 +3D_CFD,Turb_M1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135833,3D/Test/Turbulence/,3758f23f71684ac666e0b1e91da0a1c4 +3D_CFD,Turb_M2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133227,3D/Test/Turbulence/,12e528dc8ab800f69474600ec58b24d3 +3D_CFD,Turb_M4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133228,3D/Test/Turbulence/,8db384feba75903a8c5b21ebeba40083 \ No newline at end of file From 851fbf24be259ad87e2289f244c0bdf5ee233f75 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 11 May 2024 11:20:36 +0200 Subject: [PATCH 106/137] add vcnef citation details --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed5f962..05af20a 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ Below is an illustration of the directory structure of PDEBench. ------ -## Citations +## Publications & Citations Please cite the following papers if you use PDEBench datasets and/or source code in your research. @@ -323,7 +323,6 @@ booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2 url = {https://arxiv.org/abs/2210.07182} } ``` - @@ -366,7 +365,22 @@ url = {https://doi.org/10.18419/darus-2986} eprint = {2304.14118}, } ``` + + +
+ + Vectorized Conditional Neural Fields: A Framework for Solving Time-dependent Parametric Partial Differential Equations - ICLR-W'2024 & ICML'2024 + +
+ ``` +@inproceedings{vcnef-vectorized-conditional-neural-fields-hagnberger:2024, +author = {Hagnberger, Jan and Kalimuthu, Marimuthu and Musekamp, Daniel and Niepert, Mathias}, +title = {{Vectorized Conditional Neural Fields: A Framework for Solving Time-dependent Parametric Partial Differential Equations}}, +year = {2024}, +booktitle = {Proceedings of the 41st International Conference on Machine Learning (ICML 2024)} +} + ```
------ From 7acb9945dfb7d714e2cfd1b7ec3eb2db2c20e6c8 Mon Sep 17 00:00:00 2001 From: Mario Date: Tue, 14 May 2024 20:27:54 +0200 Subject: [PATCH 107/137] remove extra dimensions in data merge --- pdebench/data_gen/data_gen_NLE/Data_Merge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py index 6cfe4b4..cd10dbc 100644 --- a/pdebench/data_gen/data_gen_NLE/Data_Merge.py +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -330,7 +330,7 @@ def transform(type, savedir): with h5py.File(flnm[:-3]+'hdf5', 'w') as f: print(flnm) _data = np.load(flnm) - f.create_dataset('tensor', data = _data.astype(np.float32)) + f.create_dataset('tensor', data = _data.astype(np.float32).squeeze(axis=(0,))) # clear leading singleton dimension generated by JAX f.create_dataset('x-coordinate', data = xcrd) f.create_dataset('t-coordinate', data = tcrd) if type=='advection': @@ -362,4 +362,4 @@ def main(cfg: DictConfig) -> None: merge(type=cfg.args.type, dim=cfg.args.dim, bd=cfg.args.bd, nbatch=cfg.args.nbatch, savedir=cfg.args.savedir) if __name__=='__main__': - main() \ No newline at end of file + main() From 7b2c3a261c99070b2989af69cde7fe1f26cb2232 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 2 Oct 2024 20:06:44 +0200 Subject: [PATCH 108/137] Update README.md --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 05af20a..1af9ea9 100644 --- a/README.md +++ b/README.md @@ -383,6 +383,31 @@ booktitle = {Proceedings of the 41st International Conference on Machine Learnin ``` +
+ + Active Learning for Neural PDE Solvers - NeurIPS-W'2024 + +
+ + ``` +@article{active-learn-neuralpde-benchmark-musekamp:2024, + author = {Daniel Musekamp and + Marimuthu Kalimuthu and + David Holzm{\"{u}}ller and + Makoto Takamoto and + Mathias Niepert}, + title = {Active Learning for Neural {PDE} Solvers}, + journal = {CoRR}, + volume = {abs/2408.01536}, + year = {2024}, + url = {https://doi.org/10.48550/arXiv.2408.01536}, + doi = {10.48550/ARXIV.2408.01536}, + eprinttype = {arXiv}, + eprint = {2408.01536}, +} + ``` +
+ ------ ## Code contributors @@ -393,7 +418,7 @@ booktitle = {Proceedings of the 41st International Conference on Machine Learnin * [Raphael Leiteritz](https://github.com/leiterrl) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) * [Francesco Alesiani](https://github.com/falesiani) ([NEC laboratories Europe](https://www.neclab.eu/)) * [Dan MacKinlay](https://danmackinlay.name/) ([CSIRO’s Data61](https://data61.csiro.au/)) -* [Mario Kalimuthu](https://github.com/kmario23) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +* [Marimuthu Kalimuthu](https://github.com/kmario23) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) * [John Kim](https://github.com/johnmjkim) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) * [Gefei Shan](https://github.com/davecatmeow) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) * [Yizhou Yang](https://github.com/verdantwynnd) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) From b470a487fc37644c79d1c34d632d3360eddee357 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Wed, 2 Oct 2024 21:35:26 +0000 Subject: [PATCH 109/137] reshape jnp arrays before saving, check dir --- .../AdvectionEq/advection_multi_solution_Hydra.py | 7 +++++++ .../data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml | 2 +- .../BurgersEq/burgers_multi_solution_Hydra.py | 6 ++++++ .../data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml | 2 +- pdebench/data_gen/data_gen_NLE/Data_Merge.py | 8 ++++---- .../ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml | 2 +- .../reaction_diffusion_multi_solution_Hydra.py | 7 ++++++- pdebench/data_gen/data_gen_NLE/config/config.yaml | 4 ++-- 8 files changed, 28 insertions(+), 10 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py index db45e96..19f6a12 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -148,6 +148,7 @@ import sys from math import ceil, log, exp import random +from pathlib import Path # Hydra from omegaconf import DictConfig, OmegaConf @@ -273,7 +274,13 @@ def flux(u): vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') local_devices = jax.local_device_count() uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + + # reshape before saving + uu = uu.reshape((-1, *uu.shape[2:])) + + print('data saving...') cwd = hydra.utils.get_original_cwd() + '/' + Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) jnp.save(cwd+cfg.multi.save+'1D_Advection_Sols_beta'+str(beta)[:5], uu) jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml index 93af1e3..4d7d9bd 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml @@ -12,4 +12,4 @@ CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py index cf84034..da17c10 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -148,6 +148,7 @@ import sys import random from math import ceil, exp, log +from pathlib import Path # Hydra from omegaconf import DictConfig, OmegaConf @@ -277,7 +278,12 @@ def flux(u): local_devices = jax.local_device_count() uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + # reshape before saving + uu = uu.reshape((-1, *uu.shape[2:])) + + print('data saving...') cwd = hydra.utils.get_original_cwd() + '/' + Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) jnp.save(cwd+cfg.multi.save+'1D_Burgers_Sols_Nu'+str(epsilon)[:5], uu) jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml index 7b17380..bf78a94 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py index cd10dbc..ddfa35a 100644 --- a/pdebench/data_gen/data_gen_NLE/Data_Merge.py +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -330,23 +330,23 @@ def transform(type, savedir): with h5py.File(flnm[:-3]+'hdf5', 'w') as f: print(flnm) _data = np.load(flnm) - f.create_dataset('tensor', data = _data.astype(np.float32).squeeze(axis=(0,))) # clear leading singleton dimension generated by JAX + f.create_dataset('tensor', data = _data.astype(np.float32)) f.create_dataset('x-coordinate', data = xcrd) f.create_dataset('t-coordinate', data = tcrd) if type=='advection': beta = float(flnm.split('/')[-1].split('_')[3][4:-4]) # advection train - print(beta) + print(f"beta: {beta}") f.attrs['beta'] = beta elif type=='burgers': Nu = float(flnm.split('/')[-1].split('_')[-1][2:-4]) # Burgers test/train - print(Nu) + print(f"Nu: {Nu}") f.attrs['Nu'] = Nu elif type=='ReacDiff': Rho = float(flnm.split('/')[-1].split('_')[-1][3:-4]) # reac-diff test Nu = float(flnm.split('/')[-1].split('_')[-2][2:]) # reac-diff test - print(Nu, Rho) + print(f"Nu, rho: {Nu, Rho}") f.attrs['Nu'] = Nu f.attrs['rho'] = Rho diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml index 572d0ff..7eb228f 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py index f279eee..1dcec4e 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py @@ -149,6 +149,7 @@ import sys import random from math import ceil, exp, log +from pathlib import Path # Hydra from omegaconf import DictConfig, OmegaConf @@ -284,9 +285,13 @@ def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method local_devices = jax.local_device_count() uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + # reshape based on device count + uu = uu.reshape((-1, *uu.shape[2:])) + print('data saving...') cwd = hydra.utils.get_original_cwd() + '/' - jnp.save(cwd + cfg.multi.save+'/ReacDiff_Nu'+str(nu)[:5]+'_Rho'+str(rho)[:5], uu) + Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) + jnp.save(cwd + cfg.multi.save+'ReacDiff_Nu'+str(nu)[:5]+'_Rho'+str(rho)[:5], uu) jnp.save(cwd + cfg.multi.save+'/x_coordinate', xc) jnp.save(cwd + cfg.multi.save+'/t_coordinate', tc) diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index f43418b..b379b19 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -9,8 +9,8 @@ hydra: dir: . args: - type: 'advection' # 'advection'/'ReacDiff'/'burgers'/'CFD' + type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 bd: 'periodic' nbatch: 1000 - savedir: './save/CFD/' \ No newline at end of file + savedir: './save/ReacDiff/' From 219e5447341e550c7fdeda0e134658d5902b4e4c Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 3 Oct 2024 00:06:16 +0200 Subject: [PATCH 110/137] update steps for 1D data generation --- pdebench/data_gen/data_gen_NLE/README.md | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/pdebench/data_gen/data_gen_NLE/README.md b/pdebench/data_gen/data_gen_NLE/README.md index 7b5ff70..b0ce900 100644 --- a/pdebench/data_gen/data_gen_NLE/README.md +++ b/pdebench/data_gen/data_gen_NLE/README.md @@ -23,3 +23,62 @@ dim: 2 python data_gen/data_gen_NLE/Data_Merge.py ``` +---------------------------- + +#### Data generation for 1D Advection Equation: + +``` +# generate data and save as .npy array +cd PDEBench/pdebench/data_gen/data_gen_NLE/AdvectionEq + +# Either generate a single file +CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta1e0.yaml + +# Or generate all files +bash run_trainset.sh + +# serialize to hdf5 by transforming npy file +# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml +cd .. +python Data_Merge.py +``` + +-------------- + +#### Data generation for 1D Burgers' Equation: + +``` +# generate data and save as .npy array +cd PDEBench/pdebench/data_gen/data_gen_NLE/BurgersEq/ + +# Either generate a single file +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-1.yaml + +# Or generate all files +bash run_trainset.sh + +# serialize to hdf5 by transforming npy file +# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml +cd .. +python Data_Merge.py +``` + +--------------- + +#### Data generation for 1D Reaction Diffusion Equation: + +``` +# generate data and save as .npy array +cd PDEBench/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/ + +# Either generate a single file +CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho2e0_Nu5e0.yaml + +# Or generate all files +bash run_trainset.sh + +# serialize to hdf5 by transforming npy file +# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml +cd .. +python Data_Merge.py +``` From b11eab3870466b996da27807c64f6e40a8185916 Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 3 Oct 2024 00:13:28 +0200 Subject: [PATCH 111/137] update steps for 1D PDE data generation --- pdebench/data_gen/data_gen_NLE/README.md | 34 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/README.md b/pdebench/data_gen/data_gen_NLE/README.md index b0ce900..eabacd8 100644 --- a/pdebench/data_gen/data_gen_NLE/README.md +++ b/pdebench/data_gen/data_gen_NLE/README.md @@ -36,9 +36,18 @@ CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta # Or generate all files bash run_trainset.sh +``` + +- Update `data_gen/data_gen_NLE/config/config.yaml` to: + +```yaml +type: 'advection' # 'advection'/'ReacDiff'/'burgers'/'CFD' +dim: 1 +savedir: './save/advection' +``` +``` # serialize to hdf5 by transforming npy file -# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml cd .. python Data_Merge.py ``` @@ -56,9 +65,19 @@ CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-1.y # Or generate all files bash run_trainset.sh +``` + +- Update `data_gen/data_gen_NLE/config/config.yaml` to: +```yaml +type: 'burgers' # 'advection'/'ReacDiff'/'burgers'/'CFD' +dim: 1 +savedir: './save/burgers' +``` + + +``` # serialize to hdf5 by transforming npy file -# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml cd .. python Data_Merge.py ``` @@ -76,9 +95,18 @@ CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +m # Or generate all files bash run_trainset.sh +``` + +- Update `data_gen/data_gen_NLE/config/config.yaml` to: +```yaml +type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' +dim: 1 +savedir: './save/ReacDiff' +``` + +``` # serialize to hdf5 by transforming npy file -# before executing Data_Merge.py, change "type" and "savedir" values in PDEBench/pdebench/data_gen/data_gen_NLE/config/config.yaml cd .. python Data_Merge.py ``` From e58e6982b7b122d3451eb3b7314498fecc5bfd13 Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 4 Oct 2024 01:26:47 +0200 Subject: [PATCH 112/137] update grid var to post subsampling --- pdebench/models/fno/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 6ad16af..03f45eb 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -351,7 +351,7 @@ def __init__(self, filename, gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) mgridX, mgridY = np.meshgrid(gridx, gridy, indexing='ij') _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) - grid = _grid[::reduced_resolution, ::reduced_resolution, ...] + _grid = _grid[::reduced_resolution, ::reduced_resolution, ...] _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) tsteps_t = _tsteps_t[::reduced_resolution_t] self.data = _data From 9174a50567bf2679e1e1b892ab611b3801ca495f Mon Sep 17 00:00:00 2001 From: Gerhard Reinerth Date: Sun, 20 Oct 2024 15:51:36 +0200 Subject: [PATCH 113/137] =?UTF-8?q?(feature):=20Approximate=20vorticity=20?= =?UTF-8?q?fields=20using=20velocity=20data.=20Include=20=E2=80=A6=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (feature): Approximate vorticity fields using velocity data. Include github ci/cd stuff and code quality ensuring stuff * (fix): included missing files * (fix): included missing file _version.pyi * (chore): further cleaning of code * (fix): fixing import error in module vorticity2velocity --- .devcontainer/devcontainer.json | 24 + .github/dependabot.yml | 12 + .github/workflows/precommit.yml | 14 + .github/workflows/python-package.yml | 33 + .github/workflows/python-publish.yml | 38 + .pre-commit-config.yaml | 88 ++ LICENSE.txt | 2 +- README.md | 386 +++-- pdebench/__init__.py | 5 +- pdebench/_version.pyi | 4 + pdebench/data_download/README.md | 56 +- pdebench/data_download/config/config.yaml | 10 +- pdebench/data_download/download_direct.py | 6 +- .../data_download/download_easydataverse.py | 13 +- pdebench/data_download/pdebench_data_urls.csv | 2 +- pdebench/data_download/visualize_pdes.py | 166 ++- pdebench/data_gen/configs/diff-react.yaml | 28 +- pdebench/data_gen/configs/diff-sorp.yaml | 28 +- pdebench/data_gen/configs/mode/debug.yaml | 1 - pdebench/data_gen/configs/mode/default.yaml | 1 - pdebench/data_gen/configs/mode/slurm.yaml | 1 - pdebench/data_gen/configs/ns_incomp.yaml | 25 +- .../data_gen/configs/radial_dam_break.yaml | 23 +- .../AdvectionEq/advection_exact_Hydra.py | 33 +- .../advection_multi_solution_Hydra.py | 64 +- .../AdvectionEq/config/args/beta1e-1.yaml | 6 +- .../AdvectionEq/config/args/beta1e0.yaml | 6 +- .../AdvectionEq/config/args/beta1e1.yaml | 6 +- .../AdvectionEq/config/args/beta2e-1.yaml | 6 +- .../AdvectionEq/config/args/beta2e0.yaml | 6 +- .../AdvectionEq/config/args/beta4e-1.yaml | 6 +- .../AdvectionEq/config/args/beta4e0.yaml | 6 +- .../AdvectionEq/config/args/config.yaml | 6 +- .../AdvectionEq/config/multi/beta1e-1.yaml | 6 +- .../AdvectionEq/config/multi/beta1e0.yaml | 4 +- .../AdvectionEq/config/multi/beta2e-1.yaml | 6 +- .../AdvectionEq/config/multi/beta2e0.yaml | 6 +- .../AdvectionEq/config/multi/beta4e-1.yaml | 6 +- .../AdvectionEq/config/multi/beta4e0.yaml | 6 +- .../AdvectionEq/config/multi/config.yaml | 4 +- .../AdvectionEq/config/multi/config2D.yaml | 6 +- .../data_gen_NLE/AdvectionEq/run_testset.sh | 3 +- .../data_gen_NLE/AdvectionEq/run_trainset.sh | 1 + .../data_gen_NLE/BurgersEq/burgers_Hydra.py | 103 +- .../BurgersEq/burgers_multi_solution_Hydra.py | 87 +- .../BurgersEq/config/args/config.yaml | 10 +- .../BurgersEq/config/args/possin_eps1e-1.yaml | 8 +- .../BurgersEq/config/args/possin_eps1e-2.yaml | 8 +- .../BurgersEq/config/args/possin_eps1e-3.yaml | 8 +- .../BurgersEq/config/args/possin_eps1e0.yaml | 8 +- .../BurgersEq/config/args/possin_eps1e1.yaml | 8 +- .../BurgersEq/config/args/possin_eps1e2.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e-1.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e-2.yaml | 8 +- .../config/args/sin_eps1e-2_u01e-1.yaml | 8 +- .../config/args/sin_eps1e-2_u01e-2.yaml | 8 +- .../config/args/sin_eps1e-2_u01e1.yaml | 8 +- .../config/args/sin_eps1e-2_u01e2.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e-3.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e0.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e1.yaml | 8 +- .../BurgersEq/config/args/sin_eps1e2.yaml | 8 +- .../config/args/sinsin_eps1e-2_du01.yaml | 8 +- .../config/args/sinsin_eps1e-2_du025.yaml | 8 +- .../config/args/sinsin_eps1e-2_du05.yaml | 8 +- .../config/args/sinsin_eps1e-2_du1.yaml | 8 +- .../config/args/sinsin_eps1e-2_du2.yaml | 8 +- .../config/args/sinsin_eps1e-2_du5.yaml | 8 +- .../BurgersEq/config/multi/1e-1.yaml | 4 +- .../BurgersEq/config/multi/1e-2.yaml | 4 +- .../BurgersEq/config/multi/1e-3.yaml | 4 +- .../BurgersEq/config/multi/1e0.yaml | 2 +- .../BurgersEq/config/multi/2e-1.yaml | 4 +- .../BurgersEq/config/multi/2e-2.yaml | 4 +- .../BurgersEq/config/multi/2e-3.yaml | 4 +- .../BurgersEq/config/multi/2e0.yaml | 4 +- .../BurgersEq/config/multi/4e-1.yaml | 4 +- .../BurgersEq/config/multi/4e-2.yaml | 4 +- .../BurgersEq/config/multi/4e-3.yaml | 4 +- .../BurgersEq/config/multi/4e0.yaml | 4 +- .../BurgersEq/config/multi/config.yaml | 2 +- .../data_gen_NLE/BurgersEq/run_testset.sh | 3 +- .../data_gen_NLE/BurgersEq/run_trainset.sh | 3 +- .../CompressibleFluid/CFD_Hydra.py | 369 +++-- .../CompressibleFluid/CFD_multi_Hydra.py | 697 ++++++--- .../config/args/1D_Multi.yaml | 8 +- .../config/args/1D_Multi_shock.yaml | 8 +- .../config/args/1D_Multi_trans.yaml | 8 +- .../config/args/1D_ShockTube.yaml | 8 +- .../config/args/1D_ShockTube2.yaml | 8 +- .../config/args/1D_ShockTube3.yaml | 8 +- .../config/args/1D_ShockTube4.yaml | 8 +- .../config/args/1D_ShockTube5.yaml | 8 +- .../config/args/1D_ShockTube6.yaml | 8 +- .../config/args/1D_ShockTube7.yaml | 8 +- .../config/args/2D_KH_M01_dk1.yaml | 8 +- .../config/args/2D_KH_M01_dk10.yaml | 10 +- .../config/args/2D_KH_M01_dk2.yaml | 8 +- .../config/args/2D_KH_M01_dk5.yaml | 8 +- .../config/args/2D_KH_M02_dk1.yaml | 8 +- .../config/args/2D_KH_M04_dk1.yaml | 8 +- .../config/args/2D_KH_M1_dk1.yaml | 8 +- .../config/args/2D_KH_M2_dk1.yaml | 12 +- .../config/args/2D_Multi_KH.yaml | 10 +- .../config/args/2D_Multi_Rand.yaml | 8 +- .../config/args/2D_Multi_Rand_HR.yaml | 8 +- .../config/args/2D_Multi_Turb.yaml | 10 +- .../config/args/2D_ShockTube.yaml | 8 +- .../CompressibleFluid/config/args/2D_TOV.yaml | 8 +- .../config/args/3D_BlastWave.yaml | 8 +- .../config/args/3D_Multi_Rand.yaml | 10 +- .../config/args/3D_Multi_TurbM1.yaml | 10 +- .../config/args/3D_TurbM01.yaml | 8 +- .../config/args/3D_TurbM05.yaml | 8 +- .../config/args/3D_TurbM1.yaml | 8 +- .../config/args/3D_TurbM2.yaml | 8 +- .../config/args/3D_TurbM4.yaml | 8 +- .../config/args/default.yaml | 10 +- .../CompressibleFluid/config/config.yaml | 2 +- .../CompressibleFluid/run_testset.sh | 4 +- .../CompressibleFluid/run_testset_3DTurb.sh | 2 +- .../CompressibleFluid/run_testset_KHI.sh | 1 + .../CompressibleFluid/run_trainset_1D.sh | 9 +- .../CompressibleFluid/run_trainset_1DShock.sh | 9 +- .../run_trainset_1D_trans.sh | 9 +- .../CompressibleFluid/run_trainset_2D.sh | 11 +- .../CompressibleFluid/run_trainset_2DTurb.sh | 11 +- .../CompressibleFluid/run_trainset_3D.sh | 9 +- .../CompressibleFluid/run_trainset_3DTurb.sh | 9 +- pdebench/data_gen/data_gen_NLE/Data_Merge.py | 329 +++-- pdebench/data_gen/data_gen_NLE/README.md | 24 +- .../config/args/Rho1e0_Nu1e0.yaml | 6 +- .../config/args/Rho1e0_Nu2e0.yaml | 6 +- .../config/args/Rho1e0_Nu5e-1.yaml | 6 +- .../config/args/Rho1e0_Nu5e0.yaml | 6 +- .../config/args/Rho1e1_Nu1e0.yaml | 6 +- .../config/args/Rho1e1_Nu2e0.yaml | 6 +- .../config/args/Rho1e1_Nu5e-1.yaml | 6 +- .../config/args/Rho1e1_Nu5e0.yaml | 6 +- .../config/args/Rho2e0_Nu1e0.yaml | 6 +- .../config/args/Rho2e0_Nu2e0.yaml | 6 +- .../config/args/Rho2e0_Nu5e-1.yaml | 6 +- .../config/args/Rho2e0_Nu5e0.yaml | 6 +- .../config/args/Rho5e0_Nu1e0.yaml | 6 +- .../config/args/Rho5e0_Nu2e0.yaml | 6 +- .../config/args/Rho5e0_Nu5e-1.yaml | 6 +- .../config/args/Rho5e0_Nu5e0.yaml | 6 +- .../config/args/config.yaml | 6 +- .../config/multi/Rho1e0_Nu1e0.yaml | 2 +- .../config/multi/Rho1e0_Nu1e1.yaml | 4 +- .../config/multi/Rho1e0_Nu2e0.yaml | 4 +- .../config/multi/Rho1e0_Nu5e-1.yaml | 4 +- .../config/multi/Rho1e0_Nu5e0.yaml | 4 +- .../config/multi/Rho1e1_Nu1e0.yaml | 4 +- .../config/multi/Rho1e1_Nu1e1.yaml | 4 +- .../config/multi/Rho1e1_Nu2e0.yaml | 4 +- .../config/multi/Rho1e1_Nu5e-1.yaml | 4 +- .../config/multi/Rho1e1_Nu5e0.yaml | 4 +- .../config/multi/Rho2e0_Nu1e0.yaml | 4 +- .../config/multi/Rho2e0_Nu1e1.yaml | 4 +- .../config/multi/Rho2e0_Nu2e0.yaml | 4 +- .../config/multi/Rho2e0_Nu5e-1.yaml | 4 +- .../config/multi/Rho2e0_Nu5e0.yaml | 4 +- .../config/multi/Rho5e0_Nu1e0.yaml | 4 +- .../config/multi/Rho5e0_Nu1e1.yaml | 4 +- .../config/multi/Rho5e0_Nu2e0.yaml | 4 +- .../config/multi/Rho5e0_Nu5e-1.yaml | 4 +- .../config/multi/Rho5e0_Nu5e0.yaml | 4 +- .../config/multi/config.yaml | 2 +- .../config/multi/config_2D.yaml | 4 +- ...ction_diffusion_2D_multi_solution_Hydra.py | 129 +- .../reaction_diffusion_Hydra.py | 76 +- ...reaction_diffusion_multi_solution_Hydra.py | 82 +- .../ReactionDiffusionEq/run_DarcyFlow2D.sh | 11 +- .../ReactionDiffusionEq/run_testset.sh | 1 + .../ReactionDiffusionEq/run_trainset.sh | 1 + .../data_gen/data_gen_NLE/config/config.yaml | 3 +- pdebench/data_gen/data_gen_NLE/utils.py | 1245 ++++++++++------- pdebench/data_gen/gen_diff_react.py | 66 +- pdebench/data_gen/gen_diff_sorp.py | 75 +- pdebench/data_gen/gen_ns_incomp.py | 8 +- pdebench/data_gen/gen_radial_dam_break.py | 38 +- pdebench/data_gen/plot.py | 19 +- pdebench/data_gen/src/_attic/grf.py | 39 +- pdebench/data_gen/src/data_io.py | 112 +- pdebench/data_gen/src/plots.py | 46 +- pdebench/data_gen/src/pytorch_dataset.py | 22 +- pdebench/data_gen/src/sim_diff_react.py | 146 +- pdebench/data_gen/src/sim_diff_sorp.py | 113 +- pdebench/data_gen/src/sim_ns_incomp_2d.py | 272 ++-- pdebench/data_gen/src/sim_radial_dam_break.py | 59 +- pdebench/data_gen/src/utils.py | 23 +- pdebench/data_gen/src/vorticity.py | 149 ++ pdebench/data_gen/uploader.py | 55 +- pdebench/data_gen/velocity2vorticity.py | 104 ++ pdebench/models/analyse_result_forward.py | 98 +- pdebench/models/analyse_result_inverse.py | 49 +- pdebench/models/config/README.md | 71 +- pdebench/models/config/args/config_1DCFD.yaml | 8 +- pdebench/models/config/args/config_2DCFD.yaml | 6 +- pdebench/models/config/args/config_3DCFD.yaml | 6 +- pdebench/models/config/args/config_Adv.yaml | 54 +- pdebench/models/config/args/config_Bgs.yaml | 54 +- pdebench/models/config/args/config_Darcy.yaml | 8 +- .../models/config/args/config_ReacDiff.yaml | 6 +- .../models/config/args/config_diff-react.yaml | 8 +- .../models/config/args/config_diff-sorp.yaml | 8 +- .../models/config/args/config_pinn_CFD1d.yaml | 10 +- .../config/args/config_pinn_diff-react.yaml | 12 +- .../config/args/config_pinn_diff-sorp.yaml | 12 +- .../models/config/args/config_pinn_pde1d.yaml | 10 +- .../models/config/args/config_pinn_swe2d.yaml | 10 +- pdebench/models/config/args/config_rdb.yaml | 8 +- pdebench/models/config/config.yaml | 102 +- pdebench/models/config/config_darcy.yaml | 18 +- pdebench/models/config/config_rdb.yaml | 100 +- pdebench/models/config/results.yaml | 23 +- pdebench/models/fno/fno.py | 266 +++- pdebench/models/fno/train.py | 365 ++--- pdebench/models/fno/utils.py | 444 ++++-- pdebench/models/inverse/inverse.py | 152 +- pdebench/models/inverse/train.py | 361 ++--- pdebench/models/inverse/utils.py | 199 +-- pdebench/models/metrics.py | 601 ++++---- pdebench/models/pinn/pde_definitions.py | 24 +- pdebench/models/pinn/train.py | 340 +++-- pdebench/models/pinn/utils.py | 132 +- pdebench/models/run_forward_1D.sh | 3 +- pdebench/models/run_inverse.sh | 12 +- pdebench/models/train_models_forward.py | 15 +- pdebench/models/train_models_inverse.py | 149 +- pdebench/models/unet/train.py | 559 ++++---- pdebench/models/unet/unet.py | 11 +- pdebench/models/unet/utils.py | 318 +++-- pyproject.toml | 171 ++- tests/test_vorticity.py | 76 + 236 files changed, 6933 insertions(+), 4390 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/precommit.yml create mode 100644 .github/workflows/python-package.yml create mode 100644 .github/workflows/python-publish.yml create mode 100644 .pre-commit-config.yaml create mode 100644 pdebench/_version.pyi create mode 100644 pdebench/data_gen/src/vorticity.py create mode 100644 pdebench/data_gen/velocity2vorticity.py create mode 100644 tests/test_vorticity.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b20527d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.10-bookworm", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + "runArgs": ["--gpus", "all"], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "sudo apt update -y && sudo apt upgrade -y && sudo apt install gfortran -y && pip3 install --user -e .[dev,test,datagen310]" + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..63fa9ed --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for more information: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/precommit.yml b/.github/workflows/precommit.yml new file mode 100644 index 0000000..d0707c1 --- /dev/null +++ b/.github/workflows/precommit.yml @@ -0,0 +1,14 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [main] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..9c714d1 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,33 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python package + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --user -e .[test] + - name: Lint and test with nox + run: | + # stop the build if there are Python syntax errors or undefined names + nox -s pylint tests diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..10b7eff --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,38 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Upload Python Package + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..defc249 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,88 @@ +ci: + autoupdate_commit_msg: "chore: update pre-commit hooks" + autofix_commit_msg: "style: pre-commit fixes" + +repos: + - repo: https://github.com/adamchainz/blacken-docs + rev: "1.16.0" + hooks: + - id: blacken-docs + additional_dependencies: [black==23.*] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v4.5.0" + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: name-tests-test + args: ["--pytest-test-first"] + - id: requirements-txt-fixer + - id: trailing-whitespace + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.10.0" + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json] + args: [--prose-wrap=always] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.1.14" + hooks: + - id: ruff + args: ["--fix", "--show-fixes"] + - id: ruff-format + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: "v1.8.0" + hooks: + - id: mypy + files: pdebench|tests + args: [] + additional_dependencies: + - pytest + + - repo: https://github.com/codespell-project/codespell + rev: "v2.2.6" + hooks: + - id: codespell + exclude_types: [jupyter] + + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: "v0.9.0.6" + hooks: + - id: shellcheck + + - repo: local + hooks: + - id: disallow-caps + name: Disallow improper capitalization + language: pygrep + entry: PyBind|Numpy|Cmake|CCache|Github|PyTest + exclude: .pre-commit-config.yaml + + - repo: https://github.com/abravalheri/validate-pyproject + rev: "v0.16" + hooks: + - id: validate-pyproject + additional_dependencies: ["validate-pyproject-schema-store[all]"] + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: "0.27.3" + hooks: + - id: check-dependabot + - id: check-github-workflows + - id: check-readthedocs diff --git a/LICENSE.txt b/LICENSE.txt index b36c10f..49e3e47 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -6,4 +6,4 @@ Except where otherwise stated this code is released under the MIT license. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 1af9ea9..ea37558 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,31 @@ # PDEBench -The code repository for the NeurIPS 2022 paper -[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) - -:tada: [**SimTech Best Paper Award 2023**](https://www.simtech.uni-stuttgart.de/press/SimTech-Best-Paper-Award-2023-Benchmark-for-ML-for-scientific-simulations) :confetti_ball: - -PDEBench provides a diverse and comprehensive set of benchmarks for scientific machine learning, including challenging and realistic physical problems. This repository consists of the code used to generate the datasets, to upload and download the datasets from the data repository, as well as to train and evaluate different machine learning models as baselines. PDEBench features a much wider range of PDEs than existing benchmarks and includes realistic and difficult problems (both forward and inverse), larger ready-to-use datasets comprising various initial and boundary conditions, and PDE parameters. Moreover, PDEBench was created to make the source code extensible and we invite active participation from the SciML community to improve and extend the benchmark. +The code repository for the NeurIPS 2022 paper +[PDEBench: An Extensive Benchmark for Scientific Machine Learning](https://arxiv.org/abs/2210.07182) + +:tada: +[**SimTech Best Paper Award 2023**](https://www.simtech.uni-stuttgart.de/press/SimTech-Best-Paper-Award-2023-Benchmark-for-ML-for-scientific-simulations) +:confetti_ball: + +PDEBench provides a diverse and comprehensive set of benchmarks for scientific +machine learning, including challenging and realistic physical problems. This +repository consists of the code used to generate the datasets, to upload and +download the datasets from the data repository, as well as to train and evaluate +different machine learning models as baselines. PDEBench features a much wider +range of PDEs than existing benchmarks and includes realistic and difficult +problems (both forward and inverse), larger ready-to-use datasets comprising +various initial and boundary conditions, and PDE parameters. Moreover, PDEBench +was created to make the source code extensible and we invite active +participation from the SciML community to improve and extend the benchmark. ![Visualizations of some PDE problems covered by the benchmark.](https://github.com/pdebench/PDEBench/blob/main/pdebench_examples.PNG) -Created and maintained by Makoto Takamoto ``, Timothy Praditia ``, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, and Mathias Niepert. +Created and maintained by Makoto Takamoto +``, Timothy Praditia +``, Raphael Leiteritz, Dan MacKinlay, +Francesco Alesiani, Dirk Pflüger, and Mathias Niepert. ---------------- +--- ## Datasets and Pretrained Models @@ -23,8 +37,7 @@ https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-298 PDEBench Pre-Trained Models: https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987 - -DOIs +DOIs [![DOI:10.18419/darus-2986](https://img.shields.io/badge/DOI-doi%3A10.18419%2Fdarus--2986-red)](https://doi.org/10.18419/darus-2986) [![DOI:10.18419/darus-2987](https://img.shields.io/badge/DOI-doi%3A10.18419%2Fdarus--2987-red)](https://doi.org/10.18419/darus-2987) @@ -34,22 +47,27 @@ DOIs ### Using pip Locally: + ```bash pip install --upgrade pip wheel pip install . ``` From PyPI: + ```bash pip install pdebench ``` To include dependencies for data generation: + ```bash pip install "pdebench[datagen310]" pip install ".[datagen310]" # locally ``` + or + ```bash pip install "pdebench[datagen39]" pip install ".[datagen39]" # locally @@ -59,14 +77,19 @@ pip install ".[datagen39]" # locally For GPU support there are additional platform-specific instructions: -For PyTorch, the latest version we support is v1.13.1 [see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). - -For JAX, which is approximately 6 times faster for simulations than PyTorch in our tests, [see jax#pip-installation-gpu-cuda-installed-via-pip](https://github.com/google/jax#pip-installation-gpu-cuda-installed-via-pip-easier) +For PyTorch, the latest version we support is v1.13.1 +[see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). +For JAX, which is approximately 6 times faster for simulations than PyTorch in +our tests, +[see jax#pip-installation-gpu-cuda-installed-via-pip](https://github.com/google/jax#pip-installation-gpu-cuda-installed-via-pip-easier) ## Installation using conda: -If you like you can also install dependencies using anaconda, we suggest to use [mambaforge](https://github.com/conda-forge/miniforge#mambaforge) as a distribution. Otherwise you may have to __enable the conda-forge__ channel for the following commands. +If you like you can also install dependencies using anaconda, we suggest to use +[mambaforge](https://github.com/conda-forge/miniforge#mambaforge) as a +distribution. Otherwise you may have to **enable the conda-forge** channel for +the following commands. Starting from a fresh environment: @@ -76,81 +99,112 @@ conda activate myenv ``` Install dependencies for model training: + ``` conda install deepxde hydra-core h5py -c conda-forge ``` -According to your hardware availability, either install PyTorch with CUDA support: +According to your hardware availability, either install PyTorch with CUDA +support: - - [see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). +- [see previous-versions/#linux - CUDA 11.7](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). ``` conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.7 -c pytorch -c nvidia ``` - - [or CPU only binaries](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). +- [or CPU only binaries](https://pytorch.org/get-started/previous-versions/#linux-and-windows-2). ``` conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 cpuonly -c pytorch ``` - Optional dependencies for data generation: + ``` conda install clawpack jax jaxlib python-dotenv ``` ## Configuring DeepXDE -In our tests we used PyTorch as backend for DeepXDE. Please [follow the documentation](https://deepxde.readthedocs.io/en/latest/user/installation.html#working-with-different-backends) to enable this. +In our tests we used PyTorch as backend for DeepXDE. Please +[follow the documentation](https://deepxde.readthedocs.io/en/latest/user/installation.html#working-with-different-backends) +to enable this. ## Data Generation + The data generation codes are contained in [data_gen](./pdebench/data_gen): + - `gen_diff_react.py` to generate the 2D diffusion-reaction data. - `gen_diff_sorp.py` to generate the 1D diffusion-sorption data. - `gen_radial_dam_break.py` to generate the 2D shallow-water data. -- `gen_ns_incomp.py` to generate the 2D incompressible inhomogenous Navier-Stokes data. +- `gen_ns_incomp.py` to generate the 2D incompressible inhomogeneous + Navier-Stokes data. - `plot.py` to plot the generated data. - `uploader.py` to upload the generated data to the data repository. -- `.env` is the environment data to store Dataverse URL and API token to upload the generated data. Note that the filename should be strictly `.env` (i.e. remove the `example` from the filename) -- `configs` directory contains the yaml files storing the configuration for the simulation. Arguments for the simulation are problem-specific and detailed explanation can be found in the simulation scripts. -- `src` directory contains the simulation scripts for different problems: `sim_diff_react-py` for 2D diffusion-reaction, `sim_diff_sorp.py` for 1D diffusion-sorption, and `swe` for the shallow-water equation. +- `.env` is the environment data to store Dataverse URL and API token to upload + the generated data. Note that the filename should be strictly `.env` (i.e. + remove the `example` from the filename) +- `configs` directory contains the yaml files storing the configuration for the + simulation. Arguments for the simulation are problem-specific and detailed + explanation can be found in the simulation scripts. +- `src` directory contains the simulation scripts for different problems: + `sim_diff_react-py` for 2D diffusion-reaction, `sim_diff_sorp.py` for 1D + diffusion-sorption, and `swe` for the shallow-water equation. ### Data Generation for 1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes Equations -The data generation codes are contained in [data_gen_NLE](./pdebench/data_gen/data_gen_NLE/): -- `utils.py` util file for data generation, mainly boundary conditions and initial conditions. -- `AdvectionEq` directory with the source codes to generate 1D Advection equation training samples -- `BurgersEq` directory with the source codes to generate 1D Burgers equation training samples -- `CompressibleFluid` directory with the source codes to generate compressible Navier-Stokes equations training samples - - `ReactionDiffusionEq` directory with the source codes to generate 1D Reaction-Diffusion equation training samples (**Note: [DarcyFlow data can be generated by run_DarcyFlow2D.sh](pdebench/data_gen/data_gen_NLE/README.md) in this folder.**) + +The data generation codes are contained in +[data_gen_NLE](./pdebench/data_gen/data_gen_NLE/): + +- `utils.py` util file for data generation, mainly boundary conditions and + initial conditions. +- `AdvectionEq` directory with the source codes to generate 1D Advection + equation training samples +- `BurgersEq` directory with the source codes to generate 1D Burgers equation + training samples +- `CompressibleFluid` directory with the source codes to generate compressible + Navier-Stokes equations training samples + + - `ReactionDiffusionEq` directory with the source codes to generate 1D + Reaction-Diffusion equation training samples (**Note: + [DarcyFlow data can be generated by run_DarcyFlow2D.sh](pdebench/data_gen/data_gen_NLE/README.md) + in this folder.**) - `save` directory saving the generated training samples -A typical example to generate training samples (1D Advection Equation): -(in `data_gen/data_gen_NLE/AdvectionEq/`) +A typical example to generate training samples (1D Advection Equation): (in +`data_gen/data_gen_NLE/AdvectionEq/`) + ```bash python3 advection_multi_solution_Hydra.py +multi=beta1e0.yaml ``` + which is assumed to be performed in each directory. -Examples for generating other PDEs are provided in `run_trainset.sh` in each PDE's directories. -The config files for Hydra are stored in `config` directory in each PDE's directory. +Examples for generating other PDEs are provided in `run_trainset.sh` in each +PDE's directories. The config files for Hydra are stored in `config` directory +in each PDE's directory. #### Data Transformaion and Merge into HDF5 format -1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes Equations save data as a numpy array. -So, to read those data via our dataloaders, the data transformation/merge should be performed. -This can be done using `data_gen_NLE/Data_Merge.py` whose config file is located at: `data_gen/data_gen_NLE/config/config.yaml`. -After properly setting the parameters in the config file (type: name of PDEs, dim: number of spatial-dimension, bd: boundary condition), -the corresponding HDF5 file could be obtained as: + +1D Advection/Burgers/Reaction-Diffusion/2D DarcyFlow/Compressible Navier-Stokes +Equations save data as a numpy array. So, to read those data via our +dataloaders, the data transformation/merge should be performed. This can be done +using `data_gen_NLE/Data_Merge.py` whose config file is located at: +`data_gen/data_gen_NLE/config/config.yaml`. After properly setting the +parameters in the config file (type: name of PDEs, dim: number of +spatial-dimension, bd: boundary condition), the corresponding HDF5 file could be +obtained as: ```bash python3 Data_Merge.py ``` - ## Configuration -You can set the default values for data locations for this project by putting config vars like this in the `.env` file: +You can set the default values for data locations for this project by putting +config vars like this in the `.env` file: ``` WORKING_DIR=~/Data/Working @@ -159,45 +213,82 @@ ARCHIVE_DATA_DIR=~/Data/Archive There is an example in `example.env`. - ## Data Download -The download scripts are provided in [data_download](./pdebench/data_download). There are two options to download data. -1) Using `download_direct.py` (**recommended**) - - Retrieves data shards directly using URLs. Sample command for each PDE is given in the README file in the [data_download](./pdebench/data_download) directory. -2) Using `download_easydataverse.py` (might be slow and you could encounter errors/issues; hence, not recommended!) - - Use the config files from the `config` directory that contains the yaml files storing the configuration. Any files in the dataset matching `args.filename` will be downloaded into `args.data_folder`. +The download scripts are provided in [data_download](./pdebench/data_download). +There are two options to download data. +1. Using `download_direct.py` (**recommended**) + - Retrieves data shards directly using URLs. Sample command for each PDE is + given in the README file in the [data_download](./pdebench/data_download) + directory. +2. Using `download_easydataverse.py` (might be slow and you could encounter + errors/issues; hence, not recommended!) + - Use the config files from the `config` directory that contains the yaml + files storing the configuration. Any files in the dataset matching + `args.filename` will be downloaded into `args.data_folder`. ## Baseline Models -In this work, we provide three different ML models to be trained and evaluated against the benchmark datasets, namely [FNO](https://arxiv.org/pdf/2010.08895.pdf), [U-Net](https://www.sciencedirect.com/science/article/abs/pii/S0010482519301520?via%3Dihub), and [PINN](https://www.sciencedirect.com/science/article/pii/S0021999118307125). -The codes for the baseline model implementations are contained in [models](./pdebench/models): - -- `train_models_forward.py` is the main script to train and evaluate the model. It will call on model-specific script based on the input argument. -- `train_models_inverse.py` is the main script to train and evaluate the model for inverse problems. It will call on model-specific script based on the input argument. -- `metrics.py` is the script to evaluate the trained models based on various evaluation metrics described in our paper. Additionally, it also plots the prediction and target data. -- `analyse_result_forward.py` is the script to convert the saved pickle file from the metrics calculation script into pandas dataframe format and save it as a CSV file. Additionally it also plots a bar chart to compare the results between different models. -- `analyse_result_inverse.py` is the script to convert the saved pickle file from the metrics calculation script into pandas dataframe format and save it as a CSV file. This script is used for the inverse problems. Additionally it also plots a bar chart to compare the results between different models. -- `fno` contains the scripts of FNO implementation. These are partly adapted from the [FNO repository](https://github.com/zongyi-li/fourier_neural_operator). -- `unet` contains the scripts of U-Net implementation. These are partly adapted from the [U-Net repository](https://github.com/mateuszbuda/brain-segmentation-pytorch). -- `pinn` contains the scripts of PINN implementation. These utilize the [DeepXDE library](https://github.com/lululxvi/deepxde). -- `inverse` contains the model for inverse model based on gradient. -- `config` contains the yaml files for the model training input. The default templates for different equations are provided in the [args](./pdebench/models/config/args) directory. User just needs to copy and paste them to the args keyword in the [config.yaml](./pdebench/models/config/config.yaml) file. -An example to run the forward model training can be found in [run_forward_1D.sh](./pdebench/models/run_forward_1D.sh), and an example to run the inverse model training can be found in [run_inverse.sh](./pdebench/models/run_inverse.sh). +In this work, we provide three different ML models to be trained and evaluated +against the benchmark datasets, namely +[FNO](https://arxiv.org/pdf/2010.08895.pdf), +[U-Net](https://www.sciencedirect.com/science/article/abs/pii/S0010482519301520?via%3Dihub), +and [PINN](https://www.sciencedirect.com/science/article/pii/S0021999118307125). +The codes for the baseline model implementations are contained in +[models](./pdebench/models): + +- `train_models_forward.py` is the main script to train and evaluate the model. + It will call on model-specific script based on the input argument. +- `train_models_inverse.py` is the main script to train and evaluate the model + for inverse problems. It will call on model-specific script based on the input + argument. +- `metrics.py` is the script to evaluate the trained models based on various + evaluation metrics described in our paper. Additionally, it also plots the + prediction and target data. +- `analyse_result_forward.py` is the script to convert the saved pickle file + from the metrics calculation script into pandas dataframe format and save it + as a CSV file. Additionally it also plots a bar chart to compare the results + between different models. +- `analyse_result_inverse.py` is the script to convert the saved pickle file + from the metrics calculation script into pandas dataframe format and save it + as a CSV file. This script is used for the inverse problems. Additionally it + also plots a bar chart to compare the results between different models. +- `fno` contains the scripts of FNO implementation. These are partly adapted + from the + [FNO repository](https://github.com/zongyi-li/fourier_neural_operator). +- `unet` contains the scripts of U-Net implementation. These are partly adapted + from the + [U-Net repository](https://github.com/mateuszbuda/brain-segmentation-pytorch). +- `pinn` contains the scripts of PINN implementation. These utilize the + [DeepXDE library](https://github.com/lululxvi/deepxde). +- `inverse` contains the model for inverse model based on gradient. +- `config` contains the yaml files for the model training input. The default + templates for different equations are provided in the + [args](./pdebench/models/config/args) directory. User just needs to copy and + paste them to the args keyword in the + [config.yaml](./pdebench/models/config/config.yaml) file. +An example to run the forward model training can be found in +[run_forward_1D.sh](./pdebench/models/run_forward_1D.sh), and an example to run +the inverse model training can be found in +[run_inverse.sh](./pdebench/models/run_inverse.sh). ### Short explanations on the config args -- model_name: string, containing the baseline model name, either 'FNO', 'Unet', or 'PINN'. + +- model_name: string, containing the baseline model name, either 'FNO', 'Unet', + or 'PINN'. - if_training: bool, set True for training, or False for evaluation. -- continue_training: bool, set True to continute training from a checkpoint. +- continue_training: bool, set True to continue training from a checkpoint. - num_workers: int, number of workers for the PyTorch dataloader. - batch_size: int, training batch size. - initial_step: int, number of time steps used as input for FNO and U-Net. -- t_train: int, number of the last time step used for training (for extrapolation testing, set this to be < Nt). +- t_train: int, number of the last time step used for training (for + extrapolation testing, set this to be < Nt). - model_update: int, number of epochs to save model. - filename: str, has to match the dataset filename. -- single_file: bool, set False for 2D diffusion-reaction, 1D diffusion-sorption, and the radial dam break scenarios, and set True otherwise. +- single_file: bool, set False for 2D diffusion-reaction, 1D diffusion-sorption, + and the radial dam break scenarios, and set True otherwise. - reduced_resolution: int, factor to downsample spatial resolution. - reduced_resolution_t: int, factor to downsample temporal resolution. - reduced_batch: int, factor to downsample sample size used for training. @@ -207,31 +298,38 @@ An example to run the forward model training can be found in [run_forward_1D.sh] - scheduler_gamma: float, decay rate of the learning rate. #### U-Net specific args: + - in_channels: int, number of input channels - out_channels: int, number of output channels - ar_mode: bool, set True for fully autoregressive or pushforward training. -- pushforward: bool, set True for pushforward training, False otherwise (ar_mode also has to be set True). -- unroll_step: int, number of time steps to backpropagate in the pushforward training. +- pushforward: bool, set True for pushforward training, False otherwise (ar_mode + also has to be set True). +- unroll_step: int, number of time steps to backpropagate in the pushforward + training. #### FNO specific args: + - num_channels: int, number of channels (variables). - modes: int, number of Fourier modes to multiply. - width: int, number of channels for the Fourier layer. #### INVERSE specific args: - - base_path: string, location of the data directory - - training_type: string, type of training, autoregressive, single - - mcmc_num_samples: int, number of generated samples - - mcmc_warmup_steps: 10 - - mcmc_num_chains: 1 - - num_samples_max: 1000 - - in_channels_hid: 64 - - inverse_model_type: string, type of inverse inference model, ProbRasterLatent, InitialConditionInterp - - inverse_epochs: int, number of epochs for the gradient based method - - inverse_learning_rate: float, learning rate for the gradient based method - - inverse_verbose_flag: bool, some printing + +- base_path: string, location of the data directory +- training_type: string, type of training, autoregressive, single +- mcmc_num_samples: int, number of generated samples +- mcmc_warmup_steps: 10 +- mcmc_num_chains: 1 +- num_samples_max: 1000 +- in_channels_hid: 64 +- inverse_model_type: string, type of inverse inference model, ProbRasterLatent, + InitialConditionInterp +- inverse_epochs: int, number of epochs for the gradient based method +- inverse_learning_rate: float, learning rate for the gradient based method +- inverse_verbose_flag: bool, some printing #### Plotting specific args: + - plot: bool, set True to activate plotting. - channel_plot: int, determines which channel/variable to plot. - x_min: float, left spatial domain. @@ -242,11 +340,17 @@ An example to run the forward model training can be found in [run_forward_1D.sh] - t_max: float, end of temporal domain. ## Datasets and pretrained models -We provide the benchmark datasets we used in the paper through our [DaRUS data repository](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986). -The data generation configuration can be found in the paper. -Additionally, the pretrained models are also available to be downloaded from [PDEBench Pretrained Models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987) DaRus repository. To use the pretrained models, users can specify the argument `continue_training: True` in the [config file](./pdebench/models/config/config.yaml). -------- +We provide the benchmark datasets we used in the paper through our +[DaRUS data repository](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2986). +The data generation configuration can be found in the paper. Additionally, the +pretrained models are also available to be downloaded from +[PDEBench Pretrained Models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987) +DaRus repository. To use the pretrained models, users can specify the argument +`continue_training: True` in the +[config file](./pdebench/models/config/config.yaml). + +--- ## Directory Tour @@ -255,8 +359,8 @@ Below is an illustration of the directory structure of PDEBench. ``` 📂 pdebench |_📁 models - |_📁 pinn # Model: Physics-Informed Neural Network - |_📄 train.py + |_📁 pinn # Model: Physics-Informed Neural Network + |_📄 train.py |_📄 utils.py |_📄 pde_definitions.py |_📁 fno # Model: Fourier Neural Operator @@ -301,12 +405,12 @@ Below is an illustration of the directory structure of PDEBench. |_📄 __init__.py ``` - ------- +--- ## Publications & Citations -Please cite the following papers if you use PDEBench datasets and/or source code in your research. +Please cite the following papers if you use PDEBench datasets and/or source code +in your research.
@@ -323,8 +427,8 @@ booktitle = {36th Conference on Neural Information Processing Systems (NeurIPS 2 url = {https://arxiv.org/abs/2210.07182} } ``` -
+
@@ -342,6 +446,7 @@ doi = {10.18419/darus-2986}, url = {https://doi.org/10.18419/darus-2986} } ``` +
@@ -350,21 +455,22 @@ url = {https://doi.org/10.18419/darus-2986}
- ``` - @article{cape-takamoto:2023, - author = {Makoto Takamoto and - Francesco Alesiani and - Mathias Niepert}, - title = {Learning Neural {PDE} Solvers with Parameter-Guided Channel Attention}, - journal = {CoRR}, - volume = {abs/2304.14118}, - year = {2023}, - url = {https://doi.org/10.48550/arXiv.2304.14118}, - doi = {10.48550/arXiv.2304.14118}, - eprinttype = {arXiv}, - eprint = {2304.14118}, - } - ``` +``` +@article{cape-takamoto:2023, + author = {Makoto Takamoto and + Francesco Alesiani and + Mathias Niepert}, + title = {Learning Neural {PDE} Solvers with Parameter-Guided Channel Attention}, + journal = {CoRR}, + volume = {abs/2304.14118}, + year = {2023}, + url = {https://doi.org/10.48550/arXiv.2304.14118}, + doi = {10.48550/arXiv.2304.14118}, + eprinttype = {arXiv}, + eprint = {2304.14118}, + } +``` +
@@ -373,14 +479,15 @@ url = {https://doi.org/10.18419/darus-2986}
- ``` +``` @inproceedings{vcnef-vectorized-conditional-neural-fields-hagnberger:2024, author = {Hagnberger, Jan and Kalimuthu, Marimuthu and Musekamp, Daniel and Niepert, Mathias}, title = {{Vectorized Conditional Neural Fields: A Framework for Solving Time-dependent Parametric Partial Differential Equations}}, year = {2024}, booktitle = {Proceedings of the 41st International Conference on Machine Learning (ICML 2024)} } - ``` +``` +
@@ -389,44 +496,53 @@ booktitle = {Proceedings of the 41st International Conference on Machine Learnin
- ``` +``` @article{active-learn-neuralpde-benchmark-musekamp:2024, - author = {Daniel Musekamp and - Marimuthu Kalimuthu and - David Holzm{\"{u}}ller and - Makoto Takamoto and - Mathias Niepert}, - title = {Active Learning for Neural {PDE} Solvers}, - journal = {CoRR}, - volume = {abs/2408.01536}, - year = {2024}, - url = {https://doi.org/10.48550/arXiv.2408.01536}, - doi = {10.48550/ARXIV.2408.01536}, - eprinttype = {arXiv}, - eprint = {2408.01536}, + author = {Daniel Musekamp and + Marimuthu Kalimuthu and + David Holzm{\"{u}}ller and + Makoto Takamoto and + Mathias Niepert}, + title = {Active Learning for Neural {PDE} Solvers}, + journal = {CoRR}, + volume = {abs/2408.01536}, + year = {2024}, + url = {https://doi.org/10.48550/arXiv.2408.01536}, + doi = {10.48550/ARXIV.2408.01536}, + eprinttype = {arXiv}, + eprint = {2408.01536}, } - ``` +``` +
------- +--- ## Code contributors - -* [Makato Takamoto](https://github.com/mtakamoto-D) ([NEC laboratories Europe](https://www.neclab.eu/)) -* [Timothy Praditia](https://github.com/timothypraditia) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) -* [Raphael Leiteritz](https://github.com/leiterrl) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) -* [Francesco Alesiani](https://github.com/falesiani) ([NEC laboratories Europe](https://www.neclab.eu/)) -* [Dan MacKinlay](https://danmackinlay.name/) ([CSIRO’s Data61](https://data61.csiro.au/)) -* [Marimuthu Kalimuthu](https://github.com/kmario23) ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) -* [John Kim](https://github.com/johnmjkim) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) -* [Gefei Shan](https://github.com/davecatmeow) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) -* [Yizhou Yang](https://github.com/verdantwynnd) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) -* [Ran Zhang](https://github.com/maphyca) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) -* [Simon Brown](https://github.com/SimonSyBrown) ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) - +- [Makato Takamoto](https://github.com/mtakamoto-D) + ([NEC laboratories Europe](https://www.neclab.eu/)) +- [Timothy Praditia](https://github.com/timothypraditia) + ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +- [Raphael Leiteritz](https://github.com/leiterrl) + ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +- [Francesco Alesiani](https://github.com/falesiani) + ([NEC laboratories Europe](https://www.neclab.eu/)) +- [Dan MacKinlay](https://danmackinlay.name/) + ([CSIRO’s Data61](https://data61.csiro.au/)) +- [Marimuthu Kalimuthu](https://github.com/kmario23) + ([Stuttgart Center for Simulation Science | University of Stuttgart](https://www.simtech.uni-stuttgart.de/)) +- [John Kim](https://github.com/johnmjkim) + ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +- [Gefei Shan](https://github.com/davecatmeow) + ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +- [Yizhou Yang](https://github.com/verdantwynnd) + ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +- [Ran Zhang](https://github.com/maphyca) + ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) +- [Simon Brown](https://github.com/SimonSyBrown) + ([ANU TechLauncher](https://comp.anu.edu.au/TechLauncher/)/[CSIRO’s Data61](https://data61.csiro.au/)) ## License -MIT licensed, except where otherwise stated. -See `LICENSE.txt` file. +MIT licensed, except where otherwise stated. See `LICENSE.txt` file. diff --git a/pdebench/__init__.py b/pdebench/__init__.py index 010919c..4b4fd31 100644 --- a/pdebench/__init__.py +++ b/pdebench/__init__.py @@ -19,7 +19,8 @@ """ +from __future__ import annotations __version__ = "0.0.1" -__author__ = 'Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert' -__credits__ = 'NEC labs Europe, University of Stuttgart, CSIRO''s Data61' +__author__ = "Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert" +__credits__ = "NEC labs Europe, University of Stuttgart, CSIRO" "s Data61" diff --git a/pdebench/_version.pyi b/pdebench/_version.pyi new file mode 100644 index 0000000..91744f9 --- /dev/null +++ b/pdebench/_version.pyi @@ -0,0 +1,4 @@ +from __future__ import annotations + +version: str +version_tuple: tuple[int, int, int] | tuple[int, int, int, str, str] diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index d86d5f6..8e619db 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -1,27 +1,30 @@ - # Downloading PDEBench Datasets :earth_asia: -Here we enumerate the list of all available PDEs in PDEBench and the commands to download them. - -| PDEs | Dataset Download | Dataset Size | -| ----------- | :----------------------------------------------------------- | ------------ | -| advection | ```python download_direct.py --root_folder $proj_home/data --pde_name advection``` | 47 GB | -| burgers | ```python download_direct.py --root_folder $proj_home/data --pde_name burgers``` | 93 GB | -| 1d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_cfd``` | 88 GB | -| diff_sorp | ```python download_direct.py --root_folder $proj_home/data --pde_name diff_sorp``` | 4 GB | -| 1d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 1d_reacdiff``` | 62 GB | -| 2d_reacdiff | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_reacdiff``` | 13 GB | -| 2d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd``` | 551 GB | -| 3d_cfd | ```python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd``` | 285 GB | -| darcy | ```python download_direct.py --root_folder $proj_home/data --pde_name darcy``` | 6.2 GB | -| ns_incom | ```python download_direct.py --root_folder $proj_home/data --pde_name ns_incom``` | 2.3 TB | -| swe | ```python download_direct.py --root_folder $proj_home/data --pde_name swe``` | 6.2 GB | - --------- +Here we enumerate the list of all available PDEs in PDEBench and the commands to +download them. + +| PDEs | Dataset Download | Dataset Size | +| ----------- | :------------------------------------------------------------------------------- | ------------ | +| advection | `python download_direct.py --root_folder $proj_home/data --pde_name advection` | 47 GB | +| burgers | `python download_direct.py --root_folder $proj_home/data --pde_name burgers` | 93 GB | +| 1d_cfd | `python download_direct.py --root_folder $proj_home/data --pde_name 1d_cfd` | 88 GB | +| diff_sorp | `python download_direct.py --root_folder $proj_home/data --pde_name diff_sorp` | 4 GB | +| 1d_reacdiff | `python download_direct.py --root_folder $proj_home/data --pde_name 1d_reacdiff` | 62 GB | +| 2d_reacdiff | `python download_direct.py --root_folder $proj_home/data --pde_name 2d_reacdiff` | 13 GB | +| 2d_cfd | `python download_direct.py --root_folder $proj_home/data --pde_name 2d_cfd` | 551 GB | +| 3d_cfd | `python download_direct.py --root_folder $proj_home/data --pde_name 3d_cfd` | 285 GB | +| darcy | `python download_direct.py --root_folder $proj_home/data --pde_name darcy` | 6.2 GB | +| ns_incom | `python download_direct.py --root_folder $proj_home/data --pde_name ns_incom` | 2.3 TB | +| swe | `python download_direct.py --root_folder $proj_home/data --pde_name swe` | 6.2 GB | + +--- # Visualizing PDEs :ocean: -Below are some illustrations for how to visualize a certain PDE. It is assumed that you first download the data shard you'd like to visualize for a desired PDE. Then you can use the `visualize_pde.py` script to generate an animation (i.e., `.gif`). +Below are some illustrations for how to visualize a certain PDE. It is assumed +that you first download the data shard you'd like to visualize for a desired +PDE. Then you can use the `visualize_pde.py` script to generate an animation +(i.e., `.gif`). ###### 1D Diffusion Sorption Eqn @@ -33,7 +36,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133020 python visualize_pdes.py --pde_name "diff_sorp" --data_path "./" ``` ----------- +--- ###### 1D Diffusion Reaction Eqn @@ -45,7 +48,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133181 python visualize_pdes.py --pde_name "1d_reacdiff" ``` ----------- +--- ###### 1D Advection Eqn @@ -57,7 +60,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133110 python visualize_pdes.py --pde_name "advection" ``` ------------ +--- ###### 1D Burgers Eqn @@ -69,7 +72,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133136 python visualize_pdes.py --pde_name "burgers" ``` --------------------- +--- ###### 1D CFD Eqn @@ -81,7 +84,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/135485 python visualize_pdes.py --pde_name "1d_cfd" ``` -------------- +--- ###### 2D Diffusion Reaction Eqn @@ -93,7 +96,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133017 python visualize_pdes.py --pde_name "2d_reacdiff" ``` -------------- +--- ###### 2D Darcy Flow Eqn @@ -105,7 +108,7 @@ https://darus.uni-stuttgart.de/api/access/datafile/133219 python visualize_pdes.py --pde_name "darcy" ``` ------------------- +--- ###### 2D Shallow Water Eqn @@ -116,4 +119,3 @@ https://darus.uni-stuttgart.de/api/access/datafile/133021 # visualize python visualize_pdes.py --pde_name "swe" --data_path "./" ``` - diff --git a/pdebench/data_download/config/config.yaml b/pdebench/data_download/config/config.yaml index 7cd39eb..13d7b8b 100644 --- a/pdebench/data_download/config/config.yaml +++ b/pdebench/data_download/config/config.yaml @@ -7,9 +7,9 @@ hydra: output_subdir: null run: dir: . - + args: - filename: 'Advection_beta' - dataverse_url: 'https://darus.uni-stuttgart.de' - dataset_id: 'doi:10.18419/darus-2986' - data_folder: 'data' + filename: "Advection_beta" + dataverse_url: "https://darus.uni-stuttgart.de" + dataset_id: "doi:10.18419/darus-2986" + data_folder: "data" diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 35ee0e0..0dc4b39 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -1,9 +1,11 @@ -import os +from __future__ import annotations + import argparse +import os -from tqdm import tqdm import pandas as pd from torchvision.datasets.utils import download_url +from tqdm import tqdm def parse_metadata(pde_names): diff --git a/pdebench/data_download/download_easydataverse.py b/pdebench/data_download/download_easydataverse.py index 5e3976f..cc1042c 100644 --- a/pdebench/data_download/download_easydataverse.py +++ b/pdebench/data_download/download_easydataverse.py @@ -1,15 +1,17 @@ +from __future__ import annotations + +import logging import os import hydra +from easyDataverse import Dataset from hydra.utils import get_original_cwd from omegaconf import DictConfig -import logging - from pyDataverse.api import NativeApi -from easyDataverse import Dataset log = logging.getLogger(__name__) + @hydra.main(config_path="config/", config_name="config") def main(config: DictConfig): """ @@ -26,7 +28,7 @@ def main(config: DictConfig): # Extract dataset from the given DOI dataset = Dataset() - setattr(dataset, "p_id", config.args.dataset_id) + dataset.p_id = config.args.dataset_id # Extract file list contained in the dataset api = NativeApi(config.args.dataverse_url) @@ -40,7 +42,7 @@ def main(config: DictConfig): files.append(file["dataFile"]["filename"]) # Download the files - + dataset = Dataset.from_dataverse_doi( doi=config.args.dataset_id, dataverse_url=config.args.dataverse_url, @@ -49,6 +51,5 @@ def main(config: DictConfig): ) - if __name__ == "__main__": main() diff --git a/pdebench/data_download/pdebench_data_urls.csv b/pdebench/data_download/pdebench_data_urls.csv index ba845ea..3332b67 100644 --- a/pdebench/data_download/pdebench_data_urls.csv +++ b/pdebench/data_download/pdebench_data_urls.csv @@ -373,4 +373,4 @@ SWE,2D_rdb_NA_NA.h5,https://darus.uni-stuttgart.de/api/access/datafile/133021,2D 3D_CFD,Turb_M05.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133225,3D/Test/Turbulence/,f9407dff1a75a1d14d93e7dd570af728 3D_CFD,Turb_M1.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/135833,3D/Test/Turbulence/,3758f23f71684ac666e0b1e91da0a1c4 3D_CFD,Turb_M2.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133227,3D/Test/Turbulence/,12e528dc8ab800f69474600ec58b24d3 -3D_CFD,Turb_M4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133228,3D/Test/Turbulence/,8db384feba75903a8c5b21ebeba40083 \ No newline at end of file +3D_CFD,Turb_M4.hdf5,https://darus.uni-stuttgart.de/api/access/datafile/133228,3D/Test/Turbulence/,8db384feba75903a8c5b21ebeba40083 diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index 1b7f397..a117bcf 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -1,12 +1,13 @@ -import os +from __future__ import annotations + import argparse +import os -from tqdm import tqdm import h5py -import numpy as np import matplotlib.pyplot as plt -import matplotlib.animation as animation - +import numpy as np +from matplotlib import animation +from tqdm import tqdm pdes = ( "advection", @@ -35,10 +36,10 @@ def visualize_diff_sorp(path, seed=None): # Read the h5 file and store the data h5_file = h5py.File(os.path.join(path, "1D_diff-sorp_NA_NA.h5"), "r") num_samples = len(h5_file.keys()) - - # randomly choose a seed for picking a sample that will subsequently be visualized + + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, num_samples) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" @@ -55,9 +56,11 @@ def visualize_diff_sorp(path, seed=None): ims = [] for i in tqdm(range(data.shape[0])): if i == 0: - im = ax.plot(data[0].squeeze(), animated=True, color="blue") # show an initial one first + im = ax.plot( + data[0].squeeze(), animated=True, color="blue" + ) # show an initial one first else: - im = ax.plot(data[i].squeeze(), animated=True, color="blue") + im = ax.plot(data[i].squeeze(), animated=True, color="blue") ax.plot ims.append([im[0]]) @@ -82,15 +85,16 @@ def visualize_2d_reacdiff(path, seed=None): h5_file = h5py.File(os.path.join(path, "2D_diff-react_NA_NA.h5"), "r") num_samples = len(h5_file.keys()) - # randomly choose a seed for picking a sample that will subsequently be visualized + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, num_samples) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" seed = str(seed).zfill(4) - data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 2] + # dim = [101, 128, 128, 2] + data = np.array(h5_file[f"{seed}/data"], dtype="f") h5_file.close() @@ -103,8 +107,10 @@ def visualize_2d_reacdiff(path, seed=None): im1 = ax[0].imshow(data[i, ..., 0].squeeze(), animated=True) im2 = ax[1].imshow(data[i, ..., 1].squeeze(), animated=True) if i == 0: - ax[0].imshow(data[0, ..., 0].squeeze()) # show an initial one first - ax[1].imshow(data[0, ..., 1].squeeze()) # show an initial one first + # show an initial one first + ax[0].imshow(data[0, ..., 0].squeeze()) + # show an initial one first + ax[1].imshow(data[0, ..., 1].squeeze()) ims.append([im1, im2]) # Animate the plot @@ -127,16 +133,17 @@ def visualize_swe(path, seed=None): # Read the h5 file and store the data h5_file = h5py.File(os.path.join(path, "2D_rdb_NA_NA.h5"), "r") num_samples = len(h5_file.keys()) - - # randomly choose a seed for picking a sample that will subsequently be visualized + + # randomly choose a seed for picking a sample that will subsequently be visualized if not seed: - seed = np.random.randint(0, num_samples) + seed = np.random.randint(0, num_samples) # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" seed = str(seed).zfill(4) - data = np.array(h5_file[f"{seed}/data"], dtype="f") # dim = [101, 128, 128, 1] + # dim = [101, 128, 128, 1] + data = np.array(h5_file[f"{seed}/data"], dtype="f") h5_file.close() @@ -171,14 +178,16 @@ def visualize_burgers(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "1D_Burgers_Sols_Nu" + str(param) + ".hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+path + flnm + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "1D_Burgers_Sols_Nu0.01.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) - data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + data = np.array(h5_file["tensor"], dtype=np.float32)[ + nb + ] # (batch, t, x, channel) --> (t, x, channel) # Initialize plot fig, ax = plt.subplots() @@ -190,7 +199,9 @@ def visualize_burgers(path, param=None): if i == 0: im = ax.plot(xcrd, data[i].squeeze(), animated=True, color="blue") else: - im = ax.plot(xcrd, data[i].squeeze(), animated=True, color="blue") # show an initial one first + im = ax.plot( + xcrd, data[i].squeeze(), animated=True, color="blue" + ) # show an initial one first ax.plot ims.append([im[0]]) @@ -214,14 +225,16 @@ def visualize_advection(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "1D_Advection_Sols_beta" + str(param) + ".hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "1D_Advection_Sols_beta0.4.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) - data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + data = np.array(h5_file["tensor"], dtype=np.float32)[ + nb + ] # (batch, t, x, channel) --> (t, x, channel) # Initialize plot fig, ax = plt.subplots() @@ -254,23 +267,35 @@ def visualize_1d_cfd(path, param=None): # Read the h5 file and store the data if param is not None: - assert len(param) == 4, 'param should include type,eta,zeta,boundary as list' - flnm = "1D_CFD_" + str(param[0]) + "_Eta" + str(param[1]) + '_Zeta' + str(param[2]) +"_" + str(param[3]) + "_Train.hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert len(param) == 4, "param should include type,eta,zeta,boundary as list" + flnm = ( + "1D_CFD_" + + str(param[0]) + + "_Eta" + + str(param[1]) + + "_Zeta" + + str(param[2]) + + "_" + + str(param[3]) + + "_Train.hdf5" + ) + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) - dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + dd = np.array(h5_file["density"], dtype=np.float32)[ + nb + ] # (batch, t, x, channel) --> (t, x, channel) # Initialize plot fig, ax = plt.subplots() # Store the plot handle at each time step in the 'ims' list ims = [] - ax.set_title('density') + ax.set_title("density") for i in tqdm(range(dd.shape[0])): im = ax.plot(xcrd, dd[i].squeeze(), animated=True) if i == 0: @@ -297,22 +322,40 @@ def visualize_2d_cfd(path, param=None): # Read the h5 file and store the data if param is not None: - assert len(param) == 6, 'param should include type,M,eta,zeta,boundary, resolution as list' - flnm = "2D_CFD_" + str(param[0]) + "_M" + str(param[1]) + "_Eta" + str(param[2]) + '_Zeta' + str(param[3]) + "_" + str(param[4]) + "_" + str(param[5]) + "_Train.hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert ( + len(param) == 6 + ), "param should include type,M,eta,zeta,boundary, resolution as list" + flnm = ( + "2D_CFD_" + + str(param[0]) + + "_M" + + str(param[1]) + + "_Eta" + + str(param[2]) + + "_Zeta" + + str(param[3]) + + "_" + + str(param[4]) + + "_" + + str(param[5]) + + "_Train.hdf5" + ) + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "2D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_512_Train.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: - dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + dd = np.array(h5_file["density"], dtype=np.float32)[ + nb + ] # (batch, t, x, y, channel) --> (t, x, y, channel) # Initialize plot fig, ax = plt.subplots() # Store the plot handle at each time step in the 'ims' list ims = [] - ax.set_title('density') + ax.set_title("density") for i in range(dd.shape[0]): im = ax.imshow(dd[i].squeeze(), animated=True) ims.append([im]) @@ -328,22 +371,36 @@ def visualize_2d_cfd(path, param=None): def visualize_3d_cfd(path, param=None): # Read the h5 file and store the data if param is not None: - assert len(param) == 5, 'param should include type,M,eta,zeta,boundary as list' - flnm = "3D_CFD_" + str(param[0]) + "_M" + str(param[1]) + "_Eta" + str(param[2]) + '_Zeta' + str(param[3]) + "_" + str(param[4]) + "_Train.hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert len(param) == 5, "param should include type,M,eta,zeta,boundary as list" + flnm = ( + "3D_CFD_" + + str(param[0]) + + "_M" + + str(param[1]) + + "_Eta" + + str(param[2]) + + "_Zeta" + + str(param[3]) + + "_" + + str(param[4]) + + "_Train.hdf5" + ) + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "3D_CFD_Rand_M1.0_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: - dd = np.array(h5_file["density"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + dd = np.array(h5_file["density"], dtype=np.float32)[ + nb + ] # (batch, t, x, y, channel) --> (t, x, y, channel) # Initialize plot fig, ax = plt.subplots() # Store the plot handle at each time step in the 'ims' list ims = [] - ax.set_title('density') + ax.set_title("density") for i in range(dd.shape[0]): im = ax.imshow(dd[i, :, :, 32].squeeze(), animated=True) ims.append([im]) @@ -356,7 +413,7 @@ def visualize_3d_cfd(path, param=None): print("saved") -def visualize_ns_incom(): +def visualize_ns_incom() -> None: pass @@ -372,23 +429,27 @@ def visualize_darcy(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "2D_DarcyFlow_beta" + str(param) + "_Train.hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "2D_DarcyFlow_beta1.0_Train.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: - data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) - nu = np.array(h5_file["nu"], dtype=np.float32)[nb] # (batch, t, x, y, channel) --> (t, x, y, channel) + data = np.array(h5_file["tensor"], dtype=np.float32)[ + nb + ] # (batch, t, x, y, channel) --> (t, x, y, channel) + nu = np.array(h5_file["nu"], dtype=np.float32)[ + nb + ] # (batch, t, x, y, channel) --> (t, x, y, channel) # Initialize plot fig, ax = plt.subplots(1, 2, figsize=(16, 8)) ax[0].imshow(data.squeeze()) ax[1].imshow(nu.squeeze()) - ax[0].set_title('Data u') - ax[1].set_title('diffusion coefficient nu') - plt.savefig('2D_DarcyFlow.pdf') + ax[0].set_title("Data u") + ax[1].set_title("diffusion coefficient nu") + plt.savefig("2D_DarcyFlow.pdf") print("plot saved") @@ -403,16 +464,18 @@ def visualize_1d_reacdiff(path, param=None): # Read the h5 file and store the data if param is not None: - assert len(param) == 2, 'param should include Nu and Rho as list' - flnm = "ReacDiff_Nu" + str(param[0]) + '_Rho' + str(param[1]) +".hdf5" - assert os.path.isfile(path + flnm), 'no such file! '+ path + flnm + assert len(param) == 2, "param should include Nu and Rho as list" + flnm = "ReacDiff_Nu" + str(param[0]) + "_Rho" + str(param[1]) + ".hdf5" + assert os.path.isfile(path + flnm), "no such file! " + path + flnm else: flnm = "ReacDiff_Nu1.0_Rho1.0.hdf5" nb = 0 with h5py.File(os.path.join(path, flnm), "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) - data = np.array(h5_file["tensor"], dtype=np.float32)[nb] # (batch, t, x, channel) --> (t, x, channel) + data = np.array(h5_file["tensor"], dtype=np.float32)[ + nb + ] # (batch, t, x, channel) --> (t, x, channel) # Initialize plot fig, ax = plt.subplots() @@ -464,7 +527,7 @@ def visualize_1d_reacdiff(path, param=None): ) arg_parser.add_argument( "--params", - nargs='+', + nargs="+", default=None, help="PDE parameters to be plotted", ) @@ -493,4 +556,3 @@ def visualize_1d_reacdiff(path, param=None): visualize_1d_reacdiff(args.data_path, args.params) else: raise ValueError("PDE name not recognized!") - diff --git a/pdebench/data_gen/configs/diff-react.yaml b/pdebench/data_gen/configs/diff-react.yaml index 39372cc..92ab753 100644 --- a/pdebench/data_gen/configs/diff-react.yaml +++ b/pdebench/data_gen/configs/diff-react.yaml @@ -34,13 +34,12 @@ sim: y_top: 1.0 ydim: 128 n: 1 - seed: '???' - -plot: - t_idx: 1.0 # Fraction of the final time step idx to be plotted - dim: 2 # Spatial dimension - channel_idx: 0 # Index of the variable to be plotted + seed: "???" +plot: + t_idx: 1.0 # Fraction of the final time step idx to be plotted + dim: 2 # Spatial dimension + channel_idx: 0 # Index of the variable to be plotted dataverse: lib_name: pyDaRUS @@ -53,8 +52,9 @@ dataverse: identifier_scheme: ORCID identifier: 0000-0003-3619-9122 description: - - text: 2D diffusion-reaction dataset generated for the PDE benchmark paper - date: '2022' + - text: + 2D diffusion-reaction dataset generated for the PDE benchmark paper + date: "2022" contact: - name: Timothy Praditia affiliation: Universität Stuttgart @@ -66,10 +66,18 @@ dataverse: process: processing_methods: - name: FVM - description: Finite Volume Method is a spatial discretization method to calculate spatial derivative in a Partial Differential Equation. It integrates the fluxes at all discrete cell boundaries so that it ensures conservation. + description: + Finite Volume Method is a spatial discretization method to calculate + spatial derivative in a Partial Differential Equation. It integrates + the fluxes at all discrete cell boundaries so that it ensures + conservation. parameters: cell length, cell width - name: RK45 - description: Explicit Runge-Kutta method of order 5(4) is a time integration method to solve the temporal derivative in a Partial Differential Equation. It is an adaptive time integration scheme to ensure better accuracy and computation efficiency. + description: + Explicit Runge-Kutta method of order 5(4) is a time integration + method to solve the temporal derivative in a Partial Differential + Equation. It is an adaptive time integration scheme to ensure better + accuracy and computation efficiency. parameters: time step size, total time, error tolerance method_parameters: - name: cell length diff --git a/pdebench/data_gen/configs/diff-sorp.yaml b/pdebench/data_gen/configs/diff-sorp.yaml index e45a24e..2ad7e1f 100644 --- a/pdebench/data_gen/configs/diff-sorp.yaml +++ b/pdebench/data_gen/configs/diff-sorp.yaml @@ -34,13 +34,12 @@ sim: x_right: 1.0 xdim: 1024 n: 1 - seed: '???' - + seed: "???" + plot: - t_idx: 1.0 # Fraction of the final time step idx to be plotted - dim: 1 # Spatial dimension - channel_idx: 0 # Index of the variable to be plotted - + t_idx: 1.0 # Fraction of the final time step idx to be plotted + dim: 1 # Spatial dimension + channel_idx: 0 # Index of the variable to be plotted dataverse: lib_name: pyDaRUS @@ -53,8 +52,9 @@ dataverse: identifier_scheme: ORCID identifier: 0000-0003-3619-9122 description: - - text: 1D diffusion-sorption dataset generated for the PDE benchmark paper - date: '2022' + - text: + 1D diffusion-sorption dataset generated for the PDE benchmark paper + date: "2022" contact: - name: Timothy Praditia affiliation: Universität Stuttgart @@ -66,10 +66,18 @@ dataverse: process: processing_methods: - name: FVM - description: Finite Volume Method is a spatial discretization method to calculate spatial derivative in a Partial Differential Equation. It integrates the fluxes at all discrete cell boundaries so that it ensures conservation. + description: + Finite Volume Method is a spatial discretization method to calculate + spatial derivative in a Partial Differential Equation. It integrates + the fluxes at all discrete cell boundaries so that it ensures + conservation. parameters: cell length - name: RK45 - description: Explicit Runge-Kutta method of order 5(4) is a time integration method to solve the temporal derivative in a Partial Differential Equation. It is an adaptive time integration scheme to ensure better accuracy and computation efficiency. + description: + Explicit Runge-Kutta method of order 5(4) is a time integration + method to solve the temporal derivative in a Partial Differential + Equation. It is an adaptive time integration scheme to ensure better + accuracy and computation efficiency. parameters: time step size, total time, error tolerance method_parameters: - name: cell length diff --git a/pdebench/data_gen/configs/mode/debug.yaml b/pdebench/data_gen/configs/mode/debug.yaml index e17c0d7..2225b85 100644 --- a/pdebench/data_gen/configs/mode/debug.yaml +++ b/pdebench/data_gen/configs/mode/debug.yaml @@ -14,4 +14,3 @@ hydra: subdir: ${hydra.job.num} launcher: n_jobs: 1 - diff --git a/pdebench/data_gen/configs/mode/default.yaml b/pdebench/data_gen/configs/mode/default.yaml index ecb1b8f..bd34f5a 100644 --- a/pdebench/data_gen/configs/mode/default.yaml +++ b/pdebench/data_gen/configs/mode/default.yaml @@ -8,4 +8,3 @@ hydra: sweep: dir: ${oc.env:WORKING_DIR,multirun}/${hydra.job.name}/${now:%Y-%m-%d}/${now:%H-%M-%S}/${hydra.job.override_dirname} subdir: ${hydra.job.num} - diff --git a/pdebench/data_gen/configs/mode/slurm.yaml b/pdebench/data_gen/configs/mode/slurm.yaml index c3c619e..eaff80a 100644 --- a/pdebench/data_gen/configs/mode/slurm.yaml +++ b/pdebench/data_gen/configs/mode/slurm.yaml @@ -15,4 +15,3 @@ hydra: tasks_per_node: 1 mem_gb: 16 timeout_min: 719 # just under 12 hours is good for many clusters - diff --git a/pdebench/data_gen/configs/ns_incomp.yaml b/pdebench/data_gen/configs/ns_incomp.yaml index ec76433..086b771 100644 --- a/pdebench/data_gen/configs/ns_incomp.yaml +++ b/pdebench/data_gen/configs/ns_incomp.yaml @@ -7,25 +7,25 @@ artefact_dir: ${oc.env:ARTEFACT_DIR,artefacts} dataverse: dataset_id: doi:10.18419/darus-2984 -sim_name: 'ns_sim_2d' +sim_name: "ns_sim_2d" label: null -# Solver Parameters +# Solver Parameters domain_size: [1, 1] -grid_size: [256,256] +grid_size: [256, 256] #['scalar_grid', extrapolation_x:(type or bound), extrapolation_y:(type or bound)] -particle_extrapolation: 'BOUNDARY' - +particle_extrapolation: "BOUNDARY" + #['staggered_grid', extrapolation_x:(type or bound), extrapolation_y:(type or bound)] -velocity_extrapolation: 'ZERO' +velocity_extrapolation: "ZERO" # Fluid characteristics NU: 0.01 #(kinematic viscosity) # External force # enable_gravity: false -force_extrapolation: 'ZERO' +force_extrapolation: "ZERO" # Fluctuation Generator Parameters (Noise) seed: 1 @@ -36,7 +36,7 @@ force_scale: 0.15 #params for IncompressibleFlow(Physics) n_steps: 100000 -DT : 0.00005 +DT: 0.00005 frame_int: 100 n_batch: 1 @@ -45,10 +45,9 @@ n_batch: 1 # save_images: false # save_gif: false save_h5: true -profile: false # Run performance profiling -upload: false # upload to DARUS - requires key +profile: false # Run performance profiling +upload: false # upload to DARUS - requires key -backend: 'jax' -device: 'GPU' +backend: "jax" +device: "GPU" jit: true - diff --git a/pdebench/data_gen/configs/radial_dam_break.yaml b/pdebench/data_gen/configs/radial_dam_break.yaml index 3071940..fb5e13c 100644 --- a/pdebench/data_gen/configs/radial_dam_break.yaml +++ b/pdebench/data_gen/configs/radial_dam_break.yaml @@ -33,13 +33,12 @@ sim: x_right: 2.5 y_bottom: -2.5 y_top: 2.5 - seed: '???' + seed: "???" - plot: - t_idx: 1.0 # Fraction of the final time step idx to be plotted - dim: 2 # Spatial dimension - channel_idx: 0 # Index of the variable to be plotted + t_idx: 1.0 # Fraction of the final time step idx to be plotted + dim: 2 # Spatial dimension + channel_idx: 0 # Index of the variable to be plotted dataverse: lib_name: pyDaRUS @@ -52,8 +51,10 @@ dataverse: identifier_scheme: ORCID identifier: 0000-0001-8070-2384 description: - - text: 2D shallow-water equation dataset generated for the PDE benchmark paper - date: '2022' + - text: + 2D shallow-water equation dataset generated for the PDE benchmark + paper + date: "2022" contact: - name: Raphael Leiteritz affiliation: Universität Stuttgart @@ -65,7 +66,11 @@ dataverse: process: processing_methods: - name: FVM - description: Finite Volume Method is a spatial discretization method to calculate spatial derivative in a Partial Differential Equation. It integrates the fluxes at all discrete cell boundaries so that it ensures conservation. + description: + Finite Volume Method is a spatial discretization method to calculate + spatial derivative in a Partial Differential Equation. It integrates + the fluxes at all discrete cell boundaries so that it ensures + conservation. parameters: cell length, cell width method_parameters: - name: cell length @@ -85,4 +90,4 @@ dataverse: unit: s value: 1 engMeta: {} - codeMeta: {} \ No newline at end of file + codeMeta: {} diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py index 1938425..e1e8eb9 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,24 +144,24 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations import time -import sys from math import ceil -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax import jax.numpy as jnp from jax import device_put +# Hydra +from omegaconf import DictConfig + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: - print('advection velocity: {}'.format(cfg.args.beta)) + print(f"advection velocity: {cfg.args.beta}") # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -177,37 +176,37 @@ def evolve(u): i_save = 0 tm_ini = time.time() - it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time)/cfg.args.dt_save) + 1 + it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) while t < cfg.args.fin_time: - print('save data at t = {0:.3f}'.format(t)) + print(f"save data at t = {t:.3f}") u = set_function(xc, t, cfg.args.beta) uu = uu.at[i_save].set(u) t += cfg.args.dt_save i_save += 1 tm_fin = time.time() - print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + print(f"total elapsed time is {tm_fin - tm_ini} sec") uu = uu.at[-1].set(u) return uu, t @jax.jit def set_function(x, t, beta): - return jnp.sin(2.*jnp.pi*(x - beta*t)) + return jnp.sin(2.0 * jnp.pi * (x - beta * t)) u = set_function(xc, t=0, beta=cfg.args.beta) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print('final time is: {0:.3f}'.format(t)) + print(f"final time is: {t:.3f}") + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" + jnp.save(cwd + cfg.args.save + "/Advection_beta" + str(cfg.args.beta), uu) + jnp.save(cwd + cfg.args.save + "/x_coordinate", xe) + jnp.save(cwd + cfg.args.save + "/t_coordinate", tc) - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' - jnp.save(cwd + cfg.args.save + '/Advection_beta' + str(cfg.args.beta), uu) - jnp.save(cwd + cfg.args.save + '/x_coordinate', xe) - jnp.save(cwd + cfg.args.save + '/t_coordinate', tc) -if __name__=='__main__': +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py index 19f6a12..ea951f9 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,33 +144,33 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import sys -from math import ceil, log, exp +from __future__ import annotations + import random from pathlib import Path -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax -from jax import vmap import jax.numpy as jnp from jax import device_put, lax -sys.path.append('..') -from utils import init_multi, Courant, save_data, bc, limiting +# Hydra +from omegaconf import DictConfig + +sys.path.append("..") +from utils import Courant, bc, init_multi, limiting def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: # basic parameters dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # cell edge coordinate xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) @@ -187,11 +186,13 @@ def main(cfg: DictConfig) -> None: dt_save = cfg.multi.dt_save CFL = cfg.multi.CFL if cfg.multi.if_rand_param: - beta = exp(random.uniform(log(0.01), log(100))) # uniform number between 0.01 to 100 + beta = exp( + random.uniform(log(0.01), log(100)) + ) # uniform number between 0.01 to 100 else: beta = cfg.multi.beta - print('beta: ', beta) + print("beta: ", beta) @jax.jit def evolve(u): @@ -199,7 +200,7 @@ def evolve(u): tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) @@ -244,7 +245,7 @@ def _update(carry): return u, dt carry = u, dt - u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + u, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -253,37 +254,44 @@ def _update(carry): @jax.jit def update(u, u_tmp, dt): f = flux(u_tmp) - u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + u -= dt * dx_inv * (f[1 : cfg.multi.nx + 1] - f[0 : cfg.multi.nx]) return u def flux(u): - _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u + _u = bc( + u, dx, Ncell=cfg.multi.nx + ) # index 2 for _U is equivalent with index 0 for u uL, uR = limiting(_u, cfg.multi.nx, if_second_order=cfg.multi.if_second_order) fL = uL * beta fR = uR * beta # upwind advection scheme - f_upwd = 0.5 * (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] - - jnp.abs(beta)*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) + f_upwd = 0.5 * ( + fR[1 : cfg.multi.nx + 2] + + fL[2 : cfg.multi.nx + 3] + - jnp.abs(beta) * (uL[2 : cfg.multi.nx + 3] - uR[1 : cfg.multi.nx + 2]) + ) return f_upwd u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) u = device_put(u) # putting variables in GPU (not necessary??) - #vm_evolve = vmap(evolve, 0, 0) - #uu = vm_evolve(u) - vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + # vm_evolve = vmap(evolve, 0, 0) + # uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name="j"), axis_name="i") local_devices = jax.local_device_count() - uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers // local_devices, -1])) # reshape before saving uu = uu.reshape((-1, *uu.shape[2:])) - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) - jnp.save(cwd+cfg.multi.save+'1D_Advection_Sols_beta'+str(beta)[:5], uu) - jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) - jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) + jnp.save(cwd + cfg.multi.save + "1D_Advection_Sols_beta" + str(beta)[:5], uu) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) + -if __name__=='__main__': +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml index 481e65f..8c484b7 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e-1.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e-1 +beta: 1.e-1 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml index c1a1492..bebc582 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e0.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e0 +beta: 1.e0 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml index a7164b1..db14c50 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta1e1.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e1 +beta: 1.e1 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml index 3d324c5..aa5539e 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e-1.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 2.e-1 +beta: 2.e-1 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml index fd69b3c..73832a2 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta2e0.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 2.e0 +beta: 2.e0 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml index 91a0ad9..d8114c8 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e-1.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 4.e-1 +beta: 4.e-1 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml index 3788878..30ab03a 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/beta4e0.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 4.e0 +beta: 4.e0 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml index c1a1492..bebc582 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/args/config.yaml @@ -1,10 +1,10 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e0 +beta: 1.e0 if_show: 1 -init_mode: 'sin' +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml index 569c31a..0bdcd23 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e-1.yaml @@ -1,15 +1,15 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e-1 +beta: 1.e-1 if_show: 1 numbers: 10000 CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml index 4d7d9bd..5d8f5fd 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta1e0.yaml @@ -1,11 +1,11 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e0 +beta: 1.e0 if_show: 1 numbers: 10000 CFL: 4.e-1 diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml index 25edbee..cb46b32 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e-1.yaml @@ -1,15 +1,15 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 2.e-1 +beta: 2.e-1 if_show: 1 numbers: 10000 CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml index 5171dc6..3185fc7 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta2e0.yaml @@ -1,15 +1,15 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 2.e0 +beta: 2.e0 if_show: 1 numbers: 10000 CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml index 2b5385d..0125f48 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e-1.yaml @@ -1,15 +1,15 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 4.e-1 +beta: 4.e-1 if_show: 1 numbers: 10000 CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml index a9b2961..d6d8772 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/beta4e0.yaml @@ -1,15 +1,15 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 4.e0 +beta: 4.e0 if_show: 1 numbers: 10000 CFL: 4.e-1 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml index 6e60508..ce8be8b 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config.yaml @@ -1,11 +1,11 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. nx: 1024 xL: 0. xR: 1. -beta : 1.e0 +beta: 1.e0 if_show: 1 numbers: 100 CFL: 3.e-1 diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml index 06c0f34..21ce8e9 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/config/multi/config2D.yaml @@ -1,4 +1,4 @@ -save: '../save/advection/' +save: "../save/advection/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -8,8 +8,8 @@ xL: 0. xR: 1. yL: 0. yR: 1. -betaX : 1.e0 -betaY : 1.e0 +betaX: 1.e0 +betaY: 1.e0 if_show: 1 numbers: 4 CFL: 2.5e-1 diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh index 384989a..08e2ebd 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_testset.sh @@ -1,7 +1,8 @@ +#! /bin/bash CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e0.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e-1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e0.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e-1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e0.yaml -CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml \ No newline at end of file +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh index 0d92fee..48f5d93 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/run_trainset.sh @@ -1,3 +1,4 @@ +#! /bin/bash CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta1e0.yaml CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta1e-1.yaml CUDA_VISIBLE_DEVICES='2,3' python3 advection_multi_solution_Hydra.py +multi=beta2e0.yaml diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py index 4adc063..f8eab18 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,32 +144,35 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import time import sys +import time from math import ceil -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax import jax.numpy as jnp from jax import device_put, lax -sys.path.append('..') -from utils import init, Courant, Courant_diff, save_data, bc, limiting +# Hydra +from omegaconf import DictConfig + +sys.path.append("..") +from utils import Courant, Courant_diff, bc, init, limiting + def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: # basic parameters - pi_inv = 1. / jnp.pi + pi_inv = 1.0 / jnp.pi dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -193,7 +195,7 @@ def evolve(u): tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) @@ -225,14 +227,14 @@ def _save(_carry): uu = uu.at[-1].set(u) tm_fin = time.time() - print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + print(f"total elapsed time is {tm_fin - tm_ini} sec") return uu, t @jax.jit def simulation_fn(i, carry): u, t, dt, steps, tsave = carry dt_adv = Courant(u, dx) * CFL - dt_dif = Courant_diff(dx, cfg.args.epsilon*pi_inv) * CFL + dt_dif = Courant_diff(dx, cfg.args.epsilon * pi_inv) * CFL dt = jnp.min(jnp.array([dt_adv, dt_dif, fin_time - t, tsave - t])) def _update(carry): @@ -244,7 +246,7 @@ def _update(carry): return u, dt carry = u, dt - u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + u, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -253,36 +255,69 @@ def _update(carry): @jax.jit def update(u, u_tmp, dt): f = flux(u_tmp) - u -= dt * dx_inv * (f[1:cfg.args.nx + 1] - f[0:cfg.args.nx]) + u -= dt * dx_inv * (f[1 : cfg.args.nx + 1] - f[0 : cfg.args.nx]) return u def flux(u): - _u = bc(u, dx, Ncell=cfg.args.nx) # index 2 for _U is equivalent with index 0 for u - uL, uR = limiting(_u, cfg.args.nx, if_second_order=1.) - fL = 0.5*uL**2 - fR = 0.5*uR**2 + _u = bc( + u, dx, Ncell=cfg.args.nx + ) # index 2 for _U is equivalent with index 0 for u + uL, uR = limiting(_u, cfg.args.nx, if_second_order=1.0) + fL = 0.5 * uL**2 + fR = 0.5 * uR**2 # upwind advection scheme - f_upwd = 0.5 * (fR[1:cfg.args.nx+2] + fL[2:cfg.args.nx+3] - - 0.5*jnp.abs(uL[2:cfg.args.nx+3] + uR[1:cfg.args.nx+2])*(uL[2:cfg.args.nx+3] - uR[1:cfg.args.nx+2])) + f_upwd = 0.5 * ( + fR[1 : cfg.args.nx + 2] + + fL[2 : cfg.args.nx + 3] + - 0.5 + * jnp.abs(uL[2 : cfg.args.nx + 3] + uR[1 : cfg.args.nx + 2]) + * (uL[2 : cfg.args.nx + 3] - uR[1 : cfg.args.nx + 2]) + ) # source term - f_upwd += - cfg.args.epsilon*pi_inv*(_u[2:cfg.args.nx+3] - _u[1:cfg.args.nx+2])*dx_inv + f_upwd += ( + -cfg.args.epsilon + * pi_inv + * (_u[2 : cfg.args.nx + 3] - _u[1 : cfg.args.nx + 2]) + * dx_inv + ) return f_upwd u = init(xc=xc, mode=cfg.args.init_mode, u0=cfg.args.u0, du=cfg.args.du) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print('final time is: {0:.3f}'.format(t)) - - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' - if cfg.args.init_mode=='sinsin': - jnp.save(cwd + cfg.args.save + '/Burgers_' + cfg.args.init_mode + '_u' + str(cfg.args.u0) + '_du' + str( - cfg.args.du) + '_Nu' + str(cfg.args.epsilon), uu) + print(f"final time is: {t:.3f}") + + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" + if cfg.args.init_mode == "sinsin": + jnp.save( + cwd + + cfg.args.save + + "/Burgers_" + + cfg.args.init_mode + + "_u" + + str(cfg.args.u0) + + "_du" + + str(cfg.args.du) + + "_Nu" + + str(cfg.args.epsilon), + uu, + ) else: - jnp.save(cwd + cfg.args.save + '/Burgers_' + cfg.args.init_mode + '_u' + str(cfg.args.u0) + '_Nu' + str( - cfg.args.epsilon), uu) - jnp.save(cwd + cfg.args.save+'/x_coordinate', xc) - jnp.save(cwd + cfg.args.save+'/t_coordinate', tc) - -if __name__=='__main__': + jnp.save( + cwd + + cfg.args.save + + "/Burgers_" + + cfg.args.init_mode + + "_u" + + str(cfg.args.u0) + + "_Nu" + + str(cfg.args.epsilon), + uu, + ) + jnp.save(cwd + cfg.args.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.args.save + "/t_coordinate", tc) + + +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py index da17c10..5e07805 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,34 +144,36 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import sys +from __future__ import annotations + import random +import sys from math import ceil, exp, log from pathlib import Path -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax -from jax import vmap import jax.numpy as jnp from jax import device_put, lax -sys.path.append('..') -from utils import init_multi, Courant, Courant_diff, save_data, bc, limiting +# Hydra +from omegaconf import DictConfig + +sys.path.append("..") +from utils import Courant, Courant_diff, bc, init_multi, limiting def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: # basic parameters - pi_inv = 1. / jnp.pi + pi_inv = 1.0 / jnp.pi dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # cell edge coordinate xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) @@ -185,10 +186,12 @@ def main(cfg: DictConfig) -> None: dt_save = cfg.multi.dt_save CFL = cfg.multi.CFL if cfg.multi.if_rand_param: - epsilon = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + epsilon = exp( + random.uniform(log(0.001), log(10)) + ) # uniform number between 0.01 to 100 else: epsilon = cfg.multi.epsilon - print('epsilon: ', epsilon) + print("epsilon: ", epsilon) # t-coordinate it_tot = ceil((fin_time - ini_time) / dt_save) + 1 tc = jnp.arange(it_tot + 1) * dt_save @@ -199,7 +202,7 @@ def evolve(u): tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) @@ -233,7 +236,7 @@ def _show(_carry): def simulation_fn(i, carry): u, t, dt, steps, tsave = carry dt_adv = Courant(u, dx) * CFL - dt_dif = Courant_diff(dx, epsilon*pi_inv) * CFL + dt_dif = Courant_diff(dx, epsilon * pi_inv) * CFL dt = jnp.min(jnp.array([dt_adv, dt_dif, fin_time - t, tsave - t])) def _update(carry): @@ -245,7 +248,7 @@ def _update(carry): return u, dt carry = u, dt - u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + u, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -254,39 +257,57 @@ def _update(carry): @jax.jit def update(u, u_tmp, dt): f = flux(u_tmp) - u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + u -= dt * dx_inv * (f[1 : cfg.multi.nx + 1] - f[0 : cfg.multi.nx]) return u def flux(u): - _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u - uL, uR = limiting(_u, cfg.multi.nx, if_second_order=1.) - fL = 0.5*uL**2 - fR = 0.5*uR**2 + _u = bc( + u, dx, Ncell=cfg.multi.nx + ) # index 2 for _U is equivalent with index 0 for u + uL, uR = limiting(_u, cfg.multi.nx, if_second_order=1.0) + fL = 0.5 * uL**2 + fR = 0.5 * uR**2 # upwind advection scheme - f_upwd = 0.5 * (fR[1:cfg.multi.nx+2] + fL[2:cfg.multi.nx+3] - - 0.5*jnp.abs(uL[2:cfg.multi.nx+3] + uR[1:cfg.multi.nx+2])*(uL[2:cfg.multi.nx+3] - uR[1:cfg.multi.nx+2])) + f_upwd = 0.5 * ( + fR[1 : cfg.multi.nx + 2] + + fL[2 : cfg.multi.nx + 3] + - 0.5 + * jnp.abs(uL[2 : cfg.multi.nx + 3] + uR[1 : cfg.multi.nx + 2]) + * (uL[2 : cfg.multi.nx + 3] - uR[1 : cfg.multi.nx + 2]) + ) # source term - f_upwd += - epsilon*pi_inv*(_u[2:cfg.multi.nx+3] - _u[1:cfg.multi.nx+2])*dx_inv + f_upwd += ( + -epsilon + * pi_inv + * (_u[2 : cfg.multi.nx + 3] - _u[1 : cfg.multi.nx + 2]) + * dx_inv + ) return f_upwd u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) u = device_put(u) # putting variables in GPU (not necessary??) - #vm_evolve = vmap(evolve, 0, 0) - #uu = vm_evolve(u) - vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + # vm_evolve = vmap(evolve, 0, 0) + # uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name="j"), axis_name="i") local_devices = jax.local_device_count() - uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers // local_devices, -1])) + + cwd = hydra.utils.get_original_cwd() + "/" + jnp.save(cwd + cfg.multi.save + "1D_Burgers_Sols_Nu" + str(epsilon)[:5], uu) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) # reshape before saving uu = uu.reshape((-1, *uu.shape[2:])) - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) - jnp.save(cwd+cfg.multi.save+'1D_Burgers_Sols_Nu'+str(epsilon)[:5], uu) - jnp.save(cwd + cfg.multi.save + '/x_coordinate', xc) - jnp.save(cwd + cfg.multi.save + '/t_coordinate', tc) + jnp.save(cwd + cfg.multi.save + "1D_Burgers_Sols_Nu" + str(epsilon)[:5], uu) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) + -if __name__=='__main__': +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml index 1fa8247..bdb4851 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/config.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,12 +6,12 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' +init_mode: "sin" init_key: 2022 -if_rand_param: None \ No newline at end of file +if_rand_param: None diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml index 62def9e..2e2faaa 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-1 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml index 1f56fe3..a069740 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml index f2ea891..88907d2 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e-3.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-3 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml index 88f0d44..0563653 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e0 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml index 2acd61c..214752e 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e1 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml index fb53b28..6161d32 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/possin_eps1e2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'possin' \ No newline at end of file +init_mode: "possin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml index 7c5b1f2..1a6d55a 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-1 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml index 942c834..137e61c 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml index 99edd16..67f0974 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1.e-1 -du : 0.1 +u0: 1.e-1 +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml index 9309d59..9078bcd 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1.e-2 -du : 0.1 +u0: 1.e-2 +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml index 814a1b8..fef36a8 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1.e1 -du : 0.1 +u0: 1.e1 +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml index bf4118d..efe85dd 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-2_u01e2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1.e2 -du : 0.1 +u0: 1.e2 +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml index a190a58..59dbfe7 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e-3.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-3 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml index b1e460d..4aaea8b 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e0 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml index 48cf919..4e03682 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e1 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml index 0515b8c..8526ee6 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sin_eps1e2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sin' \ No newline at end of file +init_mode: "sin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml index d1a644e..1bb49e3 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du01.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.1 +u0: 1. +du: 0.1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml index 2376eb6..051f70a 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du025.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.25 +u0: 1. +du: 0.25 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml index 9274eef..fa55f69 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du05.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 0.5 +u0: 1. +du: 0.5 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml index a561103..d1d512a 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 1 +u0: 1. +du: 1 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml index 9137a1a..e0d48c8 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 2 +u0: 1. +du: 2 CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml index cd789a5..6f0ea11 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/args/sinsin_eps1e-2_du5.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -6,10 +6,10 @@ nx: 1024 xL: -1. xR: 1. epsilon: 1.e-2 -u0 : 1. -du : 5. +u0: 1. +du: 5. CFL: 4.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'sinsin' \ No newline at end of file +init_mode: "sinsin" diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml index db263b5..76d5873 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml index 315f7f7..f8fccd3 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml index 9a981a8..798bc67 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e-3.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml index bf78a94..6562ba4 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml index 3f4d4d0..93d252b 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml index 13c9935..638746a 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml index 52ad377..afe96db 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e-3.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml index fc30c15..a867e22 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/2e0.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml index eaf7974..9b2f9e6 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml index 07ba0c2..c3b6706 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-2.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml index 6ff09f2..15c940f 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e-3.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml index 0e8a8d2..59c9465 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/4e0.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,4 +11,4 @@ if_second_order: 1. numbers: 10000 show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml index 39fb552..3bf3e39 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/config/multi/config.yaml @@ -1,4 +1,4 @@ -save: '../save/burgers/' +save: "../save/burgers/" dt_save: 0.05 ini_time: 0. fin_time: 2. diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh index 82b0310..95db658 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_testset.sh @@ -1,3 +1,4 @@ +#!/bin/bash CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e0.yaml CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e1.yaml CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=possin_eps1e2.yaml @@ -22,4 +23,4 @@ CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du01.yaml CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du2.yaml CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du5.yaml CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du05.yaml -CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du025.yaml \ No newline at end of file +CUDA_VISIBLE_DEVICES='3' python3 burgers_Hydra.py +args=sinsin_eps1e-2_du025.yaml diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh index 5899961..25994e9 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/run_trainset.sh @@ -1,3 +1,4 @@ +#!/bin/sh CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e0.yaml CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-1.yaml CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=1e-2.yaml @@ -9,4 +10,4 @@ CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=2e-3.y CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e0.yaml CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-1.yaml CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-2.yaml -CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-3.yaml \ No newline at end of file +CUDA_VISIBLE_DEVICES='0,2' python3 burgers_multi_solution_Hydra.py +multi=4e-3.yaml diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py index 3ac7d98..ad2366b 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,57 +144,58 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import time import sys -from math import ceil +import time from functools import partial +from math import ceil -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - -from jax import jit import jax.numpy as jnp -from jax import device_put, lax +from jax import device_put, jit, lax + +# Hydra +from omegaconf import DictConfig # if double precision -#from jax.config import config -#config.update("jax_enable_x64", True) +# from jax.config import config +# config.update("jax_enable_x64", True) -sys.path.append('..') -from utils import init_HD, Courant_HD, Courant_vis_HD, save_data_HD, bc_HD, limiting_HD, bc_HD_vis +sys.path.append("..") +from utils import Courant_HD, Courant_vis_HD, bc_HD, init_HD, limiting_HD, save_data_HD def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config", config_name="config") def main(cfg: DictConfig) -> None: # physical constants gamma = cfg.args.gamma # 3D non-relativistic gas - gammi1 = gamma - 1. - gamminv1 = 1. / gammi1 + gammi1 = gamma - 1.0 + gamminv1 = 1.0 / gammi1 gamgamm1inv = gamma * gamminv1 - gammi1 = gamma - 1. - gampl1 = gamma + 1. - gammi3 = gamma - 3. - gampl3 = gamma + 3. + gammi1 = gamma - 1.0 + gampl1 = gamma + 1.0 + gammi3 = gamma - 3.0 + gampl3 = gamma + 3.0 - visc = cfg.args.zeta + cfg.args.eta / 3. + visc = cfg.args.zeta + cfg.args.eta / 3.0 - BCs = ['trans', 'periodic', 'KHI'] # reflect + BCs = ["trans", "periodic", "KHI"] # reflect assert cfg.args.bc in BCs, "bc should be in 'trans, reflect, periodic'" dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny - dy_inv = 1. / dy + dy_inv = 1.0 / dy # dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz - dz_inv = 1. / dz + dz_inv = 1.0 / dz # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -216,56 +216,77 @@ def evolve(Q): steps = 0 i_save = 0 tm_ini = time.time() - dt = 0. + dt = 0.0 while t < cfg.args.fin_time: if t >= tsave: - print('save data at t = {0:.3f}'.format(t)) - save_data_HD(Q[:,2:-2,2:-2,2:-2], xc, yc, zc, i_save, cfg.args.save) + print(f"save data at t = {t:.3f}") + save_data_HD(Q[:, 2:-2, 2:-2, 2:-2], xc, yc, zc, i_save, cfg.args.save) tsave += cfg.args.dt_save i_save += 1 - if steps%cfg.args.show_steps==0 and cfg.args.if_show: - print('now {0:d}-steps, t = {1:.3f}, dt = {2:.3f}'.format(steps, t, dt)) + if steps % cfg.args.show_steps == 0 and cfg.args.if_show: + print(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") carry = (Q, t, dt, steps, tsave) - Q, t, dt, steps, tsave = lax.fori_loop(0, cfg.args.show_steps, simulation_fn, carry) + Q, t, dt, steps, tsave = lax.fori_loop( + 0, cfg.args.show_steps, simulation_fn, carry + ) tm_fin = time.time() - print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) - save_data_HD(Q[:,2:-2,2:-2,2:-2], xc, yc, zc, - i_save, cfg.args.save, cfg.args.dt_save, if_final=True) + print(f"total elapsed time is {tm_fin - tm_ini} sec") + save_data_HD( + Q[:, 2:-2, 2:-2, 2:-2], + xc, + yc, + zc, + i_save, + cfg.args.save, + cfg.args.dt_save, + if_final=True, + ) return t @jit def simulation_fn(i, carry): Q, t, dt, steps, tsave = carry - dt = Courant_HD(Q[:,2:-2,2:-2,2:-2], dx, dy, dz, cfg.args.gamma) * cfg.args.CFL + dt = ( + Courant_HD(Q[:, 2:-2, 2:-2, 2:-2], dx, dy, dz, cfg.args.gamma) + * cfg.args.CFL + ) dt = jnp.min(jnp.array([dt, cfg.args.fin_time - t, tsave - t])) def _update(carry): Q, dt = carry # preditor step for calculating t+dt/2-th time step - Q_tmp = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = bc_HD( + Q, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u Q_tmp = update(Q, Q_tmp, dt * 0.5) # update using flux at t+dt/2-th time step - Q_tmp = bc_HD(Q_tmp, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = bc_HD( + Q_tmp, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u Q = update(Q, Q_tmp, dt) # update via viscosity - #d_min = jnp.min(Q[0]) - #dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity - dt_vis = Courant_vis_HD(dx, dy, dz, cfg.args.eta, cfg.args.zeta) * cfg.args.CFL + # d_min = jnp.min(Q[0]) + # dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity + dt_vis = ( + Courant_vis_HD(dx, dy, dz, cfg.args.eta, cfg.args.zeta) * cfg.args.CFL + ) dt_vis = jnp.min(jnp.array([dt_vis, dt])) - t_vis = 0. + t_vis = 0.0 carry = Q, dt, dt_vis, t_vis - Q, dt, dt_vis, t_vis = lax.while_loop(lambda x: x[1] - x[3] > 1.e-8, update_vis, carry) + Q, dt, dt_vis, t_vis = lax.while_loop( + lambda x: x[1] - x[3] > 1.0e-8, update_vis, carry + ) return Q, dt carry = Q, dt - Q, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + Q, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -275,10 +296,10 @@ def _update(carry): def update(Q, Q_tmp, dt): # calculate conservative variables D0 = Q[0] - Mx = Q[1]*Q[0] - My = Q[2]*Q[0] - Mz = Q[3]*Q[0] - E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + Mx = Q[1] * Q[0] + My = Q[2] * Q[0] + Mz = Q[3] * Q[0] + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -292,34 +313,46 @@ def update(Q, Q_tmp, dt): fz = flux_z(Q_tmp) # update conservative variables - dtdx, dtdy, dtdz = dt*dx_inv, dt*dy_inv, dt*dz_inv - D0 -= dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2])\ + dtdx, dtdy, dtdz = dt * dx_inv, dt * dy_inv, dt * dz_inv + D0 -= ( + dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2]) + + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2]) + dtdz * (fz[0, 2:-2, 2:-2, 1:] - fz[0, 2:-2, 2:-2, :-1]) + ) - Mx -= dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2])\ + Mx -= ( + dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2]) + + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2]) + dtdz * (fz[1, 2:-2, 2:-2, 1:] - fz[1, 2:-2, 2:-2, :-1]) + ) - My -= dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2])\ + My -= ( + dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2]) + + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2]) + dtdz * (fz[2, 2:-2, 2:-2, 1:] - fz[2, 2:-2, 2:-2, :-1]) + ) - Mz -= dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2])\ + Mz -= ( + dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2]) + + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2]) + dtdz * (fz[3, 2:-2, 2:-2, 1:] - fz[3, 2:-2, 2:-2, :-1]) + ) - E0 -= dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2])\ + E0 -= ( + dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2]) + + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2]) + dtdz * (fz[4, 2:-2, 2:-2, 1:] - fz[4, 2:-2, 2:-2, :-1]) + ) # reverse primitive variables - Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d - Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx/D0) # vx - Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My/D0) # vy - Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz/D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2)/D0)) # p - Q = Q.at[4].set(jnp.where(Q[4] > 1.e-8, Q[4], cfg.args.p_floor)) + Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p + Q = Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) return Q @@ -339,17 +372,20 @@ def _update_vis_x(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-1, 2:-2, 2:-2] + D0[1:-2, 2:-2, 2:-2]) - fMx = (eta + visc) * Dm * dx_inv * (\ - Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) - fMy = eta * Dm * dx_inv * (\ - Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) - fMz = eta * Dm * dx_inv * (\ - Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) - fE = 0.5 * (eta + visc) * Dm * dx_inv * (\ - Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2)\ - + 0.5 * eta * Dm * dx_inv * (\ - (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2)\ - + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2)) + fMx = ( + (eta + visc) + * Dm + * dx_inv + * (Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) + ) + fMy = eta * Dm * dx_inv * (Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) + fMz = eta * Dm * dx_inv * (Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dx_inv * ( + Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2 + ) + 0.5 * eta * Dm * dx_inv * ( + (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2) + + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -366,7 +402,9 @@ def _update_vis_x(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt @@ -384,17 +422,20 @@ def _update_vis_y(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-2, 2:-1, 2:-2] + D0[2:-2, 1:-2, 2:-2]) - fMx = eta * Dm * dy_inv * (\ - Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) - fMy = (eta + visc) * Dm * dy_inv * (\ - Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) - fMz = eta * Dm * dy_inv * (\ - Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) - fE = 0.5 * (eta + visc) * Dm * dy_inv * (\ - Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2)\ - + 0.5 * eta * Dm * dy_inv * ( \ - (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) \ - + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2)) + fMx = eta * Dm * dy_inv * (Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) + fMy = ( + (eta + visc) + * Dm + * dy_inv + * (Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) + ) + fMz = eta * Dm * dy_inv * (Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dy_inv * ( + Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2 + ) + 0.5 * eta * Dm * dy_inv * ( + (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) + + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -411,7 +452,9 @@ def _update_vis_y(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt @@ -429,17 +472,20 @@ def _update_vis_z(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-2, 2:-2, 2:-1] + D0[2:-2, 2:-2, 1:-2]) - fMx = eta * Dm * dz_inv * (\ - Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) - fMy = eta * Dm * dz_inv * (\ - Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) - fMz = (eta + visc) * Dm * dz_inv * (\ - Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) - fE = 0.5 * (eta + visc) * Dm * dz_inv * (\ - Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2)\ - + 0.5 * eta * Dm * dz_inv * ( \ - (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) \ - + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2)) + fMx = eta * Dm * dz_inv * (Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) + fMy = eta * Dm * dz_inv * (Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) + fMz = ( + (eta + visc) + * Dm + * dz_inv + * (Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) + ) + fE = 0.5 * (eta + visc) * Dm * dz_inv * ( + Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2 + ) + 0.5 * eta * Dm * dz_inv * ( + (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) + + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -456,12 +502,16 @@ def _update_vis_z(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt Q, dt, dt_vis, t_vis = carry - Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = bc_HD( + Q, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) carry = Q, dt_ev @@ -477,7 +527,7 @@ def _update_vis_z(carry): @jit def flux_x(Q): QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = HLL(QL, QR, direc=0) + # f_Riemann = HLL(QL, QR, direc=0) f_Riemann = HLLC(QL, QR, direc=0) return f_Riemann @@ -485,15 +535,17 @@ def flux_x(Q): def flux_y(Q): _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) - f_Riemann = jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + # f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + f_Riemann = jnp.transpose( + HLLC(QL, QR, direc=1), (0, 3, 1, 2) + ) # (x,y,z) = (Z,X,Y) return f_Riemann @jit def flux_z(Q): _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) + # f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) return f_Riemann @@ -503,21 +555,31 @@ def HLL(QL, QR, direc): iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 cfL = jnp.sqrt(gamma * QL[4] / QL[0]) cfR = jnp.sqrt(gamma * QR[4] / QR[0]) - Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave - Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave - dcfi = 1. / (Sfr - Sfl + 1.e-8) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # right-going wave + dcfi = 1.0 / (Sfr - Sfl + 1.0e-8) UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) UL = UL.at[0].set(QL[0]) UL = UL.at[iX].set(QL[0] * QL[iX]) UL = UL.at[iY].set(QL[0] * QL[iY]) UL = UL.at[iZ].set(QL[0] * QL[iZ]) - UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UL = UL.at[4].set( + gamminv1 * QL[4] + + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ]) + ) UR = UR.at[0].set(QR[0]) UR = UR.at[iX].set(QR[0] * QR[iX]) UR = UR.at[iY].set(QR[0] * QR[iY]) UR = UR.at[iZ].set(QR[0] * QR[iZ]) - UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + UR = UR.at[4].set( + gamminv1 * QR[4] + + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ]) + ) fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) fL = fL.at[0].set(UL[iX]) @@ -531,38 +593,56 @@ def HLL(QL, QR, direc): fR = fR.at[iZ].set(UR[iX] * QR[iZ]) fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) # upwind advection scheme - fHLL = dcfi * (Sfr * fR[:, 1:-2] - Sfl * fL[:, 2:-1] - + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2])) + fHLL = dcfi * ( + Sfr * fR[:, 1:-2] + - Sfl * fL[:, 2:-1] + + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2]) + ) # L: left of cell = right-going, R: right of cell: left-going - f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fHLL) - f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) + f_Riemann = jnp.where(Sfl > 0.0, fR[:, 1:-2], fHLL) + f_Riemann = jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) return f_Riemann @partial(jit, static_argnums=(2,)) def HLLC(QL, QR, direc): - """ full-Godunov method -- exact shock solution""" + """full-Godunov method -- exact shock solution""" iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 cfL = jnp.sqrt(gamma * QL[4] / QL[0]) cfR = jnp.sqrt(gamma * QR[4] / QR[0]) - Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave - Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # right-going wave UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) UL = UL.at[0].set(QL[0]) UL = UL.at[iX].set(QL[0] * QL[iX]) UL = UL.at[iY].set(QL[0] * QL[iY]) UL = UL.at[iZ].set(QL[0] * QL[iZ]) - UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UL = UL.at[4].set( + gamminv1 * QL[4] + + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ]) + ) UR = UR.at[0].set(QR[0]) UR = UR.at[iX].set(QR[0] * QR[iX]) UR = UR.at[iY].set(QR[0] * QR[iY]) UR = UR.at[iZ].set(QR[0] * QR[iZ]) - UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) - - Va = (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2]- QL[4, 2:-1] + QR[4, 1:-2] + UR = UR.at[4].set( + gamminv1 * QR[4] + + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ]) + ) + + Va = ( + (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] + - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2] + - QL[4, 2:-1] + + QR[4, 1:-2] + ) Va /= (Sfr - QL[iX, 2:-1]) * QL[0, 2:-1] - (Sfl - QR[iX, 1:-2]) * QR[0, 1:-2] Pa = QR[4, 1:-2] + QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) * (Va - QR[iX, 1:-2]) @@ -587,26 +667,55 @@ def HLLC(QL, QR, direc): far = far.at[iX].set(Dar * Va**2 + Pa) far = far.at[iY].set(Dar * Va * QL[iY, 2:-1]) far = far.at[iZ].set(Dar * Va * QL[iZ, 2:-1]) - far = far.at[4].set( (gamgamm1inv * Pa + 0.5 * Dar * (Va**2 + QL[iY, 2:-1]**2 + QL[iZ, 2:-1]**2)) * Va) + far = far.at[4].set( + ( + gamgamm1inv * Pa + + 0.5 * Dar * (Va**2 + QL[iY, 2:-1] ** 2 + QL[iZ, 2:-1] ** 2) + ) + * Va + ) fal = fal.at[0].set(Dal * Va) fal = fal.at[iX].set(Dal * Va**2 + Pa) fal = fal.at[iY].set(Dal * Va * QR[iY, 1:-2]) fal = fal.at[iZ].set(Dal * Va * QR[iZ, 1:-2]) - fal = fal.at[4].set( (gamgamm1inv * Pa + 0.5 * Dal * (Va**2 + QR[iY, 1:-2]**2 + QR[iZ, 1:-2]**2)) * Va) - - f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fL[:, 2:-1]) # Sf2 > 0 : supersonic - f_Riemann = jnp.where(Sfl*Va < 0., fal, f_Riemann) # SL < 0 and Va > 0 : sub-sonic - f_Riemann = jnp.where(Sfr*Va < 0., far, f_Riemann) # Va < 0 and SR > 0 : sub-sonic - #f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic + fal = fal.at[4].set( + ( + gamgamm1inv * Pa + + 0.5 * Dal * (Va**2 + QR[iY, 1:-2] ** 2 + QR[iZ, 1:-2] ** 2) + ) + * Va + ) + + f_Riemann = jnp.where( + Sfl > 0.0, fR[:, 1:-2], fL[:, 2:-1] + ) # Sf2 > 0 : supersonic + f_Riemann = jnp.where( + Sfl * Va < 0.0, fal, f_Riemann + ) # SL < 0 and Va > 0 : sub-sonic + f_Riemann = jnp.where( + Sfr * Va < 0.0, far, f_Riemann + ) # Va < 0 and SR > 0 : sub-sonic + # f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic return f_Riemann Q = jnp.zeros([5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4]) - Q = init_HD(Q, xc, yc, zc, mode=cfg.args.init_mode, direc='x', init_key=cfg.args.init_key, - M0=cfg.args.M0, dk=cfg.args.dk, gamma=cfg.args.gamma) + Q = init_HD( + Q, + xc, + yc, + zc, + mode=cfg.args.init_mode, + direc="x", + init_key=cfg.args.init_key, + M0=cfg.args.M0, + dk=cfg.args.dk, + gamma=cfg.args.gamma, + ) Q = device_put(Q) # putting variables in GPU (not necessary??) t = evolve(Q) - print('final time is: {0:.3f}'.format(t)) + print(f"final time is: {t:.3f}") + -if __name__=='__main__': +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py index a734e54..23cd971 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,61 +144,74 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import time +import os import random -import sys, os +import sys +import time from functools import partial from math import ceil, exp, log -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax -from jax import jit, vmap import jax.numpy as jnp -from jax import device_put, lax - -os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' -os.environ['XLA_PYTHON_CLIENT_MEM_FRACTION'] = '.9' +from jax import device_put, jit, lax -sys.path.append('..') -from utils import Courant_HD, Courant_vis_HD, bc_HD, limiting_HD, bc_HD_vis -from utils import init_multi_HD, init_multi_HD_shock, init_multi_HD_KH -from utils import init_multi_HD_2DTurb, init_multi_HD_2DRand, init_multi_HD_3DTurb, init_multi_HD_3DRand +# Hydra +from omegaconf import DictConfig + +os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" +os.environ["XLA_PYTHON_CLIENT_MEM_FRACTION"] = ".9" + +sys.path.append("..") +from utils import ( + Courant_HD, + Courant_vis_HD, + bc_HD, + init_multi_HD, + init_multi_HD_2DRand, + init_multi_HD_2DTurb, + init_multi_HD_3DRand, + init_multi_HD_3DTurb, + init_multi_HD_KH, + init_multi_HD_shock, + limiting_HD, +) # if double precision -#from jax.config import config -#config.update("jax_enable_x64", True) +# from jax.config import config +# config.update("jax_enable_x64", True) + def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config", config_name="config") def main(cfg: DictConfig) -> None: # physical constants gamma = cfg.args.gamma # 3D non-relativistic gas - gammi1 = gamma - 1. - gamminv1 = 1. / gammi1 + gammi1 = gamma - 1.0 + gamminv1 = 1.0 / gammi1 gamgamm1inv = gamma * gamminv1 - gammi1 = gamma - 1. - gampl1 = gamma + 1. - gammi3 = gamma - 3. - gampl3 = gamma + 3. + gammi1 = gamma - 1.0 + gampl1 = gamma + 1.0 + gammi3 = gamma - 3.0 + gampl3 = gamma + 3.0 - BCs = ['trans', 'periodic', 'KHI'] # reflect + BCs = ["trans", "periodic", "KHI"] # reflect assert cfg.args.bc in BCs, "bc should be in 'trans, reflect, periodic'" dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny - dy_inv = 1. / dy + dy_inv = 1.0 / dy # dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz - dz_inv = 1. / dz + dz_inv = 1.0 / dz # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -221,20 +233,24 @@ def main(cfg: DictConfig) -> None: # set viscosity if cfg.args.if_rand_param: - zeta = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 - eta = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + zeta = exp( + random.uniform(log(0.001), log(10)) + ) # uniform number between 0.01 to 100 + eta = exp( + random.uniform(log(0.001), log(10)) + ) # uniform number between 0.01 to 100 else: zeta = cfg.args.zeta eta = cfg.args.eta - print('zeta: {0:>5f}, eta: {1:>5f}'.format(zeta, eta)) - visc = zeta + eta / 3. + print(f"zeta: {zeta:>5f}, eta: {eta:>5f}") + visc = zeta + eta / 3.0 def evolve(Q): t = ini_time tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 tm_ini = time.time() @@ -244,11 +260,11 @@ def evolve(Q): VVz = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) PPP = jnp.zeros([it_tot, cfg.args.nx, cfg.args.ny, cfg.args.nz]) # initial time-step - DDD = DDD.at[0].set(Q[0,2:-2,2:-2,2:-2]) - VVx = VVx.at[0].set(Q[1,2:-2,2:-2,2:-2]) - VVy = VVy.at[0].set(Q[2,2:-2,2:-2,2:-2]) - VVz = VVz.at[0].set(Q[3,2:-2,2:-2,2:-2]) - PPP = PPP.at[0].set(Q[4,2:-2,2:-2,2:-2]) + DDD = DDD.at[0].set(Q[0, 2:-2, 2:-2, 2:-2]) + VVx = VVx.at[0].set(Q[1, 2:-2, 2:-2, 2:-2]) + VVy = VVy.at[0].set(Q[2, 2:-2, 2:-2, 2:-2]) + VVz = VVz.at[0].set(Q[3, 2:-2, 2:-2, 2:-2]) + PPP = PPP.at[0].set(Q[4, 2:-2, 2:-2, 2:-2]) cond_fun = lambda x: x[0] < fin_time @@ -256,11 +272,11 @@ def _body_fun(carry): def _save(_carry): Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP = _carry - DDD = DDD.at[i_save].set(Q[0,2:-2,2:-2,2:-2]) - VVx = VVx.at[i_save].set(Q[1,2:-2,2:-2,2:-2]) - VVy = VVy.at[i_save].set(Q[2,2:-2,2:-2,2:-2]) - VVz = VVz.at[i_save].set(Q[3,2:-2,2:-2,2:-2]) - PPP = PPP.at[i_save].set(Q[4,2:-2,2:-2,2:-2]) + DDD = DDD.at[i_save].set(Q[0, 2:-2, 2:-2, 2:-2]) + VVx = VVx.at[i_save].set(Q[1, 2:-2, 2:-2, 2:-2]) + VVy = VVy.at[i_save].set(Q[2, 2:-2, 2:-2, 2:-2]) + VVz = VVz.at[i_save].set(Q[3, 2:-2, 2:-2, 2:-2]) + PPP = PPP.at[i_save].set(Q[4, 2:-2, 2:-2, 2:-2]) tsave += dt_save i_save += 1 @@ -270,7 +286,9 @@ def _save(_carry): # if save data carry = (Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP) - Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP = lax.cond(t >= tsave, _save, _pass, carry) + Q, tsave, i_save, DDD, VVx, VVy, VVz, PPP = lax.cond( + t >= tsave, _save, _pass, carry + ) carry = (Q, t, dt, steps, tsave) Q, t, dt, steps, tsave = lax.fori_loop(0, show_steps, simulation_fn, carry) @@ -278,10 +296,12 @@ def _save(_carry): return (t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP) carry = t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP - t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP = lax.while_loop(cond_fun, _body_fun, carry) + t, tsave, steps, i_save, dt, Q, DDD, VVx, VVy, VVz, PPP = lax.while_loop( + cond_fun, _body_fun, carry + ) tm_fin = time.time() - print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + print(f"total elapsed time is {tm_fin - tm_ini} sec") DDD = DDD.at[-1].set(Q[0, 2:-2, 2:-2, 2:-2]) VVx = VVx.at[-1].set(Q[1, 2:-2, 2:-2, 2:-2]) VVy = VVy.at[-1].set(Q[2, 2:-2, 2:-2, 2:-2]) @@ -292,33 +312,42 @@ def _save(_carry): @jit def simulation_fn(i, carry): Q, t, dt, steps, tsave = carry - dt = Courant_HD(Q[:,2:-2,2:-2,2:-2], dx, dy, dz, cfg.args.gamma) * cfg.args.CFL + dt = ( + Courant_HD(Q[:, 2:-2, 2:-2, 2:-2], dx, dy, dz, cfg.args.gamma) + * cfg.args.CFL + ) dt = jnp.min(jnp.array([dt, cfg.args.fin_time - t, tsave - t])) def _update(carry): Q, dt = carry # preditor step for calculating t+dt/2-th time step - Q_tmp = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = bc_HD( + Q, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u Q_tmp = update(Q, Q_tmp, dt * 0.5) # update using flux at t+dt/2-th time step - Q_tmp = bc_HD(Q_tmp, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q_tmp = bc_HD( + Q_tmp, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u Q = update(Q, Q_tmp, dt) # update via viscosity - #d_min = jnp.min(Q[0]) - #dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity + # d_min = jnp.min(Q[0]) + # dt_vis = Courant_vis_HD(dx, dy, dz, eta/d_min, zeta/d_min) * cfg.args.CFL # for realistic viscosity dt_vis = Courant_vis_HD(dx, dy, dz, eta, zeta) * cfg.args.CFL dt_vis = jnp.min(jnp.array([dt_vis, dt])) - t_vis = 0. + t_vis = 0.0 carry = Q, dt, dt_vis, t_vis - Q, dt, dt_vis, t_vis = lax.while_loop(lambda x: x[1] - x[3] > 1.e-8, update_vis, carry) + Q, dt, dt_vis, t_vis = lax.while_loop( + lambda x: x[1] - x[3] > 1.0e-8, update_vis, carry + ) return Q, dt carry = Q, dt - Q, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + Q, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -328,10 +357,10 @@ def _update(carry): def update(Q, Q_tmp, dt): # calculate conservative variables D0 = Q[0] - Mx = Q[1]*Q[0] - My = Q[2]*Q[0] - Mz = Q[3]*Q[0] - E0 = Q[4] * gamminv1 + 0.5*(Mx*Q[1] + My*Q[2] + Mz*Q[3]) + Mx = Q[1] * Q[0] + My = Q[2] * Q[0] + Mz = Q[3] * Q[0] + E0 = Q[4] * gamminv1 + 0.5 * (Mx * Q[1] + My * Q[2] + Mz * Q[3]) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -345,34 +374,46 @@ def update(Q, Q_tmp, dt): fz = flux_z(Q_tmp) # update conservative variables - dtdx, dtdy, dtdz = dt*dx_inv, dt*dy_inv, dt*dz_inv - D0 -= dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2])\ + dtdx, dtdy, dtdz = dt * dx_inv, dt * dy_inv, dt * dz_inv + D0 -= ( + dtdx * (fx[0, 1:, 2:-2, 2:-2] - fx[0, :-1, 2:-2, 2:-2]) + + dtdy * (fy[0, 2:-2, 1:, 2:-2] - fy[0, 2:-2, :-1, 2:-2]) + dtdz * (fz[0, 2:-2, 2:-2, 1:] - fz[0, 2:-2, 2:-2, :-1]) + ) - Mx -= dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2])\ + Mx -= ( + dtdx * (fx[1, 1:, 2:-2, 2:-2] - fx[1, :-1, 2:-2, 2:-2]) + + dtdy * (fy[1, 2:-2, 1:, 2:-2] - fy[1, 2:-2, :-1, 2:-2]) + dtdz * (fz[1, 2:-2, 2:-2, 1:] - fz[1, 2:-2, 2:-2, :-1]) + ) - My -= dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2])\ + My -= ( + dtdx * (fx[2, 1:, 2:-2, 2:-2] - fx[2, :-1, 2:-2, 2:-2]) + + dtdy * (fy[2, 2:-2, 1:, 2:-2] - fy[2, 2:-2, :-1, 2:-2]) + dtdz * (fz[2, 2:-2, 2:-2, 1:] - fz[2, 2:-2, 2:-2, :-1]) + ) - Mz -= dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2])\ + Mz -= ( + dtdx * (fx[3, 1:, 2:-2, 2:-2] - fx[3, :-1, 2:-2, 2:-2]) + + dtdy * (fy[3, 2:-2, 1:, 2:-2] - fy[3, 2:-2, :-1, 2:-2]) + dtdz * (fz[3, 2:-2, 2:-2, 1:] - fz[3, 2:-2, 2:-2, :-1]) + ) - E0 -= dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2])\ - + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2])\ + E0 -= ( + dtdx * (fx[4, 1:, 2:-2, 2:-2] - fx[4, :-1, 2:-2, 2:-2]) + + dtdy * (fy[4, 2:-2, 1:, 2:-2] - fy[4, 2:-2, :-1, 2:-2]) + dtdz * (fz[4, 2:-2, 2:-2, 1:] - fz[4, 2:-2, 2:-2, :-1]) + ) # reverse primitive variables - Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d - Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx/D0) # vx - Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My/D0) # vy - Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz/D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5*(Mx**2 + My**2 + Mz**2)/D0)) # p - Q = Q.at[4].set(jnp.where(Q[4] > 1.e-8, Q[4], cfg.args.p_floor)) + Q = Q.at[0, 2:-2, 2:-2, 2:-2].set(D0) # d + Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx + Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy + Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p + Q = Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) return Q @@ -392,17 +433,20 @@ def _update_vis_x(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-1, 2:-2, 2:-2] + D0[1:-2, 2:-2, 2:-2]) - fMx = (eta + visc) * Dm * dx_inv * (\ - Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) - fMy = eta * Dm * dx_inv * (\ - Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) - fMz = eta * Dm * dx_inv * (\ - Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) - fE = 0.5 * (eta + visc) * Dm * dx_inv * (\ - Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2)\ - + 0.5 * eta * Dm * dx_inv * (\ - (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2)\ - + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2)) + fMx = ( + (eta + visc) + * Dm + * dx_inv + * (Q[1, 2:-1, 2:-2, 2:-2] - Q[1, 1:-2, 2:-2, 2:-2]) + ) + fMy = eta * Dm * dx_inv * (Q[2, 2:-1, 2:-2, 2:-2] - Q[2, 1:-2, 2:-2, 2:-2]) + fMz = eta * Dm * dx_inv * (Q[3, 2:-1, 2:-2, 2:-2] - Q[3, 1:-2, 2:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dx_inv * ( + Q[1, 2:-1, 2:-2, 2:-2] ** 2 - Q[1, 1:-2, 2:-2, 2:-2] ** 2 + ) + 0.5 * eta * Dm * dx_inv * ( + (Q[2, 2:-1, 2:-2, 2:-2] ** 2 - Q[2, 1:-2, 2:-2, 2:-2] ** 2) + + (Q[3, 2:-1, 2:-2, 2:-2] ** 2 - Q[3, 1:-2, 2:-2, 2:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -419,7 +463,9 @@ def _update_vis_x(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt @@ -437,17 +483,20 @@ def _update_vis_y(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-2, 2:-1, 2:-2] + D0[2:-2, 1:-2, 2:-2]) - fMx = eta * Dm * dy_inv * (\ - Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) - fMy = (eta + visc) * Dm * dy_inv * (\ - Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) - fMz = eta * Dm * dy_inv * (\ - Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) - fE = 0.5 * (eta + visc) * Dm * dy_inv * (\ - Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2)\ - + 0.5 * eta * Dm * dy_inv * ( \ - (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) \ - + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2)) + fMx = eta * Dm * dy_inv * (Q[1, 2:-2, 2:-1, 2:-2] - Q[1, 2:-2, 1:-2, 2:-2]) + fMy = ( + (eta + visc) + * Dm + * dy_inv + * (Q[2, 2:-2, 2:-1, 2:-2] - Q[2, 2:-2, 1:-2, 2:-2]) + ) + fMz = eta * Dm * dy_inv * (Q[3, 2:-2, 2:-1, 2:-2] - Q[3, 2:-2, 1:-2, 2:-2]) + fE = 0.5 * (eta + visc) * Dm * dy_inv * ( + Q[2, 2:-2, 2:-1, 2:-2] ** 2 - Q[2, 2:-2, 1:-2, 2:-2] ** 2 + ) + 0.5 * eta * Dm * dy_inv * ( + (Q[3, 2:-2, 2:-1, 2:-2] ** 2 - Q[3, 2:-2, 1:-2, 2:-2] ** 2) + + (Q[1, 2:-2, 2:-1, 2:-2] ** 2 - Q[1, 2:-2, 1:-2, 2:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -464,7 +513,9 @@ def _update_vis_y(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt @@ -482,17 +533,20 @@ def _update_vis_z(carry): # here the viscosity is eta*D0, so that dv/dt = eta*d^2v/dx^2 (not realistic viscosity but fast to calculate) Dm = 0.5 * (D0[2:-2, 2:-2, 2:-1] + D0[2:-2, 2:-2, 1:-2]) - fMx = eta * Dm * dz_inv * (\ - Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) - fMy = eta * Dm * dz_inv * (\ - Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) - fMz = (eta + visc) * Dm * dz_inv * (\ - Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) - fE = 0.5 * (eta + visc) * Dm * dz_inv * (\ - Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2)\ - + 0.5 * eta * Dm * dz_inv * ( \ - (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) \ - + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2)) + fMx = eta * Dm * dz_inv * (Q[1, 2:-2, 2:-2, 2:-1] - Q[1, 2:-2, 2:-2, 1:-2]) + fMy = eta * Dm * dz_inv * (Q[2, 2:-2, 2:-2, 2:-1] - Q[2, 2:-2, 2:-2, 1:-2]) + fMz = ( + (eta + visc) + * Dm + * dz_inv + * (Q[3, 2:-2, 2:-2, 2:-1] - Q[3, 2:-2, 2:-2, 1:-2]) + ) + fE = 0.5 * (eta + visc) * Dm * dz_inv * ( + Q[3, 2:-2, 2:-2, 2:-1] ** 2 - Q[3, 2:-2, 2:-2, 1:-2] ** 2 + ) + 0.5 * eta * Dm * dz_inv * ( + (Q[1, 2:-2, 2:-2, 2:-1] ** 2 - Q[1, 2:-2, 2:-2, 1:-2] ** 2) + + (Q[2, 2:-2, 2:-2, 2:-1] ** 2 - Q[2, 2:-2, 2:-2, 1:-2] ** 2) + ) D0 = D0[2:-2, 2:-2, 2:-2] Mx = Mx[2:-2, 2:-2, 2:-2] @@ -509,12 +563,16 @@ def _update_vis_z(carry): Q = Q.at[1, 2:-2, 2:-2, 2:-2].set(Mx / D0) # vx Q = Q.at[2, 2:-2, 2:-2, 2:-2].set(My / D0) # vy Q = Q.at[3, 2:-2, 2:-2, 2:-2].set(Mz / D0) # vz - Q = Q.at[4, 2:-2, 2:-2, 2:-2].set(gammi1 * (E0 - 0.5 * (Mx ** 2 + My ** 2 + Mz ** 2) / D0)) # p + Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( + gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) + ) # p return Q, dt Q, dt, dt_vis, t_vis = carry - Q = bc_HD(Q, mode=cfg.args.bc) # index 2 for _U is equivalent with index 0 for u + Q = bc_HD( + Q, mode=cfg.args.bc + ) # index 2 for _U is equivalent with index 0 for u dt_ev = jnp.min(jnp.array([dt, dt_vis, dt - t_vis])) carry = Q, dt_ev @@ -530,7 +588,7 @@ def _update_vis_z(carry): @jit def flux_x(Q): QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = HLL(QL, QR, direc=0) + # f_Riemann = HLL(QL, QR, direc=0) f_Riemann = HLLC(QL, QR, direc=0) return f_Riemann @@ -538,15 +596,17 @@ def flux_x(Q): def flux_y(Q): _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) - f_Riemann = jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + # f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) + f_Riemann = jnp.transpose( + HLLC(QL, QR, direc=1), (0, 3, 1, 2) + ) # (x,y,z) = (Z,X,Y) return f_Riemann @jit def flux_z(Q): _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) - #f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) + # f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) return f_Riemann @@ -556,21 +616,31 @@ def HLL(QL, QR, direc): iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 cfL = jnp.sqrt(gamma * QL[4] / QL[0]) cfR = jnp.sqrt(gamma * QR[4] / QR[0]) - Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave - Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave - dcfi = 1. / (Sfr - Sfl + 1.e-8) + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # right-going wave + dcfi = 1.0 / (Sfr - Sfl + 1.0e-8) UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) UL = UL.at[0].set(QL[0]) UL = UL.at[iX].set(QL[0] * QL[iX]) UL = UL.at[iY].set(QL[0] * QL[iY]) UL = UL.at[iZ].set(QL[0] * QL[iZ]) - UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UL = UL.at[4].set( + gamminv1 * QL[4] + + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ]) + ) UR = UR.at[0].set(QR[0]) UR = UR.at[iX].set(QR[0] * QR[iX]) UR = UR.at[iY].set(QR[0] * QR[iY]) UR = UR.at[iZ].set(QR[0] * QR[iZ]) - UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) + UR = UR.at[4].set( + gamminv1 * QR[4] + + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ]) + ) fL, fR = jnp.zeros_like(QL), jnp.zeros_like(QR) fL = fL.at[0].set(UL[iX]) @@ -584,38 +654,56 @@ def HLL(QL, QR, direc): fR = fR.at[iZ].set(UR[iX] * QR[iZ]) fR = fR.at[4].set((UR[4] + QR[4]) * QR[iX]) # upwind advection scheme - fHLL = dcfi * (Sfr * fR[:, 1:-2] - Sfl * fL[:, 2:-1] - + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2])) + fHLL = dcfi * ( + Sfr * fR[:, 1:-2] + - Sfl * fL[:, 2:-1] + + Sfl * Sfr * (UL[:, 2:-1] - UR[:, 1:-2]) + ) # L: left of cell = right-going, R: right of cell: left-going - f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fHLL) - f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) + f_Riemann = jnp.where(Sfl > 0.0, fR[:, 1:-2], fHLL) + f_Riemann = jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) return f_Riemann @partial(jit, static_argnums=(2,)) def HLLC(QL, QR, direc): - """ full-Godunov method -- exact shock solution""" + """full-Godunov method -- exact shock solution""" iX, iY, iZ = direc + 1, (direc + 1) % 3 + 1, (direc + 2) % 3 + 1 cfL = jnp.sqrt(gamma * QL[4] / QL[0]) cfR = jnp.sqrt(gamma * QR[4] / QR[0]) - Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum(cfL[2:-1], cfR[1:-2]) # left-going wave - Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum(cfL[2:-1], cfR[1:-2]) # right-going wave + Sfl = jnp.minimum(QL[iX, 2:-1], QR[iX, 1:-2]) - jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # left-going wave + Sfr = jnp.maximum(QL[iX, 2:-1], QR[iX, 1:-2]) + jnp.maximum( + cfL[2:-1], cfR[1:-2] + ) # right-going wave UL, UR = jnp.zeros_like(QL), jnp.zeros_like(QR) UL = UL.at[0].set(QL[0]) UL = UL.at[iX].set(QL[0] * QL[iX]) UL = UL.at[iY].set(QL[0] * QL[iY]) UL = UL.at[iZ].set(QL[0] * QL[iZ]) - UL = UL.at[4].set(gamminv1 * QL[4] + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ])) + UL = UL.at[4].set( + gamminv1 * QL[4] + + 0.5 * (UL[iX] * QL[iX] + UL[iY] * QL[iY] + UL[iZ] * QL[iZ]) + ) UR = UR.at[0].set(QR[0]) UR = UR.at[iX].set(QR[0] * QR[iX]) UR = UR.at[iY].set(QR[0] * QR[iY]) UR = UR.at[iZ].set(QR[0] * QR[iZ]) - UR = UR.at[4].set(gamminv1 * QR[4] + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ])) - - Va = (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2]- QL[4, 2:-1] + QR[4, 1:-2] + UR = UR.at[4].set( + gamminv1 * QR[4] + + 0.5 * (UR[iX] * QR[iX] + UR[iY] * QR[iY] + UR[iZ] * QR[iZ]) + ) + + Va = ( + (Sfr - QL[iX, 2:-1]) * UL[iX, 2:-1] + - (Sfl - QR[iX, 1:-2]) * UR[iX, 1:-2] + - QL[4, 2:-1] + + QR[4, 1:-2] + ) Va /= (Sfr - QL[iX, 2:-1]) * QL[0, 2:-1] - (Sfl - QR[iX, 1:-2]) * QR[0, 1:-2] Pa = QR[4, 1:-2] + QR[0, 1:-2] * (Sfl - QR[iX, 1:-2]) * (Va - QR[iX, 1:-2]) @@ -640,82 +728,202 @@ def HLLC(QL, QR, direc): far = far.at[iX].set(Dar * Va**2 + Pa) far = far.at[iY].set(Dar * Va * QL[iY, 2:-1]) far = far.at[iZ].set(Dar * Va * QL[iZ, 2:-1]) - far = far.at[4].set( (gamgamm1inv * Pa + 0.5 * Dar * (Va**2 + QL[iY, 2:-1]**2 + QL[iZ, 2:-1]**2)) * Va) + far = far.at[4].set( + ( + gamgamm1inv * Pa + + 0.5 * Dar * (Va**2 + QL[iY, 2:-1] ** 2 + QL[iZ, 2:-1] ** 2) + ) + * Va + ) fal = fal.at[0].set(Dal * Va) fal = fal.at[iX].set(Dal * Va**2 + Pa) fal = fal.at[iY].set(Dal * Va * QR[iY, 1:-2]) fal = fal.at[iZ].set(Dal * Va * QR[iZ, 1:-2]) - fal = fal.at[4].set( (gamgamm1inv * Pa + 0.5 * Dal * (Va**2 + QR[iY, 1:-2]**2 + QR[iZ, 1:-2]**2)) * Va) - - f_Riemann = jnp.where(Sfl > 0., fR[:, 1:-2], fL[:, 2:-1]) # Sf2 > 0 : supersonic - f_Riemann = jnp.where(Sfl*Va < 0., fal, f_Riemann) # SL < 0 and Va > 0 : sub-sonic - f_Riemann = jnp.where(Sfr*Va < 0., far, f_Riemann) # Va < 0 and SR > 0 : sub-sonic - #f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic + fal = fal.at[4].set( + ( + gamgamm1inv * Pa + + 0.5 * Dal * (Va**2 + QR[iY, 1:-2] ** 2 + QR[iZ, 1:-2] ** 2) + ) + * Va + ) + + f_Riemann = jnp.where( + Sfl > 0.0, fR[:, 1:-2], fL[:, 2:-1] + ) # Sf2 > 0 : supersonic + f_Riemann = jnp.where( + Sfl * Va < 0.0, fal, f_Riemann + ) # SL < 0 and Va > 0 : sub-sonic + f_Riemann = jnp.where( + Sfr * Va < 0.0, far, f_Riemann + ) # Va < 0 and SR > 0 : sub-sonic + # f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic return f_Riemann - Q = jnp.zeros([cfg.args.numbers, 5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4]) - if cfg.args.init_mode_Multi == '1D_rand': - Q = Q.at[:, 0, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, - k_tot=3, init_key=cfg.args.init_key, - num_choise_k=2, - umin=1.e0, umax=1.e1, if_renorm=True)) - Q = Q.at[:, 4, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, - k_tot=3, init_key=cfg.args.init_key+1, - num_choise_k=2, - umin=1.e1, umax=1.e2, if_renorm=True)) - Q = Q.at[:, 1, 2:-2, 2:-2, 2:-2].set(init_multi_HD(xc, yc, zc, numbers=cfg.args.numbers, - k_tot=3, init_key=cfg.args.init_key+2, - num_choise_k=2, - if_renorm=False)) - elif cfg.args.init_mode_Multi == '1D_shocks': - Q = Q.at[:,0,2:-2,2:-2,2:-2].set(init_multi_HD_shock( - xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - umin=1.e0, umax=1.e1)) - Q = Q.at[:,4,2:-2,2:-2,2:-2].set(init_multi_HD_shock( - xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key+1, - umin=1.e1, umax=1.e2)) - Q = Q.at[:,1,2:-2,2:-2,2:-2].set(init_multi_HD_shock( - xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key+2, - umin=-0.5e0, umax=0.5e0)) - elif cfg.args.init_mode_Multi == 'KHs': - assert 2. * yc[0] - (yc[1] - yc[0]) == 0., 'yL is assumed 0!' - print('now we are coming into KHs...') - Q = init_multi_HD_KH(Q, xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - M0=cfg.args.M0, dkMx=cfg.args.dkMx, gamma = cfg.args.gamma) - elif cfg.args.init_mode_Multi == '2D_Turbs': - print('now we are coming into 2DTurbs......') - Q = init_multi_HD_2DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) - elif cfg.args.init_mode_Multi == '2D_rand': - assert xe[0] == 0. and ye[0] == 0. and xe[-1] == 1. and ye[-1] == 1., 'xc, yc should be between 0 and 1!' - print('now we are coming into 2Drand......') - Q = init_multi_HD_2DRand(Q, xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) - elif cfg.args.init_mode_Multi == '3D_Turbs': - print('now we are coming into 3DTurbs......') - Q = init_multi_HD_3DTurb(Q, xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) - elif cfg.args.init_mode_Multi == '3D_rand': - print('now we are coming into 3Drand......') - Q = init_multi_HD_3DRand(Q, xc, yc, zc, numbers=cfg.args.numbers, - init_key=cfg.args.init_key, - M0=cfg.args.M0, k_tot=cfg.args.k_tot, gamma=cfg.args.gamma) - print('initial conditions were prepared!!') + Q = jnp.zeros( + [cfg.args.numbers, 5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4] + ) + if cfg.args.init_mode_Multi == "1D_rand": + Q = Q.at[:, 0, 2:-2, 2:-2, 2:-2].set( + init_multi_HD( + xc, + yc, + zc, + numbers=cfg.args.numbers, + k_tot=3, + init_key=cfg.args.init_key, + num_choise_k=2, + umin=1.0e0, + umax=1.0e1, + if_renorm=True, + ) + ) + Q = Q.at[:, 4, 2:-2, 2:-2, 2:-2].set( + init_multi_HD( + xc, + yc, + zc, + numbers=cfg.args.numbers, + k_tot=3, + init_key=cfg.args.init_key + 1, + num_choise_k=2, + umin=1.0e1, + umax=1.0e2, + if_renorm=True, + ) + ) + Q = Q.at[:, 1, 2:-2, 2:-2, 2:-2].set( + init_multi_HD( + xc, + yc, + zc, + numbers=cfg.args.numbers, + k_tot=3, + init_key=cfg.args.init_key + 2, + num_choise_k=2, + if_renorm=False, + ) + ) + elif cfg.args.init_mode_Multi == "1D_shocks": + Q = Q.at[:, 0, 2:-2, 2:-2, 2:-2].set( + init_multi_HD_shock( + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + umin=1.0e0, + umax=1.0e1, + ) + ) + Q = Q.at[:, 4, 2:-2, 2:-2, 2:-2].set( + init_multi_HD_shock( + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key + 1, + umin=1.0e1, + umax=1.0e2, + ) + ) + Q = Q.at[:, 1, 2:-2, 2:-2, 2:-2].set( + init_multi_HD_shock( + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key + 2, + umin=-0.5e0, + umax=0.5e0, + ) + ) + elif cfg.args.init_mode_Multi == "KHs": + assert 2.0 * yc[0] - (yc[1] - yc[0]) == 0.0, "yL is assumed 0!" + print("now we are coming into KHs...") + Q = init_multi_HD_KH( + Q, + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, + dkMx=cfg.args.dkMx, + gamma=cfg.args.gamma, + ) + elif cfg.args.init_mode_Multi == "2D_Turbs": + print("now we are coming into 2DTurbs......") + Q = init_multi_HD_2DTurb( + Q, + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, + k_tot=cfg.args.k_tot, + gamma=cfg.args.gamma, + ) + elif cfg.args.init_mode_Multi == "2D_rand": + assert ( + xe[0] == 0.0 and ye[0] == 0.0 and xe[-1] == 1.0 and ye[-1] == 1.0 + ), "xc, yc should be between 0 and 1!" + print("now we are coming into 2Drand......") + Q = init_multi_HD_2DRand( + Q, + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, + k_tot=cfg.args.k_tot, + gamma=cfg.args.gamma, + ) + elif cfg.args.init_mode_Multi == "3D_Turbs": + print("now we are coming into 3DTurbs......") + Q = init_multi_HD_3DTurb( + Q, + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, + k_tot=cfg.args.k_tot, + gamma=cfg.args.gamma, + ) + elif cfg.args.init_mode_Multi == "3D_rand": + print("now we are coming into 3Drand......") + Q = init_multi_HD_3DRand( + Q, + xc, + yc, + zc, + numbers=cfg.args.numbers, + init_key=cfg.args.init_key, + M0=cfg.args.M0, + k_tot=cfg.args.k_tot, + gamma=cfg.args.gamma, + ) + print("initial conditions were prepared!!") Q = device_put(Q) # putting variables in GPU (not necessary??) local_device_count = jax.local_device_count() - pm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') - t, DDD, VVx, VVy, VVz, PPP = pm_evolve(Q.reshape([local_device_count, - cfg.args.numbers//local_device_count, - 5, cfg.args.nx+4, cfg.args.ny+4, cfg.args.nz+4])) + pm_evolve = jax.pmap(jax.vmap(evolve, axis_name="j"), axis_name="i") + t, DDD, VVx, VVy, VVz, PPP = pm_evolve( + Q.reshape( + [ + local_device_count, + cfg.args.numbers // local_device_count, + 5, + cfg.args.nx + 4, + cfg.args.ny + 4, + cfg.args.nz + 4, + ] + ) + ) itot = DDD.shape[2] DDD = DDD.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) @@ -723,16 +931,87 @@ def HLLC(QL, QR, direc): VVy = VVy.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) VVz = VVz.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) PPP = PPP.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) - print('now data saving...') - jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_D', DDD) - jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vx', VVx) - jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vy', VVy) - jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_Vz', VVz) - jnp.save(cfg.args.save+'HD_Sols_'+cfg.args.init_mode_Multi+'_Eta'+str(eta)[:5]+'_Zeta'+str(zeta)[:5]+'_M'+str(cfg.args.M0)+'_key'+str(cfg.args.init_key)+'_P', PPP) - jnp.save(cfg.args.save + '/x_coordinate', xc) - jnp.save(cfg.args.save + '/y_coordinate', yc) - jnp.save(cfg.args.save + '/z_coordinate', zc) - jnp.save(cfg.args.save + '/t_coordinate', tc) - -if __name__=='__main__': + print("now data saving...") + jnp.save( + cfg.args.save + + "HD_Sols_" + + cfg.args.init_mode_Multi + + "_Eta" + + str(eta)[:5] + + "_Zeta" + + str(zeta)[:5] + + "_M" + + str(cfg.args.M0) + + "_key" + + str(cfg.args.init_key) + + "_D", + DDD, + ) + jnp.save( + cfg.args.save + + "HD_Sols_" + + cfg.args.init_mode_Multi + + "_Eta" + + str(eta)[:5] + + "_Zeta" + + str(zeta)[:5] + + "_M" + + str(cfg.args.M0) + + "_key" + + str(cfg.args.init_key) + + "_Vx", + VVx, + ) + jnp.save( + cfg.args.save + + "HD_Sols_" + + cfg.args.init_mode_Multi + + "_Eta" + + str(eta)[:5] + + "_Zeta" + + str(zeta)[:5] + + "_M" + + str(cfg.args.M0) + + "_key" + + str(cfg.args.init_key) + + "_Vy", + VVy, + ) + jnp.save( + cfg.args.save + + "HD_Sols_" + + cfg.args.init_mode_Multi + + "_Eta" + + str(eta)[:5] + + "_Zeta" + + str(zeta)[:5] + + "_M" + + str(cfg.args.M0) + + "_key" + + str(cfg.args.init_key) + + "_Vz", + VVz, + ) + jnp.save( + cfg.args.save + + "HD_Sols_" + + cfg.args.init_mode_Multi + + "_Eta" + + str(eta)[:5] + + "_Zeta" + + str(zeta)[:5] + + "_M" + + str(cfg.args.M0) + + "_key" + + str(cfg.args.init_key) + + "_P", + PPP, + ) + jnp.save(cfg.args.save + "/x_coordinate", xc) + jnp.save(cfg.args.save + "/y_coordinate", yc) + jnp.save(cfg.args.save + "/z_coordinate", zc) + jnp.save(cfg.args.save + "/t_coordinate", tc) + + +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml index 1e5092f..b291196 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-2 zeta: 1.e-2 @@ -21,7 +21,7 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 1000 -init_mode_Multi: '1D_rand' +init_mode_Multi: "1D_rand" init_key: 2022 if_rand_param: False -M0 : 0. \ No newline at end of file +M0: 0. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml index 958b043..8140b8f 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_shock.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.005 ini_time: 0. fin_time: 0.4 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,7 +21,7 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 1000 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2020 if_rand_param: False -M0 : 0 \ No newline at end of file +M0: 0 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml index b16c66d..6c172a3 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_Multi_trans.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,7 +21,7 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 1000 -init_mode_Multi: '1D_rand' +init_mode_Multi: "1D_rand" init_key: 2022 if_rand_param: False -M0 : 0 \ No newline at end of file +M0: 0 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml index 993ba0c..3c8a1f5 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 0.4 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube1' +init_mode: "shocktube1" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml index e3c8126..9bd4d9e 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube2.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 0.15 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube2' +init_mode: "shocktube2" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml index 9d634cb..7f07b3e 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube3.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.001 ini_time: 0. fin_time: 0.012 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube3' +init_mode: "shocktube3" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml index f474760..469a755 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube4.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.001 ini_time: 0. fin_time: 0.035 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube4' +init_mode: "shocktube4" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml index 9ae2430..23569c7 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube5.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.001 ini_time: 0. fin_time: 0.012 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube5' +init_mode: "shocktube5" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml index 692bd6f..c5e3579 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube6.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube6' +init_mode: "shocktube6" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml index fc59bbf..682e7fb 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/1D_ShockTube7.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 2. @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,10 +19,10 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube7' +init_mode: "shocktube7" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' +init_mode_Multi: "1D_shocks" init_key: 2022 M0: 1. dk: 1. diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml index 19e03c0..07b2df6 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/KH/KH_M01_dk1_Re1e3/' +save: "../save/CFD/KH/KH_M01_dk1_Re1e3/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 0.1 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml index a4718c1..6520922 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk10.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' -M0 : 0.1 +init_mode: "KHI" +M0: 0.1 dk: 10. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml index 91b9315..f5fcd7c 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk2.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/KH/KH_M01_dk2_Re1e3/' +save: "../save/CFD/KH/KH_M01_dk2_Re1e3/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 0.1 dk: 2. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml index 4f9164b..0893cfc 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M01_dk5.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 0.1 dk: 5. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml index 1c90c61..a81f895 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M02_dk1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 0.2 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml index dfe76cd..4c46603 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M04_dk1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 0.4 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml index 1ca5586..9d36c74 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M1_dk1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/KH/KH_M1_dk1_Re1e3/' +save: "../save/CFD/KH/KH_M1_dk1_Re1e3/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' +init_mode: "KHI" M0: 1. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml index 50d9364..2c176a1 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_KH_M2_dk1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 5. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-3 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: 'KHI' -M0 : 2. -dk : 1 +init_mode: "KHI" +M0: 2. +dk: 1 dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml index 85ea371..4d80a12 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_KH.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.1 ini_time: 0. fin_time: 2. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'KHI' +bc: "KHI" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,7 +21,7 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 4 -init_mode_Multi: 'KHs' -M0 : 0.1 +init_mode_Multi: "KHs" +M0: 0.1 dkMx: 2. -init_key: 2022 \ No newline at end of file +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml index 3faa563..a0a24e4 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.05 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,8 +21,8 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 100 -init_mode_Multi: '2D_rand' -M0 : 0.1 +init_mode_Multi: "2D_rand" +M0: 0.1 k_tot: 4 init_key: 2020 if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml index 0b1ecd0..42055c1 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Rand_HR.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.05 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,8 +21,8 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 20 -init_mode_Multi: '2D_rand' -M0 : 0.5 +init_mode_Multi: "2D_rand" +M0: 0.5 k_tot: 4 init_key: 2020 if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml index c73faee..acaf6a8 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_Multi_Turb.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.05 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,8 +21,8 @@ if_show: 1 show_steps: 100 p_floor: 1.e-4 numbers: 20 -init_mode_Multi: '2DTurbs' -M0 : 0.1 +init_mode_Multi: "2DTurbs" +M0: 0.1 k_tot: 4 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml index 783f4ef..257764c 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_ShockTube.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 4000 p_floor: 1.e-4 -init_mode: '2D-shock' +init_mode: "2D-shock" M0: 1. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml index 2774a16..4fc92bc 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/2D_TOV.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'OTVortex' +init_mode: "OTVortex" M0: 1. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml index b0bfaa9..7d753a6 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_BlastWave.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 0.5 @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'BlastWave' +init_mode: "BlastWave" M0: 1. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml index e7a4edf..f36ce4b 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_Rand.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.05 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,8 +21,8 @@ if_show: 1 show_steps: 400 p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '3D_rand' -M0 : 1. +init_mode_Multi: "3D_rand" +M0: 1. k_tot: 4 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml index a284aac..75bb721 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_Multi_TurbM1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.05 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -21,8 +21,8 @@ if_show: 1 show_steps: 400 p_floor: 1.e-4 numbers: 20 -init_mode_Multi: '3D_Turbs' -M0 : 1. +init_mode_Multi: "3D_Turbs" +M0: 1. k_tot: 4 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml index cca4a94..3978a73 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM01.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'turbulence' +init_mode: "turbulence" M0: 1.e-1 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml index 65ab227..59d5cc5 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM05.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'turbulence' +init_mode: "turbulence" M0: 5.e-1 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml index cca4a94..3978a73 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM1.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'turbulence' +init_mode: "turbulence" M0: 1.e-1 dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml index 50d8f32..a47492e 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM2.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'turbulence' +init_mode: "turbulence" M0: 2. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml index b7058e6..fbe6aa0 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/3D_TurbM4.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.025 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: 0. yR: 1. zL: 0. zR: 1. -bc: 'periodic' +bc: "periodic" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -20,10 +20,10 @@ if_second_order: 1. if_show: 1 show_steps: 400 p_floor: 1.e-4 -init_mode: 'turbulence' +init_mode: "turbulence" M0: 4. dk: 1. dkMx: 1. numbers: 4 -init_mode_Multi: 'KHs' +init_mode_Multi: "KHs" init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml index 437e632..185a013 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/args/default.yaml @@ -1,4 +1,4 @@ -save: '../save/CFD/' +save: "../save/CFD/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -11,7 +11,7 @@ yL: -1. yR: 1. zL: -1. zR: 1. -bc: 'trans' +bc: "trans" gamma: 1.6666666666666667 eta: 1.e-8 zeta: 1.e-8 @@ -19,8 +19,8 @@ CFL: 3.e-1 if_second_order: 1. if_show: 1 show_steps: 100 -init_mode: 'shocktube1' +init_mode: "shocktube1" p_floor: 1.e-4 numbers: 10 -init_mode_Multi: '1D_shocks' -init_key: 2022 \ No newline at end of file +init_mode_Multi: "1D_shocks" +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml index 3a4b989..b3b9da6 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/config/config.yaml @@ -6,4 +6,4 @@ defaults: hydra: output_subdir: null run: - dir: . \ No newline at end of file + dir: . diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh index 384989a..1b02061 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset.sh @@ -1,7 +1,9 @@ +#!/bin/bash + CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e0.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta1e-1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e0.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta2e-1.yaml CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e0.yaml -CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml \ No newline at end of file +CUDA_VISIBLE_DEVICES='3' python3 advection_exact_Hydra.py +args=beta4e-1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh index 770b5f7..b29f590 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_3DTurb.sh @@ -1,6 +1,6 @@ +#!/bin/bash CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM01.yaml CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM05.yaml CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM1.yaml CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM2.yaml CUDA_VISIBLE_DEVICES='0' python3 CFD_Hydra.py +args=3D_TurbM4.yaml - diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh index 1bd7d44..03a6151 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_testset_KHI.sh @@ -1,3 +1,4 @@ +#!/bin/bash CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M01_dk1.yaml CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M02_dk1.yaml CUDA_VISIBLE_DEVICES='1' python3 CFD_Hydra.py +args=2D_KH_M04_dk1.yaml diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh index 777f36f..b1e6886 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D.sh @@ -1,9 +1,10 @@ +#!/bin/bash nn=1 key=2020 -while [ $nn -le 10 ]; do - CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh index aa9676c..0324a72 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1DShock.sh @@ -1,9 +1,10 @@ +#!/bin/bash nn=1 key=2031 -while [ $nn -le 10 ]; do - CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_shock.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_shock.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh index 502b3d9..2ce7891 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_1D_trans.sh @@ -1,9 +1,10 @@ +#!/bin/bash nn=1 key=2020 -while [ $nn -le 10 ]; do - CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_trans.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=1D_Multi_trans.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh index a1c7c06..d7752c6 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2D.sh @@ -1,10 +1,11 @@ +#!/bin/bash nn=1 key=2031 -while [ $nn -le 100 ]; do - CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand.yaml ++args.init_key=$key - #CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand_HR.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 100 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand.yaml ++args.init_key="$key" + #CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Rand_HR.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh index 9dd556c..bdd281e 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_2DTurb.sh @@ -1,10 +1,11 @@ +#!/bin/bash nn=1 key=2031 -#while [ $nn -le 100 ]; do -while [ $nn -le 55 ]; do - CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Turb.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +#while [ "$nn" -le 100 ]; do +while [ "$nn" -le 55 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=2D_Multi_Turb.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh index e6de413..ed0028f 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3D.sh @@ -1,9 +1,10 @@ +#!/bin/bash nn=1 key=2031 -while [ $nn -le 10 ]; do - CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=3D_Multi_Rand.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 10 ]; do + CUDA_VISIBLE_DEVICES='0,1' python3 CFD_multi_Hydra.py +args=3D_Multi_Rand.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh index 154a0fd..9ce9b1f 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/run_trainset_3DTurb.sh @@ -1,9 +1,10 @@ +#!/bin/bash nn=1 key=2031 -while [ $nn -le 6 ]; do - CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=3D_Multi_TurbM1.yaml ++args.init_key=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +while [ "$nn" -le 6 ]; do + CUDA_VISIBLE_DEVICES='0,1,2,3' python3 CFD_multi_Hydra.py +args=3D_Multi_TurbM1.yaml ++args.init_key="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" done diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py index ddfa35a..d738cff 100644 --- a/pdebench/data_gen/data_gen_NLE/Data_Merge.py +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -143,223 +143,284 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -''' +""" Data_Merge.py This is a script creating HDF5 from the generated data (numpy array) by our data generation scripts. -A more detailed explanation how to use this script is provided in the README. -''' +A more detailed explanation how to use this script is provided in the README. +""" -import numpy as np -import h5py +# Hydra + + +from __future__ import annotations + import glob -# Hydra +import h5py import hydra +import numpy as np from omegaconf import DictConfig + def _mergeRD(var, DataND, savedir): - _vars = ['2D', 'nu'] + _vars = ["2D", "nu"] if var not in _vars: - print(var+' is not defined!') + print(var + " is not defined!") return None idx = 0 - datas = glob.glob(savedir+'/' + var + '*key*.npy') - datas.sort() - for data in datas: + data = glob.glob(savedir + "/" + var + "*key*.npy") + data.sort() + for data in data: print(idx, data) test = np.load(data).squeeze() batch = min(test.shape[0], DataND.shape[0] - idx) - if var == '2D': - DataND[idx:idx + batch] = test[:batch, -2] + if var == "2D": + DataND[idx : idx + batch] = test[:batch, -2] else: - DataND[idx:idx + batch] = test[:batch] + DataND[idx : idx + batch] = test[:batch] idx += batch return DataND[:idx] + def _merge(var, DataND, dim, savedir): if dim == 1: - _vars = ['D', 'P', 'Vx'] + _vars = ["D", "P", "Vx"] elif dim == 2: - _vars = ['D', 'P', 'Vx', 'Vy'] + _vars = ["D", "P", "Vx", "Vy"] elif dim == 3: - _vars = ['D', 'P', 'Vx', 'Vy', 'Vz'] + _vars = ["D", "P", "Vx", "Vy", "Vz"] if var not in _vars: - print(var+' is not defined!') + print(var + " is not defined!") return None idx = 0 - datas = glob.glob(savedir+'/HD*' + var + '.npy') - datas.sort() - for data in datas: + data = glob.glob(savedir + "/HD*" + var + ".npy") + data.sort() + for data in data: print(idx, data) test = np.load(data).squeeze() batch = min(test.shape[0], DataND.shape[0] - idx) - DataND[idx:idx+batch] = test[:batch] + DataND[idx : idx + batch] = test[:batch] idx += batch return DataND[:idx] + def nan_check(data): - data = np.abs(data).reshape([data.shape[0], data.shape[1],-1]).sum(axis=-1) - return np.where(data[:,-2] < 1.e-6)[0], np.where(data[:,-2] > 1.e-6)[0] + data = np.abs(data).reshape([data.shape[0], data.shape[1], -1]).sum(axis=-1) + return np.where(data[:, -2] < 1.0e-6)[0], np.where(data[:, -2] > 1.0e-6)[0] + def merge(type, dim, bd, nbatch, savedir): - if type=='CFD': - datas = glob.glob(savedir+'/HD*D.npy') - datas.sort() - test = np.load(datas[0]) + if type == "CFD": + data = glob.glob(savedir + "/HD*D.npy") + data.sort() + test = np.load(data[0]) __nbatch, nt, nx, ny, nz = test.shape - _nbatch = __nbatch * len(datas) - print('nb, nt, nx, ny, nz: ', _nbatch, nt, nx, ny, nz) - print('nbatch: {0}, _nbatch: {1}'.format(nbatch, _nbatch)) - assert nbatch <= _nbatch, 'nbatch should be equal or less than the number of generated samples' - assert 2*nbatch > _nbatch, '2*nbatch should be larger than the number of generated samples' + _nbatch = __nbatch * len(data) + print("nb, nt, nx, ny, nz: ", _nbatch, nt, nx, ny, nz) + print(f"nbatch: {nbatch}, _nbatch: {_nbatch}") + assert ( + nbatch <= _nbatch + ), "nbatch should be equal or less than the number of generated samples" + assert ( + 2 * nbatch > _nbatch + ), "2*nbatch should be larger than the number of generated samples" if dim == 1: - DataND = np.zeros([2*nbatch, nt, nx], dtype=np.float32) - vars = ['D', 'P', 'Vx'] + DataND = np.zeros([2 * nbatch, nt, nx], dtype=np.float32) + vars = ["D", "P", "Vx"] elif dim == 2: - DataND = np.zeros([2*nbatch, nt, nx, ny], dtype=np.float32) - vars = ['D', 'P', 'Vx', 'Vy'] + DataND = np.zeros([2 * nbatch, nt, nx, ny], dtype=np.float32) + vars = ["D", "P", "Vx", "Vy"] elif dim == 3: - DataND = np.zeros([2*nbatch, nt, nx, ny, nz], dtype=np.float32) - vars = ['D', 'P', 'Vx', 'Vy', 'Vz'] + DataND = np.zeros([2 * nbatch, nt, nx, ny, nz], dtype=np.float32) + vars = ["D", "P", "Vx", "Vy", "Vz"] - elif type=='ReacDiff': - datas = glob.glob(savedir+'/nu*.npy') - datas.sort() - test = np.load(datas[0]) + elif type == "ReacDiff": + data = glob.glob(savedir + "/nu*.npy") + data.sort() + test = np.load(data[0]) __nbatch, nx, ny = test.shape - _nbatch = __nbatch * len(datas) - print('nbatch: {0}, _nbatch: {1}'.format(nbatch, _nbatch)) - assert nbatch == _nbatch, 'nbatch should be equal or less than the number of generated samples' - print('nb, nx, ny: ', _nbatch, nx, ny) + _nbatch = __nbatch * len(data) + print(f"nbatch: {nbatch}, _nbatch: {_nbatch}") + assert ( + nbatch == _nbatch + ), "nbatch should be equal or less than the number of generated samples" + print("nb, nx, ny: ", _nbatch, nx, ny) DataND = np.zeros([nbatch, nx, ny], dtype=np.float32) - vars = ['2D', 'nu'] + vars = ["2D", "nu"] for var in vars: - if type=='CFD': + if type == "CFD": _DataND = _merge(var, DataND, dim, savedir) - if var=='D': + if var == "D": idx_neg, idx_pos = nan_check(_DataND) - print('idx_neg: {0}, idx_pos: {1}'.format(len(idx_neg), len(idx_pos))) + print(f"idx_neg: {len(idx_neg)}, idx_pos: {len(idx_pos)}") if len(idx_pos) < nbatch: - print('too many ill-defined data...') - print('nbatch: {0}, idx_pos: {1}'.format(nbatch, len(idx_pos))) + print("too many ill-defined data...") + print(f"nbatch: {nbatch}, idx_pos: {len(idx_pos)}") _DataND = _DataND[idx_pos] _DataND = _DataND[:nbatch] - np.save(savedir+'/' + var + '.npy', _DataND) - elif type == 'ReacDiff': + np.save(savedir + "/" + var + ".npy", _DataND) + elif type == "ReacDiff": DataND = _mergeRD(var, DataND, savedir) - np.save(savedir+'/' + var + '.npy', DataND) - - datas = glob.glob(savedir+'/*npy') - datas.sort() - - if type == 'CFD': - zcrd = np.load(datas[-1]) - del (datas[-1]) - ycrd = np.load(datas[-1]) - del (datas[-1]) - xcrd = np.load(datas[-1]) - del (datas[-1]) - tcrd = np.load(datas[-1]) - del (datas[-1]) - if type=='ReacDiff': - #datas = glob.glob('save/' + type + '/nu*key*npy') - datas = glob.glob(savedir+'/nu*key*npy') - datas.sort() - _beta = datas[0].split('/')[-1].split('_')[3] - flnm = savedir+'/2D_DecayFlow_' + _beta + '_Train.hdf5' - with h5py.File(flnm, 'w') as f: - f.create_dataset('tensor', data=np.load(savedir+'/2D.npy')[:, None, :, :]) - f.create_dataset('nu', data=np.load(savedir+'/nu.npy')) - f.create_dataset('x-coordinate', data=xcrd) - f.create_dataset('y-coordinate', data=ycrd) - f.attrs['beta'] = float(_beta[4:]) + np.save(savedir + "/" + var + ".npy", DataND) + + data = glob.glob(savedir + "/*npy") + data.sort() + + if type == "CFD": + zcrd = np.load(data[-1]) + del data[-1] + ycrd = np.load(data[-1]) + del data[-1] + xcrd = np.load(data[-1]) + del data[-1] + tcrd = np.load(data[-1]) + del data[-1] + if type == "ReacDiff": + # data = glob.glob('save/' + type + '/nu*key*npy') + data = glob.glob(savedir + "/nu*key*npy") + data.sort() + _beta = data[0].split("/")[-1].split("_")[3] + flnm = savedir + "/2D_DecayFlow_" + _beta + "_Train.hdf5" + with h5py.File(flnm, "w") as f: + f.create_dataet("tensor", data=np.load(savedir + "/2D.npy")[:, None, :, :]) + f.create_dataet("nu", data=np.load(savedir + "/nu.npy")) + f.create_dataet("x-coordinate", data=xcrd) + f.create_dataet("y-coordinate", data=ycrd) + f.attrs["beta"] = float(_beta[4:]) return 0 - mode = datas[1].split('/')[-1].split('_')[3] - _eta = datas[1].split('/')[-1].split('_')[4] - _zeta = datas[1].split('/')[-1].split('_')[5] - _M = datas[1].split('/')[-1].split('_')[6] + mode = data[1].split("/")[-1].split("_")[3] + _eta = data[1].split("/")[-1].split("_")[4] + _zeta = data[1].split("/")[-1].split("_")[5] + _M = data[1].split("/")[-1].split("_")[6] if dim == 1: - flnm = savedir+'/1D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + bd + '_Train.hdf5' + flnm = ( + savedir + + "/1D_CFD_" + + mode + + "_" + + _eta + + "_" + + _zeta + + "_" + + bd + + "_Train.hdf5" + ) elif dim == 2: - flnm = savedir+'/2D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + flnm = ( + savedir + + "/2D_CFD_" + + mode + + "_" + + _eta + + "_" + + _zeta + + "_" + + _M + + "_" + + bd + + "_Train.hdf5" + ) elif dim == 3: - flnm = savedir+'/3D_CFD_' + mode + '_' + _eta + '_' + _zeta + '_' + _M + '_' + bd + '_Train.hdf5' + flnm = ( + savedir + + "/3D_CFD_" + + mode + + "_" + + _eta + + "_" + + _zeta + + "_" + + _M + + "_" + + bd + + "_Train.hdf5" + ) print(flnm) - del(DataND) + del DataND - with h5py.File(flnm, 'w') as f: - f.create_dataset('density', data=np.load(savedir+'/D.npy')) - f.create_dataset('pressure', data=np.load(savedir+'/P.npy')) - f.create_dataset('Vx', data=np.load(savedir+'/Vx.npy')) + with h5py.File(flnm, "w") as f: + f.create_dataet("density", data=np.load(savedir + "/D.npy")) + f.create_dataet("pressure", data=np.load(savedir + "/P.npy")) + f.create_dataet("Vx", data=np.load(savedir + "/Vx.npy")) if dim > 1: - f.create_dataset('Vy', data=np.load(savedir+'/Vy.npy')) - f.create_dataset('y-coordinate', data=ycrd) + f.create_dataet("Vy", data=np.load(savedir + "/Vy.npy")) + f.create_dataet("y-coordinate", data=ycrd) if dim == 3: - f.create_dataset('Vz', data=np.load(savedir+'/Vz.npy')) - f.create_dataset('z-coordinate', data=zcrd) - f.create_dataset('x-coordinate', data = xcrd) - f.create_dataset('t-coordinate', data = tcrd) + f.create_dataet("Vz", data=np.load(savedir + "/Vz.npy")) + f.create_dataet("z-coordinate", data=zcrd) + f.create_dataet("x-coordinate", data=xcrd) + f.create_dataet("t-coordinate", data=tcrd) eta = float(_eta[3:]) zeta = float(_zeta[4:]) - print('(eta, zeta) = ', eta, zeta) - f.attrs['eta'] = eta - f.attrs['zeta'] = zeta + print("(eta, zeta) = ", eta, zeta) + f.attrs["eta"] = eta + f.attrs["zeta"] = zeta if dim > 1: M = float(_M[1:]) - f.attrs['M'] = M - print('M: ', M) + f.attrs["M"] = M + print("M: ", M) + def transform(type, savedir): - datas = glob.glob(savedir+'/*npy') - datas.sort() - xcrd = np.load(datas[-1]) - del (datas[-1]) - tcrd = np.load(datas[-1]) - del (datas[-1]) - - flnm = datas[0] - with h5py.File(flnm[:-3]+'hdf5', 'w') as f: + data = glob.glob(savedir + "/*npy") + data.sort() + xcrd = np.load(data[-1]) + del data[-1] + tcrd = np.load(data[-1]) + del data[-1] + + flnm = data[0] + with h5py.File(flnm[:-3] + "hdf5", "w") as f: print(flnm) _data = np.load(flnm) - f.create_dataset('tensor', data = _data.astype(np.float32)) - f.create_dataset('x-coordinate', data = xcrd) - f.create_dataset('t-coordinate', data = tcrd) - if type=='advection': - beta = float(flnm.split('/')[-1].split('_')[3][4:-4]) # advection train + + f.create_dataset("tensor", data=_data.astype(np.float32)) + f.create_dataset("x-coordinate", data=xcrd) + f.create_dataset("t-coordinate", data=tcrd) + if type == "advection": + beta = float(flnm.split("/")[-1].split("_")[3][4:-4]) # advection train print(f"beta: {beta}") - f.attrs['beta'] = beta + f.attrs["beta"] = beta - elif type=='burgers': - Nu = float(flnm.split('/')[-1].split('_')[-1][2:-4]) # Burgers test/train + elif type == "burgers": + Nu = float(flnm.split("/")[-1].split("_")[-1][2:-4]) # Burgers test/train print(f"Nu: {Nu}") - f.attrs['Nu'] = Nu + f.attrs["Nu"] = Nu - elif type=='ReacDiff': - Rho = float(flnm.split('/')[-1].split('_')[-1][3:-4]) # reac-diff test - Nu = float(flnm.split('/')[-1].split('_')[-2][2:]) # reac-diff test + elif type == "ReacDiff": + Rho = float(flnm.split("/")[-1].split("_")[-1][3:-4]) # reac-diff test + Nu = float(flnm.split("/")[-1].split("_")[-2][2:]) # reac-diff test print(f"Nu, rho: {Nu, Rho}") - f.attrs['Nu'] = Nu - f.attrs['rho'] = Rho + f.attrs["Nu"] = Nu + f.attrs["rho"] = Rho + # Init arguments with Hydra @hydra.main(config_path="config", config_name="config") def main(cfg: DictConfig) -> None: - pde1ds = ['advection', 'burgers', 'ReacDiff'] - if cfg.args.type in pde1ds and cfg.args.dim==1: + pde1ds = ["advection", "burgers", "ReacDiff"] + if cfg.args.type in pde1ds and cfg.args.dim == 1: transform(type=cfg.args.type, savedir=cfg.args.savedir) else: - bds = ['periodic', 'trans'] - assert cfg.args.bd in bds, 'bd should be either periodic or trans' - merge(type=cfg.args.type, dim=cfg.args.dim, bd=cfg.args.bd, nbatch=cfg.args.nbatch, savedir=cfg.args.savedir) - -if __name__=='__main__': + bds = ["periodic", "trans"] + assert cfg.args.bd in bds, "bd should be either periodic or trans" + merge( + type=cfg.args.type, + dim=cfg.args.dim, + bd=cfg.args.bd, + nbatch=cfg.args.nbatch, + savedir=cfg.args.savedir, + ) + + +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/README.md b/pdebench/data_gen/data_gen_NLE/README.md index eabacd8..695c5cb 100644 --- a/pdebench/data_gen/data_gen_NLE/README.md +++ b/pdebench/data_gen/data_gen_NLE/README.md @@ -8,12 +8,13 @@ bash data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh ``` -which will in turn run the python script `data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_soluion_Hydra.py` +which will in turn run the python script +`data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_soluion_Hydra.py` - Update `data_gen/data_gen_NLE/config/config.yaml` to: ```yaml -type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' +type: "ReacDiff" # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 2 ``` @@ -23,7 +24,7 @@ dim: 2 python data_gen/data_gen_NLE/Data_Merge.py ``` ----------------------------- +--- #### Data generation for 1D Advection Equation: @@ -41,9 +42,9 @@ bash run_trainset.sh - Update `data_gen/data_gen_NLE/config/config.yaml` to: ```yaml -type: 'advection' # 'advection'/'ReacDiff'/'burgers'/'CFD' +type: "advection" # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 -savedir: './save/advection' +savedir: "./save/advection" ``` ``` @@ -52,7 +53,7 @@ cd .. python Data_Merge.py ``` --------------- +--- #### Data generation for 1D Burgers' Equation: @@ -70,19 +71,18 @@ bash run_trainset.sh - Update `data_gen/data_gen_NLE/config/config.yaml` to: ```yaml -type: 'burgers' # 'advection'/'ReacDiff'/'burgers'/'CFD' +type: "burgers" # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 -savedir: './save/burgers' +savedir: "./save/burgers" ``` - ``` # serialize to hdf5 by transforming npy file cd .. python Data_Merge.py ``` ---------------- +--- #### Data generation for 1D Reaction Diffusion Equation: @@ -100,9 +100,9 @@ bash run_trainset.sh - Update `data_gen/data_gen_NLE/config/config.yaml` to: ```yaml -type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' +type: "ReacDiff" # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 -savedir: './save/ReacDiff' +savedir: "./save/ReacDiff" ``` ``` diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml index 30f3641..82892c5 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu1e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 1.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml index f551aa0..b3a41a4 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu2e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 2.e0 +nu: 2.e0 rho: 1.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml index cdd76db..3c4721f 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e-1.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e-1 +nu: 5.e-1 rho: 1.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml index bef66db..986fcf1 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e0_Nu5e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e0 +nu: 5.e0 rho: 1.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml index 1a996f7..21aef17 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu1e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 1.e1 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml index 42228e0..2df6cd9 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu2e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 2.e0 +nu: 2.e0 rho: 1.e1 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml index d8f4513..114a987 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e-1.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e-1 +nu: 5.e-1 rho: 1.e1 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml index fe56c62..a2c6bea 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho1e1_Nu5e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e0 +nu: 5.e0 rho: 1.e1 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml index 9804ca1..087cd63 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu1e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 2.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml index ce67bba..4121f05 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu2e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 2.e0 +nu: 2.e0 rho: 2.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml index 04abd46..c90ac52 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e-1.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e-1 +nu: 5.e-1 rho: 2.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml index 755beaf..5861512 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho2e0_Nu5e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e0 +nu: 5.e0 rho: 2.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml index 33d3e25..eea3bbe 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu1e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 5.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml index d9b17ad..97781ac 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu2e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 2.e0 +nu: 2.e0 rho: 5.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml index f1a7705..f584839 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e-1.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e-1 +nu: 5.e-1 rho: 5.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml index 6039f11..d2ce48e 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/Rho5e0_Nu5e0.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 5.e0 +nu: 5.e0 rho: 5.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml index 30f3641..82892c5 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config.yaml @@ -1,13 +1,13 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 1024 xL: 0. xR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 1.e0 CFL: 4.e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml index 7eb228f..180ed0a 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml index f661d34..f313da7 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml index 12479a0..3aaa868 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu2e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml index e7c4bcd..63445a0 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml index cad5d73..d483cf8 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e0_Nu5e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml index c9de644..f12dea6 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml index cccd76a..da255bc 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml index cff657e..3b12386 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu2e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml index 512fcd1..17d81ce 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml index 7965ac3..1400217 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho1e1_Nu5e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml index e98c943..35594b5 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml index 3b71699..b12a5af 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml index 2e2fc56..f3d9c47 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu2e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml index e201d68..86db6f7 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml index 17996c7..b8ed164 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho2e0_Nu5e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml index 7cddd71..ff4cc51 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml index e2025e3..472c8fb 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu1e1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 100 if_second_order: 1. show_steps: 10000 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml index 8580063..999ef74 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu2e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml index aae3611..5092fab 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e-1.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 10000 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml index 47f91c8..261a646 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/Rho5e0_Nu5e0.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -13,4 +13,4 @@ numbers: 10000 if_second_order: 1. show_steps: 100 init_key: 2022 -if_rand_param: False \ No newline at end of file +if_rand_param: False diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml index 60e27b4..c26af21 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml index 00169c2..f37cfd1 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff//' +save: "../save/ReacDiff//" dt_save: 0.25 ini_time: 0. fin_time: 2. @@ -14,4 +14,4 @@ if_show: 1 numbers: 200 if_second_order: 1. show_steps: 100 -init_key: 2022 \ No newline at end of file +init_key: 2022 diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py index b3b6fca..203dab9 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,35 +144,35 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations import sys -import random -from math import ceil, exp, log +from math import ceil -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax -from jax import vmap import jax.numpy as jnp -from jax import device_put, lax +from jax import device_put, lax, vmap + +# Hydra +from omegaconf import DictConfig -sys.path.append('..') +sys.path.append("..") from utils import Courant_diff_2D, bc_2D, init_multi_2DRand def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: # basic parameters dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx - dx_inv = 1. / dx - dy = (cfg.multi.yR - cfg.multi.yL)/cfg.multi.ny - dy_inv = 1./dy + dx_inv = 1.0 / dx + dy = (cfg.multi.yR - cfg.multi.yL) / cfg.multi.ny + dy_inv = 1.0 / dy # cell edge coordinate xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) @@ -199,7 +198,7 @@ def evolve(u, nu): tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0], u.shape[1]]) uu = uu.at[0].set(u) @@ -219,12 +218,16 @@ def _show(_carry): u, tsave, i_save, uu = lax.cond(t >= tsave, _show, _pass, carry) carry = (u, t, dt, steps, tsave, nu) - u, t, dt, steps, tsave, nu = lax.fori_loop(0, show_steps, simulation_fn, carry) + u, t, dt, steps, tsave, nu = lax.fori_loop( + 0, show_steps, simulation_fn, carry + ) return (t, tsave, steps, i_save, dt, u, uu, nu) carry = t, tsave, steps, i_save, dt, u, uu, nu - t, tsave, steps, i_save, dt, u, uu, nu = lax.while_loop(cond_fun, _body_fun, carry) + t, tsave, steps, i_save, dt, u, uu, nu = lax.while_loop( + cond_fun, _body_fun, carry + ) uu = uu.at[-1].set(u) return uu @@ -244,64 +247,108 @@ def _update(carry): return u, dt, nu carry = u, dt, nu - u, dt, nu = lax.cond(dt > 1.e-8, _update, _pass, carry) + u, dt, nu = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 return u, t, dt, steps, tsave, nu - @jax.jit def update(u, u_tmp, dt, nu): # boundary condition - _u = bc_2D(u_tmp, mode='Neumann') + _u = bc_2D(u_tmp, mode="Neumann") # diffusion dtdx = dt * dx_inv dtdy = dt * dy_inv - fx = - 0.5 * (nu[2:-1, 2:-2] + nu[1:-2, 2:-2]) * dx_inv * (_u[2:-1, 2:-2] - _u[1:-2, 2:-2]) - fy = - 0.5 * (nu[2:-2, 2:-1] + nu[2:-2, 1:-2]) * dy_inv * (_u[2:-2, 2:-1] - _u[2:-2, 1:-2]) - u -= dtdx * (fx[1:, :] - fx[:-1, :])\ - + dtdy * (fy[:, 1:] - fy[:, :-1]) + fx = ( + -0.5 + * (nu[2:-1, 2:-2] + nu[1:-2, 2:-2]) + * dx_inv + * (_u[2:-1, 2:-2] - _u[1:-2, 2:-2]) + ) + fy = ( + -0.5 + * (nu[2:-2, 2:-1] + nu[2:-2, 1:-2]) + * dy_inv + * (_u[2:-2, 2:-1] - _u[2:-2, 1:-2]) + ) + u -= dtdx * (fx[1:, :] - fx[:-1, :]) + dtdy * (fy[:, 1:] - fy[:, :-1]) # source term: f = 1 * beta u += dt * beta return u - u = init_multi_2DRand(xc, yc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) + u = init_multi_2DRand( + xc, yc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key + ) u = device_put(u) # putting variables in GPU (not necessary??) # generate random diffusion coefficient key = jax.random.PRNGKey(cfg.multi.init_key) - xms = jax.random.uniform(key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.xL, maxval=cfg.multi.xR) + xms = jax.random.uniform( + key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.xL, maxval=cfg.multi.xR + ) key, subkey = jax.random.split(key) - yms = jax.random.uniform(key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.yL, maxval=cfg.multi.yR) + yms = jax.random.uniform( + key, shape=[cfg.multi.numbers, 5], minval=cfg.multi.yL, maxval=cfg.multi.yR + ) key, subkey = jax.random.split(key) - stds = 0.5*(cfg.multi.xR - cfg.multi.xL) * jax.random.uniform(key, shape=[cfg.multi.numbers, 5]) + stds = ( + 0.5 + * (cfg.multi.xR - cfg.multi.xL) + * jax.random.uniform(key, shape=[cfg.multi.numbers, 5]) + ) nu = jnp.zeros_like(u) for i in range(5): - nu += jnp.exp(-((xc[None, :, None] - xms[:, None, None, i]) ** 2 - + (yc[None, None, :] - yms[:, None, None, i]) ** 2) / stds[:, None, None, i]) + nu += jnp.exp( + -( + (xc[None, :, None] - xms[:, None, None, i]) ** 2 + + (yc[None, None, :] - yms[:, None, None, i]) ** 2 + ) + / stds[:, None, None, i] + ) nu = jnp.where(nu > nu.mean(), 1, 0.1) - nu = vmap(bc_2D, axis_name='i')(nu) + nu = vmap(bc_2D, axis_name="i")(nu) local_devices = jax.local_device_count() if local_devices > 1: nb, nx, ny = u.shape - vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') - uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, nx, ny]),\ - nu.reshape([local_devices, cfg.multi.numbers//local_devices, nx+4, ny+4])) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name="j"), axis_name="i") + uu = vm_evolve( + u.reshape([local_devices, cfg.multi.numbers // local_devices, nx, ny]), + nu.reshape( + [local_devices, cfg.multi.numbers // local_devices, nx + 4, ny + 4] + ), + ) uu = uu.reshape([nb, -1, nx, ny]) else: vm_evolve = vmap(evolve, 0, 0) uu = vm_evolve(u, nu) - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' - jnp.save(cwd + cfg.multi.save+'/2D_ReacDiff_Multi_beta'+str(beta)[:5]+'_key'+str(cfg.multi.init_key), uu) - jnp.save(cwd + cfg.multi.save+'/x_coordinate', xc) - jnp.save(cwd + cfg.multi.save+'/y_coordinate', yc) - jnp.save(cwd + cfg.multi.save+'/t_coordinate', tc) - jnp.save(cwd + cfg.multi.save+'/nu_diff_coef_beta'+str(beta)[:5]+'_key'+str(cfg.multi.init_key), nu[:,2:-2,2:-2]) - -if __name__=='__main__': + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" + jnp.save( + cwd + + cfg.multi.save + + "/2D_ReacDiff_Multi_beta" + + str(beta)[:5] + + "_key" + + str(cfg.multi.init_key), + uu, + ) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/y_coordinate", yc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) + jnp.save( + cwd + + cfg.multi.save + + "/nu_diff_coef_beta" + + str(beta)[:5] + + "_key" + + str(cfg.multi.init_key), + nu[:, 2:-2, 2:-2], + ) + + +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py index 638ffcc..2e6156c 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,36 +144,37 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import time import sys +import time from math import ceil -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax import jax.numpy as jnp from jax import device_put, lax -sys.path.append('..') -from utils import init, Courant_diff, save_data, bc +# Hydra +from omegaconf import DictConfig + +sys.path.append("..") +from utils import Courant_diff, bc, init # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: - print('nu: {0:.3f}, rho: {1:.3f}'.format(cfg.args.nu, cfg.args.rho)) + print(f"nu: {cfg.args.nu:.3f}, rho: {cfg.args.rho:.3f}") # basic parameters - dx = (cfg.args.xR - cfg.args.xL)/cfg.args.nx - dx_inv = 1./dx + dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx + dx_inv = 1.0 / dx # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) # cell center coordinate - xc = xe[:-1] + 0.5*dx + xc = xe[:-1] + 0.5 * dx # t-coordinate it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 tc = jnp.arange(it_tot + 1) * cfg.args.dt_save @@ -185,7 +185,7 @@ def evolve(u): steps = 0 i_save = 0 tm_ini = time.time() - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) @@ -196,14 +196,16 @@ def evolve(u): tsave += cfg.args.dt_save i_save += 1 - if steps%cfg.args.show_steps==0 and cfg.args.if_show: - print('now {0:d}-steps, t = {1:.3f}, dt = {2:.3f}'.format(steps, t, dt)) + if steps % cfg.args.show_steps == 0 and cfg.args.if_show: + print(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") carry = (u, t, dt, steps, tsave) - u, t, dt, steps, tsave = lax.fori_loop(0, cfg.args.show_steps, simulation_fn, carry) + u, t, dt, steps, tsave = lax.fori_loop( + 0, cfg.args.show_steps, simulation_fn, carry + ) tm_fin = time.time() - print('total elapsed time is {} sec'.format(tm_fin - tm_ini)) + print(f"total elapsed time is {tm_fin - tm_ini} sec") return uu, t @jax.jit @@ -219,11 +221,12 @@ def _update(carry): # update using flux at t+dt/2-th time step u = update(u, u_tmp, dt) return u, dt + def _pass(carry): return carry carry = u, dt - u, dt = lax.cond(t > 1.e-8, _update, _pass, carry) + u, dt = lax.cond(t > 1.0e-8, _update, _pass, carry) t += dt steps += 1 @@ -235,32 +238,45 @@ def update(u, u_tmp, dt): u = Piecewise_Exact_Solution(u, dt) # diffusion f = flux(u_tmp) - u -= dt * dx_inv * (f[1:cfg.args.nx + 1] - f[0:cfg.args.nx]) + u -= dt * dx_inv * (f[1 : cfg.args.nx + 1] - f[0 : cfg.args.nx]) return u @jax.jit def flux(u): - _u = bc(u, dx, Ncell=cfg.args.nx) # index 2 for _U is equivalent with index 0 for u + _u = bc( + u, dx, Ncell=cfg.args.nx + ) # index 2 for _U is equivalent with index 0 for u # source term - f = - cfg.args.nu*(_u[2:cfg.args.nx+3] - _u[1:cfg.args.nx+2])*dx_inv + f = -cfg.args.nu * (_u[2 : cfg.args.nx + 3] - _u[1 : cfg.args.nx + 2]) * dx_inv return f @jax.jit def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method # stiff equation - u = 1./(1. + jnp.exp(- cfg.args.rho*dt)*(1. - u)/u) + u = 1.0 / (1.0 + jnp.exp(-cfg.args.rho * dt) * (1.0 - u) / u) return u u = init(xc=xc, mode=cfg.args.init_mode) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print('final time is: {0:.3f}'.format(t)) - - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' - jnp.save(cwd + cfg.args.save + '/ReacDiff_' + cfg.args.init_mode + '_Nu' + str(cfg.args.nu) + '_Rho' + str(cfg.args.rho), uu) - jnp.save(cwd + cfg.args.save + '/x_coordinate', xc) - jnp.save(cwd + cfg.args.save + '/t_coordinate', tc) - -if __name__=='__main__': + print(f"final time is: {t:.3f}") + + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" + jnp.save( + cwd + + cfg.args.save + + "/ReacDiff_" + + cfg.args.init_mode + + "_Nu" + + str(cfg.args.nu) + + "_Rho" + + str(cfg.args.rho), + uu, + ) + jnp.save(cwd + cfg.args.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.args.save + "/t_coordinate", tc) + + +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py index 1dcec4e..8b10ae2 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ @@ -145,34 +144,35 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import sys import random +import sys from math import ceil, exp, log from pathlib import Path -# Hydra -from omegaconf import DictConfig, OmegaConf import hydra - import jax -from jax import vmap import jax.numpy as jnp from jax import device_put, lax -sys.path.append('..') -from utils import init_multi, Courant, Courant_diff, save_data, bc, limiting +# Hydra +from omegaconf import DictConfig + +sys.path.append("..") +from utils import Courant_diff, bc, init_multi def _pass(carry): return carry + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: # basic parameters dx = (cfg.multi.xR - cfg.multi.xL) / cfg.multi.nx - dx_inv = 1. / dx + dx_inv = 1.0 / dx # cell edge coordinate xe = jnp.linspace(cfg.multi.xL, cfg.multi.xR, cfg.multi.nx + 1) @@ -185,12 +185,16 @@ def main(cfg: DictConfig) -> None: dt_save = cfg.multi.dt_save CFL = cfg.multi.CFL if cfg.multi.if_rand_param: - rho = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 - nu = exp(random.uniform(log(0.001), log(10))) # uniform number between 0.01 to 100 + rho = exp( + random.uniform(log(0.001), log(10)) + ) # uniform number between 0.01 to 100 + nu = exp( + random.uniform(log(0.001), log(10)) + ) # uniform number between 0.01 to 100 else: rho = cfg.multi.rho nu = cfg.multi.nu - print('rho: {0:>5f}, nu: {1:>5f}'.format(rho, nu)) + print(f"rho: {rho:>5f}, nu: {nu:>5f}") # t-coordinate it_tot = ceil((fin_time - ini_time) / dt_save) + 1 @@ -202,7 +206,7 @@ def evolve(u): tsave = t steps = 0 i_save = 0 - dt = 0. + dt = 0.0 uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) @@ -247,53 +251,71 @@ def _update(carry): return u, dt carry = u, dt - u, dt = lax.cond(dt > 1.e-8, _update, _pass, carry) + u, dt = lax.cond(dt > 1.0e-8, _update, _pass, carry) t += dt steps += 1 return u, t, dt, steps, tsave - @jax.jit def update(u, u_tmp, dt): # stiff part u = Piecewise_Exact_Solution(u, dt) # diffusion f = flux(u_tmp) - u -= dt * dx_inv * (f[1:cfg.multi.nx + 1] - f[0:cfg.multi.nx]) + u -= dt * dx_inv * (f[1 : cfg.multi.nx + 1] - f[0 : cfg.multi.nx]) return u @jax.jit def flux(u): - _u = bc(u, dx, Ncell=cfg.multi.nx) # index 2 for _U is equivalent with index 0 for u + _u = bc( + u, dx, Ncell=cfg.multi.nx + ) # index 2 for _U is equivalent with index 0 for u # 2nd-order diffusion flux - f = - nu*(_u[2:cfg.multi.nx+3] - _u[1:cfg.multi.nx+2])*dx_inv + f = -nu * (_u[2 : cfg.multi.nx + 3] - _u[1 : cfg.multi.nx + 2]) * dx_inv return f @jax.jit def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method # stiff equation - u = 1./(1. + jnp.exp(- rho*dt)*(1. - u)/u) + u = 1.0 / (1.0 + jnp.exp(-rho * dt) * (1.0 - u) / u) return u - u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key, if_norm=True) + u = init_multi( + xc, + numbers=cfg.multi.numbers, + k_tot=4, + init_key=cfg.multi.init_key, + if_norm=True, + ) u = device_put(u) # putting variables in GPU (not necessary??) - #vm_evolve = vmap(evolve, 0, 0) - #uu = vm_evolve(u) - vm_evolve = jax.pmap(jax.vmap(evolve, axis_name='j'), axis_name='i') + # vm_evolve = vmap(evolve, 0, 0) + # uu = vm_evolve(u) + vm_evolve = jax.pmap(jax.vmap(evolve, axis_name="j"), axis_name="i") local_devices = jax.local_device_count() - uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers//local_devices, -1])) + uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers // local_devices, -1])) + + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" + jnp.save( + cwd + cfg.multi.save + "/ReacDiff_Nu" + str(nu)[:5] + "_Rho" + str(rho)[:5], uu + ) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) # reshape based on device count uu = uu.reshape((-1, *uu.shape[2:])) - print('data saving...') - cwd = hydra.utils.get_original_cwd() + '/' + print("data saving...") + cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) - jnp.save(cwd + cfg.multi.save+'ReacDiff_Nu'+str(nu)[:5]+'_Rho'+str(rho)[:5], uu) - jnp.save(cwd + cfg.multi.save+'/x_coordinate', xc) - jnp.save(cwd + cfg.multi.save+'/t_coordinate', tc) + jnp.save( + cwd + cfg.multi.save + "ReacDiff_Nu" + str(nu)[:5] + "_Rho" + str(rho)[:5], uu + ) + jnp.save(cwd + cfg.multi.save + "/x_coordinate", xc) + jnp.save(cwd + cfg.multi.save + "/t_coordinate", tc) + -if __name__=='__main__': +if __name__ == "__main__": main() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh index 1ee0716..c948681 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_DarcyFlow2D.sh @@ -1,10 +1,11 @@ +#! /bin/bash nn=1 key=2020 -while [ $nn -le 50 ]; do +while [ "$nn" -le 50 ]; do CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_2D_multi_solution_Hydra.py +multi=config_2D.yaml ++multi.init_k\ -ey=$key - nn=$(expr $nn + 1) - key=$(expr $key + 1) +ey="$key" + nn=$(${nn} + 1) + key=$(${key} + 1) echo "$nn" echo "$key" -done \ No newline at end of file +done diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh index 7f6bd11..513aadc 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_testset.sh @@ -1,3 +1,4 @@ +#! /bin/bash #CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu1e0.yaml #CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu2e0.yaml #CUDA_VISIBLE_DEVICES='3' python3 reaction_diffusion_Hydra.py +args=Rho1e0_Nu5e0.yaml diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh index bfc78db..21b8a51 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/run_trainset.sh @@ -1,3 +1,4 @@ +#! /bin/bash CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu1e0.yaml CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu2e0.yaml CUDA_VISIBLE_DEVICES='0,1' python3 reaction_diffusion_multi_solution_Hydra.py +multi=Rho1e0_Nu5e0.yaml diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index b379b19..bc22dcf 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -11,6 +11,7 @@ hydra: args: type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' dim: 1 - bd: 'periodic' + bd: "periodic" nbatch: 1000 + savedir: "./save/CFD/" savedir: './save/ReacDiff/' diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index d8ff81f..5e9cf87 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1,42 +1,46 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- +from __future__ import annotations + import math as mt +from functools import partial + import jax -import numpy as np import jax.numpy as jnp -from jax import random, jit, nn, lax, vmap, scipy -from functools import partial +import numpy as np +from jax import jit, lax, nn, random, scipy, vmap # if double precision -#from jax.config import config -#config.update("jax_enable_x64", True) +# from jax.config import config +# config.update("jax_enable_x64", True) -def init(xc, mode='sin', u0=1., du=0.1): +def init(xc, mode="sin", u0=1.0, du=0.1): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - modes = ['sin', 'sinsin', 'Gaussian', 'react', 'possin'] - assert mode in modes, 'mode is not defined!!' - if mode == 'sin': # sinusoidal wave - u = u0 * jnp.sin((xc + 1.) * jnp.pi) - elif mode == 'sinsin': # sinusoidal wave - u = jnp.sin((xc + 1.) * jnp.pi) + du * jnp.sin((xc + 1.) * jnp.pi*8.) - elif mode == 'Gaussian': # for diffusion check + modes = ["sin", "sinsin", "Gaussian", "react", "possin"] + assert mode in modes, "mode is not defined!!" + if mode == "sin": # sinusoidal wave + u = u0 * jnp.sin((xc + 1.0) * jnp.pi) + elif mode == "sinsin": # sinusoidal wave + u = jnp.sin((xc + 1.0) * jnp.pi) + du * jnp.sin((xc + 1.0) * jnp.pi * 8.0) + elif mode == "Gaussian": # for diffusion check t0 = 0.01 - u = jnp.exp(-xc**2*jnp.pi/(4.*t0))/jnp.sqrt(2.*t0) - elif mode == 'react': # for reaction-diffusion eq. - logu = - 0.5*(xc - jnp.pi)**2/(0.25*jnp.pi)**2 + u = jnp.exp(-(xc**2) * jnp.pi / (4.0 * t0)) / jnp.sqrt(2.0 * t0) + elif mode == "react": # for reaction-diffusion eq. + logu = -0.5 * (xc - jnp.pi) ** 2 / (0.25 * jnp.pi) ** 2 u = jnp.exp(logu) - elif mode == 'possin': # sinusoidal wave - u = u0 * jnp.abs(jnp.sin((xc + 1.) * jnp.pi)) + elif mode == "possin": # sinusoidal wave + u = u0 * jnp.abs(jnp.sin((xc + 1.0) * jnp.pi)) return u @partial(jit, static_argnums=(1, 2, 3, 4)) -def init_multi(xc, numbers=10000, k_tot=8, init_key=2022, num_choise_k=2, if_norm=False): +def init_multi( + xc, numbers=10000, k_tot=8, init_key=2022, num_choise_k=2, if_norm=False +): """ :param xc: cell center coordinate :param mode: initial condition @@ -75,19 +79,21 @@ def _norm(carry): return u cond, u = carry - u = lax.cond(cond==True, _norm, _pass, u) + u = lax.cond(cond == True, _norm, _pass, u) return cond, u key = random.PRNGKey(init_key) - selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = random.randint( + key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot + ) selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) - kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + kk = jnp.pi * 2.0 * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) amp = random.uniform(key, shape=[numbers, k_tot, 1]) key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + phs = 2.0 * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) _u = jnp.sum(_u, axis=1) @@ -113,17 +119,20 @@ def _norm(carry): _u *= mask carry = if_norm, _u - _, _u = normalize(carry) # normalize value between [0, 1] for reaction-diffusion eq. + _, _u = normalize( + carry + ) # normalize value between [0, 1] for reaction-diffusion eq. return _u -def init_multi_2DRand(xc, yc, numbers=10000, init_key=2022, k_tot=4, duMx = 1.e1): + +def init_multi_2DRand(xc, yc, numbers=10000, init_key=2022, k_tot=4, duMx=1.0e1): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def _pass(carry): return carry @@ -151,12 +160,12 @@ def __create_2DRand_init(u0, delu): qLx = dx * nx qLy = dy * ny - ## random field + # random field u = jnp.zeros([nx, ny]) key = random.PRNGKey(init_key) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy for j in range(-k_tot, k_tot + 1): ky = ky0 * j # from 1 to k_tot @@ -166,9 +175,9 @@ def __create_2DRand_init(u0, delu): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[1]) # (vi, k) + phs = 2.0 * jnp.pi * random.uniform(key, shape=[1]) # (vi, k) - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + uk = 1.0 / jnp.sqrt(jnp.sqrt(kx**2 + ky**2)) kdx = kx * xc[:, None] + ky * yc[None, :] u += uk * jnp.sin(kdx + phs) @@ -178,10 +187,10 @@ def __create_2DRand_init(u0, delu): return u key = random.PRNGKey(init_key) - u0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=duMx) + u0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=duMx) key, subkey = random.split(key) - delu = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.5) - u = jax.vmap(__create_2DRand_init, axis_name='i')(u0, delu) + delu = random.uniform(key, shape=([numbers, 1]), minval=1.0e-2, maxval=0.5) + u = jax.vmap(__create_2DRand_init, axis_name="i")(u0, delu) # perform window function key, subkey = random.split(key) @@ -199,123 +208,148 @@ def __create_2DRand_init(u0, delu): cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) u = u * mask - u = u + u0[:,:,None] * (1. - mask) + u = u + u0[:, :, None] * (1.0 - mask) return u -def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, - M0=0.1, dk=1, gamma=.1666666667): + +def init_HD( + u, + xc, + yc, + zc, + mode="shocktube1", + direc="x", + init_key=2022, + M0=0.1, + dk=1, + gamma=0.1666666667, +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ print(mode) - modes = ['shocktube0','shocktube1','shocktube2','shocktube3','shocktube4','shocktube5','shocktube6','shocktube7', - '2D-shock', 'OTVortex', 'KHI', 'turbulence', 'sound_wave', 'c_discon', 'BlastWave'] - assert mode in modes, 'mode is not defined!!' + modes = [ + "shocktube0", + "shocktube1", + "shocktube2", + "shocktube3", + "shocktube4", + "shocktube5", + "shocktube6", + "shocktube7", + "2D-shock", + "OTVortex", + "KHI", + "turbulence", + "sound_wave", + "c_discon", + "BlastWave", + ] + assert mode in modes, "mode is not defined!!" _, nx, ny, nz = u.shape - if mode[:-1] == 'shocktube': # shock tube - - if direc == 'x': + if mode[:-1] == "shocktube": # shock tube + if direc == "x": iX, iY, iZ = 1, 2, 3 Ncell = nx _u = jnp.zeros_like(u) - elif direc == 'y': + elif direc == "y": iX, iY, iZ = 2, 3, 1 Ncell = ny _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': + if direc == "z": iX, iY, iZ = 3, 1, 2 Ncell = nz _u = jnp.transpose(u, (0, 3, 1, 2)) - if mode[-1] == '0': # test 0 for viscosity - nx0 = int(0.5*Ncell) - uL = [1., 0.75, 0.2, -0.3, 1.] - uR = [0.125, 0., 0.1, 0.9, 0.1] - elif mode[-1] == '1': # test 1 - nx0 = int(0.3*Ncell) - uL = [1., 0.75, 0., 0., 1.] - uR = [0.125, 0., 0., 0., 0.1] - elif mode[-1] == '2': # test 2 - nx0 = int(0.5*Ncell) - uL = [1., -2., 0., 0., 0.4] - uR = [1., 2., 0., 0., 0.4] - elif mode[-1] == '3': # test 3 - nx0 = int(0.5*Ncell) - uL = [1., 0., 0., 0., 1.e3] - uR = [1., 0., 0., 0., 0.01] - elif mode[-1] == '4': # test 4 - nx0 = int(0.4*Ncell) - uL = [5.99924, 19.5975, 0., 0., 460.894] - uR = [5.99242, -6.19633, 0., 0., 46.095] - elif mode[-1] == '5': # test 5 + if mode[-1] == "0": # test 0 for viscosity + nx0 = int(0.5 * Ncell) + uL = [1.0, 0.75, 0.2, -0.3, 1.0] + uR = [0.125, 0.0, 0.1, 0.9, 0.1] + elif mode[-1] == "1": # test 1 + nx0 = int(0.3 * Ncell) + uL = [1.0, 0.75, 0.0, 0.0, 1.0] + uR = [0.125, 0.0, 0.0, 0.0, 0.1] + elif mode[-1] == "2": # test 2 + nx0 = int(0.5 * Ncell) + uL = [1.0, -2.0, 0.0, 0.0, 0.4] + uR = [1.0, 2.0, 0.0, 0.0, 0.4] + elif mode[-1] == "3": # test 3 + nx0 = int(0.5 * Ncell) + uL = [1.0, 0.0, 0.0, 0.0, 1.0e3] + uR = [1.0, 0.0, 0.0, 0.0, 0.01] + elif mode[-1] == "4": # test 4 + nx0 = int(0.4 * Ncell) + uL = [5.99924, 19.5975, 0.0, 0.0, 460.894] + uR = [5.99242, -6.19633, 0.0, 0.0, 46.095] + elif mode[-1] == "5": # test 5 nx0 = int(0.8 * Ncell) - uL = [1., -19.59745, 0., 0., 1.e3] - uR = [1., -19.59745, 0., 0., 0.01] - elif mode[-1] == '6': # test 6 + uL = [1.0, -19.59745, 0.0, 0.0, 1.0e3] + uR = [1.0, -19.59745, 0.0, 0.0, 0.01] + elif mode[-1] == "6": # test 6 nx0 = int(0.5 * Ncell) - uL = [1.4, 0., 0., 0., 1.] - uR = [1., 0., 0., 0., 1.] - elif mode[-1] == '7': # test 7 + uL = [1.4, 0.0, 0.0, 0.0, 1.0] + uR = [1.0, 0.0, 0.0, 0.0, 1.0] + elif mode[-1] == "7": # test 7 nx0 = int(0.5 * Ncell) - uL = [1.4, 0.1, 0., 0., 1.] - uR = [1., 0.1, 0., 0., 1.] + uL = [1.4, 0.1, 0.0, 0.0, 1.0] + uR = [1.0, 0.1, 0.0, 0.0, 1.0] # left - _u = _u.at[0, :nx0].set(uL[0]) - _u = _u.at[iX, :nx0].set(uL[1]) - _u = _u.at[iY, :nx0].set(uL[2]) - _u = _u.at[iZ, :nx0].set(uL[3]) - _u = _u.at[4, :nx0].set(uL[4]) + _u = _u.loc[0, :nx0].set(uL[0]) + _u = _u.loc[iX, :nx0].set(uL[1]) + _u = _u.loc[iY, :nx0].set(uL[2]) + _u = _u.loc[iZ, :nx0].set(uL[3]) + _u = _u.loc[4, :nx0].set(uL[4]) # right - _u = _u.at[0, nx0:].set(uR[0]) - _u = _u.at[iX, nx0:].set(uR[1]) - _u = _u.at[iY, nx0:].set(uR[2]) - _u = _u.at[iZ, nx0:].set(uR[3]) - _u = _u.at[4, nx0:].set(uR[4]) + _u = _u.loc[0, nx0:].set(uR[0]) + _u = _u.loc[iX, nx0:].set(uR[1]) + _u = _u.loc[iY, nx0:].set(uR[2]) + _u = _u.loc[iZ, nx0:].set(uR[3]) + _u = _u.loc[4, nx0:].set(uR[4]) - if direc == 'x': + if direc == "x": u = _u - elif direc == 'y': + elif direc == "y": u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': + elif direc == "z": u = jnp.transpose(_u, (0, 2, 3, 1)) - elif mode == '2D-shock': # shock tube - u1 = [0.5, 0., 0., 0., 0.1] - u2 = [0.1, 0., 1., 0., 1.] - u3 = [0.1, 1., 0., 0., 1.] - u4 = [0.1, 0., 0., 0., 0.01] + elif mode == "2D-shock": # shock tube + u1 = [0.5, 0.0, 0.0, 0.0, 0.1] + u2 = [0.1, 0.0, 1.0, 0.0, 1.0] + u3 = [0.1, 1.0, 0.0, 0.0, 1.0] + u4 = [0.1, 0.0, 0.0, 0.0, 0.01] # left-bottom - u = u.at[0, :nx//2, :ny//2].set(u1[0]) - u = u.at[1, :nx//2, :ny//2].set(u1[1]) - u = u.at[2, :nx//2, :ny//2].set(u1[2]) - u = u.at[3, :nx//2, :ny//2].set(u1[3]) - u = u.at[4, :nx//2, :ny//2].set(u1[4]) + u = u.loc[0, : nx // 2, : ny // 2].set(u1[0]) + u = u.loc[1, : nx // 2, : ny // 2].set(u1[1]) + u = u.loc[2, : nx // 2, : ny // 2].set(u1[2]) + u = u.loc[3, : nx // 2, : ny // 2].set(u1[3]) + u = u.loc[4, : nx // 2, : ny // 2].set(u1[4]) # right-bottom - u = u.at[0, nx//2:, :ny//2].set(u2[0]) - u = u.at[1, nx//2:, :ny//2].set(u2[1]) - u = u.at[2, nx//2:, :ny//2].set(u2[2]) - u = u.at[3, nx//2:, :ny//2].set(u2[3]) - u = u.at[4, nx//2:, :ny//2].set(u2[4]) + u = u.loc[0, nx // 2 :, : ny // 2].set(u2[0]) + u = u.loc[1, nx // 2 :, : ny // 2].set(u2[1]) + u = u.loc[2, nx // 2 :, : ny // 2].set(u2[2]) + u = u.loc[3, nx // 2 :, : ny // 2].set(u2[3]) + u = u.loc[4, nx // 2 :, : ny // 2].set(u2[4]) # left-top - u = u.at[0, :nx//2, ny//2:].set(u3[0]) - u = u.at[1, :nx//2, ny//2:].set(u3[1]) - u = u.at[2, :nx//2, ny//2:].set(u3[2]) - u = u.at[3, :nx//2, ny//2:].set(u3[3]) - u = u.at[4, :nx//2, ny//2:].set(u3[4]) + u = u.loc[0, : nx // 2, ny // 2 :].set(u3[0]) + u = u.loc[1, : nx // 2, ny // 2 :].set(u3[1]) + u = u.loc[2, : nx // 2, ny // 2 :].set(u3[2]) + u = u.loc[3, : nx // 2, ny // 2 :].set(u3[3]) + u = u.loc[4, : nx // 2, ny // 2 :].set(u3[4]) # right-top - u = u.at[0, nx//2:, ny//2:].set(u4[0]) - u = u.at[1, nx//2:, ny//2:].set(u4[1]) - u = u.at[2, nx//2:, ny//2:].set(u4[2]) - u = u.at[3, nx//2:, ny//2:].set(u4[3]) - u = u.at[4, nx//2:, ny//2:].set(u4[4]) + u = u.loc[0, nx // 2 :, ny // 2 :].set(u4[0]) + u = u.loc[1, nx // 2 :, ny // 2 :].set(u4[1]) + u = u.loc[2, nx // 2 :, ny // 2 :].set(u4[2]) + u = u.loc[3, nx // 2 :, ny // 2 :].set(u4[3]) + u = u.loc[4, nx // 2 :, ny // 2 :].set(u4[4]) - elif mode == 'OTVortex': # shock tube + elif mode == "OTVortex": # shock tube dx = xc[1] - xc[0] dy = yc[1] - yc[0] qLx = dx * xc.shape[0] @@ -326,34 +360,34 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, _yc = _yc.at[2:-2].set(yc) _xc = _xc.at[:2].set(jnp.array([-2 * dx, -dx])) _yc = _yc.at[:2].set(jnp.array([-2 * dy, -dy])) - _xc = _xc.at[-2:].set(jnp.array([xc[-1] + dx, xc[-1] + 2. * dx])) - _yc = _yc.at[-2:].set(jnp.array([yc[-1] + dy, yc[-1] + 2. * dy])) + _xc = _xc.at[-2:].set(jnp.array([xc[-1] + dx, xc[-1] + 2.0 * dx])) + _yc = _yc.at[-2:].set(jnp.array([yc[-1] + dy, yc[-1] + 2.0 * dy])) - u = u.at[0].add(gamma ** 2) - u = u.at[1].set(- jnp.sin(2. * jnp.pi * _yc[None, :, None] / qLy)) - u = u.at[2].set(jnp.sin(2. * jnp.pi * _xc[:, None, None] / qLx)) - u = u.at[3].add(0.) - u = u.at[4].add(gamma) + u = u.loc[0].add(gamma**2) + u = u.loc[1].set(-jnp.sin(2.0 * jnp.pi * _yc[None, :, None] / qLy)) + u = u.loc[2].set(jnp.sin(2.0 * jnp.pi * _xc[:, None, None] / qLx)) + u = u.loc[3].add(0.0) + u = u.loc[4].add(gamma) - elif mode == 'KHI': # Kelvin-Helmholtz instability + elif mode == "KHI": # Kelvin-Helmholtz instability nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - #gamma = 1.666666666666667 - #k = 1. # moved to the external input - d0_u = 2./(dk + 1.) + # gamma = 1.666666666666667 + # k = 1. # moved to the external input + d0_u = 2.0 / (dk + 1.0) d0_d = dk * d0_u d0 = 0.5 * (d0_u + d0_d) - #M0 = 0.1 # Mach number # moved to external input - ux = 1. - cs = ux/M0 - #ux = 0.1 * cs # << cs - p0 = cs**2 * d0/gamma + # M0 = 0.1 # Mach number # moved to external input + ux = 1.0 + cs = ux / M0 + # ux = 0.1 * cs # << cs + p0 = cs**2 * d0 / gamma dx = xc[1] - xc[0] dy = yc[1] - yc[0] qLx = dx * nx qLy = dy * ny - kk = 4. # wave number - kx = kk * 2. * jnp.pi / qLx - dl = 5.e-3 * qLy + kk = 4.0 # wave number + kx = kk * 2.0 * jnp.pi / qLx + dl = 5.0e-3 * qLy bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 @@ -365,19 +399,18 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, vx = vx.at[i, :, :].set(_vx[:, None]) dd = dd.at[i, :, :].set(_dd[:, None]) - u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2].set(0.) - u = u.at[3].add(0.) - u = u.at[4].add(p0) - - elif mode == 'turbulence': # 3D decaying turbulence + u = u.loc[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2].set(0.0) + u = u.loc[3].add(0.0) + u = u.loc[4].add(p0) + elif mode == "turbulence": # 3D decaying turbulence nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma + d0 = 1.0 + cs = 1.0 / M0 + u0 = 1.0 # fixed + p0 = cs**2 * d0 / gamma dx = xc[1] - xc[0] dy = yc[1] - yc[0] @@ -386,15 +419,19 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, qLy = dy * ny qLz = dz * nz - ## random velocity field + # random velocity field k_tot = 3 - vx, vy, vz = np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]), np.zeros([nx, ny, nz]) + vx, vy, vz = ( + np.zeros([nx, ny, nz]), + np.zeros([nx, ny, nz]), + np.zeros([nx, ny, nz]), + ) key = random.PRNGKey(init_key) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy + kz0 = jnp.pi * 2.0 / qLz for k in range(-k_tot, k_tot + 1): kz = kz0 * k # from 1 to k_tot @@ -406,21 +443,25 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + phs = 2.0 * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1.0 / jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = ( + kx * xc[:, None, None] + + ky * yc[None, :, None] + + kz * zc[None, None, :] + ) vx += uk * jnp.sin(kdx + phs[0]) vy += uk * jnp.sin(kdx + phs[1]) vz += uk * jnp.sin(kdx + phs[2]) - del(kdx, uk, phs) + del (kdx, uk, phs) # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz - fx = dfx * (np.arange(nx) - 1. - nx//2) - fy = dfy * (np.arange(ny) - 1. - ny//2) - fz = dfz * (np.arange(nz) - 1. - nz//2) + dfx, dfy, dfz = 1.0 / qLx, 1.0 / qLy, 1.0 / qLz + fx = dfx * (np.arange(nx) - 1.0 - nx // 2) + fy = dfy * (np.arange(ny) - 1.0 - ny // 2) + fz = dfz * (np.arange(nz) - 1.0 - nz // 2) vkx = np.fft.fftn(vx) * dx * dy * dz vky = np.fft.fftn(vy) * dx * dy * dz @@ -431,7 +472,7 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, vky = np.fft.fftshift(vky) vkz = np.fft.fftshift(vkz) - #for k in range(nz): + # for k in range(nz): # for j in range(ny): # for i in range(nx): # ff = (fx[i]**2 + fy[j]**2 + fz[k]**2) @@ -442,14 +483,16 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, # vky -= fdv * fy[j] * fi # vkz -= fdv * fz[k] * fi - fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 - fi = np.where(fi > 1.e-8, 1./fi, 0.) + fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 + fz[None, None, :] ** 2 + fi = np.where(fi > 1.0e-8, 1.0 / fi, 0.0) - fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi - vkx -= fdv * fx[:,None,None] - vky -= fdv * fy[None,:,None] - vkz -= fdv * fz[None,None,:] - del(fi, fdv) + fdv = ( + fx[:, None, None] * vkx + fy[None, :, None] * vky + fz[None, None, :] * vkz + ) * fi + vkx -= fdv * fx[:, None, None] + vky -= fdv * fy[None, :, None] + vkz -= fdv * fz[None, None, :] + del (fi, fdv) # shift back to original order vkx = np.fft.ifftshift(vkx) @@ -467,19 +510,19 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, vy *= u0 / vtot vz *= u0 / vtot - u = u.at[0].set(d0) - u = u.at[1,2:-2,2:-2,2:-2].set(jnp.array(vx)) - u = u.at[2,2:-2,2:-2,2:-2].set(jnp.array(vy)) - u = u.at[3,2:-2,2:-2,2:-2].add(jnp.array(vz)) - u = u.at[4].add(p0) + u = u.loc[0].set(d0) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(jnp.array(vx)) + u = u.loc[2, 2:-2, 2:-2, 2:-2].set(jnp.array(vy)) + u = u.loc[3, 2:-2, 2:-2, 2:-2].add(jnp.array(vz)) + u = u.loc[4].add(p0) - elif mode == 'BlastWave': # Kelvin-Helmholtz instability + elif mode == "BlastWave": # Kelvin-Helmholtz instability """ Stone Gardiner 2009 without B """ nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - db = 1. + db = 1.0 pb = 0.1 - pc = 1.e2 # central region + pc = 1.0e2 # central region dx = xc[1] - xc[0] dy = yc[1] - yc[0] @@ -487,97 +530,112 @@ def init_HD(u, xc, yc, zc, mode='shocktube1', direc='x', init_key=2022, qLx = dx * nx qLy = dy * ny qLz = dz * nz - qL = (qLx + qLy + qLz)/3. - - #p0 = jnp.ones([nx, ny, nz]) * pb - RR = jnp.sqrt((xc[:,None,None] - xc[nx//2])**2 - + (yc[None,:,None] - yc[ny//2])**2 - + (zc[None,None,:] - zc[nz//2])**2) - p0 = jnp.where(RR > 0.05 * qL, pb, pc) - #for k in range(nz): + qL = (qLx + qLy + qLz) / 3.0 + + # p0 = jnp.ones([nx, ny, nz]) * pb + RR = jnp.sqrt( + (xc[:, None, None] - xc[nx // 2]) ** 2 + + (yc[None, :, None] - yc[ny // 2]) ** 2 + + (zc[None, None, :] - zc[nz // 2]) ** 2 + ) + p0 = jnp.where(0.05 * qL < RR, pb, pc) + # for k in range(nz): # for j in range(ny): # for i in range(nx): # RR = jnp.sqrt((xc[i] - 0.5 * qLx)**2 + (yc[j] - 0.5 * qLy)**2 + (zc[k] - 0.5 * qLz)**2) # if RR < 0.1 * qL: # p0 = p0.at[i,j,k].set(pc) - u = u.at[0].set(db) - u = u.at[1].set(0.) - u = u.at[2].set(0.) - u = u.at[3].set(0.) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p0) + u = u.loc[0].set(db) + u = u.loc[1].set(0.0) + u = u.loc[2].set(0.0) + u = u.loc[3].set(0.0) + u = u.loc[4, 2:-2, 2:-2, 2:-2].set(p0) - elif mode == 'sound_wave': # sound wave + elif mode == "sound_wave": # sound wave nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] gamma = 1.666666666666667 - d0 = 1. - cs = 2. - p0 = cs**2 * d0/gamma - if direc == 'x': + d0 = 1.0 + cs = 2.0 + p0 = cs**2 * d0 / gamma + if direc == "x": iX, iY, iZ = 1, 2, 3 XC = xc qL = (xc[1] - xc[0]) * nx _u = jnp.zeros_like(u) - elif direc == 'y': + elif direc == "y": iX, iY, iZ = 2, 3, 1 XC = yc qL = (yc[1] - yc[0]) * ny _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': + if direc == "z": iX, iY, iZ = 3, 1, 2 XC = zc qL = (zc[1] - zc[0]) * nz _u = jnp.transpose(u, (0, 3, 1, 2)) - kk = 2. * jnp.pi / qL - _u = _u.at[0,2:-2].set(d0 * (1. + 1.e-3 * jnp.sin(kk * XC[:, None, None]))) - _u = _u.at[iX].set((_u[0] - d0) * cs /d0) - _u = _u.at[4].set(p0 + cs**2 * (_u[0] - d0) ) + kk = 2.0 * jnp.pi / qL + _u = _u.loc[0, 2:-2].set(d0 * (1.0 + 1.0e-3 * jnp.sin(kk * XC[:, None, None]))) + _u = _u.loc[iX].set((_u[0] - d0) * cs / d0) + _u = _u.loc[4].set(p0 + cs**2 * (_u[0] - d0)) - if direc == 'x': + if direc == "x": u = _u - elif direc == 'y': + elif direc == "y": u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': + elif direc == "z": u = jnp.transpose(_u, (0, 2, 3, 1)) - elif mode == 'c_discon': # tangent discontinuity + elif mode == "c_discon": # tangent discontinuity nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - p0 = 1. + d0 = 1.0 + p0 = 1.0 vy0 = 0.1 - if direc == 'x': + if direc == "x": iX, iY, iZ = 1, 2, 3 XC = xc qL = (xc[1] - xc[0]) * nx _u = jnp.zeros_like(u) - elif direc == 'y': + elif direc == "y": iX, iY, iZ = 2, 3, 1 XC = yc qL = (yc[1] - yc[0]) * ny _u = jnp.transpose(u, (0, 2, 3, 1)) - if direc == 'z': + if direc == "z": iX, iY, iZ = 3, 1, 2 XC = zc qL = (zc[1] - zc[0]) * nz _u = jnp.transpose(u, (0, 3, 1, 2)) - _u = _u.at[0].set(d0) - _u = _u.at[iY, 2:-2].set(vy0 * scipy.special.erf(0.5 * XC[:, None, None] / jnp.sqrt(0.1))) - _u = _u.at[4].set(p0) + _u = _u.loc[0].set(d0) + _u = _u.loc[iY, 2:-2].set( + vy0 * scipy.special.erf(0.5 * XC[:, None, None] / jnp.sqrt(0.1)) + ) + _u = _u.loc[4].set(p0) - if direc == 'x': + if direc == "x": u = _u - elif direc == 'y': + elif direc == "y": u = jnp.transpose(_u, (0, 3, 1, 2)) - elif direc == 'z': + elif direc == "z": u = jnp.transpose(_u, (0, 2, 3, 1)) return u + @partial(jit, static_argnums=(3, 4, 5, 6, 7, 8, 9)) -def init_multi_HD(xc, yc, zc, numbers=10000, k_tot=10, init_key=2022, num_choise_k=2, - if_renorm=False, umax=1.e4, umin=1.e-8): +def init_multi_HD( + xc, + yc, + zc, + numbers=10000, + k_tot=10, + init_key=2022, + num_choise_k=2, + if_renorm=False, + umax=1.0e4, + umin=1.0e-8, +): """ :param xc: cell center coordinate :param mode: initial condition @@ -615,31 +673,37 @@ def _norm(carry): u /= jnp.max(u, axis=1, keepdims=True) # normalize key, subkey = random.split(key) - m_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + m_val = random.uniform( + key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax) + ) m_val = jnp.exp(m_val) key, subkey = random.split(key) - b_val = random.uniform(key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax)) + b_val = random.uniform( + key, shape=[numbers], minval=mt.log(umin), maxval=mt.log(umax) + ) b_val = jnp.exp(b_val) return u * m_val[:, None] + b_val[:, None], key cond, u, key = carry carry = u, key - u, key = lax.cond(cond==True, _norm, _pass, carry) + u, key = lax.cond(cond == True, _norm, _pass, carry) return cond, u, key - assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" key = random.PRNGKey(init_key) - selected = random.randint(key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot) + selected = random.randint( + key, shape=[numbers, num_choise_k], minval=0, maxval=k_tot + ) selected = nn.one_hot(selected, k_tot, dtype=int).sum(axis=1) - kk = jnp.pi * 2. * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) + kk = jnp.pi * 2.0 * jnp.arange(1, k_tot + 1) * selected / (xc[-1] - xc[0]) amp = random.uniform(key, shape=[numbers, k_tot, 1]) key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) + phs = 2.0 * jnp.pi * random.uniform(key, shape=[numbers, k_tot, 1]) _u = amp * jnp.sin(kk[:, :, jnp.newaxis] * xc[jnp.newaxis, jnp.newaxis, :] + phs) _u = jnp.sum(_u, axis=1) @@ -667,17 +731,20 @@ def _norm(carry): carry = if_renorm, _u, key _, _u, _ = renormalize(carry) # renormalize value between a given values - return _u[...,None,None] + return _u[..., None, None] + -#@partial(jit, static_argnums=(3, 4, 5, 6)) -def init_multi_HD_shock(xc, yc, zc, numbers=10000, init_key=2022, umax=1.e4, umin=1.e-8): +# @partial(jit, static_argnums=(3, 4, 5, 6)) +def init_multi_HD_shock( + xc, yc, zc, numbers=10000, init_key=2022, umax=1.0e4, umin=1.0e-8 +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert yc.shape[0] == 1 and zc.shape[0] == 1, 'ny and nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def select_var(carry): def _func(carry): @@ -688,7 +755,7 @@ def _pass(carry): return carry vmin, vmax = carry - vmin, vmax = lax.cond(vmin > 0., _func, _pass, carry) + vmin, vmax = lax.cond(vmin > 0.0, _func, _pass, carry) return vmin, vmax nx = xc.shape[0] @@ -709,33 +776,45 @@ def _pass(carry): u = jnp.arange(xc.shape[0]) u = jnp.tile(u, (numbers, 1)) - u = jax.vmap(jnp.where, axis_name='i')(u < nx0s, QLs, QRs) - return u[...,None,None] - -#@partial(jit, static_argnums=(4, 5, 6, 7, 8, 9)) -def init_multi_HD_KH(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, dkMx=2., kmax=4., gamma=1.666666667): + u = jax.vmap(jnp.where, axis_name="i")(u < nx0s, QLs, QRs) + return u[..., None, None] + + +# @partial(jit, static_argnums=(4, 5, 6, 7, 8, 9)) +def init_multi_HD_KH( + u, + xc, + yc, + zc, + numbers=10000, + init_key=2022, + M0=0.1, + dkMx=2.0, + kmax=4.0, + gamma=1.666666667, +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert zc.shape[0] == 1, "nz is assumed to be 1!!" + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def __create_KH_init(u, dk, kk): nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0_u = 2./(dk + 1.) + d0_u = 2.0 / (dk + 1.0) d0_d = dk * d0_u d0 = 0.5 * (d0_u + d0_d) - ux = 1. - cs = ux/M0 - p0 = cs**2 * d0/gamma + ux = 1.0 + cs = ux / M0 + p0 = cs**2 * d0 / gamma dx = xc[1] - xc[0] dy = yc[1] - yc[0] qLx = dx * nx qLy = dy * ny - kx = kk * 2. * jnp.pi / qLx - dl = 5.e-3 * qLy + kx = kk * 2.0 * jnp.pi / qLx + dl = 5.0e-3 * qLy # (numbers, nx) bound = 0.5 * qLy + dl * jnp.sin(kx * xc) # assuming yL = 0 @@ -747,40 +826,43 @@ def __create_KH_init(u, dk, kk): vx = vx.at[i, :, :].set(_vx[:, None]) dd = dd.at[i, :, :].set(_dd[:, None]) - u = u.at[0, 2:-2, 2:-2, 2:-2].set(dd) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2].set(0.) - u = u.at[3].add(0.) - u = u.at[4].add(p0) + u = u.loc[0, 2:-2, 2:-2, 2:-2].set(dd) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2].set(0.0) + u = u.loc[3].add(0.0) + u = u.loc[4].add(p0) return u # create random density ratio key = random.PRNGKey(init_key) - dk = random.uniform(key, shape=([numbers, 1]), minval=1. / dkMx, maxval=dkMx) - #create random wave-numbers + dk = random.uniform(key, shape=([numbers, 1]), minval=1.0 / dkMx, maxval=dkMx) + # create random wave-numbers key, subkey = random.split(key) kk = random.randint(key, shape=([numbers, 1]), minval=1, maxval=kmax) - print('vmap...') - u = jax.vmap(__create_KH_init, axis_name='i')(u, dk, kk) + print("vmap...") + u = jax.vmap(__create_KH_init, axis_name="i")(u, dk, kk) return u -#@partial(jit, static_argnums=(4, 5, 6, 7, 8)) -def init_multi_HD_2DTurb(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + +# @partial(jit, static_argnums=(4, 5, 6, 7, 8)) +def init_multi_HD_2DTurb( + u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4.0, gamma=1.666666667 +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert zc.shape[0] == 1, "nz is assumed to be 1!!" + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def __create_2DTurb_init(u, keys): nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma + d0 = 1.0 + cs = 1.0 / M0 + u0 = 1.0 # fixed + p0 = cs**2 * d0 / gamma dx = xc[1] - xc[0] dy = yc[1] - yc[0] @@ -788,13 +870,13 @@ def __create_2DTurb_init(u, keys): qLx = dx * nx qLy = dy * ny - ## random velocity field + # random velocity field vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) key = random.PRNGKey(keys) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy for j in range(-k_tot, k_tot + 1): ky = ky0 * j # from 1 to k_tot @@ -804,9 +886,9 @@ def __create_2DTurb_init(u, keys): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[2]) # (vi, k) + phs = 2.0 * jnp.pi * random.uniform(key, shape=[2]) # (vi, k) - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + uk = 1.0 / jnp.sqrt(jnp.sqrt(kx**2 + ky**2)) kdx = kx * xc[:, None, None] + ky * yc[None, :, None] vx += uk * jnp.sin(kdx + phs[0]) vy += uk * jnp.sin(kdx + phs[1]) @@ -814,9 +896,9 @@ def __create_2DTurb_init(u, keys): del (kdx, uk, phs) # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy = 1. / qLx, 1. / qLy - fx = dfx * (jnp.arange(nx) - 1. - nx // 2) - fy = dfy * (jnp.arange(ny) - 1. - ny // 2) + dfx, dfy = 1.0 / qLx, 1.0 / qLy + fx = dfx * (jnp.arange(nx) - 1.0 - nx // 2) + fy = dfy * (jnp.arange(ny) - 1.0 - ny // 2) vkx = jnp.fft.fftn(vx) * dx * dy vky = jnp.fft.fftn(vy) * dx * dy @@ -826,7 +908,7 @@ def __create_2DTurb_init(u, keys): vky = jnp.fft.fftshift(vky) fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 - fi = jnp.where(fi > 1.e-8, 1. / fi, 0.) + fi = jnp.where(fi > 1.0e-8, 1.0 / fi, 0.0) fdv = (fx[:, None, None] * vkx + fy[None, :, None] * vky) * fi vkx -= fdv * fx[:, None, None] @@ -842,31 +924,50 @@ def __create_2DTurb_init(u, keys): vy = jnp.fft.ifftn(vky).real * dfx * dfy # renormalize total velocity - vtot = jnp.sqrt(vx ** 2 + vy ** 2).mean() + vtot = jnp.sqrt(vx**2 + vy**2).mean() vx *= u0 / vtot vy *= u0 / vtot - u = u.at[0].set(d0) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[4].add(p0) + u = u.loc[0].set(d0) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.loc[4].add(p0) return u key = random.PRNGKey(init_key) - keys = random.randint(key, [numbers,], minval=0, maxval=10000000) - u = jax.vmap(__create_2DTurb_init, axis_name='i')(u, keys) + keys = random.randint( + key, + [ + numbers, + ], + minval=0, + maxval=10000000, + ) + u = jax.vmap(__create_2DTurb_init, axis_name="i")(u, keys) return u -def init_multi_HD_2DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, - dMx=1.e1, TMx=1.e1): + +def init_multi_HD_2DRand( + u, + xc, + yc, + zc, + numbers=10000, + init_key=2022, + M0=0.1, + k_tot=4.0, + gamma=1.666666667, + dMx=1.0e1, + TMx=1.0e1, +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert zc.shape[0] == 1, 'nz is assumed to be 1!!' - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert zc.shape[0] == 1, "nz is assumed to be 1!!" + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def _pass(carry): return carry @@ -898,12 +999,17 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): qLx = dx * nx qLy = dy * ny - ## random velocity field - d, p, vx, vy = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + # random velocity field + d, p, vx, vy = ( + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + ) key = random.PRNGKey(keys) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy for j in range(-k_tot, k_tot + 1): ky = ky0 * j # from 1 to k_tot @@ -913,9 +1019,9 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[4]) # (vi, k) + phs = 2.0 * jnp.pi * random.uniform(key, shape=[4]) # (vi, k) - uk = 1. / jnp.sqrt(jnp.sqrt(kx ** 2 + ky ** 2)) + uk = 1.0 / jnp.sqrt(jnp.sqrt(kx**2 + ky**2)) kdx = kx * xc[:, None, None] + ky * yc[None, :, None] vx += uk * jnp.sin(kdx + phs[0]) vy += uk * jnp.sin(kdx + phs[1]) @@ -925,33 +1031,41 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): del (kdx, uk, phs) # renormalize total velocity - vtot = jnp.sqrt(vx ** 2 + vy ** 2).mean() + vtot = jnp.sqrt(vx**2 + vy**2).mean() vx *= u0 / vtot vy *= u0 / vtot - #d = d0 + delD * d / jnp.abs(d).mean() - #p = p0 + delP * p / jnp.abs(p).mean() - d = d0 * (1. + delD * d / jnp.abs(d).mean()) - p = p0 * (1. + delP * p / jnp.abs(p).mean()) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + # d = d0 + delD * d / jnp.abs(d).mean() + # p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1.0 + delD * d / jnp.abs(d).mean()) + p = p0 * (1.0 + delP * p / jnp.abs(p).mean()) + + u = u.loc[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.loc[4, 2:-2, 2:-2, 2:-2].set(p) return u key = random.PRNGKey(init_key) - d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=dMx) key, subkey = random.split(key) - delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.0e-2, maxval=0.2) key, subkey = random.split(key) - T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=TMx) key, subkey = random.split(key) - delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.0e-2, maxval=0.2) key, subkey = random.split(key) - keys = random.randint(key, shape=([numbers, ]), minval=0, maxval=10000000) - u = jax.vmap(__create_2DRand_init, axis_name='i')(u, d0, T0, delD, delP, keys) - + keys = random.randint( + key, + shape=( + [ + numbers, + ] + ), + minval=0, + maxval=10000000, + ) + u = jax.vmap(__create_2DRand_init, axis_name="i")(u, d0, T0, delD, delP, keys) # perform window function key, subkey = random.split(key) @@ -968,26 +1082,35 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): carry = cond, mask, _xc, _yc, xL, xR, yL, yR, trns cond, mask, _xc, _yc, xL, xR, yL, yR, trns = vmap(select_W, 0, 0)(carry) - u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, None]) - u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, None])) - u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, None])) + u = u.loc[:, :, 2:-2, 2:-2, 2:-2].set( + u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, None] + ) + u = u.loc[:, 0, 2:-2, 2:-2, 2:-2].add( + d0[:, :, None, None] * (1.0 - mask[:, :, :, None]) + ) + u = u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( + d0[:, :, None, None] * T0[:, :, None, None] * (1.0 - mask[:, :, :, None]) + ) return u -def init_multi_HD_3DTurb(u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667): + +def init_multi_HD_3DTurb( + u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4.0, gamma=1.666666667 +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def __create_3DTurb_init(u, keys): nx, ny, nz = xc.shape[0], yc.shape[0], zc.shape[0] - d0 = 1. - cs = 1./M0 - u0 = 1. # fixed - p0 = cs ** 2 * d0 / gamma + d0 = 1.0 + cs = 1.0 / M0 + u0 = 1.0 # fixed + p0 = cs**2 * d0 / gamma dx = xc[1] - xc[0] dy = yc[1] - yc[0] @@ -997,14 +1120,18 @@ def __create_3DTurb_init(u, keys): qLy = dy * ny qLz = dz * nz - ## random velocity field - vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + # random velocity field + vx, vy, vz = ( + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + ) key = random.PRNGKey(keys) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy + kz0 = jnp.pi * 2.0 / qLz for k in range(-k_tot, k_tot + 1): kz = kz0 * k # from 1 to k_tot @@ -1016,21 +1143,25 @@ def __create_3DTurb_init(u, keys): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + phs = 2.0 * jnp.pi * random.uniform(key, shape=[3]) # (vi, k) + + uk = 1.0 / jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = ( + kx * xc[:, None, None] + + ky * yc[None, :, None] + + kz * zc[None, None, :] + ) vx += uk * jnp.sin(kdx + phs[0]) vy += uk * jnp.sin(kdx + phs[1]) vz += uk * jnp.sin(kdx + phs[2]) - del(kdx, uk, phs) + del (kdx, uk, phs) # Helmholtz decomposition to subtract expansion: k.vk - dfx, dfy, dfz = 1./qLx, 1./qLy, 1./qLz - fx = dfx * (jnp.arange(nx) - 1. - nx//2) - fy = dfy * (jnp.arange(ny) - 1. - ny//2) - fz = dfz * (jnp.arange(nz) - 1. - nz//2) + dfx, dfy, dfz = 1.0 / qLx, 1.0 / qLy, 1.0 / qLz + fx = dfx * (jnp.arange(nx) - 1.0 - nx // 2) + fy = dfy * (jnp.arange(ny) - 1.0 - ny // 2) + fz = dfz * (jnp.arange(nz) - 1.0 - nz // 2) vkx = jnp.fft.fftn(vx) * dx * dy * dz vky = jnp.fft.fftn(vy) * dx * dy * dz @@ -1041,14 +1172,16 @@ def __create_3DTurb_init(u, keys): vky = jnp.fft.fftshift(vky) vkz = jnp.fft.fftshift(vkz) - fi = fx[:,None,None]**2 + fy[None,:,None]**2 + fz[None,None,:]**2 - fi = jnp.where(fi > 1.e-8, 1./fi, 0.) + fi = fx[:, None, None] ** 2 + fy[None, :, None] ** 2 + fz[None, None, :] ** 2 + fi = jnp.where(fi > 1.0e-8, 1.0 / fi, 0.0) - fdv = (fx[:,None,None] * vkx + fy[None,:,None] * vky + fz[None,None,:] * vkz) * fi - vkx -= fdv * fx[:,None,None] - vky -= fdv * fy[None,:,None] - vkz -= fdv * fz[None,None,:] - del(fi, fdv) + fdv = ( + fx[:, None, None] * vkx + fy[None, :, None] * vky + fz[None, None, :] * vkz + ) * fi + vkx -= fdv * fx[:, None, None] + vky -= fdv * fy[None, :, None] + vkz -= fdv * fz[None, None, :] + del (fi, fdv) # shift back to original order vkx = jnp.fft.ifftshift(vkx) @@ -1066,27 +1199,46 @@ def __create_3DTurb_init(u, keys): vy *= u0 / vtot vz *= u0 / vtot - u = u.at[0].set(d0) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) - u = u.at[4].add(p0) + u = u.loc[0].set(d0) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.loc[3, 2:-2, 2:-2, 2:-2].set(vz) + u = u.loc[4].add(p0) return u key = random.PRNGKey(init_key) - keys = random.randint(key, [numbers,], minval=0, maxval=10000000) - u = jax.vmap(__create_3DTurb_init, axis_name='i')(u, keys) + keys = random.randint( + key, + [ + numbers, + ], + minval=0, + maxval=10000000, + ) + u = jax.vmap(__create_3DTurb_init, axis_name="i")(u, keys) return u -def init_multi_HD_3DRand(u, xc, yc, zc, numbers=10000, init_key=2022, M0=0.1, k_tot=4., gamma=1.666666667, - dMx=1.e1, TMx=1.e1): + +def init_multi_HD_3DRand( + u, + xc, + yc, + zc, + numbers=10000, + init_key=2022, + M0=0.1, + k_tot=4.0, + gamma=1.666666667, + dMx=1.0e1, + TMx=1.0e1, +): """ :param xc: cell center coordinate :param mode: initial condition :return: 1D scalar function u at cell center """ - assert numbers % jax.device_count() == 0, 'numbers should be : GPUs x integer!!' + assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def _pass(carry): return carry @@ -1103,7 +1255,9 @@ def _window(carry): cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns = carry carry = xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns - xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns = lax.cond(cond == 1, _window, _pass, carry) + xx, yy, zz, value, xL, xR, yL, yR, zL, zR, trns = lax.cond( + cond == 1, _window, _pass, carry + ) return cond, value, xx, yy, zz, xL, xR, yL, yR, zL, zR, trns def __create_3DRand_init(u, d0, T0, delD, delP, keys): @@ -1121,14 +1275,19 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): qLy = dy * ny qLz = dz * nz - ## random velocity field - d, p, vx, vy, vz = jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), \ - jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]), jnp.zeros([nx, ny, nz]) + # random velocity field + d, p, vx, vy, vz = ( + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + jnp.zeros([nx, ny, nz]), + ) key = random.PRNGKey(keys) - kx0 = jnp.pi * 2. / qLx - ky0 = jnp.pi * 2. / qLy - kz0 = jnp.pi * 2. / qLz + kx0 = jnp.pi * 2.0 / qLx + ky0 = jnp.pi * 2.0 / qLy + kz0 = jnp.pi * 2.0 / qLz for k in range(-k_tot, k_tot + 1): kz = kz0 * k # from 1 to k_tot @@ -1140,47 +1299,58 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): continue # random phase key, subkey = random.split(key) - phs = 2. * jnp.pi * random.uniform(key, shape=[5]) # (vi, k) - - uk = 1./jnp.sqrt(kx**2 + ky**2 + kz**2) - kdx = kx * xc[:,None,None] + ky * yc[None,:,None] + kz * zc[None,None,:] + phs = 2.0 * jnp.pi * random.uniform(key, shape=[5]) # (vi, k) + + uk = 1.0 / jnp.sqrt(kx**2 + ky**2 + kz**2) + kdx = ( + kx * xc[:, None, None] + + ky * yc[None, :, None] + + kz * zc[None, None, :] + ) vx += uk * jnp.sin(kdx + phs[0]) vy += uk * jnp.sin(kdx + phs[1]) vz += uk * jnp.sin(kdx + phs[2]) p += uk * jnp.sin(kdx + phs[3]) d += uk * jnp.sin(kdx + phs[4]) - del(kdx, uk, phs) + del (kdx, uk, phs) # renormalize total velocity - vtot = jnp.sqrt(vx ** 2 + vy ** 2 + vz ** 2).mean() + vtot = jnp.sqrt(vx**2 + vy**2 + vz**2).mean() vx *= u0 / vtot vy *= u0 / vtot vz *= u0 / vtot - #d = d0 + delD * d / jnp.abs(d).mean() - #p = p0 + delP * p / jnp.abs(p).mean() - d = d0 * (1. + delD * d / jnp.abs(d).mean()) - p = p0 * (1. + delP * p / jnp.abs(p).mean()) - - u = u.at[0, 2:-2, 2:-2, 2:-2].set(d) - u = u.at[1, 2:-2, 2:-2, 2:-2].set(vx) - u = u.at[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.at[3, 2:-2, 2:-2, 2:-2].set(vz) - u = u.at[4, 2:-2, 2:-2, 2:-2].set(p) + # d = d0 + delD * d / jnp.abs(d).mean() + # p = p0 + delP * p / jnp.abs(p).mean() + d = d0 * (1.0 + delD * d / jnp.abs(d).mean()) + p = p0 * (1.0 + delP * p / jnp.abs(p).mean()) + + u = u.loc[0, 2:-2, 2:-2, 2:-2].set(d) + u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) + u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) + u = u.loc[3, 2:-2, 2:-2, 2:-2].set(vz) + u = u.loc[4, 2:-2, 2:-2, 2:-2].set(p) return u key = random.PRNGKey(init_key) - d0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=dMx) + d0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=dMx) key, subkey = random.split(key) - delD = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + delD = random.uniform(key, shape=([numbers, 1]), minval=1.0e-2, maxval=0.2) key, subkey = random.split(key) - T0 = random.uniform(key, shape=([numbers, 1]), minval=1.e-1, maxval=TMx) + T0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=TMx) key, subkey = random.split(key) - delP = random.uniform(key, shape=([numbers, 1]), minval=1.e-2, maxval=0.2) + delP = random.uniform(key, shape=([numbers, 1]), minval=1.0e-2, maxval=0.2) key, subkey = random.split(key) - keys = random.randint(key, [numbers,], minval=0, maxval=10000000) - u = jax.vmap(__create_3DRand_init, axis_name='i')(u, d0, T0, delD, delP, keys) + keys = random.randint( + key, + [ + numbers, + ], + minval=0, + maxval=10000000, + ) + u = jax.vmap(__create_3DRand_init, axis_name="i")(u, d0, T0, delD, delP, keys) # perform window function key, subkey = random.split(key) @@ -1199,115 +1369,127 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): _zc = jnp.repeat(zc[None, :], numbers, axis=0) trns = 0.01 * jnp.ones_like(cond) carry = cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns - cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns = vmap(select_W, 0, 0)(carry) - - u = u.at[:, :, 2:-2, 2:-2, 2:-2].set(u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, :]) - u = u.at[:, 0, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * (1. - mask[:, :, :, :])) - u = u.at[:, 4, 2:-2, 2:-2, 2:-2].add(d0[:, :, None, None] * T0[:, :, None, None] * (1. - mask[:, :, :, :])) + cond, mask, _xc, _yc, _zc, xL, xR, yL, yR, zL, zR, trns = vmap(select_W, 0, 0)( + carry + ) + + u = u.loc[:, :, 2:-2, 2:-2, 2:-2].set( + u[:, :, 2:-2, 2:-2, 2:-2] * mask[:, None, :, :, :] + ) + u = u.loc[:, 0, 2:-2, 2:-2, 2:-2].add( + d0[:, :, None, None] * (1.0 - mask[:, :, :, :]) + ) + u = u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( + d0[:, :, None, None] * T0[:, :, None, None] * (1.0 - mask[:, :, :, :]) + ) return u -def bc(u, dx, Ncell, mode='periodic'): - _u = jnp.zeros(Ncell+4) # because of 2nd-order precision in space - _u = _u.at[2:Ncell+2].set(u) - if mode=='periodic': # periodic boundary condition - _u = _u.at[0:2].set(u[-2:]) # left hand side - _u = _u.at[Ncell + 2:Ncell + 4].set(u[0:2]) # right hand side - elif mode=='reflection': - _u = _u.at[0].set(- u[3]) # left hand side - _u = _u.at[1].set(- u[2]) # left hand side - _u = _u.at[-2].set(- u[-3]) # right hand side - _u = _u.at[-1].set(- u[-4]) # right hand side - elif mode=='copy': - _u = _u.at[0].set(u[3]) # left hand side - _u = _u.at[1].set(u[2]) # left hand side - _u = _u.at[-2].set(u[-3]) # right hand side - _u = _u.at[-1].set(u[-4]) # right hand side + +def bc(u, dx, Ncell, mode="periodic"): + _u = jnp.zeros(Ncell + 4) # because of 2nd-order precision in space + _u = _u.loc[2 : Ncell + 2].set(u) + if mode == "periodic": # periodic boundary condition + _u = _u.loc[0:2].set(u[-2:]) # left hand side + _u = _u.loc[Ncell + 2 : Ncell + 4].set(u[0:2]) # right hand side + elif mode == "reflection": + _u = _u.loc[0].set(-u[3]) # left hand side + _u = _u.loc[1].set(-u[2]) # left hand side + _u = _u.loc[-2].set(-u[-3]) # right hand side + _u = _u.loc[-1].set(-u[-4]) # right hand side + elif mode == "copy": + _u = _u.loc[0].set(u[3]) # left hand side + _u = _u.loc[1].set(u[2]) # left hand side + _u = _u.loc[-2].set(u[-3]) # right hand side + _u = _u.loc[-1].set(u[-4]) # right hand side return _u -def bc_2D(_u, mode='trans'): + +def bc_2D(_u, mode="trans"): Nx, Ny = _u.shape - u = jnp.zeros([Nx + 4, Ny + 4]) # because of 2nd-order precision in space - u = u.at[2:-2, 2:-2].set(_u) + u = jnp.zeros([Nx + 4, Ny + 4]) # because of 2nd-order precision in space + u = u.loc[2:-2, 2:-2].set(_u) Nx += 2 Ny += 2 - if mode=='periodic': # periodic boundary condition + if mode == "periodic": # periodic boundary condition # left hand side - u = u.at[0:2, 2:-2].set(u[Nx-2:Nx, 2:-2]) # x - u = u.at[2:-2, 0:2].set(u[2:-2, Ny-2:Ny]) # y + u = u.loc[0:2, 2:-2].set(u[Nx - 2 : Nx, 2:-2]) # x + u = u.loc[2:-2, 0:2].set(u[2:-2, Ny - 2 : Ny]) # y # right hand side - u = u.at[Nx:Nx+2, 2:-2].set(u[2:4, 2:-2]) - u = u.at[2:-2, Ny:Ny+2].set(u[2:-2, 2:4]) - elif mode=='trans': # periodic boundary condition + u = u.loc[Nx : Nx + 2, 2:-2].set(u[2:4, 2:-2]) + u = u.loc[2:-2, Ny : Ny + 2].set(u[2:-2, 2:4]) + elif mode == "trans": # periodic boundary condition # left hand side - u = u.at[0, 2:-2].set(u[3, 2:-2]) # x - u = u.at[2:-2, 0].set(u[2:-2, 3]) # y - u = u.at[1, 2:-2].set(u[2, 2:-2]) # x - u = u.at[2:-2, 1].set(u[2:-2, 2]) # y + u = u.loc[0, 2:-2].set(u[3, 2:-2]) # x + u = u.loc[2:-2, 0].set(u[2:-2, 3]) # y + u = u.loc[1, 2:-2].set(u[2, 2:-2]) # x + u = u.loc[2:-2, 1].set(u[2:-2, 2]) # y # right hand side - u = u.at[-2, 2:-2].set(u[-3, 2:-2]) - u = u.at[2:-2, -2].set(u[2:-2, -3]) - u = u.at[-1, 2:-2].set(u[-4, 2:-2]) - u = u.at[2:-2, -1].set(u[2:-2, -4]) - elif mode=='Neumann': # periodic boundary condition + u = u.loc[-2, 2:-2].set(u[-3, 2:-2]) + u = u.loc[2:-2, -2].set(u[2:-2, -3]) + u = u.loc[-1, 2:-2].set(u[-4, 2:-2]) + u = u.loc[2:-2, -1].set(u[2:-2, -4]) + elif mode == "Neumann": # periodic boundary condition # left hand side - u = u.at[0, 2:-2].set(0.) # x - u = u.at[2:-2, 0].set(0.) # y - u = u.at[1, 2:-2].set(0.) # x - u = u.at[2:-2, 1].set(0.) # y + u = u.loc[0, 2:-2].set(0.0) # x + u = u.loc[2:-2, 0].set(0.0) # y + u = u.loc[1, 2:-2].set(0.0) # x + u = u.loc[2:-2, 1].set(0.0) # y # right hand side - u = u.at[-2, 2:-2].set(0.) - u = u.at[2:-2, -2].set(0.) - u = u.at[-1, 2:-2].set(0.) - u = u.at[2:-2, -1].set(0.) + u = u.loc[-2, 2:-2].set(0.0) + u = u.loc[2:-2, -2].set(0.0) + u = u.loc[-1, 2:-2].set(0.0) + u = u.loc[2:-2, -1].set(0.0) return u + def bc_HD(u, mode): _, Nx, Ny, Nz = u.shape Nx -= 2 Ny -= 2 Nz -= 2 - if mode=='periodic': # periodic boundary condition + if mode == "periodic": # periodic boundary condition # left hand side - u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx-2:Nx, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0:2, 2:-2].set(u[:, 2:-2, Ny-2:Ny, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0:2].set(u[:, 2:-2, 2:-2, Nz-2:Nz]) # z + u = u.loc[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2 : Nx, 2:-2, 2:-2]) # x + u = u.loc[:, 2:-2, 0:2, 2:-2].set(u[:, 2:-2, Ny - 2 : Ny, 2:-2]) # y + u = u.loc[:, 2:-2, 2:-2, 0:2].set(u[:, 2:-2, 2:-2, Nz - 2 : Nz]) # z # right hand side - u = u.at[:, Nx:Nx+2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, Ny:Ny+2, 2:-2].set(u[:, 2:-2, 2:4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, Nz:Nz+2].set(u[:, 2:-2, 2:-2, 2:4]) - elif mode=='trans': # periodic boundary condition + u = u.loc[:, Nx : Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.loc[:, 2:-2, Ny : Ny + 2, 2:-2].set(u[:, 2:-2, 2:4, 2:-2]) + u = u.loc[:, 2:-2, 2:-2, Nz : Nz + 2].set(u[:, 2:-2, 2:-2, 2:4]) + elif mode == "trans": # periodic boundary condition # left hand side - u = u.at[:, 0, 2:-2, 2:-2].set(u[:, 3, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z - u = u.at[:, 1, 2:-2, 2:-2].set(u[:, 2, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + u = u.loc[:, 0, 2:-2, 2:-2].set(u[:, 3, 2:-2, 2:-2]) # x + u = u.loc[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.loc[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.loc[:, 1, 2:-2, 2:-2].set(u[:, 2, 2:-2, 2:-2]) # x + u = u.loc[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.loc[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z # right hand side - u = u.at[:, -2, 2:-2, 2:-2].set(u[:, -3, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) - u = u.at[:, -1, 2:-2, 2:-2].set(u[:, -4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) - elif mode=='KHI': # x: periodic, y, z : trans + u = u.loc[:, -2, 2:-2, 2:-2].set(u[:, -3, 2:-2, 2:-2]) + u = u.loc[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.loc[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.loc[:, -1, 2:-2, 2:-2].set(u[:, -4, 2:-2, 2:-2]) + u = u.loc[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.loc[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + elif mode == "KHI": # x: periodic, y, z : trans # left hand side - u = u.at[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2:Nx, 2:-2, 2:-2]) # x - u = u.at[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z - u = u.at[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y - u = u.at[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z + u = u.loc[:, 0:2, 2:-2, 2:-2].set(u[:, Nx - 2 : Nx, 2:-2, 2:-2]) # x + u = u.loc[:, 2:-2, 0, 2:-2].set(u[:, 2:-2, 3, 2:-2]) # y + u = u.loc[:, 2:-2, 2:-2, 0].set(u[:, 2:-2, 2:-2, 3]) # z + u = u.loc[:, 2:-2, 1, 2:-2].set(u[:, 2:-2, 2, 2:-2]) # y + u = u.loc[:, 2:-2, 2:-2, 1].set(u[:, 2:-2, 2:-2, 2]) # z # right hand side - u = u.at[:, Nx:Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) - u = u.at[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) - u = u.at[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) - u = u.at[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) + u = u.loc[:, Nx : Nx + 2, 2:-2, 2:-2].set(u[:, 2:4, 2:-2, 2:-2]) + u = u.loc[:, 2:-2, -2, 2:-2].set(u[:, 2:-2, -3, 2:-2]) + u = u.loc[:, 2:-2, 2:-2, -2].set(u[:, 2:-2, 2:-2, -3]) + u = u.loc[:, 2:-2, -1, 2:-2].set(u[:, 2:-2, -4, 2:-2]) + u = u.loc[:, 2:-2, 2:-2, -1].set(u[:, 2:-2, 2:-2, -4]) return u + def bc_HD_vis(u, if_periodic=True): # for viscosity """ for the moment, assuming periodic/copy boundary @@ -1319,119 +1501,144 @@ def bc_HD_vis(u, if_periodic=True): # for viscosity Nz -= 2 if if_periodic: - u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, Nx - 2:Nx, Ny - 2:Ny, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2:Nx, 2:-2, Nz - 2:Nz]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, Nx - 2:Nx, 2:4, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, Nx - 2:Nx, 2:-2, 2:4]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2:Ny, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2:Nz]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT - else: # trans - u = u.at[:, 0:2, 0:2, 2:-2].set(u[:, 4:2, 4:2, 2:-2]) # xByT - u = u.at[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB - u = u.at[:, 0:2, Ny:Ny + 2, 2:-2].set(u[:, 4:2, Ny:Ny-2, 2:-2]) # xByB - u = u.at[:, 0:2, 2:-2, Nz:Nz + 2].set(u[:, 4:2, 2:-2, Nz:Nz-2]) # xBzT - u = u.at[:, Nx:Nx + 2, 0:2, 2:-2].set(u[:, Nx:Nx-2, 4:2, 2:-2]) # xTyB - u = u.at[:, Nx:Nx + 2, 2:-2, 0:2].set(u[:, Nx:Nx-2, 2:-2, 4:2]) # xTzB - u = u.at[:, Nx:Nx + 2, Ny:Ny + 2, 2:-2].set(u[:, Nx:Nx-2, Ny:Ny-2, 2:-2]) # xTyT - u = u.at[:, Nx:Nx + 2, 2:-2, Nz:Nz + 2].set(u[:, Nx:Nx-2, 2:-2, Nz:Nz-2]) # xTzT + u = u.loc[:, 0:2, 0:2, 2:-2].set(u[:, Nx - 2 : Nx, Ny - 2 : Ny, 2:-2]) # xByB + u = u.loc[:, 0:2, 2:-2, 0:2].set(u[:, Nx - 2 : Nx, 2:-2, Nz - 2 : Nz]) # xBzB + u = u.loc[:, 0:2, Ny : Ny + 2, 2:-2].set(u[:, Nx - 2 : Nx, 2:4, 2:-2]) # xByT + u = u.loc[:, 0:2, 2:-2, Nz : Nz + 2].set(u[:, Nx - 2 : Nx, 2:-2, 2:4]) # xBzT + u = u.loc[:, Nx : Nx + 2, 0:2, 2:-2].set(u[:, 2:4, Ny - 2 : Ny, 2:-2]) # xTyB + u = u.loc[:, Nx : Nx + 2, 2:-2, 0:2].set(u[:, 2:4, 2:-2, Nz - 2 : Nz]) # xTzB + u = u.loc[:, Nx : Nx + 2, Ny : Ny + 2, 2:-2].set(u[:, 2:4, 2:4, 2:-2]) # xTyT + u = u.loc[:, Nx : Nx + 2, 2:-2, Nz : Nz + 2].set(u[:, 2:4, 2:-2, 2:4]) # xTzT + else: # trans + u = u.loc[:, 0:2, 0:2, 2:-2].set(u[:, 4:2, 4:2, 2:-2]) # xByT + u = u.loc[:, 0:2, 2:-2, 0:2].set(u[:, 4:2, 2:-2, 4:2]) # xBzB + u = u.loc[:, 0:2, Ny : Ny + 2, 2:-2].set(u[:, 4:2, Ny : Ny - 2, 2:-2]) # xByB + u = u.loc[:, 0:2, 2:-2, Nz : Nz + 2].set(u[:, 4:2, 2:-2, Nz : Nz - 2]) # xBzT + u = u.loc[:, Nx : Nx + 2, 0:2, 2:-2].set(u[:, Nx : Nx - 2, 4:2, 2:-2]) # xTyB + u = u.loc[:, Nx : Nx + 2, 2:-2, 0:2].set(u[:, Nx : Nx - 2, 2:-2, 4:2]) # xTzB + u = u.loc[:, Nx : Nx + 2, Ny : Ny + 2, 2:-2].set( + u[:, Nx : Nx - 2, Ny : Ny - 2, 2:-2] + ) # xTyT + u = u.loc[:, Nx : Nx + 2, 2:-2, Nz : Nz + 2].set( + u[:, Nx : Nx - 2, 2:-2, Nz : Nz - 2] + ) # xTzT return u -def VLlimiter(a, b, c, alpha=2.): - return jnp.sign(c)\ - *(0.5 + 0.5*jnp.sign(a*b))\ - *jnp.minimum(alpha*jnp.minimum(jnp.abs(a), jnp.abs(b)), jnp.abs(c)) + +def VLlimiter(a, b, c, alpha=2.0): + return ( + jnp.sign(c) + * (0.5 + 0.5 * jnp.sign(a * b)) + * jnp.minimum(alpha * jnp.minimum(jnp.abs(a), jnp.abs(b)), jnp.abs(c)) + ) + def limiting(u, Ncell, if_second_order): # under construction - duL = u[1:Ncell + 3] - u[0:Ncell + 2] - duR = u[2:Ncell + 4] - u[1:Ncell + 3] - duM = (u[2:Ncell + 4] - u[0:Ncell + 2])*0.5 - gradu = VLlimiter(duL, duR, duM) * if_second_order + du_L = u[1 : Ncell + 3] - u[0 : Ncell + 2] + du_R = u[2 : Ncell + 4] - u[1 : Ncell + 3] + du_M = (u[2 : Ncell + 4] - u[0 : Ncell + 2]) * 0.5 + gradu = VLlimiter(du_L, du_R, du_M) * if_second_order # -1:Ncell - #uL, uR = jnp.zeros(Ncell+4), jnp.zeros(Ncell+4) + # uL, uR = jnp.zeros(Ncell+4), jnp.zeros(Ncell+4) uL, uR = jnp.zeros_like(u), jnp.zeros_like(u) - uL = uL.at[1:Ncell+3].set(u[1:Ncell+3] - 0.5*gradu) # left of cell - uR = uR.at[1:Ncell+3].set(u[1:Ncell+3] + 0.5*gradu) # right of cell + # left of cell + uL = uL.loc[1 : Ncell + 3].set(u[1 : Ncell + 3] - 0.5 * gradu) + # right of cell + uR = uR.loc[1 : Ncell + 3].set(u[1 : Ncell + 3] + 0.5 * gradu) return uL, uR + def limiting_HD(u, if_second_order): - nd, nx, ny, nz = u.shape + _, nx, _, _ = u.shape uL, uR = u, u nx -= 4 - duL = u[:, 1:nx + 3, :, :] - u[:, 0:nx + 2, :, :] - duR = u[:, 2:nx + 4, :, :] - u[:, 1:nx + 3, :, :] - duM = (u[:, 2:nx + 4, :, :] - u[:, 0:nx + 2, :, :]) * 0.5 - gradu = VLlimiter(duL, duR, duM) * if_second_order + du_L = u[:, 1 : nx + 3, :, :] - u[:, 0 : nx + 2, :, :] + du_R = u[:, 2 : nx + 4, :, :] - u[:, 1 : nx + 3, :, :] + du_M = (u[:, 2 : nx + 4, :, :] - u[:, 0 : nx + 2, :, :]) * 0.5 + gradu = VLlimiter(du_L, du_R, du_M) * if_second_order # -1:Ncell - uL = uL.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] - 0.5*gradu) # left of cell - uR = uR.at[:, 1:nx + 3, :, :].set(u[:, 1:nx + 3, :, :] + 0.5*gradu) # right of cell - - uL = jnp.where(uL[0] > 0., uL, u) - uL = jnp.where(uL[4] > 0., uL, u) - uR = jnp.where(uR[0] > 0., uR, u) - uR = jnp.where(uR[4] > 0., uR, u) + uL = uL.loc[:, 1 : nx + 3, :, :].set( + u[:, 1 : nx + 3, :, :] - 0.5 * gradu + ) # left of cell + uR = uR.loc[:, 1 : nx + 3, :, :].set( + u[:, 1 : nx + 3, :, :] + 0.5 * gradu + ) # right of cell + + uL = jnp.where(uL[0] > 0.0, uL, u) + uL = jnp.where(uL[4] > 0.0, uL, u) + uR = jnp.where(uR[0] > 0.0, uR, u) + uR = jnp.where(uR[4] > 0.0, uR, u) return uL, uR + def save_data(u, xc, i_save, save_dir, dt_save=None, if_final=False): if if_final: - jnp.save(save_dir+'/x_coordinate', xc) + jnp.save(save_dir + "/x_coordinate", xc) # - tc = jnp.arange(i_save+1)*dt_save - jnp.save(save_dir+'/t_coordinate', tc) + tc = jnp.arange(i_save + 1) * dt_save + jnp.save(save_dir + "/t_coordinate", tc) # - flnm = save_dir+'/Data_'+str(i_save).zfill(4) + flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) else: - flnm = save_dir+'/Data_'+str(i_save).zfill(4) + flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) + def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): if if_final: - jnp.save(save_dir+'/x_coordinate', xc) - jnp.save(save_dir+'/y_coordinate', yc) - jnp.save(save_dir+'/z_coordinate', zc) + jnp.save(save_dir + "/x_coordinate", xc) + jnp.save(save_dir + "/y_coordinate", yc) + jnp.save(save_dir + "/z_coordinate", zc) # - tc = jnp.arange(i_save+1)*dt_save - jnp.save(save_dir+'/t_coordinate', tc) + tc = jnp.arange(i_save + 1) * dt_save + jnp.save(save_dir + "/t_coordinate", tc) # - flnm = save_dir+'/Data_'+str(i_save).zfill(4) + flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) else: - flnm = save_dir+'/Data_'+str(i_save).zfill(4) + flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) + def Courant(u, dx): - stability_adv = dx/(jnp.max(jnp.abs(u)) + 1.e-8) + stability_adv = dx / (jnp.max(jnp.abs(u)) + 1.0e-8) return stability_adv -def Courant_diff(dx, epsilon=1.e-3): - stability_dif = 0.5*dx**2/(epsilon + 1.e-8) + +def Courant_diff(dx, epsilon=1.0e-3): + stability_dif = 0.5 * dx**2 / (epsilon + 1.0e-8) return stability_dif -def Courant_diff_2D(dx, dy, epsilon=1.e-3): - stability_dif_x = 0.5*dx**2/(epsilon + 1.e-8) - stability_dif_y = 0.5*dy**2/(epsilon + 1.e-8) + +def Courant_diff_2D(dx, dy, epsilon=1.0e-3): + stability_dif_x = 0.5 * dx**2 / (epsilon + 1.0e-8) + stability_dif_y = 0.5 * dy**2 / (epsilon + 1.0e-8) return jnp.min(jnp.array([stability_dif_x, stability_dif_y])) + def Courant_HD(u, dx, dy, dz, gamma): - cs = jnp.sqrt(gamma*u[4]/u[0]) # sound velocity - stability_adv_x = dx/(jnp.max(cs + jnp.abs(u[1])) + 1.e-8) - stability_adv_y = dy/(jnp.max(cs + jnp.abs(u[2])) + 1.e-8) - stability_adv_z = dz/(jnp.max(cs + jnp.abs(u[3])) + 1.e-8) - stability_adv = jnp.min(jnp.array([stability_adv_x, stability_adv_y, stability_adv_z])) + cs = jnp.sqrt(gamma * u[4] / u[0]) # sound velocity + stability_adv_x = dx / (jnp.max(cs + jnp.abs(u[1])) + 1.0e-8) + stability_adv_y = dy / (jnp.max(cs + jnp.abs(u[2])) + 1.0e-8) + stability_adv_z = dz / (jnp.max(cs + jnp.abs(u[3])) + 1.0e-8) + stability_adv = jnp.min( + jnp.array([stability_adv_x, stability_adv_y, stability_adv_z]) + ) return stability_adv + def Courant_vis_HD(dx, dy, dz, eta, zeta): - #visc = jnp.max(jnp.array([eta, zeta])) - visc = 4. / 3. * eta + zeta # maximum - stability_dif_x = 0.5*dx**2/(visc + 1.e-8) - stability_dif_y = 0.5*dy**2/(visc + 1.e-8) - stability_dif_z = 0.5*dz**2/(visc + 1.e-8) - stability_dif = jnp.min(jnp.array([stability_dif_x, stability_dif_y, stability_dif_z])) + # visc = jnp.max(jnp.array([eta, zeta])) + visc = 4.0 / 3.0 * eta + zeta # maximum + stability_dif_x = 0.5 * dx**2 / (visc + 1.0e-8) + stability_dif_y = 0.5 * dy**2 / (visc + 1.0e-8) + stability_dif_z = 0.5 * dz**2 / (visc + 1.0e-8) + stability_dif = jnp.min( + jnp.array([stability_dif_x, stability_dif_y, stability_dif_z]) + ) return stability_dif - diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index 1d710df..f175786 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -1,7 +1,10 @@ #!/usr/bin/env python +from __future__ import annotations import os + import dotenv + # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir # this allows us to keep defaults local to the machine @@ -19,25 +22,25 @@ os.environ["VECLIB_MAXIMUM_THREADS"] = num_threads os.environ["NUMEXPR_NUM_THREADS"] = num_threads -import dotenv -import hydra -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf import logging import multiprocessing as mp from itertools import repeat -import numpy as np -from pdebench.data_gen.src import utils +import dotenv import h5py +import hydra +import numpy as np +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) + def simulator(config, i): - from pdebench.data_gen.src import sim_diff_react - + config.sim.seed = i log.info(f"Starting seed {i}") start_time = time.time() @@ -47,7 +50,7 @@ def simulator(config, i): log.info(f"Seed {config.sim.seed} took {duration} to finish") seed_str = str(i).zfill(4) - + while True: try: with h5py.File(utils.expand_path(config.output_path), "a") as data_f: @@ -56,27 +59,37 @@ def simulator(config, i): ## should be by batch and less than 1MB ## lzf compression for float32 is kind of pointless though. data_f.create_dataset( - f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + f"{seed_str}/data", + data=data_sample, + dtype="float32", + compression="lzf", ) data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + f"{seed_str}/grid/x", + data=sim_obj.x, + dtype="float32", + compression="lzf", ) data_f.create_dataset( - f"{seed_str}/grid/y", data=sim_obj.y, dtype="float32", compression="lzf" + f"{seed_str}/grid/y", + data=sim_obj.y, + dtype="float32", + compression="lzf", ) data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + f"{seed_str}/grid/t", + data=sim_obj.t, + dtype="float32", + compression="lzf", ) seed_group = data_f[seed_str] seed_group.attrs["config"] = OmegaConf.to_yaml(config) - except IOError: + except OSError: time.sleep(0.1) continue else: break - - @hydra.main(config_path="configs/", config_name="diff-react") def main(config: DictConfig): @@ -92,17 +105,14 @@ def main(config: DictConfig): temp_path = os.getcwd() os.chdir(get_original_cwd()) - from src import utils - import h5py - # Change back to the hydra working directory os.chdir(temp_path) - + work_path = os.path.dirname(config.work_dir) output_path = os.path.join(work_path, config.data_dir, config.output_path) if not os.path.isdir(output_path): os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + '.h5' + config.output_path = os.path.join(output_path, config.output_path) + ".h5" num_samples_init = 0 num_samples_final = 1000 @@ -115,16 +125,12 @@ def main(config: DictConfig): if config.upload: dataverse_upload( file_path=config.output_path, - dataverse_url=os.getenv( - 'DATAVERSE_URL', 'https://darus.uni-stuttgart.de'), - dataverse_token=os.getenv( - 'DATAVERSE_API_TOKEN', ''), + dataverse_url=os.getenv("DATAVERSE_URL", "https://darus.uni-stuttgart.de"), + dataverse_token=os.getenv("DATAVERSE_API_TOKEN", ""), dataverse_dir=config.name, - dataverse_id=os.getenv( - 'DATAVERSE_ID', ''), - log=log) - - return + dataverse_id=os.getenv("DATAVERSE_ID", ""), + log=log, + ) import os diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index dfa2017..ec11951 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -1,7 +1,10 @@ #!/usr/bin/env python +from __future__ import annotations import os + import dotenv + # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir # this allows us to keep defaults local to the machine @@ -19,26 +22,25 @@ os.environ["VECLIB_MAXIMUM_THREADS"] = num_threads os.environ["NUMEXPR_NUM_THREADS"] = num_threads -import dotenv -import hydra -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf import logging import multiprocessing as mp from itertools import repeat -import numpy as np -from pdebench.data_gen.src import utils +import dotenv import h5py +import hydra +import numpy as np +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) def simulator(config, i): - from pdebench.data_gen.src import sim_diff_sorp - + config.sim.seed = i log.info(f"Starting seed {i}") start_time = time.time() @@ -46,7 +48,7 @@ def simulator(config, i): data_sample = sim_obj.generate_sample() duration = time.time() - start_time log.info(f"Seed {config.sim.seed} took {duration} to finish") - + seed_str = str(i).zfill(4) while True: @@ -57,23 +59,30 @@ def simulator(config, i): ## should be by batch and less than 1MB ## lzf compression for float32 is kind of pointless though. data_f.create_dataset( - f"{seed_str}/data", data=data_sample, dtype="float32", compression="lzf" + f"{seed_str}/data", + data=data_sample, + dtype="float32", + compression="lzf", ) data_f.create_dataset( - f"{seed_str}/grid/x", data = sim_obj.x, dtype="float32", compression="lzf" + f"{seed_str}/grid/x", + data=sim_obj.x, + dtype="float32", + compression="lzf", ) data_f.create_dataset( - f"{seed_str}/grid/t", data=sim_obj.t, dtype="float32", compression="lzf" + f"{seed_str}/grid/t", + data=sim_obj.t, + dtype="float32", + compression="lzf", ) seed_group = data_f[seed_str] seed_group.attrs["config"] = OmegaConf.to_yaml(config) - except IOError: + except OSError: time.sleep(0.1) continue else: break - - @hydra.main(config_path="configs/", config_name="diff-sorp") @@ -84,49 +93,41 @@ def main(config: DictConfig): # Imports should be nested inside @hydra.main to optimize tab completion # Read more here: https://github.com/facebookresearch/hydra/issues/934 - + # Change to original working directory to import modules - + temp_path = os.getcwd() os.chdir(get_original_cwd()) - - from src import utils - import h5py - - # Change back to the hydra working directory + + # Change back to the hydra working directory os.chdir(temp_path) - + work_path = os.path.dirname(config.work_dir) output_path = os.path.join(work_path, config.data_dir, config.output_path) if not os.path.isdir(output_path): os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + '.h5' - + config.output_path = os.path.join(output_path, config.output_path) + ".h5" + num_samples_init = 0 num_samples_final = 10000 - + pool = mp.Pool(mp.cpu_count()) seed = np.arange(num_samples_init, num_samples_final) seed = seed.tolist() pool.starmap(simulator, zip(repeat(config), seed)) - + if config.upload: dataverse_upload( file_path=config.output_path, - dataverse_url=os.getenv( - 'DATAVERSE_URL', 'https://darus.uni-stuttgart.de'), - dataverse_token=os.getenv( - 'DATAVERSE_API_TOKEN', ''), + dataverse_url=os.getenv("DATAVERSE_URL", "https://darus.uni-stuttgart.de"), + dataverse_token=os.getenv("DATAVERSE_API_TOKEN", ""), dataverse_dir=config.name, - dataverse_id=os.getenv( - 'DATAVERSE_ID', ''), - log=log) - - return + dataverse_id=os.getenv("DATAVERSE_ID", ""), + log=log, + ) import os if __name__ == "__main__": test = main() - \ No newline at end of file diff --git a/pdebench/data_gen/gen_ns_incomp.py b/pdebench/data_gen/gen_ns_incomp.py index 2542965..d66c36f 100644 --- a/pdebench/data_gen/gen_ns_incomp.py +++ b/pdebench/data_gen/gen_ns_incomp.py @@ -1,8 +1,9 @@ #!/usr/bin/env python +from __future__ import annotations -import hydra import dotenv -from omegaconf import DictConfig, OmegaConf +import hydra +from omegaconf import DictConfig # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir @@ -17,8 +18,9 @@ def main(config: DictConfig): Args: config: This function uses hydra configuration for all parameters. """ - + from src import sim_ns_incomp_2d + sim_ns_incomp_2d.ns_sim(config=config, **config) diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index 89b0e8a..664cd93 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -1,7 +1,8 @@ #!/usr/bin/env python +from __future__ import annotations -from copy import deepcopy import os +from copy import deepcopy # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir @@ -21,25 +22,24 @@ os.environ["NUMEXPR_NUM_THREADS"] = num_threads os.environ["NUMEXPR_MAX_THREADS"] = num_threads -import hydra -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf -import h5py import logging import multiprocessing as mp +import time from itertools import repeat -from pdebench.data_gen.src import utils + +import h5py +import hydra import numpy as np -from pdebench.data_gen.uploader import dataverse_upload -import time +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils from pdebench.data_gen.src.sim_radial_dam_break import RadialDamBreak2D +from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) def simulator(base_config, i): - - config = deepcopy(base_config) config.sim.seed = i log.info(f"Starting seed {i}") @@ -60,14 +60,14 @@ def simulator(base_config, i): duration = time.time() - start_time seed_str = str(i).zfill(4) log.info(f"Seed {seed_str} took {duration} to finish") - + while True: try: with h5py.File(utils.expand_path(config.output_path), "a") as h5_file: scenario.save_state_to_disk(h5_file, seed_str) seed_group = h5_file[seed_str] seed_group.attrs["config"] = OmegaConf.to_yaml(config) - except IOError: + except OSError: time.sleep(0.1) continue else: @@ -82,7 +82,7 @@ def simulator(base_config, i): dataverse_id=os.getenv("DATAVERSE_ID", ""), log=log, ) - + @hydra.main(config_path="configs/", config_name="radial_dam_break") def main(config: DictConfig): @@ -98,26 +98,24 @@ def main(config: DictConfig): temp_path = os.getcwd() os.chdir(get_original_cwd()) - - # Change back to the hydra working directory + + # Change back to the hydra working directory os.chdir(temp_path) - + work_path = os.path.dirname(config.work_dir) output_path = os.path.join(work_path, config.data_dir, config.output_path) if not os.path.isdir(output_path): os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + '.h5' + config.output_path = os.path.join(output_path, config.output_path) + ".h5" num_samples_init = 0 num_samples_final = 10000 - + pool = mp.Pool(mp.cpu_count()) seed = np.arange(num_samples_init, num_samples_final) seed = seed.tolist() pool.starmap(simulator, zip(repeat(config), seed)) - return - if __name__ == "__main__": main() diff --git a/pdebench/data_gen/plot.py b/pdebench/data_gen/plot.py index 02a3c6f..fd952ad 100644 --- a/pdebench/data_gen/plot.py +++ b/pdebench/data_gen/plot.py @@ -1,19 +1,18 @@ -# -*- coding: utf-8 -*- """ Created on Wed May 4 09:53:18 2022 @author: timot """ +from __future__ import annotations - +import h5py import hydra -from omegaconf import DictConfig import numpy as np -import h5py -import matplotlib.pyplot as plt from hydra.utils import get_original_cwd +from omegaconf import DictConfig from pdebench.data_gen.src.plots import plot_data + @hydra.main(config_path="configs/", config_name="diff-sorp") def main(config: DictConfig): """ @@ -25,11 +24,11 @@ def main(config: DictConfig): if not os.path.isdir(output_path): os.makedirs(output_path) config.output_path = os.path.join(output_path, config.output_path) - + # Open and load file - data_path = config.output_path + '.h5' + data_path = config.output_path + ".h5" h5_file = h5py.File(data_path, "r") - + if "seed" in config.sim.keys(): # Choose random sample number idx_max = 10000 if config.plot.dim == 1 else 1000 @@ -46,7 +45,7 @@ def main(config: DictConfig): t = np.array(h5_file["grid/t"], dtype="f") t = t[postfix] # data dim = [t, x1, ..., xd, v] - + h5_file.close() os.chdir(get_original_cwd()) @@ -60,8 +59,6 @@ def main(config: DictConfig): config.name + "_" + postfix + ".png", ) - return - import os diff --git a/pdebench/data_gen/src/_attic/grf.py b/pdebench/data_gen/src/_attic/grf.py index 5004f65..5a14504 100644 --- a/pdebench/data_gen/src/_attic/grf.py +++ b/pdebench/data_gen/src/_attic/grf.py @@ -1,17 +1,19 @@ +from __future__ import annotations + import jax import jax.numpy as jnp -def grf( - seed: int = 1234, - xdim: int = 256, - ydim: int = 256, - sigma: float = 0.1, - rho: float = 0.1, - n: int = 1, - ): +def grf( + seed: int = 1234, + xdim: int = 256, + ydim: int = 256, + sigma: float = 0.1, + rho: float = 0.1, + n: int = 1, +): """ - Variables seeting for random + Variables seeding for random seed : random seed sigma : scale(?) @@ -25,21 +27,22 @@ def grf( """ rng = jax.random.PRNGKey(seed) fx, fy = jnp.meshgrid( - jnp.fft.fftfreq(xdim) * xdim, - jnp.fft.rfftfreq(ydim) * ydim, - indexing = 'ij') + jnp.fft.fftfreq(xdim) * xdim, jnp.fft.rfftfreq(ydim) * ydim, indexing="ij" + ) nfx, nfy = fx.shape fnorm = jnp.sqrt(fx**2 + fy**2) - power = jnp.exp(-(fnorm**2/rho)) - gain = jnp.sqrt(sigma**2 * power/power.sum()) # Lazy not calculating normalisation + power = jnp.exp(-(fnorm**2 / rho)) + gain = jnp.sqrt( + sigma**2 * power / power.sum() + ) # Lazy not calculating normalisation noise = ( jax.random.normal(rng, (n, nfx, nfy)) + jax.random.normal(rng, (n, nfx, nfy)) * 1j ) - noise = noise.at[...,0].set(jnp.abs(noise[..., 0])) - ## TODO: This is the rbf kernel; Matern kernel has more plausible smoothness. - ## Matern 3/2 PSD is - #(18 * jnp.sqrt(3)* jnp.pi * sigma**2)/((4 * k^2 * jnp.pi**2 + 3/(rho**2))^(5/2) rho^3) + noise = noise.at[..., 0].set(jnp.abs(noise[..., 0])) + # TODO: This is the rbf kernel; Matern kernel has more plausible smoothness. + # Matern 3/2 PSD is + # (18 * jnp.sqrt(3)* jnp.pi * sigma**2)/((4 * k^2 * jnp.pi**2 + 3/(rho**2))^(5/2) rho^3) field = jnp.fft.irfft2(noise * gain, (xdim, ydim), norm="forward") return field diff --git a/pdebench/data_gen/src/data_io.py b/pdebench/data_gen/src/data_io.py index 64ea494..f3da0da 100644 --- a/pdebench/data_gen/src/data_io.py +++ b/pdebench/data_gen/src/data_io.py @@ -1,47 +1,39 @@ -import os, os.path -import subprocess +from __future__ import annotations + import json import logging +import os +import os.path +import subprocess +import h5py +import numpy as np +from omegaconf import OmegaConf +from phi.field import Field from phi.flow import * -from phi.field import Field from phi.math import Shape -import numpy as np -import h5py - -from omegaconf import DictConfig, OmegaConf - - log = logging.getLogger(__name__) -def dims_for( - n_steps=1000, - grid_size=(100, 100), - frame_int = 1, - n_batch = 1, - **kwargs): +def dims_for(n_steps=1000, grid_size=(100, 100), frame_int=1, n_batch=1, **kwargs): """ return a dict of fields and their shapes """ - n_frames = ((n_steps-1)//frame_int) + 1 + n_frames = ((n_steps - 1) // frame_int) + 1 return dict( - velocity = (n_batch, n_frames, *grid_size, len(grid_size)), - particles= (n_batch, n_frames, *grid_size, 1), - force= (n_batch, *grid_size, len(grid_size)), - t= (n_batch, n_frames), + velocity=(n_batch, n_frames, *grid_size, len(grid_size)), + particles=(n_batch, n_frames, *grid_size, 1), + force=(n_batch, *grid_size, len(grid_size)), + t=(n_batch, n_frames), ) def dict_for(config): spec = dims_for(**config) - data_store = dict( - latest_index = -1, - config = config - ) + data_store = dict(latest_index=-1, config=config) for field_name, full_shape in spec.items(): - data_store[field_name] = np.ndarray(full_shape, dtype='float32') + data_store[field_name] = np.ndarray(full_shape, dtype="float32") return data_store @@ -50,48 +42,49 @@ def h5_for(config): spec = dims_for(**config) log.info(f"spec: {spec}") fname = f"{config['sim_name']}-{config['seed']}.h5" - data_store = h5py.File(fname, 'a') - data_store.attrs['config'] = OmegaConf.to_yaml(config) - data_store.attrs['latestIndex'] = -1 + data_store = h5py.File(fname, "a") + data_store.attrs["config"] = OmegaConf.to_yaml(config) + data_store.attrs["latestIndex"] = -1 for field_name, full_shape in spec.items(): # dataset shape is (batch, t_length, x1, ..., xd, v) - chunk_shape = (1, 1, *full_shape[2:]) # chunk shape in (1, 1, x1, ..., xd, v) + chunk_shape = (1, 1, *full_shape[2:]) # chunk shape in (1, 1, x1, ..., xd, v) # Open a dataset, creating it if it doesn’t exist. - data_store.require_dataset( + data_store.require_dataset( field_name, full_shape, - 'float32', + "float32", compression="lzf", chunks=chunk_shape, - shuffle=True) + shuffle=True, + ) return data_store def to_centre_grid(field: Field) -> CenteredGrid: - ''' + """ resample the input `Field` and return a corresponding `CenterGrid` used because the `StaggeredGrid`, which is usually the Object for velocity, does pack into nice tensors for typical neural nets - ''' + """ if isinstance(field, CenteredGrid): return field return CenteredGrid(field, resolution=field.shape.spatial, bounds=field.bounds) def _get_dim_order(shape: Shape): - ''' + """ Return a tuple of string, represents the order of dimensions e.g. ('batch','x','y','vector') If the current Shape does not have channel dims, fill in "vector" as 1. - ''' - batchNames = shape.batch.names if (shape.batch_rank > 0) else ('batch',) - channelNames = shape.channel.names if (shape.channel_rank > 0) else ('vector',) + """ + batchNames = shape.batch.names if (shape.batch_rank > 0) else ("batch",) + channelNames = shape.channel.names if (shape.channel_rank > 0) else ("vector",) return batchNames + shape.spatial.names + channelNames def to_ndarray(field: Field) -> np.ndarray: - ''' + """ Turn the current Field into ndarray, with shape (batch, x1, ..., xd, v) - ''' + """ centered = to_centre_grid(field) order = _get_dim_order(centered.shape) ndarray = centered.values.numpy(order=order) @@ -99,30 +92,33 @@ def to_ndarray(field: Field) -> np.ndarray: def dataverse_upload( - file_path, - dataverse_url, - dataverse_token, - dataverse_id, - dataverse_dir=None, - retry=10): - ''' + file_path, + dataverse_url, + dataverse_token, + dataverse_id, + dataverse_dir=None, + retry=10, +): + """ Upload a file to dataverse - ''' - darus_struct = { - "description":"", - "categories":["Data"], - "restrict": "false" - } + """ + darus_struct = {"description": "", "categories": ["Data"], "restrict": "false"} if dataverse_dir is not None: darus_struct["directoryLabel"] = f"{dataverse_dir}/" cmd = [ "curl", - "-X", "POST", - "-H", f"X-Dataverse-key:{dataverse_token}", - "-F", f"file=@{file_path}", - "-F", 'jsonData='+json.dumps(darus_struct), + "-X", + "POST", + "-H", + f"X-Dataverse-key:{dataverse_token}", + "-F", + f"file=@{file_path}", + "-F", + "jsonData=" + json.dumps(darus_struct), f"{dataverse_url}/api/datasets/:persistentId/add?persistentId={dataverse_id}", - "--retry", str(retry)] + "--retry", + str(retry), + ] log.info(f"upload cmd {cmd}") subprocess.Popen(cmd) log.info(f"upload cmd {os.getcwd()}$ {' '.join(cmd)}") diff --git a/pdebench/data_gen/src/plots.py b/pdebench/data_gen/src/plots.py index 29b95c5..cba9a74 100644 --- a/pdebench/data_gen/src/plots.py +++ b/pdebench/data_gen/src/plots.py @@ -2,11 +2,12 @@ Author : John Kim, Simon Brown, Timothy Praditia PDE Simulation packages """ +from __future__ import annotations + +import imageio import matplotlib.pyplot as plt import numpy as np -import imageio import phi.vis as phivis -import os def plot_data(data, t, dim, channel, t_fraction, config, filename): @@ -37,7 +38,7 @@ def plot_data(data, t, dim, channel, t_fraction, config, filename): plt.savefig(filename) -def save_phi_plot(result, title, filepath, bbox_inches='tight', pad_inches=0): +def save_phi_plot(result, title, filepath, bbox_inches="tight", pad_inches=0): """ save one custom figure from an array """ @@ -47,25 +48,41 @@ def save_phi_plot(result, title, filepath, bbox_inches='tight', pad_inches=0): plt.close() -def phi_plots(results, T_results, title, filepath, scale = 1, bbox_inches='tight', pad_inches=0): +def phi_plots( + results, T_results, title, filepath, scale=1, bbox_inches="tight", pad_inches=0 +): """ Save simulation custom figures, get images list """ images = [] upperfilepath = filepath for i, arr in enumerate(T_results): - filename = '{}.png'.format(title) - if upperfilepath == '': + filename = f"{title}.png" + if upperfilepath == "": filepath = filename else: - filepath = upperfilepath + '/{}'.format(filename) + filepath = upperfilepath + f"/{filename}" save_phi_plot( - scale * results[i], title, filepath, bbox_inches=bbox_inches, pad_inches=pad_inches) + scale * results[i], + title, + filepath, + bbox_inches=bbox_inches, + pad_inches=pad_inches, + ) images.append(imageio.imread(filepath)) return images -def save_sim_figures(results, T_results, simulation_name, kinematic_value, filepath, scale = 1, bbox_inches='tight', pad_inches=0): +def save_sim_figures( + results, + T_results, + simulation_name, + kinematic_value, + filepath, + scale=1, + bbox_inches="tight", + pad_inches=0, +): """ save figures, get images list """ @@ -73,14 +90,15 @@ def save_sim_figures(results, T_results, simulation_name, kinematic_value, filep upperfilepath = filepath for i, arr in enumerate(T_results): res = arr[0] - title = '{}_{}_t={}'.format(simulation_name, kinematic_value, round(T_results[i], 2)) - filename = '{}.png'.format(title) - if upperfilepath == '': + title = f"{simulation_name}_{kinematic_value}_t={round(T_results[i], 2)}" + filename = f"{title}.png" + if upperfilepath == "": filepath = filename else: - filepath = upperfilepath + '/{}'.format(filename) + filepath = upperfilepath + f"/{filename}" save_phi_plot( - scale * res, title, filepath, bbox_inches=bbox_inches, pad_inches=pad_inches) + scale * res, title, filepath, bbox_inches=bbox_inches, pad_inches=pad_inches + ) images.append(imageio.imread(filepath)) return images diff --git a/pdebench/data_gen/src/pytorch_dataset.py b/pdebench/data_gen/src/pytorch_dataset.py index 4cd2c6b..942523a 100644 --- a/pdebench/data_gen/src/pytorch_dataset.py +++ b/pdebench/data_gen/src/pytorch_dataset.py @@ -1,7 +1,10 @@ -import h5py -from torch.utils.data import Dataset, DataLoader +from __future__ import annotations + from pathlib import Path + +import h5py from pytorch_lightning import LightningDataModule +from torch.utils.data import DataLoader, Dataset class HDF5Dataset(Dataset): @@ -14,7 +17,7 @@ def __init__(self, dir_path, transform=None): super().__init__() path = Path(dir_path) assert path.is_dir() - files_path = list(path.glob('*.h5')) # all .h5 files' path + files_path = list(path.glob("*.h5")) # all .h5 files' path assert len(files_path) > 0 self.data_info = {} @@ -25,7 +28,7 @@ def __init__(self, dir_path, transform=None): for files_path in files_path: with h5py.File(str(files_path.resolve())) as f: - config = f.attrs.get('config') + config = f.attrs.get("config") for ds_name, ds in f.items(): self.names.append(ds_name) b = ds.shape[0] @@ -60,6 +63,7 @@ def _load_data(self, idx): # PATH_DATASETS = 'dummy_dataset' + class HDF5DatasetLightning(LightningDataModule): def __init__(self, data_dir: str, batch_size: int = 64, transforms=None): super().__init__() @@ -78,7 +82,7 @@ def train_dataloader(self): if __name__ == "__main__": - dir_path = 'download_dataset' # random_force_field--ns_sim--10.h5 in this directory + dir_path = "download_dataset" # random_force_field--ns_sim--10.h5 in this directory # test pytorch dataset dataset = HDF5Dataset(dir_path=dir_path, transform=None) @@ -86,8 +90,8 @@ def train_dataloader(self): dataloader = DataLoader(dataset, batch_size=64, shuffle=True) data, config = next(iter(dataloader)) for i, d in enumerate(data): - print(f'{names[i].upper()} batched data shape: ', d.size()) - print('number of config files: ', len(config)) + print(f"{names[i].upper()} batched data shape: ", d.size()) + print("number of config files: ", len(config)) # test pytorch lightning dataset lightning_dataset = HDF5DatasetLightning(dir_path, batch_size=64, transforms=None) @@ -95,5 +99,5 @@ def train_dataloader(self): lightning_dataloader = lightning_dataset.train_dataloader() data, config = next(iter(lightning_dataloader)) for i, d in enumerate(data): - print(f'{names[i].upper()} batched data shape: ', d.size()) - print('number of config files: ', len(config)) + print(f"{names[i].upper()} batched data shape: ", d.size()) + print("number of config files: ", len(config)) diff --git a/pdebench/data_gen/src/sim_diff_react.py b/pdebench/data_gen/src/sim_diff_react.py index 821e7cb..027eb53 100644 --- a/pdebench/data_gen/src/sim_diff_react.py +++ b/pdebench/data_gen/src/sim_diff_react.py @@ -1,25 +1,29 @@ +from __future__ import annotations + +import logging + import numpy as np from scipy.integrate import solve_ivp from scipy.sparse import diags -import logging + class Simulator: - - def __init__(self, - Du: float = 1E-3, - Dv: float = 5E-3, - k: float = 5E-3, - t: float = 50, - tdim: int = 501, - x_left: float = -1.0, - x_right: float = 1.0, - xdim: int = 50, - y_bottom: float = -1.0, - y_top: float = 1.0, - ydim: int = 50, - n: int = 1, - seed: int = 0): - + def __init__( + self, + Du: float = 1e-3, + Dv: float = 5e-3, + k: float = 5e-3, + t: float = 50, + tdim: int = 501, + x_left: float = -1.0, + x_right: float = 1.0, + xdim: int = 50, + y_bottom: float = -1.0, + y_top: float = 1.0, + ydim: int = 50, + n: int = 1, + seed: int = 0, + ): """ Constructor method initializing the parameters for the diffusion sorption problem. @@ -47,70 +51,72 @@ def __init__(self, self.X1 = x_right self.Y0 = y_bottom self.Y1 = y_top - + self.Nx = xdim self.Ny = ydim self.Nt = tdim - - # Calculate grid size and generate grid - self.dx = (self.X1 - self.X0)/(self.Nx) - self.dy = (self.Y1 - self.Y0)/(self.Ny) - - self.x = np.linspace(self.X0 + self.dx/2, self.X1 - self.dx/2, self.Nx) - self.y = np.linspace(self.Y0 + self.dy/2, self.Y1 - self.dy/2, self.Ny) - + + # Calculate grid size and generate grid + self.dx = (self.X1 - self.X0) / (self.Nx) + self.dy = (self.Y1 - self.Y0) / (self.Ny) + + self.x = np.linspace(self.X0 + self.dx / 2, self.X1 - self.dx / 2, self.Nx) + self.y = np.linspace(self.Y0 + self.dy / 2, self.Y1 - self.dy / 2, self.Ny) + # Time steps to store the simulation results self.t = np.linspace(0, self.T, self.Nt) - + # Initialize the logger self.log = logging.getLogger(__name__) - + self.seed = seed - + def generate_sample(self): """ Single sample generation using the parameters of this simulator. :return: The generated sample as numpy array(t, x, y, num_features) """ - + np.random.seed(self.seed) - - u0 = np.random.randn(self.Nx*self.Ny) - v0 = np.random.randn(self.Nx*self.Ny) - - u0 = u0.reshape(self.Nx*self.Ny) - v0 = v0.reshape(self.Nx*self.Ny) - u0 = np.concatenate((u0,v0)) - + + u0 = np.random.randn(self.Nx * self.Ny) + v0 = np.random.randn(self.Nx * self.Ny) + + u0 = u0.reshape(self.Nx * self.Ny) + v0 = v0.reshape(self.Nx * self.Ny) + u0 = np.concatenate((u0, v0)) + # # Normalize u0 # u0 = 2 * (u0 - u0.min()) / (u0.max() - u0.min()) - 1 # Generate arrays as diagonal inputs to the Laplacian matrix - main_diag = -2*np.ones(self.Nx)/self.dx**2 -2*np.ones(self.Nx)/self.dy**2 - main_diag[0] = -1/self.dx**2 -2/self.dy**2 - main_diag[-1] = -1/self.dx**2 -2/self.dy**2 + main_diag = ( + -2 * np.ones(self.Nx) / self.dx**2 - 2 * np.ones(self.Nx) / self.dy**2 + ) + main_diag[0] = -1 / self.dx**2 - 2 / self.dy**2 + main_diag[-1] = -1 / self.dx**2 - 2 / self.dy**2 main_diag = np.tile(main_diag, self.Ny) - main_diag[:self.Nx] = -2/self.dx**2 -1/self.dy**2 - main_diag[self.Nx*(self.Ny-1):] = -2/self.dx**2 -1/self.dy**2 - main_diag[0] = -1/self.dx**2 -1/self.dy**2 - main_diag[self.Nx-1] = -1/self.dx**2 -1/self.dy**2 - main_diag[self.Nx*(self.Ny-1)] = -1/self.dx**2 -1/self.dy**2 - main_diag[-1] = -1/self.dx**2 -1/self.dy**2 - + main_diag[: self.Nx] = -2 / self.dx**2 - 1 / self.dy**2 + main_diag[self.Nx * (self.Ny - 1) :] = -2 / self.dx**2 - 1 / self.dy**2 + main_diag[0] = -1 / self.dx**2 - 1 / self.dy**2 + main_diag[self.Nx - 1] = -1 / self.dx**2 - 1 / self.dy**2 + main_diag[self.Nx * (self.Ny - 1)] = -1 / self.dx**2 - 1 / self.dy**2 + main_diag[-1] = -1 / self.dx**2 - 1 / self.dy**2 + left_diag = np.ones(self.Nx) left_diag[0] = 0 left_diag = np.tile(left_diag, self.Ny) - left_diag = left_diag[1:]/self.dx**2 - + left_diag = left_diag[1:] / self.dx**2 + right_diag = np.ones(self.Nx) right_diag[-1] = 0 right_diag = np.tile(right_diag, self.Ny) - right_diag = right_diag[:-1]/self.dx**2 - - bottom_diag = np.ones(self.Nx*(self.Ny-1))/self.dy**2 - - top_diag = np.ones(self.Nx*(self.Ny-1))/self.dy**2 - + right_diag = right_diag[:-1] / self.dx**2 + + bottom_diag = np.ones(self.Nx * (self.Ny - 1)) / self.dy**2 + + top_diag = np.ones(self.Nx * (self.Ny - 1)) / self.dy**2 + # Generate the sparse Laplacian matrix diagonals = [main_diag, left_diag, right_diag, bottom_diag, top_diag] offsets = [0, -1, 1, -self.Nx, self.Nx] @@ -120,10 +126,14 @@ def generate_sample(self): prob = solve_ivp(self.rc_ode, (0, self.T), u0, t_eval=self.t) ode_data = prob.y - sample_u = np.transpose(ode_data[:self.Nx*self.Ny]).reshape(-1,self.Ny,self.Nx) - sample_v = np.transpose(ode_data[self.Nx*self.Ny:]).reshape(-1,self.Ny,self.Nx) + sample_u = np.transpose(ode_data[: self.Nx * self.Ny]).reshape( + -1, self.Ny, self.Nx + ) + sample_v = np.transpose(ode_data[self.Nx * self.Ny :]).reshape( + -1, self.Ny, self.Nx + ) - return np.stack((sample_u, sample_v),axis=-1) + return np.stack((sample_u, sample_v), axis=-1) def rc_ode(self, t, y): """ @@ -132,23 +142,23 @@ def rc_ode(self, t, y): :param y: The equation values to solve :return: A finite volume solution """ - + # Separate y into u and v - u = y[:self.Nx*self.Ny] - v = y[self.Nx*self.Ny:] - + u = y[: self.Nx * self.Ny] + v = y[self.Nx * self.Ny :] + # Calculate reaction function for each unknown react_u = u - u**3 - self.k - v react_v = u - v - + # Calculate time derivative for each unknown u_t = react_u + self.Du * (self.lap @ u) v_t = react_v + self.Dv * (self.lap @ v) - + # Stack the time derivative into a single array y_t - y_t = np.concatenate((u_t,v_t)) - + y_t = np.concatenate((u_t, v_t)) + # Log the simulation progress # self.log.info('t = ' + str(t)) - + return y_t diff --git a/pdebench/data_gen/src/sim_diff_sorp.py b/pdebench/data_gen/src/sim_diff_sorp.py index 7e84aaa..4160400 100644 --- a/pdebench/data_gen/src/sim_diff_sorp.py +++ b/pdebench/data_gen/src/sim_diff_sorp.py @@ -1,25 +1,28 @@ +from __future__ import annotations + +import logging + import numpy as np from scipy.integrate import solve_ivp from scipy.sparse import diags -import logging + class Simulator: - - def __init__(self, - D: float = 5E-4, - por: float = 0.29, - rho_s: float = 2880, - k_f: float = 3.5E-4, - n_f: float = 0.874, - sol: float = 1.0, - t: float = 2500, - tdim: int = 501, - x_left: float = 0.0, - x_right: float = 1.0, - xdim: int = 50, - n: int = 1, - seed: int = 0): - + def __init__( + self, + D: float = 5e-4, + por: float = 0.29, + rho_s: float = 2880, + k_f: float = 3.5e-4, + n_f: float = 0.874, + sol: float = 1.0, + t: float = 2500, + tdim: int = 501, + x_left: float = 0.0, + x_right: float = 1.0, + xdim: int = 50, + seed: int = 0, + ): """ Constructor method initializing the parameters for the diffusion sorption problem. @@ -34,7 +37,6 @@ def __init__(self, :param x_left: Left end of the 2D simulation field :param x_right: Right end of the 2D simulation field :param xdim: Number of spatial steps between x_left and x_right - :param n: Number of batches """ # Set class parameters @@ -48,82 +50,73 @@ def __init__(self, self.T = t self.X0 = x_left self.X1 = x_right - + self.Nx = xdim self.Nt = tdim - - # Calculate grid size and generate grid - self.dx = (self.X1 - self.X0)/(self.Nx) - self.x = np.linspace(self.X0 + self.dx/2, self.X1 - self.dx/2, self.Nx) - + + # Calculate grid size and generate grid + self.dx = (self.X1 - self.X0) / (self.Nx) + self.x = np.linspace(self.X0 + self.dx / 2, self.X1 - self.dx / 2, self.Nx) + # Time steps to store the simulation results self.t = np.linspace(0, self.T, self.Nt) - + # Initialize the logger self.log = logging.getLogger(__name__) - + self.seed = seed - - def generate_sample(self): + + def generate_sample(self) -> np.ndarray: """ Single sample generation using the parameters of this simulator. :return: The generated sample as numpy array(t, x, y, num_features) """ - np.random.seed(self.seed) - + generator = np.random.default_rng(self.seed) # Generate initial condition - u0 = np.ones(self.Nx) * np.random.uniform(0,0.2) + u0 = np.ones(self.Nx) * generator.uniform(0, 0.2) # Generate arrays as diagonal inputs to the Laplacian matrix - main_diag = -2*np.ones(self.Nx)/self.dx**2 - - left_diag = np.ones(self.Nx-1)/self.dx**2 - - right_diag = np.ones(self.Nx-1)/self.dx**2 - + main_diag = -2 * np.ones(self.Nx) / self.dx**2 + + left_diag = np.ones(self.Nx - 1) / self.dx**2 + + right_diag = np.ones(self.Nx - 1) / self.dx**2 + # Generate the sparse Laplacian matrix diagonals = [main_diag, left_diag, right_diag] offsets = [0, -1, 1] self.lap = diags(diagonals, offsets) - + # Initialize the right hand side to account for the boundary condition self.rhs = np.zeros(self.Nx) # Solve the diffusion reaction problem prob = solve_ivp(self.rc_ode, (0, self.T), u0, t_eval=self.t) ode_data = prob.y - + sample_c = np.transpose(ode_data) - + return np.expand_dims(sample_c, axis=-1) - def rc_ode(self, t, y): + def rc_ode(self, t: float, y): """ Solves a given equation for a particular time step. :param t: The current time step :param y: The equation values to solve :return: A finite volume solution """ - - c = y - + # Define left and right boundary conditions left_BC = self.sol - right_BC = (c[-2]-c[-1])/self.dx * self.D - + right_BC = (y[-2] - y[-1]) / self.dx * self.D + # Calculate the Freundlich retardation factor - retardation = 1 + ((1 - self.por)/self.por)*self.rho_s\ - *self.k_f*self.n_f*(c + 1e-6)**(self.n_f-1) - + retardation = 1 + ( + (1 - self.por) / self.por + ) * self.rho_s * self.k_f * self.n_f * (y + 1e-6) ** (self.n_f - 1) + # Calculate the right hand side - self.rhs[0] = self.D/retardation[0]/(self.dx**2)*left_BC - self.rhs[-1] = self.D/retardation[-1]/(self.dx**2)*right_BC - - # Calculate time derivative - c_t = self.D/retardation * (self.lap @ c) + self.rhs - y_t = c_t - - # Log the simulation progress - # self.log.info('t = ' + str(t)) - - return y_t + self.rhs[0] = self.D / retardation[0] / (self.dx**2) * left_BC + self.rhs[-1] = self.D / retardation[-1] / (self.dx**2) * right_BC + + return self.D / retardation * (self.lap @ y) + self.rhs diff --git a/pdebench/data_gen/src/sim_ns_incomp_2d.py b/pdebench/data_gen/src/sim_ns_incomp_2d.py index 0b3f82c..79509f9 100644 --- a/pdebench/data_gen/src/sim_ns_incomp_2d.py +++ b/pdebench/data_gen/src/sim_ns_incomp_2d.py @@ -2,83 +2,120 @@ Author : John Kim, Ran Zhang, Dan MacKinlay PDE Simulation packages """ -from contextlib import nullcontext -from typing import Optional +from __future__ import annotations + import logging import os +from contextlib import nullcontext +from typing import Optional import imageio import numpy as np -from tqdm import tqdm -import hydra from pdebench.data_gen.src import data_io -from pdebench.data_gen.src import image_processor +from tqdm import tqdm -log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, filename=__name__) +logging.root.setLevel(logging.INFO) # import wandb -def call_many(fn_list, *args, **kwargs): + +def call_many(fn_list, *args, **kwargs) -> list[callable]: """ Surely there is already a helper function for this somewhere. inverse map? """ return [fn(*args, **kwargs) for fn in fn_list] - + def ns_sim( - seed: int, - label: Optional[str]=None, - sim_name: str='ns_sim_2d', - particle_extrapolation:str='BOUNDARY', - velocity_extrapolation:str='ZERO', - NU: float=0.01, - scale: float= 10.0, - smoothness: float=3.0, - grid_size=(100,100), - enable_gravity: bool=False, - enable_obstacles: bool=False, - force_extrapolation: str='ZERO', - save_images: bool=True, - save_gif: bool=True, - save_h5:bool=True, - n_steps: int=10, - DT: float=0.01, - frame_int: int=1, - n_batch=1, - backend='jax', - device='GPU', - jit=True, - profile: bool=False, - upload: bool=False, - exec_dir: Optional[str]=None, - artefact_dir: Optional[str] = None, #hackish way of writing artefacts to a good location without fighting hydra's conf interpolation - dataverse: Optional[dict] = None, - config={}, - ): + seed: int, + label: Optional[str] = None, + sim_name: str = "ns_sim_2d", + particle_extrapolation: str = "BOUNDARY", + velocity_extrapolation: str = "ZERO", + NU: float = 0.01, + scale: float = 10.0, + smoothness: float = 3.0, + grid_size=(100, 100), + enable_gravity: bool = False, + enable_obstacles: bool = False, + force_extrapolation: str = "ZERO", + save_images: bool = True, + save_gif: bool = True, + save_h5: bool = True, + n_steps: int = 10, + DT: float = 0.01, + frame_int: int = 1, + n_batch: int = 1, + backend: str = "jax", + device: str = "GPU", + jit: bool = True, + profile: bool = False, + upload: bool = False, + dataverse: Optional[dict] = None, + config: dict | None = None, +): """ Run the actual simulation. """ - # log.info(f"exec_dir {exec_dir}") - # log.info(f"orig_dir {hydra.utils.get_original_cwd()}") - # log.info(f"artefact_dir {artefact_dir}") - # log.info(f"WORKING_DIR {os.getenv('WORKING_DIR')}") - # log.info(f"cwd {os.getcwd()}") - - if backend == 'jax': - from phi.jax.flow import extrapolation, Box, Obstacle, advect, diffuse, fluid, math, Solve, CenteredGrid, StaggeredGrid, Noise, batch - elif backend == 'pytorch': - from phi.torch.flow import extrapolation, Box, Obstacle, advect, diffuse, fluid, math, Solve, CenteredGrid, StaggeredGrid, Noise, batch + if config is None: + config = {} + + if backend == "jax": + from phi.jax.flow import ( + Box, + CenteredGrid, + Noise, + Obstacle, + Solve, + StaggeredGrid, + advect, + batch, + diffuse, + extrapolation, + fluid, + math, + ) + elif backend == "pytorch": from phi.torch import TORCH + from phi.torch.flow import ( + Box, + CenteredGrid, + Noise, + Obstacle, + Solve, + StaggeredGrid, + advect, + batch, + diffuse, + extrapolation, + fluid, + math, + ) + TORCH.set_default_device(device) else: - from phi.flow import extrapolation, Box, Obstacle, advect, diffuse, fluid, math, Solve, CenteredGrid, StaggeredGrid, Noise, batch + from phi.flow import ( + Box, + CenteredGrid, + Noise, + Obstacle, + Solve, + StaggeredGrid, + advect, + batch, + diffuse, + extrapolation, + fluid, + math, + ) # from torch.profiler import profile, record_function, ProfilerActivity - def bounds_select(x, y): + def bounds_select(x: int | None = None, y: int | None = None) -> Box: """ This function generates a 2D Phiflow Box with scale from zero to the number indicated by the bounds or infinite. @@ -89,16 +126,15 @@ def bounds_select(x, y): Returns: Box: A Box type of Phiflow """ - if x == None: + if x is None: return Box[:, 0:y] - elif y == None: + if y is None: return Box[0:x, :] - else: - return Box[0:x, 0:y] - - + return Box[0:x, 0:y] - def cauchy_momentum_step(velocity, particles, body_acceleration, NU, DT, obstacles = None): + def cauchy_momentum_step( + velocity, particles, body_acceleration, NU, DT, obstacles: tuple | None = None + ) -> tuple[fluid.Field, fluid.Field]: """ Navier-Stokes Simulation cauchy_momentum_step returns velocity and particles by solving cauchy momentum equation for one step @@ -112,23 +148,25 @@ def cauchy_momentum_step(velocity, particles, body_acceleration, NU, DT, obstacl **kwargs : Other obstacles (Simulation constraints etc) """ # Set empty obstacle as empty tuple - if obstacles == None: + if obstacles is None: obstacles = () # Computing velocity term first # Cauchy-momentum equation - velocity = advect.semi_lagrangian(velocity, velocity, dt=DT) # advection - velocity = diffuse.explicit(velocity, NU, dt=DT) # diffusion - + velocity = advect.semi_lagrangian(velocity, velocity, dt=DT) # advection + velocity = diffuse.explicit(velocity, NU, dt=DT) # diffusion + # Add external body_acceleration, constraints - velocity += DT * particles * body_acceleration # external body_acceleration - velocity = fluid.apply_boundary_conditions(velocity, obstacles) # obstacles - + velocity += DT * particles * body_acceleration # external body_acceleration + velocity = fluid.apply_boundary_conditions(velocity, obstacles) # obstacles + # Make incompressible - velocity, _ = fluid.make_incompressible(velocity, obstacles, solve=Solve('CG-adaptive', 1e-3, 0, x0=None)) # particles - - # Computing particles term next + velocity, _ = fluid.make_incompressible( + velocity, obstacles, solve=Solve("CG-adaptive", 1e-3, 0, x0=None) + ) # particles + + # Computing particles term next particles = advect.semi_lagrangian(particles, velocity, dt=DT) - + return velocity, particles # Setting the random seed for simulation. This is a global seed for Phiflow. @@ -140,18 +178,16 @@ def cauchy_momentum_step(velocity, particles, body_acceleration, NU, DT, obstacl if save_h5: data_store = data_io.h5_for(config) h5_path = data_store.filename + def _store(frame_i, t, particles, velocity, **kwargs): - data_store['particles'][:, frame_i, ...] = data_io.to_ndarray(particles) - data_store['velocity'][:, frame_i, ...] = data_io.to_ndarray(velocity) - data_store['t'][:, frame_i] = t - data_store.attrs['latestIndex'] = frame_i - callbacks.append( - _store - ) - cleanups.append( - lambda *args, **kwargs: data_store.close() - ) - ## Move output to artefacts dir here + data_store["particles"][:, frame_i, ...] = data_io.to_ndarray(particles) + data_store["velocity"][:, frame_i, ...] = data_io.to_ndarray(velocity) + data_store["t"][:, frame_i] = t + data_store.attrs["latestIndex"] = frame_i + + callbacks.append(_store) + cleanups.append(lambda *args, **kwargs: data_store.close()) + # Move output to artefacts dir here # if artefact_dir is not None: # cleanups.append( # lambda *args, **kwargs: data_store.close() @@ -162,30 +198,30 @@ def _store(frame_i, t, particles, velocity, **kwargs): lambda *args, **kwargs: data_io.dataverse_upload( file_path=h5_path, dataverse_url=os.getenv( - 'DATAVERSE_URL', 'https://darus.uni-stuttgart.de'), - dataverse_token=os.getenv( - 'DATAVERSE_API_TOKEN', ''), + "DATAVERSE_URL", "https://darus.uni-stuttgart.de" + ), + dataverse_token=os.getenv("DATAVERSE_API_TOKEN", ""), dataverse_dir=label, - dataverse_id=dataverse['dataset_id'], + dataverse_id=dataverse["dataset_id"], ) ) else: data_store = data_io.dict_for(config) + def _store(frame_i, t, particles, velocity, **kwargs): - data_store['particles'][:, frame_i, ...] = data_io.to_ndarray(particles) - data_store['velocity'][:, frame_i, ...] = data_io.to_ndarray(velocity) - data_store['t'][:, frame_i] = t - callbacks.append( - _store - ) + data_store["particles"][:, frame_i, ...] = data_io.to_ndarray(particles) + data_store["velocity"][:, frame_i, ...] = data_io.to_ndarray(velocity) + data_store["t"][:, frame_i] = t + + callbacks.append(_store) if save_images: + def _save_img(frame_i, t, particles, velocity, **kwargs): particles_images.append() velocity_images.append() - callbacks.append( - _save_img - ) + + callbacks.append(_save_img) profile_ctx = nullcontext() if profile: @@ -194,8 +230,7 @@ def _save_img(frame_i, t, particles, velocity, **kwargs): with profile_ctx as prof: # Initialization of the particles (i.e. density of the flow) grid with a Phiflow Noise() method particles = CenteredGrid( - Noise(batch(batch=n_batch), - scale=scale, smoothness=smoothness), + Noise(batch(batch=n_batch), scale=scale, smoothness=smoothness), extrapolation=getattr(extrapolation, particle_extrapolation), x=grid_size[0], y=grid_size[1], @@ -213,17 +248,17 @@ def _save_img(frame_i, t, particles, velocity, **kwargs): # Initialization of the force grid. The force is also a staggered grid with a Phiflow Noise() method or using gravity if enable_gravity: - force = math.tensor(batch(batch=n_batch),[0, -9.81]) + force = math.tensor(batch(batch=n_batch), [0, -9.81]) else: force = StaggeredGrid( - Noise(batch(batch=n_batch),vector=2), + Noise(batch(batch=n_batch), vector=2), extrapolation=getattr(extrapolation, force_extrapolation), x=grid_size[0], y=grid_size[1], bounds=bounds_select(*grid_size), ) - data_store['force'][:,...] = data_io.to_ndarray(force) + data_store["force"][:, ...] = data_io.to_ndarray(force) # Set the obstacles. Obstacles are not enabled by default. obstacles = [] @@ -235,44 +270,53 @@ def _save_img(frame_i, t, particles, velocity, **kwargs): # Use "python gen_ns_incomp.py save_gif=false" for not saving .gif animation and only save to pictures call_many( - callbacks, frame_i=0, t=0.0, - velocity=velocity, particles=particles, prof=prof) + callbacks, + frame_i=0, + t=0.0, + velocity=velocity, + particles=particles, + prof=prof, + ) - def sim_step(velocity, particles): - return cauchy_momentum_step( - velocity, particles, force, NU, DT, obstacles) + def sim_step(velocity, particles) -> tuple[fluid.Field, fluid.Field]: + return cauchy_momentum_step(velocity, particles, force, NU, DT, obstacles) if jit: sim_step = math.jit_compile(sim_step) - ts = np.linspace(0, n_steps*DT, n_steps, endpoint=False) - n_steps_actual = ((n_steps-1)//frame_int) * frame_int + 1 + ts = np.linspace(0, n_steps * DT, n_steps, endpoint=False) + n_steps_actual = ((n_steps - 1) // frame_int) * frame_int + 1 ts = ts[1:n_steps_actual] - # log.info("ts: {}".format(ts)) for step, t in enumerate(tqdm(ts), start=1): velocity, particles = sim_step( - velocity, particles,) - + velocity, + particles, + ) + if step % frame_int == 0: - frame_i = step//frame_int - log.info(f"step {step} frame_i {frame_i}") + frame_i = step // frame_int + msg = f"step {step} frame_i {frame_i}" + logging.info(msg) call_many( - callbacks, frame_i=frame_i, t=t, - velocity=velocity, particles=particles, prof=prof) + callbacks, + frame_i=frame_i, + t=t, + velocity=velocity, + particles=particles, + prof=prof, + ) if save_images and save_gif: # Saving images into .gif animation imageio.mimsave( - "{}_velocity.gif".format(sim_name), + f"{sim_name}_velocity.gif", velocity_images, duration=DT, ) imageio.mimsave( - "{}_particles.gif".format(sim_name), + f"{sim_name}_particles.gif", particles_images, duration=DT, ) call_many(cleanups) - - diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index 7d92550..bdbc949 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -1,15 +1,15 @@ -from abc import abstractmethod -from abc import ABC +from __future__ import annotations +import logging import os -import sys -import time +from abc import ABC, abstractmethod -import h5py import numpy as np import torch -from clawpack import riemann -from clawpack import pyclaw +from clawpack import pyclaw, riemann + +logging.basicConfig(level=logging.INFO, filename=__name__) +logging.root.setLevel(logging.INFO) class Basic2DScenario(ABC): @@ -66,7 +66,7 @@ def __get_hu(self): def __get_hv(self): return self.claw_state.q[self.momentumId_y, :].tolist() - def register_state_getters(self): + def register_state_getters(self) -> None: self.state_getters = { "h": self.__get_h, "u": self.__get_u, @@ -75,11 +75,11 @@ def register_state_getters(self): "hv": self.__get_hv, } - def add_save_state(self): + def add_save_state(self) -> None: for key, getter in self.state_getters.items(): self.save_state[key].append(getter()) - def init_save_state(self, T, tsteps): + def init_save_state(self, T: float, tsteps: int) -> None: self.save_state = {} self.save_state["x"] = self.domain.grid.x.centers.tolist() self.save_state["y"] = self.domain.grid.y.centers.tolist() @@ -87,7 +87,7 @@ def init_save_state(self, T, tsteps): for key, getter in self.state_getters.items(): self.save_state[key] = [getter()] - def save_state_to_disk(self, data_f, seed_str): + def save_state_to_disk(self, data_f, seed_str) -> None: T = np.asarray(self.save_state["t"]) X = np.asarray(self.save_state["x"]) Y = np.asarray(self.save_state["y"]) @@ -98,29 +98,35 @@ def save_state_to_disk(self, data_f, seed_str): data_f.create_dataset(f"{seed_str}/grid/y", data=Y, dtype="f") data_f.create_dataset(f"{seed_str}/grid/t", data=T, dtype="f") - def simulate(self, t): + def simulate(self, t) -> None: if all(v is not None for v in [self.domain, self.claw_state, self.solver]): self.solver.evolve_to_time(self.solution, t) else: - print("Simulate failed: No scenario defined.") + msg = "Simulate failed: No scenario defined." + logging.info(msg) - def run(self, T=1.0, tsteps=20, plot=False): + def run(self, T: float = 1.0, tsteps: int = 20) -> None: self.init_save_state(T, tsteps) self.solution = pyclaw.Solution(self.claw_state, self.domain) - dt = T / tsteps - start = time.time() + dt = T / float(tsteps) + for tstep in range(1, tsteps + 1): t = tstep * dt - # print("Simulating timestep {}/{} at t={:f}".format(tstep, tsteps, t)) self.simulate(t) self.add_save_state() - # print("Simulation took: {}".format(time.time() - start)) class RadialDamBreak2D(Basic2DScenario): name = "RadialDamBreak" - def __init__(self, xdim, ydim, grav=1.0, dam_radius=0.5, inner_height=2.0): + def __init__( + self, + xdim, + ydim, + grav: float = 1.0, + dam_radius: float = 0.5, + inner_height: float = 2.0, + ): self.depthId = 0 self.momentumId_x = 1 self.momentumId_y = 2 @@ -132,7 +138,7 @@ def __init__(self, xdim, ydim, grav=1.0, dam_radius=0.5, inner_height=2.0): super().__init__() # self.state_getters['bathymetry'] = self.__get_bathymetry - def setup_solver(self): + def setup_solver(self) -> None: rs = riemann.shallow_roe_with_efix_2D self.solver = pyclaw.ClawSolver2D(rs) self.solver.limiters = pyclaw.limiters.tvd.MC @@ -143,7 +149,7 @@ def setup_solver(self): self.momentumId_x = 1 self.momentumId_y = 2 - def create_domain(self): + def create_domain(self) -> None: self.xlower = -2.5 self.xupper = 2.5 self.ylower = -2.5 @@ -155,7 +161,7 @@ def create_domain(self): self.domain = pyclaw.Domain([x, y]) self.claw_state = pyclaw.State(self.domain, self.solver.num_eqn) - def set_boundary_conditions(self): + def set_boundary_conditions(self) -> None: """ Sets homogeneous Neumann boundary conditions at each end for q=(u, h*u) and for the bathymetry (auxiliary variable). @@ -165,8 +171,7 @@ def set_boundary_conditions(self): self.solver.bc_lower[1] = pyclaw.BC.extrap self.solver.bc_upper[1] = pyclaw.BC.extrap - @staticmethod - def initial_h(coords): + def initial_h(self, coords): x0 = 0.0 y0 = 0.0 x = coords[:, 0] @@ -177,17 +182,17 @@ def initial_h(coords): return h_in * (r <= self.dam_radius) + h_out * (r > self.dam_radius) @staticmethod - def initial_momentum_x(coords): + def initial_momentum_x() -> torch.Tensor: return torch.tensor(0.0) @staticmethod - def initial_momentum_y(coords): + def initial_momentum_y() -> torch.Tensor: return torch.tensor(0.0) def __get_bathymetry(self): return self.claw_state.aux[0, :].tolist() - def set_initial_conditions(self): + def set_initial_conditions(self) -> None: self.claw_state.problem_data["grav"] = self.grav xc = self.claw_state.grid.x.centers diff --git a/pdebench/data_gen/src/utils.py b/pdebench/data_gen/src/utils.py index 1193d91..8e284bc 100644 --- a/pdebench/data_gen/src/utils.py +++ b/pdebench/data_gen/src/utils.py @@ -1,17 +1,17 @@ -import logging -import warnings -from typing import List, Sequence -import os +from __future__ import annotations + import glob +import os from pprint import pprint from omegaconf import DictConfig, OmegaConf + def expand_path(path, unique=True): """ Resolve a path that may contain variables and user home directory references. """ - return os.path.expandvars(os.path.expanduser(path)) + return os.path.expandvars(os.path.expanduser(path)) def matching_paths(glob_exp): @@ -28,22 +28,25 @@ def resolve_path(path, idx=None, unique=True): if "unique" is True, and there are many matches, panic. Otherwise return the result at index "idx", which could reasonably be 0 or -1; if it is, we sort the list of files """ - matches = matching_paths(path) + matches = matching_paths(path) if idx is None: idx = 0 else: matches = sorted(matches) - + if unique and len(matches) > 1: - raise ValueError("Too many matches for glob: {}".format(path)) + raise ValueError(f"Too many matches for glob: {path}") else: try: return matches[idx] except IndexError: - raise FileNotFoundError("No matches for glob: {}".format(path)) + raise FileNotFoundError(f"No matches for glob: {path}") -def print_config(config: DictConfig, resolve: bool = True,): +def print_config( + config: DictConfig, + resolve: bool = True, +): """ basic pretty-printer for omegaconf configs """ diff --git a/pdebench/data_gen/src/vorticity.py b/pdebench/data_gen/src/vorticity.py new file mode 100644 index 0000000..385e5f3 --- /dev/null +++ b/pdebench/data_gen/src/vorticity.py @@ -0,0 +1,149 @@ +r""" Generate vorticity field :math:`\boldsymbol{\omega} = \nabla \times \boldsymbol{v}` given + velocity field :math:`\boldsymbol{v}` using numerical approximation. + +Assuming the velocitiy field of shape [n, sx, sy, sz, 3] (5D) consists of a trajectory of equidistant cells, +the vorticity field is calculated by using spectral derivatives and the Fast Fourier Transform +such that :math:`\mathcal{F}\{\frac{\partial f}{\partial x}\ = i \omega \mathcal{F}\{f\}}`. + +The code is inspired by +Brunton, S. L.,; Kutz, J. N. (2022). Data-Driven Science and Engineering: Machine Learning, Dynamical Systems, and Control (2nd ed.). +and adapted to operate on 5D data. + +This module provides the functions + - compute_spectral_vorticity_np (numpy) + - compute_spectral_vorticity_jnp (jax.numpy) + +for approximating the vorticity field. +""" +from __future__ import annotations + +import jax +import jax.numpy as jnp +import numpy as np + + +def compute_spectral_vorticity_np( + velocities: np.ndarray, dx: float, dy: float, dz: float +) -> np.ndarray: + r"""Compute vorcitity field of a [n, sx, sy, sz, 3] field, + where n denotes the number of timesteps, sx, sy, sz are the number + of bins in x-, y-, z-direction. + + :param velocities: 5D Velocity field of shape [n, sx, sy, sz, 3]. + :param lx: length x-direction + :type lx: float + :param ly: length y-direction + :type ly: float + :param lz: length z-direction + :type lz: float + :type velocities: np.ndarray + :raises ValueError: If `velocities` is not a 5D array or + of shape [n, sx, sy, sz, 3]. + :return: Vorticity field :math:`\boldsymbol{\omega} \in \mathbb{R}^{n \times s_x \times s_y \times s_z \times 3}` + :rtype: np.ndarray + """ + + if velocities.ndim != 5 or velocities.shape[-1] != 3: + msg = "Expected 5D array of shape [n, sx, sy, sz, 3]!" + raise ValueError(msg) + + dx = abs(dx) + dy = abs(dy) + dz = abs(dz) + + vx = velocities[..., 0] + vy = velocities[..., 1] + vz = velocities[..., 2] + + fxy = np.fft.fft(vx, axis=2) + fyx = np.fft.fft(vy, axis=1) + fxz = np.fft.fft(vx, axis=3) + fzx = np.fft.fft(vz, axis=1) + fyz = np.fft.fft(vy, axis=3) + fzy = np.fft.fft(vz, axis=2) + + kappa_xy = 2.0 * np.pi * np.fft.fftfreq(n=fxy.shape[2], d=dy) + kappa_yx = 2.0 * np.pi * np.fft.fftfreq(n=fyx.shape[1], d=dx) + kappa_xz = 2.0 * np.pi * np.fft.fftfreq(n=fxz.shape[3], d=dz) + kappa_zx = 2.0 * np.pi * np.fft.fftfreq(n=fzx.shape[1], d=dx) + kappa_yz = 2.0 * np.pi * np.fft.fftfreq(n=fyz.shape[3], d=dz) + kappa_zy = 2.0 * np.pi * np.fft.fftfreq(n=fzy.shape[2], d=dy) + + vxy = np.fft.ifft(1j * kappa_xy[None, None, :, None] * fxy, axis=2).real + vyx = np.fft.ifft(1j * kappa_yx[None, :, None, None] * fyx, axis=1).real + vxz = np.fft.ifft(1j * kappa_xz[None, None, None, :] * fxz, axis=3).real + vzx = np.fft.ifft(1j * kappa_zx[None, :, None, None] * fzx, axis=1).real + vyz = np.fft.ifft(1j * kappa_yz[None, None, None, :] * fyz, axis=3).real + vzy = np.fft.ifft(1j * kappa_zy[None, None, :, None] * fzy, axis=2).real + + omega_x = vzy - vyz + omega_y = vxz - vzx + omega_z = vyx - vxy + + return np.concatenate( + [omega_x[..., None], omega_y[..., None], omega_z[..., None]], axis=-1 + ) + + +@jax.jit # type: ignore[misc] +def compute_spectral_vorticity_jnp( + velocities: jnp.ndarray, dx: float, dy: float, dz: float +) -> jnp.ndarray: + r"""Compute vorcitity field of a [n, sx, sy, sz, 3] field, + where n denotes the number of timesteps, sx, sy, sz are the number + of bins in x-, y-, z-direction. In this case computations are performed on GPU + + :param velocities: 5D Velocity field of shape [n, sx, sy, sz, 3]. + :param lx: length x-direction + :type lx: float + :param ly: length y-direction + :type ly: float + :param lz: length z-direction + :type lz: float + :type velocities: np.ndarray + :raises ValueError: If `velocities` is not a 5D array or + of shape [n, sx, sy, sz, 3]. + :return: Vorticity field :math:`\boldsymbol{\omega} \in \mathbb{R}^{n \times s_x \times s_y \times s_z \times 3}` + :rtype: np.ndarray + """ + + if velocities.ndim != 5 or velocities.shape[-1] != 3: + msg = "Expected 5D array of shape [n, sx, sy, sz, 3]!" + raise ValueError(msg) + + dx = abs(dx) + dy = abs(dy) + dz = abs(dz) + + vx = velocities[..., 0] + vy = velocities[..., 1] + vz = velocities[..., 2] + + fxy = jnp.fft.fft(vx, axis=2) + fyx = jnp.fft.fft(vy, axis=1) + fxz = jnp.fft.fft(vx, axis=3) + fzx = jnp.fft.fft(vz, axis=1) + fyz = jnp.fft.fft(vy, axis=3) + fzy = jnp.fft.fft(vz, axis=2) + + kappa_xy = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fxy.shape[2], d=dy) + kappa_yx = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fyx.shape[1], d=dx) + kappa_xz = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fxz.shape[3], d=dz) + kappa_zx = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fzx.shape[1], d=dx) + kappa_yz = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fyz.shape[3], d=dz) + kappa_zy = 2.0 * jnp.pi * jnp.fft.fftfreq(n=fzy.shape[2], d=dy) + + vxy = jnp.fft.ifft(1j * kappa_xy[None, None, :, None] * fxy, axis=2).real + vyx = jnp.fft.ifft(1j * kappa_yx[None, :, None, None] * fyx, axis=1).real + vxz = jnp.fft.ifft(1j * kappa_xz[None, None, None, :] * fxz, axis=3).real + vzx = jnp.fft.ifft(1j * kappa_zx[None, :, None, None] * fzx, axis=1).real + vyz = jnp.fft.ifft(1j * kappa_yz[None, None, None, :] * fyz, axis=3).real + vzy = jnp.fft.ifft(1j * kappa_zy[None, None, :, None] * fzy, axis=2).real + + omega_x = vzy - vyz + omega_y = vxz - vzx + omega_z = vyx - vxy + + return jnp.concatenate( + [omega_x[..., None], omega_y[..., None], omega_z[..., None]], axis=-1 + ) diff --git a/pdebench/data_gen/uploader.py b/pdebench/data_gen/uploader.py index c111903..02eb262 100644 --- a/pdebench/data_gen/uploader.py +++ b/pdebench/data_gen/uploader.py @@ -1,29 +1,42 @@ -import subprocess +from __future__ import annotations + import json +import subprocess + def dataverse_upload( - file_path, - dataverse_url, - dataverse_token, - dataverse_dir, - dataverse_id, - log, - retry=10): - ''' + file_path, + dataverse_url, + dataverse_token, + dataverse_dir, + dataverse_id, + log, + retry=10, +): + """ Upload a file to dataverse - ''' + """ cmd = [ "curl", - "-X", "POST", - "-H", f"X-Dataverse-key:{dataverse_token}", - "-F", f"file=@{file_path}", - "-F", 'jsonData='+json.dumps({ - "description":"", - "directoryLabel":f"{dataverse_dir}/", - "categories":["Data"], - "restrict": "false" - }), + "-X", + "POST", + "-H", + f"X-Dataverse-key:{dataverse_token}", + "-F", + f"file=@{file_path}", + "-F", + "jsonData=" + + json.dumps( + { + "description": "", + "directoryLabel": f"{dataverse_dir}/", + "categories": ["Data"], + "restrict": "false", + } + ), f"{dataverse_url}/api/datasets/:persistentId/add?persistentId={dataverse_id}", - "--retry", str(retry)] + "--retry", + str(retry), + ] log.info(cmd) - subprocess.Popen(cmd) \ No newline at end of file + subprocess.Popen(cmd) diff --git a/pdebench/data_gen/velocity2vorticity.py b/pdebench/data_gen/velocity2vorticity.py new file mode 100644 index 0000000..8181dc4 --- /dev/null +++ b/pdebench/data_gen/velocity2vorticity.py @@ -0,0 +1,104 @@ +""" Convert velocity- to vorticity field assuming 3D CFD exampe was downloaded. + The resulting .hdf5 file does not store pressure and density. +""" +from __future__ import annotations + +import argparse +from pathlib import Path + +import h5py as h5 +import jax.numpy as jnp +import numpy as np +from tqdm import tqdm + +from .src.vorticity import ( + compute_spectral_vorticity_jnp, +) + + +def convert_velocity() -> None: + parser = argparse.ArgumentParser("Convert velocity field to vorticity!") + parser.add_argument( + "-d", + "--data", + type=str, + required=True, + dest="data", + help=" Specify path to .hdf5 data file", + ) + + args = parser.parse_args() + + if not Path(args.data).exists(): + msg = f"{args.data} does not exist!" + raise FileNotFoundError(msg) + + h5file = h5.File(args.data, "r") + fname = args.data.split("/")[-1] + fname = fname.split(".hdf5")[0] + outpath = str(Path(args.data).parent / fname) + "_vorticity.hdf5" + dx = h5file["x-coordinate"][1] - h5file["x-coordinate"][0] + dy = h5file["y-coordinate"][1] - h5file["y-coordinate"][0] + dz = h5file["z-coordinate"][1] - h5file["z-coordinate"][0] + + if not Path(str(outpath)).exists(): + outfile = h5.File(str(outpath), "a") + outfile.create_dataset( + "omega_x", shape=h5file["Vx"].shape, dtype=h5file["Vx"].dtype + ) + outfile.create_dataset( + "omega_y", shape=h5file["Vy"].shape, dtype=h5file["Vy"].dtype + ) + outfile.create_dataset( + "omega_z", shape=h5file["Vz"].shape, dtype=h5file["Vz"].dtype + ) + outfile.create_dataset( + "t-coordinate", + shape=h5file["t-coordinate"].shape, + dtype=h5file["t-coordinate"].dtype, + ) + outfile.create_dataset( + "x-coordinate", + shape=h5file["x-coordinate"].shape, + dtype=h5file["x-coordinate"].dtype, + ) + outfile.create_dataset( + "y-coordinate", + shape=h5file["y-coordinate"].shape, + dtype=h5file["y-coordinate"].dtype, + ) + outfile.create_dataset( + "z-coordinate", + shape=h5file["z-coordinate"].shape, + dtype=h5file["z-coordinate"].dtype, + ) + + xcoords = h5file["x-coordinate"][:] + ycoords = h5file["y-coordinate"][:] + zcoords = h5file["z-coordinate"][:] + + outfile["t-coordinate"][:] = h5file["t-coordinate"][:] + outfile["x-coordinate"][:] = xcoords + outfile["y-coordinate"][:] = ycoords + outfile["z-coordinate"][:] = zcoords + + trials = h5file["Vx"].shape[0] + + for i in tqdm(range(trials), total=trials): + vx = h5file["Vx"][i][:] + vy = h5file["Vy"][i][:] + vz = h5file["Vz"][i][:] + velocity = np.concatenate( + [vx[..., None], vy[..., None], vz[..., None]], axis=-1 + ) + + vorticity = compute_spectral_vorticity_jnp( + jnp.array(velocity), dx, dy, dz) + + outfile["omega_x"][i] = np.array(vorticity[..., 0]) + outfile["omega_y"][i] = np.array(vorticity[..., 1]) + outfile["omega_z"][i] = np.array(vorticity[..., 2]) + + +if __name__ == "__main__": + convert_velocity() diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 6397843..e33b16e 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -144,97 +144,113 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import pandas as pd -import numpy as np +from __future__ import annotations + import glob + import _pickle as cPickle import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + def main(): # get results - files = glob.glob('./*pickle') + files = glob.glob("./*pickle") files.sort() - + # metric names - var_names = ['MSE', 'normalized MSE', 'Conservation MSE', 'Maximum Error', 'MSE at boundary', - 'MSE FT low', 'MSE FT mid', 'MSE FT high'] - + var_names = [ + "MSE", + "normalized MSE", + "Conservation MSE", + "Maximum Error", + "MSE at boundary", + "MSE FT low", + "MSE FT mid", + "MSE FT high", + ] + # define index index1, index2, index3 = [], [], [] for j, fl in enumerate(files): - with open(fl, 'rb') as f: - title = fl.split('\\')[-1][:-7].split('_') + with open(fl, "rb") as f: + title = fl.split("\\")[-1][:-7].split("_") print(title) - if title[0] == '1D': - if title[1] == 'CFD': + if title[0] == "1D": + if title[1] == "CFD": index1.append(title[0] + title[1]) - index2.append(title[3] + title[4] + '_' + title[2] + '_' + title[5]) + index2.append(title[3] + title[4] + "_" + title[2] + "_" + title[5]) index3.append(title[7]) else: index1.append(title[1]) index2.append(title[3]) index3.append(title[4]) - elif title[0] == '2D': - if title[1] == 'CFD': + elif title[0] == "2D": + if title[1] == "CFD": index1.append(title[0] + title[1]) - index2.append(title[3] + title[3] + title[4] + '_' + title[2] + '_' + title[6]) + index2.append( + title[3] + title[3] + title[4] + "_" + title[2] + "_" + title[6] + ) index3.append(title[9]) else: index1.append(title[1]) index2.append(title[2]) index3.append(title[4]) - elif title[0] == '3D': + elif title[0] == "3D": index1.append(title[0] + title[1]) - index2.append(title[3] + title[4] + title[5] + '_' + title[2] + '_' + title[6]) + index2.append( + title[3] + title[4] + title[5] + "_" + title[2] + "_" + title[6] + ) index3.append(title[8]) else: index1.append(title[0]) index2.append(title[1] + title[2]) index3.append(title[3]) indexes = [index1, index2, index3] - + # create dataframe data = np.zeros([len(files), 8]) for j, fl in enumerate(files): - with open(fl, 'rb') as f: + with open(fl, "rb") as f: test = cPickle.load(f) for i, var in enumerate(test): - if i==5: + if i == 5: data[j, i:] = var else: data[j, i] = var - - index = pd.MultiIndex.from_arrays(indexes, names=('PDE', 'param', 'model')) + + index = pd.MultiIndex.from_arrays(indexes, names=("PDE", "param", "model")) data = pd.DataFrame(data, columns=var_names, index=index) - data.to_csv('Results.csv') - + data.to_csv("Results.csv") + pdes = index.get_level_values(0).drop_duplicates() num_pdes = len(pdes) models = index.get_level_values(2).drop_duplicates() num_models = len(models) x = np.arange(num_pdes) - + if num_models == 1: width = 0.5 else: - width = 0.5/(num_models-1) - - fig, ax = plt.subplots(figsize=(8,6)) + width = 0.5 / (num_models - 1) + + fig, ax = plt.subplots(figsize=(8, 6)) for i in range(num_models): - pos = x-0.3 + 0.5/(num_models-1)*i - ax.bar(pos, data[data.index.isin([models[i]],level=2)]['MSE'], width) - + pos = x - 0.3 + 0.5 / (num_models - 1) * i + ax.bar(pos, data[data.index.isin([models[i]], level=2)]["MSE"], width) + ax.set_xticks(x) - ax.set_xticklabels(pdes,fontsize=30) - ax.tick_params(axis='y',labelsize=30) - ax.set_yscale('log') - ax.set_xlabel('PDEs',fontsize=30) - ax.set_ylabel('MSE',fontsize=30) - fig.legend(models,loc=8,ncol=num_models,fontsize=20) - plt.tight_layout(rect=[0,0.1,1,1]) - plt.savefig('Results.pdf') - + ax.set_xticklabels(pdes, fontsize=30) + ax.tick_params(axis="y", labelsize=30) + ax.set_yscale("log") + ax.set_xlabel("PDEs", fontsize=30) + ax.set_ylabel("MSE", fontsize=30) + fig.legend(models, loc=8, ncol=num_models, fontsize=20) + plt.tight_layout(rect=[0, 0.1, 1, 1]) + plt.savefig("Results.pdf") + if __name__ == "__main__": main() - print("Done.") \ No newline at end of file + print("Done.") diff --git a/pdebench/models/analyse_result_inverse.py b/pdebench/models/analyse_result_inverse.py index 9111613..a0ad534 100644 --- a/pdebench/models/analyse_result_inverse.py +++ b/pdebench/models/analyse_result_inverse.py @@ -144,40 +144,45 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import pandas as pd -import numpy as np -import glob -import _pickle as cPickle +from __future__ import annotations + import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + def main(): - filename = 'inverse.csv' + filename = "inverse.csv" data = pd.read_csv(filename) - pdes = data['pde'].drop_duplicates() + pdes = data["pde"].drop_duplicates() num_pdes = len(pdes) models = list(data.columns.values[-2:]) num_models = len(models) x = np.arange(num_pdes) - width = 0.5/(num_models) - - fig, ax = plt.subplots(figsize=(8,6)) + width = 0.5 / (num_models) + + fig, ax = plt.subplots(figsize=(8, 6)) for i in range(num_models): - pos = x - 0.125 + 0.5/(num_models)*i - ax.bar(pos, data[data.iloc[:,1] == 'mean'][models[i]], - yerr = data[data.iloc[:,1] == 'std'][models[i]], width=width) + pos = x - 0.125 + 0.5 / (num_models) * i + ax.bar( + pos, + data[data.iloc[:, 1] == "mean"][models[i]], + yerr=data[data.iloc[:, 1] == "std"][models[i]], + width=width, + ) print(width, pos) - + ax.set_xticks(x) - ax.set_xticklabels(pdes,rotation=45,fontsize=30) - ax.tick_params(axis='y',labelsize=30) - ax.set_yscale('log') - ax.set_xlabel('PDEs',fontsize=30) - ax.set_ylabel('MSE',fontsize=30) - fig.legend(models,loc=1,ncol=num_models,fontsize=20) + ax.set_xticklabels(pdes, rotation=45, fontsize=30) + ax.tick_params(axis="y", labelsize=30) + ax.set_yscale("log") + ax.set_xlabel("PDEs", fontsize=30) + ax.set_ylabel("MSE", fontsize=30) + fig.legend(models, loc=1, ncol=num_models, fontsize=20) plt.tight_layout() - plt.savefig('ResultsInverse.pdf') - + plt.savefig("ResultsInverse.pdf") + if __name__ == "__main__": main() - print("Done.") \ No newline at end of file + print("Done.") diff --git a/pdebench/models/config/README.md b/pdebench/models/config/README.md index c3d2b2d..41a1a85 100644 --- a/pdebench/models/config/README.md +++ b/pdebench/models/config/README.md @@ -1,37 +1,40 @@ # Config Documentation -This is the documentation of the config files that were used to generate the provided [pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). -Since the default config files for all problems are already provided, this file only provides the values for the arguments that need to be changed. -N/A values mean that the default values can be used. -The complete explanation of the arguments can be found in the [README file](/README.md) +This is the documentation of the config files that were used to generate the +provided +[pre-trained models](https://darus.uni-stuttgart.de/dataset.xhtml?persistentId=doi:10.18419/darus-2987). +Since the default config files for all problems are already provided, this file +only provides the values for the arguments that need to be changed. N/A values +mean that the default values can be used. The complete explanation of the +arguments can be found in the [README file](/README.md) -| Pre-trained model | Config filename | model_name| filename (data) | ar_mode | pushforward | unroll_step | modes | width | -| :--- | :---- | :--- | :--- | :--- | :--- | ---: | ---: | ---: | -| 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | FNO | 1D_diff-sorp_NA_NA | N/A | N/A | N/A | 16 | 64 | -| 1D_diff-sorp_NA_NA_Unet-1-step.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | False | False | N/A | N/A | N/A | -| 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | False | N/A | N/A | N/A | -| 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | True | 20 | N/A | N/A | -| 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | PINN | 1D_diff-sorp_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | -| 1D_CFD_Shock_trans_Train_FNO.pt | config_1DCFD.yaml | FNO | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_CFD_Shock_trans_Train_Unet.pt | config_1DCFD.yaml | Unet | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | True | True | 20 | N/A | N/A | -| ReacDiff_Nu1.0_Rho2.0_FNO.pt | config_ReacDiff.yaml | FNO | ReacDiff_Nu1.0_Rho2.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| ReacDiff_Nu1.0_Rho2.0_Unet.pt | config_ReacDiff.yaml | Unet | ReacDiff_Nu1.0_Rho2.0.hdf5 | True | True | 10 | N/A | N/A | -| 1D_Advection_Sols_beta4.0_FNO.pt | config_Adv.yaml | FNO | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_Advection_Sols_beta4.0_Unet.pt | config_Adv.yaml | Unet | 1D_Advection_Sols_beta4.0.hdf5 | True | True | 20 | N/A | N/A | -| 1D_Advection_Sols_beta4.0_PINN.pt-15000.pt | config_pinn_pde1d.yaml | PINN | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | N/A | N/A | -| 1D_Burgers_Sols_Nu1.0_FNO.pt | config_Bgs.yaml | FNO | 1D_Burgers_Sols_Nu1.0.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 1D_Burgers_Sols_Nu1.0_Unet-PF-20.pt | config_Bgs.yaml | Unet | 1D_Burgers_Sols_Nu1.0.hdf5 | True | True | 20 | N/A | N/A | -| 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | FNO | 2D_diff-react_NA_NA | N/A | N/A | N/A | 12 | 20 | -| 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | False | False | N/A | N/A | N/A | -| 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | False | N/A | N/A | N/A | -| 2D_diff-react_NA_NA_Unet-PF-20.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | True | 20 | N/A | N/A | -| 2D_diff-react_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_diff-react.yaml | PINN | 2D_diff-react_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | -| 2D_rdb_NA_NA_FNO.pt | config_rdb.yaml | FNO | 2D_rdb_NA_NA | N/A | N/A | N/A | 12 | 20 | -| 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | False | False | N/A | N/A | N/A | -| 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | False | N/A | N/A | N/A | -| 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | True | 20 | N/A | N/A | -| 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | PINN | 2D_rdb_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | -| 2D_DarcyFlow_beta0.01_Train_FNO.pt | config_Darcy.yaml | FNO | 2D_DarcyFlow_beta0.01_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 2D_DarcyFlow_beta0.01_Train_Unet_PF_1.pt | config_Darcy.yaml | Unet | 2D_DarcyFlow_beta0.01_Train.hdf5 | False | False | N/A | N/A | N/A | -| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_FNO.pt | config_3DCFD.yaml | FNO | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | -| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_Unet-PF-20.pt | config_3DCFD.yaml | Unet | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | True | True | 20 | N/A | N/A | \ No newline at end of file +| Pre-trained model | Config filename | model_name | filename (data) | ar_mode | pushforward | unroll_step | modes | width | +| :--------------------------------------------------------------- | :-------------------------- | :--------- | :------------------------------------------------------ | :------ | :---------- | ----------: | ----: | ----: | +| 1D_diff-sorp_NA_NA_FNO.pt | config_diff-sorp.yaml | FNO | 1D_diff-sorp_NA_NA | N/A | N/A | N/A | 16 | 64 | +| 1D_diff-sorp_NA_NA_Unet-1-step.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | False | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-AR.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | False | N/A | N/A | N/A | +| 1D_diff-sorp_NA_NA_Unet-PF-20.pt | config_diff-sorp.yaml | Unet | 1D_diff-sorp_NA_NA | True | True | 20 | N/A | N/A | +| 1D_diff-sorp_NA_NA_0001.h5_PINN.pt-15000.pt | config_pinn_diff-sorp.yaml | PINN | 1D_diff-sorp_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 1D_CFD_Shock_trans_Train_FNO.pt | config_1DCFD.yaml | FNO | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_CFD_Shock_trans_Train_Unet.pt | config_1DCFD.yaml | Unet | 1D_CFD_Shock_Eta1.e-8_Zeta1.e-8_trans_Train.hdf5 | True | True | 20 | N/A | N/A | +| ReacDiff_Nu1.0_Rho2.0_FNO.pt | config_ReacDiff.yaml | FNO | ReacDiff_Nu1.0_Rho2.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| ReacDiff_Nu1.0_Rho2.0_Unet.pt | config_ReacDiff.yaml | Unet | ReacDiff_Nu1.0_Rho2.0.hdf5 | True | True | 10 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_FNO.pt | config_Adv.yaml | FNO | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Advection_Sols_beta4.0_Unet.pt | config_Adv.yaml | Unet | 1D_Advection_Sols_beta4.0.hdf5 | True | True | 20 | N/A | N/A | +| 1D_Advection_Sols_beta4.0_PINN.pt-15000.pt | config_pinn_pde1d.yaml | PINN | 1D_Advection_Sols_beta4.0.hdf5 | N/A | N/A | N/A | N/A | N/A | +| 1D_Burgers_Sols_Nu1.0_FNO.pt | config_Bgs.yaml | FNO | 1D_Burgers_Sols_Nu1.0.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 1D_Burgers_Sols_Nu1.0_Unet-PF-20.pt | config_Bgs.yaml | Unet | 1D_Burgers_Sols_Nu1.0.hdf5 | True | True | 20 | N/A | N/A | +| 2D_diff-react_NA_NA_FNO.pt | config_diff-react.yaml | FNO | 2D_diff-react_NA_NA | N/A | N/A | N/A | 12 | 20 | +| 2D_diff-react_NA_NA_Unet-1-step.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | False | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-AR.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | False | N/A | N/A | N/A | +| 2D_diff-react_NA_NA_Unet-PF-20.pt | config_diff-react.yaml | Unet | 2D_diff-react_NA_NA | True | True | 20 | N/A | N/A | +| 2D_diff-react_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_diff-react.yaml | PINN | 2D_diff-react_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 2D_rdb_NA_NA_FNO.pt | config_rdb.yaml | FNO | 2D_rdb_NA_NA | N/A | N/A | N/A | 12 | 20 | +| 2D_rdb_NA_NA_Unet-1-step.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | False | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-AR.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | False | N/A | N/A | N/A | +| 2D_rdb_NA_NA_Unet-PF-20.pt | config_rdb.yaml | Unet | 2D_rdb_NA_NA | True | True | 20 | N/A | N/A | +| 2D_rdb_NA_NA_0000.h5_PINN.pt-15000.pt | config_pinn_swe2d.yaml | PINN | 2D_rdb_NA_NA.h5 | N/A | N/A | N/A | N/A | N/A | +| 2D_DarcyFlow_beta0.01_Train_FNO.pt | config_Darcy.yaml | FNO | 2D_DarcyFlow_beta0.01_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 2D_DarcyFlow_beta0.01_Train_Unet_PF_1.pt | config_Darcy.yaml | Unet | 2D_DarcyFlow_beta0.01_Train.hdf5 | False | False | N/A | N/A | N/A | +| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_FNO.pt | config_3DCFD.yaml | FNO | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | N/A | N/A | N/A | 12 | 20 | +| 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train_Unet-PF-20.pt | config_3DCFD.yaml | Unet | 3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5 | True | True | 20 | N/A | N/A | diff --git a/pdebench/models/config/args/config_1DCFD.yaml b/pdebench/models/config/args/config_1DCFD.yaml index 87863a1..d8d6524 100644 --- a/pdebench/models/config/args/config_1DCFD.yaml +++ b/pdebench/models/config/args/config_1DCFD.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: True continue_training: False num_workers: 2 @@ -6,7 +6,7 @@ batch_size: 100 initial_step: 10 t_train: 100 model_update: 2 -filename: '1D_CFD_periodic_Train.hdf5' +filename: "1D_CFD_periodic_Train.hdf5" single_file: True reduced_resolution: 8 reduced_resolution_t: 5 @@ -34,8 +34,8 @@ mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 -inverse_model_type: InitialConditionInterp +inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 -inverse_verbose_flag: False \ No newline at end of file +inverse_verbose_flag: False diff --git a/pdebench/models/config/args/config_2DCFD.yaml b/pdebench/models/config/args/config_2DCFD.yaml index 0ae0ce7..c276c0f 100644 --- a/pdebench/models/config/args/config_2DCFD.yaml +++ b/pdebench/models/config/args/config_2DCFD.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: True continue_training: False batch_size: 20 @@ -6,7 +6,7 @@ unroll_step: 20 num_workers: 2 t_train: 21 model_update: 2 -filename: '2D_CFD_M0.1_Eta0.01_Zeta0.01_periodic_128_Train.hdf5' +filename: "2D_CFD_M0.1_Eta0.01_Zeta0.01_periodic_128_Train.hdf5" single_file: True reduced_resolution: 2 reduced_resolution_t: 1 @@ -22,4 +22,4 @@ modes: 12 width: 20 scheduler_step: 100 scheduler_gamma: 0.5 -initial_step: 10 \ No newline at end of file +initial_step: 10 diff --git a/pdebench/models/config/args/config_3DCFD.yaml b/pdebench/models/config/args/config_3DCFD.yaml index 2bbcfc9..25df7aa 100644 --- a/pdebench/models/config/args/config_3DCFD.yaml +++ b/pdebench/models/config/args/config_3DCFD.yaml @@ -1,11 +1,11 @@ -model_name: 'Unet' +model_name: "Unet" if_training: True t_train: 21 continue_training: False batch_size: 5 unroll_step: 20 model_update: 1 -filename: '3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5' +filename: "3D_CFD_Rand_M1.0_Eta1e-08_Zeta1e-08_periodic_Train.hdf5" single_file: True reduced_resolution: 2 reduced_resolution_t: 1 @@ -21,4 +21,4 @@ modes: 12 width: 20 scheduler_step: 100 scheduler_gamma: 0.5 -initial_step: 10 # should be the same value to unroll_step ?? \ No newline at end of file +initial_step: 10 # should be the same value to unroll_step ?? diff --git a/pdebench/models/config/args/config_Adv.yaml b/pdebench/models/config/args/config_Adv.yaml index 37d8f5f..54129d6 100644 --- a/pdebench/models/config/args/config_Adv.yaml +++ b/pdebench/models/config/args/config_Adv.yaml @@ -1,27 +1,27 @@ -model_name: 'Unet' -if_training: True -continue_training: False -batch_size: 50 -unroll_step: 20 -t_train: 200 -model_update: 1 -filename: '1D_Advection_Sols_beta4.0.hdf5' -single_file: True -reduced_resolution: 4 -reduced_resolution_t: 5 -reduced_batch: 1 -epochs: 500 -learning_rate: 1.e-3 -num_workers: 0 -#Unet -in_channels: 1 -out_channels: 1 -ar_mode: True -pushforward: True -#FNO -num_channels: 1 -modes: 12 -width: 20 -scheduler_step: 100 -scheduler_gamma: 0.5 -initial_step: 10 \ No newline at end of file +model_name: "Unet" +if_training: True +continue_training: False +batch_size: 50 +unroll_step: 20 +t_train: 200 +model_update: 1 +filename: "1D_Advection_Sols_beta4.0.hdf5" +single_file: True +reduced_resolution: 4 +reduced_resolution_t: 5 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +num_workers: 0 +#Unet +in_channels: 1 +out_channels: 1 +ar_mode: True +pushforward: True +#FNO +num_channels: 1 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 diff --git a/pdebench/models/config/args/config_Bgs.yaml b/pdebench/models/config/args/config_Bgs.yaml index 43e6917..3702b3b 100644 --- a/pdebench/models/config/args/config_Bgs.yaml +++ b/pdebench/models/config/args/config_Bgs.yaml @@ -1,27 +1,27 @@ -model_name: 'Unet' -if_training: True -continue_training: False -batch_size: 50 -t_train: 200 -model_update: 1 -filename: '1D_Burgers_Sols_Nu1.0.hdf5' -single_file: True -reduced_resolution: 4 -reduced_resolution_t: 5 -reduced_batch: 1 -epochs: 500 -learning_rate: 1.e-3 -num_workers: 0 -#Unet -in_channels: 1 -out_channels: 1 -ar_mode: True -pushforward: True -unroll_step: 20 -#FNO -num_channels: 1 -modes: 12 -width: 20 -scheduler_step: 100 -scheduler_gamma: 0.5 -initial_step: 10 \ No newline at end of file +model_name: "Unet" +if_training: True +continue_training: False +batch_size: 50 +t_train: 200 +model_update: 1 +filename: "1D_Burgers_Sols_Nu1.0.hdf5" +single_file: True +reduced_resolution: 4 +reduced_resolution_t: 5 +reduced_batch: 1 +epochs: 500 +learning_rate: 1.e-3 +num_workers: 0 +#Unet +in_channels: 1 +out_channels: 1 +ar_mode: True +pushforward: True +unroll_step: 20 +#FNO +num_channels: 1 +modes: 12 +width: 20 +scheduler_step: 100 +scheduler_gamma: 0.5 +initial_step: 10 diff --git a/pdebench/models/config/args/config_Darcy.yaml b/pdebench/models/config/args/config_Darcy.yaml index bca0bfa..5bd9879 100644 --- a/pdebench/models/config/args/config_Darcy.yaml +++ b/pdebench/models/config/args/config_Darcy.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: True continue_training: False num_workers: 2 @@ -6,7 +6,7 @@ batch_size: 50 initial_step: 10 t_train: 1 model_update: 2 -filename: '2D_DarcyFlow_beta0.01_Train.hdf5' +filename: "2D_DarcyFlow_beta0.01_Train.hdf5" single_file: True reduced_resolution: 2 reduced_resolution_t: 1 @@ -34,8 +34,8 @@ mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 -inverse_model_type: InitialConditionInterp +inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 -inverse_verbose_flag: False \ No newline at end of file +inverse_verbose_flag: False diff --git a/pdebench/models/config/args/config_ReacDiff.yaml b/pdebench/models/config/args/config_ReacDiff.yaml index 7b8f045..5ebfe91 100644 --- a/pdebench/models/config/args/config_ReacDiff.yaml +++ b/pdebench/models/config/args/config_ReacDiff.yaml @@ -1,10 +1,10 @@ -model_name: 'Unet' +model_name: "Unet" if_training: True continue_training: False batch_size: 50 t_train: 30 model_update: 1 -filename: 'ReacDiff_Nu0.5_Rho1.0.hdf5' +filename: "ReacDiff_Nu0.5_Rho1.0.hdf5" single_file: True reduced_resolution: 4 reduced_resolution_t: 1 @@ -24,4 +24,4 @@ modes: 12 width: 20 scheduler_step: 100 scheduler_gamma: 0.5 -initial_step: 5 \ No newline at end of file +initial_step: 5 diff --git a/pdebench/models/config/args/config_diff-react.yaml b/pdebench/models/config/args/config_diff-react.yaml index d7a8abe..a650a07 100644 --- a/pdebench/models/config/args/config_diff-react.yaml +++ b/pdebench/models/config/args/config_diff-react.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: False continue_training: False num_workers: 2 @@ -6,7 +6,7 @@ batch_size: 5 initial_step: 10 t_train: 101 model_update: 10 -filename: '2D_diff-react_NA_NA' +filename: "2D_diff-react_NA_NA" single_file: False reduced_resolution: 1 reduced_resolution_t: 1 @@ -34,7 +34,7 @@ mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 -inverse_model_type: InitialConditionInterp +inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 @@ -47,4 +47,4 @@ x_max: 1 y_min: -1 y_max: 1 t_min: 0 -t_max: 5 \ No newline at end of file +t_max: 5 diff --git a/pdebench/models/config/args/config_diff-sorp.yaml b/pdebench/models/config/args/config_diff-sorp.yaml index c352fc5..7ed4371 100644 --- a/pdebench/models/config/args/config_diff-sorp.yaml +++ b/pdebench/models/config/args/config_diff-sorp.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: False continue_training: False num_workers: 2 @@ -6,7 +6,7 @@ batch_size: 50 initial_step: 10 t_train: 101 model_update: 10 -filename: '1D_diff-sorp_NA_NA' +filename: "1D_diff-sorp_NA_NA" single_file: False reduced_resolution: 1 reduced_resolution_t: 1 @@ -34,7 +34,7 @@ mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 -inverse_model_type: InitialConditionInterp +inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 @@ -47,4 +47,4 @@ x_max: 1 y_min: 0 y_max: 1 t_min: 0 -t_max: 500 \ No newline at end of file +t_max: 500 diff --git a/pdebench/models/config/args/config_pinn_CFD1d.yaml b/pdebench/models/config/args/config_pinn_CFD1d.yaml index 4efa594..3b688a5 100644 --- a/pdebench/models/config/args/config_pinn_CFD1d.yaml +++ b/pdebench/models/config/args/config_pinn_CFD1d.yaml @@ -1,15 +1,15 @@ -model_name: 'PINN' -scenario: 'pde1D' +model_name: "PINN" +scenario: "pde1D" model_update: 500 -filename: '1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5' +filename: "1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5" epochs: 15000 input_ch: 2 output_ch: 3 learning_rate: 1.e-3 -root_path: '../data' +root_path: "../data" val_num: 10 if_periodic_bc: True period: 5000 val_time: 1.0 val_batch_idx: 10 -aux_params: [1.6666666667] \ No newline at end of file +aux_params: [1.6666666667] diff --git a/pdebench/models/config/args/config_pinn_diff-react.yaml b/pdebench/models/config/args/config_pinn_diff-react.yaml index 60f71cd..f251aac 100644 --- a/pdebench/models/config/args/config_pinn_diff-react.yaml +++ b/pdebench/models/config/args/config_pinn_diff-react.yaml @@ -1,15 +1,15 @@ -model_name: 'PINN' +model_name: "PINN" -scenario: 'diff-react' +scenario: "diff-react" model_update: 500 -filename: '2D_diff-react_NA_NA/2D_diff-react_NA_NA.h5' +filename: "2D_diff-react_NA_NA/2D_diff-react_NA_NA.h5" epochs: 100 learning_rate: 1.e-3 -seed: '0000' +seed: "0000" # unused arguments input_ch: 0 output_ch: 1 -root_path: '.' +root_path: "." val_num: 1 if_periodic_bc: False -aux_params: 0 \ No newline at end of file +aux_params: 0 diff --git a/pdebench/models/config/args/config_pinn_diff-sorp.yaml b/pdebench/models/config/args/config_pinn_diff-sorp.yaml index 8bc0701..d3e6512 100644 --- a/pdebench/models/config/args/config_pinn_diff-sorp.yaml +++ b/pdebench/models/config/args/config_pinn_diff-sorp.yaml @@ -1,15 +1,15 @@ -model_name: 'PINN' +model_name: "PINN" -scenario: 'diff-sorp' +scenario: "diff-sorp" model_update: 500 -filename: '1D_diff-sorp_NA_NA/1D_diff-sorp_NA_NA.h5' +filename: "1D_diff-sorp_NA_NA/1D_diff-sorp_NA_NA.h5" epochs: 15000 learning_rate: 1.e-3 -seed: '0000' +seed: "0000" # unused arguments input_ch: 0 output_ch: 1 -root_path: '.' +root_path: "." val_num: 1 if_periodic_bc: False -aux_params: 0 \ No newline at end of file +aux_params: 0 diff --git a/pdebench/models/config/args/config_pinn_pde1d.yaml b/pdebench/models/config/args/config_pinn_pde1d.yaml index b0013ef..b76d3cc 100644 --- a/pdebench/models/config/args/config_pinn_pde1d.yaml +++ b/pdebench/models/config/args/config_pinn_pde1d.yaml @@ -1,15 +1,15 @@ -model_name: 'PINN' -scenario: 'pde1D' +model_name: "PINN" +scenario: "pde1D" model_update: 500 -filename: '1D_Advection_Sols_beta0.1.hdf5' +filename: "1D_Advection_Sols_beta0.1.hdf5" epochs: 15000 input_ch: 2 output_ch: 1 learning_rate: 1.e-3 -root_path: '../data' +root_path: "../data" val_num: 10 if_periodic_bc: True period: 5000 val_time: 2.0 val_batch_idx: 10 -aux_params: [0.1] \ No newline at end of file +aux_params: [0.1] diff --git a/pdebench/models/config/args/config_pinn_swe2d.yaml b/pdebench/models/config/args/config_pinn_swe2d.yaml index b4fe9b0..bff678a 100644 --- a/pdebench/models/config/args/config_pinn_swe2d.yaml +++ b/pdebench/models/config/args/config_pinn_swe2d.yaml @@ -1,15 +1,15 @@ -model_name: 'PINN' +model_name: "PINN" -scenario: 'swe2d' +scenario: "swe2d" model_update: 500 -filename: '2D_rdb_NA_NA/2D_rdb_NA_NA.h5' +filename: "2D_rdb_NA_NA/2D_rdb_NA_NA.h5" epochs: 15000 learning_rate: 1.e-3 -seed: '0000' +seed: "0000" # unused arguments input_ch: 0 output_ch: 1 -root_path: '.' +root_path: "." val_num: 1 if_periodic_bc: False aux_params: 0 diff --git a/pdebench/models/config/args/config_rdb.yaml b/pdebench/models/config/args/config_rdb.yaml index d58fd4d..1690d9d 100644 --- a/pdebench/models/config/args/config_rdb.yaml +++ b/pdebench/models/config/args/config_rdb.yaml @@ -1,4 +1,4 @@ -model_name: 'FNO' +model_name: "FNO" if_training: False continue_training: False num_workers: 2 @@ -6,7 +6,7 @@ batch_size: 5 initial_step: 10 t_train: 101 model_update: 10 -filename: '2D_rdb_NA_NA' +filename: "2D_rdb_NA_NA" single_file: False reduced_resolution: 1 reduced_resolution_t: 1 @@ -34,7 +34,7 @@ mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 -inverse_model_type: InitialConditionInterp +inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 @@ -47,4 +47,4 @@ x_max: 2.5 y_min: -2.5 y_max: 2.5 t_min: 0 -t_max: 1 \ No newline at end of file +t_max: 1 diff --git a/pdebench/models/config/config.yaml b/pdebench/models/config/config.yaml index 198ad5c..63ca75a 100644 --- a/pdebench/models/config/config.yaml +++ b/pdebench/models/config/config.yaml @@ -7,55 +7,55 @@ hydra: output_subdir: null run: dir: . - + args: - model_name: 'FNO' - if_training: False - continue_training: False - num_workers: 2 - batch_size: 5 - initial_step: 10 - t_train: 101 - model_update: 10 - filename: '2D_diff-react_NA_NA' - single_file: False - reduced_resolution: 1 - reduced_resolution_t: 1 - reduced_batch: 1 - epochs: 500 - learning_rate: 1.e-3 - scheduler_step: 100 - scheduler_gamma: 0.5 - #Unet - in_channels: 2 - out_channels: 2 - ar_mode: True - pushforward: True - unroll_step: 20 - #FNO - num_channels: 2 - modes: 12 - width: 20 - #Inverse - base_path: ../data/ - training_type: autoregressive - #Inverse MCMC - mcmc_num_samples: 20 - mcmc_warmup_steps: 10 - mcmc_num_chains: 1 - num_samples_max: 1000 - in_channels_hid: 64 - inverse_model_type: InitialConditionInterp - #Inverse grad - inverse_epochs: 100 - inverse_learning_rate: 0.2 - inverse_verbose_flag: False - #Plotting - plot: False - channel_plot: 0 # Which channel/variable to be plotted - x_min: -1 - x_max: 1 - y_min: -1 - y_max: 1 - t_min: 0 - t_max: 5 \ No newline at end of file + model_name: "FNO" + if_training: False + continue_training: False + num_workers: 2 + batch_size: 5 + initial_step: 10 + t_train: 101 + model_update: 10 + filename: "2D_diff-react_NA_NA" + single_file: False + reduced_resolution: 1 + reduced_resolution_t: 1 + reduced_batch: 1 + epochs: 500 + learning_rate: 1.e-3 + scheduler_step: 100 + scheduler_gamma: 0.5 + #Unet + in_channels: 2 + out_channels: 2 + ar_mode: True + pushforward: True + unroll_step: 20 + #FNO + num_channels: 2 + modes: 12 + width: 20 + #Inverse + base_path: ../data/ + training_type: autoregressive + #Inverse MCMC + mcmc_num_samples: 20 + mcmc_warmup_steps: 10 + mcmc_num_chains: 1 + num_samples_max: 1000 + in_channels_hid: 64 + inverse_model_type: InitialConditionInterp + #Inverse grad + inverse_epochs: 100 + inverse_learning_rate: 0.2 + inverse_verbose_flag: False + #Plotting + plot: False + channel_plot: 0 # Which channel/variable to be plotted + x_min: -1 + x_max: 1 + y_min: -1 + y_max: 1 + t_min: 0 + t_max: 5 diff --git a/pdebench/models/config/config_darcy.yaml b/pdebench/models/config/config_darcy.yaml index 02bc9dc..4272742 100644 --- a/pdebench/models/config/config_darcy.yaml +++ b/pdebench/models/config/config_darcy.yaml @@ -9,15 +9,15 @@ hydra: dir: . args: - model_name: 'FNO' + model_name: "FNO" if_training: True continue_training: False num_workers: 2 batch_size: 50 initial_step: 1 - t_train: 1 # steady-state + t_train: 1 # steady-state model_update: 2 - filename: '2D_DarcyFlow_beta0.01_Train.hdf5' + filename: "2D_DarcyFlow_beta0.01_Train.hdf5" single_file: True reduced_resolution: 1 reduced_resolution_t: 1 @@ -37,15 +37,15 @@ args: modes: 12 width: 20 #Inverse - data_path: '../data/2D/DarcyFlow/Train/' - training_type: 'single' #autoregressive + data_path: "../data/2D/DarcyFlow/Train/" + training_type: "single" #autoregressive #Inverse MCMC mcmc_num_samples: 20 mcmc_warmup_steps: 10 mcmc_num_chains: 1 num_samples_max: 1000 in_channels_hid: 64 - inverse_model_type: InitialConditionInterp + inverse_model_type: InitialConditionInterp #Inverse grad inverse_epochs: 100 inverse_learning_rate: 0.2 @@ -54,8 +54,8 @@ args: plot: False channel_plot: 0 # Which channel/variable to be plotted x_min: -1 - x_max: 1 # spatial dimension x: [-1, 1] + x_max: 1 # spatial dimension x: [-1, 1] y_min: -1 - y_max: 1 # spatial dimension y: [-1, 1] + y_max: 1 # spatial dimension y: [-1, 1] t_min: 0 - t_max: 5 # time dimension t: [0, 5] + t_max: 5 # time dimension t: [0, 5] diff --git a/pdebench/models/config/config_rdb.yaml b/pdebench/models/config/config_rdb.yaml index ffa9ea0..e28a163 100644 --- a/pdebench/models/config/config_rdb.yaml +++ b/pdebench/models/config/config_rdb.yaml @@ -9,53 +9,53 @@ hydra: dir: . args: - model_name: 'FNO' - if_training: False - continue_training: False - num_workers: 2 - batch_size: 5 - initial_step: 10 - t_train: 101 - model_update: 2 - filename: '2D_rdb_NA_NA.h5' - single_file: True - data_path: '/path/to/swe2d/h5' - reduced_resolution: 1 - reduced_resolution_t: 1 - reduced_batch: 1 - epochs: 500 - learning_rate: 1.e-3 - scheduler_step: 100 - scheduler_gamma: 0.5 - #Unet - in_channels: 1 - out_channels: 1 - ar_mode: True - pushforward: True - unroll_step: 20 - #FNO - num_channels: 1 - modes: 12 - width: 20 - #Inverse - training_type: 'autoregressive' - #Inverse MCMC - mcmc_num_samples: 20 - mcmc_warmup_steps: 10 - mcmc_num_chains: 1 - num_samples_max: 1000 - in_channels_hid: 64 - inverse_model_type: InitialConditionInterp - #Inverse grad - inverse_epochs: 100 - inverse_learning_rate: 0.2 - inverse_verbose_flag: False - #Plotting - plot: False - channel_plot: 0 # Which channel/variable to be plotted - x_min: -2.5 - x_max: 2.5 - y_min: -2.5 - y_max: 2.5 - t_min: 0 - t_max: 1 + model_name: "FNO" + if_training: False + continue_training: False + num_workers: 2 + batch_size: 5 + initial_step: 10 + t_train: 101 + model_update: 2 + filename: "2D_rdb_NA_NA.h5" + single_file: True + data_path: "/path/to/swe2d/h5" + reduced_resolution: 1 + reduced_resolution_t: 1 + reduced_batch: 1 + epochs: 500 + learning_rate: 1.e-3 + scheduler_step: 100 + scheduler_gamma: 0.5 + #Unet + in_channels: 1 + out_channels: 1 + ar_mode: True + pushforward: True + unroll_step: 20 + #FNO + num_channels: 1 + modes: 12 + width: 20 + #Inverse + training_type: "autoregressive" + #Inverse MCMC + mcmc_num_samples: 20 + mcmc_warmup_steps: 10 + mcmc_num_chains: 1 + num_samples_max: 1000 + in_channels_hid: 64 + inverse_model_type: InitialConditionInterp + #Inverse grad + inverse_epochs: 100 + inverse_learning_rate: 0.2 + inverse_verbose_flag: False + #Plotting + plot: False + channel_plot: 0 # Which channel/variable to be plotted + x_min: -2.5 + x_max: 2.5 + y_min: -2.5 + y_max: 2.5 + t_min: 0 + t_max: 1 diff --git a/pdebench/models/config/results.yaml b/pdebench/models/config/results.yaml index 12e1728..67195b9 100644 --- a/pdebench/models/config/results.yaml +++ b/pdebench/models/config/results.yaml @@ -7,13 +7,18 @@ hydra: output_subdir: null run: dir: . - -args: - model_names: [FNO, Unet] - base_path : /home/alesiani/python/pde_benchmark/pdebench/data/ - inverse_model_type : InitialConditionInterp - filenames : [/1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5,/1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5, /1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5, /1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5] - shortfilenames : [Advection, Burgers, ReacDiff, CFD] - results_values : [ mseloss_pred_u0 ] - result_filename : csv/results_inverse.csv +args: + model_names: [FNO, Unet] + base_path: /home/alesiani/python/pde_benchmark/pdebench/data/ + inverse_model_type: InitialConditionInterp + filenames: + [ + /1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5, + /1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5, + /1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5, + /1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5, + ] + shortfilenames: [Advection, Burgers, ReacDiff, CFD] + results_values: [mseloss_pred_u0] + result_filename: csv/results_inverse.csv diff --git a/pdebench/models/fno/fno.py b/pdebench/models/fno/fno.py index 6371228..806d194 100644 --- a/pdebench/models/fno/fno.py +++ b/pdebench/models/fno/fno.py @@ -26,11 +26,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +from __future__ import annotations import torch -import torch.nn as nn -import numpy as np import torch.nn.functional as F +from torch import nn class SpectralConv1d(nn.Module): @@ -38,15 +38,21 @@ def __init__(self, in_channels, out_channels, modes1): super(SpectralConv1d, self).__init__() """ - 1D Fourier layer. It does FFT, linear transform, and Inverse FFT. + 1D Fourier layer. It does FFT, linear transform, and Inverse FFT. """ self.in_channels = in_channels self.out_channels = out_channels - self.modes1 = modes1 #Number of Fourier modes to multiply, at most floor(N/2) + 1 + self.modes1 = ( + # Number of Fourier modes to multiply, at most floor(N/2) + 1 + modes1 + ) - self.scale = (1 / (in_channels*out_channels)) - self.weights1 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, dtype=torch.cfloat)) + self.scale = 1 / (in_channels * out_channels) + self.weights1 = nn.Parameter( + self.scale + * torch.rand(in_channels, out_channels, self.modes1, dtype=torch.cfloat) + ) # Complex multiplication def compl_mul1d(self, input, weights): @@ -55,17 +61,26 @@ def compl_mul1d(self, input, weights): def forward(self, x): batchsize = x.shape[0] - #Compute Fourier coeffcients up to factor of e^(- something constant) + # Compute Fourier coefficients up to factor of e^(- something constant) x_ft = torch.fft.rfft(x) - # Multiply relevant Fourier modes - out_ft = torch.zeros(batchsize, self.out_channels, x.size(-1)//2 + 1, device=x.device, dtype=torch.cfloat) - out_ft[:, :, :self.modes1] = self.compl_mul1d(x_ft[:, :, :self.modes1], self.weights1) - - #Return to physical space + # Multiply relevant Fourier modes + out_ft = torch.zeros( + batchsize, + self.out_channels, + x.size(-1) // 2 + 1, + device=x.device, + dtype=torch.cfloat, + ) + out_ft[:, :, : self.modes1] = self.compl_mul1d( + x_ft[:, :, : self.modes1], self.weights1 + ) + + # Return to physical space x = torch.fft.irfft(out_ft, n=x.size(-1)) return x + class FNO1d(nn.Module): def __init__(self, num_channels, modes=16, width=64, initial_step=10): super(FNO1d, self).__init__() @@ -76,7 +91,7 @@ def __init__(self, num_channels, modes=16, width=64, initial_step=10): 2. 4 layers of the integral operators u' = (W + K)(u). W defined by self.w; K defined by self.conv . 3. Project from the channel space to the output space by self.fc1 and self.fc2 . - + input: the solution of the initial condition and location (a(x), x) input shape: (batchsize, x=s, c=2) output: the solution of a later timestep @@ -85,8 +100,10 @@ def __init__(self, num_channels, modes=16, width=64, initial_step=10): self.modes1 = modes self.width = width - self.padding = 2 # pad the domain if input is non-periodic - self.fc0 = nn.Linear(initial_step*num_channels+1, self.width) # input channel is 2: (a(x), x) + self.padding = 2 # pad the domain if input is non-periodic + self.fc0 = nn.Linear( + initial_step * num_channels + 1, self.width + ) # input channel is 2: (a(x), x) self.conv0 = SpectralConv1d(self.width, self.width, self.modes1) self.conv1 = SpectralConv1d(self.width, self.width, self.modes1) @@ -105,8 +122,9 @@ def forward(self, x, grid): x = torch.cat((x, grid), dim=-1) x = self.fc0(x) x = x.permute(0, 2, 1) - - x = F.pad(x, [0, self.padding]) # pad the domain if input is non-periodic + + # pad the domain if input is non-periodic + x = F.pad(x, [0, self.padding]) x1 = self.conv0(x) x2 = self.w0(x) @@ -127,7 +145,7 @@ def forward(self, x, grid): x2 = self.w3(x) x = x1 + x2 - x = x[..., :-self.padding] + x = x[..., : -self.padding] x = x.permute(0, 2, 1) x = self.fc1(x) x = F.gelu(x) @@ -140,17 +158,30 @@ def __init__(self, in_channels, out_channels, modes1, modes2): super(SpectralConv2d_fast, self).__init__() """ - 2D Fourier layer. It does FFT, linear transform, and Inverse FFT. + 2D Fourier layer. It does FFT, linear transform, and Inverse FFT. """ self.in_channels = in_channels self.out_channels = out_channels - self.modes1 = modes1 #Number of Fourier modes to multiply, at most floor(N/2) + 1 + self.modes1 = ( + # Number of Fourier modes to multiply, at most floor(N/2) + 1 + modes1 + ) self.modes2 = modes2 - self.scale = (1 / (in_channels * out_channels)) - self.weights1 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, dtype=torch.cfloat)) - self.weights2 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, dtype=torch.cfloat)) + self.scale = 1 / (in_channels * out_channels) + self.weights1 = nn.Parameter( + self.scale + * torch.rand( + in_channels, out_channels, self.modes1, self.modes2, dtype=torch.cfloat + ) + ) + self.weights2 = nn.Parameter( + self.scale + * torch.rand( + in_channels, out_channels, self.modes1, self.modes2, dtype=torch.cfloat + ) + ) # Complex multiplication def compl_mul2d(self, input, weights): @@ -159,20 +190,30 @@ def compl_mul2d(self, input, weights): def forward(self, x): batchsize = x.shape[0] - #Compute Fourier coeffcients up to factor of e^(- something constant) + # Compute Fourier coefficients up to factor of e^(- something constant) x_ft = torch.fft.rfft2(x) # Multiply relevant Fourier modes - out_ft = torch.zeros(batchsize, self.out_channels, x.size(-2), x.size(-1)//2 + 1, dtype=torch.cfloat, device=x.device) - out_ft[:, :, :self.modes1, :self.modes2] = \ - self.compl_mul2d(x_ft[:, :, :self.modes1, :self.modes2], self.weights1) - out_ft[:, :, -self.modes1:, :self.modes2] = \ - self.compl_mul2d(x_ft[:, :, -self.modes1:, :self.modes2], self.weights2) - - #Return to physical space + out_ft = torch.zeros( + batchsize, + self.out_channels, + x.size(-2), + x.size(-1) // 2 + 1, + dtype=torch.cfloat, + device=x.device, + ) + out_ft[:, :, : self.modes1, : self.modes2] = self.compl_mul2d( + x_ft[:, :, : self.modes1, : self.modes2], self.weights1 + ) + out_ft[:, :, -self.modes1 :, : self.modes2] = self.compl_mul2d( + x_ft[:, :, -self.modes1 :, : self.modes2], self.weights2 + ) + + # Return to physical space x = torch.fft.irfft2(out_ft, s=(x.size(-2), x.size(-1))) return x + class FNO2d(nn.Module): def __init__(self, num_channels, modes1=12, modes2=12, width=20, initial_step=10): super(FNO2d, self).__init__() @@ -183,7 +224,7 @@ def __init__(self, num_channels, modes1=12, modes2=12, width=20, initial_step=10 2. 4 layers of the integral operators u' = (W + K)(u). W defined by self.w; K defined by self.conv . 3. Project from the channel space to the output space by self.fc1 and self.fc2 . - + input: the solution of the previous 10 timesteps + 2 locations (u(t-10, x, y), ..., u(t-1, x, y), x, y) input shape: (batchsize, x, y, c) output: the solution of the next timestep @@ -193,14 +234,22 @@ def __init__(self, num_channels, modes1=12, modes2=12, width=20, initial_step=10 self.modes1 = modes1 self.modes2 = modes2 self.width = width - self.padding = 2 # pad the domain if input is non-periodic - self.fc0 = nn.Linear(initial_step*num_channels+2, self.width) + self.padding = 2 # pad the domain if input is non-periodic + self.fc0 = nn.Linear(initial_step * num_channels + 2, self.width) # input channel is 12: the solution of the previous 10 timesteps + 2 locations (u(t-10, x, y), ..., u(t-1, x, y), x, y) - self.conv0 = SpectralConv2d_fast(self.width, self.width, self.modes1, self.modes2) - self.conv1 = SpectralConv2d_fast(self.width, self.width, self.modes1, self.modes2) - self.conv2 = SpectralConv2d_fast(self.width, self.width, self.modes1, self.modes2) - self.conv3 = SpectralConv2d_fast(self.width, self.width, self.modes1, self.modes2) + self.conv0 = SpectralConv2d_fast( + self.width, self.width, self.modes1, self.modes2 + ) + self.conv1 = SpectralConv2d_fast( + self.width, self.width, self.modes1, self.modes2 + ) + self.conv2 = SpectralConv2d_fast( + self.width, self.width, self.modes1, self.modes2 + ) + self.conv3 = SpectralConv2d_fast( + self.width, self.width, self.modes1, self.modes2 + ) self.w0 = nn.Conv2d(self.width, self.width, 1) self.w1 = nn.Conv2d(self.width, self.width, 1) self.w2 = nn.Conv2d(self.width, self.width, 1) @@ -214,7 +263,7 @@ def forward(self, x, grid): x = torch.cat((x, grid), dim=-1) x = self.fc0(x) x = x.permute(0, 3, 1, 2) - + # Pad tensor with boundary condition x = F.pad(x, [0, self.padding, 0, self.padding]) @@ -237,34 +286,77 @@ def forward(self, x, grid): x2 = self.w3(x) x = x1 + x2 - x = x[..., :-self.padding, :-self.padding] # Unpad the tensor + x = x[..., : -self.padding, : -self.padding] # Unpad the tensor x = x.permute(0, 2, 3, 1) x = self.fc1(x) x = F.gelu(x) x = self.fc2(x) - + return x.unsqueeze(-2) - + class SpectralConv3d(nn.Module): def __init__(self, in_channels, out_channels, modes1, modes2, modes3): super(SpectralConv3d, self).__init__() """ - 3D Fourier layer. It does FFT, linear transform, and Inverse FFT. + 3D Fourier layer. It does FFT, linear transform, and Inverse FFT. """ self.in_channels = in_channels self.out_channels = out_channels - self.modes1 = modes1 #Number of Fourier modes to multiply, at most floor(N/2) + 1 + self.modes1 = ( + # Number of Fourier modes to multiply, at most floor(N/2) + 1 + modes1 + ) self.modes2 = modes2 self.modes3 = modes3 - self.scale = (1 / (in_channels * out_channels)) - self.weights1 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, self.modes3, dtype=torch.cfloat)) - self.weights2 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, self.modes3, dtype=torch.cfloat)) - self.weights3 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, self.modes3, dtype=torch.cfloat)) - self.weights4 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, self.modes1, self.modes2, self.modes3, dtype=torch.cfloat)) + self.scale = 1 / (in_channels * out_channels) + self.weights1 = nn.Parameter( + self.scale + * torch.rand( + in_channels, + out_channels, + self.modes1, + self.modes2, + self.modes3, + dtype=torch.cfloat, + ) + ) + self.weights2 = nn.Parameter( + self.scale + * torch.rand( + in_channels, + out_channels, + self.modes1, + self.modes2, + self.modes3, + dtype=torch.cfloat, + ) + ) + self.weights3 = nn.Parameter( + self.scale + * torch.rand( + in_channels, + out_channels, + self.modes1, + self.modes2, + self.modes3, + dtype=torch.cfloat, + ) + ) + self.weights4 = nn.Parameter( + self.scale + * torch.rand( + in_channels, + out_channels, + self.modes1, + self.modes2, + self.modes3, + dtype=torch.cfloat, + ) + ) # Complex multiplication def compl_mul3d(self, input, weights): @@ -273,26 +365,41 @@ def compl_mul3d(self, input, weights): def forward(self, x): batchsize = x.shape[0] - #Compute Fourier coeffcients up to factor of e^(- something constant) - x_ft = torch.fft.rfftn(x, dim=[-3,-2,-1]) + # Compute Fourier coefficients up to factor of e^(- something constant) + x_ft = torch.fft.rfftn(x, dim=[-3, -2, -1]) # Multiply relevant Fourier modes - out_ft = torch.zeros(batchsize, self.out_channels, x.size(-3), x.size(-2), x.size(-1)//2 + 1, dtype=torch.cfloat, device=x.device) - out_ft[:, :, :self.modes1, :self.modes2, :self.modes3] = \ - self.compl_mul3d(x_ft[:, :, :self.modes1, :self.modes2, :self.modes3], self.weights1) - out_ft[:, :, -self.modes1:, :self.modes2, :self.modes3] = \ - self.compl_mul3d(x_ft[:, :, -self.modes1:, :self.modes2, :self.modes3], self.weights2) - out_ft[:, :, :self.modes1, -self.modes2:, :self.modes3] = \ - self.compl_mul3d(x_ft[:, :, :self.modes1, -self.modes2:, :self.modes3], self.weights3) - out_ft[:, :, -self.modes1:, -self.modes2:, :self.modes3] = \ - self.compl_mul3d(x_ft[:, :, -self.modes1:, -self.modes2:, :self.modes3], self.weights4) - - #Return to physical space + out_ft = torch.zeros( + batchsize, + self.out_channels, + x.size(-3), + x.size(-2), + x.size(-1) // 2 + 1, + dtype=torch.cfloat, + device=x.device, + ) + out_ft[:, :, : self.modes1, : self.modes2, : self.modes3] = self.compl_mul3d( + x_ft[:, :, : self.modes1, : self.modes2, : self.modes3], self.weights1 + ) + out_ft[:, :, -self.modes1 :, : self.modes2, : self.modes3] = self.compl_mul3d( + x_ft[:, :, -self.modes1 :, : self.modes2, : self.modes3], self.weights2 + ) + out_ft[:, :, : self.modes1, -self.modes2 :, : self.modes3] = self.compl_mul3d( + x_ft[:, :, : self.modes1, -self.modes2 :, : self.modes3], self.weights3 + ) + out_ft[:, :, -self.modes1 :, -self.modes2 :, : self.modes3] = self.compl_mul3d( + x_ft[:, :, -self.modes1 :, -self.modes2 :, : self.modes3], self.weights4 + ) + + # Return to physical space x = torch.fft.irfftn(out_ft, s=(x.size(-3), x.size(-2), x.size(-1))) return x + class FNO3d(nn.Module): - def __init__(self, num_channels, modes1=8, modes2=8, modes3=8, width=20, initial_step=10): + def __init__( + self, num_channels, modes1=8, modes2=8, modes3=8, width=20, initial_step=10 + ): super(FNO3d, self).__init__() """ @@ -301,7 +408,7 @@ def __init__(self, num_channels, modes1=8, modes2=8, modes3=8, width=20, initial 2. 4 layers of the integral operators u' = (W + K)(u). W defined by self.w; K defined by self.conv . 3. Project from the channel space to the output space by self.fc1 and self.fc2 . - + input: the solution of the first 10 timesteps + 3 locations (u(1, x, y), ..., u(10, x, y), x, y, t). It's a constant function in time, except for the last index. input shape: (batchsize, x=64, y=64, t=40, c=13) output: the solution of the next 40 timesteps @@ -312,14 +419,22 @@ def __init__(self, num_channels, modes1=8, modes2=8, modes3=8, width=20, initial self.modes2 = modes2 self.modes3 = modes3 self.width = width - self.padding = 6 # pad the domain if input is non-periodic - self.fc0 = nn.Linear(initial_step*num_channels+3, self.width) + self.padding = 6 # pad the domain if input is non-periodic + self.fc0 = nn.Linear(initial_step * num_channels + 3, self.width) # input channel is 12: the solution of the first 10 timesteps + 3 locations (u(1, x, y), ..., u(10, x, y), x, y, t) - self.conv0 = SpectralConv3d(self.width, self.width, self.modes1, self.modes2, self.modes3) - self.conv1 = SpectralConv3d(self.width, self.width, self.modes1, self.modes2, self.modes3) - self.conv2 = SpectralConv3d(self.width, self.width, self.modes1, self.modes2, self.modes3) - self.conv3 = SpectralConv3d(self.width, self.width, self.modes1, self.modes2, self.modes3) + self.conv0 = SpectralConv3d( + self.width, self.width, self.modes1, self.modes2, self.modes3 + ) + self.conv1 = SpectralConv3d( + self.width, self.width, self.modes1, self.modes2, self.modes3 + ) + self.conv2 = SpectralConv3d( + self.width, self.width, self.modes1, self.modes2, self.modes3 + ) + self.conv3 = SpectralConv3d( + self.width, self.width, self.modes1, self.modes2, self.modes3 + ) self.w0 = nn.Conv3d(self.width, self.width, 1) self.w1 = nn.Conv3d(self.width, self.width, 1) self.w2 = nn.Conv3d(self.width, self.width, 1) @@ -337,8 +452,9 @@ def forward(self, x, grid): x = torch.cat((x, grid), dim=-1) x = self.fc0(x) x = x.permute(0, 4, 1, 2, 3) - - x = F.pad(x, [0, self.padding]) # pad the domain if input is non-periodic + + # pad the domain if input is non-periodic + x = F.pad(x, [0, self.padding]) x1 = self.conv0(x) x2 = self.w0(x) @@ -359,9 +475,9 @@ def forward(self, x, grid): x2 = self.w3(x) x = x1 + x2 - x = x[..., :-self.padding] - x = x.permute(0, 2, 3, 4, 1) # pad the domain if input is non-periodic + x = x[..., : -self.padding] + x = x.permute(0, 2, 3, 4, 1) # pad the domain if input is non-periodic x = self.fc1(x) x = F.gelu(x) x = self.fc2(x) - return x.unsqueeze(-2) \ No newline at end of file + return x.unsqueeze(-2) diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index a3f06c1..25c40bf 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -1,182 +1,212 @@ -import sys -import torch -import numpy as np -import pickle -import torch.nn as nn -import torch.nn.functional as F - -import operator -from functools import reduce -from functools import partial +from __future__ import annotations +import pickle from timeit import default_timer +import numpy as np +import torch +from torch import nn + # torch.manual_seed(0) # np.random.seed(0) -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d -from pdebench.models.fno.utils import FNODatasetSingle, FNODatasetMult +from pdebench.models.fno.utils import FNODatasetMult, FNODatasetSingle from pdebench.models.metrics import metrics -def run_training(if_training, - continue_training, - num_workers, - modes, - width, - initial_step, - t_train, - num_channels, - batch_size, - epochs, - learning_rate, - scheduler_step, - scheduler_gamma, - model_update, - flnm, - single_file, - reduced_resolution, - reduced_resolution_t, - reduced_batch, - plot, - channel_plot, - x_min, - x_max, - y_min, - y_max, - t_min, - t_max, - base_path='../data/', - training_type='autoregressive' - ): - - print(f'Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}') - + +def run_training( + if_training, + continue_training, + num_workers, + modes, + width, + initial_step, + t_train, + num_channels, + batch_size, + epochs, + learning_rate, + scheduler_step, + scheduler_gamma, + model_update, + flnm, + single_file, + reduced_resolution, + reduced_resolution_t, + reduced_batch, + plot, + channel_plot, + x_min, + x_max, + y_min, + y_max, + t_min, + t_max, + base_path="../data/", + training_type="autoregressive", +): + print( + f"Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}" + ) + ################################################################ # load data ################################################################ - + if single_file: # filename - model_name = flnm[:-5] + '_FNO' + model_name = flnm[:-5] + "_FNO" print("FNODatasetSingle") # Initialize the dataset and dataloader - train_data = FNODatasetSingle(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - initial_step=initial_step, - saved_folder = base_path - ) - val_data = FNODatasetSingle(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - initial_step=initial_step, - if_test=True, - saved_folder = base_path - ) - + train_data = FNODatasetSingle( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + initial_step=initial_step, + saved_folder=base_path, + ) + val_data = FNODatasetSingle( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + initial_step=initial_step, + if_test=True, + saved_folder=base_path, + ) + else: # filename - model_name = flnm + '_FNO' - + model_name = flnm + "_FNO" + print("FNODatasetMult") - train_data = FNODatasetMult(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - saved_folder = base_path - ) - val_data = FNODatasetMult(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - if_test=True, - saved_folder = base_path) - - train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, - num_workers=num_workers, shuffle=True) - val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, - num_workers=num_workers, shuffle=False) - + train_data = FNODatasetMult( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + saved_folder=base_path, + ) + val_data = FNODatasetMult( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + if_test=True, + saved_folder=base_path, + ) + + train_loader = torch.utils.data.DataLoader( + train_data, batch_size=batch_size, num_workers=num_workers, shuffle=True + ) + val_loader = torch.utils.data.DataLoader( + val_data, batch_size=batch_size, num_workers=num_workers, shuffle=False + ) + ################################################################ # training and evaluation ################################################################ - + _, _data, _ = next(iter(val_loader)) dimensions = len(_data.shape) - print('Spatial Dimension', dimensions - 3) + print("Spatial Dimension", dimensions - 3) if dimensions == 4: - model = FNO1d(num_channels=num_channels, - width=width, - modes=modes, - initial_step=initial_step).to(device) + model = FNO1d( + num_channels=num_channels, + width=width, + modes=modes, + initial_step=initial_step, + ).to(device) elif dimensions == 5: - model = FNO2d(num_channels=num_channels, - width=width, - modes1=modes, - modes2=modes, - initial_step=initial_step).to(device) + model = FNO2d( + num_channels=num_channels, + width=width, + modes1=modes, + modes2=modes, + initial_step=initial_step, + ).to(device) elif dimensions == 6: - model = FNO3d(num_channels=num_channels, - width=width, - modes1=modes, - modes2=modes, - modes3=modes, - initial_step=initial_step).to(device) - + model = FNO3d( + num_channels=num_channels, + width=width, + modes1=modes, + modes2=modes, + modes3=modes, + initial_step=initial_step, + ).to(device) + # Set maximum time step of the data to train if t_train > _data.shape[-2]: t_train = _data.shape[-2] model_path = model_name + ".pt" - + total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - print(f'Total parameters = {total_params}') - - optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=1e-4) - scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=scheduler_step, gamma=scheduler_gamma) - + print(f"Total parameters = {total_params}") + + optimizer = torch.optim.Adam( + model.parameters(), lr=learning_rate, weight_decay=1e-4 + ) + scheduler = torch.optim.lr_scheduler.StepLR( + optimizer, step_size=scheduler_step, gamma=scheduler_gamma + ) + loss_fn = nn.MSELoss(reduction="mean") loss_val_min = np.infty - + start_epoch = 0 if not if_training: checkpoint = torch.load(model_path, map_location=device) - model.load_state_dict(checkpoint['model_state_dict']) + model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) model.eval() - Lx, Ly, Lz = 1., 1., 1. - errs = metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, - model_name, x_min, x_max, y_min, y_max, - t_min, t_max, initial_step=initial_step) - pickle.dump(errs, open(model_name+'.pickle', "wb")) - + Lx, Ly, Lz = 1.0, 1.0, 1.0 + errs = metrics( + val_loader, + model, + Lx, + Ly, + Lz, + plot, + channel_plot, + model_name, + x_min, + x_max, + y_min, + y_max, + t_min, + t_max, + initial_step=initial_step, + ) + pickle.dump(errs, open(model_name + ".pickle", "wb")) + return # If desired, restore the network by loading the weights saved in the .pt # file if continue_training: - print('Restoring model (that is the network\'s weights) from file...') + print("Restoring model (that is the network's weights) from file...") checkpoint = torch.load(model_path, map_location=device) - model.load_state_dict(checkpoint['model_state_dict']) + model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) model.train() - + # Load optimizer state dict - optimizer.load_state_dict(checkpoint['optimizer_state_dict']) + optimizer.load_state_dict(checkpoint["optimizer_state_dict"]) for state in optimizer.state.values(): for k, v in state.items(): if isinstance(v, torch.Tensor): state[k] = v.to(device) - - start_epoch = checkpoint['epoch'] - loss_val_min = checkpoint['loss'] - + + start_epoch = checkpoint["epoch"] + loss_val_min = checkpoint["loss"] + for ep in range(start_epoch, epochs): model.train() t1 = default_timer() @@ -184,7 +214,7 @@ def run_training(if_training, train_l2_full = 0 for xx, yy, grid in train_loader: loss = 0 - + # xx: input tensor (first few time steps) [b, x1, ..., xd, t_init, v] # yy: target tensor [b, x1, ..., xd, t, v] # grid: meshgrid [b, x1, ..., xd, dims] @@ -199,16 +229,15 @@ def run_training(if_training, inp_shape = list(xx.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - - if training_type in ['autoregressive']: + + if training_type in ["autoregressive"]: # Autoregressive loop for t in range(initial_step, t_train): - # Reshape input tensor into [b, x1, ..., xd, t_init*v] inp = xx.reshape(inp_shape) - + # Extract target at current time step - y = yy[..., t:t+1, :] + y = yy[..., t : t + 1, :] # Model run im = model(inp, grid) @@ -216,11 +245,11 @@ def run_training(if_training, # Loss calculation _batch = im.size(0) loss += loss_fn(im.reshape(_batch, -1), y.reshape(_batch, -1)) - + # Concatenate the prediction at current time step into the # prediction tensor pred = torch.cat((pred, im), -2) - + # Concatenate the prediction at the current time step to be used # as input for the next time step xx = torch.cat((xx[..., 1:, :], im), dim=-2) @@ -230,21 +259,21 @@ def run_training(if_training, _yy = yy[..., :t_train, :] # if t_train is not -1 l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() - + optimizer.zero_grad() loss.backward() optimizer.step() - if training_type in ['single']: - x = xx[..., 0 , :] - y = yy[..., t_train-1:t_train, :] + if training_type in ["single"]: + x = xx[..., 0, :] + y = yy[..., t_train - 1 : t_train, :] pred = model(x, grid) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) - + train_l2_step += loss.item() train_l2_full += loss.item() - + optimizer.zero_grad() loss.backward() optimizer.step() @@ -258,57 +287,65 @@ def run_training(if_training, xx = xx.to(device) yy = yy.to(device) grid = grid.to(device) - - if training_type in ['autoregressive']: + + if training_type in ["autoregressive"]: pred = yy[..., :initial_step, :] inp_shape = list(xx.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - + for t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) - y = yy[..., t:t+1, :] + y = yy[..., t : t + 1, :] im = model(inp, grid) _batch = im.size(0) - loss += loss_fn(im.reshape(_batch, -1), y.reshape(_batch, -1)) + loss += loss_fn( + im.reshape(_batch, -1), y.reshape(_batch, -1) + ) pred = torch.cat((pred, im), -2) - + xx = torch.cat((xx[..., 1:, :], im), dim=-2) - + val_l2_step += loss.item() _batch = yy.size(0) _pred = pred[..., initial_step:t_train, :] _yy = yy[..., initial_step:t_train, :] - val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() + val_l2_full += loss_fn( + _pred.reshape(_batch, -1), _yy.reshape(_batch, -1) + ).item() - if training_type in ['single']: - x = xx[..., 0 , :] - y = yy[..., t_train-1:t_train, :] + if training_type in ["single"]: + x = xx[..., 0, :] + y = yy[..., t_train - 1 : t_train, :] pred = model(x, grid) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) - + val_l2_step += loss.item() val_l2_full += loss.item() - - if val_l2_full < loss_val_min: + + if val_l2_full < loss_val_min: loss_val_min = val_l2_full - torch.save({ - 'epoch': ep, - 'model_state_dict': model.state_dict(), - 'optimizer_state_dict': optimizer.state_dict(), - 'loss': loss_val_min - }, model_path) - - + torch.save( + { + "epoch": ep, + "model_state_dict": model.state_dict(), + "optimizer_state_dict": optimizer.state_dict(), + "loss": loss_val_min, + }, + model_path, + ) + t2 = default_timer() scheduler.step() - print('epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}'\ - .format(ep, loss.item(), t2 - t1, train_l2_full, val_l2_full)) - + print( + "epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}".format( + ep, loss.item(), t2 - t1, train_l2_full, val_l2_full + ) + ) + + if __name__ == "__main__": - run_training() print("Done.") - diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 03f45eb..01b507e 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ @@ -147,29 +146,32 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import torch -from torch.utils.data import Dataset, IterableDataset -from torch.utils.data import DataLoader +import math as mt import os -import glob + import h5py import numpy as np -import math as mt +import torch +from torch.utils.data import Dataset + class FNODatasetSingle(Dataset): - def __init__(self, filename, - initial_step=10, - saved_folder='../data/', - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, - if_test=False, - test_ratio=0.1, - num_samples_max = -1 - ): + def __init__( + self, + filename, + initial_step=10, + saved_folder="../data/", + reduced_resolution=1, + reduced_resolution_t=1, + reduced_batch=1, + if_test=False, + test_ratio=0.1, + num_samples_max=-1, + ): """ - + :param filename: filename that contains the dataset :type filename: STR :param filenum: array containing indices of filename included in the dataset @@ -178,120 +180,218 @@ def __init__(self, filename, :type initial_step: INT, optional """ - + # Define path to files root_path = os.path.join(os.path.abspath(saved_folder), filename) - if filename[-2:] != 'h5': - print(f".HDF5 file extension is assumed hereafter") - - with h5py.File(root_path, 'r') as f: + if filename[-2:] != "h5": + print(".HDF5 file extension is assumed hereafter") + + with h5py.File(root_path, "r") as f: keys = list(f.keys()) keys.sort() - if 'tensor' not in keys: - _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... + if "tensor" not in keys: + _data = np.array( + f["density"], dtype=np.float32 + ) # batch, time, x,... idx_cfd = _data.shape - if len(idx_cfd)==3: # 1D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 3], - dtype=np.float32) - #density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + if len(idx_cfd) == 3: # 1D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 3, + ], + dtype=np.float32, + ) + # density + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = np.array( + f["Vx"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,2] = _data # batch, x, t, ch + self.data[..., 2] = _data # batch, x, t, ch self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + self.grid = torch.tensor( + self.grid[::reduced_resolution], dtype=torch.float + ).unsqueeze(-1) print(self.data.shape) - if len(idx_cfd)==4: # 2D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 4], - dtype=np.float32) + if len(idx_cfd) == 4: # 2D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + idx_cfd[3] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 4, + ], + dtype=np.float32, + ) # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["Vx"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,2] = _data # batch, x, t, ch + self.data[..., 2] = _data # batch, x, t, ch # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["Vy"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,3] = _data # batch, x, t, ch + self.data[..., 3] = _data # batch, x, t, ch x = np.array(f["x-coordinate"], dtype=np.float32) y = np.array(f["y-coordinate"], dtype=np.float32) x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y, indexing='ij') - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] - - if len(idx_cfd)==5: # 3D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - idx_cfd[4]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 5], - dtype=np.float32) + X, Y = torch.meshgrid(x, y, indexing="ij") + self.grid = torch.stack((X, Y), axis=-1)[ + ::reduced_resolution, ::reduced_resolution + ] + + if len(idx_cfd) == 5: # 3D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + idx_cfd[3] // reduced_resolution, + idx_cfd[4] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 5, + ], + dtype=np.float32, + ) # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["Vx"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,2] = _data # batch, x, t, ch + self.data[..., 2] = _data # batch, x, t, ch # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["Vy"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,3] = _data # batch, x, t, ch + self.data[..., 3] = _data # batch, x, t, ch # Vz - _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["Vz"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,4] = _data # batch, x, t, ch + self.data[..., 4] = _data # batch, x, t, ch x = np.array(f["x-coordinate"], dtype=np.float32) y = np.array(f["y-coordinate"], dtype=np.float32) @@ -299,33 +399,55 @@ def __init__(self, filename, x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z, indexing='ij') - self.grid = torch.stack((X, Y, Z), axis=-1)[::reduced_resolution,\ - ::reduced_resolution,\ - ::reduced_resolution] - + X, Y, Z = torch.meshgrid(x, y, z, indexing="ij") + self.grid = torch.stack((X, Y, Z), axis=-1)[ + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] + else: # scalar equations ## data dim = [t, x1, ..., xd, v] - _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... + _data = np.array( + f["tensor"], dtype=np.float32 + ) # batch, time, x,... if len(_data.shape) == 3: # 1D - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) self.data = _data[:, :, :, None] # batch, x, t, ch self.grid = np.array(f["x-coordinate"], dtype=np.float32) - self.grid = torch.tensor(self.grid[::reduced_resolution], dtype=torch.float).unsqueeze(-1) + self.grid = torch.tensor( + self.grid[::reduced_resolution], dtype=torch.float + ).unsqueeze(-1) if len(_data.shape) == 4: # 2D Darcy flow # u: label - _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, + :, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - #if _data.shape[-1]==1: # if nt==1 + # if _data.shape[-1]==1: # if nt==1 # _data = np.tile(_data, (1, 1, 1, 2)) self.data = _data # nu: input - _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["nu"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + None, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) self.data = np.concatenate([_data, self.data], axis=-1) @@ -335,31 +457,52 @@ def __init__(self, filename, y = np.array(f["y-coordinate"], dtype=np.float32) x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y, indexing='ij') - self.grid = torch.stack((X, Y), axis=-1)[::reduced_resolution, ::reduced_resolution] + X, Y = torch.meshgrid(x, y, indexing="ij") + self.grid = torch.stack((X, Y), axis=-1)[ + ::reduced_resolution, ::reduced_resolution + ] + + elif filename[-2:] == "h5": # SWE-2D (RDB) + print(".H5 file extension is assumed hereafter") - elif filename[-2:] == 'h5': # SWE-2D (RDB) - print(f".H5 file extension is assumed hereafter") - - with h5py.File(root_path, 'r') as f: + with h5py.File(root_path, "r") as f: keys = list(f.keys()) keys.sort() - data_arrays = [np.array(f[key]['data'], dtype=np.float32) for key in keys] - _data = torch.from_numpy(np.stack(data_arrays, axis=0)) # [batch, nt, nx, ny, nc] - _data = _data[::reduced_batch, ::reduced_resolution_t, ::reduced_resolution, ::reduced_resolution, ...] - _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] - gridx, gridy = np.array(f['0023']['grid']['x'], dtype=np.float32), np.array(f['0023']['grid']['y'], dtype=np.float32) - mgridX, mgridY = np.meshgrid(gridx, gridy, indexing='ij') - _grid = torch.stack((torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1) + + data_arrays = [ + np.array(f[key]["data"], dtype=np.float32) for key in keys + ] + _data = torch.from_numpy( + np.stack(data_arrays, axis=0) + ) # [batch, nt, nx, ny, nc] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ..., + ] + _data = torch.permute(_data, (0, 2, 3, 1, 4)) # [batch, nx, ny, nt, nc] + gridx, gridy = ( + np.array(f["0023"]["grid"]["x"], dtype=np.float32), + np.array(f["0023"]["grid"]["y"], dtype=np.float32), + ) + mgridX, mgridY = np.meshgrid(gridx, gridy, indexing="ij") + _grid = torch.stack( + (torch.from_numpy(mgridX), torch.from_numpy(mgridY)), axis=-1 + ) _grid = _grid[::reduced_resolution, ::reduced_resolution, ...] - _tsteps_t = torch.from_numpy(np.array(f['0023']['grid']['t'], dtype=np.float32)) + _tsteps_t = torch.from_numpy( + np.array(f["0023"]["grid"]["t"], dtype=np.float32) + ) + tsteps_t = _tsteps_t[::reduced_resolution_t] self.data = _data self.grid = _grid self.tsteps_t = tsteps_t - if num_samples_max>0: - num_samples_max = min(num_samples_max, self.data.shape[0]) + if num_samples_max > 0: + num_samples_max = min(num_samples_max, self.data.shape[0]) else: num_samples_max = self.data.shape[0] @@ -376,23 +519,25 @@ def __init__(self, filename, def __len__(self): return len(self.data) - + def __getitem__(self, idx): - - return self.data[idx,...,:self.initial_step,:], self.data[idx], self.grid + return self.data[idx, ..., : self.initial_step, :], self.data[idx], self.grid class FNODatasetMult(Dataset): - def __init__(self, filename, - initial_step=10, - saved_folder='../data/', - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, - if_test=False, test_ratio=0.1 - ): + def __init__( + self, + filename, + initial_step=10, + saved_folder="../data/", + reduced_resolution=1, + reduced_resolution_t=1, + reduced_batch=1, + if_test=False, + test_ratio=0.1, + ): """ - + :param filename: filename that contains the dataset :type filename: STR :param filenum: array containing indices of filename included in the dataset @@ -401,64 +546,63 @@ def __init__(self, filename, :type initial_step: INT, optional """ - + # Define path to files self.file_path = os.path.abspath(saved_folder + filename + ".h5") - + # Extract list of seeds - with h5py.File(self.file_path, 'r') as h5_file: + with h5py.File(self.file_path, "r") as h5_file: data_list = sorted(h5_file.keys()) - test_idx = int(len(data_list) * (1-test_ratio)) + test_idx = int(len(data_list) * (1 - test_ratio)) if if_test: self.data_list = np.array(data_list[test_idx:]) else: self.data_list = np.array(data_list[:test_idx]) - + # Time steps used as initial conditions self.initial_step = initial_step def __len__(self): return len(self.data_list) - + def __getitem__(self, idx): - # Open file and read data - with h5py.File(self.file_path, 'r') as h5_file: + with h5py.File(self.file_path, "r") as h5_file: seed_group = h5_file[self.data_list[idx]] - + # data dim = [t, x1, ..., xd, v] - data = np.array(seed_group["data"], dtype='f') + data = np.array(seed_group["data"], dtype="f") data = torch.tensor(data, dtype=torch.float) - + # convert to [x1, ..., xd, t, v] - permute_idx = list(range(1,len(data.shape)-1)) + permute_idx = list(range(1, len(data.shape) - 1)) permute_idx.extend(list([0, -1])) data = data.permute(permute_idx) - + # Extract spatial dimension of data - dim = len(data.shape) - 2 - + dim = len(data.shape) - 2 + # x, y and z are 1-D arrays # Convert the spatial coordinates to meshgrid if dim == 1: - grid = np.array(seed_group["grid"]["x"], dtype='f') + grid = np.array(seed_group["grid"]["x"], dtype="f") grid = torch.tensor(grid, dtype=torch.float).unsqueeze(-1) elif dim == 2: - x = np.array(seed_group["grid"]["x"], dtype='f') - y = np.array(seed_group["grid"]["y"], dtype='f') + x = np.array(seed_group["grid"]["x"], dtype="f") + y = np.array(seed_group["grid"]["y"], dtype="f") x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) - X, Y = torch.meshgrid(x, y, indexing='ij') - grid = torch.stack((X,Y),axis=-1) + X, Y = torch.meshgrid(x, y, indexing="ij") + grid = torch.stack((X, Y), axis=-1) elif dim == 3: - x = np.array(seed_group["grid"]["x"], dtype='f') - y = np.array(seed_group["grid"]["y"], dtype='f') - z = np.array(seed_group["grid"]["z"], dtype='f') + x = np.array(seed_group["grid"]["x"], dtype="f") + y = np.array(seed_group["grid"]["y"], dtype="f") + z = np.array(seed_group["grid"]["z"], dtype="f") x = torch.tensor(x, dtype=torch.float) y = torch.tensor(y, dtype=torch.float) z = torch.tensor(z, dtype=torch.float) - X, Y, Z = torch.meshgrid(x, y, z, indexing='ij') - grid = torch.stack((X,Y,Z),axis=-1) - - return data[...,:self.initial_step,:], data, grid + X, Y, Z = torch.meshgrid(x, y, z, indexing="ij") + grid = torch.stack((X, Y, Z), axis=-1) + + return data[..., : self.initial_step, :], data, grid diff --git a/pdebench/models/inverse/inverse.py b/pdebench/models/inverse/inverse.py index 44c4cff..bfe3e34 100644 --- a/pdebench/models/inverse/inverse.py +++ b/pdebench/models/inverse/inverse.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ File: inverse.py Authors: Francesco Alesiani (makoto.takamoto@neclab.eu) - Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) + Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. @@ -145,49 +144,45 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import sys +import pyro +import pyro.distributions as dist import torch -import numpy as np -import pickle -import torch.nn as nn import torch.nn.functional as F - -import operator -from functools import reduce -from functools import partial - -import pyro +from numpy import prod from pyro.nn import PyroModule, PyroSample -import pyro.distributions as dist -from pyro.infer import MCMC, NUTS -from pyro import poutine +from torch import nn class ElementStandardScaler: - def fit(self, x): - self.mean = x.mean() - self.std = x.std(unbiased=False) - def transform(self, x): - eps = 1e-20 - x = x - self.mean - x = x/(self.std + eps) - return x - def fit_transform(self, x): - self.fit(x) - return self.transform(x) + def fit(self, x): + self.mean = x.mean() + self.std = x.std(unbiased=False) + + def transform(self, x): + eps = 1e-20 + x = x - self.mean + x = x / (self.std + eps) + return x + + def fit_transform(self, x): + self.fit(x) + return self.transform(x) + class ProbRasterLatent(PyroModule): def __init__( - self, - process_predictor: "nn.Module", - dims = (256,256), - latent_dims = (16,16), - interpolation = "bilinear", - prior_scale = 0.01, - obs_scale = 0.01, - prior_std = 0.01, - device=None): + self, + process_predictor: nn.Module, + dims=(256, 256), + latent_dims=(16, 16), + interpolation="bilinear", + prior_scale=0.01, + obs_scale=0.01, + prior_std=0.01, + device=None, + ): super().__init__() self.dims = dims self.device = device @@ -200,64 +195,50 @@ def __init__( self.obs_scale = torch.tensor(obs_scale, device=self.device, dtype=torch.float) self.process_predictor = process_predictor process_predictor.train(False) - ## Do not fit the process predictor weights + # Do not fit the process predictor weights for param in self.process_predictor.parameters(): param.requires_grad = False - _m,_s = torch.tensor([0], device=self.device, dtype=torch.float), torch.tensor([self.prior_std], device=self.device, dtype=torch.float) - self.latent = PyroSample(dist.Normal(_m,_s).expand(latent_dims).to_event(2)) - print(self.latent_dims,self.dims) + _m, _s = ( + torch.tensor([0], device=self.device, dtype=torch.float), + torch.tensor([self.prior_std], device=self.device, dtype=torch.float), + ) + self.latent = PyroSample(dist.Normal(_m, _s).expand(latent_dims).to_event(2)) + print(self.latent_dims, self.dims) def get_latent(self): - if self.latent_dims==self.dims: + if self.latent_dims == self.dims: return self.latent.unsqueeze(0) # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( + l = F.interpolate( self.latent.unsqueeze(1), self.dims, mode=self.interpolation, - align_corners=False - ).squeeze(0) #squeeze/unsqueeze is because of weird interpolate semantics + align_corners=False, + ).squeeze(0) # squeeze/unsqueeze is because of weird interpolate semantics return l - def latent2source(self,latent): - if latent.shape==self.dims: + def latent2source(self, latent): + if latent.shape == self.dims: return latent.unsqueeze(0) # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( - latent.unsqueeze(1), - self.dims, - mode=self.interpolation, - align_corners=False - ).squeeze(0) #squeeze/unsqueeze is because of weird interpolate semantics + l = F.interpolate( + latent.unsqueeze(1), self.dims, mode=self.interpolation, align_corners=False + ).squeeze(0) # squeeze/unsqueeze is because of weird interpolate semantics return l def forward(self, grid, y=None): - #overwrite process predictor batch with my own latent + # overwrite process predictor batch with my own latent x = self.get_latent() # print("forward:x.shape,grid.shape=",x.shape,grid.shape) - mean = self.process_predictor(x.to(self.device),grid.to(self.device)) - o = pyro.sample( - "obs", dist.Normal(mean, self.obs_scale).to_event(2), - obs=y) - return o - - -import sys -import torch -import numpy as np -import pickle -import torch.nn as nn -import torch.nn.functional as F + mean = self.process_predictor(x.to(self.device), grid.to(self.device)) + o = pyro.sample("obs", dist.Normal(mean, self.obs_scale).to_event(2), obs=y) + return o -import operator -from functools import reduce -from functools import partial -from numpy import prod class InitialConditionInterp(nn.Module): """ InitialConditionInterp - Class for the inital conditions using interpoliation. Works for 1d,2d and 3d + Class for the initial conditions using interpoliation. Works for 1d,2d and 3d model_ic = InitialConditionInterp([16],[8]) model_ic = InitialConditionInterp([16,16],[8,8]) @@ -265,30 +246,31 @@ class InitialConditionInterp(nn.Module): June 2022, F.Alesiani """ + def __init__(self, dims, hidden_dim): super(InitialConditionInterp, self).__init__() self.spatial_dim = len(hidden_dim) - self.dims = [1]+dims if len(dims)==1 else dims + self.dims = [1] + dims if len(dims) == 1 else dims # self.dims = [1,1,1]+dims - self.hidden_dim = [1]+hidden_dim if len(hidden_dim)==1 else hidden_dim - self.interpolation = "bilinear" if len(hidden_dim)<3 else "trilinear" - self.scale = (1 / prod(hidden_dim)) - self.latent = nn.Parameter(self.scale * torch.rand(1, 1, *self.hidden_dim, dtype=torch.float)) + self.hidden_dim = [1] + hidden_dim if len(hidden_dim) == 1 else hidden_dim + self.interpolation = "bilinear" if len(hidden_dim) < 3 else "trilinear" + self.scale = 1 / prod(hidden_dim) + self.latent = nn.Parameter( + self.scale * torch.rand(1, 1, *self.hidden_dim, dtype=torch.float) + ) # print(self.latent.shape) - def latent2source(self,latent): - if latent.shape[2:]==self.dims: + def latent2source(self, latent): + if latent.shape[2:] == self.dims: return latent # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( - latent, - self.dims, - mode=self.interpolation, - align_corners=False - ) + l = F.interpolate( + latent, self.dims, mode=self.interpolation, align_corners=False + ) return l.view(self.dims) + def forward(self): x = self.latent2source(self.latent) if self.spatial_dim == 1: - x = x.squeeze(0) - return x \ No newline at end of file + x = x.squeeze(0) + return x diff --git a/pdebench/models/inverse/train.py b/pdebench/models/inverse/train.py index 30a2f0f..fa4b110 100644 --- a/pdebench/models/inverse/train.py +++ b/pdebench/models/inverse/train.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ File: inverse.py Authors: Francesco Alesiani (makoto.takamoto@neclab.eu) - Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) + Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. @@ -145,266 +144,310 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import sys -import torch -import numpy as np -import pickle -import torch.nn as nn -import torch.nn.functional as F - -import operator -from functools import reduce -from functools import partial - -import pyro -from pyro.nn import PyroModule, PyroSample -import pyro.distributions as dist -from pyro.infer import MCMC, NUTS -from pyro import poutine - +import logging from timeit import default_timer - -import sys, os import hydra +import pandas as pd +import torch from omegaconf import DictConfig -from omegaconf import OmegaConf -from omegaconf import open_dict - - -import pdebench as pde -from pdebench.models.fno.fno import FNO1d,FNO2d,FNO3d -from pdebench.models.fno.utils import FNODatasetSingle, FNODatasetMult - +from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d +from pdebench.models.fno.utils import FNODatasetSingle +from pdebench.models.inverse.inverse import ( + ElementStandardScaler, + InitialConditionInterp, + ProbRasterLatent, +) +from pdebench.models.metrics import inverse_metrics from pdebench.models.unet.unet import UNet1d, UNet2d, UNet3d -from pdebench.models.unet.utils import UNetDatasetSingle,UNetDatasetMult - -from pdebench.models import metrics -from pdebench.models.metrics import LpLoss,FftLpLoss,FftMseLoss,inverse_metrics -import pandas as pd - +from pdebench.models.unet.utils import UNetDatasetSingle +from pyro.infer import MCMC, NUTS +from torch import nn +from tqdm import tqdm -from pdebench.models.inverse.inverse import ProbRasterLatent, ElementStandardScaler, InitialConditionInterp -from pdebench.models.inverse.utils import plot_ic_solution_mcmc +logging.basicConfig(level=logging.INFO, filename=__name__) +logging.root.setLevel(logging.INFO) -from torch.distributions.normal import Normal -from tqdm import tqdm +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') -def load_model(model,model_path, device): +def load_model(model, model_path, device): checkpoint = torch.load(model_path, map_location=device) - model.load_state_dict(checkpoint['model_state_dict']) + model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) model.eval() return model -@hydra.main(config_path='../config', config_name='config') +@hydra.main(config_path="../config", config_name="config") def main(cfg: DictConfig): - print(cfg.args.filename) - print(cfg.args) - - # we use the test data - if cfg.args.model_name in ['FNO']: - inverse_data = FNODatasetSingle(cfg.args.filename, - saved_folder = cfg.args.base_path, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - initial_step=cfg.args.initial_step, - if_test=True, - num_samples_max = cfg.args.num_samples_max - ) - - _data, _, _ = next(iter(inverse_loader)) + logging.info(cfg.args.filename) + logging.info(cfg.args) + + # we use the test data + if cfg.args.model_name in ["FNO"]: + inverse_data = FNODatasetSingle( + cfg.args.filename, + saved_folder=cfg.args.base_path, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + initial_step=cfg.args.initial_step, + if_test=True, + num_samples_max=cfg.args.num_samples_max, + ) + + _data, _, _ = next(iter(inverse_data)) dimensions = len(_data.shape) spatial_dim = dimensions - 3 - if cfg.args.model_name in ['UNET','Unet']: - inverse_data = UNetDatasetSingle(cfg.args.filename, - saved_folder = cfg.args.base_path, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - initial_step=cfg.args.initial_step, - if_test=True, - num_samples_max = cfg.args.num_samples_max) - - inverse_loader = torch.utils.data.DataLoader(inverse_data, batch_size=1,shuffle=False) - _data, _ = next(iter(inverse_loader)) + if cfg.args.model_name in ["UNET", "Unet"]: + inverse_data = UNetDatasetSingle( + cfg.args.filename, + saved_folder=cfg.args.base_path, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + initial_step=cfg.args.initial_step, + if_test=True, + num_samples_max=cfg.args.num_samples_max, + ) + + inverse_loader = torch.utils.data.DataLoader( + inverse_data, batch_size=1, shuffle=False + ) + _data, _ = next(iter(inverse_loader)) dimensions = len(_data.shape) spatial_dim = dimensions - 3 - initial_step = cfg.args.initial_step t_train = cfg.args.t_train - - model_name = cfg.args.filename[:-5] + '_' + cfg.args.model_name + + model_name = cfg.args.filename[:-5] + "_" + cfg.args.model_name model_path = cfg.args.base_path + model_name + ".pt" - if cfg.args.model_name in ['FNO']: + if cfg.args.model_name in ["FNO"]: if dimensions == 4: - print(cfg.args.num_channels) - model = FNO1d(num_channels=cfg.args.num_channels, - width=cfg.args.width, - modes=cfg.args.modes, - initial_step=cfg.args.initial_step).to(device) + logging.info(cfg.args.num_channels) + model = FNO1d( + num_channels=cfg.args.num_channels, + width=cfg.args.width, + modes=cfg.args.modes, + initial_step=cfg.args.initial_step, + ).to(device) if dimensions == 5: - model = FNO2d(num_channels=cfg.args.num_channels, - width=cfg.args.width, - modes1=cfg.args.modes, - modes2=cfg.args.modes, - initial_step=cfg.args.initial_step).to(device) + model = FNO2d( + num_channels=cfg.args.num_channels, + width=cfg.args.width, + modes1=cfg.args.modes, + modes2=cfg.args.modes, + initial_step=cfg.args.initial_step, + ).to(device) if dimensions == 6: - model = FNO3d(num_channels=cfg.args.num_channels, - width=cfg.args.width, - modes1=cfg.args.modes, - modes2=cfg.args.modes, - modes3=cfg.args.modes, - initial_step=cfg.args.initial_step).to(device) - - if cfg.args.model_name in ['UNET','Unet']: + model = FNO3d( + num_channels=cfg.args.num_channels, + width=cfg.args.width, + modes1=cfg.args.modes, + modes2=cfg.args.modes, + modes3=cfg.args.modes, + initial_step=cfg.args.initial_step, + ).to(device) + + if cfg.args.model_name in ["UNET", "Unet"]: if dimensions == 4: model = UNet1d(cfg.args.in_channels, cfg.args.out_channels).to(device) elif dimensions == 5: model = UNet2d(cfg.args.in_channels, cfg.args.out_channels).to(device) elif dimensions == 6: - model = UNet3d(cfg.args.in_channels, cfg.args.out_channels).to(device) + model = UNet3d(cfg.args.in_channels, cfg.args.out_channels).to(device) - model = load_model(model,model_path, device) + model = load_model(model, model_path, device) model.eval() - if cfg.args.inverse_model_type in ['ProbRasterLatent']: - assert(spatial_dim==1), "give me time" - if spatial_dim==1: - ns,nx,nt,nc = _data.shape + if cfg.args.inverse_model_type in ["ProbRasterLatent"]: + assert spatial_dim == 1, "give me time" + if spatial_dim == 1: + ns, nx, nt, nc = _data.shape model_inverse = ProbRasterLatent( model.to(device), - dims=[nx,1], - latent_dims = [1,cfg.args.in_channels_hid,1], - prior_scale = 0.1, - obs_scale = 0.01, - prior_std = 0.01, - device=device - ) - - if cfg.args.inverse_model_type in ['InitialConditionInterp']: + dims=[nx, 1], + latent_dims=[1, cfg.args.in_channels_hid, 1], + prior_scale=0.1, + obs_scale=0.01, + prior_std=0.01, + device=device, + ) + + if cfg.args.inverse_model_type in ["InitialConditionInterp"]: loss_fn = nn.MSELoss(reduction="mean") - input_dims = list(_data.shape[1:1+spatial_dim]) - latent_dims = len(input_dims)*[cfg.args.in_channels_hid] - if cfg.args.num_channels> 1: - input_dims=input_dims+[cfg.args.num_channels] - latent_dims=latent_dims+[cfg.args.num_channels] - print(input_dims,latent_dims) - model_ic = InitialConditionInterp(input_dims,latent_dims).to(device) - model.to(device) + input_dims = list(_data.shape[1 : 1 + spatial_dim]) + latent_dims = len(input_dims) * [cfg.args.in_channels_hid] + if cfg.args.num_channels > 1: + input_dims = [*input_dims, cfg.args.num_channels] + latent_dims = [*latent_dims, cfg.args.num_channels] + model_ic = InitialConditionInterp(input_dims, latent_dims).to(device) + model.to(device) scaler = ElementStandardScaler() loss_fn = nn.MSELoss(reduction="mean") - inverse_u0_l2_full,inverse_y_l2_full = 0,0 + inverse_u0_l2_full, inverse_y_l2_full = 0, 0 all_metric = [] t1 = default_timer() - for ks,sample in enumerate(inverse_loader): - if cfg.args.model_name in ['FNO']: + for ks, sample in enumerate(inverse_loader): + if cfg.args.model_name in ["FNO"]: (xx, yy, grid) = sample xx = xx.to(device) yy = yy.to(device) grid = grid.to(device) - model_ = lambda x, grid: model(x,grid) - if cfg.args.model_name in ['UNET','Unet']: + def model_(x, grid): + return model(x, grid) + + if cfg.args.model_name in ["UNET", "Unet"]: (xx, yy) = sample grid = None xx = xx.to(device) yy = yy.to(device) - model_ = lambda x, grid: model(x.permute([0, 2, 1])).permute([0, 2, 1]) - num_samples = ks + 1 - loss = 0 + def model_(x, grid): + return model(x.permute([0, 2, 1])).permute([0, 2, 1]) + num_samples = ks + 1 - x = xx[..., 0 , :] - y = yy[..., t_train:t_train+1 , :] + x = xx[..., 0, :] + y = yy[..., t_train : t_train + 1, :] - if ks==0: - print(x.shape,y.shape) + if ks == 0: + msg = f"{x.shape}, {y.shape}" + logging.info(msg) - #scale the input and output + # scale the input and output x = scaler.fit_transform(x) y = scaler.transform(y) - if cfg.args.inverse_model_type in ['ProbRasterLatent']: - #Create model + if cfg.args.inverse_model_type in ["ProbRasterLatent"]: + # Create model model_inverse.to(device) - nuts_kernel = NUTS(model_inverse, full_mass=False, max_tree_depth=5, jit_compile=True) # high performacne config - - mcmc = MCMC(nuts_kernel, num_samples=cfg.args.mcmc_num_samples, warmup_steps=cfg.args.mcmc_warmup_steps, num_chains=cfg.args.mcmc_num_chains,disable_progbar=True) + nuts_kernel = NUTS( + model_inverse, full_mass=False, max_tree_depth=5, jit_compile=True + ) # high performacne config + + mcmc = MCMC( + nuts_kernel, + num_samples=cfg.args.mcmc_num_samples, + warmup_steps=cfg.args.mcmc_warmup_steps, + num_chains=cfg.args.mcmc_num_chains, + disable_progbar=True, + ) mcmc.run(grid, y) - mc_samples = {k: v.detach().cpu().numpy() for k, v in mcmc.get_samples().items()} + mc_samples = { + k: v.detach().cpu().numpy() for k, v in mcmc.get_samples().items() + } # get the initial solution - latent = torch.tensor(mc_samples['latent']) + latent = torch.tensor(mc_samples["latent"]) u0 = model_inverse.latent2source(latent[0]).to(device) pred_u0 = model(u0, grid) - if cfg.args.inverse_model_type in ['InitialConditionInterp']: - optimizer = torch.optim.Adam(model_ic.parameters(), lr=cfg.args.inverse_learning_rate, weight_decay=1e-4) + if cfg.args.inverse_model_type in ["InitialConditionInterp"]: + optimizer = torch.optim.Adam( + model_ic.parameters(), + lr=cfg.args.inverse_learning_rate, + weight_decay=1e-4, + ) # scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=scheduler_step, gamma=scheduler_gamma) if cfg.args.inverse_verbose_flag: _iter = tqdm(range(cfg.args.inverse_epochs)) else: _iter = range(cfg.args.inverse_epochs) - for epoch in _iter: - if cfg.args.num_channels>1: + for _ in _iter: + if cfg.args.num_channels > 1: u0 = model_ic().unsqueeze(0) else: u0 = model_ic().unsqueeze(0).unsqueeze(-1) - - pred_u0 = model_(u0,grid) - - loss_u0 = loss_fn(pred_u0,y) + + pred_u0 = model_(u0, grid) + + loss_u0 = loss_fn(pred_u0, y) optimizer.zero_grad() loss_u0.backward() optimizer.step() t2 = default_timer() if cfg.args.inverse_verbose_flag: - _iter.set_description(f"loss={loss_u0.item()}, t2-t1= {t2-t1}") + _iter.set_description(f"loss={loss_u0.item()}, t2-t1= {t2-t1}") - #compute losses + # compute losses loss_u0 = loss_fn(u0.reshape(1, -1), x.reshape(1, -1)).item() loss_y = loss_fn(pred_u0.reshape(1, -1), y.reshape(1, -1)).item() inverse_u0_l2_full += loss_u0 inverse_y_l2_full += loss_y - metric = inverse_metrics(u0,x,pred_u0,y) - metric['sample'] = ks + metric = inverse_metrics(u0, x, pred_u0, y) + metric["sample"] = ks + + all_metric += [metric] - all_metric+=[metric] - t2 = default_timer() - print('samples: {}, loss_u0: {:.5f},loss_y: {:.5f}, t2-t1: {:.5f}, mse_inverse_u0_L2: {:.5f}, mse_inverse_y_L2: {:.5f}'\ - .format(ks+1, loss_u0, loss_y, t2 - t1, inverse_u0_l2_full/num_samples, inverse_y_l2_full/num_samples)) + msg = ", ".join( + [ + f"samples: {ks + 1}", + f"loss_u0: {loss_u0:.5f}", + f"loss_y: {loss_y:.5f}", + f"t2-t1: {t2 - t1:.5f}", + f"mse_inverse_u0_L2: {inverse_u0_l2_full / num_samples:.5f}", + f"mse_inverse_y_L2: {inverse_y_l2_full / num_samples:.5f}", + ] + ) + logging.info(msg) df_metric = pd.DataFrame(all_metric) - inverse_metric_filename = cfg.args.base_path + cfg.args.filename[:-5] + '_' + cfg.args.model_name +'_'+cfg.args.inverse_model_type + ".csv" - print("saving in :", inverse_metric_filename) + inverse_metric_filename = ( + cfg.args.base_path + + cfg.args.filename[:-5] + + "_" + + cfg.args.model_name + + "_" + + cfg.args.inverse_model_type + + ".csv" + ) + msg = f"saving in : {inverse_metric_filename}" + logging.info(msg) df_metric.to_csv(inverse_metric_filename) - inverse_metric_filename = cfg.args.base_path + cfg.args.filename[:-5] + '_' + cfg.args.model_name +'_'+cfg.args.inverse_model_type+ ".pickle" - print("saving in :", inverse_metric_filename) + inverse_metric_filename = ( + cfg.args.base_path + + cfg.args.filename[:-5] + + "_" + + cfg.args.model_name + + "_" + + cfg.args.inverse_model_type + + ".pickle" + ) + msg = f"saving in : {inverse_metric_filename}" + logging.info(msg) df_metric.to_pickle(inverse_metric_filename) - inverse_metric_filename = cfg.args.base_path + cfg.args.filename[:-5] + '_' + cfg.args.model_name +'_'+cfg.args.inverse_model_type+ "_stats.csv" - print("saving in :", inverse_metric_filename) + inverse_metric_filename = ( + cfg.args.base_path + + cfg.args.filename[:-5] + + "_" + + cfg.args.model_name + + "_" + + cfg.args.inverse_model_type + + "_stats.csv" + ) + msg = f"saving in : {inverse_metric_filename}" + logging.info(msg) df_metric = df_metric.describe() df_metric.to_csv(inverse_metric_filename) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/pdebench/models/inverse/utils.py b/pdebench/models/inverse/utils.py index 7e4f0ce..2e3a74d 100644 --- a/pdebench/models/inverse/utils.py +++ b/pdebench/models/inverse/utils.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- """ File: utils.py Authors: Francesco Alesiani (makoto.takamoto@neclab.eu) - Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) + Dan MacKinlay (Dan.MacKinlay@data61.csiro.au) NEC Laboratories Europe GmbH, Copyright (c) , All rights reserved. @@ -145,73 +144,93 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations +import logging +import hydra import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from omegaconf import DictConfig +from scipy.signal import welch + +logging.basicConfig(level=logging.INFO, filename=__name__) +logging.root.setLevel(logging.INFO) -def plot_ic_solution_mcmc(latent,x,y,grid,model_inverse,model,device,fname_save="IC_inverse_problem_mcmc.pdf"): + +def plot_ic_solution_mcmc( + latent, + x, + y, + grid, + model_inverse, + model, + device, + fname_save="IC_inverse_problem_mcmc.pdf", +): """ Plots the prediction of the initial condition estimated using MCMC from the latent with the model "model" y = model(x) - y[i] = model(latent[i]), i =0, ... + y[i] = model(latent[i]), i =0, ... June 2022, F.Alesiani - """ - fig, axes = plt.subplots(1,2,figsize=(15,7)) - ax = axes[0] + """ + fig, axes = plt.subplots(1, 2, figsize=(15, 7)) + ax = axes[0] u0 = model_inverse.latent2source(latent[0]).to(device) pred_u0 = model(u0, grid) - ax.plot(u0.detach().cpu().flatten(),'r',label="Predicted Initial Condition") + ax.plot(u0.detach().cpu().flatten(), "r", label="Predicted Initial Condition") for _latent in latent: u0 = model_inverse.latent2source(_latent).to(device) - ax.plot(u0.detach().cpu().flatten(),'r',alpha=0.1) - ax.plot(x.detach().cpu().flatten(),'b--',label="True Initial Condition") + ax.plot(u0.detach().cpu().flatten(), "r", alpha=0.1) + ax.plot(x.detach().cpu().flatten(), "b--", label="True Initial Condition") ax.legend() # plt.show() - ax = axes[1] - ax.plot(pred_u0.detach().cpu().flatten(),'r',label="Predicted forward value") - ax.plot(y.detach().cpu().flatten(),'b--',label="True forward value") + ax = axes[1] + ax.plot(pred_u0.detach().cpu().flatten(), "r", label="Predicted forward value") + ax.plot(y.detach().cpu().flatten(), "b--", label="True forward value") for _latent in latent: u0 = model_inverse.latent2source(_latent).to(device) pred_u0 = model(u0, grid) - ax.plot(pred_u0.detach().cpu().flatten(),'r',alpha=0.1) + ax.plot(pred_u0.detach().cpu().flatten(), "r", alpha=0.1) ax.legend() if fname_save: - plt.savefig(fname_save, bbox_inches='tight') - + plt.savefig(fname_save, bbox_inches="tight") -def plot_ic_solution_grad(model_ic,x,y,grid,model,device,fname_save="IC_inverse_problem_grad.pdf"): +def plot_ic_solution_grad( + model_ic, x, y, grid, model, device, fname_save="IC_inverse_problem_grad.pdf" +): """ Plots the prediction of the initial condition estimated using model_ic with the model "model" y = model(x) y' = model(model_ic()) June 2022, F.Alesiani - """ + """ - fig, axes = plt.subplots(1,2,figsize=(15,7)) - ax = axes[0] + fig, axes = plt.subplots(1, 2, figsize=(15, 7)) + ax = axes[0] u0 = model_ic().to(device).unsqueeze(0).unsqueeze(-1) pred_u0 = model(u0, grid) - ax.plot(u0.detach().cpu().flatten(),'r',label="Predicted Initial Condition") - ax.plot(x.detach().cpu().flatten(),'b--',label="True Initial Condition") + ax.plot(u0.detach().cpu().flatten(), "r", label="Predicted Initial Condition") + ax.plot(x.detach().cpu().flatten(), "b--", label="True Initial Condition") ax.legend() # plt.show() - ax = axes[1] - ax.plot(pred_u0.detach().cpu().flatten(),'r',label="Predicted forward value") - ax.plot(y.detach().cpu().flatten(),'b--',label="True forward value") + ax = axes[1] + ax.plot(pred_u0.detach().cpu().flatten(), "r", label="Predicted forward value") + ax.plot(y.detach().cpu().flatten(), "b--", label="True forward value") ax.legend() if fname_save: - plt.savefig(fname_save, bbox_inches='tight') + plt.savefig(fname_save, bbox_inches="tight") -from scipy.signal import welch -import matplotlib.pyplot as plt - -def plot_ic_solution_grad_psd(model_ic,x,y,grid,model,device,fname_save="IC_inverse_problem_grad_psd.pdf"): +def plot_ic_solution_grad_psd( + model_ic, x, y, grid, model, device, fname_save="IC_inverse_problem_grad_psd.pdf" +): """ Plots the prediction of the initial condition estimated using model_ic with the model "model" y = model(x) @@ -219,19 +238,19 @@ def plot_ic_solution_grad_psd(model_ic,x,y,grid,model,device,fname_save="IC_inve It also shows the power density June 2022, F.Alesiani - """ - fig, axes = plt.subplots(1,3,figsize=(22,7)) - ax = axes[0] + """ + fig, axes = plt.subplots(1, 3, figsize=(22, 7)) + ax = axes[0] u0 = model_ic().to(device).unsqueeze(0).unsqueeze(-1) pred_u0 = model(u0, grid) - ax.plot(u0.detach().cpu().flatten(),'r',label="Predicted Initial Condition") - ax.plot(x.detach().cpu().flatten(),'b--',label="True Initial Condition") + ax.plot(u0.detach().cpu().flatten(), "r", label="Predicted Initial Condition") + ax.plot(x.detach().cpu().flatten(), "b--", label="True Initial Condition") ax.legend() # plt.show() - ax = axes[1] - ax.plot(pred_u0.detach().cpu().flatten(),'r',label="Predicted forward value") - ax.plot(y.detach().cpu().flatten(),'b--',label="True forward value") + ax = axes[1] + ax.plot(pred_u0.detach().cpu().flatten(), "r", label="Predicted forward value") + ax.plot(y.detach().cpu().flatten(), "b--", label="True forward value") ax.legend() _u0 = u0.detach().cpu().flatten() @@ -239,76 +258,90 @@ def plot_ic_solution_grad_psd(model_ic,x,y,grid,model,device,fname_save="IC_inve fz = u0.shape[1] - fu,puu = welch(_u0,fz) - fx,pxx = welch(_x,fz) + fu, puu = welch(_u0, fz) + fx, pxx = welch(_x, fz) - ax = axes[2] - ax.semilogy(fu,puu,'r',label="predicted u0") - ax.semilogy(fx,pxx,'b--',label="x true") - ax.set_xlabel('spatial frequency') - ax.set_ylabel('PSD') + ax = axes[2] + ax.semilogy(fu, puu, "r", label="predicted u0") + ax.semilogy(fx, pxx, "b--", label="x true") + ax.set_xlabel("spatial frequency") + ax.set_ylabel("PSD") ax.legend() if fname_save: - plt.savefig(fname_save, bbox_inches='tight') - + plt.savefig(fname_save, bbox_inches="tight") -import sys, os -import hydra -from omegaconf import DictConfig -from omegaconf import OmegaConf -from omegaconf import open_dict -import pandas as pd -import numpy as np - - -def get_metric_name(filename,model_name, base_path,inverse_model_type): +def get_metric_name(filename, model_name, base_path, inverse_model_type): """ returns the name convention for the result file June 2022, F.Alesiani """ - inverse_metric_filename = base_path + filename[:-5] + '_' + model_name +'_'+ inverse_model_type + ".pickle" - return inverse_metric_filename - -def read_results(model_names,inverse_model_type, base_path, filenames,shortfilenames, verbose=False): + return ( + base_path + + filename[:-5] + + "_" + + model_name + + "_" + + inverse_model_type + + ".pickle" + ) + + +def read_results( + model_names, inverse_model_type, base_path, filenames, shortfilenames, verbose=False +): """ - reads and merges the result files. + reads and merges the result files. Shortnames are used for the name of the dataset as alternative to the file name. June 2022, F.Alesiani """ dfs = [] for model_name in model_names: - for filename,shortfilename in zip(filenames,shortfilenames): + for filename, shortfilename in zip(filenames, shortfilenames): # print(filename) - inverse_metric_filename = get_metric_name(filename,model_name, base_path,inverse_model_type) - if verbose: print ("reading resul file: ",inverse_metric_filename) - df = pd.read_pickle(inverse_metric_filename) - df['model'] = model_name - df['pde'] = shortfilename - dfs+=[df] - keys = ['pde','model'] - df = pd.concat(dfs,axis=0) - return df, keys - -@hydra.main(config_path='../config', config_name='results') + inverse_metric_filename = get_metric_name( + filename, model_name, base_path, inverse_model_type + ) + if verbose: + msg = f"reading result file: {inverse_metric_filename}" + logging.info(msg) + + dframe = pd.read_pickle(inverse_metric_filename) + dframe["model"] = model_name + dframe["pde"] = shortfilename + dfs += [dframe] + keys = ["pde", "model"] + dframe = pd.concat(dfs, axis=0) + return dframe, keys + + +@hydra.main(config_path="../config", config_name="results") def process_results(cfg: DictConfig): """ - reads and merges the result files and aggregate the results with the selected values. The results are aggregated by datafile. + reads and merges the result files and aggregate the results with the selected values. The results are aggregated by datafile. June 2022, F.Alesiani - """ - print(cfg.args) - - df, keys = read_results(cfg.args.model_names,cfg.args.inverse_model_type, cfg.args.base_path, cfg.args.filenames, cfg.args.shortfilenames) + """ + logging.info(cfg.args) + + df, keys = read_results( + cfg.args.model_names, + cfg.args.inverse_model_type, + cfg.args.base_path, + cfg.args.filenames, + cfg.args.shortfilenames, + ) df1p3 = df[keys + list(cfg.args.results_values)] - df2p3 = df1p3.groupby(by=keys).agg([np.mean,np.std]).reset_index() - print("saving results into: ", cfg.args.base_path + cfg.args.result_filename) - df2p3.to_csv(cfg.args.base_path + cfg.args.result_filename) + df2p3 = df1p3.groupby(by=keys).agg([np.mean, np.std]).reset_index() + msg = "saving results into: {cfg.args.base_path + cfg.args.result_filename}" + logging.info(msg) + df2p3.to_csv(cfg.args.base_path + cfg.args.result_filename) if __name__ == "__main__": process_results() - print("Done.") \ No newline at end of file + msg = "Done." + logging.info(msg) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 11697c8..913faab 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ @@ -145,16 +144,20 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import torch -import numpy as np import math as mt + import matplotlib.pyplot as plt +import numpy as np +import torch from mpl_toolkits.axes_grid1 import make_axes_locatable +from torch import nn + +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') -def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=12): +def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12): """ code for calculate metrics discussed in the Brain-storming session RMSE, normalized RMSE, max error, RMSE at the boundaries, conserved variables, RMSE in Fourier space, temporal sensitivity @@ -175,14 +178,25 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 nb, nc, nt = idxs[0], idxs[1], idxs[-1] # RMSE - err_mean = torch.sqrt(torch.mean((pred.view([nb, nc, -1, nt]) - target.view([nb, nc, -1, nt])) ** 2, dim=2)) + err_mean = torch.sqrt( + torch.mean( + (pred.view([nb, nc, -1, nt]) - target.view([nb, nc, -1, nt])) ** 2, dim=2 + ) + ) err_RMSE = torch.mean(err_mean, axis=0) nrm = torch.sqrt(torch.mean(target.view([nb, nc, -1, nt]) ** 2, dim=2)) err_nRMSE = torch.mean(err_mean / nrm, dim=0) - err_CSV = torch.sqrt(torch.mean( - (torch.sum(pred.view([nb, nc, -1, nt]), dim=2) - torch.sum(target.view([nb, nc, -1, nt]), dim=2)) ** 2, - dim=0)) + err_CSV = torch.sqrt( + torch.mean( + ( + torch.sum(pred.view([nb, nc, -1, nt]), dim=2) + - torch.sum(target.view([nb, nc, -1, nt]), dim=2) + ) + ** 2, + dim=0, + ) + ) if len(idxs) == 4: nx = idxs[2] err_CSV /= nx @@ -193,20 +207,27 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 nx, ny, nz = idxs[2:5] err_CSV /= nx * ny * nz # worst case in all the data - err_Max = torch.max(torch.max( - torch.abs(pred.view([nb, nc, -1, nt]) - target.view([nb, nc, -1, nt])), dim=2)[0], dim=0)[0] + err_Max = torch.max( + torch.max( + torch.abs(pred.view([nb, nc, -1, nt]) - target.view([nb, nc, -1, nt])), + dim=2, + )[0], + dim=0, + )[0] if len(idxs) == 4: # 1D err_BD = (pred[:, :, 0, :] - target[:, :, 0, :]) ** 2 err_BD += (pred[:, :, -1, :] - target[:, :, -1, :]) ** 2 - err_BD = torch.mean(torch.sqrt(err_BD / 2.), dim=0) + err_BD = torch.mean(torch.sqrt(err_BD / 2.0), dim=0) elif len(idxs) == 5: # 2D nx, ny = idxs[2:4] err_BD_x = (pred[:, :, 0, :, :] - target[:, :, 0, :, :]) ** 2 err_BD_x += (pred[:, :, -1, :, :] - target[:, :, -1, :, :]) ** 2 err_BD_y = (pred[:, :, :, 0, :] - target[:, :, :, 0, :]) ** 2 err_BD_y += (pred[:, :, :, -1, :] - target[:, :, :, -1, :]) ** 2 - err_BD = (torch.sum(err_BD_x, dim=-2) + torch.sum(err_BD_y, dim=-2)) / (2 * nx + 2 * ny) + err_BD = (torch.sum(err_BD_x, dim=-2) + torch.sum(err_BD_y, dim=-2)) / ( + 2 * nx + 2 * ny + ) err_BD = torch.mean(torch.sqrt(err_BD), dim=0) elif len(idxs) == 6: # 3D nx, ny, nz = idxs[2:5] @@ -216,9 +237,11 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 err_BD_y += (pred[:, :, :, -1, :] - target[:, :, :, -1, :]) ** 2 err_BD_z = (pred[:, :, :, :, 0] - target[:, :, :, :, 0]) ** 2 err_BD_z += (pred[:, :, :, :, -1] - target[:, :, :, :, -1]) ** 2 - err_BD = torch.sum(err_BD_x.view([nb, -1, nt]), dim=-2) \ - + torch.sum(err_BD_y.view([nb, -1, nt]), dim=-2) \ - + torch.sum(err_BD_z.view([nb, -1, nt]), dim=-2) + err_BD = ( + torch.sum(err_BD_x.view([nb, -1, nt]), dim=-2) + + torch.sum(err_BD_y.view([nb, -1, nt]), dim=-2) + + torch.sum(err_BD_z.view([nb, -1, nt]), dim=-2) + ) err_BD = err_BD / (2 * nx * ny + 2 * ny * nz + 2 * nz * nx) err_BD = torch.mean(torch.sqrt(err_BD), dim=0) @@ -226,7 +249,9 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 nx = idxs[2] pred_F = torch.fft.rfft(pred, dim=2) target_F = torch.fft.rfft(target, dim=2) - _err_F = torch.sqrt(torch.mean(torch.abs(pred_F - target_F) ** 2, axis=0)) / nx * Lx + _err_F = ( + torch.sqrt(torch.mean(torch.abs(pred_F - target_F) ** 2, axis=0)) / nx * Lx + ) if len(idxs) == 5: # 2D pred_F = torch.fft.fftn(pred, dim=[2, 3]) target_F = torch.fft.fftn(target, dim=[2, 3]) @@ -235,7 +260,7 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 err_F = torch.zeros([nb, nc, min(nx // 2, ny // 2), nt]).to(device) for i in range(nx // 2): for j in range(ny // 2): - it = mt.floor(mt.sqrt(i ** 2 + j ** 2)) + it = mt.floor(mt.sqrt(i**2 + j**2)) if it > min(nx // 2, ny // 2) - 1: continue err_F[:, :, it] += _err_F[:, :, i, j] @@ -249,30 +274,49 @@ def metric_func(pred, target, if_mean=True, Lx=1., Ly=1., Lz=1., iLow=4, iHigh=1 for i in range(nx // 2): for j in range(ny // 2): for k in range(nz // 2): - it = mt.floor(mt.sqrt(i ** 2 + j ** 2 + k ** 2)) + it = mt.floor(mt.sqrt(i**2 + j**2 + k**2)) if it > min(nx // 2, ny // 2, nz // 2) - 1: continue err_F[:, :, it] += _err_F[:, :, i, j, k] _err_F = torch.sqrt(torch.mean(err_F, axis=0)) / (nx * ny * nz) * Lx * Ly * Lz err_F = torch.zeros([nc, 3, nt]).to(device) - err_F[:,0] += torch.mean(_err_F[:,:iLow], dim=1) # low freq - err_F[:,1] += torch.mean(_err_F[:,iLow:iHigh], dim=1) # middle freq - err_F[:,2] += torch.mean(_err_F[:,iHigh:], dim=1) # high freq + err_F[:, 0] += torch.mean(_err_F[:, :iLow], dim=1) # low freq + err_F[:, 1] += torch.mean(_err_F[:, iLow:iHigh], dim=1) # middle freq + err_F[:, 2] += torch.mean(_err_F[:, iHigh:], dim=1) # high freq if if_mean: - return torch.mean(err_RMSE, dim=[0, -1]), \ - torch.mean(err_nRMSE, dim=[0, -1]), \ - torch.mean(err_CSV, dim=[0, -1]), \ - torch.mean(err_Max, dim=[0, -1]), \ - torch.mean(err_BD, dim=[0, -1]), \ - torch.mean(err_F, dim=[0, -1]) + return ( + torch.mean(err_RMSE, dim=[0, -1]), + torch.mean(err_nRMSE, dim=[0, -1]), + torch.mean(err_CSV, dim=[0, -1]), + torch.mean(err_Max, dim=[0, -1]), + torch.mean(err_BD, dim=[0, -1]), + torch.mean(err_F, dim=[0, -1]), + ) else: return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F -def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min, - x_max, y_min, y_max, t_min, t_max, mode='FNO', initial_step=None, ): - if mode=='Unet': + +def metrics( + val_loader, + model, + Lx, + Ly, + Lz, + plot, + channel_plot, + model_name, + x_min, + x_max, + y_min, + y_max, + t_min, + t_max, + mode="FNO", + initial_step=None, +): + if mode == "Unet": with torch.no_grad(): itot = 0 for xx, yy in val_loader: @@ -287,24 +331,36 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min for t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend([i for i in range(1, len(inp.shape) - 1)]) inp = inp.permute(temp_shape) - - y = yy[..., t:t+1, :] - + + y = yy[..., t : t + 1, :] + temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend([i for i in range(2, len(inp.shape))]) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) pred = torch.cat((pred, im), -2) xx = torch.cat((xx[..., 1:, :], im), dim=-2) - _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F \ - = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) + ( + _err_RMSE, + _err_nRMSE, + _err_CSV, + _err_Max, + _err_BD, + _err_F, + ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) if itot == 0: - err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F \ - = _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F + err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( + _err_RMSE, + _err_nRMSE, + _err_CSV, + _err_Max, + _err_BD, + _err_F, + ) pred_plot = pred[:1] target_plot = yy[:1] val_l2_time = torch.zeros(yy.shape[-2]).to(device) @@ -315,15 +371,17 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min err_Max += _err_Max err_BD += _err_BD err_F += _err_F - - mean_dim = [i for i in range(len(yy.shape)-2)] + + mean_dim = [i for i in range(len(yy.shape) - 2)] mean_dim.append(-1) mean_dim = tuple(mean_dim) - val_l2_time += torch.sqrt(torch.mean((pred-yy)**2, dim=mean_dim)) - + val_l2_time += torch.sqrt( + torch.mean((pred - yy) ** 2, dim=mean_dim) + ) + itot += 1 - elif mode=='FNO': + elif mode == "FNO": with torch.no_grad(): itot = 0 for xx, yy, grid in val_loader: @@ -338,16 +396,28 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min for t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) - y = yy[..., t:t + 1, :] + y = yy[..., t : t + 1, :] im = model(inp, grid) pred = torch.cat((pred, im), -2) xx = torch.cat((xx[..., 1:, :], im), dim=-2) - _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F \ - = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) + ( + _err_RMSE, + _err_nRMSE, + _err_CSV, + _err_Max, + _err_BD, + _err_F, + ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) if itot == 0: - err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F \ - = _err_RMSE, _err_nRMSE, _err_CSV, _err_Max, _err_BD, _err_F + err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( + _err_RMSE, + _err_nRMSE, + _err_CSV, + _err_Max, + _err_BD, + _err_F, + ) pred_plot = pred[:1] target_plot = yy[:1] val_l2_time = torch.zeros(yy.shape[-2]).to(device) @@ -358,108 +428,135 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min err_Max += _err_Max err_BD += _err_BD err_F += _err_F - - mean_dim = [i for i in range(len(yy.shape)-2)] + + mean_dim = [i for i in range(len(yy.shape) - 2)] mean_dim.append(-1) mean_dim = tuple(mean_dim) - val_l2_time += torch.sqrt(torch.mean((pred-yy)**2, dim=mean_dim)) + val_l2_time += torch.sqrt( + torch.mean((pred - yy) ** 2, dim=mean_dim) + ) itot += 1 elif mode == "PINN": raise NotImplementedError + err_RMSE = np.array(err_RMSE.data.cpu() / itot) + err_nRMSE = np.array(err_nRMSE.data.cpu() / itot) + err_CSV = np.array(err_CSV.data.cpu() / itot) + err_Max = np.array(err_Max.data.cpu() / itot) + err_BD = np.array(err_BD.data.cpu() / itot) + err_F = np.array(err_F.data.cpu() / itot) + print(f"RMSE: {err_RMSE:.5f}") + print(f"normalized RMSE: {err_nRMSE:.5f}") + print(f"RMSE of conserved variables: {err_CSV:.5f}") + print(f"Maximum value of rms error: {err_Max:.5f}") + print(f"RMSE at boundaries: {err_BD:.5f}") + print(f"RMSE in Fourier space: {err_F}") + + val_l2_time = val_l2_time / itot - err_RMSE = np.array(err_RMSE.data.cpu()/itot) - err_nRMSE = np.array(err_nRMSE.data.cpu()/itot) - err_CSV = np.array(err_CSV.data.cpu()/itot) - err_Max = np.array(err_Max.data.cpu()/itot) - err_BD = np.array(err_BD.data.cpu()/itot) - err_F = np.array(err_F.data.cpu()/itot) - print('RMSE: {0:.5f}'.format(err_RMSE)) - print('normalized RMSE: {0:.5f}'.format(err_nRMSE)) - print('RMSE of conserved variables: {0:.5f}'.format(err_CSV)) - print('Maximum value of rms error: {0:.5f}'.format(err_Max)) - print('RMSE at boundaries: {0:.5f}'.format(err_BD)) - print('RMSE in Fourier space: {0}'.format(err_F)) - - val_l2_time = val_l2_time/itot - if plot: dim = len(yy.shape) - 3 plt.ioff() if dim == 1: - - fig, ax = plt.subplots(figsize=(6.5,6)) - h = ax.imshow(pred_plot[...,channel_plot].squeeze().detach().cpu(), - extent=[t_min, t_max, x_min, x_max], origin='lower', aspect='auto') - h.set_clim(target_plot[...,channel_plot].min(), target_plot[...,channel_plot].max()) + fig, ax = plt.subplots(figsize=(6.5, 6)) + h = ax.imshow( + pred_plot[..., channel_plot].squeeze().detach().cpu(), + extent=[t_min, t_max, x_min, x_max], + origin="lower", + aspect="auto", + ) + h.set_clim( + target_plot[..., channel_plot].min(), + target_plot[..., channel_plot].max(), + ) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) cbar.ax.tick_params(labelsize=30) ax.set_title("Prediction", fontsize=30) - ax.tick_params(axis='x',labelsize=30) - ax.tick_params(axis='y',labelsize=30) + ax.tick_params(axis="x", labelsize=30) + ax.tick_params(axis="y", labelsize=30) ax.set_ylabel("$x$", fontsize=30) ax.set_xlabel("$t$", fontsize=30) plt.tight_layout() - filename = model_name + '_pred.pdf' + filename = model_name + "_pred.pdf" plt.savefig(filename) - - fig, ax = plt.subplots(figsize=(6.5,6)) - h = ax.imshow(target_plot[...,channel_plot].squeeze().detach().cpu(), - extent=[t_min, t_max, x_min, x_max], origin='lower', aspect='auto') - h.set_clim(target_plot[...,channel_plot].min(), target_plot[...,channel_plot].max()) + + fig, ax = plt.subplots(figsize=(6.5, 6)) + h = ax.imshow( + target_plot[..., channel_plot].squeeze().detach().cpu(), + extent=[t_min, t_max, x_min, x_max], + origin="lower", + aspect="auto", + ) + h.set_clim( + target_plot[..., channel_plot].min(), + target_plot[..., channel_plot].max(), + ) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) cbar.ax.tick_params(labelsize=30) ax.set_title("Data", fontsize=30) - ax.tick_params(axis='x',labelsize=30) - ax.tick_params(axis='y',labelsize=30) + ax.tick_params(axis="x", labelsize=30) + ax.tick_params(axis="y", labelsize=30) ax.set_ylabel("$x$", fontsize=30) ax.set_xlabel("$t$", fontsize=30) plt.tight_layout() - filename = model_name + '_data.pdf' + filename = model_name + "_data.pdf" plt.savefig(filename) - + elif dim == 2: - - fig, ax = plt.subplots(figsize=(6.5,6)) - h = ax.imshow(pred_plot[...,-1,channel_plot].squeeze().t().detach().cpu(), - extent=[x_min, x_max, y_min, y_max], origin='lower', aspect='auto') - h.set_clim(target_plot[...,-1,channel_plot].min(), target_plot[...,-1,channel_plot].max()) + fig, ax = plt.subplots(figsize=(6.5, 6)) + h = ax.imshow( + pred_plot[..., -1, channel_plot].squeeze().t().detach().cpu(), + extent=[x_min, x_max, y_min, y_max], + origin="lower", + aspect="auto", + ) + h.set_clim( + target_plot[..., -1, channel_plot].min(), + target_plot[..., -1, channel_plot].max(), + ) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) cbar.ax.tick_params(labelsize=30) ax.set_title("Prediction", fontsize=30) - ax.tick_params(axis='x',labelsize=30) - ax.tick_params(axis='y',labelsize=30) + ax.tick_params(axis="x", labelsize=30) + ax.tick_params(axis="y", labelsize=30) ax.set_ylabel("$y$", fontsize=30) ax.set_xlabel("$x$", fontsize=30) plt.tight_layout() - filename = model_name + '_pred.pdf' + filename = model_name + "_pred.pdf" plt.savefig(filename) - - fig, ax = plt.subplots(figsize=(6.5,6)) - h = ax.imshow(target_plot[...,-1,channel_plot].squeeze().t().detach().cpu(), - extent=[x_min, x_max, y_min, y_max], origin='lower', aspect='auto') - h.set_clim(target_plot[...,-1,channel_plot].min(), target_plot[...,-1,channel_plot].max()) + + fig, ax = plt.subplots(figsize=(6.5, 6)) + h = ax.imshow( + target_plot[..., -1, channel_plot].squeeze().t().detach().cpu(), + extent=[x_min, x_max, y_min, y_max], + origin="lower", + aspect="auto", + ) + h.set_clim( + target_plot[..., -1, channel_plot].min(), + target_plot[..., -1, channel_plot].max(), + ) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(h, cax=cax) cbar.ax.tick_params(labelsize=30) ax.set_title("Data", fontsize=30) - ax.tick_params(axis='x',labelsize=30) - ax.tick_params(axis='y',labelsize=30) + ax.tick_params(axis="x", labelsize=30) + ax.tick_params(axis="y", labelsize=30) ax.set_ylabel("$y$", fontsize=30) ax.set_xlabel("$x$", fontsize=30) plt.tight_layout() - filename = model_name + '_data.pdf' + filename = model_name + "_data.pdf" plt.savefig(filename) - + # plt.figure(figsize=(8,8)) # plt.semilogy(torch.arange(initial_step,yy.shape[-2]), # val_l2_time[initial_step:].detach().cpu()) @@ -469,234 +566,236 @@ def metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, model_name, x_min # plt.tight_layout() # filename = model_name + '_mse_time.pdf' # plt.savefig(filename) - - filename = model_name + 'mse_time.npz' - np.savez(filename, t=torch.arange(initial_step,yy.shape[-2]).cpu(), - mse=val_l2_time[initial_step:].detach().cpu()) + + filename = model_name + "mse_time.npz" + np.savez( + filename, + t=torch.arange(initial_step, yy.shape[-2]).cpu(), + mse=val_l2_time[initial_step:].detach().cpu(), + ) return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F -# LpLoss Function -class LpLoss(object): +# LpLoss Function +class LpLoss: """ - Lp loss function + Lp loss function """ - def __init__(self, p=2, reduction='mean'): + + def __init__(self, p=2, reduction="mean"): super(LpLoss, self).__init__() - #Dimension and Lp-norm type are postive + # Dimension and Lp-norm type are positive assert p > 0 self.p = p self.reduction = reduction + def __call__(self, x, y, eps=1e-20): num_examples = x.size()[0] - _diff = x.view(num_examples,-1) - y.view(num_examples,-1) + _diff = x.view(num_examples, -1) - y.view(num_examples, -1) _diff = torch.norm(_diff, self.p, 1) - _norm = eps + torch.norm(y.view(num_examples,-1), self.p, 1) - if self.reduction in ['mean']: - return torch.mean(_diff/_norm) - if self.reduction in ['sum']: - return torch.sum(_diff/_norm) - return _diff/_norm - -# FftLoss Function -class FftLpLoss(object): + _norm = eps + torch.norm(y.view(num_examples, -1), self.p, 1) + if self.reduction in ["mean"]: + return torch.mean(_diff / _norm) + if self.reduction in ["sum"]: + return torch.sum(_diff / _norm) + return _diff / _norm + + +# FftLoss Function +class FftLpLoss: """ loss function in Fourier space June 2022, F.Alesiani """ - def __init__(self, p=2, reduction='mean'): + + def __init__(self, p=2, reduction="mean"): super(FftLpLoss, self).__init__() - #Dimension and Lp-norm type are postive + # Dimension and Lp-norm type are positive assert p > 0 self.p = p self.reduction = reduction - def __call__(self, x, y, flow=None,fhigh=None, eps=1e-20): + + def __call__(self, x, y, flow=None, fhigh=None, eps=1e-20): num_examples = x.size()[0] others_dims = x.shape[1:] - dims = list(range(1,len(x.shape))) - xf = torch.fft.fftn(x,dim=dims) - yf = torch.fft.fftn(y,dim=dims) - if flow is None: flow = 0 - if fhigh is None: fhigh = np.max(xf.shape[1:]) - - if len(others_dims) ==1: - xf = xf[:,flow:fhigh] - yf = yf[:,flow:fhigh] - if len(others_dims) ==2: - xf = xf[:,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh] - if len(others_dims) ==3: - xf = xf[:,flow:fhigh,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh,flow:fhigh] - if len(others_dims) ==4: - xf = xf[:,flow:fhigh,flow:fhigh,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh,flow:fhigh,flow:fhigh] + dims = list(range(1, len(x.shape))) + xf = torch.fft.fftn(x, dim=dims) + yf = torch.fft.fftn(y, dim=dims) + if flow is None: + flow = 0 + if fhigh is None: + fhigh = np.max(xf.shape[1:]) + + if len(others_dims) == 1: + xf = xf[:, flow:fhigh] + yf = yf[:, flow:fhigh] + if len(others_dims) == 2: + xf = xf[:, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh] + if len(others_dims) == 3: + xf = xf[:, flow:fhigh, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh, flow:fhigh] + if len(others_dims) == 4: + xf = xf[:, flow:fhigh, flow:fhigh, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh, flow:fhigh, flow:fhigh] _diff = xf - yf.reshape(xf.shape) - _diff = torch.norm(_diff.reshape(num_examples,-1), self.p, 1) - _norm = eps + torch.norm(yf.reshape(num_examples,-1), self.p, 1) - - if self.reduction in ['mean']: - return torch.mean(_diff/_norm) - if self.reduction in ['sum']: - return torch.sum(_diff/_norm) - return _diff/_norm - -import torch.nn.functional as F -# FftLoss Function -class FftMseLoss(object): + _diff = torch.norm(_diff.reshape(num_examples, -1), self.p, 1) + _norm = eps + torch.norm(yf.reshape(num_examples, -1), self.p, 1) + + if self.reduction in ["mean"]: + return torch.mean(_diff / _norm) + if self.reduction in ["sum"]: + return torch.sum(_diff / _norm) + return _diff / _norm + + +# FftLoss Function +class FftMseLoss: """ loss function in Fourier space June 2022, F.Alesiani """ - def __init__(self, reduction='mean'): + + def __init__(self, reduction="mean"): super(FftMseLoss, self).__init__() - #Dimension and Lp-norm type are postive + # Dimension and Lp-norm type are positive self.reduction = reduction - def __call__(self, x, y, flow=None,fhigh=None, eps=1e-20): + + def __call__(self, x, y, flow=None, fhigh=None, eps=1e-20): num_examples = x.size()[0] others_dims = x.shape[1:-2] for d in others_dims: - assert (d>1), "we expect the dimension to be the same and greater the 1" + assert d > 1, "we expect the dimension to be the same and greater the 1" # print(others_dims) - dims = list(range(1,len(x.shape)-1)) - xf = torch.fft.fftn(x,dim=dims) - yf = torch.fft.fftn(y,dim=dims) - if flow is None: flow = 0 - if fhigh is None: fhigh = np.max(xf.shape[1:]) - - if len(others_dims) ==1: - xf = xf[:,flow:fhigh] - yf = yf[:,flow:fhigh] - if len(others_dims) ==2: - xf = xf[:,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh] - if len(others_dims) ==3: - xf = xf[:,flow:fhigh,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh,flow:fhigh] - if len(others_dims) ==4: - xf = xf[:,flow:fhigh,flow:fhigh,flow:fhigh,flow:fhigh] - yf = yf[:,flow:fhigh,flow:fhigh,flow:fhigh,flow:fhigh] + dims = list(range(1, len(x.shape) - 1)) + xf = torch.fft.fftn(x, dim=dims) + yf = torch.fft.fftn(y, dim=dims) + if flow is None: + flow = 0 + if fhigh is None: + fhigh = np.max(xf.shape[1:]) + + if len(others_dims) == 1: + xf = xf[:, flow:fhigh] + yf = yf[:, flow:fhigh] + if len(others_dims) == 2: + xf = xf[:, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh] + if len(others_dims) == 3: + xf = xf[:, flow:fhigh, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh, flow:fhigh] + if len(others_dims) == 4: + xf = xf[:, flow:fhigh, flow:fhigh, flow:fhigh, flow:fhigh] + yf = yf[:, flow:fhigh, flow:fhigh, flow:fhigh, flow:fhigh] _diff = xf - yf - _diff = _diff.reshape(num_examples,-1).abs()**2 - if self.reduction in ['mean']: + _diff = _diff.reshape(num_examples, -1).abs() ** 2 + if self.reduction in ["mean"]: return torch.mean(_diff).abs() - if self.reduction in ['sum']: + if self.reduction in ["sum"]: return torch.sum(_diff).abs() return _diff.abs() -import torch.nn as nn -def inverse_metrics(u0,x,pred_u0,y): +def inverse_metrics(u0, x, pred_u0, y): """ computes all the metrics in the base and fourier space - u0: esimated initial condition, - pred_u0: prediction from the esimated initial condition, pred_u0 = model(u0) + u0: estimated initial condition, + pred_u0: prediction from the estimated initial condition, pred_u0 = model(u0) x: true initial condition y: true prediction, y = model(x) June 2022, F.Alesiani """ - + mseloss_fn = nn.MSELoss(reduction="mean") - l2loss_fn = LpLoss(p=2,reduction='mean') - l3loss_fn = LpLoss(p=3,reduction='mean') - + l2loss_fn = LpLoss(p=2, reduction="mean") + l3loss_fn = LpLoss(p=3, reduction="mean") + fftmseloss_fn = FftMseLoss(reduction="mean") - fftl2loss_fn = FftLpLoss(p=2,reduction="mean") - fftl3loss_fn = FftLpLoss(p=3,reduction="mean") + fftl2loss_fn = FftLpLoss(p=2, reduction="mean") + fftl3loss_fn = FftLpLoss(p=3, reduction="mean") - #initial condition + # initial condition mseloss_u0 = mseloss_fn(u0.view(1, -1), x.view(1, -1)).item() l2loss_u0 = l2loss_fn(u0.view(1, -1), x.view(1, -1)).item() l3loss_u0 = l3loss_fn(u0.view(1, -1), x.view(1, -1)).item() - - - fmid = u0.shape[1]//4 + + fmid = u0.shape[1] // 4 fftmseloss_u0 = fftmseloss_fn(u0, x).item() fftmseloss_low_u0 = fftmseloss_fn(u0, x, 0, fmid).item() - fftmseloss_mid_u0 = fftmseloss_fn(u0, x, fmid, 2*fmid).item() - fftmseloss_hi_u0 = fftmseloss_fn(u0, x, 2*fmid).item() - + fftmseloss_mid_u0 = fftmseloss_fn(u0, x, fmid, 2 * fmid).item() + fftmseloss_hi_u0 = fftmseloss_fn(u0, x, 2 * fmid).item() + fftl2loss_u0 = fftl2loss_fn(u0, x).item() fftl2loss_low_u0 = fftl2loss_fn(u0, x, 0, fmid).item() - fftl2loss_mid_u0 = fftl2loss_fn(u0, x, fmid, 2*fmid).item() - fftl2loss_hi_u0 = fftl2loss_fn(u0, x, 2*fmid).item() + fftl2loss_mid_u0 = fftl2loss_fn(u0, x, fmid, 2 * fmid).item() + fftl2loss_hi_u0 = fftl2loss_fn(u0, x, 2 * fmid).item() fftl3loss_u0 = fftl3loss_fn(u0, x).item() fftl3loss_low_u0 = fftl3loss_fn(u0, x, 0, fmid).item() - fftl3loss_mid_u0 = fftl3loss_fn(u0, x, fmid, 2*fmid).item() - fftl3loss_hi_u0 = fftl3loss_fn(u0, x, 2*fmid).item() + fftl3loss_mid_u0 = fftl3loss_fn(u0, x, fmid, 2 * fmid).item() + fftl3loss_hi_u0 = fftl3loss_fn(u0, x, 2 * fmid).item() - #prediction + # prediction mseloss_pred_u0 = mseloss_fn(pred_u0.reshape(1, -1), y.reshape(1, -1)).item() l2loss_pred_u0 = l2loss_fn(pred_u0.reshape(1, -1), y.reshape(1, -1)).item() l3loss_pred_u0 = l3loss_fn(pred_u0.reshape(1, -1), y.reshape(1, -1)).item() - fmid = pred_u0.shape[1]//4 + fmid = pred_u0.shape[1] // 4 pred_u0 = pred_u0.squeeze(-1) y = y.squeeze(-1) - + fftmseloss_pred_u0 = fftmseloss_fn(pred_u0, y).item() fftmseloss_low_pred_u0 = fftmseloss_fn(pred_u0, y, 0, fmid).item() - fftmseloss_mid_pred_u0 = fftmseloss_fn(pred_u0, y, fmid, 2*fmid).item() - fftmseloss_hi_pred_u0 = fftmseloss_fn(pred_u0, y, 2*fmid).item() - + fftmseloss_mid_pred_u0 = fftmseloss_fn(pred_u0, y, fmid, 2 * fmid).item() + fftmseloss_hi_pred_u0 = fftmseloss_fn(pred_u0, y, 2 * fmid).item() + fftl2loss_pred_u0 = fftl2loss_fn(pred_u0, y).item() fftl2loss_low_pred_u0 = fftl2loss_fn(pred_u0, y, 0, fmid).item() - fftl2loss_mid_pred_u0 = fftl2loss_fn(pred_u0, y, fmid, 2*fmid).item() - fftl2loss_hi_pred_u0= fftl2loss_fn(pred_u0, y, 2*fmid).item() + fftl2loss_mid_pred_u0 = fftl2loss_fn(pred_u0, y, fmid, 2 * fmid).item() + fftl2loss_hi_pred_u0 = fftl2loss_fn(pred_u0, y, 2 * fmid).item() fftl3loss_pred_u0 = fftl3loss_fn(pred_u0, y).item() fftl3loss_low_pred_u0 = fftl3loss_fn(pred_u0, y, 0, fmid).item() - fftl3loss_mid_pred_u0 = fftl3loss_fn(pred_u0, y, fmid, 2*fmid).item() - fftl3loss_hi_pred_u0 = fftl3loss_fn(pred_u0, y, 2*fmid).item() + fftl3loss_mid_pred_u0 = fftl3loss_fn(pred_u0, y, fmid, 2 * fmid).item() + fftl3loss_hi_pred_u0 = fftl3loss_fn(pred_u0, y, 2 * fmid).item() metric = { - 'mseloss_u0': mseloss_u0 - ,'l2loss_u0': l2loss_u0 - ,'l3loss_u0': l3loss_u0 - - ,'mseloss_pred_u0': mseloss_pred_u0 - ,'l2loss_pred_u0': l2loss_pred_u0 - ,'l3loss_pred_u0': l3loss_pred_u0 - - - ,'fftmseloss_u0': fftmseloss_u0 - ,'fftmseloss_low_u0': fftmseloss_low_u0 - ,'fftmseloss_mid_u0': fftmseloss_mid_u0 - ,'fftmseloss_hi_u0': fftmseloss_hi_u0 - - - - ,'fftmseloss_pred_u0': fftmseloss_pred_u0 - ,'fftmseloss_low_pred_u0': fftmseloss_low_pred_u0 - ,'fftmseloss_mid_pred_u0': fftmseloss_mid_pred_u0 - ,'fftmseloss_hi_pred_u0': fftmseloss_hi_pred_u0 - - ,'fftl2loss_u0': fftl2loss_u0 - ,'fftl2loss_low_u0': fftl2loss_low_u0 - ,'fftl2loss_mid_u0': fftl2loss_mid_u0 - ,'fftl2loss_hi_u0': fftl2loss_hi_u0 - - ,'fftl2loss_pred_u0': fftl2loss_pred_u0 - ,'fftl2loss_low_pred_u0': fftl2loss_low_pred_u0 - ,'fftl2loss_mid_pred_u0': fftl2loss_mid_pred_u0 - ,'fftl2loss_hi_pred_u0': fftl2loss_hi_pred_u0 - - ,'fftl3loss_u0': fftl3loss_u0 - ,'fftl3loss_low_u0': fftl3loss_low_u0 - ,'fftl3loss_mid_u0': fftl3loss_mid_u0 - ,'fftl3loss_hi_u0': fftl3loss_hi_u0 - - ,'fftl3loss_pred_u0': fftl3loss_pred_u0 - ,'fftl3loss_low_pred_u0': fftl3loss_low_pred_u0 - ,'fftl3loss_mid_pred_u0': fftl3loss_mid_pred_u0 - ,'fftl3loss_hi_pred_u0': fftl3loss_hi_pred_u0 - } + "mseloss_u0": mseloss_u0, + "l2loss_u0": l2loss_u0, + "l3loss_u0": l3loss_u0, + "mseloss_pred_u0": mseloss_pred_u0, + "l2loss_pred_u0": l2loss_pred_u0, + "l3loss_pred_u0": l3loss_pred_u0, + "fftmseloss_u0": fftmseloss_u0, + "fftmseloss_low_u0": fftmseloss_low_u0, + "fftmseloss_mid_u0": fftmseloss_mid_u0, + "fftmseloss_hi_u0": fftmseloss_hi_u0, + "fftmseloss_pred_u0": fftmseloss_pred_u0, + "fftmseloss_low_pred_u0": fftmseloss_low_pred_u0, + "fftmseloss_mid_pred_u0": fftmseloss_mid_pred_u0, + "fftmseloss_hi_pred_u0": fftmseloss_hi_pred_u0, + "fftl2loss_u0": fftl2loss_u0, + "fftl2loss_low_u0": fftl2loss_low_u0, + "fftl2loss_mid_u0": fftl2loss_mid_u0, + "fftl2loss_hi_u0": fftl2loss_hi_u0, + "fftl2loss_pred_u0": fftl2loss_pred_u0, + "fftl2loss_low_pred_u0": fftl2loss_low_pred_u0, + "fftl2loss_mid_pred_u0": fftl2loss_mid_pred_u0, + "fftl2loss_hi_pred_u0": fftl2loss_hi_pred_u0, + "fftl3loss_u0": fftl3loss_u0, + "fftl3loss_low_u0": fftl3loss_low_u0, + "fftl3loss_mid_u0": fftl3loss_mid_u0, + "fftl3loss_hi_u0": fftl3loss_hi_u0, + "fftl3loss_pred_u0": fftl3loss_pred_u0, + "fftl3loss_low_pred_u0": fftl3loss_low_pred_u0, + "fftl3loss_mid_pred_u0": fftl3loss_mid_pred_u0, + "fftl3loss_hi_pred_u0": fftl3loss_hi_pred_u0, + } return metric diff --git a/pdebench/models/pinn/pde_definitions.py b/pdebench/models/pinn/pde_definitions.py index 9369cf3..834fd37 100644 --- a/pdebench/models/pinn/pde_definitions.py +++ b/pdebench/models/pinn/pde_definitions.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import deepxde as dde import numpy as np -import torch def reaction_1(u1, u2): @@ -14,7 +15,6 @@ def reaction_2(u1, u2): def pde_diffusion_reaction(x, y): - d1 = 1e-3 d2 = 5e-3 @@ -55,8 +55,8 @@ def pde_diffusion_sorption(x, y): ) return du1_t - D / retardation_factor * du1_xx - - + + def pde_swe1d(): raise NotImplementedError @@ -85,15 +85,18 @@ def pde_swe2d(x, y): return eq1 + eq2 + eq3 + def pde_adv1d(x, y, beta): dy_x = dde.grad.jacobian(y, x, i=0, j=0) dy_t = dde.grad.jacobian(y, x, i=0, j=1) return dy_t + beta * dy_x + def pde_diffusion_reaction_1d(x, y, nu, rho): dy_t = dde.grad.jacobian(y, x, i=0, j=1) dy_xx = dde.grad.hessian(y, x, i=0, j=0) - return dy_t - nu * dy_xx - rho * y * (1. - y) + return dy_t - nu * dy_xx - rho * y * (1.0 - y) + def pde_burgers1D(x, y, nu): dy_x = dde.grad.jacobian(y, x, i=0, j=0) @@ -101,11 +104,12 @@ def pde_burgers1D(x, y, nu): dy_xx = dde.grad.hessian(y, x, i=0, j=0) return dy_t + y * dy_x - nu / np.pi * dy_xx + def pde_CFD1d(x, y, gamma): h = y[..., 0].unsqueeze(1) # rho u = y[..., 1].unsqueeze(1) # v p = y[..., 2].unsqueeze(1) # p - E = p/(gamma - 1.) + 0.5 * h * u**2 + E = p / (gamma - 1.0) + 0.5 * h * u**2 E = E.unsqueeze(1) Fx = u * (E + p) Fx = Fx.unsqueeze(1) @@ -125,12 +129,13 @@ def pde_CFD1d(x, y, gamma): return eq1 + eq2 + eq3 + def pde_CFD2d(x, y, gamma): h = y[..., 0].unsqueeze(1) # rho ux = y[..., 1].unsqueeze(1) # vx uy = y[..., 2].unsqueeze(1) # vy p = y[..., 3].unsqueeze(1) # p - E = p/(gamma - 1.) + 0.5 * h * (ux**2 + uy**2) + E = p / (gamma - 1.0) + 0.5 * h * (ux**2 + uy**2) E = E.unsqueeze(1) Fx = ux * (E + p) Fx = Fx.unsqueeze(1) @@ -160,13 +165,14 @@ def pde_CFD2d(x, y, gamma): return eq1 + eq2 + eq3 + eq4 + def pde_CFD3d(x, y, gamma): h = y[..., 0].unsqueeze(1) # rho ux = y[..., 1].unsqueeze(1) # vx uy = y[..., 2].unsqueeze(1) # vy uz = y[..., 3].unsqueeze(1) # vz p = y[..., 4].unsqueeze(1) # p - E = p/(gamma - 1.) + 0.5 * h * (ux**2 + uy**2 + uz**2) + E = p / (gamma - 1.0) + 0.5 * h * (ux**2 + uy**2 + uz**2) E = E.unsqueeze(1) Fx = ux * (E + p) Fx = Fx.unsqueeze(1) @@ -206,4 +212,4 @@ def pde_CFD3d(x, y, gamma): eq4 = h * (uz_t + ux * uz_x + uy * uz_y + uz * uz_z) - p_z eq5 = E_t + Fx_x + Fy_y + Fz_z - return eq1 + eq2 + eq3 + eq4 + eq5 \ No newline at end of file + return eq1 + eq2 + eq3 + eq4 + eq5 diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index 4d3ec2f..07de94a 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -1,37 +1,32 @@ """Backend supported: tensorflow.compat.v1, tensorflow, pytorch""" -import deepxde as dde -import numpy as np +from __future__ import annotations + import pickle + +import deepxde as dde import matplotlib.pyplot as plt -import os, sys +import numpy as np import torch - -from typing import Tuple - -from pdebench.models.pinn.utils import ( - PINNDatasetRadialDambreak, - PINNDatasetDiffReact, - PINNDataset2D, - PINNDatasetDiffSorption, - PINNDatasetBump, - PINNDataset1Dpde, - PINNDataset2Dpde, - PINNDataset3Dpde, -) +from pdebench.models.metrics import metric_func from pdebench.models.pinn.pde_definitions import ( - pde_diffusion_reaction, - pde_swe2d, - pde_diffusion_sorption, - pde_swe1d, pde_adv1d, - pde_diffusion_reaction_1d, pde_burgers1D, pde_CFD1d, pde_CFD2d, - pde_CFD3d, + pde_diffusion_reaction, + pde_diffusion_reaction_1d, + pde_diffusion_sorption, + pde_swe2d, +) +from pdebench.models.pinn.utils import ( + PINNDataset1Dpde, + PINNDataset2D, + PINNDataset2Dpde, + PINNDataset3Dpde, + PINNDatasetDiffReact, + PINNDatasetDiffSorption, + PINNDatasetRadialDambreak, ) - -from pdebench.models.metrics import metrics, metric_func def setup_diffusion_sorption(filename, seed): @@ -93,6 +88,7 @@ def transform_output(x, y): return model, dataset + def setup_diffusion_reaction(filename, seed): # TODO: read from dataset config file geom = dde.geometry.Rectangle((-1, -1), (1, 1)) @@ -134,8 +130,7 @@ def setup_diffusion_reaction(filename, seed): return model, dataset -def setup_swe_2d(filename, seed) -> Tuple[dde.Model, PINNDataset2D]: - +def setup_swe_2d(filename, seed) -> tuple[dde.Model, PINNDataset2D]: dataset = PINNDatasetRadialDambreak(filename, seed) # TODO: read from dataset config file @@ -182,50 +177,63 @@ def setup_swe_2d(filename, seed) -> Tuple[dde.Model, PINNDataset2D]: return model, dataset + def _boundary_r(x, on_boundary, xL, xR): - return (on_boundary and np.isclose(x[0], xL)) or (on_boundary and np.isclose(x[0], xR)) - -def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", - root_path='data', - val_batch_idx=-1, - input_ch=2, - output_ch=1, - hidden_ch=40, - xL=0., - xR=1., - if_periodic_bc=True, - aux_params=[0.1]): + return (on_boundary and np.isclose(x[0], xL)) or ( + on_boundary and np.isclose(x[0], xR) + ) + +def setup_pde1D( + filename="1D_Advection_Sols_beta0.1.hdf5", + root_path="data", + val_batch_idx=-1, + input_ch=2, + output_ch=1, + hidden_ch=40, + xL=0.0, + xR=1.0, + if_periodic_bc=True, + aux_params=[0.1], +): # TODO: read from dataset config file geom = dde.geometry.Interval(xL, xR) boundary_r = lambda x, on_boundary: _boundary_r(x, on_boundary, xL, xR) - if filename[0] == 'R': + if filename[0] == "R": timedomain = dde.geometry.TimeDomain(0, 1.0) - pde = lambda x, y : pde_diffusion_reaction_1d(x, y, aux_params[0], aux_params[1]) + pde = lambda x, y: pde_diffusion_reaction_1d(x, y, aux_params[0], aux_params[1]) else: - if filename.split('_')[1][0]=='A': + if filename.split("_")[1][0] == "A": timedomain = dde.geometry.TimeDomain(0, 2.0) pde = lambda x, y: pde_adv1d(x, y, aux_params[0]) - elif filename.split('_')[1][0] == 'B': + elif filename.split("_")[1][0] == "B": timedomain = dde.geometry.TimeDomain(0, 2.0) pde = lambda x, y: pde_burgers1D(x, y, aux_params[0]) - elif filename.split('_')[1][0]=='C': + elif filename.split("_")[1][0] == "C": timedomain = dde.geometry.TimeDomain(0, 1.0) pde = lambda x, y: pde_CFD1d(x, y, aux_params[0]) geomtime = dde.geometry.GeometryXTime(geom, timedomain) - dataset = PINNDataset1Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + dataset = PINNDataset1Dpde( + filename, root_path=root_path, val_batch_idx=val_batch_idx + ) # prepare initial condition initial_input, initial_u = dataset.get_initial_condition() - if filename.split('_')[1][0] == 'C': - ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,0].unsqueeze(1), component=0) - ic_data_v = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,1].unsqueeze(1), component=1) - ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[:,2].unsqueeze(1), component=2) + if filename.split("_")[1][0] == "C": + ic_data_d = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[:, 0].unsqueeze(1), component=0 + ) + ic_data_v = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[:, 1].unsqueeze(1), component=1 + ) + ic_data_p = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[:, 2].unsqueeze(1), component=2 + ) else: ic_data_u = dde.icbc.PointSetBC(initial_input.cpu(), initial_u, component=0) # prepare boundary condition if if_periodic_bc: - if filename.split('_')[1][0] == 'C': + if filename.split("_")[1][0] == "C": bc_D = dde.icbc.PeriodicBC(geomtime, 0, boundary_r) bc_V = dde.icbc.PeriodicBC(geomtime, 1, boundary_r) bc_P = dde.icbc.PeriodicBC(geomtime, 2, boundary_r) @@ -250,7 +258,9 @@ def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", ) else: ic = dde.icbc.IC( - geomtime, lambda x: -np.sin(np.pi * x[:, 0:1]), lambda _, on_initial: on_initial + geomtime, + lambda x: -np.sin(np.pi * x[:, 0:1]), + lambda _, on_initial: on_initial, ) bd_input, bd_uL, bd_uR = dataset.get_boundary_condition() bc_data_uL = dde.icbc.PointSetBC(bd_input.cpu(), bd_uL, component=0) @@ -264,74 +274,104 @@ def setup_pde1D(filename="1D_Advection_Sols_beta0.1.hdf5", num_boundary=1000, num_initial=5000, ) - net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + net = dde.nn.FNN( + [input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal" + ) model = dde.Model(data, net) return model, dataset -def setup_CFD2D(filename="2D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", - root_path='data', - val_batch_idx=-1, - input_ch=2, - output_ch=4, - hidden_ch=40, - xL=0., - xR=1., - yL=0., - yR=1., - if_periodic_bc=True, - aux_params=[1.6667]): +def setup_CFD2D( + filename="2D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", + root_path="data", + val_batch_idx=-1, + input_ch=2, + output_ch=4, + hidden_ch=40, + xL=0.0, + xR=1.0, + yL=0.0, + yR=1.0, + if_periodic_bc=True, + aux_params=[1.6667], +): # TODO: read from dataset config file geom = dde.geometry.Rectangle((-1, -1), (1, 1)) - timedomain = dde.geometry.TimeDomain(0., 1.0) + timedomain = dde.geometry.TimeDomain(0.0, 1.0) pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) geomtime = dde.geometry.GeometryXTime(geom, timedomain) - dataset = PINNDataset2Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + dataset = PINNDataset2Dpde( + filename, root_path=root_path, val_batch_idx=val_batch_idx + ) # prepare initial condition initial_input, initial_u = dataset.get_initial_condition() - ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,0].unsqueeze(1), component=0) - ic_data_vx = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,1].unsqueeze(1), component=1) - ic_data_vy = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,2].unsqueeze(1), component=2) - ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,3].unsqueeze(1), component=3) + ic_data_d = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 0].unsqueeze(1), component=0 + ) + ic_data_vx = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 1].unsqueeze(1), component=1 + ) + ic_data_vy = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 2].unsqueeze(1), component=2 + ) + ic_data_p = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 3].unsqueeze(1), component=3 + ) # prepare boundary condition bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) data = dde.data.TimePDE( geomtime, pde, - [ic_data_d, ic_data_vx, ic_data_vy, ic_data_p],#, bc], + [ic_data_d, ic_data_vx, ic_data_vy, ic_data_p], # , bc], num_domain=1000, num_boundary=1000, num_initial=5000, ) - net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + net = dde.nn.FNN( + [input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal" + ) model = dde.Model(data, net) return model, dataset -def setup_CFD3D(filename="3D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", - root_path='data', - val_batch_idx=-1, - input_ch=2, - output_ch=4, - hidden_ch=40, - aux_params=[1.6667]): +def setup_CFD3D( + filename="3D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", + root_path="data", + val_batch_idx=-1, + input_ch=2, + output_ch=4, + hidden_ch=40, + aux_params=[1.6667], +): # TODO: read from dataset config file - geom = dde.geometry.Cuboid((0., 0., 0.), (1., 1., 1.)) - timedomain = dde.geometry.TimeDomain(0., 1.0) + geom = dde.geometry.Cuboid((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)) + timedomain = dde.geometry.TimeDomain(0.0, 1.0) pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) geomtime = dde.geometry.GeometryXTime(geom, timedomain) - dataset = PINNDataset3Dpde(filename, root_path=root_path, val_batch_idx=val_batch_idx) + dataset = PINNDataset3Dpde( + filename, root_path=root_path, val_batch_idx=val_batch_idx + ) # prepare initial condition initial_input, initial_u = dataset.get_initial_condition() - ic_data_d = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,0].unsqueeze(1), component=0) - ic_data_vx = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,1].unsqueeze(1), component=1) - ic_data_vy = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,2].unsqueeze(1), component=2) - ic_data_vz = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,3].unsqueeze(1), component=3) - ic_data_p = dde.icbc.PointSetBC(initial_input.cpu(), initial_u[...,4].unsqueeze(1), component=4) + ic_data_d = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 0].unsqueeze(1), component=0 + ) + ic_data_vx = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 1].unsqueeze(1), component=1 + ) + ic_data_vy = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 2].unsqueeze(1), component=2 + ) + ic_data_vz = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 3].unsqueeze(1), component=3 + ) + ic_data_p = dde.icbc.PointSetBC( + initial_input.cpu(), initial_u[..., 4].unsqueeze(1), component=4 + ) # prepare boundary condition bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) data = dde.data.TimePDE( @@ -342,16 +382,29 @@ def setup_CFD3D(filename="3D_CFD_RAND_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5", num_boundary=1000, num_initial=5000, ) - net = dde.nn.FNN([input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal") + net = dde.nn.FNN( + [input_ch] + [hidden_ch] * 6 + [output_ch], "tanh", "Glorot normal" + ) model = dde.Model(data, net) return model, dataset -def _run_training(scenario, epochs, learning_rate, model_update, flnm, - input_ch, output_ch, - root_path, val_batch_idx, if_periodic_bc, aux_params, - if_single_run, - seed): + +def _run_training( + scenario, + epochs, + learning_rate, + model_update, + flnm, + input_ch, + output_ch, + root_path, + val_batch_idx, + if_periodic_bc, + aux_params, + if_single_run, + seed, +): if scenario == "swe2d": model, dataset = setup_swe_2d(filename=flnm, seed=seed) n_components = 1 @@ -362,32 +415,38 @@ def _run_training(scenario, epochs, learning_rate, model_update, flnm, model, dataset = setup_diffusion_sorption(filename=flnm, seed=seed) n_components = 1 elif scenario == "pde1D": - model, dataset = setup_pde1D(filename=flnm, - root_path=root_path, - input_ch=input_ch, - output_ch=output_ch, - val_batch_idx=val_batch_idx, - if_periodic_bc=if_periodic_bc, - aux_params=aux_params) - if flnm.split('_')[1][0] == 'C': + model, dataset = setup_pde1D( + filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + if_periodic_bc=if_periodic_bc, + aux_params=aux_params, + ) + if flnm.split("_")[1][0] == "C": n_components = 3 else: n_components = 1 elif scenario == "CFD2D": - model, dataset = setup_CFD2D(filename=flnm, - root_path=root_path, - input_ch=input_ch, - output_ch=output_ch, - val_batch_idx=val_batch_idx, - aux_params=aux_params) + model, dataset = setup_CFD2D( + filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + aux_params=aux_params, + ) n_components = 4 - elif scenario == "CFD3D": - model, dataset = setup_CFD3D(filename=flnm, - root_path=root_path, - input_ch=input_ch, - output_ch=output_ch, - val_batch_idx=val_batch_idx, - aux_params=aux_params) + elif scenario == "CFD3D": + model, dataset = setup_CFD3D( + filename=flnm, + root_path=root_path, + input_ch=input_ch, + output_ch=output_ch, + val_batch_idx=val_batch_idx, + aux_params=aux_params, + ) n_components = 5 else: raise NotImplementedError(f"PINN training not implemented for {scenario}") @@ -455,21 +514,54 @@ def _run_training(scenario, epochs, learning_rate, model_update, flnm, else: return test_pred, test_gt, model_name -def run_training(scenario, epochs, learning_rate, model_update, flnm, - input_ch=1, output_ch=1, - root_path='../data/', val_num=10, if_periodic_bc=True, aux_params=[None], seed='0000'): +def run_training( + scenario, + epochs, + learning_rate, + model_update, + flnm, + input_ch=1, + output_ch=1, + root_path="../data/", + val_num=10, + if_periodic_bc=True, + aux_params=[None], + seed="0000", +): if val_num == 1: # single job - _run_training(scenario, epochs, learning_rate, model_update, flnm, - input_ch, output_ch, - root_path, -val_num, if_periodic_bc, aux_params, - if_single_run=True, seed=seed) + _run_training( + scenario, + epochs, + learning_rate, + model_update, + flnm, + input_ch, + output_ch, + root_path, + -val_num, + if_periodic_bc, + aux_params, + if_single_run=True, + seed=seed, + ) else: for val_batch_idx in range(-1, -val_num, -1): - test_pred, test_gt, model_name = _run_training(scenario, epochs, learning_rate, model_update, flnm, - input_ch, output_ch, - root_path, val_batch_idx, if_periodic_bc, aux_params, - if_single_run=False, seed=seed) + test_pred, test_gt, model_name = _run_training( + scenario, + epochs, + learning_rate, + model_update, + flnm, + input_ch, + output_ch, + root_path, + val_batch_idx, + if_periodic_bc, + aux_params, + if_single_run=False, + seed=seed, + ) if val_batch_idx == -1: pred, target = test_pred.unsqueeze(0), test_gt.unsqueeze(0) else: diff --git a/pdebench/models/pinn/utils.py b/pdebench/models/pinn/utils.py index ba11980..91d13a3 100644 --- a/pdebench/models/pinn/utils.py +++ b/pdebench/models/pinn/utils.py @@ -1,18 +1,17 @@ -# -*- coding: utf-8 -*- """ Created on Wed Apr 20 09:43:15 2022 @author: timot """ +from __future__ import annotations -import numpy as np -import torch -from torch.utils.data import Dataset -from torch.utils.data import DataLoader import os + import h5py -from omegaconf import DictConfig, OmegaConf +import numpy as np +import torch import yaml +from torch.utils.data import Dataset class PINNDataset1D(Dataset): @@ -319,8 +318,9 @@ def get_initial_condition(self): return (self.data_input[:Nx, :], np.expand_dims(u0, 1)) + class PINNDataset1Dpde(Dataset): - def __init__(self, filename, root_path='data', val_batch_idx=-1): + def __init__(self, filename, root_path="data", val_batch_idx=-1): """ :param filename: filename that contains the dataset :type filename: STR @@ -343,9 +343,10 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1): # main data keys = list(h5_file.keys()) keys.sort() - if 'tensor' in keys: - self.data_output = torch.tensor(np.array(h5_file["tensor"][val_batch_idx]), - dtype=torch.float) + if "tensor" in keys: + self.data_output = torch.tensor( + np.array(h5_file["tensor"][val_batch_idx]), dtype=torch.float + ) # permute from [t, x] -> [x, t] self.data_output = self.data_output.T @@ -358,12 +359,14 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1): _data1 = np.array(h5_file["density"][val_batch_idx]) _data2 = np.array(h5_file["Vx"][val_batch_idx]) _data3 = np.array(h5_file["pressure"][val_batch_idx]) - _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None]], axis=-1) + _data = np.concatenate( + [_data1[..., None], _data2[..., None], _data3[..., None]], axis=-1 + ) # permute from [t, x] -> [x, t] _data = np.transpose(_data, (1, 0, 2)) self.data_output = torch.tensor(_data, dtype=torch.float) - del(_data, _data1, _data2, _data3) + del (_data, _data1, _data2, _data3) # for init/boundary conditions self.init_data = self.data_output[:, 0] @@ -371,7 +374,7 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1): self.bd_data_R = self.data_output[-1] self.tdim = self.data_output.size(1) - self.data_grid_t = self.data_grid_t[:self.tdim] + self.data_grid_t = self.data_grid_t[: self.tdim] XX, TT = torch.meshgrid( [self.data_grid_x, self.data_grid_t], @@ -381,18 +384,18 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1): self.data_input = torch.vstack([XX.ravel(), TT.ravel()]).T h5_file.close() - if 'tensor' in keys: + if "tensor" in keys: self.data_output = self.data_output.reshape(-1, 1) else: self.data_output = self.data_output.reshape(-1, 3) def get_initial_condition(self): # return (self.data_grid_x[:, None], self.init_data) - return (self.data_input[::self.tdim, :], self.init_data) + return (self.data_input[:: self.tdim, :], self.init_data) def get_boundary_condition(self): # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) - return (self.data_input[:self.xdim, :], self.bd_data_L, self.bd_data_R) + return (self.data_input[: self.xdim, :], self.bd_data_L, self.bd_data_R) def get_test_data(self, n_last_time_steps, n_components=1): n_x = len(self.data_grid_x) @@ -442,8 +445,9 @@ def __len__(self): def __getitem__(self, idx): return self.data_input[idx, :], self.data_output[idx] + class PINNDataset2Dpde(Dataset): - def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=9, rdc_y=9): + def __init__(self, filename, root_path="data", val_batch_idx=-1, rdc_x=9, rdc_y=9): """ :param filename: filename that contains the dataset :type filename: STR @@ -476,24 +480,31 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=9, rdc_y= _data2 = np.array(h5_file["Vx"][val_batch_idx]) _data3 = np.array(h5_file["Vy"][val_batch_idx]) _data4 = np.array(h5_file["pressure"][val_batch_idx]) - _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None], _data4[...,None]], - axis=-1) + _data = np.concatenate( + [ + _data1[..., None], + _data2[..., None], + _data3[..., None], + _data4[..., None], + ], + axis=-1, + ) # permute from [t, x, y, v] -> [x, y, t, v] _data = np.transpose(_data, (1, 2, 0, 3)) _data = _data[::rdc_x, ::rdc_y] self.data_output = torch.tensor(_data, dtype=torch.float) - del(_data, _data1, _data2, _data3, _data4) + del (_data, _data1, _data2, _data3, _data4) # for init/boundary conditions self.init_data = self.data_output[..., 0, :] self.bd_data_xL = self.data_output[0] self.bd_data_xR = self.data_output[-1] - self.bd_data_yL = self.data_output[:,0] - self.bd_data_yR = self.data_output[:,-1] + self.bd_data_yL = self.data_output[:, 0] + self.bd_data_yR = self.data_output[:, -1] self.tdim = self.data_output.size(2) - self.data_grid_t = self.data_grid_t[:self.tdim] + self.data_grid_t = self.data_grid_t[: self.tdim] XX, YY, TT = torch.meshgrid( [self.data_grid_x, self.data_grid_y, self.data_grid_t], @@ -507,14 +518,17 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=9, rdc_y= def get_initial_condition(self): # return (self.data_grid_x[:, None], self.init_data) - return (self.data_input[::self.tdim, :], self.init_data) + return (self.data_input[:: self.tdim, :], self.init_data) def get_boundary_condition(self): # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) - return (self.data_input[:self.xdim*self.ydim, :], - self.bd_data_xL, self.bd_data_xR, - self.bd_data_yL, self.bd_data_yR - ) + return ( + self.data_input[: self.xdim * self.ydim, :], + self.bd_data_xL, + self.bd_data_xR, + self.bd_data_yL, + self.bd_data_yR, + ) def get_test_data(self, n_last_time_steps, n_components=1): n_x = len(self.data_grid_x) @@ -565,8 +579,11 @@ def __len__(self): def __getitem__(self, idx): return self.data_input[idx, :], self.data_output[idx].unsqueeze(1) + class PINNDataset3Dpde(Dataset): - def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=2, rdc_y=2, rdc_z=2): + def __init__( + self, filename, root_path="data", val_batch_idx=-1, rdc_x=2, rdc_y=2, rdc_z=2 + ): """ :param filename: filename that contains the dataset :type filename: STR @@ -607,48 +624,62 @@ def __init__(self, filename, root_path='data', val_batch_idx=-1, rdc_x=2, rdc_y= _data3 = np.array(h5_file["Vy"][val_batch_idx]) _data4 = np.array(h5_file["Vz"][val_batch_idx]) _data5 = np.array(h5_file["pressure"][val_batch_idx]) - _data = np.concatenate([_data1[...,None], _data2[...,None], _data3[...,None], _data4[...,None], _data5[...,None]], - axis=-1) + _data = np.concatenate( + [ + _data1[..., None], + _data2[..., None], + _data3[..., None], + _data4[..., None], + _data5[..., None], + ], + axis=-1, + ) # permute from [t, x, y, z, v] -> [x, y, z, t, v] _data = np.transpose(_data, (1, 2, 3, 0, 4)) _data = _data[::rdc_x, ::rdc_y, ::rdc_z] self.data_output = torch.tensor(_data, dtype=torch.float) - del(_data, _data1, _data2, _data3, _data4, _data5) + del (_data, _data1, _data2, _data3, _data4, _data5) # for init/boundary conditions self.init_data = self.data_output[..., 0, :] self.bd_data_xL = self.data_output[0] self.bd_data_xR = self.data_output[-1] - self.bd_data_yL = self.data_output[:,0] - self.bd_data_yR = self.data_output[:,-1] - self.bd_data_zL = self.data_output[:,:,0] - self.bd_data_zR = self.data_output[:,:,-1] + self.bd_data_yL = self.data_output[:, 0] + self.bd_data_yR = self.data_output[:, -1] + self.bd_data_zL = self.data_output[:, :, 0] + self.bd_data_zR = self.data_output[:, :, -1] self.tdim = self.data_output.size(3) - self.data_grid_t = self.data_grid_t[:self.tdim] + self.data_grid_t = self.data_grid_t[: self.tdim] XX, YY, ZZ, TT = torch.meshgrid( [self.data_grid_x, self.data_grid_y, self.data_grid_z, self.data_grid_t], indexing="ij", ) - self.data_input = torch.vstack([XX.ravel(), YY.ravel(), ZZ.ravel(), TT.ravel()]).T + self.data_input = torch.vstack( + [XX.ravel(), YY.ravel(), ZZ.ravel(), TT.ravel()] + ).T h5_file.close() self.data_output = self.data_output.reshape(-1, 5) def get_initial_condition(self): # return (self.data_grid_x[:, None], self.init_data) - return (self.data_input[::self.tdim, :], self.init_data) + return (self.data_input[:: self.tdim, :], self.init_data) def get_boundary_condition(self): # return (self.data_grid_t[:self.nt, None], self.bd_data_L, self.bd_data_R) - return (self.data_input[:self.xdim*self.ydim*self.zdim, :], - self.bd_data_xL, self.bd_data_xR, - self.bd_data_yL, self.bd_data_yR, - self.bd_data_zL, self.bd_data_zR, - ) + return ( + self.data_input[: self.xdim * self.ydim * self.zdim, :], + self.bd_data_xL, + self.bd_data_xR, + self.bd_data_yL, + self.bd_data_yR, + self.bd_data_zL, + self.bd_data_zR, + ) def get_test_data(self, n_last_time_steps, n_components=1): n_x = len(self.data_grid_x) @@ -671,7 +702,12 @@ def get_test_data(self, n_last_time_steps, n_components=1): test_output = test_output[:, :, :, -n_last_time_steps:, :] test_input = torch.vstack( - [test_input_x.ravel(), test_input_y.ravel(), test_input_z.ravel(), test_input_t.ravel()] + [ + test_input_x.ravel(), + test_input_y.ravel(), + test_input_z.ravel(), + test_input_t.ravel(), + ] ).T # stack depending on number of output components @@ -692,7 +728,9 @@ def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): n_x = len(self.data_grid_x) n_y = len(self.data_grid_y) n_z = len(self.data_grid_z) - return raveled_tensor.reshape((1, n_x, n_y, n_z, n_last_time_steps, n_components)) + return raveled_tensor.reshape( + (1, n_x, n_y, n_z, n_last_time_steps, n_components) + ) def generate_plot_input(self, time=1.0): return None @@ -701,4 +739,4 @@ def __len__(self): return len(self.data_output) def __getitem__(self, idx): - return self.data_input[idx, :], self.data_output[idx].unsqueeze(1) \ No newline at end of file + return self.data_input[idx, :], self.data_output[idx].unsqueeze(1) diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index 9128b3e..82b2acd 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -1,3 +1,4 @@ +#!/bin/bash ## 'FNO' # Advection CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_Adv.yaml ++args.filename='1D_Advection_Sols_beta0.1.hdf5' ++args.model_name='FNO' @@ -65,7 +66,7 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.aux_params=[0.5,10.] ++args.val_time=0.5 CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.aux_params=[2.,1.] ++args.val_time=0.5 CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.aux_params=[2.,10.] ++args.val_time=0.5 -# Burgers Eq. +# Burgers Eq. CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.aux_params=[0.001] CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.aux_params=[0.01] CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.aux_params=[0.1] diff --git a/pdebench/models/run_inverse.sh b/pdebench/models/run_inverse.sh index 2242dde..fe828e7 100644 --- a/pdebench/models/run_inverse.sh +++ b/pdebench/models/run_inverse.sh @@ -1,27 +1,25 @@ -# /bin/bash +#! /bin/bash # F.Alesiani, 2022, June 6th # Train forward model HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5' ++args.model_name='FNO' -HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='FNO' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 +HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='FNO' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5' ++args.model_name='Unet' -HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='Unet' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 +HYDRA_FULL_ERROR=1 python3 train_models_inverse.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='Unet' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 # Inverse HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='FNO' HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='FNO' HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5' ++args.model_name='FNO' -HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='FNO' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 +HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='FNO' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/Advection/Train/1D_Advection_Sols_beta4.0.hdf5' ++args.model_name='Unet' HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/Burgers/Train/1D_Burgers_Sols_Nu1.0.hdf5' ++args.model_name='Unet' HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/ReactionDiffusion/Train/ReacDiff_Nu1.0_Rho2.0.hdf5' ++args.model_name='Unet' -HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='Unet' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 - - +HYDRA_FULL_ERROR=1 python3 inverse/train.py ++args.filename='/1D/CFD/Train/1D_CFD_Shock_trans_Train.hdf5' ++args.model_name='Unet' ++args.in_channels=3 ++args.out_channels=3 ++args.num_channels=3 ++args.final_time=5 diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 5ebd40a..8ca3f75 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -145,22 +145,17 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import sys, os +from __future__ import annotations + import hydra from omegaconf import DictConfig -import operator -from functools import reduce -from functools import partial - -from timeit import default_timer - - @hydra.main(version_base="1.2", config_path="config", config_name="config_rdb") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": from pdebench.models.fno.train import run_training as run_training_FNO + print("FNO") run_training_FNO( if_training=cfg.args.if_training, @@ -195,6 +190,7 @@ def main(cfg: DictConfig): ) elif cfg.args.model_name == "Unet": from pdebench.models.unet.train import run_training as run_training_Unet + print("Unet") run_training_Unet( if_training=cfg.args.if_training, @@ -231,6 +227,7 @@ def main(cfg: DictConfig): elif cfg.args.model_name == "PINN": # not importing globally as DeepXDE changes some global PyTorch settings from pdebench.models.pinn.train import run_training as run_training_PINN + print("PINN") run_training_PINN( scenario=cfg.args.scenario, @@ -244,7 +241,7 @@ def main(cfg: DictConfig): root_path=cfg.args.root_path, val_num=cfg.args.val_num, if_periodic_bc=cfg.args.if_periodic_bc, - aux_params=cfg.args.aux_params + aux_params=cfg.args.aux_params, ) diff --git a/pdebench/models/train_models_inverse.py b/pdebench/models/train_models_inverse.py index c5099be..918b9c6 100644 --- a/pdebench/models/train_models_inverse.py +++ b/pdebench/models/train_models_inverse.py @@ -144,90 +144,86 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -import sys, os +from __future__ import annotations + import hydra from omegaconf import DictConfig - -import operator -from functools import reduce -from functools import partial - -from timeit import default_timer - from pdebench.models.fno.train import run_training as run_training_FNO from pdebench.models.pinn.train import run_training as run_training_PINN from pdebench.models.unet.train import run_training as run_training_Unet -@hydra.main(config_path='config', config_name='config') +@hydra.main(config_path="config", config_name="config") def main(cfg: DictConfig): print(cfg.args) - if cfg.args.model_name=='FNO': - print('FNO') - run_training_FNO(if_training=cfg.args.if_training, - continue_training=cfg.args.continue_training, - num_workers=cfg.args.num_workers, - modes=cfg.args.modes, - width=cfg.args.width, - initial_step=cfg.args.initial_step, - t_train=cfg.args.t_train, - num_channels=cfg.args.num_channels, - batch_size=cfg.args.batch_size, - epochs=cfg.args.epochs, - learning_rate=cfg.args.learning_rate, - scheduler_step=cfg.args.scheduler_step, - scheduler_gamma=cfg.args.scheduler_gamma, - model_update=cfg.args.model_update, - flnm=cfg.args.filename, - single_file=cfg.args.single_file, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - plot=cfg.args.plot, - channel_plot=cfg.args.channel_plot, - x_min=cfg.args.x_min, - x_max=cfg.args.x_max, - y_min=cfg.args.y_min, - y_max=cfg.args.y_max, - t_min=cfg.args.t_min, - t_max=cfg.args.t_max, - base_path = cfg.args.base_path, - training_type = cfg.args.training_type - ) - elif cfg.args.model_name=='Unet': - print('Unet') - run_training_Unet(if_training=cfg.args.if_training, - continue_training=cfg.args.continue_training, - num_workers=cfg.args.num_workers, - initial_step=cfg.args.initial_step, - t_train=cfg.args.t_train, - in_channels=cfg.args.in_channels, - out_channels=cfg.args.out_channels, - batch_size=cfg.args.batch_size, - unroll_step=cfg.args.unroll_step, - ar_mode=cfg.args.ar_mode, - pushforward=cfg.args.pushforward, - epochs=cfg.args.epochs, - learning_rate=cfg.args.learning_rate, - scheduler_step=cfg.args.scheduler_step, - scheduler_gamma=cfg.args.scheduler_gamma, - model_update=cfg.args.model_update, - flnm=cfg.args.filename, - single_file=cfg.args.single_file, - reduced_resolution=cfg.args.reduced_resolution, - reduced_resolution_t=cfg.args.reduced_resolution_t, - reduced_batch=cfg.args.reduced_batch, - plot=cfg.args.plot, - channel_plot=cfg.args.channel_plot, - x_min=cfg.args.x_min, - x_max=cfg.args.x_max, - y_min=cfg.args.y_min, - y_max=cfg.args.y_max, - t_min=cfg.args.t_min, - t_max=cfg.args.t_max, - base_path = cfg.args.base_path, - training_type = cfg.args.training_type - ) + if cfg.args.model_name == "FNO": + print("FNO") + run_training_FNO( + if_training=cfg.args.if_training, + continue_training=cfg.args.continue_training, + num_workers=cfg.args.num_workers, + modes=cfg.args.modes, + width=cfg.args.width, + initial_step=cfg.args.initial_step, + t_train=cfg.args.t_train, + num_channels=cfg.args.num_channels, + batch_size=cfg.args.batch_size, + epochs=cfg.args.epochs, + learning_rate=cfg.args.learning_rate, + scheduler_step=cfg.args.scheduler_step, + scheduler_gamma=cfg.args.scheduler_gamma, + model_update=cfg.args.model_update, + flnm=cfg.args.filename, + single_file=cfg.args.single_file, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + plot=cfg.args.plot, + channel_plot=cfg.args.channel_plot, + x_min=cfg.args.x_min, + x_max=cfg.args.x_max, + y_min=cfg.args.y_min, + y_max=cfg.args.y_max, + t_min=cfg.args.t_min, + t_max=cfg.args.t_max, + base_path=cfg.args.base_path, + training_type=cfg.args.training_type, + ) + elif cfg.args.model_name == "Unet": + print("Unet") + run_training_Unet( + if_training=cfg.args.if_training, + continue_training=cfg.args.continue_training, + num_workers=cfg.args.num_workers, + initial_step=cfg.args.initial_step, + t_train=cfg.args.t_train, + in_channels=cfg.args.in_channels, + out_channels=cfg.args.out_channels, + batch_size=cfg.args.batch_size, + unroll_step=cfg.args.unroll_step, + ar_mode=cfg.args.ar_mode, + pushforward=cfg.args.pushforward, + epochs=cfg.args.epochs, + learning_rate=cfg.args.learning_rate, + scheduler_step=cfg.args.scheduler_step, + scheduler_gamma=cfg.args.scheduler_gamma, + model_update=cfg.args.model_update, + flnm=cfg.args.filename, + single_file=cfg.args.single_file, + reduced_resolution=cfg.args.reduced_resolution, + reduced_resolution_t=cfg.args.reduced_resolution_t, + reduced_batch=cfg.args.reduced_batch, + plot=cfg.args.plot, + channel_plot=cfg.args.channel_plot, + x_min=cfg.args.x_min, + x_max=cfg.args.x_max, + y_min=cfg.args.y_min, + y_max=cfg.args.y_max, + t_min=cfg.args.t_min, + t_max=cfg.args.t_max, + base_path=cfg.args.base_path, + training_type=cfg.args.training_type, + ) elif cfg.args.model_name == "PINN": print("PINN") run_training_PINN( @@ -239,6 +235,7 @@ def main(cfg: DictConfig): seed=cfg.args.seed, ) + if __name__ == "__main__": main() - print("Done.") \ No newline at end of file + print("Done.") diff --git a/pdebench/models/unet/train.py b/pdebench/models/unet/train.py index 7b301c9..30bdcfa 100644 --- a/pdebench/models/unet/train.py +++ b/pdebench/models/unet/train.py @@ -1,361 +1,402 @@ -# -*- coding: utf-8 -*- +from __future__ import annotations -import sys -import torch -import numpy as np +import logging import pickle -import torch.nn as nn -import torch.nn.functional as F - -import operator -from functools import reduce -from functools import partial - +from pathlib import Path from timeit import default_timer +import numpy as np +import torch +from pdebench.models.metrics import metrics +from pdebench.models.unet.unet import UNet1d, UNet2d, UNet3d +from pdebench.models.unet.utils import UNetDatasetMult, UNetDatasetSingle +from torch import nn + # torch.manual_seed(0) # np.random.seed(0) +logging.basicConfig(level=logging.INFO, filename=__name__) +logging.root.setLevel(logging.INFO) -device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -from pdebench.models.unet.unet import UNet1d, UNet2d, UNet3d -from pdebench.models.unet.utils import UNetDatasetSingle, UNetDatasetMult -from pdebench.models.metrics import metrics -def run_training(if_training, - continue_training, - num_workers, - initial_step, - t_train, - in_channels, - out_channels, - batch_size, - unroll_step, - ar_mode, - pushforward, - epochs, - learning_rate, - scheduler_step, - scheduler_gamma, - model_update, - flnm, - single_file, - reduced_resolution, - reduced_resolution_t, - reduced_batch, - plot, - channel_plot, - x_min, - x_max, - y_min, - y_max, - t_min, - t_max, - base_path='../data/', - training_type='autoregressive' - ): - - print(f'Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}') - +def run_training( + if_training, + continue_training, + num_workers, + initial_step, + t_train, + in_channels, + out_channels, + batch_size, + unroll_step, + ar_mode, + pushforward, + epochs, + learning_rate, + scheduler_step, + scheduler_gamma, + model_update, + flnm, + single_file, + reduced_resolution, + reduced_resolution_t, + reduced_batch, + plot, + channel_plot, + x_min, + x_max, + y_min, + y_max, + t_min, + t_max, + base_path="../data/", + training_type="autoregressive", +): + msg = f"Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}" + logging.info(msg) + ################################################################ # load data ################################################################ - + if single_file: # filename - model_name = flnm[:-5] + '_Unet' - + model_name = flnm[:-5] + "_Unet" + # Initialize the dataset and dataloader - train_data = UNetDatasetSingle(flnm, - saved_folder=base_path, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - initial_step=initial_step) - val_data = UNetDatasetSingle(flnm, - saved_folder=base_path, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - initial_step=initial_step, - if_test=True) - + train_data = UNetDatasetSingle( + flnm, + saved_folder=base_path, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + initial_step=initial_step, + ) + val_data = UNetDatasetSingle( + flnm, + saved_folder=base_path, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + initial_step=initial_step, + if_test=True, + ) + else: # filename - model_name = flnm + '_Unet' - - train_data = UNetDatasetMult(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - saved_folder=base_path) - val_data = UNetDatasetMult(flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, - if_test=True, - saved_folder=base_path) - - train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, - num_workers=num_workers, shuffle=True) - val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, - num_workers=num_workers, shuffle=False) - + model_name = flnm + "_Unet" + + train_data = UNetDatasetMult( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + saved_folder=base_path, + ) + val_data = UNetDatasetMult( + flnm, + reduced_resolution=reduced_resolution, + reduced_resolution_t=reduced_resolution_t, + reduced_batch=reduced_batch, + if_test=True, + saved_folder=base_path, + ) + + train_loader = torch.utils.data.DataLoader( + train_data, batch_size=batch_size, num_workers=num_workers, shuffle=True + ) + val_loader = torch.utils.data.DataLoader( + val_data, batch_size=batch_size, num_workers=num_workers, shuffle=False + ) + ################################################################ # training and evaluation ################################################################ - - #model = UNet2d(in_channels, out_channels).to(device) + + # model = UNet2d(in_channels, out_channels).to(device) _, _data = next(iter(val_loader)) dimensions = len(_data.shape) - print('Spatial Dimension', dimensions - 3) - if training_type in ['autoregressive']: + msg = f"Spatial Dimension: {dimensions - 3}" + logging.info(msg) + if training_type in ["autoregressive"]: if dimensions == 4: - model = UNet1d(in_channels*initial_step, out_channels).to(device) + model = UNet1d(in_channels * initial_step, out_channels).to(device) elif dimensions == 5: - model = UNet2d(in_channels*initial_step, out_channels).to(device) + model = UNet2d(in_channels * initial_step, out_channels).to(device) elif dimensions == 6: - model = UNet3d(in_channels*initial_step, out_channels).to(device) - if training_type in ['single']: + model = UNet3d(in_channels * initial_step, out_channels).to(device) + if training_type in ["single"]: if dimensions == 4: model = UNet1d(in_channels, out_channels).to(device) elif dimensions == 5: model = UNet2d(in_channels, out_channels).to(device) elif dimensions == 6: model = UNet3d(in_channels, out_channels).to(device) - + # Set maximum time step of the data to train - if t_train > _data.shape[-2]: - t_train = _data.shape[-2] + t_train = min(t_train, _data.shape[-2]) # Set maximum of unrolled time step for the pushforward trick if t_train - unroll_step < 1: unroll_step = t_train - 1 - if training_type in ['autoregressive']: + if training_type in ["autoregressive"]: if ar_mode: if pushforward: - model_name = model_name + '-PF-' + str(unroll_step) + model_name = model_name + "-PF-" + str(unroll_step) if not pushforward: unroll_step = _data.shape[-2] - model_name = model_name + '-AR' + model_name = model_name + "-AR" else: - model_name = model_name + '-1-step' - + model_name = model_name + "-1-step" + model_path = model_name + ".pt" - + total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - print(f'Total parameters = {total_params}') - - optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=1e-4) - scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=scheduler_step, gamma=scheduler_gamma) - + msg = f"Total parameters = {total_params}" + logging.info(msg) + + optimizer = torch.optim.Adam( + model.parameters(), lr=learning_rate, weight_decay=1e-4 + ) + scheduler = torch.optim.lr_scheduler.StepLR( + optimizer, step_size=scheduler_step, gamma=scheduler_gamma + ) + loss_fn = nn.MSELoss(reduction="mean") - loss_val_min = np.infty - + loss_val_min = np.inf + start_epoch = 0 if not if_training: checkpoint = torch.load(model_path, map_location=device) - model.load_state_dict(checkpoint['model_state_dict']) + model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) model.eval() - Lx, Ly, Lz = 1., 1., 1. - errs = metrics(val_loader, model, Lx, Ly, Lz, plot, channel_plot, - model_name, x_min, x_max, y_min, y_max, - t_min, t_max, mode='Unet', initial_step=initial_step) - pickle.dump(errs, open(model_name+'.pickle', "wb")) - + Lx, Ly, Lz = 1.0, 1.0, 1.0 + errs = metrics( + val_loader, + model, + Lx, + Ly, + Lz, + plot, + channel_plot, + model_name, + x_min, + x_max, + y_min, + y_max, + t_min, + t_max, + mode="Unet", + initial_step=initial_step, + ) + pickle.dump(errs, Path.open(model_name + ".pickle", "wb")) + return # If desired, restore the network by loading the weights saved in the .pt # file if continue_training: - print('Restoring model (that is the network\'s weights) from file...') + msg = "Restoring model (that is the network's weights) from file..." + logging.info(msg) checkpoint = torch.load(model_path, map_location=device) - model.load_state_dict(checkpoint['model_state_dict']) + model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) model.train() - + # Load optimizer state dict - optimizer.load_state_dict(checkpoint['optimizer_state_dict']) + optimizer.load_state_dict(checkpoint["optimizer_state_dict"]) for state in optimizer.state.values(): for k, v in state.items(): if isinstance(v, torch.Tensor): state[k] = v.to(device) - - start_epoch = checkpoint['epoch'] - loss_val_min = checkpoint['loss'] - print('start training...') - + start_epoch = checkpoint["epoch"] + loss_val_min = checkpoint["loss"] + + msg = "start training..." + logging.info(msg) + if ar_mode: - for ep in range(start_epoch, epochs): model.train() t1 = default_timer() train_l2_step = 0 train_l2_full = 0 - + for xx, yy in train_loader: loss = 0 - + # xx: input tensor (first few time steps) [b, x1, ..., xd, t_init, v] # yy: target tensor [b, x1, ..., xd, t, v] # grid: meshgrid [b, x1, ..., xd, dims] - xx = xx.to(device) - yy = yy.to(device) - - if training_type in ['autoregressive']: + xx_tensor = xx.to(device) + yy_tensor = yy.to(device) + if training_type in ["autoregressive"]: # Initialize the prediction tensor pred = yy[..., :initial_step, :] - + # Extract shape of the input tensor for reshaping (i.e. stacking the # time and channels dimension together) - inp_shape = list(xx.shape) + inp_shape = list(xx_tensor.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - + # Autoregressive loop for t in range(initial_step, t_train): - - if t < t_train-unroll_step: + if t < t_train - unroll_step: with torch.no_grad(): # Reshape input tensor into [b, x1, ..., xd, t_init*v] - inp = xx.reshape(inp_shape) + inp = xx_tensor.reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - + # Extract target at current time step - y = yy[..., t:t+1, :] - + y = yy_tensor[..., t : t + 1, :] + # Model run temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) - + # Concatenate the prediction at current time step into the # prediction tensor pred = torch.cat((pred, im), -2) - + # Concatenate the prediction at the current time step to be used # as input for the next time step - xx = torch.cat((xx[..., 1:, :], im), dim=-2) - + xx_tensor = torch.cat( + (xx_tensor[..., 1:, :], im), dim=-2 + ) + else: # Reshape input tensor into [b, x1, ..., xd, t_init*v] - inp = xx.reshape(inp_shape) + inp = xx_tensor.reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - + # Extract target at current time step - y = yy[..., t:t+1, :] - + y = yy_tensor[..., t : t + 1, :] + # Model run temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) - + # Loss calculation - loss += loss_fn(im.reshape(batch_size, -1), y.reshape(batch_size, -1)) - + loss += loss_fn( + im.reshape(batch_size, -1), y.reshape(batch_size, -1) + ) + # Concatenate the prediction at current time step into the # prediction tensor pred = torch.cat((pred, im), -2) - + # Concatenate the prediction at the current time step to be used # as input for the next time step - xx = torch.cat((xx[..., 1:, :], im), dim=-2) - + xx_tensor = torch.cat((xx_tensor[..., 1:, :], im), dim=-2) + train_l2_step += loss.item() - _batch = yy.size(0) - _yy = yy[..., :t_train, :] + _batch = yy_tensor.size(0) + _yy = yy_tensor[..., :t_train, :] l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() - + optimizer.zero_grad() loss.backward() optimizer.step() - if training_type in ['single']: - x = xx[..., 0 , :] - y = yy[..., t_train-1:t_train, :] + if training_type in ["single"]: + x = xx[..., 0, :] + y = yy[..., t_train - 1 : t_train, :] pred = model(x.permute([0, 2, 1])).permute([0, 2, 1]) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) - + train_l2_step += loss.item() train_l2_full += loss.item() - + optimizer.zero_grad() loss.backward() optimizer.step() - if ep % model_update == 0: val_l2_step = 0 val_l2_full = 0 with torch.no_grad(): for xx, yy in val_loader: loss = 0 - xx = xx.to(device) - yy = yy.to(device) - - if training_type in ['autoregressive']: - pred = yy[..., :initial_step, :] + xx_tensor = xx.to(device) + yy_tensor = yy.to(device) + + if training_type in ["autoregressive"]: + pred = yy_tensor[..., :initial_step, :] inp_shape = list(xx.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - + for t in range(initial_step, t_train): - inp = xx.reshape(inp_shape) + inp = xx_tensor.reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - y = yy[..., t:t+1, :] + y = yy_tensor[..., t : t + 1, :] temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) - loss += loss_fn(im.reshape(batch_size, -1), y.reshape(batch_size, -1)) - + loss += loss_fn( + im.reshape(batch_size, -1), + y.reshape(batch_size, -1), + ) + pred = torch.cat((pred, im), -2) - - xx = torch.cat((xx[..., 1:, :], im), dim=-2) - + + xx_tensor = torch.cat( + (xx_tensor[..., 1:, :], im), dim=-2 + ) + val_l2_step += loss.item() _batch = yy.size(0) _pred = pred[..., initial_step:t_train, :] - _yy = yy[..., initial_step:t_train, :] - val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() - - if training_type in ['single']: - x = xx[..., 0 , :] - y = yy[..., t_train-1:t_train, :] + _yy = yy_tensor[..., initial_step:t_train, :] + val_l2_full += loss_fn( + _pred.reshape(_batch, -1), _yy.reshape(_batch, -1) + ).item() + + if training_type in ["single"]: + x = xx[..., 0, :] + y = yy[..., t_train - 1 : t_train, :] pred = model(x.permute([0, 2, 1])).permute([0, 2, 1]) _batch = yy.size(0) loss += loss_fn(pred.reshape(_batch, -1), y.reshape(_batch, -1)) - + val_l2_step += loss.item() val_l2_full += loss.item() - if val_l2_full < loss_val_min: + if val_l2_full < loss_val_min: loss_val_min = val_l2_full - torch.save({ - 'epoch': ep, - 'model_state_dict': model.state_dict(), - 'optimizer_state_dict': optimizer.state_dict(), - 'loss': loss_val_min - }, model_path) - + torch.save( + { + "epoch": ep, + "model_state_dict": model.state_dict(), + "optimizer_state_dict": optimizer.state_dict(), + "loss": loss_val_min, + }, + model_path, + ) + t2 = default_timer() scheduler.step() - print('epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}'\ - .format(ep, loss.item(), t2 - t1, train_l2_step, val_l2_step)) + msg = f"epoch: {ep}, loss: {loss.item():.5f}, t2-t1: {t2 - t1:.5f}, trainL2: {train_l2_step:.5f}, testL2: {val_l2_step:.5f}" + logging.info(msg) else: for ep in range(start_epoch, epochs): @@ -363,115 +404,125 @@ def run_training(if_training, t1 = default_timer() train_l2_step = 0 train_l2_full = 0 - + for xx, yy in train_loader: loss = 0 - + # xx: input tensor (first few time steps) [b, x1, ..., xd, t_init, v] # yy: target tensor [b, x1, ..., xd, t, v] - xx = xx.to(device) - yy = yy.to(device) - + xx_tensor = xx.to(device) + yy_tensor = yy.to(device) + # Initialize the prediction tensor - pred = yy[..., :initial_step, :] - + pred = yy_tensor[..., :initial_step, :] + # Extract shape of the input tensor for reshaping (i.e. stacking the # time and channels dimension together) - inp_shape = list(xx.shape) + inp_shape = list(xx_tensor.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - + # Autoregressive loop for t in range(initial_step, t_train): - # Reshape input tensor into [b, x1, ..., xd, t_init*v] - inp = yy[..., t-initial_step:t, :].reshape(inp_shape) + inp = yy_tensor[..., t - initial_step : t, :].reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) inp = torch.normal(inp, 0.001) - + # Extract target at current time step - y = yy[..., t:t+1, :] - + y = yy_tensor[..., t : t + 1, :] + # Model run temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) - + # Loss calculation - loss += loss_fn(im.reshape(batch_size, -1), y.reshape(batch_size, -1)) - + loss += loss_fn( + im.reshape(batch_size, -1), y.reshape(batch_size, -1) + ) + # Concatenate the prediction at current time step into the # prediction tensor pred = torch.cat((pred, im), -2) - + # Concatenate the prediction at the current time step to be used # as input for the next time step # xx = torch.cat((xx[..., 1:, :], im), dim=-2) - + train_l2_step += loss.item() _batch = yy.size(0) - _yy = yy[..., :t_train, :] # if t_train is not -1 + _yy = yy_tensor[..., :t_train, :] # if t_train is not -1 l2_full = loss_fn(pred.reshape(_batch, -1), _yy.reshape(_batch, -1)) train_l2_full += l2_full.item() - + optimizer.zero_grad() loss.backward() optimizer.step() - + if ep % model_update == 0 or ep == epochs: val_l2_step = 0 val_l2_full = 0 with torch.no_grad(): for xx, yy in val_loader: loss = 0 - xx = xx.to(device) - yy = yy.to(device) - - pred = yy[..., :initial_step, :] - inp_shape = list(xx.shape) + xx_tensor = xx.to(device) + yy_tensor = yy.to(device) + + pred = yy_tensor[..., :initial_step, :] + inp_shape = list(xx_tensor.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - + for t in range(initial_step, t_train): - inp = yy[..., t-initial_step:t, :].reshape(inp_shape) + inp = yy_tensor[..., t - initial_step : t, :].reshape( + inp_shape + ) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1,len(inp.shape)-1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - y = yy[..., t:t+1, :] + y = yy_tensor[..., t : t + 1, :] temp_shape = [0] - temp_shape.extend([i for i in range(2,len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) - loss += loss_fn(im.reshape(batch_size, -1), y.reshape(batch_size, -1)) - + loss += loss_fn( + im.reshape(batch_size, -1), y.reshape(batch_size, -1) + ) + pred = torch.cat((pred, im), -2) - + val_l2_step += loss.item() _batch = yy.size(0) _pred = pred[..., initial_step:t_train, :] - _yy = yy[..., initial_step:t_train, :] # if t_train is not -1 - val_l2_full += loss_fn(_pred.reshape(_batch, -1), _yy.reshape(_batch, -1)).item() - - if val_l2_full < loss_val_min: + # if t_train is not -1 + _yy = yy_tensor[..., initial_step:t_train, :] + val_l2_full += loss_fn( + _pred.reshape(_batch, -1), _yy.reshape(_batch, -1) + ).item() + + if val_l2_full < loss_val_min: loss_val_min = val_l2_full - torch.save({ - 'epoch': ep, - 'model_state_dict': model.state_dict(), - 'optimizer_state_dict': optimizer.state_dict(), - 'loss': loss_val_min - }, model_path) - - + torch.save( + { + "epoch": ep, + "model_state_dict": model.state_dict(), + "optimizer_state_dict": optimizer.state_dict(), + "loss": loss_val_min, + }, + model_path, + ) + t2 = default_timer() scheduler.step() - print('epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}'\ - .format(ep, loss.item(), t2 - t1, train_l2_step, val_l2_step)) + msg = f"epoch: {ep}, loss: {loss.item():.5f}, t2-t1: {t2 - t1:.5f}, trainL2: {train_l2_step:.5f}, testL2: {val_l2_step:.5f}" + logging.info(msg) if __name__ == "__main__": - run_training() - print("Done.") \ No newline at end of file + msg = "Done." + logging.info(msg) diff --git a/pdebench/models/unet/unet.py b/pdebench/models/unet/unet.py index 7bd828b..f06e7e1 100644 --- a/pdebench/models/unet/unet.py +++ b/pdebench/models/unet/unet.py @@ -18,14 +18,15 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. """ +from __future__ import annotations + from collections import OrderedDict import torch -import torch.nn as nn +from torch import nn class UNet1d(nn.Module): - def __init__(self, in_channels=3, out_channels=1, init_features=32): super(UNet1d, self).__init__() @@ -119,7 +120,6 @@ def _block(in_channels, features, name): class UNet2d(nn.Module): - def __init__(self, in_channels=3, out_channels=1, init_features=32): super(UNet2d, self).__init__() @@ -210,10 +210,9 @@ def _block(in_channels, features, name): ] ) ) - -class UNet3d(nn.Module): +class UNet3d(nn.Module): def __init__(self, in_channels=3, out_channels=1, init_features=32): super(UNet3d, self).__init__() @@ -303,4 +302,4 @@ def _block(in_channels, features, name): (name + "tanh2", nn.Tanh()), ] ) - ) \ No newline at end of file + ) diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index d38c242..d8c61cf 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ @@ -147,165 +146,252 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ +from __future__ import annotations -import torch -from torch.utils.data import Dataset -from torch.utils.data import DataLoader +import math as mt import os -import glob + import h5py import numpy as np -import math as mt +import torch +from torch.utils.data import Dataset + class UNetDatasetSingle(Dataset): - def __init__(self, filename, - initial_step=10, - saved_folder='../data/', - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, - if_test=False, - test_ratio=0.1, - num_samples_max = -1): + def __init__( + self, + filename, + initial_step=10, + saved_folder="../data/", + reduced_resolution=1, + reduced_resolution_t=1, + reduced_batch=1, + if_test=False, + test_ratio=0.1, + num_samples_max=-1, + ): """ - + :param filename: filename that contains the dataset :type filename: STR :param filenum: array containing indices of filename included in the dataset :type filenum: ARRAY """ - + # Define path to files root_path = os.path.abspath(saved_folder + filename) - assert filename[-2:] != 'h5', 'HDF5 data is assumed!!' - - with h5py.File(root_path, 'r') as f: + assert filename[-2:] != "h5", "HDF5 data is assumed!!" + + with h5py.File(root_path, "r") as f: keys = list(f.keys()) keys.sort() - if 'tensor' not in keys: - _data = np.array(f['density'], dtype=np.float32) # batch, time, x,... + if "tensor" not in keys: + _data = np.array(f["density"], dtype=np.float32) # batch, time, x,... idx_cfd = _data.shape - if len(idx_cfd)==3: # 1D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 3], - dtype=np.float32) - #density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + if len(idx_cfd) == 3: # 1D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 3, + ], + dtype=np.float32, + ) + # density + _data = _data[ + ::reduced_batch, ::reduced_resolution_t, ::reduced_resolution + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, ::reduced_resolution_t, ::reduced_resolution + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = np.array(f["Vx"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, ::reduced_resolution_t, ::reduced_resolution + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) - self.data[...,2] = _data # batch, x, t, ch - - if len(idx_cfd)==4: # 2D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 4], - dtype=np.float32) + self.data[..., 2] = _data # batch, x, t, ch + + if len(idx_cfd) == 4: # 2D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + idx_cfd[3] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 4, + ], + dtype=np.float32, + ) # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array(f["Vx"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,2] = _data # batch, x, t, ch + self.data[..., 2] = _data # batch, x, t, ch # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution] + _data = np.array(f["Vy"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 1)) - self.data[...,3] = _data # batch, x, t, ch - - if len(idx_cfd)==5: # 3D - self.data = np.zeros([idx_cfd[0]//reduced_batch, - idx_cfd[2]//reduced_resolution, - idx_cfd[3]//reduced_resolution, - idx_cfd[4]//reduced_resolution, - mt.ceil(idx_cfd[1]/reduced_resolution_t), - 5], - dtype=np.float32) + self.data[..., 3] = _data # batch, x, t, ch + + if len(idx_cfd) == 5: # 3D + self.data = np.zeros( + [ + idx_cfd[0] // reduced_batch, + idx_cfd[2] // reduced_resolution, + idx_cfd[3] // reduced_resolution, + idx_cfd[4] // reduced_resolution, + mt.ceil(idx_cfd[1] / reduced_resolution_t), + 5, + ], + dtype=np.float32, + ) # density - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,0] = _data # batch, x, t, ch + self.data[..., 0] = _data # batch, x, t, ch # pressure - _data = np.array(f['pressure'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array( + f["pressure"], dtype=np.float32 + ) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,1] = _data # batch, x, t, ch + self.data[..., 1] = _data # batch, x, t, ch # Vx - _data = np.array(f['Vx'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array(f["Vx"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,2] = _data # batch, x, t, ch + self.data[..., 2] = _data # batch, x, t, ch # Vy - _data = np.array(f['Vy'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array(f["Vy"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,3] = _data # batch, x, t, ch + self.data[..., 3] = _data # batch, x, t, ch # Vz - _data = np.array(f['Vz'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution,::reduced_resolution,::reduced_resolution] + _data = np.array(f["Vz"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + ::reduced_resolution_t, + ::reduced_resolution, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data, (0, 2, 3, 4, 1)) - self.data[...,4] = _data # batch, x, t, ch + self.data[..., 4] = _data # batch, x, t, ch else: # scalar equations ## data dim = [t, x1, ..., xd, v] - _data = np.array(f['tensor'], dtype=np.float32) # batch, time, x,... + _data = np.array(f["tensor"], dtype=np.float32) # batch, time, x,... if len(_data.shape) == 3: # 1D - _data = _data[::reduced_batch,::reduced_resolution_t,::reduced_resolution] + _data = _data[ + ::reduced_batch, ::reduced_resolution_t, ::reduced_resolution + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :], (0, 2, 1)) self.data = _data[:, :, :, None] # batch, x, t, ch if len(_data.shape) == 4: # 2D Darcy flow # u: label - _data = _data[::reduced_batch,:,::reduced_resolution,::reduced_resolution] + _data = _data[ + ::reduced_batch, :, ::reduced_resolution, ::reduced_resolution + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) - #if _data.shape[-1]==1: # if nt==1 + # if _data.shape[-1]==1: # if nt==1 # _data = np.tile(_data, (1, 1, 1, 2)) self.data = _data # nu: input - _data = np.array(f['nu'], dtype=np.float32) # batch, time, x,... - _data = _data[::reduced_batch, None,::reduced_resolution,::reduced_resolution] + _data = np.array(f["nu"], dtype=np.float32) # batch, time, x,... + _data = _data[ + ::reduced_batch, + None, + ::reduced_resolution, + ::reduced_resolution, + ] ## convert to [x1, ..., xd, t, v] _data = np.transpose(_data[:, :, :, :], (0, 2, 3, 1)) self.data = np.concatenate([_data, self.data], axis=-1) self.data = self.data[:, :, :, :, None] # batch, x, y, t, ch - if num_samples_max>0: - num_samples_max = min(num_samples_max,self.data.shape[0]) + if num_samples_max > 0: + num_samples_max = min(num_samples_max, self.data.shape[0]) else: num_samples_max = self.data.shape[0] @@ -317,29 +403,30 @@ def __init__(self, filename, # Time steps used as initial conditions self.initial_step = initial_step - - self.data = torch.tensor(self.data) + self.data = torch.tensor(self.data) def __len__(self): return len(self.data) - + def __getitem__(self, idx): - - return self.data[idx,...,:self.initial_step,:], self.data[idx] - + return self.data[idx, ..., : self.initial_step, :], self.data[idx] + class UNetDatasetMult(Dataset): - def __init__(self, filename, - initial_step=10, - saved_folder='../data/', - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, - if_test=False, test_ratio=0.1 - ): + def __init__( + self, + filename, + initial_step=10, + saved_folder="../data/", + reduced_resolution=1, + reduced_resolution_t=1, + reduced_batch=1, + if_test=False, + test_ratio=0.1, + ): """ - + :param filename: filename that contains the dataset :type filename: STR :param filenum: array containing indices of filename included in the dataset @@ -348,39 +435,38 @@ def __init__(self, filename, :type initial_step: INT, optional """ - + # Define path to files self.file_path = os.path.abspath(saved_folder + filename + ".h5") - + # Extract list of seeds - with h5py.File(self.file_path, 'r') as h5_file: + with h5py.File(self.file_path, "r") as h5_file: data_list = sorted(h5_file.keys()) - test_idx = int(len(data_list) * (1-test_ratio)) + test_idx = int(len(data_list) * (1 - test_ratio)) if if_test: self.data_list = np.array(data_list[test_idx:]) else: self.data_list = np.array(data_list[:test_idx]) - + # Time steps used as initial conditions self.initial_step = initial_step def __len__(self): return len(self.data_list) - + def __getitem__(self, idx): - # Open file and read data - with h5py.File(self.file_path, 'r') as h5_file: + with h5py.File(self.file_path, "r") as h5_file: seed_group = h5_file[self.data_list[idx]] - + # data dim = [t, x1, ..., xd, v] - data = np.array(seed_group["data"], dtype='f') + data = np.array(seed_group["data"], dtype="f") data = torch.tensor(data, dtype=torch.float) - + # convert to [x1, ..., xd, t, v] - permute_idx = list(range(1,len(data.shape)-1)) + permute_idx = list(range(1, len(data.shape) - 1)) permute_idx.extend(list([0, -1])) data = data.permute(permute_idx) - - return data[...,:self.initial_step,:], data \ No newline at end of file + + return data[..., : self.initial_step, :], data diff --git a/pyproject.toml b/pyproject.toml index 3e1d3f1..9ff674d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" [project] requires-python = ">=3.9,<3.11" @@ -19,26 +19,23 @@ authors = [ ] license = {file = "LICENSE.txt"} dependencies = [ - "scipy", - "matplotlib", - "h5py", - "pandas", - "python-dotenv", - "hydra-core", + "scipy", + "numpy<2", + "matplotlib", + "h5py", + "pandas", + "python-dotenv", + "hydra-core", "torch~=1.13.0", - "torchvision~=0.14.1", - "deepxde~=1.1.3", - "pyro-ppl", + "torchvision~=0.14.1", + "deepxde~=1.1.3", + "pyro-ppl", "tqdm", ] -[project.urls] -Homepage = "https://github.com/pdebench/PDEBenchm" -Documentation = "https://github.com/pdebench/PDEBench" -Repository = "https://github.com/pdebench/PDEBench" - [project.optional-dependencies] datagen310 = [ + "six", "clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93", "dash", "phiflow", @@ -50,6 +47,7 @@ datagen310 = [ "jaxlib @ https://storage.googleapis.com/jax-releases/cuda11/jaxlib-0.4.11+cuda11.cudnn86-cp310-cp310-manylinux2014_x86_64.whl", ] datagen39 = [ + "six", "clawpack@git+https://github.com/clawpack/clawpack.git@d619d6835ce128a0421aa52d70d2a6c9d9d1ce93", "dash", "phiflow", @@ -61,5 +59,144 @@ datagen39 = [ "jaxlib @ https://storage.googleapis.com/jax-releases/cuda11/jaxlib-0.4.11+cuda11.cudnn86-cp39-cp39-manylinux2014_x86_64.whl" ] +test = [ + "pytest >=6", + "pytest-cov >=3", + "jax", # cpu only + "nox" +] + +docs = [ + "sphinx>=7.0", + "myst_parser>=0.13", + "sphinx_copybutton", + "sphinx_autodoc_typehints", + "furo>=2023.08.17" +] + +dev = ["anybadge", + "ruff", + "pytest", + "pytest-coverage", + "pytest-mypy", + "hatchling", + "nox", + "pre-commit"] + +[project.urls] +Homepage = "https://github.com/pdebench/PDEBenchm" +Documentation = "https://github.com/pdebench/PDEBench" +Repository = "https://github.com/pdebench/PDEBench" + +[tool.hatch] +version.source = "vcs" +build.hooks.vcs.version-file = "pdebench/_version.py" +build.include = [ + "pdebench" +] +metadata.allow-direct-references = true + +[tool.hatch.envs.default] +features = ["test"] +scripts.test = "pytest {args}" + + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"] +xfail_strict = true +filterwarnings = [ + "error", +] +log_cli_level = "INFO" +testpaths = [ + "tests", +] + + +[tool.coverage] +run.source = ["pdebench"] +report.exclude_also = [ + '\.\.\.', + 'if typing.TYPE_CHECKING:', +] + +[tool.mypy] +files = ["pdebench", "tests"] +python_version = "3.8" +warn_unused_configs = true +strict = true +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +warn_unreachable = true +disallow_untyped_defs = false +disallow_incomplete_defs = false +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "pdebench.*" +disallow_untyped_defs = true +disallow_incomplete_defs = true + +[tool.ruff] +src = ["pdebench"] + +[tool.ruff.format] +exclude = ["*.pyi"] + +[tool.ruff.lint] +extend-select = [ + "B", # flake8-bugbear + "I", # isort + "ARG", # flake8-unused-arguments + "C4", # flake8-comprehensions + "EM", # flake8-errmsg + "ICN", # flake8-import-conventions + "G", # flake8-logging-format + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "RET", # flake8-return + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 + "EXE", # flake8-executable + "NPY", # NumPy specific rules + "PD", # pandas-vet +] +ignore = [ + "PLR09", # Too many <...> + "PLR2004", # Magic value used in comparison + "ISC001", # Conflicts with formatter + "UP007" +] +isort.required-imports = ["from __future__ import annotations"] +# Uncomment if using a _compat.typing backport +# typing-modules = ["pdebench._compat.typing"] + +[tool.ruff.lint.per-file-ignores] +"tests/**" = ["T20"] +"noxfile.py" = ["T20"] + + +[tool.pylint] +py-version = "3.8" +ignore-paths = [".*/_version.py"] +reports.output-format = "colorized" +similarities.ignore-imports = "yes" +messages_control.disable = [ + "design", + "fixme", + "line-too-long", + "missing-module-docstring", + "wrong-import-position", +] + [tool.setuptools.dynamic] -readme = {file = ["README.md"], content-type = "text/markdown"} \ No newline at end of file +readme = {file = ["README.md"], content-type = "text/markdown"} + +[project.scripts] +velocity2vorticity = "pdebench.data_gen.velocity2vorticity:convert_velocity" diff --git a/tests/test_vorticity.py b/tests/test_vorticity.py new file mode 100644 index 0000000..b984b20 --- /dev/null +++ b/tests/test_vorticity.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import jax.numpy as jnp +import numpy as np +import pytest +from pdebench.data_gen.src.vorticity import ( + compute_spectral_vorticity_jnp, + compute_spectral_vorticity_np, +) + + +@pytest.fixture() +def generate_random_spectral_velvor() -> tuple[np.ndarray, np.ndarray]: + """Generate random 5D velocity- and corresponding vorticity field + + :return: Velocity- and vorticity field + :rtype: tuple[np.ndarray, np.ndarray] + """ + generator = np.random.default_rng(seed=None) + vel = generator.uniform(size=(10, 16, 32, 32, 3)) + vx = vel[..., 0] + vy = vel[..., 1] + vz = vel[..., 2] + + fxy = np.fft.fft(vx, axis=2) + fxz = np.fft.fft(vx, axis=3) + fyx = np.fft.fft(vy, axis=1) + fyz = np.fft.fft(vy, axis=3) + fzx = np.fft.fft(vz, axis=1) + fzy = np.fft.fft(vz, axis=2) + + kappa_xy = 2.0 * np.pi * np.fft.fftfreq(vel.shape[2], 1.0 / vel.shape[2]) + kappa_xz = 2.0 * np.pi * np.fft.fftfreq(vel.shape[3], 1.0 / vel.shape[3]) + kappa_yx = 2.0 * np.pi * np.fft.fftfreq(vel.shape[1], 1.0 / vel.shape[1]) + kappa_yz = 2.0 * np.pi * np.fft.fftfreq(vel.shape[3], 1.0 / vel.shape[3]) + kappa_zx = 2.0 * np.pi * np.fft.fftfreq(vel.shape[1], 1.0 / vel.shape[1]) + kappa_zy = 2.0 * np.pi * np.fft.fftfreq(vel.shape[2], 1.0 / vel.shape[2]) + + vxy = np.fft.ifft(1j * kappa_xy[None, None, :, None] * fxy, axis=2).real + vyx = np.fft.ifft(1j * kappa_yx[None, :, None, None] * fyx, axis=1).real + vxz = np.fft.ifft(1j * kappa_xz[None, None, None, :] * fxz, axis=3).real + vzx = np.fft.ifft(1j * kappa_zx[None, :, None, None] * fzx, axis=1).real + vyz = np.fft.ifft(1j * kappa_yz[None, None, None, :] * fyz, axis=3).real + vzy = np.fft.ifft(1j * kappa_zy[None, None, :, None] * fzy, axis=2).real + + omegax = vzy - vyz + omegay = vxz - vzx + omegaz = vyx - vxy + + omega = np.concatenate( + [omegax[..., None], omegay[..., None], omegaz[..., None]], axis=-1 + ) + + return vel, omega + + +def test_vorticity_np(generate_random_spectral_velvor) -> None: + """Test approximated vorticity by spectral derivation""" + vel, vort = generate_random_spectral_velvor + dx = 1.0 / vel.shape[1] + dy = 1.0 / vel.shape[2] + dz = 1.0 / vel.shape[3] + + vort_np = compute_spectral_vorticity_np(vel, dx, dy, dz) + np.testing.assert_almost_equal(vort_np, vort) + + +def test_vorticity_jnp(generate_random_spectral_velvor) -> None: + """Test approximated vorticity by spectral derivation""" + vel, vort = generate_random_spectral_velvor + dx = 1.0 / vel.shape[1] + dy = 1.0 / vel.shape[2] + dz = 1.0 / vel.shape[3] + + vort_jnp = compute_spectral_vorticity_jnp(jnp.array(vel), dx, dy, dz) + np.testing.assert_almost_equal(np.array(vort_jnp), vort, decimal=4) From aa0bf12d465a2481186fc3a1da7f33fc4b3085f6 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 14:06:35 +0000 Subject: [PATCH 114/137] fix typo of ReactDiff 2D domain --- .../data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml index 23cc8c9..acd486e 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml @@ -3,7 +3,7 @@ dt_save: 0.01 ini_time: 0. fin_time: 1. nx: 64 -nx: 64 +ny: 64 xL: 0. xR: 6.28318530718 yL: 0. From 79396f2d10cfaa4a2e774708b6f7f2d516842813 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 14:12:17 +0000 Subject: [PATCH 115/137] remove duplicate key --- pdebench/data_gen/data_gen_NLE/config/config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index bc22dcf..c79ac76 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -14,4 +14,3 @@ args: bd: "periodic" nbatch: 1000 savedir: "./save/CFD/" - savedir: './save/ReacDiff/' From 9942bddc5c5d78626dee06769192112ae19f306c Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 14:12:44 +0000 Subject: [PATCH 116/137] remove extra trailing slash --- .../ReactionDiffusionEq/config/multi/config_2D.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml index f37cfd1..5c1891e 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/multi/config_2D.yaml @@ -1,4 +1,4 @@ -save: "../save/ReacDiff//" +save: "../save/ReacDiff/" dt_save: 0.25 ini_time: 0. fin_time: 2. From c4b3001e1861e0a8294449b39a94c2f3f17bd5d1 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 15:04:42 +0000 Subject: [PATCH 117/137] comply with ruff for download_direct.py --- pdebench/data_download/download_direct.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 0dc4b39..35393dd 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -2,6 +2,7 @@ import argparse import os +from pathlib import Path import pandas as pd from torchvision.datasets.utils import download_url @@ -51,14 +52,13 @@ def parse_metadata(pde_names): ] assert all( - [name.lower() in pde_list for name in pde_names] + name.lower() in pde_list for name in pde_names ), "PDE name not defined." # Filter the files to be downloaded meta_df["PDE"] = meta_df["PDE"].str.lower() - pde_df = meta_df[meta_df["PDE"].isin(pde_names)] - return pde_df + return meta_df[meta_df["PDE"].isin(pde_names)] def download_data(root_folder, pde_name): @@ -70,14 +70,14 @@ def download_data(root_folder, pde_name): pde_name : The name of the PDE for which the data to be downloaded """ - print(f"Downloading data for {pde_name} ...") + # print(f"Downloading data for {pde_name} ...") # Load and parse metadata csv file pde_df = parse_metadata(pde_name) # Iterate filtered dataframe and download the files - for index, row in tqdm(pde_df.iterrows(), total=pde_df.shape[0]): - file_path = os.path.join(root_folder, row["Path"]) + for _, row in tqdm(pde_df.iterrows(), total=pde_df.shape[0]): + file_path = Path(root_folder) / row["Path"] download_url(row["URL"], file_path, row["Filename"], md5=row["MD5"]) From 6b23c01624f6302553e69c4f8c9013ece789280c Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 15:29:11 +0000 Subject: [PATCH 118/137] comply with ruff linter --- .../data_download/download_easydataverse.py | 2 +- pdebench/data_download/visualize_pdes.py | 31 +++++++------------ 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/pdebench/data_download/download_easydataverse.py b/pdebench/data_download/download_easydataverse.py index cc1042c..64c87d9 100644 --- a/pdebench/data_download/download_easydataverse.py +++ b/pdebench/data_download/download_easydataverse.py @@ -37,7 +37,7 @@ def main(config: DictConfig): # Compile list of files that matches the desired filename files = [] - for i, file in enumerate(files_list): + for _, file in enumerate(files_list): if config.args.filename in file["dataFile"]["filename"]: files.append(file["dataFile"]["filename"]) diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index a117bcf..838cc5f 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -2,6 +2,7 @@ import argparse import os +from pathlib import Path import h5py import matplotlib.pyplot as plt @@ -34,7 +35,7 @@ def visualize_diff_sorp(path, seed=None): """ # Read the h5 file and store the data - h5_file = h5py.File(os.path.join(path, "1D_diff-sorp_NA_NA.h5"), "r") + h5_file = h5py.File(Path(path) / "1D_diff-sorp_NA_NA.h5", "r") num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized @@ -69,7 +70,6 @@ def visualize_diff_sorp(path, seed=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_diff_sorp.gif", writer=writer) - print("Animation saved") def visualize_2d_reacdiff(path, seed=None): @@ -82,7 +82,7 @@ def visualize_2d_reacdiff(path, seed=None): """ # Read the h5 file and store the data - h5_file = h5py.File(os.path.join(path, "2D_diff-react_NA_NA.h5"), "r") + h5_file = h5py.File(Path(path) / "2D_diff-react_NA_NA.h5", "r") num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized @@ -118,7 +118,6 @@ def visualize_2d_reacdiff(path, seed=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_2d_reacdiff.gif", writer=writer) - print("Animation saved") def visualize_swe(path, seed=None): @@ -131,7 +130,7 @@ def visualize_swe(path, seed=None): """ # Read the h5 file and store the data - h5_file = h5py.File(os.path.join(path, "2D_rdb_NA_NA.h5"), "r") + h5_file = h5py.File(Path(path) / "2D_rdb_NA_NA.h5", "r") num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized @@ -163,7 +162,6 @@ def visualize_swe(path, seed=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_swe.gif", writer=writer) - print("Animation saved") def visualize_burgers(path, param=None): @@ -183,7 +181,7 @@ def visualize_burgers(path, param=None): flnm = "1D_Burgers_Sols_Nu0.01.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) data = np.array(h5_file["tensor"], dtype=np.float32)[ nb @@ -210,7 +208,6 @@ def visualize_burgers(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_burgers.gif", writer=writer) - print("Animation saved") def visualize_advection(path, param=None): @@ -230,7 +227,7 @@ def visualize_advection(path, param=None): flnm = "1D_Advection_Sols_beta0.4.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) data = np.array(h5_file["tensor"], dtype=np.float32)[ nb @@ -253,7 +250,6 @@ def visualize_advection(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_advection.gif", writer=writer) - print("Animation saved") def visualize_1d_cfd(path, param=None): @@ -284,7 +280,7 @@ def visualize_1d_cfd(path, param=None): flnm = "1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) dd = np.array(h5_file["density"], dtype=np.float32)[ nb @@ -308,7 +304,6 @@ def visualize_1d_cfd(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_1d_cfd.gif", writer=writer) - print("Animation saved") def visualize_2d_cfd(path, param=None): @@ -345,7 +340,7 @@ def visualize_2d_cfd(path, param=None): flnm = "2D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_512_Train.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: dd = np.array(h5_file["density"], dtype=np.float32)[ nb ] # (batch, t, x, y, channel) --> (t, x, y, channel) @@ -365,7 +360,6 @@ def visualize_2d_cfd(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie.gif", writer=writer) - print("saved") def visualize_3d_cfd(path, param=None): @@ -390,7 +384,7 @@ def visualize_3d_cfd(path, param=None): flnm = "3D_CFD_Rand_M1.0_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: dd = np.array(h5_file["density"], dtype=np.float32)[ nb ] # (batch, t, x, y, channel) --> (t, x, y, channel) @@ -410,7 +404,6 @@ def visualize_3d_cfd(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie.gif", writer=writer) - print("saved") def visualize_ns_incom() -> None: @@ -434,7 +427,7 @@ def visualize_darcy(path, param=None): flnm = "2D_DarcyFlow_beta1.0_Train.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: data = np.array(h5_file["tensor"], dtype=np.float32)[ nb ] # (batch, t, x, y, channel) --> (t, x, y, channel) @@ -450,7 +443,6 @@ def visualize_darcy(path, param=None): ax[0].set_title("Data u") ax[1].set_title("diffusion coefficient nu") plt.savefig("2D_DarcyFlow.pdf") - print("plot saved") def visualize_1d_reacdiff(path, param=None): @@ -471,7 +463,7 @@ def visualize_1d_reacdiff(path, param=None): flnm = "ReacDiff_Nu1.0_Rho1.0.hdf5" nb = 0 - with h5py.File(os.path.join(path, flnm), "r") as h5_file: + with h5py.File(Path(path) / flnm, "r") as h5_file: xcrd = np.array(h5_file["x-coordinate"], dtype=np.float32) data = np.array(h5_file["tensor"], dtype=np.float32)[ nb @@ -494,7 +486,6 @@ def visualize_1d_reacdiff(path, param=None): writer = animation.PillowWriter(fps=15, bitrate=1800) ani.save("movie_1d_reacdiff.gif", writer=writer) - print("Animation saved") if __name__ == "__main__": From be93f68d683f66a4e24c946f086dc4922b5ebebf Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 16:12:00 +0000 Subject: [PATCH 119/137] comply with ruff linter --- pdebench/data_download/README.md | 4 +-- pdebench/data_download/visualize_pdes.py | 33 ++++++++++++------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/pdebench/data_download/README.md b/pdebench/data_download/README.md index 8e619db..5be2a5d 100644 --- a/pdebench/data_download/README.md +++ b/pdebench/data_download/README.md @@ -57,7 +57,7 @@ python visualize_pdes.py --pde_name "1d_reacdiff" https://darus.uni-stuttgart.de/api/access/datafile/133110 # visualize -python visualize_pdes.py --pde_name "advection" +python visualize_pdes.py --pde_name "advection" --param 0.4 ``` --- @@ -69,7 +69,7 @@ python visualize_pdes.py --pde_name "advection" https://darus.uni-stuttgart.de/api/access/datafile/133136 # visualize -python visualize_pdes.py --pde_name "burgers" +python visualize_pdes.py --pde_name "burgers" --param 0.01 ``` --- diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index 838cc5f..b5fddae 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -39,8 +39,9 @@ def visualize_diff_sorp(path, seed=None): num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized + rng = np.random.default_rng() if not seed: - seed = np.random.randint(0, num_samples) + seed = rng.integers(low=0, high=num_samples, size=1).item() # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" @@ -62,7 +63,6 @@ def visualize_diff_sorp(path, seed=None): ) # show an initial one first else: im = ax.plot(data[i].squeeze(), animated=True, color="blue") - ax.plot ims.append([im[0]]) # Animate the plot @@ -86,8 +86,9 @@ def visualize_2d_reacdiff(path, seed=None): num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized + rng = np.random.default_rng() if not seed: - seed = np.random.randint(0, num_samples) + seed = rng.integers(low=0, high=num_samples, size=1).item() # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" @@ -134,8 +135,9 @@ def visualize_swe(path, seed=None): num_samples = len(h5_file.keys()) # randomly choose a seed for picking a sample that will subsequently be visualized + rng = np.random.default_rng() if not seed: - seed = np.random.randint(0, num_samples) + seed = rng.integers(low=0, high=num_samples, size=1).item() # Ensure the seed number is defined assert seed < num_samples, "Seed number too high!" @@ -176,7 +178,7 @@ def visualize_burgers(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "1D_Burgers_Sols_Nu" + str(param) + ".hdf5" - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "1D_Burgers_Sols_Nu0.01.hdf5" @@ -200,7 +202,6 @@ def visualize_burgers(path, param=None): im = ax.plot( xcrd, data[i].squeeze(), animated=True, color="blue" ) # show an initial one first - ax.plot ims.append([im[0]]) # Animate the plot @@ -222,7 +223,7 @@ def visualize_advection(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "1D_Advection_Sols_beta" + str(param) + ".hdf5" - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "1D_Advection_Sols_beta0.4.hdf5" @@ -242,7 +243,6 @@ def visualize_advection(path, param=None): im = ax.plot(xcrd, data[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, data[i].squeeze()) # show an initial one first - ax.plot ims.append([im[0]]) # Animate the plot @@ -275,7 +275,7 @@ def visualize_1d_cfd(path, param=None): + str(param[3]) + "_Train.hdf5" ) - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "1D_CFD_Rand_Eta1.e-8_Zeta1.e-8_periodic_Train.hdf5" @@ -296,7 +296,6 @@ def visualize_1d_cfd(path, param=None): im = ax.plot(xcrd, dd[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, dd[i].squeeze()) # show an initial one first - ax.plot ims.append([im[0]]) # Animate the plot @@ -335,7 +334,7 @@ def visualize_2d_cfd(path, param=None): + str(param[5]) + "_Train.hdf5" ) - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "2D_CFD_Rand_M0.1_Eta1e-8_Zeta1e-8_periodic_512_Train.hdf5" @@ -379,7 +378,7 @@ def visualize_3d_cfd(path, param=None): + str(param[4]) + "_Train.hdf5" ) - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "3D_CFD_Rand_M1.0_Eta1e-8_Zeta1e-8_periodic_Train.hdf5" @@ -422,7 +421,7 @@ def visualize_darcy(path, param=None): # Read the h5 file and store the data if param is not None: flnm = "2D_DarcyFlow_beta" + str(param) + "_Train.hdf5" - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "2D_DarcyFlow_beta1.0_Train.hdf5" @@ -458,7 +457,7 @@ def visualize_1d_reacdiff(path, param=None): if param is not None: assert len(param) == 2, "param should include Nu and Rho as list" flnm = "ReacDiff_Nu" + str(param[0]) + "_Rho" + str(param[1]) + ".hdf5" - assert os.path.isfile(path + flnm), "no such file! " + path + flnm + assert Path(path + flnm).is_file(), "no such file! " + path + flnm else: flnm = "ReacDiff_Nu1.0_Rho1.0.hdf5" @@ -478,7 +477,6 @@ def visualize_1d_reacdiff(path, param=None): im = ax.plot(xcrd, data[i].squeeze(), animated=True) if i == 0: ax.plot(xcrd, data[i].squeeze()) # show an initial one first - ax.plot ims.append([im[0]]) # Animate the plot @@ -498,7 +496,7 @@ def visualize_1d_reacdiff(path, param=None): arg_parser.add_argument( "--data_path", type=str, - default=".", + default="./", help="Path to the hdf5 data where the downloaded data reside", ) arg_parser.add_argument( @@ -546,4 +544,5 @@ def visualize_1d_reacdiff(path, param=None): elif args.pde_name == "1d_reacdiff": visualize_1d_reacdiff(args.data_path, args.params) else: - raise ValueError("PDE name not recognized!") + errmsg = "PDE name not recognized!" + raise ValueError(errmsg) From dc19ad9345c3464cf415c42c7dc9b63ecc4fa8b6 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 17:06:55 +0000 Subject: [PATCH 120/137] comply with ruff linter --- pdebench/models/fno/fno.py | 21 +++++++++----------- pdebench/models/fno/train.py | 38 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/pdebench/models/fno/fno.py b/pdebench/models/fno/fno.py index 806d194..7524db8 100644 --- a/pdebench/models/fno/fno.py +++ b/pdebench/models/fno/fno.py @@ -35,7 +35,7 @@ class SpectralConv1d(nn.Module): def __init__(self, in_channels, out_channels, modes1): - super(SpectralConv1d, self).__init__() + super().__init__() """ 1D Fourier layer. It does FFT, linear transform, and Inverse FFT. @@ -77,13 +77,12 @@ def forward(self, x): ) # Return to physical space - x = torch.fft.irfft(out_ft, n=x.size(-1)) - return x + return torch.fft.irfft(out_ft, n=x.size(-1)) class FNO1d(nn.Module): def __init__(self, num_channels, modes=16, width=64, initial_step=10): - super(FNO1d, self).__init__() + super().__init__() """ The overall network. It contains 4 layers of the Fourier layer. @@ -155,7 +154,7 @@ def forward(self, x, grid): class SpectralConv2d_fast(nn.Module): def __init__(self, in_channels, out_channels, modes1, modes2): - super(SpectralConv2d_fast, self).__init__() + super().__init__() """ 2D Fourier layer. It does FFT, linear transform, and Inverse FFT. @@ -210,13 +209,12 @@ def forward(self, x): ) # Return to physical space - x = torch.fft.irfft2(out_ft, s=(x.size(-2), x.size(-1))) - return x + return torch.fft.irfft2(out_ft, s=(x.size(-2), x.size(-1))) class FNO2d(nn.Module): def __init__(self, num_channels, modes1=12, modes2=12, width=20, initial_step=10): - super(FNO2d, self).__init__() + super().__init__() """ The overall network. It contains 4 layers of the Fourier layer. @@ -297,7 +295,7 @@ def forward(self, x, grid): class SpectralConv3d(nn.Module): def __init__(self, in_channels, out_channels, modes1, modes2, modes3): - super(SpectralConv3d, self).__init__() + super().__init__() """ 3D Fourier layer. It does FFT, linear transform, and Inverse FFT. @@ -392,15 +390,14 @@ def forward(self, x): ) # Return to physical space - x = torch.fft.irfftn(out_ft, s=(x.size(-3), x.size(-2), x.size(-1))) - return x + return torch.fft.irfftn(out_ft, s=(x.size(-3), x.size(-2), x.size(-1))) class FNO3d(nn.Module): def __init__( self, num_channels, modes1=8, modes2=8, modes3=8, width=20, initial_step=10 ): - super(FNO3d, self).__init__() + super().__init__() """ The overall network. It contains 4 layers of the Fourier layer. diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 25c40bf..02b707a 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -2,20 +2,19 @@ import pickle from timeit import default_timer +from pathlib import Path import numpy as np import torch from torch import nn -# torch.manual_seed(0) -# np.random.seed(0) - -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d from pdebench.models.fno.utils import FNODatasetMult, FNODatasetSingle from pdebench.models.metrics import metrics +# torch.manual_seed(0) +# np.random.seed(0) +device = torch.device("cuda" if torch.cuda.is_available() else "cpu") def run_training( if_training, @@ -48,9 +47,9 @@ def run_training( base_path="../data/", training_type="autoregressive", ): - print( - f"Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}" - ) + # print( + # f"Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}" + # ) ################################################################ # load data @@ -59,7 +58,7 @@ def run_training( if single_file: # filename model_name = flnm[:-5] + "_FNO" - print("FNODatasetSingle") + # print("FNODatasetSingle") # Initialize the dataset and dataloader train_data = FNODatasetSingle( @@ -84,7 +83,7 @@ def run_training( # filename model_name = flnm + "_FNO" - print("FNODatasetMult") + # print("FNODatasetMult") train_data = FNODatasetMult( flnm, reduced_resolution=reduced_resolution, @@ -114,7 +113,7 @@ def run_training( _, _data, _ = next(iter(val_loader)) dimensions = len(_data.shape) - print("Spatial Dimension", dimensions - 3) + # print("Spatial Dimension", dimensions - 3) if dimensions == 4: model = FNO1d( num_channels=num_channels, @@ -147,7 +146,7 @@ def run_training( model_path = model_name + ".pt" total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) - print(f"Total parameters = {total_params}") + # print(f"Total parameters = {total_params}") optimizer = torch.optim.Adam( model.parameters(), lr=learning_rate, weight_decay=1e-4 @@ -184,14 +183,15 @@ def run_training( t_max, initial_step=initial_step, ) - pickle.dump(errs, open(model_name + ".pickle", "wb")) + with Path(model_name + ".pickle").open("wb") as pb: + pickle.dump(errs, pb) return # If desired, restore the network by loading the weights saved in the .pt # file if continue_training: - print("Restoring model (that is the network's weights) from file...") + # print("Restoring model (that is the network's weights) from file...") checkpoint = torch.load(model_path, map_location=device) model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) @@ -339,11 +339,11 @@ def run_training( t2 = default_timer() scheduler.step() - print( - "epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}".format( - ep, loss.item(), t2 - t1, train_l2_full, val_l2_full - ) - ) + # print( + # "epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}".format( + # ep, loss.item(), t2 - t1, train_l2_full, val_l2_full + # ) + # ) if __name__ == "__main__": From 0df2b7412db7240e9f7dfd6740821a33a1451269 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 18:07:16 +0000 Subject: [PATCH 121/137] comply with ruff linter --- pdebench/models/fno/fno.py | 2 -- pdebench/models/fno/train.py | 8 ++++---- pdebench/models/fno/utils.py | 13 +++++++------ pdebench/models/metrics.py | 14 +++++++------- pdebench/models/unet/unet.py | 8 +++----- pdebench/models/unet/utils.py | 7 ++++--- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/pdebench/models/fno/fno.py b/pdebench/models/fno/fno.py index 7524db8..3e50e09 100644 --- a/pdebench/models/fno/fno.py +++ b/pdebench/models/fno/fno.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - """ FNO. Implementation taken and modified from https://github.com/zongyi-li/fourier_neural_operator diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 02b707a..25a6912 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -145,7 +145,7 @@ def run_training( model_path = model_name + ".pt" - total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + # total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) # print(f"Total parameters = {total_params}") optimizer = torch.optim.Adam( @@ -209,7 +209,7 @@ def run_training( for ep in range(start_epoch, epochs): model.train() - t1 = default_timer() + # t1 = default_timer() train_l2_step = 0 train_l2_full = 0 for xx, yy, grid in train_loader: @@ -337,7 +337,7 @@ def run_training( model_path, ) - t2 = default_timer() + # t2 = default_timer() scheduler.step() # print( # "epoch: {0}, loss: {1:.5f}, t2-t1: {2:.5f}, trainL2: {3:.5f}, testL2: {4:.5f}".format( @@ -348,4 +348,4 @@ def run_training( if __name__ == "__main__": run_training() - print("Done.") + # print("Done.") diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 01b507e..a98cbe9 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -150,6 +150,7 @@ import math as mt import os +from pathlib import Path import h5py import numpy as np @@ -182,9 +183,9 @@ def __init__( """ # Define path to files - root_path = os.path.join(os.path.abspath(saved_folder), filename) + root_path = Path(Path(saved_folder).resolve()) / filename if filename[-2:] != "h5": - print(".HDF5 file extension is assumed hereafter") + # print(".HDF5 file extension is assumed hereafter") with h5py.File(root_path, "r") as f: keys = list(f.keys()) @@ -242,7 +243,7 @@ def __init__( self.grid = torch.tensor( self.grid[::reduced_resolution], dtype=torch.float ).unsqueeze(-1) - print(self.data.shape) + # print(self.data.shape) if len(idx_cfd) == 4: # 2D self.data = np.zeros( [ @@ -463,7 +464,7 @@ def __init__( ] elif filename[-2:] == "h5": # SWE-2D (RDB) - print(".H5 file extension is assumed hereafter") + # print(".H5 file extension is assumed hereafter") with h5py.File(root_path, "r") as f: keys = list(f.keys()) @@ -548,7 +549,7 @@ def __init__( """ # Define path to files - self.file_path = os.path.abspath(saved_folder + filename + ".h5") + self.file_path = Path(saved_folder + filename + ".h5").resolve() # Extract list of seeds with h5py.File(self.file_path, "r") as h5_file: @@ -577,7 +578,7 @@ def __getitem__(self, idx): # convert to [x1, ..., xd, t, v] permute_idx = list(range(1, len(data.shape) - 1)) - permute_idx.extend(list([0, -1])) + permute_idx.extend([0, -1]) data = data.permute(permute_idx) # Extract spatial dimension of data diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 913faab..ab16704 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -331,13 +331,13 @@ def metrics( for t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) temp_shape = [0, -1] - temp_shape.extend([i for i in range(1, len(inp.shape) - 1)]) + temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) y = yy[..., t : t + 1, :] temp_shape = [0] - temp_shape.extend([i for i in range(2, len(inp.shape))]) + temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) pred = torch.cat((pred, im), -2) @@ -372,7 +372,7 @@ def metrics( err_BD += _err_BD err_F += _err_F - mean_dim = [i for i in range(len(yy.shape) - 2)] + mean_dim = list(range(len(yy.shape) - 2)) mean_dim.append(-1) mean_dim = tuple(mean_dim) val_l2_time += torch.sqrt( @@ -429,7 +429,7 @@ def metrics( err_BD += _err_BD err_F += _err_F - mean_dim = [i for i in range(len(yy.shape) - 2)] + mean_dim = list(range(len(yy.shape) - 2)) mean_dim.append(-1) mean_dim = tuple(mean_dim) val_l2_time += torch.sqrt( @@ -584,7 +584,7 @@ class LpLoss: """ def __init__(self, p=2, reduction="mean"): - super(LpLoss, self).__init__() + super().__init__() # Dimension and Lp-norm type are positive assert p > 0 self.p = p @@ -611,7 +611,7 @@ class FftLpLoss: """ def __init__(self, p=2, reduction="mean"): - super(FftLpLoss, self).__init__() + super().__init__() # Dimension and Lp-norm type are positive assert p > 0 self.p = p @@ -661,7 +661,7 @@ class FftMseLoss: """ def __init__(self, reduction="mean"): - super(FftMseLoss, self).__init__() + super().__init__() # Dimension and Lp-norm type are positive self.reduction = reduction diff --git a/pdebench/models/unet/unet.py b/pdebench/models/unet/unet.py index f06e7e1..95dfb1a 100644 --- a/pdebench/models/unet/unet.py +++ b/pdebench/models/unet/unet.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - """ U-Net. Implementation taken and modified from https://github.com/mateuszbuda/brain-segmentation-pytorch @@ -28,7 +26,7 @@ class UNet1d(nn.Module): def __init__(self, in_channels=3, out_channels=1, init_features=32): - super(UNet1d, self).__init__() + super().__init__() features = init_features self.encoder1 = UNet1d._block(in_channels, features, name="enc1") @@ -121,7 +119,7 @@ def _block(in_channels, features, name): class UNet2d(nn.Module): def __init__(self, in_channels=3, out_channels=1, init_features=32): - super(UNet2d, self).__init__() + super().__init__() features = init_features self.encoder1 = UNet2d._block(in_channels, features, name="enc1") @@ -214,7 +212,7 @@ def _block(in_channels, features, name): class UNet3d(nn.Module): def __init__(self, in_channels=3, out_channels=1, init_features=32): - super(UNet3d, self).__init__() + super().__init__() features = init_features self.encoder1 = UNet3d._block(in_channels, features, name="enc1") diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index d8c61cf..4175b10 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -150,6 +150,7 @@ import math as mt import os +from pathlib import Path import h5py import numpy as np @@ -180,7 +181,7 @@ def __init__( """ # Define path to files - root_path = os.path.abspath(saved_folder + filename) + root_path = Path(saved_folder + filename).resolve() assert filename[-2:] != "h5", "HDF5 data is assumed!!" with h5py.File(root_path, "r") as f: @@ -437,7 +438,7 @@ def __init__( """ # Define path to files - self.file_path = os.path.abspath(saved_folder + filename + ".h5") + self.file_path = Path(saved_folder + filename + ".h5").resolve() # Extract list of seeds with h5py.File(self.file_path, "r") as h5_file: @@ -466,7 +467,7 @@ def __getitem__(self, idx): # convert to [x1, ..., xd, t, v] permute_idx = list(range(1, len(data.shape) - 1)) - permute_idx.extend(list([0, -1])) + permute_idx.extend([0, -1]) data = data.permute(permute_idx) return data[..., : self.initial_step, :], data From da2d938331cf1f171d73cf61d050cab7149c7d58 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 18:37:50 +0000 Subject: [PATCH 122/137] update darcy config --- pdebench/models/config/args/config_Darcy.yaml | 2 +- pdebench/models/config/config_darcy.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/models/config/args/config_Darcy.yaml b/pdebench/models/config/args/config_Darcy.yaml index 5bd9879..bb9b0d8 100644 --- a/pdebench/models/config/args/config_Darcy.yaml +++ b/pdebench/models/config/args/config_Darcy.yaml @@ -4,7 +4,7 @@ continue_training: False num_workers: 2 batch_size: 50 initial_step: 10 -t_train: 1 +t_train: 2 model_update: 2 filename: "2D_DarcyFlow_beta0.01_Train.hdf5" single_file: True diff --git a/pdebench/models/config/config_darcy.yaml b/pdebench/models/config/config_darcy.yaml index 4272742..2949cb0 100644 --- a/pdebench/models/config/config_darcy.yaml +++ b/pdebench/models/config/config_darcy.yaml @@ -15,7 +15,7 @@ args: num_workers: 2 batch_size: 50 initial_step: 1 - t_train: 1 # steady-state + t_train: 2 # steady-state model_update: 2 filename: "2D_DarcyFlow_beta0.01_Train.hdf5" single_file: True From 1c2d5647bed1d68f2467e541d49d45bc73ba099c Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 18:38:25 +0000 Subject: [PATCH 123/137] comply with ruff linter --- pdebench/data_download/download_direct.py | 1 - pdebench/data_download/visualize_pdes.py | 1 - 2 files changed, 2 deletions(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 35393dd..5cd0ee4 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -1,7 +1,6 @@ from __future__ import annotations import argparse -import os from pathlib import Path import pandas as pd diff --git a/pdebench/data_download/visualize_pdes.py b/pdebench/data_download/visualize_pdes.py index b5fddae..1c75218 100644 --- a/pdebench/data_download/visualize_pdes.py +++ b/pdebench/data_download/visualize_pdes.py @@ -1,7 +1,6 @@ from __future__ import annotations import argparse -import os from pathlib import Path import h5py From a60ec47a93cbddaadfcc48520c9cfaf235709bd4 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 18:57:00 +0000 Subject: [PATCH 124/137] comply with ruff linter --- pdebench/data_download/download_direct.py | 4 +--- .../ReactionDiffusionEq/config/args/config_2D.yaml | 6 +++--- pdebench/data_gen/data_gen_NLE/config/config.yaml | 2 +- pdebench/data_gen/velocity2vorticity.py | 3 +-- pdebench/models/fno/train.py | 2 +- pdebench/models/fno/utils.py | 1 - pdebench/models/unet/utils.py | 1 - 7 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pdebench/data_download/download_direct.py b/pdebench/data_download/download_direct.py index 5cd0ee4..5a87249 100644 --- a/pdebench/data_download/download_direct.py +++ b/pdebench/data_download/download_direct.py @@ -50,9 +50,7 @@ def parse_metadata(pde_names): "3d_cfd", ] - assert all( - name.lower() in pde_list for name in pde_names - ), "PDE name not defined." + assert all(name.lower() in pde_list for name in pde_names), "PDE name not defined." # Filter the files to be downloaded meta_df["PDE"] = meta_df["PDE"].str.lower() diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml index acd486e..8909062 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/config/args/config_2D.yaml @@ -1,4 +1,4 @@ -save: '../save/ReacDiff/' +save: "../save/ReacDiff/" dt_save: 0.01 ini_time: 0. fin_time: 1. @@ -8,9 +8,9 @@ xL: 0. xR: 6.28318530718 yL: 0. yR: 6.28318530718 -nu : 1.e0 +nu: 1.e0 rho: 1.e0 CFL: 2.5e-1 if_show: 1 show_steps: 100 -init_mode: 'react' +init_mode: "react" diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index c79ac76..25f1620 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -9,7 +9,7 @@ hydra: dir: . args: - type: 'ReacDiff' # 'advection'/'ReacDiff'/'burgers'/'CFD' + type: "ReacDiff" # "advection"/"ReacDiff"/"burgers"/"CFD" dim: 1 bd: "periodic" nbatch: 1000 diff --git a/pdebench/data_gen/velocity2vorticity.py b/pdebench/data_gen/velocity2vorticity.py index 8181dc4..3081ced 100644 --- a/pdebench/data_gen/velocity2vorticity.py +++ b/pdebench/data_gen/velocity2vorticity.py @@ -92,8 +92,7 @@ def convert_velocity() -> None: [vx[..., None], vy[..., None], vz[..., None]], axis=-1 ) - vorticity = compute_spectral_vorticity_jnp( - jnp.array(velocity), dx, dy, dz) + vorticity = compute_spectral_vorticity_jnp(jnp.array(velocity), dx, dy, dz) outfile["omega_x"][i] = np.array(vorticity[..., 0]) outfile["omega_y"][i] = np.array(vorticity[..., 1]) diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 25a6912..2381c45 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -6,7 +6,7 @@ import numpy as np import torch -from torch import nn +import torch.nn as nn from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d from pdebench.models.fno.utils import FNODatasetMult, FNODatasetSingle diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index a98cbe9..4151a27 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -149,7 +149,6 @@ from __future__ import annotations import math as mt -import os from pathlib import Path import h5py diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index 4175b10..a39f564 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -149,7 +149,6 @@ from __future__ import annotations import math as mt -import os from pathlib import Path import h5py From 663dadbcd925703ad656e462ab27e4695b28a161 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 19:17:30 +0000 Subject: [PATCH 125/137] comply with ruff linter --- pdebench/data_gen/src/utils.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pdebench/data_gen/src/utils.py b/pdebench/data_gen/src/utils.py index 8e284bc..2ad4fae 100644 --- a/pdebench/data_gen/src/utils.py +++ b/pdebench/data_gen/src/utils.py @@ -2,6 +2,7 @@ import glob import os +from pathlib import Path from pprint import pprint from omegaconf import DictConfig, OmegaConf @@ -11,14 +12,14 @@ def expand_path(path, unique=True): """ Resolve a path that may contain variables and user home directory references. """ - return os.path.expandvars(os.path.expanduser(path)) + return os.path.expandvars(Path(path).expanduser()) def matching_paths(glob_exp): """ return a list of paths matching a glob expression """ - path = os.path.expandvars(os.path.expanduser(glob_exp)) + path = os.path.expandvars(Path(glob_exp).expanduser()) return glob.glob(path) @@ -35,12 +36,13 @@ def resolve_path(path, idx=None, unique=True): matches = sorted(matches) if unique and len(matches) > 1: - raise ValueError(f"Too many matches for glob: {path}") - else: - try: - return matches[idx] - except IndexError: - raise FileNotFoundError(f"No matches for glob: {path}") + valerrmsg = f"Too many matches for glob: {path}" + raise ValueError(valerrmsg) + try: + return matches[idx] + except IndexError: + idxerrmsg = f"No matches for glob: {path}" + raise FileNotFoundError(idxerrmsg) def print_config( From 1a83119cb0a292f99c75af9e67041e38b4a70b35 Mon Sep 17 00:00:00 2001 From: kmario23 Date: Sun, 20 Oct 2024 19:23:52 +0000 Subject: [PATCH 126/137] comply with ruff linter --- pdebench/data_gen/data_gen_NLE/config/config.yaml | 2 +- pdebench/models/fno/train.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/config/config.yaml b/pdebench/data_gen/data_gen_NLE/config/config.yaml index 25f1620..89a88d5 100644 --- a/pdebench/data_gen/data_gen_NLE/config/config.yaml +++ b/pdebench/data_gen/data_gen_NLE/config/config.yaml @@ -9,7 +9,7 @@ hydra: dir: . args: - type: "ReacDiff" # "advection"/"ReacDiff"/"burgers"/"CFD" + type: "ReacDiff" # "advection"/"ReacDiff"/"burgers"/"CFD" dim: 1 bd: "periodic" nbatch: 1000 diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 2381c45..d299d1f 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -6,11 +6,11 @@ import numpy as np import torch -import torch.nn as nn from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d from pdebench.models.fno.utils import FNODatasetMult, FNODatasetSingle from pdebench.models.metrics import metrics +from torch import nn # torch.manual_seed(0) # np.random.seed(0) From 9f3ca2b7765a7fedcec60578dd3f624e6e7e9645 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 21 Oct 2024 22:33:50 +0200 Subject: [PATCH 127/137] fix metrics addressing #41 and more --- pdebench/models/metrics.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index ab16704..5f87098 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -157,21 +157,24 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu") -def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12): +def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12, initial_step=1): """ code for calculate metrics discussed in the Brain-storming session RMSE, normalized RMSE, max error, RMSE at the boundaries, conserved variables, RMSE in Fourier space, temporal sensitivity """ pred, target = pred.to(device), target.to(device) # (batch, nx^i..., timesteps, nc) + # slice out `initial context` timesteps + pred = pred[..., initial_step:, :] + target = target[..., initial_step:, :] idxs = target.size() - if len(idxs) == 4: + if len(idxs) == 4: # 1D pred = pred.permute(0, 3, 1, 2) target = target.permute(0, 3, 1, 2) - if len(idxs) == 5: + if len(idxs) == 5: # 2D pred = pred.permute(0, 4, 1, 2, 3) target = target.permute(0, 4, 1, 2, 3) - elif len(idxs) == 6: + elif len(idxs) == 6: # 3D pred = pred.permute(0, 5, 1, 2, 3, 4) target = target.permute(0, 5, 1, 2, 3, 4) idxs = target.size() @@ -238,12 +241,12 @@ def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHig err_BD_z = (pred[:, :, :, :, 0] - target[:, :, :, :, 0]) ** 2 err_BD_z += (pred[:, :, :, :, -1] - target[:, :, :, :, -1]) ** 2 err_BD = ( - torch.sum(err_BD_x.view([nb, -1, nt]), dim=-2) - + torch.sum(err_BD_y.view([nb, -1, nt]), dim=-2) - + torch.sum(err_BD_z.view([nb, -1, nt]), dim=-2) + torch.sum(err_BD_x.contiguous().view([nb, -1, nt]), dim=-2) + + torch.sum(err_BD_y.contiguous().view([nb, -1, nt]), dim=-2) + + torch.sum(err_BD_z.contiguous().view([nb, -1, nt]), dim=-2) ) err_BD = err_BD / (2 * nx * ny + 2 * ny * nz + 2 * nz * nx) - err_BD = torch.mean(torch.sqrt(err_BD), dim=0) + err_BD = torch.sqrt(err_BD) if len(idxs) == 4: # 1D nx = idxs[2] @@ -350,7 +353,7 @@ def metrics( _err_Max, _err_BD, _err_F, - ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) + ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz, initial_step=initial_step) if itot == 0: err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( @@ -408,7 +411,7 @@ def metrics( _err_Max, _err_BD, _err_F, - ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz) + ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz, initial_step=initial_step) if itot == 0: err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( _err_RMSE, From 7c26040b285903692e99327ae6d4cad96850fb89 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 18 Dec 2024 21:45:02 +0100 Subject: [PATCH 128/137] manually fix linter warnings throught repository --- pdebench/__init__.py | 5 ++ .../AdvectionEq/advection_exact_Hydra.py | 14 +++-- .../advection_multi_solution_Hydra.py | 23 ++++---- .../data_gen_NLE/BurgersEq/burgers_Hydra.py | 13 +++-- .../BurgersEq/burgers_multi_solution_Hydra.py | 14 +++-- .../CompressibleFluid/CFD_Hydra.py | 2 - .../CompressibleFluid/CFD_multi_Hydra.py | 2 - pdebench/data_gen/data_gen_NLE/utils.py | 4 -- pdebench/data_gen/gen_ns_incomp.py | 1 - pdebench/data_gen/gen_radial_dam_break.py | 48 +++++++-------- pdebench/data_gen/notebooks/Analysis.ipynb | 34 +++++------ pdebench/data_gen/plot.py | 22 +++---- pdebench/data_gen/src/data_io.py | 32 +++++----- pdebench/data_gen/src/plots.py | 17 ++---- pdebench/data_gen/src/pytorch_dataset.py | 16 ++--- pdebench/data_gen/src/sim_diff_react.py | 15 ++--- pdebench/data_gen/src/sim_diff_sorp.py | 2 +- pdebench/data_gen/src/sim_radial_dam_break.py | 4 +- pdebench/data_gen/src/utils.py | 9 ++- pdebench/models/analyse_result_forward.py | 2 +- pdebench/models/fno/train.py | 7 +-- pdebench/models/metrics.py | 3 +- pdebench/models/pinn/train.py | 19 +++--- pdebench/models/pinn/utils.py | 58 +++++++------------ pdebench/models/train_models_forward.py | 11 ++-- pdebench/models/train_models_inverse.py | 13 +++-- pdebench/models/unet/utils.py | 3 - pyproject.toml | 4 +- tests/test_vorticity.py | 2 +- 29 files changed, 187 insertions(+), 212 deletions(-) diff --git a/pdebench/__init__.py b/pdebench/__init__.py index 4b4fd31..f1c6f44 100644 --- a/pdebench/__init__.py +++ b/pdebench/__init__.py @@ -21,6 +21,11 @@ """ from __future__ import annotations +import logging + +_logger = logging.getLogger(__name__) +_logger.propagate = False + __version__ = "0.0.1" __author__ = "Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert" __credits__ = "NEC labs Europe, University of Stuttgart, CSIRO" "s Data61" diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py index e1e8eb9..df33b1c 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -146,6 +145,7 @@ """ from __future__ import annotations +import logging import time from math import ceil @@ -157,11 +157,13 @@ # Hydra from omegaconf import DictConfig +logger = logging.getLogger(__name__) + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: - print(f"advection velocity: {cfg.args.beta}") + logging.info("advection velocity: %f", cfg.args.beta) # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -181,14 +183,14 @@ def evolve(u): uu = uu.at[0].set(u) while t < cfg.args.fin_time: - print(f"save data at t = {t:.3f}") + logging.info("save data at t = %f", t) u = set_function(xc, t, cfg.args.beta) uu = uu.at[i_save].set(u) t += cfg.args.dt_save i_save += 1 tm_fin = time.time() - print(f"total elapsed time is {tm_fin - tm_ini} sec") + logging.info("total elapsed time is %f sec", tm_fin - tm_ini) uu = uu.at[-1].set(u) return uu, t @@ -199,9 +201,9 @@ def set_function(x, t, beta): u = set_function(xc, t=0, beta=cfg.args.beta) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print(f"final time is: {t:.3f}") + logger.info("final time is: %f", t) - print("data saving...") + logger.info("data saving...") cwd = hydra.utils.get_original_cwd() + "/" jnp.save(cwd + cfg.args.save + "/Advection_beta" + str(cfg.args.beta), uu) jnp.save(cwd + cfg.args.save + "/x_coordinate", xe) diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py index ea951f9..8ec3e67 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -147,19 +146,24 @@ from __future__ import annotations import random +import sys + +# Hydra +from math import ceil, exp, log from pathlib import Path import hydra import jax import jax.numpy as jnp from jax import device_put, lax - -# Hydra from omegaconf import DictConfig sys.path.append("..") +import logging + from utils import Courant, bc, init_multi, limiting +logger = logging.getLogger(__name__) def _pass(carry): return carry @@ -192,7 +196,7 @@ def main(cfg: DictConfig) -> None: else: beta = cfg.multi.beta - print("beta: ", beta) + logger.info("beta: %f", beta) @jax.jit def evolve(u): @@ -204,7 +208,8 @@ def evolve(u): uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) - cond_fun = lambda x: x[0] < fin_time + def cond_fun(x): + return x[0] < fin_time def _body_fun(carry): def _show(_carry): @@ -226,9 +231,8 @@ def _show(_carry): carry = t, tsave, steps, i_save, dt, u, uu t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) - uu = uu.at[-1].set(u) + return uu.at[-1].set(u) - return uu @jax.jit def simulation_fn(i, carry): @@ -265,12 +269,11 @@ def flux(u): fL = uL * beta fR = uR * beta # upwind advection scheme - f_upwd = 0.5 * ( + return 0.5 * ( fR[1 : cfg.multi.nx + 2] + fL[2 : cfg.multi.nx + 3] - jnp.abs(beta) * (uL[2 : cfg.multi.nx + 3] - uR[1 : cfg.multi.nx + 2]) ) - return f_upwd u = init_multi(xc, numbers=cfg.multi.numbers, k_tot=4, init_key=cfg.multi.init_key) u = device_put(u) # putting variables in GPU (not necessary??) @@ -285,7 +288,7 @@ def flux(u): # reshape before saving uu = uu.reshape((-1, *uu.shape[2:])) - print("data saving...") + logger.info("data saving...") cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) jnp.save(cwd + cfg.multi.save + "1D_Advection_Sols_beta" + str(beta)[:5], uu) diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py index f8eab18..824de29 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -146,6 +145,7 @@ """ from __future__ import annotations +import logging import sys import time from math import ceil @@ -161,6 +161,8 @@ sys.path.append("..") from utils import Courant, Courant_diff, bc, init, limiting +logger = logging.getLogger(__name__) + def _pass(carry): return carry @@ -201,7 +203,8 @@ def evolve(u): tm_ini = time.time() - cond_fun = lambda x: x[0] < fin_time + def cond_fun(x): + return x[0] < fin_time def _body_fun(carry): def _save(_carry): @@ -227,7 +230,7 @@ def _save(_carry): uu = uu.at[-1].set(u) tm_fin = time.time() - print(f"total elapsed time is {tm_fin - tm_ini} sec") + logger.info("total elapsed time is %f sec", tm_fin - tm_ini) return uu, t @jax.jit @@ -285,9 +288,9 @@ def flux(u): u = init(xc=xc, mode=cfg.args.init_mode, u0=cfg.args.u0, du=cfg.args.du) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print(f"final time is: {t:.3f}") + logger.info("final time is: %.3f", t) - print("data saving...") + logger.info("data saving...") cwd = hydra.utils.get_original_cwd() + "/" if cfg.args.init_mode == "sinsin": jnp.save( diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py index 5e07805..6c01d53 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -146,6 +145,7 @@ """ from __future__ import annotations +import logging import random import sys from math import ceil, exp, log @@ -162,6 +162,8 @@ sys.path.append("..") from utils import Courant, Courant_diff, bc, init_multi, limiting +logger = logging.getLogger(__name__) + def _pass(carry): return carry @@ -191,7 +193,7 @@ def main(cfg: DictConfig) -> None: ) # uniform number between 0.01 to 100 else: epsilon = cfg.multi.epsilon - print("epsilon: ", epsilon) + logger.info("epsilon: %f", epsilon) # t-coordinate it_tot = ceil((fin_time - ini_time) / dt_save) + 1 tc = jnp.arange(it_tot + 1) * dt_save @@ -206,7 +208,8 @@ def evolve(u): uu = jnp.zeros([it_tot, u.shape[0]]) uu = uu.at[0].set(u) - cond_fun = lambda x: x[0] < fin_time + def cond_fun(x): + return x[0] < fin_time def _body_fun(carry): def _show(_carry): @@ -228,9 +231,8 @@ def _show(_carry): carry = t, tsave, steps, i_save, dt, u, uu t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) - uu = uu.at[-1].set(u) + return uu.at[-1].set(u) - return uu @jax.jit def simulation_fn(i, carry): @@ -301,7 +303,7 @@ def flux(u): # reshape before saving uu = uu.reshape((-1, *uu.shape[2:])) - print("data saving...") + logger.info("data saving...") cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) jnp.save(cwd + cfg.multi.save + "1D_Burgers_Sols_Nu" + str(epsilon)[:5], uu) diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py index ad2366b..7a106e7 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -190,10 +190,8 @@ def main(cfg: DictConfig) -> None: dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx dx_inv = 1.0 / dx - # dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny dy_inv = 1.0 / dy - # dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz dz_inv = 1.0 / dz diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py index 23cd971..aa1c6ba 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -206,10 +206,8 @@ def main(cfg: DictConfig) -> None: dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx dx_inv = 1.0 / dx - # dy = (cfg.args.yR - cfg.args.yL) / cfg.args.ny dy_inv = 1.0 / dy - # dz = (cfg.args.zR - cfg.args.zL) / cfg.args.nz dz_inv = 1.0 / dz diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index 5e9cf87..4fb1e52 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1578,10 +1578,8 @@ def limiting_HD(u, if_second_order): def save_data(u, xc, i_save, save_dir, dt_save=None, if_final=False): if if_final: jnp.save(save_dir + "/x_coordinate", xc) - # tc = jnp.arange(i_save + 1) * dt_save jnp.save(save_dir + "/t_coordinate", tc) - # flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) else: @@ -1594,10 +1592,8 @@ def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): jnp.save(save_dir + "/x_coordinate", xc) jnp.save(save_dir + "/y_coordinate", yc) jnp.save(save_dir + "/z_coordinate", zc) - # tc = jnp.arange(i_save + 1) * dt_save jnp.save(save_dir + "/t_coordinate", tc) - # flnm = save_dir + "/Data_" + str(i_save).zfill(4) jnp.save(flnm, u) else: diff --git a/pdebench/data_gen/gen_ns_incomp.py b/pdebench/data_gen/gen_ns_incomp.py index d66c36f..82eaa2a 100644 --- a/pdebench/data_gen/gen_ns_incomp.py +++ b/pdebench/data_gen/gen_ns_incomp.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python from __future__ import annotations import dotenv diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index 664cd93..c20fcbd 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -1,14 +1,25 @@ -#!/usr/bin/env python from __future__ import annotations +import logging +import multiprocessing as mp import os +import time from copy import deepcopy +from itertools import repeat # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir # this allows us to keep defaults local to the machine # e.g. HPC versus local laptop import dotenv +import h5py +import hydra +import numpy as np +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils +from pdebench.data_gen.src.sim_radial_dam_break import RadialDamBreak2D +from pdebench.data_gen.uploader import dataverse_upload dotenv.load_dotenv() @@ -22,19 +33,6 @@ os.environ["NUMEXPR_NUM_THREADS"] = num_threads os.environ["NUMEXPR_MAX_THREADS"] = num_threads -import logging -import multiprocessing as mp -import time -from itertools import repeat - -import h5py -import hydra -import numpy as np -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf -from pdebench.data_gen.src import utils -from pdebench.data_gen.src.sim_radial_dam_break import RadialDamBreak2D -from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) @@ -42,11 +40,11 @@ def simulator(base_config, i): config = deepcopy(base_config) config.sim.seed = i - log.info(f"Starting seed {i}") + log.info("Starting seed %d", i) - np.random.seed(config.sim.seed) + rng = np.random.default_rng(config.sim.seed) # config.sim.inner_height = np.random.uniform(1.5, 2.5) - config.sim.dam_radius = np.random.uniform(0.3, 0.7) + config.sim.dam_radius = rng.uniform(0.3, 0.7) scenario = RadialDamBreak2D( grav=config.sim.gravity, @@ -59,7 +57,7 @@ def simulator(base_config, i): scenario.run(T=config.sim.T_end, tsteps=config.sim.n_time_steps, plot=False) duration = time.time() - start_time seed_str = str(i).zfill(4) - log.info(f"Seed {seed_str} took {duration} to finish") + log.info("Seed %s took %s to finish", seed_str, duration) while True: try: @@ -95,18 +93,20 @@ def main(config: DictConfig): # Change to original working directory to import modules import os + from pathlib import Path - temp_path = os.getcwd() + temp_path = Path.cwd() os.chdir(get_original_cwd()) # Change back to the hydra working directory os.chdir(temp_path) - work_path = os.path.dirname(config.work_dir) - output_path = os.path.join(work_path, config.data_dir, config.output_path) - if not os.path.isdir(output_path): - os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + ".h5" + + work_path = Path(config.work_dir) + output_path = work_path / config.data_dir / config.output_path + if not output_path.is_dir(): + output_path.mkdir(parents=True) + config.output_path = output_path / config.output_path.with_suffix(".h5") num_samples_init = 0 num_samples_final = 10000 diff --git a/pdebench/data_gen/notebooks/Analysis.ipynb b/pdebench/data_gen/notebooks/Analysis.ipynb index 045cb58..661bfc8 100644 --- a/pdebench/data_gen/notebooks/Analysis.ipynb +++ b/pdebench/data_gen/notebooks/Analysis.ipynb @@ -2,29 +2,32 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "from __future__ import annotations\n", + "\n", "%load_ext autoreload\n", "%autoreload 2" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", - "import os\n", - "parentdir = os.path.dirname(os.getcwd())\n", - "sys.path.append(parentdir)" + "from pathlib import Path\n", + "\n", + "parentdir = Path.cwd()\n", + "sys.path.append(str(parentdir))" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -40,6 +43,7 @@ ], "source": [ "import dotenv\n", + "\n", "# load environment variables from `.env` file if it exists\n", "# recursively searches for `.env` in all folders starting from work dir\n", "dotenv.load_dotenv()" @@ -47,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -56,8 +60,6 @@ "import matplotlib.pyplot as plt\n", "from einops import rearrange\n", "from phi.flow import *\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", "from phi.vis import *\n", "from src.utils import resolve_path" ] @@ -226,11 +228,8 @@ } ], "metadata": { - "interpreter": { - "hash": "38db623c9a8c22d26705f5e96929a68995fd63f2ffb05d6c4e502eb3b9fc3e8d" - }, "kernelspec": { - "display_name": "Python 3.9.4 ('venv2': venv)", + "display_name": "pde_bench_tmp", "language": "python", "name": "python3" }, @@ -244,14 +243,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.4" + "version": "3.10.12" }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "7a0d8eacbb66928d1b3dec5c8bc3662d91007e449cc8cc497b49c1d19e71d254" - } - } + "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 diff --git a/pdebench/data_gen/plot.py b/pdebench/data_gen/plot.py index fd952ad..d431e71 100644 --- a/pdebench/data_gen/plot.py +++ b/pdebench/data_gen/plot.py @@ -5,6 +5,9 @@ """ from __future__ import annotations +import os +from pathlib import Path + import h5py import hydra import numpy as np @@ -18,28 +21,28 @@ def main(config: DictConfig): """ use config specifications to generate dataset """ - - work_path = os.path.dirname(config.work_dir) - output_path = os.path.join(work_path, config.data_dir, config.output_path) - if not os.path.isdir(output_path): - os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + work_path = Path(config.work_dir) + output_path = work_path / config.data_dir / config.output_path + if not output_path.is_dir(): + output_path.mkdir(parents=True) + config.output_path = output_path / config.output_path # Open and load file data_path = config.output_path + ".h5" h5_file = h5py.File(data_path, "r") + rng = np.random.default_rng() - if "seed" in config.sim.keys(): + if "seed" in config.sim: # Choose random sample number idx_max = 10000 if config.plot.dim == 1 else 1000 - config.sim.seed = np.random.randint(0, idx_max) + config.sim.seed = rng.integers(0, idx_max) postfix = str(config.sim.seed).zfill(4) data = np.array(h5_file[f"{postfix}/data"], dtype="f") t = np.array(h5_file[f"{postfix}/grid/t"], dtype="f") # data dim = [t, x1, ..., xd, v] else: idx_max = 10000 if config.plot.dim == 1 else 1000 - postfix = np.random.randint(0, idx_max) + postfix = rng.randint(0, idx_max) data = np.array(h5_file["data"], dtype="f") data = data[postfix] t = np.array(h5_file["grid/t"], dtype="f") @@ -60,7 +63,6 @@ def main(config: DictConfig): ) -import os if __name__ == "__main__": main() diff --git a/pdebench/data_gen/src/data_io.py b/pdebench/data_gen/src/data_io.py index f3da0da..ed9810b 100644 --- a/pdebench/data_gen/src/data_io.py +++ b/pdebench/data_gen/src/data_io.py @@ -2,15 +2,13 @@ import json import logging -import os -import os.path import subprocess import h5py import numpy as np from omegaconf import OmegaConf from phi.field import Field -from phi.flow import * +from phi.flow import CenteredGrid from phi.math import Shape log = logging.getLogger(__name__) @@ -21,26 +19,26 @@ def dims_for(n_steps=1000, grid_size=(100, 100), frame_int=1, n_batch=1, **kwarg return a dict of fields and their shapes """ n_frames = ((n_steps - 1) // frame_int) + 1 - return dict( - velocity=(n_batch, n_frames, *grid_size, len(grid_size)), - particles=(n_batch, n_frames, *grid_size, 1), - force=(n_batch, *grid_size, len(grid_size)), - t=(n_batch, n_frames), - ) + return { + "velocity": (n_batch, n_frames, *grid_size, len(grid_size)), + "particles": (n_batch, n_frames, *grid_size, 1), + "force": (n_batch, *grid_size, len(grid_size)), + "t": (n_batch, n_frames), + } def dict_for(config): spec = dims_for(**config) - data_store = dict(latest_index=-1, config=config) + data_store = {"latest_index": -1, "config": config} for field_name, full_shape in spec.items(): data_store[field_name] = np.ndarray(full_shape, dtype="float32") return data_store def h5_for(config): - log.info(f"config: {config}") + log.info("config: %s", config) spec = dims_for(**config) - log.info(f"spec: {spec}") + log.info("spec: %s", spec) fname = f"{config['sim_name']}-{config['seed']}.h5" data_store = h5py.File(fname, "a") data_store.attrs["config"] = OmegaConf.to_yaml(config) @@ -48,7 +46,7 @@ def h5_for(config): for field_name, full_shape in spec.items(): # dataset shape is (batch, t_length, x1, ..., xd, v) chunk_shape = (1, 1, *full_shape[2:]) # chunk shape in (1, 1, x1, ..., xd, v) - # Open a dataset, creating it if it doesn’t exist. + # Open a dataset, creating it if it doesn't exist. data_store.require_dataset( field_name, full_shape, @@ -87,8 +85,7 @@ def to_ndarray(field: Field) -> np.ndarray: """ centered = to_centre_grid(field) order = _get_dim_order(centered.shape) - ndarray = centered.values.numpy(order=order) - return ndarray + return centered.values.numpy(order=order) # noqa: PD011 def dataverse_upload( @@ -119,6 +116,7 @@ def dataverse_upload( "--retry", str(retry), ] - log.info(f"upload cmd {cmd}") + log.info("upload cmd %s", cmd) subprocess.Popen(cmd) - log.info(f"upload cmd {os.getcwd()}$ {' '.join(cmd)}") + from pathlib import Path + log.info("upload cmd %s$ %s", Path.cwd(), ' '.join(cmd)) diff --git a/pdebench/data_gen/src/plots.py b/pdebench/data_gen/src/plots.py index cba9a74..29d5c96 100644 --- a/pdebench/data_gen/src/plots.py +++ b/pdebench/data_gen/src/plots.py @@ -56,12 +56,9 @@ def phi_plots( """ images = [] upperfilepath = filepath - for i, arr in enumerate(T_results): + for i, _ in enumerate(T_results): filename = f"{title}.png" - if upperfilepath == "": - filepath = filename - else: - filepath = upperfilepath + f"/{filename}" + filepath = filename if upperfilepath == "" else upperfilepath + f"/{filename}" save_phi_plot( scale * results[i], title, @@ -74,7 +71,6 @@ def phi_plots( def save_sim_figures( - results, T_results, simulation_name, kinematic_value, @@ -88,14 +84,11 @@ def save_sim_figures( """ images = [] upperfilepath = filepath - for i, arr in enumerate(T_results): + for _, arr in enumerate(T_results): res = arr[0] - title = f"{simulation_name}_{kinematic_value}_t={round(T_results[i], 2)}" + title = f"{simulation_name}_{kinematic_value}_t={round(arr, 2)}" filename = f"{title}.png" - if upperfilepath == "": - filepath = filename - else: - filepath = upperfilepath + f"/{filename}" + filepath = filename if upperfilepath == "" else upperfilepath + f"/{filename}" save_phi_plot( scale * res, title, filepath, bbox_inches=bbox_inches, pad_inches=pad_inches ) diff --git a/pdebench/data_gen/src/pytorch_dataset.py b/pdebench/data_gen/src/pytorch_dataset.py index 942523a..4c84da4 100644 --- a/pdebench/data_gen/src/pytorch_dataset.py +++ b/pdebench/data_gen/src/pytorch_dataset.py @@ -1,11 +1,14 @@ from __future__ import annotations +import logging from pathlib import Path import h5py from pytorch_lightning import LightningDataModule from torch.utils.data import DataLoader, Dataset +logger = logging.getLogger(__name__) + class HDF5Dataset(Dataset): """hdf5 dataset, generated from phiflow model @@ -26,8 +29,8 @@ def __init__(self, dir_path, transform=None): self.config = [] self.names = [] - for files_path in files_path: - with h5py.File(str(files_path.resolve())) as f: + for file_path in files_path: + with h5py.File(str(file_path.resolve())) as f: config = f.attrs.get("config") for ds_name, ds in f.items(): self.names.append(ds_name) @@ -77,7 +80,6 @@ def setup(self, stage=None): self.train = HDF5Dataset(self.data_dir, transform=self.transforms) def train_dataloader(self): - print(self.train is None) return DataLoader(self.train, batch_size=self.batch_size) @@ -90,8 +92,8 @@ def train_dataloader(self): dataloader = DataLoader(dataset, batch_size=64, shuffle=True) data, config = next(iter(dataloader)) for i, d in enumerate(data): - print(f"{names[i].upper()} batched data shape: ", d.size()) - print("number of config files: ", len(config)) + logger.info("%s batched data shape: %s", names[i].upper(), d.size()) + logger.info("number of config files: %s", len(config)) # test pytorch lightning dataset lightning_dataset = HDF5DatasetLightning(dir_path, batch_size=64, transforms=None) @@ -99,5 +101,5 @@ def train_dataloader(self): lightning_dataloader = lightning_dataset.train_dataloader() data, config = next(iter(lightning_dataloader)) for i, d in enumerate(data): - print(f"{names[i].upper()} batched data shape: ", d.size()) - print("number of config files: ", len(config)) + logger.info("%s batched data shape: %s", names[i].upper(), d.size()) + logger.info("number of config files: %s", len(config)) diff --git a/pdebench/data_gen/src/sim_diff_react.py b/pdebench/data_gen/src/sim_diff_react.py index 027eb53..939a04e 100644 --- a/pdebench/data_gen/src/sim_diff_react.py +++ b/pdebench/data_gen/src/sim_diff_react.py @@ -21,7 +21,7 @@ def __init__( y_bottom: float = -1.0, y_top: float = 1.0, ydim: int = 50, - n: int = 1, + n: int = 1, # noqa: ARG002 seed: int = 0, ): """ @@ -77,10 +77,10 @@ def generate_sample(self): :return: The generated sample as numpy array(t, x, y, num_features) """ - np.random.seed(self.seed) + rng = np.random.default_rng(self.seed) - u0 = np.random.randn(self.Nx * self.Ny) - v0 = np.random.randn(self.Nx * self.Ny) + u0 = rng.standard_normal(self.Nx * self.Ny) + v0 = rng.standard_normal(self.Nx * self.Ny) u0 = u0.reshape(self.Nx * self.Ny) v0 = v0.reshape(self.Nx * self.Ny) @@ -135,7 +135,7 @@ def generate_sample(self): return np.stack((sample_u, sample_v), axis=-1) - def rc_ode(self, t, y): + def rc_ode(self, t, y): # noqa: ARG002 """ Solves a given equation for a particular time step. :param t: The current time step @@ -156,9 +156,6 @@ def rc_ode(self, t, y): v_t = react_v + self.Dv * (self.lap @ v) # Stack the time derivative into a single array y_t - y_t = np.concatenate((u_t, v_t)) + return np.concatenate((u_t, v_t)) - # Log the simulation progress - # self.log.info('t = ' + str(t)) - return y_t diff --git a/pdebench/data_gen/src/sim_diff_sorp.py b/pdebench/data_gen/src/sim_diff_sorp.py index 4160400..b4c2e1c 100644 --- a/pdebench/data_gen/src/sim_diff_sorp.py +++ b/pdebench/data_gen/src/sim_diff_sorp.py @@ -98,7 +98,7 @@ def generate_sample(self) -> np.ndarray: return np.expand_dims(sample_c, axis=-1) - def rc_ode(self, t: float, y): + def rc_ode(self, t: float, y): # noqa: ARG002 """ Solves a given equation for a particular time step. :param t: The current time step diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index bdbc949..2524ba9 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -1,8 +1,8 @@ from __future__ import annotations import logging -import os from abc import ABC, abstractmethod +from pathlib import Path import numpy as np import torch @@ -29,7 +29,7 @@ def __init__(self): self.set_boundary_conditions() self.set_initial_conditions() self.register_state_getters() - self.outdir = os.sep.join(["./", self.name.replace(" ", "") + "2D"]) + self.outdir = Path.cwd() / (self.name.replace(" ", "") + "2D") @abstractmethod def setup_solver(self): diff --git a/pdebench/data_gen/src/utils.py b/pdebench/data_gen/src/utils.py index 2ad4fae..a5d0a72 100644 --- a/pdebench/data_gen/src/utils.py +++ b/pdebench/data_gen/src/utils.py @@ -1,6 +1,5 @@ from __future__ import annotations -import glob import os from pathlib import Path from pprint import pprint @@ -8,7 +7,7 @@ from omegaconf import DictConfig, OmegaConf -def expand_path(path, unique=True): +def expand_path(path): """ Resolve a path that may contain variables and user home directory references. """ @@ -20,7 +19,7 @@ def matching_paths(glob_exp): return a list of paths matching a glob expression """ path = os.path.expandvars(Path(glob_exp).expanduser()) - return glob.glob(path) + return list(Path(path).glob('*')) def resolve_path(path, idx=None, unique=True): @@ -42,7 +41,7 @@ def resolve_path(path, idx=None, unique=True): return matches[idx] except IndexError: idxerrmsg = f"No matches for glob: {path}" - raise FileNotFoundError(idxerrmsg) + raise FileNotFoundError(idxerrmsg) from None def print_config( @@ -52,4 +51,4 @@ def print_config( """ basic pretty-printer for omegaconf configs """ - pprint(OmegaConf.to_yaml(config, resolve=resolve)) + pprint(OmegaConf.to_yaml(config, resolve=resolve)) # noqa: T203 diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index e33b16e..752b629 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -146,9 +146,9 @@ """ from __future__ import annotations +import _pickle as cPickle import glob -import _pickle as cPickle import matplotlib.pyplot as plt import numpy as np import pandas as pd diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index d299d1f..d723908 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -1,12 +1,10 @@ from __future__ import annotations import pickle -from timeit import default_timer from pathlib import Path import numpy as np import torch - from pdebench.models.fno.fno import FNO1d, FNO2d, FNO3d from pdebench.models.fno.utils import FNODatasetMult, FNODatasetSingle from pdebench.models.metrics import metrics @@ -140,8 +138,7 @@ def run_training( ).to(device) # Set maximum time step of the data to train - if t_train > _data.shape[-2]: - t_train = _data.shape[-2] + t_train = min(t_train, _data.shape[-2]) model_path = model_name + ".pt" @@ -156,7 +153,7 @@ def run_training( ) loss_fn = nn.MSELoss(reduction="mean") - loss_val_min = np.infty + loss_val_min = np.inf start_epoch = 0 diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 5f87098..4c85dd7 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -297,8 +297,7 @@ def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHig torch.mean(err_BD, dim=[0, -1]), torch.mean(err_F, dim=[0, -1]), ) - else: - return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F + return err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F def metrics( diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index 07de94a..eda1390 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -202,16 +202,15 @@ def setup_pde1D( if filename[0] == "R": timedomain = dde.geometry.TimeDomain(0, 1.0) pde = lambda x, y: pde_diffusion_reaction_1d(x, y, aux_params[0], aux_params[1]) - else: - if filename.split("_")[1][0] == "A": - timedomain = dde.geometry.TimeDomain(0, 2.0) - pde = lambda x, y: pde_adv1d(x, y, aux_params[0]) - elif filename.split("_")[1][0] == "B": - timedomain = dde.geometry.TimeDomain(0, 2.0) - pde = lambda x, y: pde_burgers1D(x, y, aux_params[0]) - elif filename.split("_")[1][0] == "C": - timedomain = dde.geometry.TimeDomain(0, 1.0) - pde = lambda x, y: pde_CFD1d(x, y, aux_params[0]) + elif filename.split("_")[1][0] == "A": + timedomain = dde.geometry.TimeDomain(0, 2.0) + pde = lambda x, y: pde_adv1d(x, y, aux_params[0]) + elif filename.split("_")[1][0] == "B": + timedomain = dde.geometry.TimeDomain(0, 2.0) + pde = lambda x, y: pde_burgers1D(x, y, aux_params[0]) + elif filename.split("_")[1][0] == "C": + timedomain = dde.geometry.TimeDomain(0, 1.0) + pde = lambda x, y: pde_CFD1d(x, y, aux_params[0]) geomtime = dde.geometry.GeometryXTime(geom, timedomain) dataset = PINNDataset1Dpde( diff --git a/pdebench/models/pinn/utils.py b/pdebench/models/pinn/utils.py index 91d13a3..94bb4b8 100644 --- a/pdebench/models/pinn/utils.py +++ b/pdebench/models/pinn/utils.py @@ -5,7 +5,7 @@ """ from __future__ import annotations -import os +from pathlib import Path import h5py import numpy as np @@ -23,8 +23,8 @@ def __init__(self, filename, seed): self.seed = seed # load data file - root_path = os.path.abspath("../data") - data_path = os.path.join(root_path, filename) + root_path = Path("../data").resolve() + data_path = root_path / filename with h5py.File(data_path, "r") as h5_file: seed_group = h5_file[seed] @@ -50,7 +50,7 @@ def __init__(self, filename, seed): # permute from [t, x] -> [x, t] permute_idx = list(range(1, len(self.data_output.shape) - 1)) - permute_idx.extend(list([0, -1])) + permute_idx.extend([0, -1]) self.data_output = self.data_output.permute(permute_idx) def get_test_data(self, n_last_time_steps, n_components=1): @@ -96,8 +96,7 @@ def generate_plot_input(self, time=1.0): # xx, yy = np.meshgrid(x_space, y_space) tt = np.ones_like(x_space) * time - val_input = np.vstack((x_space, tt)).T - return val_input + return np.vstack((x_space, tt)).T def __len__(self): return len(self.data_output) @@ -118,8 +117,8 @@ def __init__(self, filename, seed): self.seed = seed # load data file - root_path = os.path.abspath("../data") - data_path = os.path.join(root_path, filename) + root_path = Path("../data").resolve() + data_path = root_path / filename with h5py.File(data_path, "r") as h5_file: seed_group = h5_file[seed] @@ -147,7 +146,7 @@ def __init__(self, filename, seed): # permute from [t, x, y] -> [x, y, t] permute_idx = list(range(1, len(self.data_output.shape) - 1)) - permute_idx.extend(list([0, -1])) + permute_idx.extend([0, -1]) self.data_output = self.data_output.permute(permute_idx) def generate_plot_input(self, time=1.0): @@ -163,8 +162,7 @@ def generate_plot_input(self, time=1.0): ) xx, yy = np.meshgrid(x_space, y_space) tt = np.ones_like(xx) * time - val_input = np.vstack((np.ravel(xx), np.ravel(yy), np.ravel(tt))).T - return val_input + return np.vstack((np.ravel(xx), np.ravel(yy), np.ravel(tt))).T def __len__(self): return len(self.data_output) @@ -243,12 +241,10 @@ def initial_h(coords): h_out = 1.0 dam_radius = self.config["sim"]["dam_radius"] - h_initial = np.expand_dims( + return np.expand_dims( h_in * (r <= dam_radius) + h_out * (r > dam_radius), 1 ) - return h_initial - return initial_h @@ -263,26 +259,15 @@ def __init__(self, filename, seed): def get_initial_condition(self): Nx = len(self.data_grid_x) Ny = len(self.data_grid_y) - Nt = len(self.data_grid_t) - np.random.seed(self.config["sim"]["seed"]) + rng = np.random.default_rng(self.config["sim"]["seed"]) - u0 = np.random.randn(Nx * Ny) - v0 = np.random.randn(Nx * Ny) + u0 = rng.standard_normal(Nx * Ny) + v0 = rng.standard_normal(Nx * Ny) u0 = u0.reshape(Nx * Ny) v0 = v0.reshape(Nx * Ny) - x_space = np.linspace( - self.config["sim"]["x_left"], - self.config["sim"]["x_right"], - self.config["sim"]["xdim"], - ) - y_space = np.linspace( - self.config["sim"]["y_bottom"], - self.config["sim"]["y_top"], - self.config["sim"]["ydim"], - ) xx, yy = np.meshgrid(self.data_grid_x.cpu(), self.data_grid_y.cpu()) tt = np.zeros_like(xx) ic_input = np.vstack((np.ravel(xx), np.ravel(yy), np.ravel(tt))).T @@ -312,9 +297,9 @@ def get_initial_condition(self): # Generate initial condition Nx = self.config["sim"]["xdim"] - np.random.seed(self.config["sim"]["seed"]) + rng = np.random.default_rng(self.config["sim"]["seed"]) - u0 = np.ones(Nx) * np.random.uniform(0, 0.2) + u0 = np.ones(Nx) * rng.uniform(0, 0.2) return (self.data_input[:Nx, :], np.expand_dims(u0, 1)) @@ -327,7 +312,7 @@ def __init__(self, filename, root_path="data", val_batch_idx=-1): """ # load data file - data_path = os.path.join(root_path, filename) + data_path = Path(root_path) / filename h5_file = h5py.File(data_path, "r") # build input data from individual dimensions @@ -436,8 +421,7 @@ def generate_plot_input(self, time=1.0): # xx, yy = np.meshgrid(x_space, y_space) tt = np.ones_like(x_space) * time - val_input = np.vstack((x_space, tt)).T - return val_input + return np.vstack((x_space, tt)).T def __len__(self): return len(self.data_output) @@ -454,7 +438,7 @@ def __init__(self, filename, root_path="data", val_batch_idx=-1, rdc_x=9, rdc_y= """ # load data file - data_path = os.path.join(root_path, filename) + data_path = Path(root_path) / filename h5_file = h5py.File(data_path, "r") # build input data from individual dimensions @@ -570,7 +554,7 @@ def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): n_y = len(self.data_grid_y) return raveled_tensor.reshape((1, n_x, n_y, n_last_time_steps, n_components)) - def generate_plot_input(self, time=1.0): + def generate_plot_input(self, time=1.0): # noqa: ARG002 return None def __len__(self): @@ -590,7 +574,7 @@ def __init__( """ # load data file - data_path = os.path.join(root_path, filename) + data_path = Path(root_path) / filename h5_file = h5py.File(data_path, "r") # build input data from individual dimensions @@ -732,7 +716,7 @@ def unravel_tensor(self, raveled_tensor, n_last_time_steps, n_components=1): (1, n_x, n_y, n_z, n_last_time_steps, n_components) ) - def generate_plot_input(self, time=1.0): + def generate_plot_input(self, time=1.0): # noqa: ARG002 return None def __len__(self): diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 8ca3f75..911b18c 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -147,16 +147,20 @@ """ from __future__ import annotations +import logging + import hydra from omegaconf import DictConfig +logger = logging.getLogger(__name__) + @hydra.main(version_base="1.2", config_path="config", config_name="config_rdb") def main(cfg: DictConfig): if cfg.args.model_name == "FNO": from pdebench.models.fno.train import run_training as run_training_FNO - print("FNO") + logger.info("FNO") run_training_FNO( if_training=cfg.args.if_training, continue_training=cfg.args.continue_training, @@ -191,7 +195,7 @@ def main(cfg: DictConfig): elif cfg.args.model_name == "Unet": from pdebench.models.unet.train import run_training as run_training_Unet - print("Unet") + logger.info("Unet") run_training_Unet( if_training=cfg.args.if_training, continue_training=cfg.args.continue_training, @@ -228,7 +232,7 @@ def main(cfg: DictConfig): # not importing globally as DeepXDE changes some global PyTorch settings from pdebench.models.pinn.train import run_training as run_training_PINN - print("PINN") + logger.info("PINN") run_training_PINN( scenario=cfg.args.scenario, epochs=cfg.args.epochs, @@ -247,4 +251,3 @@ def main(cfg: DictConfig): if __name__ == "__main__": main() - print("Done.") diff --git a/pdebench/models/train_models_inverse.py b/pdebench/models/train_models_inverse.py index 918b9c6..116d3b4 100644 --- a/pdebench/models/train_models_inverse.py +++ b/pdebench/models/train_models_inverse.py @@ -146,18 +146,22 @@ """ from __future__ import annotations +import logging + import hydra from omegaconf import DictConfig from pdebench.models.fno.train import run_training as run_training_FNO from pdebench.models.pinn.train import run_training as run_training_PINN from pdebench.models.unet.train import run_training as run_training_Unet +logger = logging.getLogger(__name__) + @hydra.main(config_path="config", config_name="config") def main(cfg: DictConfig): - print(cfg.args) + logger.info(cfg.args) if cfg.args.model_name == "FNO": - print("FNO") + logger.info("FNO") run_training_FNO( if_training=cfg.args.if_training, continue_training=cfg.args.continue_training, @@ -190,7 +194,7 @@ def main(cfg: DictConfig): training_type=cfg.args.training_type, ) elif cfg.args.model_name == "Unet": - print("Unet") + logger.info("Unet") run_training_Unet( if_training=cfg.args.if_training, continue_training=cfg.args.continue_training, @@ -225,7 +229,7 @@ def main(cfg: DictConfig): training_type=cfg.args.training_type, ) elif cfg.args.model_name == "PINN": - print("PINN") + logger.info("PINN") run_training_PINN( scenario=cfg.args.scenario, epochs=cfg.args.epochs, @@ -238,4 +242,3 @@ def main(cfg: DictConfig): if __name__ == "__main__": main() - print("Done.") diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index a39f564..13f4fa8 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -419,9 +419,6 @@ def __init__( filename, initial_step=10, saved_folder="../data/", - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, if_test=False, test_ratio=0.1, ): diff --git a/pyproject.toml b/pyproject.toml index 9ff674d..87a4493 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -171,7 +171,9 @@ ignore = [ "PLR09", # Too many <...> "PLR2004", # Magic value used in comparison "ISC001", # Conflicts with formatter - "UP007" + "UP007", + "ARG001", # too many false positives + "ARG005", ] isort.required-imports = ["from __future__ import annotations"] # Uncomment if using a _compat.typing backport diff --git a/tests/test_vorticity.py b/tests/test_vorticity.py index b984b20..d941983 100644 --- a/tests/test_vorticity.py +++ b/tests/test_vorticity.py @@ -9,7 +9,7 @@ ) -@pytest.fixture() +@pytest.fixture def generate_random_spectral_velvor() -> tuple[np.ndarray, np.ndarray]: """Generate random 5D velocity- and corresponding vorticity field From 153207c1a217602a3c2e3e49e40b0398b7eaa81e Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 18 Dec 2024 22:05:00 +0100 Subject: [PATCH 129/137] unify logger usage --- .../AdvectionEq/advection_exact_Hydra.py | 6 +++--- .../CompressibleFluid/CFD_Hydra.py | 15 ++++++-------- pdebench/data_gen/src/sim_ns_incomp_2d.py | 6 ++---- pdebench/data_gen/src/sim_radial_dam_break.py | 6 ++---- pdebench/models/inverse/train.py | 20 +++++++++---------- pdebench/models/inverse/utils.py | 12 +++++------ pdebench/models/unet/train.py | 19 +++++++++--------- 7 files changed, 36 insertions(+), 48 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py index df33b1c..fe76029 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py @@ -163,7 +163,7 @@ # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: - logging.info("advection velocity: %f", cfg.args.beta) + logger.info("advection velocity: %f", cfg.args.beta) # cell edge coordinate xe = jnp.linspace(cfg.args.xL, cfg.args.xR, cfg.args.nx + 1) @@ -183,14 +183,14 @@ def evolve(u): uu = uu.at[0].set(u) while t < cfg.args.fin_time: - logging.info("save data at t = %f", t) + logger.info("save data at t = %f", t) u = set_function(xc, t, cfg.args.beta) uu = uu.at[i_save].set(u) t += cfg.args.dt_save i_save += 1 tm_fin = time.time() - logging.info("total elapsed time is %f sec", tm_fin - tm_ini) + logger.info("total elapsed time is %f sec", tm_fin - tm_ini) uu = uu.at[-1].set(u) return uu, t diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py index 7a106e7..b084c1f 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -146,10 +145,10 @@ """ from __future__ import annotations +import logging import sys import time from functools import partial -from math import ceil import hydra import jax.numpy as jnp @@ -165,6 +164,8 @@ sys.path.append("..") from utils import Courant_HD, Courant_vis_HD, bc_HD, init_HD, limiting_HD, save_data_HD +logger = logging.getLogger(__name__) + def _pass(carry): return carry @@ -179,9 +180,6 @@ def main(cfg: DictConfig) -> None: gamminv1 = 1.0 / gammi1 gamgamm1inv = gamma * gamminv1 gammi1 = gamma - 1.0 - gampl1 = gamma + 1.0 - gammi3 = gamma - 3.0 - gampl3 = gamma + 3.0 visc = cfg.args.zeta + cfg.args.eta / 3.0 @@ -204,9 +202,6 @@ def main(cfg: DictConfig) -> None: yc = ye[:-1] + 0.5 * dy zc = ze[:-1] + 0.5 * dz - # t-coordinate - it_tot = ceil((cfg.args.fin_time - cfg.args.ini_time) / cfg.args.dt_save) + 1 - tc = jnp.arange(it_tot + 1) * cfg.args.dt_save def evolve(Q): t = cfg.args.ini_time @@ -218,7 +213,7 @@ def evolve(Q): while t < cfg.args.fin_time: if t >= tsave: - print(f"save data at t = {t:.3f}") + logger.info(f"save data at t = {t:.3f}") save_data_HD(Q[:, 2:-2, 2:-2, 2:-2], xc, yc, zc, i_save, cfg.args.save) tsave += cfg.args.dt_save i_save += 1 @@ -356,6 +351,8 @@ def update(Q, Q_tmp, dt): @jit def update_vis(carry): + eta = cfg.args.eta + def _update_vis_x(carry): Q, dt = carry # calculate conservative variables diff --git a/pdebench/data_gen/src/sim_ns_incomp_2d.py b/pdebench/data_gen/src/sim_ns_incomp_2d.py index 79509f9..e9c3596 100644 --- a/pdebench/data_gen/src/sim_ns_incomp_2d.py +++ b/pdebench/data_gen/src/sim_ns_incomp_2d.py @@ -14,9 +14,7 @@ from pdebench.data_gen.src import data_io from tqdm import tqdm -logging.basicConfig(level=logging.INFO, filename=__name__) -logging.root.setLevel(logging.INFO) - +logger = logging.getLogger(__name__) # import wandb @@ -297,7 +295,7 @@ def sim_step(velocity, particles) -> tuple[fluid.Field, fluid.Field]: if step % frame_int == 0: frame_i = step // frame_int msg = f"step {step} frame_i {frame_i}" - logging.info(msg) + logger.info(msg) call_many( callbacks, frame_i=frame_i, diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index 2524ba9..dc2f173 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -8,9 +8,7 @@ import torch from clawpack import pyclaw, riemann -logging.basicConfig(level=logging.INFO, filename=__name__) -logging.root.setLevel(logging.INFO) - +logger = logging.getLogger(__name__) class Basic2DScenario(ABC): name = "" @@ -103,7 +101,7 @@ def simulate(self, t) -> None: self.solver.evolve_to_time(self.solution, t) else: msg = "Simulate failed: No scenario defined." - logging.info(msg) + logger.info(msg) def run(self, T: float = 1.0, tsteps: int = 20) -> None: self.init_save_state(T, tsteps) diff --git a/pdebench/models/inverse/train.py b/pdebench/models/inverse/train.py index fa4b110..e81faae 100644 --- a/pdebench/models/inverse/train.py +++ b/pdebench/models/inverse/train.py @@ -167,9 +167,7 @@ from torch import nn from tqdm import tqdm -logging.basicConfig(level=logging.INFO, filename=__name__) -logging.root.setLevel(logging.INFO) - +logger = logging.getLogger(__name__) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") @@ -184,8 +182,8 @@ def load_model(model, model_path, device): @hydra.main(config_path="../config", config_name="config") def main(cfg: DictConfig): - logging.info(cfg.args.filename) - logging.info(cfg.args) + logger.info(cfg.args.filename) + logger.info(cfg.args) # we use the test data if cfg.args.model_name in ["FNO"]: @@ -230,7 +228,7 @@ def main(cfg: DictConfig): if cfg.args.model_name in ["FNO"]: if dimensions == 4: - logging.info(cfg.args.num_channels) + logger.info(cfg.args.num_channels) model = FNO1d( num_channels=cfg.args.num_channels, width=cfg.args.width, @@ -325,7 +323,7 @@ def model_(x, grid): if ks == 0: msg = f"{x.shape}, {y.shape}" - logging.info(msg) + logger.info(msg) # scale the input and output x = scaler.fit_transform(x) @@ -405,7 +403,7 @@ def model_(x, grid): f"mse_inverse_y_L2: {inverse_y_l2_full / num_samples:.5f}", ] ) - logging.info(msg) + logger.info(msg) df_metric = pd.DataFrame(all_metric) inverse_metric_filename = ( @@ -418,7 +416,7 @@ def model_(x, grid): + ".csv" ) msg = f"saving in : {inverse_metric_filename}" - logging.info(msg) + logger.info(msg) df_metric.to_csv(inverse_metric_filename) inverse_metric_filename = ( @@ -431,7 +429,7 @@ def model_(x, grid): + ".pickle" ) msg = f"saving in : {inverse_metric_filename}" - logging.info(msg) + logger.info(msg) df_metric.to_pickle(inverse_metric_filename) inverse_metric_filename = ( @@ -444,7 +442,7 @@ def model_(x, grid): + "_stats.csv" ) msg = f"saving in : {inverse_metric_filename}" - logging.info(msg) + logger.info(msg) df_metric = df_metric.describe() df_metric.to_csv(inverse_metric_filename) diff --git a/pdebench/models/inverse/utils.py b/pdebench/models/inverse/utils.py index 2e3a74d..d4a6e7d 100644 --- a/pdebench/models/inverse/utils.py +++ b/pdebench/models/inverse/utils.py @@ -155,9 +155,7 @@ from omegaconf import DictConfig from scipy.signal import welch -logging.basicConfig(level=logging.INFO, filename=__name__) -logging.root.setLevel(logging.INFO) - +logger = logging.getLogger(__name__) def plot_ic_solution_mcmc( latent, @@ -307,7 +305,7 @@ def read_results( ) if verbose: msg = f"reading result file: {inverse_metric_filename}" - logging.info(msg) + logger.info(msg) dframe = pd.read_pickle(inverse_metric_filename) dframe["model"] = model_name @@ -325,7 +323,7 @@ def process_results(cfg: DictConfig): June 2022, F.Alesiani """ - logging.info(cfg.args) + logger.info(cfg.args) df, keys = read_results( cfg.args.model_names, @@ -337,11 +335,11 @@ def process_results(cfg: DictConfig): df1p3 = df[keys + list(cfg.args.results_values)] df2p3 = df1p3.groupby(by=keys).agg([np.mean, np.std]).reset_index() msg = "saving results into: {cfg.args.base_path + cfg.args.result_filename}" - logging.info(msg) + logger.info(msg) df2p3.to_csv(cfg.args.base_path + cfg.args.result_filename) if __name__ == "__main__": process_results() msg = "Done." - logging.info(msg) + logger.info(msg) diff --git a/pdebench/models/unet/train.py b/pdebench/models/unet/train.py index 30bdcfa..858f4d0 100644 --- a/pdebench/models/unet/train.py +++ b/pdebench/models/unet/train.py @@ -14,8 +14,7 @@ # torch.manual_seed(0) # np.random.seed(0) -logging.basicConfig(level=logging.INFO, filename=__name__) -logging.root.setLevel(logging.INFO) +logger = logging.getLogger(__name__) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") @@ -54,7 +53,7 @@ def run_training( training_type="autoregressive", ): msg = f"Epochs = {epochs}, learning rate = {learning_rate}, scheduler step = {scheduler_step}, scheduler gamma = {scheduler_gamma}" - logging.info(msg) + logger.info(msg) ################################################################ # load data @@ -118,7 +117,7 @@ def run_training( _, _data = next(iter(val_loader)) dimensions = len(_data.shape) msg = f"Spatial Dimension: {dimensions - 3}" - logging.info(msg) + logger.info(msg) if training_type in ["autoregressive"]: if dimensions == 4: model = UNet1d(in_channels * initial_step, out_channels).to(device) @@ -154,7 +153,7 @@ def run_training( total_params = sum(p.numel() for p in model.parameters() if p.requires_grad) msg = f"Total parameters = {total_params}" - logging.info(msg) + logger.info(msg) optimizer = torch.optim.Adam( model.parameters(), lr=learning_rate, weight_decay=1e-4 @@ -200,7 +199,7 @@ def run_training( # file if continue_training: msg = "Restoring model (that is the network's weights) from file..." - logging.info(msg) + logger.info(msg) checkpoint = torch.load(model_path, map_location=device) model.load_state_dict(checkpoint["model_state_dict"]) model.to(device) @@ -217,7 +216,7 @@ def run_training( loss_val_min = checkpoint["loss"] msg = "start training..." - logging.info(msg) + logger.info(msg) if ar_mode: for ep in range(start_epoch, epochs): @@ -396,7 +395,7 @@ def run_training( t2 = default_timer() scheduler.step() msg = f"epoch: {ep}, loss: {loss.item():.5f}, t2-t1: {t2 - t1:.5f}, trainL2: {train_l2_step:.5f}, testL2: {val_l2_step:.5f}" - logging.info(msg) + logger.info(msg) else: for ep in range(start_epoch, epochs): @@ -519,10 +518,10 @@ def run_training( t2 = default_timer() scheduler.step() msg = f"epoch: {ep}, loss: {loss.item():.5f}, t2-t1: {t2 - t1:.5f}, trainL2: {train_l2_step:.5f}, testL2: {val_l2_step:.5f}" - logging.info(msg) + logger.info(msg) if __name__ == "__main__": run_training() msg = "Done." - logging.info(msg) + logger.info(msg) From 89855366d18150211f98bf344b2ed38bf7ca3cf9 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 18 Dec 2024 23:41:59 +0100 Subject: [PATCH 130/137] fix more linting issues --- pdebench/data_gen/gen_diff_react.py | 40 ++++++++---------- pdebench/data_gen/gen_diff_sorp.py | 39 ++++++++---------- pdebench/data_gen/notebooks/Analysis.ipynb | 4 +- pdebench/data_gen/src/_attic/grf.py | 3 +- pdebench/data_gen/src/data_io.py | 5 ++- pdebench/data_gen/src/plots.py | 4 +- pdebench/models/analyse_result_forward.py | 17 +++----- pdebench/models/analyse_result_inverse.py | 4 +- pdebench/models/fno/train.py | 22 ++++------ pdebench/models/fno/utils.py | 3 -- pdebench/models/inverse/inverse.py | 23 +++++------ pdebench/models/metrics.py | 47 ++++++++++------------ pdebench/models/pinn/train.py | 44 +++++++++++--------- pyproject.toml | 3 ++ 14 files changed, 115 insertions(+), 143 deletions(-) diff --git a/pdebench/data_gen/gen_diff_react.py b/pdebench/data_gen/gen_diff_react.py index f175786..49d1a26 100644 --- a/pdebench/data_gen/gen_diff_react.py +++ b/pdebench/data_gen/gen_diff_react.py @@ -1,9 +1,20 @@ -#!/usr/bin/env python from __future__ import annotations +import logging +import multiprocessing as mp import os +import time +from itertools import repeat +from pathlib import Path import dotenv +import h5py +import hydra +import numpy as np +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils +from pdebench.data_gen.uploader import dataverse_upload # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir @@ -11,8 +22,6 @@ # e.g. HPC versus local laptop dotenv.load_dotenv() -import time - # or if the environment variables will be fixed for all executions, we can hard-code the environment variables like this: num_threads = "4" @@ -22,18 +31,6 @@ os.environ["VECLIB_MAXIMUM_THREADS"] = num_threads os.environ["NUMEXPR_NUM_THREADS"] = num_threads -import logging -import multiprocessing as mp -from itertools import repeat - -import dotenv -import h5py -import hydra -import numpy as np -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf -from pdebench.data_gen.src import utils -from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) @@ -102,17 +99,16 @@ def main(config: DictConfig): # Change to original working directory to import modules - temp_path = os.getcwd() + temp_path = Path.cwd() os.chdir(get_original_cwd()) # Change back to the hydra working directory os.chdir(temp_path) - work_path = os.path.dirname(config.work_dir) - output_path = os.path.join(work_path, config.data_dir, config.output_path) - if not os.path.isdir(output_path): - os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + ".h5" + work_path = Path(config.work_dir) + output_path: Path = work_path / config.data_dir / config.output_path + output_path.mkdir(output_path, exist_ok=True, parents=True) + config.output_path = (output_path / config.output_path).with_suffix(".h5") num_samples_init = 0 num_samples_final = 1000 @@ -133,7 +129,5 @@ def main(config: DictConfig): ) -import os - if __name__ == "__main__": test = main() diff --git a/pdebench/data_gen/gen_diff_sorp.py b/pdebench/data_gen/gen_diff_sorp.py index ec11951..2f1462b 100644 --- a/pdebench/data_gen/gen_diff_sorp.py +++ b/pdebench/data_gen/gen_diff_sorp.py @@ -1,9 +1,20 @@ -#!/usr/bin/env python from __future__ import annotations +import logging +import multiprocessing as mp import os +import time +from itertools import repeat +from pathlib import Path import dotenv +import h5py +import hydra +import numpy as np +from hydra.utils import get_original_cwd +from omegaconf import DictConfig, OmegaConf +from pdebench.data_gen.src import utils +from pdebench.data_gen.uploader import dataverse_upload # load environment variables from `.env` file if it exists # recursively searches for `.env` in all folders starting from work dir @@ -11,7 +22,6 @@ # e.g. HPC versus local laptop dotenv.load_dotenv() -import time # or if the environment variables will be fixed for all executions, we can hard-code the environment variables like this: num_threads = "4" @@ -22,18 +32,6 @@ os.environ["VECLIB_MAXIMUM_THREADS"] = num_threads os.environ["NUMEXPR_NUM_THREADS"] = num_threads -import logging -import multiprocessing as mp -from itertools import repeat - -import dotenv -import h5py -import hydra -import numpy as np -from hydra.utils import get_original_cwd -from omegaconf import DictConfig, OmegaConf -from pdebench.data_gen.src import utils -from pdebench.data_gen.uploader import dataverse_upload log = logging.getLogger(__name__) @@ -96,17 +94,16 @@ def main(config: DictConfig): # Change to original working directory to import modules - temp_path = os.getcwd() + temp_path = Path.cwd() os.chdir(get_original_cwd()) # Change back to the hydra working directory os.chdir(temp_path) - work_path = os.path.dirname(config.work_dir) - output_path = os.path.join(work_path, config.data_dir, config.output_path) - if not os.path.isdir(output_path): - os.makedirs(output_path) - config.output_path = os.path.join(output_path, config.output_path) + ".h5" + work_path = Path(config.work_dir).parent + output_path: Path = work_path / config.data_dir / config.output_path + output_path.mkdir(parents=True, exist_ok=True) + config.output_path = (output_path / config.output_path).with_suffix(".h5") num_samples_init = 0 num_samples_final = 10000 @@ -127,7 +124,5 @@ def main(config: DictConfig): ) -import os - if __name__ == "__main__": test = main() diff --git a/pdebench/data_gen/notebooks/Analysis.ipynb b/pdebench/data_gen/notebooks/Analysis.ipynb index 661bfc8..501363f 100644 --- a/pdebench/data_gen/notebooks/Analysis.ipynb +++ b/pdebench/data_gen/notebooks/Analysis.ipynb @@ -59,8 +59,6 @@ "import h5py\n", "import matplotlib.pyplot as plt\n", "from einops import rearrange\n", - "from phi.flow import *\n", - "from phi.vis import *\n", "from src.utils import resolve_path" ] }, @@ -79,7 +77,7 @@ ], "source": [ "data_path = resolve_path('${WORKING_DIR}/*/*/*/*/*/*.h5', idx=-1, unique=False)\n", - "print(data_path)" + "print(data_path) # noqa: T201\n" ] }, { diff --git a/pdebench/data_gen/src/_attic/grf.py b/pdebench/data_gen/src/_attic/grf.py index 5a14504..09ce407 100644 --- a/pdebench/data_gen/src/_attic/grf.py +++ b/pdebench/data_gen/src/_attic/grf.py @@ -44,5 +44,4 @@ def grf( # TODO: This is the rbf kernel; Matern kernel has more plausible smoothness. # Matern 3/2 PSD is # (18 * jnp.sqrt(3)* jnp.pi * sigma**2)/((4 * k^2 * jnp.pi**2 + 3/(rho**2))^(5/2) rho^3) - field = jnp.fft.irfft2(noise * gain, (xdim, ydim), norm="forward") - return field + return jnp.fft.irfft2(noise * gain, (xdim, ydim), norm="forward") diff --git a/pdebench/data_gen/src/data_io.py b/pdebench/data_gen/src/data_io.py index ed9810b..8004258 100644 --- a/pdebench/data_gen/src/data_io.py +++ b/pdebench/data_gen/src/data_io.py @@ -85,7 +85,7 @@ def to_ndarray(field: Field) -> np.ndarray: """ centered = to_centre_grid(field) order = _get_dim_order(centered.shape) - return centered.values.numpy(order=order) # noqa: PD011 + return centered.values.numpy(order=order) def dataverse_upload( @@ -119,4 +119,5 @@ def dataverse_upload( log.info("upload cmd %s", cmd) subprocess.Popen(cmd) from pathlib import Path - log.info("upload cmd %s$ %s", Path.cwd(), ' '.join(cmd)) + + log.info("upload cmd %s$ %s", Path.cwd(), " ".join(cmd)) diff --git a/pdebench/data_gen/src/plots.py b/pdebench/data_gen/src/plots.py index 29d5c96..d74cc8d 100644 --- a/pdebench/data_gen/src/plots.py +++ b/pdebench/data_gen/src/plots.py @@ -4,6 +4,7 @@ """ from __future__ import annotations +import h5py import imageio import matplotlib.pyplot as plt import numpy as np @@ -17,7 +18,8 @@ def plot_data(data, t, dim, channel, t_fraction, config, filename): plt.figure() plt.title(f"$t={t[t_idx]}$") if dim == 1: - x = np.array(h5_file["grid"]["x"], dtype="f") + with h5py.File(config.data_path, 'r') as h5_file: + x = np.array(h5_file["grid"]["x"], dtype="f") plt.plot(x.squeeze(), data[t_idx, ..., channel]) plt.xlabel("$x$") else: diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 752b629..8191f3b 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -147,7 +147,7 @@ from __future__ import annotations import _pickle as cPickle -import glob +from pathlib import Path import matplotlib.pyplot as plt import numpy as np @@ -156,7 +156,7 @@ def main(): # get results - files = glob.glob("./*pickle") + files = list(Path().glob("*.pickle")) files.sort() # metric names @@ -173,10 +173,9 @@ def main(): # define index index1, index2, index3 = [], [], [] - for j, fl in enumerate(files): - with open(fl, "rb") as f: + for _j, fl in enumerate(files): + with Path(fl).open("rb") as f: title = fl.split("\\")[-1][:-7].split("_") - print(title) if title[0] == "1D": if title[1] == "CFD": index1.append(title[0] + title[1]) @@ -212,7 +211,7 @@ def main(): # create dataframe data = np.zeros([len(files), 8]) for j, fl in enumerate(files): - with open(fl, "rb") as f: + with Path(fl).open("rb") as f: test = cPickle.load(f) for i, var in enumerate(test): if i == 5: @@ -230,10 +229,7 @@ def main(): num_models = len(models) x = np.arange(num_pdes) - if num_models == 1: - width = 0.5 - else: - width = 0.5 / (num_models - 1) + width = 0.5 if num_models == 1 else 0.5 / (num_models - 1) fig, ax = plt.subplots(figsize=(8, 6)) for i in range(num_models): @@ -253,4 +249,3 @@ def main(): if __name__ == "__main__": main() - print("Done.") diff --git a/pdebench/models/analyse_result_inverse.py b/pdebench/models/analyse_result_inverse.py index a0ad534..bcb00f5 100644 --- a/pdebench/models/analyse_result_inverse.py +++ b/pdebench/models/analyse_result_inverse.py @@ -156,7 +156,7 @@ def main(): data = pd.read_csv(filename) pdes = data["pde"].drop_duplicates() num_pdes = len(pdes) - models = list(data.columns.values[-2:]) + models = list(data.columns.to_numpy()[-2:]) num_models = len(models) x = np.arange(num_pdes) width = 0.5 / (num_models) @@ -170,7 +170,6 @@ def main(): yerr=data[data.iloc[:, 1] == "std"][models[i]], width=width, ) - print(width, pos) ax.set_xticks(x) ax.set_xticklabels(pdes, rotation=45, fontsize=30) @@ -185,4 +184,3 @@ def main(): if __name__ == "__main__": main() - print("Done.") diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index d723908..209fa6e 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -84,16 +84,10 @@ def run_training( # print("FNODatasetMult") train_data = FNODatasetMult( flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, saved_folder=base_path, ) val_data = FNODatasetMult( flnm, - reduced_resolution=reduced_resolution, - reduced_resolution_t=reduced_resolution_t, - reduced_batch=reduced_batch, if_test=True, saved_folder=base_path, ) @@ -215,9 +209,9 @@ def run_training( # xx: input tensor (first few time steps) [b, x1, ..., xd, t_init, v] # yy: target tensor [b, x1, ..., xd, t, v] # grid: meshgrid [b, x1, ..., xd, dims] - xx = xx.to(device) - yy = yy.to(device) - grid = grid.to(device) + xx = xx.to(device) # noqa: PLW2901 + yy = yy.to(device) # noqa: PLW2901 + grid = grid.to(device) # noqa: PLW2901 # Initialize the prediction tensor pred = yy[..., :initial_step, :] @@ -249,7 +243,7 @@ def run_training( # Concatenate the prediction at the current time step to be used # as input for the next time step - xx = torch.cat((xx[..., 1:, :], im), dim=-2) + xx = torch.cat((xx[..., 1:, :], im), dim=-2) # noqa: PLW2901 train_l2_step += loss.item() _batch = yy.size(0) @@ -281,9 +275,9 @@ def run_training( with torch.no_grad(): for xx, yy, grid in val_loader: loss = 0 - xx = xx.to(device) - yy = yy.to(device) - grid = grid.to(device) + xx = xx.to(device) # noqa: PLW2901 + yy = yy.to(device) # noqa: PLW2901 + grid = grid.to(device) # noqa: PLW2901 if training_type in ["autoregressive"]: pred = yy[..., :initial_step, :] @@ -302,7 +296,7 @@ def run_training( pred = torch.cat((pred, im), -2) - xx = torch.cat((xx[..., 1:, :], im), dim=-2) + xx = torch.cat((xx[..., 1:, :], im), dim=-2) # noqa: PLW2901 val_l2_step += loss.item() _batch = yy.size(0) diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index 4151a27..fd66782 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -530,9 +530,6 @@ def __init__( filename, initial_step=10, saved_folder="../data/", - reduced_resolution=1, - reduced_resolution_t=1, - reduced_batch=1, if_test=False, test_ratio=0.1, ): diff --git a/pdebench/models/inverse/inverse.py b/pdebench/models/inverse/inverse.py index bfe3e34..2e5982e 100644 --- a/pdebench/models/inverse/inverse.py +++ b/pdebench/models/inverse/inverse.py @@ -163,8 +163,7 @@ def fit(self, x): def transform(self, x): eps = 1e-20 x = x - self.mean - x = x / (self.std + eps) - return x + return x / (self.std + eps) def fit_transform(self, x): self.fit(x) @@ -203,36 +202,32 @@ def __init__( torch.tensor([self.prior_std], device=self.device, dtype=torch.float), ) self.latent = PyroSample(dist.Normal(_m, _s).expand(latent_dims).to_event(2)) - print(self.latent_dims, self.dims) def get_latent(self): if self.latent_dims == self.dims: return self.latent.unsqueeze(0) # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( + return F.interpolate( self.latent.unsqueeze(1), self.dims, mode=self.interpolation, align_corners=False, ).squeeze(0) # squeeze/unsqueeze is because of weird interpolate semantics - return l def latent2source(self, latent): if latent.shape == self.dims: return latent.unsqueeze(0) # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( + return F.interpolate( latent.unsqueeze(1), self.dims, mode=self.interpolation, align_corners=False ).squeeze(0) # squeeze/unsqueeze is because of weird interpolate semantics - return l def forward(self, grid, y=None): # overwrite process predictor batch with my own latent x = self.get_latent() # print("forward:x.shape,grid.shape=",x.shape,grid.shape) mean = self.process_predictor(x.to(self.device), grid.to(self.device)) - o = pyro.sample("obs", dist.Normal(mean, self.obs_scale).to_event(2), obs=y) - return o + return pyro.sample("obs", dist.Normal(mean, self.obs_scale).to_event(2), obs=y) class InitialConditionInterp(nn.Module): @@ -248,11 +243,11 @@ class InitialConditionInterp(nn.Module): """ def __init__(self, dims, hidden_dim): - super(InitialConditionInterp, self).__init__() + super().__init__() self.spatial_dim = len(hidden_dim) - self.dims = [1] + dims if len(dims) == 1 else dims + self.dims = [1, *dims] if len(dims) == 1 else dims # self.dims = [1,1,1]+dims - self.hidden_dim = [1] + hidden_dim if len(hidden_dim) == 1 else hidden_dim + self.hidden_dim = [1, *hidden_dim] if len(hidden_dim) == 1 else hidden_dim self.interpolation = "bilinear" if len(hidden_dim) < 3 else "trilinear" self.scale = 1 / prod(hidden_dim) self.latent = nn.Parameter( @@ -264,10 +259,10 @@ def latent2source(self, latent): if latent.shape[2:] == self.dims: return latent # `mini-batch x channels x [optional depth] x [optional height] x width`. - l = F.interpolate( + latent = F.interpolate( latent, self.dims, mode=self.interpolation, align_corners=False ) - return l.view(self.dims) + return latent.view(self.dims) def forward(self): x = self.latent2source(self.latent) diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index 4c85dd7..dc0ee5c 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -146,6 +146,7 @@ """ from __future__ import annotations +import logging import math as mt import matplotlib.pyplot as plt @@ -156,6 +157,8 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +logger = logging.getLogger(__name__) + def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12, initial_step=1): """ @@ -320,30 +323,28 @@ def metrics( ): if mode == "Unet": with torch.no_grad(): - itot = 0 - for xx, yy in val_loader: - xx = xx.to(device) - yy = yy.to(device) + for itot, (xx, yy) in enumerate(val_loader): + xx = xx.to(device) # noqa: PLW2901 + yy = yy.to(device) # noqa: PLW2901 pred = yy[..., :initial_step, :] inp_shape = list(xx.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - for t in range(initial_step, yy.shape[-2]): + for _t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) temp_shape = [0, -1] temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - y = yy[..., t : t + 1, :] temp_shape = [0] temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) im = model(inp).permute(temp_shape).unsqueeze(-2) pred = torch.cat((pred, im), -2) - xx = torch.cat((xx[..., 1:, :], im), dim=-2) + xx = torch.cat((xx[..., 1:, :], im), dim=-2) # noqa: PLW2901 ( _err_RMSE, @@ -381,27 +382,25 @@ def metrics( torch.mean((pred - yy) ** 2, dim=mean_dim) ) - itot += 1 elif mode == "FNO": with torch.no_grad(): itot = 0 - for xx, yy, grid in val_loader: - xx = xx.to(device) - yy = yy.to(device) - grid = grid.to(device) + for itot, (xx, yy, grid) in enumerate(val_loader): + xx = xx.to(device) # noqa: PLW2901 + yy = yy.to(device) # noqa: PLW2901 + grid = grid.to(device) # noqa: PLW2901 pred = yy[..., :initial_step, :] inp_shape = list(xx.shape) inp_shape = inp_shape[:-2] inp_shape.append(-1) - for t in range(initial_step, yy.shape[-2]): + for _t in range(initial_step, yy.shape[-2]): inp = xx.reshape(inp_shape) - y = yy[..., t : t + 1, :] im = model(inp, grid) pred = torch.cat((pred, im), -2) - xx = torch.cat((xx[..., 1:, :], im), dim=-2) + xx = torch.cat((xx[..., 1:, :], im), dim=-2) # noqa: PLW2901 ( _err_RMSE, @@ -438,7 +437,6 @@ def metrics( torch.mean((pred - yy) ** 2, dim=mean_dim) ) - itot += 1 elif mode == "PINN": raise NotImplementedError @@ -449,12 +447,12 @@ def metrics( err_Max = np.array(err_Max.data.cpu() / itot) err_BD = np.array(err_BD.data.cpu() / itot) err_F = np.array(err_F.data.cpu() / itot) - print(f"RMSE: {err_RMSE:.5f}") - print(f"normalized RMSE: {err_nRMSE:.5f}") - print(f"RMSE of conserved variables: {err_CSV:.5f}") - print(f"Maximum value of rms error: {err_Max:.5f}") - print(f"RMSE at boundaries: {err_BD:.5f}") - print(f"RMSE in Fourier space: {err_F}") + logger.info(f"RMSE: {err_RMSE:.5f}") + logger.info(f"normalized RMSE: {err_nRMSE:.5f}") + logger.info(f"RMSE of conserved variables: {err_CSV:.5f}") + logger.info(f"Maximum value of rms error: {err_Max:.5f}") + logger.info(f"RMSE at boundaries: {err_BD:.5f}") + logger.info(f"RMSE in Fourier space: {err_F}") val_l2_time = val_l2_time / itot @@ -667,7 +665,7 @@ def __init__(self, reduction="mean"): # Dimension and Lp-norm type are positive self.reduction = reduction - def __call__(self, x, y, flow=None, fhigh=None, eps=1e-20): + def __call__(self, x, y, flow=None, fhigh=None): num_examples = x.size()[0] others_dims = x.shape[1:-2] for d in others_dims: @@ -767,7 +765,7 @@ def inverse_metrics(u0, x, pred_u0, y): fftl3loss_mid_pred_u0 = fftl3loss_fn(pred_u0, y, fmid, 2 * fmid).item() fftl3loss_hi_pred_u0 = fftl3loss_fn(pred_u0, y, 2 * fmid).item() - metric = { + return { "mseloss_u0": mseloss_u0, "l2loss_u0": l2loss_u0, "l3loss_u0": l3loss_u0, @@ -800,4 +798,3 @@ def inverse_metrics(u0, x, pred_u0, y): "fftl3loss_hi_pred_u0": fftl3loss_hi_pred_u0, } - return metric diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index eda1390..dae24f8 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -2,6 +2,7 @@ from __future__ import annotations import pickle +from pathlib import Path import deepxde as dde import matplotlib.pyplot as plt @@ -194,9 +195,11 @@ def setup_pde1D( xL=0.0, xR=1.0, if_periodic_bc=True, - aux_params=[0.1], + aux_params=None, ): # TODO: read from dataset config file + if aux_params is None: + aux_params = [0.1] geom = dde.geometry.Interval(xL, xR) boundary_r = lambda x, on_boundary: _boundary_r(x, on_boundary, xL, xR) if filename[0] == "R": @@ -293,9 +296,11 @@ def setup_CFD2D( yL=0.0, yR=1.0, if_periodic_bc=True, - aux_params=[1.6667], + aux_params=None, ): # TODO: read from dataset config file + if aux_params is None: + aux_params = [1.6667] geom = dde.geometry.Rectangle((-1, -1), (1, 1)) timedomain = dde.geometry.TimeDomain(0.0, 1.0) pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) @@ -319,7 +324,7 @@ def setup_CFD2D( initial_input.cpu(), initial_u[..., 3].unsqueeze(1), component=3 ) # prepare boundary condition - bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) + # bc = dde.icbc.PeriodicBC(geomtime, lambda x: 0, lambda _, on_boundary: on_boundary) data = dde.data.TimePDE( geomtime, pde, @@ -343,9 +348,11 @@ def setup_CFD3D( input_ch=2, output_ch=4, hidden_ch=40, - aux_params=[1.6667], + aux_params=None, ): # TODO: read from dataset config file + if aux_params is None: + aux_params = [1.6667] geom = dde.geometry.Cuboid((0.0, 0.0, 0.0), (1.0, 1.0, 1.0)) timedomain = dde.geometry.TimeDomain(0.0, 1.0) pde = lambda x, y: pde_CFD2d(x, y, aux_params[0]) @@ -423,10 +430,7 @@ def _run_training( if_periodic_bc=if_periodic_bc, aux_params=aux_params, ) - if flnm.split("_")[1][0] == "C": - n_components = 3 - else: - n_components = 1 + n_components = 3 if flnm.split("_")[1][0] == "C" else 1 elif scenario == "CFD2D": model, dataset = setup_CFD2D( filename=flnm, @@ -448,13 +452,11 @@ def _run_training( ) n_components = 5 else: - raise NotImplementedError(f"PINN training not implemented for {scenario}") + msg = f"PINN training not implemented for {scenario}" + raise NotImplementedError(msg) # filename - if if_single_run: - model_name = flnm + "_PINN" - else: - model_name = flnm[:-5] + "_PINN" + model_name = flnm + "_PINN" if if_single_run else flnm[:-5] + "_PINN" checker = dde.callbacks.ModelCheckpoint( f"{model_name}.pt", save_better_only=True, period=5000 @@ -483,8 +485,8 @@ def _run_training( if if_single_run: errs = metric_func(test_pred, test_gt) errors = [np.array(err.cpu()) for err in errs] - print(errors) - pickle.dump(errors, open(model_name + ".pickle", "wb")) + with Path(model_name + ".pickle").open("wb") as f: + pickle.dump(errors, f) # plot sample plot_input = dataset.generate_plot_input(time=1.0) @@ -507,11 +509,11 @@ def _run_training( plt.imshow(im_data) plt.savefig(f"{model_name}.png") + return None # TODO: implement function to get specific timestep from dataset # y_true = dataset[:][1][-xdim * ydim :] - else: - return test_pred, test_gt, model_name + return test_pred, test_gt, model_name def run_training( @@ -525,9 +527,11 @@ def run_training( root_path="../data/", val_num=10, if_periodic_bc=True, - aux_params=[None], + aux_params=None, seed="0000", ): + if aux_params is None: + aux_params = [None] if val_num == 1: # single job _run_training( scenario, @@ -569,8 +573,8 @@ def run_training( errs = metric_func(test_pred, test_gt) errors = [np.array(err.cpu()) for err in errs] - print(errors) - pickle.dump(errors, open(model_name + ".pickle", "wb")) + with Path(model_name + ".pickle").open("wb") as f: + pickle.dump(errors, f) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 87a4493..dc19ee7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -174,6 +174,9 @@ ignore = [ "UP007", "ARG001", # too many false positives "ARG005", + "E731", # do not assign a lambda expression, use a def + "G004", + "PD008", # dataframe confused with jax array ] isort.required-imports = ["from __future__ import annotations"] # Uncomment if using a _compat.typing backport From 6bd9802268e05a4e5d1797296d380d897f16302b Mon Sep 17 00:00:00 2001 From: leiterrl Date: Wed, 18 Dec 2024 23:59:35 +0100 Subject: [PATCH 131/137] fix remaing linter warnings --- .../CompressibleFluid/CFD_Hydra.py | 31 +++----- .../CompressibleFluid/CFD_multi_Hydra.py | 65 +++++++--------- pdebench/data_gen/data_gen_NLE/Data_Merge.py | 62 ++++++--------- ...ction_diffusion_2D_multi_solution_Hydra.py | 7 +- .../reaction_diffusion_Hydra.py | 22 +++--- ...reaction_diffusion_multi_solution_Hydra.py | 15 +--- pdebench/data_gen/data_gen_NLE/utils.py | 76 ++++++------------- 7 files changed, 98 insertions(+), 180 deletions(-) diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py index b084c1f..fe7b86f 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_Hydra.py @@ -143,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging @@ -202,7 +203,6 @@ def main(cfg: DictConfig) -> None: yc = ye[:-1] + 0.5 * dy zc = ze[:-1] + 0.5 * dz - def evolve(Q): t = cfg.args.ini_time tsave = t @@ -219,7 +219,7 @@ def evolve(Q): i_save += 1 if steps % cfg.args.show_steps == 0 and cfg.args.if_show: - print(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") + logger.info(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") carry = (Q, t, dt, steps, tsave) Q, t, dt, steps, tsave = lax.fori_loop( @@ -227,7 +227,7 @@ def evolve(Q): ) tm_fin = time.time() - print(f"total elapsed time is {tm_fin - tm_ini} sec") + logger.info(f"total elapsed time is {tm_fin - tm_ini} sec") save_data_HD( Q[:, 2:-2, 2:-2, 2:-2], xc, @@ -345,9 +345,7 @@ def update(Q, Q_tmp, dt): Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) ) # p - Q = Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) - - return Q + return Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) @jit def update_vis(carry): @@ -523,26 +521,21 @@ def _update_vis_z(carry): def flux_x(Q): QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) # f_Riemann = HLL(QL, QR, direc=0) - f_Riemann = HLLC(QL, QR, direc=0) - return f_Riemann + return HLLC(QL, QR, direc=0) @jit def flux_y(Q): _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) # f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) - f_Riemann = jnp.transpose( - HLLC(QL, QR, direc=1), (0, 3, 1, 2) - ) # (x,y,z) = (Z,X,Y) - return f_Riemann + return jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) @jit def flux_z(Q): _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) # f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) - f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) - return f_Riemann + return jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) @partial(jit, static_argnums=(2,)) def HLL(QL, QR, direc): @@ -596,9 +589,7 @@ def HLL(QL, QR, direc): # L: left of cell = right-going, R: right of cell: left-going f_Riemann = jnp.where(Sfl > 0.0, fR[:, 1:-2], fHLL) - f_Riemann = jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) - - return f_Riemann + return jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) @partial(jit, static_argnums=(2,)) def HLLC(QL, QR, direc): @@ -687,13 +678,11 @@ def HLLC(QL, QR, direc): f_Riemann = jnp.where( Sfl * Va < 0.0, fal, f_Riemann ) # SL < 0 and Va > 0 : sub-sonic - f_Riemann = jnp.where( + return jnp.where( Sfr * Va < 0.0, far, f_Riemann ) # Va < 0 and SR > 0 : sub-sonic # f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic - return f_Riemann - Q = jnp.zeros([5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4]) Q = init_HD( Q, @@ -709,7 +698,7 @@ def HLLC(QL, QR, direc): ) Q = device_put(Q) # putting variables in GPU (not necessary??) t = evolve(Q) - print(f"final time is: {t:.3f}") + logger.info(f"final time is: {t:.3f}") if __name__ == "__main__": diff --git a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py index aa1c6ba..ecc4865 100644 --- a/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/CompressibleFluid/CFD_multi_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -144,8 +143,10 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations +import logging import os import random import sys @@ -157,14 +158,7 @@ import jax import jax.numpy as jnp from jax import device_put, jit, lax - -# Hydra from omegaconf import DictConfig - -os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" -os.environ["XLA_PYTHON_CLIENT_MEM_FRACTION"] = ".9" - -sys.path.append("..") from utils import ( Courant_HD, Courant_vis_HD, @@ -179,6 +173,15 @@ limiting_HD, ) +logger = logging.getLogger(__name__) + +# Hydra + +os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" +os.environ["XLA_PYTHON_CLIENT_MEM_FRACTION"] = ".9" + +sys.path.append("..") + # if double precision # from jax.config import config # config.update("jax_enable_x64", True) @@ -197,9 +200,6 @@ def main(cfg: DictConfig) -> None: gamminv1 = 1.0 / gammi1 gamgamm1inv = gamma * gamminv1 gammi1 = gamma - 1.0 - gampl1 = gamma + 1.0 - gammi3 = gamma - 3.0 - gampl3 = gamma + 3.0 BCs = ["trans", "periodic", "KHI"] # reflect assert cfg.args.bc in BCs, "bc should be in 'trans, reflect, periodic'" @@ -240,7 +240,7 @@ def main(cfg: DictConfig) -> None: else: zeta = cfg.args.zeta eta = cfg.args.eta - print(f"zeta: {zeta:>5f}, eta: {eta:>5f}") + logger.info(f"zeta: {zeta:>5f}, eta: {eta:>5f}") visc = zeta + eta / 3.0 def evolve(Q): @@ -299,7 +299,7 @@ def _save(_carry): ) tm_fin = time.time() - print(f"total elapsed time is {tm_fin - tm_ini} sec") + logger.info(f"total elapsed time is {tm_fin - tm_ini} sec") DDD = DDD.at[-1].set(Q[0, 2:-2, 2:-2, 2:-2]) VVx = VVx.at[-1].set(Q[1, 2:-2, 2:-2, 2:-2]) VVy = VVy.at[-1].set(Q[2, 2:-2, 2:-2, 2:-2]) @@ -411,9 +411,7 @@ def update(Q, Q_tmp, dt): Q = Q.at[4, 2:-2, 2:-2, 2:-2].set( gammi1 * (E0 - 0.5 * (Mx**2 + My**2 + Mz**2) / D0) ) # p - Q = Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) - - return Q + return Q.at[4].set(jnp.where(Q[4] > 1.0e-8, Q[4], cfg.args.p_floor)) @jit def update_vis(carry): @@ -587,26 +585,21 @@ def _update_vis_z(carry): def flux_x(Q): QL, QR = limiting_HD(Q, if_second_order=cfg.args.if_second_order) # f_Riemann = HLL(QL, QR, direc=0) - f_Riemann = HLLC(QL, QR, direc=0) - return f_Riemann + return HLLC(QL, QR, direc=0) @jit def flux_y(Q): _Q = jnp.transpose(Q, (0, 2, 3, 1)) # (y, z, x) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) # f_Riemann = jnp.transpose(HLL(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) - f_Riemann = jnp.transpose( - HLLC(QL, QR, direc=1), (0, 3, 1, 2) - ) # (x,y,z) = (Z,X,Y) - return f_Riemann + return jnp.transpose(HLLC(QL, QR, direc=1), (0, 3, 1, 2)) # (x,y,z) = (Z,X,Y) @jit def flux_z(Q): _Q = jnp.transpose(Q, (0, 3, 1, 2)) # (z, x, y) QL, QR = limiting_HD(_Q, if_second_order=cfg.args.if_second_order) # f_Riemann = jnp.transpose(HLL(QL, QR, direc=2), (0, 2, 3, 1)) - f_Riemann = jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) - return f_Riemann + return jnp.transpose(HLLC(QL, QR, direc=2), (0, 2, 3, 1)) @partial(jit, static_argnums=(2,)) def HLL(QL, QR, direc): @@ -660,9 +653,7 @@ def HLL(QL, QR, direc): # L: left of cell = right-going, R: right of cell: left-going f_Riemann = jnp.where(Sfl > 0.0, fR[:, 1:-2], fHLL) - f_Riemann = jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) - - return f_Riemann + return jnp.where(Sfr < 0.0, fL[:, 2:-1], f_Riemann) @partial(jit, static_argnums=(2,)) def HLLC(QL, QR, direc): @@ -751,13 +742,11 @@ def HLLC(QL, QR, direc): f_Riemann = jnp.where( Sfl * Va < 0.0, fal, f_Riemann ) # SL < 0 and Va > 0 : sub-sonic - f_Riemann = jnp.where( + return jnp.where( Sfr * Va < 0.0, far, f_Riemann ) # Va < 0 and SR > 0 : sub-sonic # f_Riemann = jnp.where(Sfr < 0., fL[:, 2:-1], f_Riemann) # SR < 0 : supersonic - return f_Riemann - Q = jnp.zeros( [cfg.args.numbers, 5, cfg.args.nx + 4, cfg.args.ny + 4, cfg.args.nz + 4] ) @@ -838,7 +827,7 @@ def HLLC(QL, QR, direc): ) elif cfg.args.init_mode_Multi == "KHs": assert 2.0 * yc[0] - (yc[1] - yc[0]) == 0.0, "yL is assumed 0!" - print("now we are coming into KHs...") + logger.info("now we are coming into KHs...") Q = init_multi_HD_KH( Q, xc, @@ -851,7 +840,7 @@ def HLLC(QL, QR, direc): gamma=cfg.args.gamma, ) elif cfg.args.init_mode_Multi == "2D_Turbs": - print("now we are coming into 2DTurbs......") + logger.info("now we are coming into 2DTurbs......") Q = init_multi_HD_2DTurb( Q, xc, @@ -864,10 +853,10 @@ def HLLC(QL, QR, direc): gamma=cfg.args.gamma, ) elif cfg.args.init_mode_Multi == "2D_rand": - assert ( + assert ( # noqa: PT018 xe[0] == 0.0 and ye[0] == 0.0 and xe[-1] == 1.0 and ye[-1] == 1.0 ), "xc, yc should be between 0 and 1!" - print("now we are coming into 2Drand......") + logger.info("now we are coming into 2Drand......") Q = init_multi_HD_2DRand( Q, xc, @@ -880,7 +869,7 @@ def HLLC(QL, QR, direc): gamma=cfg.args.gamma, ) elif cfg.args.init_mode_Multi == "3D_Turbs": - print("now we are coming into 3DTurbs......") + logger.info("now we are coming into 3DTurbs......") Q = init_multi_HD_3DTurb( Q, xc, @@ -893,7 +882,7 @@ def HLLC(QL, QR, direc): gamma=cfg.args.gamma, ) elif cfg.args.init_mode_Multi == "3D_rand": - print("now we are coming into 3Drand......") + logger.info("now we are coming into 3Drand......") Q = init_multi_HD_3DRand( Q, xc, @@ -905,7 +894,7 @@ def HLLC(QL, QR, direc): k_tot=cfg.args.k_tot, gamma=cfg.args.gamma, ) - print("initial conditions were prepared!!") + logger.info("initial conditions were prepared!!") Q = device_put(Q) # putting variables in GPU (not necessary??) local_device_count = jax.local_device_count() @@ -929,7 +918,7 @@ def HLLC(QL, QR, direc): VVy = VVy.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) VVz = VVz.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) PPP = PPP.reshape(cfg.args.numbers, itot, cfg.args.nx, cfg.args.ny, cfg.args.nz) - print("now data saving...") + logger.info("now data saving...") jnp.save( cfg.args.save + "HD_Sols_" diff --git a/pdebench/data_gen/data_gen_NLE/Data_Merge.py b/pdebench/data_gen/data_gen_NLE/Data_Merge.py index d738cff..16e930b 100644 --- a/pdebench/data_gen/data_gen_NLE/Data_Merge.py +++ b/pdebench/data_gen/data_gen_NLE/Data_Merge.py @@ -143,38 +143,33 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ -""" -Data_Merge.py -This is a script creating HDF5 from the generated data (numpy array) by our data generation scripts. -A more detailed explanation how to use this script is provided in the README. -""" - - -# Hydra - from __future__ import annotations -import glob +from pathlib import Path import h5py import hydra import numpy as np from omegaconf import DictConfig +""" +Data_Merge.py +This is a script creating HDF5 from the generated data (numpy array) by our data generation scripts. +A more detailed explanation how to use this script is provided in the README. +""" + def _mergeRD(var, DataND, savedir): _vars = ["2D", "nu"] if var not in _vars: - print(var + " is not defined!") return None idx = 0 - data = glob.glob(savedir + "/" + var + "*key*.npy") - data.sort() - for data in data: - print(idx, data) - test = np.load(data).squeeze() + data_list = Path(savedir).glob(var + "*key*.npy") + data_list.sort() + for data_file in data_list: + test = np.load(data_file).squeeze() batch = min(test.shape[0], DataND.shape[0] - idx) if var == "2D": DataND[idx : idx + batch] = test[:batch, -2] @@ -193,15 +188,13 @@ def _merge(var, DataND, dim, savedir): elif dim == 3: _vars = ["D", "P", "Vx", "Vy", "Vz"] if var not in _vars: - print(var + " is not defined!") return None idx = 0 - data = glob.glob(savedir + "/HD*" + var + ".npy") + data = Path(savedir).glob("HD*" + var + ".npy") data.sort() - for data in data: - print(idx, data) - test = np.load(data).squeeze() + for data_file in data: + test = np.load(data_file).squeeze() batch = min(test.shape[0], DataND.shape[0] - idx) DataND[idx : idx + batch] = test[:batch] idx += batch @@ -216,13 +209,11 @@ def nan_check(data): def merge(type, dim, bd, nbatch, savedir): if type == "CFD": - data = glob.glob(savedir + "/HD*D.npy") + data = Path(savedir).glob("HD*D.npy") data.sort() test = np.load(data[0]) __nbatch, nt, nx, ny, nz = test.shape _nbatch = __nbatch * len(data) - print("nb, nt, nx, ny, nz: ", _nbatch, nt, nx, ny, nz) - print(f"nbatch: {nbatch}, _nbatch: {_nbatch}") assert ( nbatch <= _nbatch ), "nbatch should be equal or less than the number of generated samples" @@ -241,16 +232,14 @@ def merge(type, dim, bd, nbatch, savedir): vars = ["D", "P", "Vx", "Vy", "Vz"] elif type == "ReacDiff": - data = glob.glob(savedir + "/nu*.npy") + data = Path(savedir).glob("nu*.npy") data.sort() test = np.load(data[0]) __nbatch, nx, ny = test.shape _nbatch = __nbatch * len(data) - print(f"nbatch: {nbatch}, _nbatch: {_nbatch}") assert ( nbatch == _nbatch ), "nbatch should be equal or less than the number of generated samples" - print("nb, nx, ny: ", _nbatch, nx, ny) DataND = np.zeros([nbatch, nx, ny], dtype=np.float32) vars = ["2D", "nu"] @@ -259,10 +248,8 @@ def merge(type, dim, bd, nbatch, savedir): _DataND = _merge(var, DataND, dim, savedir) if var == "D": idx_neg, idx_pos = nan_check(_DataND) - print(f"idx_neg: {len(idx_neg)}, idx_pos: {len(idx_pos)}") if len(idx_pos) < nbatch: - print("too many ill-defined data...") - print(f"nbatch: {nbatch}, idx_pos: {len(idx_pos)}") + pass _DataND = _DataND[idx_pos] _DataND = _DataND[:nbatch] np.save(savedir + "/" + var + ".npy", _DataND) @@ -270,7 +257,7 @@ def merge(type, dim, bd, nbatch, savedir): DataND = _mergeRD(var, DataND, savedir) np.save(savedir + "/" + var + ".npy", DataND) - data = glob.glob(savedir + "/*npy") + data = Path(savedir).glob("*npy") data.sort() if type == "CFD": @@ -284,7 +271,7 @@ def merge(type, dim, bd, nbatch, savedir): del data[-1] if type == "ReacDiff": # data = glob.glob('save/' + type + '/nu*key*npy') - data = glob.glob(savedir + "/nu*key*npy") + data = Path(savedir).glob("nu*key*npy") data.sort() _beta = data[0].split("/")[-1].split("_")[3] flnm = savedir + "/2D_DecayFlow_" + _beta + "_Train.hdf5" @@ -343,7 +330,6 @@ def merge(type, dim, bd, nbatch, savedir): + bd + "_Train.hdf5" ) - print(flnm) del DataND @@ -361,17 +347,17 @@ def merge(type, dim, bd, nbatch, savedir): f.create_dataet("t-coordinate", data=tcrd) eta = float(_eta[3:]) zeta = float(_zeta[4:]) - print("(eta, zeta) = ", eta, zeta) f.attrs["eta"] = eta f.attrs["zeta"] = zeta if dim > 1: M = float(_M[1:]) f.attrs["M"] = M - print("M: ", M) + return None + return None def transform(type, savedir): - data = glob.glob(savedir + "/*npy") + data = Path(savedir).glob("*npy") data.sort() xcrd = np.load(data[-1]) del data[-1] @@ -380,7 +366,6 @@ def transform(type, savedir): flnm = data[0] with h5py.File(flnm[:-3] + "hdf5", "w") as f: - print(flnm) _data = np.load(flnm) f.create_dataset("tensor", data=_data.astype(np.float32)) @@ -388,18 +373,15 @@ def transform(type, savedir): f.create_dataset("t-coordinate", data=tcrd) if type == "advection": beta = float(flnm.split("/")[-1].split("_")[3][4:-4]) # advection train - print(f"beta: {beta}") f.attrs["beta"] = beta elif type == "burgers": Nu = float(flnm.split("/")[-1].split("_")[-1][2:-4]) # Burgers test/train - print(f"Nu: {Nu}") f.attrs["Nu"] = Nu elif type == "ReacDiff": Rho = float(flnm.split("/")[-1].split("_")[-1][3:-4]) # reac-diff test Nu = float(flnm.split("/")[-1].split("_")[-2][2:]) # reac-diff test - print(f"Nu, rho: {Nu, Rho}") f.attrs["Nu"] = Nu f.attrs["rho"] = Rho diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py index 203dab9..a0a7243 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_2D_multi_solution_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -144,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import sys @@ -228,9 +228,7 @@ def _show(_carry): t, tsave, steps, i_save, dt, u, uu, nu = lax.while_loop( cond_fun, _body_fun, carry ) - uu = uu.at[-1].set(u) - - return uu + return uu.at[-1].set(u) @jax.jit def simulation_fn(i, carry): @@ -325,7 +323,6 @@ def update(u, u_tmp, dt, nu): vm_evolve = vmap(evolve, 0, 0) uu = vm_evolve(u, nu) - print("data saving...") cwd = hydra.utils.get_original_cwd() + "/" jnp.save( cwd diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py index 2e6156c..cfcfe26 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -144,8 +143,10 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations +import logging import sys import time from math import ceil @@ -161,12 +162,12 @@ sys.path.append("..") from utils import Courant_diff, bc, init +logger = logging.getLogger(__name__) + # Init arguments with Hydra @hydra.main(config_path="config") def main(cfg: DictConfig) -> None: - print(f"nu: {cfg.args.nu:.3f}, rho: {cfg.args.rho:.3f}") - # basic parameters dx = (cfg.args.xR - cfg.args.xL) / cfg.args.nx dx_inv = 1.0 / dx @@ -197,7 +198,7 @@ def evolve(u): i_save += 1 if steps % cfg.args.show_steps == 0 and cfg.args.if_show: - print(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") + logger.info(f"now {steps:d}-steps, t = {t:.3f}, dt = {dt:.3f}") carry = (u, t, dt, steps, tsave) u, t, dt, steps, tsave = lax.fori_loop( @@ -205,7 +206,7 @@ def evolve(u): ) tm_fin = time.time() - print(f"total elapsed time is {tm_fin - tm_ini} sec") + logger.info(f"total elapsed time is {tm_fin - tm_ini} sec") return uu, t @jax.jit @@ -247,21 +248,20 @@ def flux(u): u, dx, Ncell=cfg.args.nx ) # index 2 for _U is equivalent with index 0 for u # source term - f = -cfg.args.nu * (_u[2 : cfg.args.nx + 3] - _u[1 : cfg.args.nx + 2]) * dx_inv - return f + return ( + -cfg.args.nu * (_u[2 : cfg.args.nx + 3] - _u[1 : cfg.args.nx + 2]) * dx_inv + ) @jax.jit def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method # stiff equation - u = 1.0 / (1.0 + jnp.exp(-cfg.args.rho * dt) * (1.0 - u) / u) - return u + return 1.0 / (1.0 + jnp.exp(-cfg.args.rho * dt) * (1.0 - u) / u) u = init(xc=xc, mode=cfg.args.init_mode) u = device_put(u) # putting variables in GPU (not necessary??) uu, t = evolve(u) - print(f"final time is: {t:.3f}") + logger.info(f"final time is: {t:.3f}") - print("data saving...") cwd = hydra.utils.get_original_cwd() + "/" jnp.save( cwd diff --git a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py index 8b10ae2..f29e3b4 100644 --- a/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/ReactionDiffusionEq/reaction_diffusion_multi_solution_Hydra.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ @@ -144,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import random @@ -194,7 +194,6 @@ def main(cfg: DictConfig) -> None: else: rho = cfg.multi.rho nu = cfg.multi.nu - print(f"rho: {rho:>5f}, nu: {nu:>5f}") # t-coordinate it_tot = ceil((fin_time - ini_time) / dt_save) + 1 @@ -232,9 +231,7 @@ def _show(_carry): carry = t, tsave, steps, i_save, dt, u, uu t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) - uu = uu.at[-1].set(u) - - return uu + return uu.at[-1].set(u) @jax.jit def simulation_fn(i, carry): @@ -272,14 +269,12 @@ def flux(u): u, dx, Ncell=cfg.multi.nx ) # index 2 for _U is equivalent with index 0 for u # 2nd-order diffusion flux - f = -nu * (_u[2 : cfg.multi.nx + 3] - _u[1 : cfg.multi.nx + 2]) * dx_inv - return f + return -nu * (_u[2 : cfg.multi.nx + 3] - _u[1 : cfg.multi.nx + 2]) * dx_inv @jax.jit def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method # stiff equation - u = 1.0 / (1.0 + jnp.exp(-rho * dt) * (1.0 - u) / u) - return u + return 1.0 / (1.0 + jnp.exp(-rho * dt) * (1.0 - u) / u) u = init_multi( xc, @@ -296,7 +291,6 @@ def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method local_devices = jax.local_device_count() uu = vm_evolve(u.reshape([local_devices, cfg.multi.numbers // local_devices, -1])) - print("data saving...") cwd = hydra.utils.get_original_cwd() + "/" jnp.save( cwd + cfg.multi.save + "/ReacDiff_Nu" + str(nu)[:5] + "_Rho" + str(rho)[:5], uu @@ -307,7 +301,6 @@ def Piecewise_Exact_Solution(u, dt): # Piecewise_Exact_Solution method # reshape based on device count uu = uu.reshape((-1, *uu.shape[2:])) - print("data saving...") cwd = hydra.utils.get_original_cwd() + "/" Path(cwd + cfg.multi.save).mkdir(parents=True, exist_ok=True) jnp.save( diff --git a/pdebench/data_gen/data_gen_NLE/utils.py b/pdebench/data_gen/data_gen_NLE/utils.py index 4fb1e52..c9f63ce 100644 --- a/pdebench/data_gen/data_gen_NLE/utils.py +++ b/pdebench/data_gen/data_gen_NLE/utils.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python from __future__ import annotations import math as mt @@ -52,8 +51,7 @@ def _pass(carry): def select_A(carry): def _func(carry): - carry = jnp.abs(carry) - return carry + return jnp.abs(carry) cond, value = carry value = lax.cond(cond == 1, _func, _pass, value) @@ -79,7 +77,7 @@ def _norm(carry): return u cond, u = carry - u = lax.cond(cond == True, _norm, _pass, u) + u = lax.cond(cond is True, _norm, _pass, u) return cond, u key = random.PRNGKey(init_key) @@ -182,9 +180,7 @@ def __create_2DRand_init(u0, delu): u += uk * jnp.sin(kdx + phs) # renormalize total velocity - u = u0 + delu * u / jnp.abs(u).mean() - - return u + return u0 + delu * u / jnp.abs(u).mean() key = random.PRNGKey(init_key) u0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=duMx) @@ -208,9 +204,7 @@ def __create_2DRand_init(u0, delu): cond, mask, _xc, xL, xR, trns = vmap(select_W, 0, 0)(carry) u = u * mask - u = u + u0[:, :, None] * (1.0 - mask) - - return u + return u + u0[:, :, None] * (1.0 - mask) def init_HD( @@ -230,7 +224,6 @@ def init_HD( :param mode: initial condition :return: 1D scalar function u at cell center """ - print(mode) modes = [ "shocktube0", "shocktube1", @@ -647,8 +640,7 @@ def _pass(carry): def select_A(carry): def _func(carry): - carry = jnp.abs(carry) - return carry + return jnp.abs(carry) cond, value = carry value = lax.cond(cond == 1, _func, _pass, value) @@ -686,10 +678,10 @@ def _norm(carry): cond, u, key = carry carry = u, key - u, key = lax.cond(cond == True, _norm, _pass, carry) + u, key = lax.cond(cond is True, _norm, _pass, carry) return cond, u, key - assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" + assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" # noqa: PT018 assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" key = random.PRNGKey(init_key) @@ -743,7 +735,7 @@ def init_multi_HD_shock( :param mode: initial condition :return: 1D scalar function u at cell center """ - assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" + assert yc.shape[0] == 1 and zc.shape[0] == 1, "ny and nz is assumed to be 1!!" # noqa: PT018 assert numbers % jax.device_count() == 0, "numbers should be : GPUs x integer!!" def select_var(carry): @@ -830,8 +822,7 @@ def __create_KH_init(u, dk, kk): u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) u = u.loc[2].set(0.0) u = u.loc[3].add(0.0) - u = u.loc[4].add(p0) - return u + return u.loc[4].add(p0) # create random density ratio key = random.PRNGKey(init_key) @@ -839,10 +830,7 @@ def __create_KH_init(u, dk, kk): # create random wave-numbers key, subkey = random.split(key) kk = random.randint(key, shape=([numbers, 1]), minval=1, maxval=kmax) - print("vmap...") - u = jax.vmap(__create_KH_init, axis_name="i")(u, dk, kk) - - return u + return jax.vmap(__create_KH_init, axis_name="i")(u, dk, kk) # @partial(jit, static_argnums=(4, 5, 6, 7, 8)) @@ -931,8 +919,7 @@ def __create_2DTurb_init(u, keys): u = u.loc[0].set(d0) u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.loc[4].add(p0) - return u + return u.loc[4].add(p0) key = random.PRNGKey(init_key) keys = random.randint( @@ -943,9 +930,7 @@ def __create_2DTurb_init(u, keys): minval=0, maxval=10000000, ) - u = jax.vmap(__create_2DTurb_init, axis_name="i")(u, keys) - - return u + return jax.vmap(__create_2DTurb_init, axis_name="i")(u, keys) def init_multi_HD_2DRand( @@ -1042,8 +1027,7 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): u = u.loc[0, 2:-2, 2:-2, 2:-2].set(d) u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) - u = u.loc[4, 2:-2, 2:-2, 2:-2].set(p) - return u + return u.loc[4, 2:-2, 2:-2, 2:-2].set(p) key = random.PRNGKey(init_key) d0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=dMx) @@ -1088,12 +1072,10 @@ def __create_2DRand_init(u, d0, T0, delD, delP, keys): u = u.loc[:, 0, 2:-2, 2:-2, 2:-2].add( d0[:, :, None, None] * (1.0 - mask[:, :, :, None]) ) - u = u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( + return u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( d0[:, :, None, None] * T0[:, :, None, None] * (1.0 - mask[:, :, :, None]) ) - return u - def init_multi_HD_3DTurb( u, xc, yc, zc, numbers=100, init_key=2022, M0=0.1, k_tot=4.0, gamma=1.666666667 @@ -1203,8 +1185,7 @@ def __create_3DTurb_init(u, keys): u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) u = u.loc[3, 2:-2, 2:-2, 2:-2].set(vz) - u = u.loc[4].add(p0) - return u + return u.loc[4].add(p0) key = random.PRNGKey(init_key) keys = random.randint( @@ -1215,9 +1196,7 @@ def __create_3DTurb_init(u, keys): minval=0, maxval=10000000, ) - u = jax.vmap(__create_3DTurb_init, axis_name="i")(u, keys) - - return u + return jax.vmap(__create_3DTurb_init, axis_name="i")(u, keys) def init_multi_HD_3DRand( @@ -1329,8 +1308,7 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): u = u.loc[1, 2:-2, 2:-2, 2:-2].set(vx) u = u.loc[2, 2:-2, 2:-2, 2:-2].set(vy) u = u.loc[3, 2:-2, 2:-2, 2:-2].set(vz) - u = u.loc[4, 2:-2, 2:-2, 2:-2].set(p) - return u + return u.loc[4, 2:-2, 2:-2, 2:-2].set(p) key = random.PRNGKey(init_key) d0 = random.uniform(key, shape=([numbers, 1]), minval=1.0e-1, maxval=dMx) @@ -1379,12 +1357,10 @@ def __create_3DRand_init(u, d0, T0, delD, delP, keys): u = u.loc[:, 0, 2:-2, 2:-2, 2:-2].add( d0[:, :, None, None] * (1.0 - mask[:, :, :, :]) ) - u = u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( + return u.loc[:, 4, 2:-2, 2:-2, 2:-2].add( d0[:, :, None, None] * T0[:, :, None, None] * (1.0 - mask[:, :, :, :]) ) - return u - def bc(u, dx, Ncell, mode="periodic"): _u = jnp.zeros(Ncell + 4) # because of 2nd-order precision in space @@ -1602,13 +1578,11 @@ def save_data_HD(u, xc, yc, zc, i_save, save_dir, dt_save=None, if_final=False): def Courant(u, dx): - stability_adv = dx / (jnp.max(jnp.abs(u)) + 1.0e-8) - return stability_adv + return dx / (jnp.max(jnp.abs(u)) + 1.0e-8) def Courant_diff(dx, epsilon=1.0e-3): - stability_dif = 0.5 * dx**2 / (epsilon + 1.0e-8) - return stability_dif + return 0.5 * dx**2 / (epsilon + 1.0e-8) def Courant_diff_2D(dx, dy, epsilon=1.0e-3): @@ -1622,10 +1596,7 @@ def Courant_HD(u, dx, dy, dz, gamma): stability_adv_x = dx / (jnp.max(cs + jnp.abs(u[1])) + 1.0e-8) stability_adv_y = dy / (jnp.max(cs + jnp.abs(u[2])) + 1.0e-8) stability_adv_z = dz / (jnp.max(cs + jnp.abs(u[3])) + 1.0e-8) - stability_adv = jnp.min( - jnp.array([stability_adv_x, stability_adv_y, stability_adv_z]) - ) - return stability_adv + return jnp.min(jnp.array([stability_adv_x, stability_adv_y, stability_adv_z])) def Courant_vis_HD(dx, dy, dz, eta, zeta): @@ -1634,7 +1605,4 @@ def Courant_vis_HD(dx, dy, dz, eta, zeta): stability_dif_x = 0.5 * dx**2 / (visc + 1.0e-8) stability_dif_y = 0.5 * dy**2 / (visc + 1.0e-8) stability_dif_z = 0.5 * dz**2 / (visc + 1.0e-8) - stability_dif = jnp.min( - jnp.array([stability_dif_x, stability_dif_y, stability_dif_z]) - ) - return stability_dif + return jnp.min(jnp.array([stability_dif_x, stability_dif_y, stability_dif_z])) From d5638038d80bacde93c1f59965c89e3ba376aa02 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Thu, 19 Dec 2024 00:10:22 +0100 Subject: [PATCH 132/137] apply formatting --- pdebench/__init__.py | 1 + .../AdvectionEq/advection_exact_Hydra.py | 1 + .../advection_multi_solution_Hydra.py | 3 +- .../data_gen_NLE/BurgersEq/burgers_Hydra.py | 1 + .../BurgersEq/burgers_multi_solution_Hydra.py | 2 +- pdebench/data_gen/gen_radial_dam_break.py | 1 - pdebench/data_gen/notebooks/Analysis.ipynb | 35 +++++++++---------- pdebench/data_gen/plot.py | 2 +- pdebench/data_gen/src/plots.py | 3 +- pdebench/data_gen/src/sim_diff_react.py | 2 -- pdebench/data_gen/src/sim_ns_incomp_2d.py | 1 + pdebench/data_gen/src/sim_radial_dam_break.py | 1 + pdebench/data_gen/src/utils.py | 2 +- pdebench/data_gen/src/vorticity.py | 3 +- pdebench/data_gen/velocity2vorticity.py | 5 +-- pdebench/models/analyse_result_forward.py | 1 + pdebench/models/analyse_result_inverse.py | 1 + pdebench/models/fno/fno.py | 1 + pdebench/models/fno/train.py | 1 + pdebench/models/fno/utils.py | 1 + pdebench/models/inverse/inverse.py | 1 + pdebench/models/inverse/train.py | 1 + pdebench/models/inverse/utils.py | 2 ++ pdebench/models/metrics.py | 35 +++++++++++++------ pdebench/models/pinn/train.py | 1 + pdebench/models/pinn/utils.py | 1 + pdebench/models/train_models_forward.py | 1 + pdebench/models/train_models_inverse.py | 1 + pdebench/models/unet/unet.py | 1 + pdebench/models/unet/utils.py | 1 + 30 files changed, 73 insertions(+), 40 deletions(-) diff --git a/pdebench/__init__.py b/pdebench/__init__.py index f1c6f44..dad0638 100644 --- a/pdebench/__init__.py +++ b/pdebench/__init__.py @@ -19,6 +19,7 @@ """ + from __future__ import annotations import logging diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py index fe76029..cdd336b 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_exact_Hydra.py @@ -143,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging diff --git a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py index 8ec3e67..d736f43 100644 --- a/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/AdvectionEq/advection_multi_solution_Hydra.py @@ -143,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import random @@ -165,6 +166,7 @@ logger = logging.getLogger(__name__) + def _pass(carry): return carry @@ -233,7 +235,6 @@ def _show(_carry): t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) return uu.at[-1].set(u) - @jax.jit def simulation_fn(i, carry): u, t, dt, steps, tsave = carry diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py index 824de29..b3bd376 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_Hydra.py @@ -143,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging diff --git a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py index 6c01d53..8d84253 100644 --- a/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py +++ b/pdebench/data_gen/data_gen_NLE/BurgersEq/burgers_multi_solution_Hydra.py @@ -143,6 +143,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging @@ -233,7 +234,6 @@ def _show(_carry): t, tsave, steps, i_save, dt, u, uu = lax.while_loop(cond_fun, _body_fun, carry) return uu.at[-1].set(u) - @jax.jit def simulation_fn(i, carry): u, t, dt, steps, tsave = carry diff --git a/pdebench/data_gen/gen_radial_dam_break.py b/pdebench/data_gen/gen_radial_dam_break.py index c20fcbd..798f3ff 100644 --- a/pdebench/data_gen/gen_radial_dam_break.py +++ b/pdebench/data_gen/gen_radial_dam_break.py @@ -101,7 +101,6 @@ def main(config: DictConfig): # Change back to the hydra working directory os.chdir(temp_path) - work_path = Path(config.work_dir) output_path = work_path / config.data_dir / config.output_path if not output_path.is_dir(): diff --git a/pdebench/data_gen/notebooks/Analysis.ipynb b/pdebench/data_gen/notebooks/Analysis.ipynb index 501363f..1d1c2e0 100644 --- a/pdebench/data_gen/notebooks/Analysis.ipynb +++ b/pdebench/data_gen/notebooks/Analysis.ipynb @@ -76,8 +76,8 @@ } ], "source": [ - "data_path = resolve_path('${WORKING_DIR}/*/*/*/*/*/*.h5', idx=-1, unique=False)\n", - "print(data_path) # noqa: T201\n" + "data_path = resolve_path(\"${WORKING_DIR}/*/*/*/*/*/*.h5\", idx=-1, unique=False)\n", + "print(data_path) # noqa: T201" ] }, { @@ -97,7 +97,7 @@ } ], "source": [ - "data_f = h5py.File(data_path, 'r')\n", + "data_f = h5py.File(data_path, \"r\")\n", "data_f.keys()" ] }, @@ -118,7 +118,7 @@ } ], "source": [ - "data_f['particles'].shape" + "data_f[\"particles\"].shape" ] }, { @@ -140,16 +140,15 @@ } ], "source": [ - "\n", "columns = 5\n", "fsize = 12\n", "\n", - "arr = data_f['particles'][0, :columns, :, :, 0]\n", + "arr = data_f[\"particles\"][0, :columns, :, :, 0]\n", "\n", - "fig = plt.figure(figsize=(fsize, fsize/columns))\n", - "plt.imshow(rearrange(arr, 't x y -> x (t y)'))\n", + "fig = plt.figure(figsize=(fsize, fsize / columns))\n", + "plt.imshow(rearrange(arr, \"t x y -> x (t y)\"))\n", "plt.gca().set_axis_off()\n", - "plt.tight_layout(pad = 1)\n", + "plt.tight_layout(pad=1)\n", "plt.show()" ] }, @@ -172,16 +171,15 @@ } ], "source": [ - "\n", "columns = 5\n", "fsize = 12\n", "\n", - "arr = data_f['particles'][0, 1000:1000+columns, :, :, 0]\n", + "arr = data_f[\"particles\"][0, 1000 : 1000 + columns, :, :, 0]\n", "\n", - "fig = plt.figure(figsize=(fsize, fsize/columns))\n", - "plt.imshow(rearrange(arr, 't x y -> x (t y)'))\n", + "fig = plt.figure(figsize=(fsize, fsize / columns))\n", + "plt.imshow(rearrange(arr, \"t x y -> x (t y)\"))\n", "plt.gca().set_axis_off()\n", - "plt.tight_layout(pad = 1)\n", + "plt.tight_layout(pad=1)\n", "plt.show()" ] }, @@ -204,16 +202,15 @@ } ], "source": [ - "\n", "columns = 5\n", "fsize = 12\n", "\n", - "arr = data_f['force'][:columns, :, :, 0]\n", + "arr = data_f[\"force\"][:columns, :, :, 0]\n", "\n", - "fig = plt.figure(figsize=(fsize, fsize/columns))\n", - "plt.imshow(rearrange(arr, 'b x y -> x (b y)'))\n", + "fig = plt.figure(figsize=(fsize, fsize / columns))\n", + "plt.imshow(rearrange(arr, \"b x y -> x (b y)\"))\n", "plt.gca().set_axis_off()\n", - "plt.tight_layout(pad = 1)\n", + "plt.tight_layout(pad=1)\n", "plt.show()" ] }, diff --git a/pdebench/data_gen/plot.py b/pdebench/data_gen/plot.py index d431e71..99abcdc 100644 --- a/pdebench/data_gen/plot.py +++ b/pdebench/data_gen/plot.py @@ -3,6 +3,7 @@ @author: timot """ + from __future__ import annotations import os @@ -63,6 +64,5 @@ def main(config: DictConfig): ) - if __name__ == "__main__": main() diff --git a/pdebench/data_gen/src/plots.py b/pdebench/data_gen/src/plots.py index d74cc8d..ec94db2 100644 --- a/pdebench/data_gen/src/plots.py +++ b/pdebench/data_gen/src/plots.py @@ -2,6 +2,7 @@ Author : John Kim, Simon Brown, Timothy Praditia PDE Simulation packages """ + from __future__ import annotations import h5py @@ -18,7 +19,7 @@ def plot_data(data, t, dim, channel, t_fraction, config, filename): plt.figure() plt.title(f"$t={t[t_idx]}$") if dim == 1: - with h5py.File(config.data_path, 'r') as h5_file: + with h5py.File(config.data_path, "r") as h5_file: x = np.array(h5_file["grid"]["x"], dtype="f") plt.plot(x.squeeze(), data[t_idx, ..., channel]) plt.xlabel("$x$") diff --git a/pdebench/data_gen/src/sim_diff_react.py b/pdebench/data_gen/src/sim_diff_react.py index 939a04e..7c175e6 100644 --- a/pdebench/data_gen/src/sim_diff_react.py +++ b/pdebench/data_gen/src/sim_diff_react.py @@ -157,5 +157,3 @@ def rc_ode(self, t, y): # noqa: ARG002 # Stack the time derivative into a single array y_t return np.concatenate((u_t, v_t)) - - diff --git a/pdebench/data_gen/src/sim_ns_incomp_2d.py b/pdebench/data_gen/src/sim_ns_incomp_2d.py index e9c3596..092243f 100644 --- a/pdebench/data_gen/src/sim_ns_incomp_2d.py +++ b/pdebench/data_gen/src/sim_ns_incomp_2d.py @@ -2,6 +2,7 @@ Author : John Kim, Ran Zhang, Dan MacKinlay PDE Simulation packages """ + from __future__ import annotations import logging diff --git a/pdebench/data_gen/src/sim_radial_dam_break.py b/pdebench/data_gen/src/sim_radial_dam_break.py index dc2f173..34408e6 100644 --- a/pdebench/data_gen/src/sim_radial_dam_break.py +++ b/pdebench/data_gen/src/sim_radial_dam_break.py @@ -10,6 +10,7 @@ logger = logging.getLogger(__name__) + class Basic2DScenario(ABC): name = "" diff --git a/pdebench/data_gen/src/utils.py b/pdebench/data_gen/src/utils.py index a5d0a72..bcde22a 100644 --- a/pdebench/data_gen/src/utils.py +++ b/pdebench/data_gen/src/utils.py @@ -19,7 +19,7 @@ def matching_paths(glob_exp): return a list of paths matching a glob expression """ path = os.path.expandvars(Path(glob_exp).expanduser()) - return list(Path(path).glob('*')) + return list(Path(path).glob("*")) def resolve_path(path, idx=None, unique=True): diff --git a/pdebench/data_gen/src/vorticity.py b/pdebench/data_gen/src/vorticity.py index 385e5f3..26cac35 100644 --- a/pdebench/data_gen/src/vorticity.py +++ b/pdebench/data_gen/src/vorticity.py @@ -1,4 +1,4 @@ -r""" Generate vorticity field :math:`\boldsymbol{\omega} = \nabla \times \boldsymbol{v}` given +r"""Generate vorticity field :math:`\boldsymbol{\omega} = \nabla \times \boldsymbol{v}` given velocity field :math:`\boldsymbol{v}` using numerical approximation. Assuming the velocitiy field of shape [n, sx, sy, sz, 3] (5D) consists of a trajectory of equidistant cells, @@ -15,6 +15,7 @@ for approximating the vorticity field. """ + from __future__ import annotations import jax diff --git a/pdebench/data_gen/velocity2vorticity.py b/pdebench/data_gen/velocity2vorticity.py index 3081ced..807b3be 100644 --- a/pdebench/data_gen/velocity2vorticity.py +++ b/pdebench/data_gen/velocity2vorticity.py @@ -1,6 +1,7 @@ -""" Convert velocity- to vorticity field assuming 3D CFD exampe was downloaded. - The resulting .hdf5 file does not store pressure and density. +"""Convert velocity- to vorticity field assuming 3D CFD exampe was downloaded. +The resulting .hdf5 file does not store pressure and density. """ + from __future__ import annotations import argparse diff --git a/pdebench/models/analyse_result_forward.py b/pdebench/models/analyse_result_forward.py index 8191f3b..484635c 100644 --- a/pdebench/models/analyse_result_forward.py +++ b/pdebench/models/analyse_result_forward.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import _pickle as cPickle diff --git a/pdebench/models/analyse_result_inverse.py b/pdebench/models/analyse_result_inverse.py index bcb00f5..0a624c2 100644 --- a/pdebench/models/analyse_result_inverse.py +++ b/pdebench/models/analyse_result_inverse.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import matplotlib.pyplot as plt diff --git a/pdebench/models/fno/fno.py b/pdebench/models/fno/fno.py index 3e50e09..19e21bd 100644 --- a/pdebench/models/fno/fno.py +++ b/pdebench/models/fno/fno.py @@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from __future__ import annotations import torch diff --git a/pdebench/models/fno/train.py b/pdebench/models/fno/train.py index 209fa6e..0a51062 100644 --- a/pdebench/models/fno/train.py +++ b/pdebench/models/fno/train.py @@ -14,6 +14,7 @@ # np.random.seed(0) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + def run_training( if_training, continue_training, diff --git a/pdebench/models/fno/utils.py b/pdebench/models/fno/utils.py index fd66782..aae1232 100644 --- a/pdebench/models/fno/utils.py +++ b/pdebench/models/fno/utils.py @@ -146,6 +146,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import math as mt diff --git a/pdebench/models/inverse/inverse.py b/pdebench/models/inverse/inverse.py index 2e5982e..54f3e82 100644 --- a/pdebench/models/inverse/inverse.py +++ b/pdebench/models/inverse/inverse.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import pyro diff --git a/pdebench/models/inverse/train.py b/pdebench/models/inverse/train.py index e81faae..d0e5d92 100644 --- a/pdebench/models/inverse/train.py +++ b/pdebench/models/inverse/train.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging diff --git a/pdebench/models/inverse/utils.py b/pdebench/models/inverse/utils.py index d4a6e7d..9bcfda8 100644 --- a/pdebench/models/inverse/utils.py +++ b/pdebench/models/inverse/utils.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging @@ -157,6 +158,7 @@ logger = logging.getLogger(__name__) + def plot_ic_solution_mcmc( latent, x, diff --git a/pdebench/models/metrics.py b/pdebench/models/metrics.py index dc0ee5c..e33881e 100644 --- a/pdebench/models/metrics.py +++ b/pdebench/models/metrics.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging @@ -160,7 +161,9 @@ logger = logging.getLogger(__name__) -def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12, initial_step=1): +def metric_func( + pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHigh=12, initial_step=1 +): """ code for calculate metrics discussed in the Brain-storming session RMSE, normalized RMSE, max error, RMSE at the boundaries, conserved variables, RMSE in Fourier space, temporal sensitivity @@ -171,13 +174,13 @@ def metric_func(pred, target, if_mean=True, Lx=1.0, Ly=1.0, Lz=1.0, iLow=4, iHig pred = pred[..., initial_step:, :] target = target[..., initial_step:, :] idxs = target.size() - if len(idxs) == 4: # 1D + if len(idxs) == 4: # 1D pred = pred.permute(0, 3, 1, 2) target = target.permute(0, 3, 1, 2) - if len(idxs) == 5: # 2D + if len(idxs) == 5: # 2D pred = pred.permute(0, 4, 1, 2, 3) target = target.permute(0, 4, 1, 2, 3) - elif len(idxs) == 6: # 3D + elif len(idxs) == 6: # 3D pred = pred.permute(0, 5, 1, 2, 3, 4) target = target.permute(0, 5, 1, 2, 3, 4) idxs = target.size() @@ -338,7 +341,6 @@ def metrics( temp_shape.extend(list(range(1, len(inp.shape) - 1))) inp = inp.permute(temp_shape) - temp_shape = [0] temp_shape.extend(list(range(2, len(inp.shape)))) temp_shape.append(1) @@ -353,7 +355,15 @@ def metrics( _err_Max, _err_BD, _err_F, - ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz, initial_step=initial_step) + ) = metric_func( + pred, + yy, + if_mean=True, + Lx=Lx, + Ly=Ly, + Lz=Lz, + initial_step=initial_step, + ) if itot == 0: err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( @@ -382,7 +392,6 @@ def metrics( torch.mean((pred - yy) ** 2, dim=mean_dim) ) - elif mode == "FNO": with torch.no_grad(): itot = 0 @@ -409,7 +418,15 @@ def metrics( _err_Max, _err_BD, _err_F, - ) = metric_func(pred, yy, if_mean=True, Lx=Lx, Ly=Ly, Lz=Lz, initial_step=initial_step) + ) = metric_func( + pred, + yy, + if_mean=True, + Lx=Lx, + Ly=Ly, + Lz=Lz, + initial_step=initial_step, + ) if itot == 0: err_RMSE, err_nRMSE, err_CSV, err_Max, err_BD, err_F = ( _err_RMSE, @@ -437,7 +454,6 @@ def metrics( torch.mean((pred - yy) ** 2, dim=mean_dim) ) - elif mode == "PINN": raise NotImplementedError @@ -797,4 +813,3 @@ def inverse_metrics(u0, x, pred_u0, y): "fftl3loss_mid_pred_u0": fftl3loss_mid_pred_u0, "fftl3loss_hi_pred_u0": fftl3loss_hi_pred_u0, } - diff --git a/pdebench/models/pinn/train.py b/pdebench/models/pinn/train.py index dae24f8..098d701 100644 --- a/pdebench/models/pinn/train.py +++ b/pdebench/models/pinn/train.py @@ -1,4 +1,5 @@ """Backend supported: tensorflow.compat.v1, tensorflow, pytorch""" + from __future__ import annotations import pickle diff --git a/pdebench/models/pinn/utils.py b/pdebench/models/pinn/utils.py index 94bb4b8..91d39a7 100644 --- a/pdebench/models/pinn/utils.py +++ b/pdebench/models/pinn/utils.py @@ -3,6 +3,7 @@ @author: timot """ + from __future__ import annotations from pathlib import Path diff --git a/pdebench/models/train_models_forward.py b/pdebench/models/train_models_forward.py index 911b18c..001c79a 100644 --- a/pdebench/models/train_models_forward.py +++ b/pdebench/models/train_models_forward.py @@ -145,6 +145,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging diff --git a/pdebench/models/train_models_inverse.py b/pdebench/models/train_models_inverse.py index 116d3b4..dc7483d 100644 --- a/pdebench/models/train_models_inverse.py +++ b/pdebench/models/train_models_inverse.py @@ -144,6 +144,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import logging diff --git a/pdebench/models/unet/unet.py b/pdebench/models/unet/unet.py index 95dfb1a..38e6a7a 100644 --- a/pdebench/models/unet/unet.py +++ b/pdebench/models/unet/unet.py @@ -16,6 +16,7 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. """ + from __future__ import annotations from collections import OrderedDict diff --git a/pdebench/models/unet/utils.py b/pdebench/models/unet/utils.py index 13f4fa8..a272f19 100644 --- a/pdebench/models/unet/utils.py +++ b/pdebench/models/unet/utils.py @@ -146,6 +146,7 @@ THIS HEADER MAY NOT BE EXTRACTED OR MODIFIED IN ANY WAY. """ + from __future__ import annotations import math as mt From 29dfedf95a4be68710b0c3f6a293ba17e442f2d4 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Sat, 28 Dec 2024 13:16:22 +0100 Subject: [PATCH 133/137] remove unnecessary noqa --- pdebench/data_gen/src/data_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdebench/data_gen/src/data_io.py b/pdebench/data_gen/src/data_io.py index 8004258..479b767 100644 --- a/pdebench/data_gen/src/data_io.py +++ b/pdebench/data_gen/src/data_io.py @@ -85,7 +85,7 @@ def to_ndarray(field: Field) -> np.ndarray: """ centered = to_centre_grid(field) order = _get_dim_order(centered.shape) - return centered.values.numpy(order=order) + return centered.values.numpy(order=order) # noqa: PD011 def dataverse_upload( From d2ff5e5f55295ae1550ec696ff97055a5bcdca42 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Sat, 28 Dec 2024 14:04:59 +0100 Subject: [PATCH 134/137] add noxfile --- noxfile.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 noxfile.py diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..9536e44 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import shutil +from pathlib import Path + +import nox + +DIR = Path(__file__).parent.resolve() + +nox.needs_version = ">=2024.3.2" +nox.options.sessions = ["precommit", "pylint", "tests", "build"] +nox.options.default_venv_backend = "uv|mamba|virtualenv" + + +@nox.session(python=["3.10"]) +def precommit(session: nox.Session) -> None: + """ + Run the linter. + """ + session.install("pre-commit") + session.run( + "pre-commit", "run", "--all-files", "--show-diff-on-failure", *session.posargs + ) + + +@nox.session(python=["3.10"]) +def pylint(session: nox.Session) -> None: + """ + Run PyLint. + """ + # This needs to be installed into the package environment, and is slower + # than a pre-commit check + session.install(".", "pylint>=3.2") + session.run("pylint", "pdebench", *session.posargs) + + +@nox.session(python=["3.10"]) +def tests(session: nox.Session) -> None: + """ + Run the unit and regular tests. + """ + session.install(".[test]") + session.run("pytest", *session.posargs) + + +@nox.session(python=["3.10"]) +def build(session: nox.Session) -> None: + """ + Build an SDist and wheel. + """ + + build_path = DIR.joinpath("build") + if build_path.exists(): + shutil.rmtree(build_path) + + session.install("build") + session.run("python", "-m", "build") From 0cb1e077cb5061291c60764c7bda527697173516 Mon Sep 17 00:00:00 2001 From: leiterrl Date: Sat, 28 Dec 2024 14:08:36 +0100 Subject: [PATCH 135/137] disable mypy, fix more warnings --- .pre-commit-config.yaml | 20 ++++++++++---------- pdebench/__init__.py | 2 +- pdebench/data_gen/src/data_io.py | 2 +- pdebench/models/run_forward_1D.sh | 16 ++++++++-------- pyproject.toml | 5 +++-- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index defc249..5155e1c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,20 +40,20 @@ repos: args: [--prose-wrap=always] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.14" + rev: "v0.8.4" hooks: - id: ruff args: ["--fix", "--show-fixes"] - id: ruff-format - - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.8.0" - hooks: - - id: mypy - files: pdebench|tests - args: [] - additional_dependencies: - - pytest + # - repo: https://github.com/pre-commit/mirrors-mypy + # rev: "v1.8.0" + # hooks: + # - id: mypy + # files: pdebench|tests + # args: [] + # additional_dependencies: + # - pytest - repo: https://github.com/codespell-project/codespell rev: "v2.2.6" @@ -62,7 +62,7 @@ repos: exclude_types: [jupyter] - repo: https://github.com/shellcheck-py/shellcheck-py - rev: "v0.9.0.6" + rev: "v0.10.0.1" hooks: - id: shellcheck diff --git a/pdebench/__init__.py b/pdebench/__init__.py index dad0638..78651a5 100644 --- a/pdebench/__init__.py +++ b/pdebench/__init__.py @@ -29,4 +29,4 @@ __version__ = "0.0.1" __author__ = "Makoto Takamoto, Timothy Praditia, Raphael Leiteritz, Dan MacKinlay, Francesco Alesiani, Dirk Pflüger, Mathias Niepert" -__credits__ = "NEC labs Europe, University of Stuttgart, CSIRO" "s Data61" +__credits__ = "NEC labs Europe, University of Stuttgart, CSIRO's Data61" diff --git a/pdebench/data_gen/src/data_io.py b/pdebench/data_gen/src/data_io.py index 479b767..8004258 100644 --- a/pdebench/data_gen/src/data_io.py +++ b/pdebench/data_gen/src/data_io.py @@ -85,7 +85,7 @@ def to_ndarray(field: Field) -> np.ndarray: """ centered = to_centre_grid(field) order = _get_dim_order(centered.shape) - return centered.values.numpy(order=order) # noqa: PD011 + return centered.values.numpy(order=order) def dataverse_upload( diff --git a/pdebench/models/run_forward_1D.sh b/pdebench/models/run_forward_1D.sh index 82b2acd..c910093 100644 --- a/pdebench/models/run_forward_1D.sh +++ b/pdebench/models/run_forward_1D.sh @@ -62,12 +62,12 @@ CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta1.0.hdf5' ++args.aux_params=[1.] CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Advection_Sols_beta4.0.hdf5' ++args.aux_params=[4.] # Reaction Diffusion -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.aux_params=[0.5,1.] ++args.val_time=0.5 -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.aux_params=[0.5,10.] ++args.val_time=0.5 -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.aux_params=[2.,1.] ++args.val_time=0.5 -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.aux_params=[2.,10.] ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho1.0.hdf5' ++args.aux_params="[0.5,1.]" ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu0.5_Rho10.0.hdf5' ++args.aux_params="[0.5,10.]" ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho1.0.hdf5' ++args.aux_params="[2.,1.]" ++args.val_time=0.5 +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='ReacDiff_Nu2.0_Rho10.0.hdf5' ++args.aux_params="[2.,10.]" ++args.val_time=0.5 # Burgers Eq. -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.aux_params=[0.001] -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.aux_params=[0.01] -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.aux_params=[0.1] -CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.aux_params=[1.] +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.001.hdf5' ++args.aux_params="[0.001]" +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.01.hdf5' ++args.aux_params="[0.01]" +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu0.1.hdf5' ++args.aux_params="[0.1]" +CUDA_VISIBLE_DEVICES='0' python3 train_models_forward.py +args=config_pinn_pde1d.yaml ++args.filename='1D_Burgers_Sols_Nu1.0.hdf5' ++args.aux_params="[1.]" diff --git a/pyproject.toml b/pyproject.toml index dc19ee7..3b20d32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -172,11 +172,12 @@ ignore = [ "PLR2004", # Magic value used in comparison "ISC001", # Conflicts with formatter "UP007", - "ARG001", # too many false positives - "ARG005", + "ARG001", # too many false positives + "ARG005", "E731", # do not assign a lambda expression, use a def "G004", "PD008", # dataframe confused with jax array + "PD011", # dataframe confused with jax array ] isort.required-imports = ["from __future__ import annotations"] # Uncomment if using a _compat.typing backport From 0c76ad286215918df902da4a2c9b22b5635145e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 13:14:47 +0000 Subject: [PATCH 136/137] Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.12.3 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.9.0 to 1.12.3. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0...67339c736fd9354cd4f8cb0b744f2b82a74b5c70) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 10b7eff..554d880 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -32,7 +32,7 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 + uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} From 0cba3b8a13ffbe6be76e94946e3d9d575e6f6b01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 07:53:11 +0000 Subject: [PATCH 137/137] Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.12.3 to 1.12.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/67339c736fd9354cd4f8cb0b744f2b82a74b5c70...76f52bc884231f62b9a034ebfe128415bbaabdfc) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 554d880..c60b460 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -32,7 +32,7 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@67339c736fd9354cd4f8cb0b744f2b82a74b5c70 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }}