From 2d4f85ab7ea4ce75c0869a3bdf79c4a09bd33cbe Mon Sep 17 00:00:00 2001 From: hww <3188753874@qq.com> Date: Mon, 24 Nov 2025 17:37:21 +0800 Subject: [PATCH] finish the fno1d --- .../data_driven/burgers/fno1d/FNO1D.ipynb | 620 ++++++++++++++++++ .../data_driven/burgers/fno1d/FNO1D_CN.ipynb | 616 +++++++++++++++++ .../data_driven/burgers/fno1d/README.MD | 94 +++ .../data_driven/burgers/fno1d/README_CN.md | 93 +++ .../data_driven/burgers/fno1d/__init__.py | 20 + .../burgers/fno1d/configs/fno1d.yaml | 27 + .../burgers/fno1d/images/101-result.jpg | Bin 0 -> 32035 bytes .../burgers/fno1d/images/FNO-2.png | Bin 0 -> 19629 bytes .../data_driven/burgers/fno1d/images/FNO.png | Bin 0 -> 45253 bytes .../data_driven/burgers/fno1d/src/__init__.py | 22 + .../data_driven/burgers/fno1d/src/dataset.py | 94 +++ .../data_driven/burgers/fno1d/src/utils.py | 64 ++ .../data_driven/burgers/fno1d/train.py | 155 +++++ MindFlow/data/__init__.py | 30 + MindFlow/data/boundary.py | 174 +++++ MindFlow/data/data_base.py | 138 ++++ MindFlow/data/dataset.py | 340 ++++++++++ MindFlow/data/equation.py | 141 ++++ MindFlow/data/existed_data.py | 161 +++++ 19 files changed, 2789 insertions(+) create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/FNO1D.ipynb create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/FNO1D_CN.ipynb create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/README.MD create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/README_CN.md create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/__init__.py create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/images/101-result.jpg create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/images/FNO-2.png create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/images/FNO.png create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/src/__init__.py create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/src/dataset.py create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/src/utils.py create mode 100644 MindFlow/applications/data_driven/burgers/fno1d/train.py create mode 100644 MindFlow/data/__init__.py create mode 100644 MindFlow/data/boundary.py create mode 100644 MindFlow/data/data_base.py create mode 100644 MindFlow/data/dataset.py create mode 100644 MindFlow/data/equation.py create mode 100644 MindFlow/data/existed_data.py diff --git a/MindFlow/applications/data_driven/burgers/fno1d/FNO1D.ipynb b/MindFlow/applications/data_driven/burgers/fno1d/FNO1D.ipynb new file mode 100644 index 000000000..4e58c324d --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/FNO1D.ipynb @@ -0,0 +1,620 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# Solve Burgers' equation based on Fourier Neural Operator\n", + "\n", + "[![DownloadNotebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_notebook_en.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/mindflow/en/data_driven/mindspore_burgers_FNO1D.ipynb) [![DownloadCode](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_download_code_en.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/mindflow/en/data_driven/mindspore_burgers_FNO1D.py) [![ViewSource](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_source_en.png)](https://gitee.com/mindspore/docs/blob/master/docs/mindflow/docs/source_en/data_driven/burgers_FNO1D.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Overview\n", + "\n", + "Computational fluid dynamics is one of the most important techniques in the field of fluid mechanics in the 21st century. The flow analysis, prediction and control can be realized by solving the governing equations of fluid mechanics by numerical method. Traditional finite element method (FEM) and finite difference method (FDM) are inefficient because of the complex simulation process (physical modeling, meshing, numerical discretization, iterative solution, etc.) and high computing costs. Therefore, it is necessary to improve the efficiency of fluid simulation with AI.\n", + "\n", + "Machine learning methods provide a new paradigm for scientific computing by providing a fast solver similar to traditional methods. Classical neural networks learn mappings between finite dimensional spaces and can only learn solutions related to a specific discretization. Different from traditional neural networks, Fourier Neural Operator (FNO) is a new deep learning architecture that can learn mappings between infinite-dimensional function spaces. It directly learns mappings from arbitrary function parameters to solutions to solve a class of partial differential equations. Therefore, it has a stronger generalization capability. More information can be found in the paper, [Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/abs/2010.08895).\n", + "\n", + "This tutorial describes how to solve the 1-d Burgers' equation using Fourier neural operator." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Burgers' equation\n", + "\n", + "The 1-d Burgers’ equation is a non-linear PDE with various applications including modeling the one\n", + "dimensional flow of a viscous fluid. It takes the form\n", + "\n", + "$$\n", + "\\partial_t u(x, t)+\\partial_x (u^2(x, t)/2)=\\nu \\partial_{xx} u(x, t), \\quad x \\in(0,1), t \\in(0, 1]\n", + "$$\n", + "\n", + "$$\n", + "u(x, 0)=u_0(x), \\quad x \\in(0,1)\n", + "$$\n", + "\n", + "where $u$ is the velocity field, $u_0$ is the initial condition and $\\nu$ is the viscosity coefficient.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Problem Description\n", + "\n", + "We aim to learn the operator mapping the initial condition to the solution at time one:\n", + "\n", + "$$\n", + "u_0 \\mapsto u(\\cdot, 1)\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Technology Path\n", + "\n", + "MindFlow solves the problem as follows:\n", + "\n", + "1. Training Dataset Construction.\n", + "2. Model Construction.\n", + "3. Optimizer and Loss Function.\n", + "4. Model Training." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fourier Neural Operator\n", + "\n", + "The Fourier Neural Operator consists of the Lifting Layer, Fourier Layers, and the Decoding Layer.\n", + "\n", + "![Fourier Neural Operator model structure](images/FNO.png)\n", + "\n", + "Fourier layers: Start from input V. On top: apply the Fourier transform $\\mathcal{F}$; a linear transform R on the lower Fourier modes and filters out the higher modes; then apply the inverse Fourier transform $\\mathcal{F}^{-1}$. On the bottom: apply a local linear transform W. Finally, the Fourier Layer output vector is obtained through the activation function.\n", + "\n", + "![Fourier Layer structure](images/FNO-2.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "project_root = os.path.abspath(\"../../../../..\")\n", + "sys.path.append(project_root)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:549: UserWarning: The value of the smallest subnormal for type is zero.\n", + " setattr(self, word, getattr(machar, word).flat[0])\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", + " return self._float_to_str(self.smallest_subnormal)\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:549: UserWarning: The value of the smallest subnormal for type is zero.\n", + " setattr(self, word, getattr(machar, word).flat[0])\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", + " return self._float_to_str(self.smallest_subnormal)\n" + ] + } + ], + "source": [ + "import sys\n", + "sys.path.append(\"/home/mindspore/work/mindscience_newest\")\n", + "\n", + "import os\n", + "import time\n", + "import numpy as np\n", + "\n", + "from mindspore.amp import DynamicLossScaler, auto_mixed_precision, all_finite\n", + "from mindspore import context, nn, Tensor, set_seed, ops, data_sink, jit, save_checkpoint\n", + "from mindspore import dtype as mstype\n", + "from mindscience import FNO1D, RelativeRMSELoss, load_yaml_config, get_warmup_cosine_annealing_lr\n", + "from mindscience.pde import UnsteadyFlowWithLoss" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following `src` pacakage can be downloaded in [applications/data_driven/burgers/fno1d/src](https://gitee.com/mindspore/mindscience/tree/master/MindFlow/applications/data_driven/burgers/fno1d/src)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from src import create_training_dataset, visual\n", + "\n", + "set_seed(0)\n", + "np.random.seed(0)\n", + "\n", + "context.set_context(mode=context.GRAPH_MODE, device_target=\"Ascend\", device_id=0,max_device_memory=\"32GB\")\n", + "use_ascend = context.get_context(attr_key='device_target') == \"Ascend\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can get parameters of model, data and optimizer from `config`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "config = load_yaml_config('./configs/fno1d.yaml')\n", + "data_params = config[\"data\"]\n", + "model_params = config[\"model\"]\n", + "optimizer_params = config[\"optimizer\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Training Dataset Construction\n", + "\n", + "Download the training and test dataset: [data_driven/burgers/dataset](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/dataset/) .\n", + "\n", + "In this case, training datasets and test datasets are generated according to Zongyi Li's dataset in [Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/pdf/2010.08895.pdf) . The settings are as follows:\n", + "\n", + "the initial condition $u_0(x)$ is generated according to periodic boundary conditions:\n", + "\n", + "$$\n", + "u_0 \\sim \\mu, \\mu=\\mathcal{N}\\left(0,625(-\\Delta+25 I)^{-2}\\right)\n", + "$$\n", + "\n", + "We set the viscosity to $\\nu=0.1$ and solve the equation using a split step method where the heat equation part is solved exactly in Fourier space then the non-linear part is advanced, again in Fourier space, using a very fine forward Euler method. The number of samples in the training set is 1000, and the number of samples in the test set is 200.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data preparation finished\n", + "input_path: (1000, 1024, 1)\n", + "label_path: (1000, 1024)\n" + ] + } + ], + "source": [ + "# create training dataset\n", + "train_dataset = create_training_dataset(data_params, model_params, shuffle=True)\n", + "\n", + "# create test dataset\n", + "test_input, test_label = np.load(os.path.join(data_params[\"root_dir\"], \"test/inputs.npy\")), \\\n", + " np.load(os.path.join(data_params[\"root_dir\"], \"test/label.npy\"))\n", + "test_input = Tensor(np.expand_dims(test_input, -2), mstype.float32)\n", + "test_label = Tensor(np.expand_dims(test_label, -2), mstype.float32)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Model Construction\n", + "\n", + "The network is composed of 1 lifting layer, multiple Fourier layers and 1 decoding layer:\n", + "\n", + "- The Lifting layer corresponds to the `FNO1D.fc0` in the case, and maps the output data $x$ to the high dimension;\n", + "\n", + "- Multi-layer Fourier Layer corresponds to the `FNO1D.fno_seq` in the case. Discrete Fourier transform is used to realize the conversion between time domain and frequency domain;\n", + "\n", + "- The Decoding layer corresponds to `FNO1D.fc1` and `FNO1D.fc2` in the case to obtain the final predictive value.\n", + "\n", + "The initialization of the model based on the network above, parameters can be modified in [configuration file](https://gitee.com/mindspore/mindscience/blob/master/MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name:FNO1D_in_channels:1_out_channels:1_modes:16_resolutions:1024_hidden_channels:64_depths:4\n" + ] + } + ], + "source": [ + "model = FNO1D(in_channels=model_params[\"in_channels\"],\n", + " out_channels=model_params[\"out_channels\"],\n", + " n_modes=model_params[\"modes\"],\n", + " resolutions=model_params[\"resolutions\"],\n", + " hidden_channels=model_params[\"hidden_channels\"],\n", + " n_layers=model_params[\"depths\"],\n", + " projection_channels=4*model_params[\"hidden_channels\"],\n", + " )\n", + "\n", + "model_params_list = []\n", + "for k, v in model_params.items():\n", + " model_params_list.append(f\"{k}:{v}\")\n", + "model_name = \"_\".join(model_params_list)\n", + "print(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Optimizer and Loss Function" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "steps_per_epoch = train_dataset.get_dataset_size()\n", + "lr = get_warmup_cosine_annealing_lr(lr_init=optimizer_params[\"learning_rate\"],\n", + " last_epoch=optimizer_params[\"epochs\"],\n", + " steps_per_epoch=steps_per_epoch,\n", + " warmup_epochs=1)\n", + "optimizer = nn.Adam(model.trainable_params(), learning_rate=Tensor(lr))\n", + "\n", + "if use_ascend:\n", + " from mindspore.amp import DynamicLossScaler, auto_mixed_precision, all_finite\n", + " loss_scaler = DynamicLossScaler(1024, 2, 100)\n", + " auto_mixed_precision(model, 'O3')\n", + "else:\n", + " loss_scaler = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## Model Training\n", + "\n", + "With **MindSpore version >= 2.0.0**, we can use the functional programming for training neural networks. `MindFlow` provide a training interface for unsteady problems `UnsteadyFlowWithLoss` for model training and evaluation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "./summary/name:FNO1D_in_channels:1_out_channels:1_modes:16_resolutions:1024_hidden_channels:64_depths:4\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[ERROR] CORE(14182,ffffa35e7020,python):2025-11-21-12:48:27.409.781 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_14182/2488151560.py]\n", + "[ERROR] CORE(14182,ffffa35e7020,python):2025-11-21-12:48:27.412.318 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_14182/2488151560.py]\n", + "[ERROR] CORE(14182,ffffa35e7020,python):2025-11-21-12:48:27.412.373 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_14182/2488151560.py]\n", + "[ERROR] CORE(14182,ffffa35e7020,python):2025-11-21-12:48:27.412.551 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_14182/2488151560.py]\n", + "[ERROR] CORE(14182,ffffa35e7020,python):2025-11-21-12:48:27.412.600 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_14182/2488151560.py]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "..epoch: 1 train loss: 0.23552468 epoch time: 18.98s step time: 0.1518s\n", + "epoch: 2 train loss: 0.18982483 epoch time: 2.04s step time: 0.0163s\n", + "epoch: 3 train loss: 0.27205247 epoch time: 2.05s step time: 0.0164s\n", + "epoch: 4 train loss: 0.34487391 epoch time: 2.04s step time: 0.0163s\n", + "epoch: 5 train loss: 0.17643432 epoch time: 2.07s step time: 0.0166s\n", + "epoch: 6 train loss: 0.16570295 epoch time: 2.08s step time: 0.0166s\n", + "epoch: 7 train loss: 0.14640932 epoch time: 2.08s step time: 0.0167s\n", + "epoch: 8 train loss: 0.14050895 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 9 train loss: 0.19395757 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 10 train loss: 0.16622333 epoch time: 2.08s step time: 0.0166s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.019113144\n", + "=================================End Evaluation=================================\n", + "epoch: 11 train loss: 0.23629785 epoch time: 2.02s step time: 0.0161s\n", + "epoch: 12 train loss: 0.19908370 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 13 train loss: 0.12757140 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 14 train loss: 0.16622943 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 15 train loss: 0.11379100 epoch time: 2.08s step time: 0.0166s\n", + "epoch: 16 train loss: 0.17636013 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 17 train loss: 0.10928177 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 18 train loss: 0.20923793 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 19 train loss: 0.08066848 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 20 train loss: 0.09059334 epoch time: 2.25s step time: 0.0180s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.024397178\n", + "=================================End Evaluation=================================\n", + "epoch: 21 train loss: 0.13586798 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 22 train loss: 0.14321962 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 23 train loss: 0.21398574 epoch time: 2.30s step time: 0.0184s\n", + "epoch: 24 train loss: 0.08882140 epoch time: 2.28s step time: 0.0183s\n", + "epoch: 25 train loss: 0.06722423 epoch time: 2.27s step time: 0.0182s\n", + "epoch: 26 train loss: 0.10766524 epoch time: 2.28s step time: 0.0182s\n", + "epoch: 27 train loss: 0.06810039 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 28 train loss: 0.10507604 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 29 train loss: 0.16339272 epoch time: 2.19s step time: 0.0175s\n", + "epoch: 30 train loss: 0.08156310 epoch time: 2.10s step time: 0.0168s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0124527775\n", + "=================================End Evaluation=================================\n", + "epoch: 31 train loss: 0.08973831 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 32 train loss: 0.13080209 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 33 train loss: 0.14176241 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 34 train loss: 0.05777378 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 35 train loss: 0.12602332 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 36 train loss: 0.09481418 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 37 train loss: 0.14047202 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 38 train loss: 0.08627664 epoch time: 2.29s step time: 0.0183s\n", + "epoch: 39 train loss: 0.13403821 epoch time: 2.22s step time: 0.0178s\n", + "epoch: 40 train loss: 0.16013417 epoch time: 2.12s step time: 0.0169s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.013994617\n", + "=================================End Evaluation=================================\n", + "epoch: 41 train loss: 0.08353695 epoch time: 2.26s step time: 0.0181s\n", + "epoch: 42 train loss: 0.09881860 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 43 train loss: 0.06267320 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 44 train loss: 0.10684262 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 45 train loss: 0.05414104 epoch time: 2.28s step time: 0.0183s\n", + "epoch: 46 train loss: 0.05261404 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 47 train loss: 0.06279431 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 48 train loss: 0.10730355 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 49 train loss: 0.06588797 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 50 train loss: 0.09819019 epoch time: 2.26s step time: 0.0181s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.012260153\n", + "=================================End Evaluation=================================\n", + "epoch: 51 train loss: 0.06321388 epoch time: 2.27s step time: 0.0182s\n", + "epoch: 52 train loss: 0.06101272 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 53 train loss: 0.05362028 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 54 train loss: 0.05654792 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 55 train loss: 0.05520554 epoch time: 2.22s step time: 0.0177s\n", + "epoch: 56 train loss: 0.06365128 epoch time: 2.31s step time: 0.0185s\n", + "epoch: 57 train loss: 0.05951193 epoch time: 2.26s step time: 0.0181s\n", + "epoch: 58 train loss: 0.03393903 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 59 train loss: 0.05697101 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 60 train loss: 0.05424955 epoch time: 2.23s step time: 0.0178s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0060359137\n", + "=================================End Evaluation=================================\n", + "epoch: 61 train loss: 0.06071539 epoch time: 2.29s step time: 0.0183s\n", + "epoch: 62 train loss: 0.02931701 epoch time: 2.26s step time: 0.0181s\n", + "epoch: 63 train loss: 0.04120826 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 64 train loss: 0.03222458 epoch time: 2.33s step time: 0.0187s\n", + "epoch: 65 train loss: 0.05904165 epoch time: 2.26s step time: 0.0181s\n", + "epoch: 66 train loss: 0.04761104 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 67 train loss: 0.04821903 epoch time: 2.22s step time: 0.0178s\n", + "epoch: 68 train loss: 0.02590032 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 69 train loss: 0.04634136 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 70 train loss: 0.03768738 epoch time: 2.24s step time: 0.0180s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0039586676\n", + "=================================End Evaluation=================================\n", + "epoch: 71 train loss: 0.02253874 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 72 train loss: 0.02789907 epoch time: 2.27s step time: 0.0181s\n", + "epoch: 73 train loss: 0.02196864 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 74 train loss: 0.03262508 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 75 train loss: 0.02065851 epoch time: 2.25s step time: 0.0180s\n", + "epoch: 76 train loss: 0.02069751 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 77 train loss: 0.02388563 epoch time: 2.24s step time: 0.0179s\n", + "epoch: 78 train loss: 0.02619347 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 79 train loss: 0.01359341 epoch time: 2.26s step time: 0.0181s\n", + "epoch: 80 train loss: 0.01603020 epoch time: 2.23s step time: 0.0178s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0023475932\n", + "=================================End Evaluation=================================\n", + "epoch: 81 train loss: 0.01459295 epoch time: 2.20s step time: 0.0176s\n", + "epoch: 82 train loss: 0.00991342 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 83 train loss: 0.01342667 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 84 train loss: 0.00776235 epoch time: 2.22s step time: 0.0177s\n", + "epoch: 85 train loss: 0.00917393 epoch time: 2.27s step time: 0.0181s\n", + "epoch: 86 train loss: 0.01329082 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 87 train loss: 0.01279202 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 88 train loss: 0.00875893 epoch time: 2.22s step time: 0.0178s\n", + "epoch: 89 train loss: 0.01236088 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 90 train loss: 0.00878343 epoch time: 2.24s step time: 0.0179s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0013322836\n", + "=================================End Evaluation=================================\n", + "epoch: 91 train loss: 0.00726357 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 92 train loss: 0.00755119 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 93 train loss: 0.00635631 epoch time: 2.22s step time: 0.0178s\n", + "epoch: 94 train loss: 0.00615191 epoch time: 2.21s step time: 0.0177s\n", + "epoch: 95 train loss: 0.00564437 epoch time: 2.19s step time: 0.0175s\n", + "epoch: 96 train loss: 0.00481336 epoch time: 2.28s step time: 0.0182s\n", + "epoch: 97 train loss: 0.00603918 epoch time: 2.30s step time: 0.0184s\n", + "epoch: 98 train loss: 0.00529245 epoch time: 2.36s step time: 0.0189s\n", + "epoch: 99 train loss: 0.00602503 epoch time: 2.23s step time: 0.0178s\n", + "epoch: 100 train loss: 0.00625564 epoch time: 2.30s step time: 0.0184s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.001108932\n", + "=================================End Evaluation=================================\n" + ] + } + ], + "source": [ + "problem = UnsteadyFlowWithLoss(model, loss_fn=RelativeRMSELoss(), data_format=\"NHWTC\")\n", + "\n", + "summary_dir = os.path.join(config[\"summary\"][\"summary_dir\"], model_name)\n", + "print(summary_dir)\n", + "\n", + "def forward_fn(data, label):\n", + " loss = problem.get_loss(data, label)\n", + " if use_ascend:\n", + " loss = loss_scaler.scale(loss)\n", + " return loss\n", + "\n", + "grad_fn = ops.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=False)\n", + "\n", + "@jit\n", + "def train_step(data, label):\n", + " loss, grads = grad_fn(data, label)\n", + " if use_ascend:\n", + " loss = loss_scaler.unscale(loss)\n", + " if all_finite(grads):\n", + " grads = loss_scaler.unscale(grads)\n", + " loss = ops.depend(loss, optimizer(grads))\n", + " return loss\n", + "\n", + "sink_process = data_sink(train_step, train_dataset, 1)\n", + "ckpt_dir = os.path.join(summary_dir, \"ckpt\")\n", + "if not os.path.exists(ckpt_dir):\n", + " os.makedirs(ckpt_dir)\n", + "\n", + "for epoch in range(1, optimizer_params[\"epochs\"] + 1):\n", + " model.set_train()\n", + " local_time_beg = time.time()\n", + " for _ in range(steps_per_epoch):\n", + " cur_loss = sink_process()\n", + " print(\n", + " f\"epoch: {epoch} train loss: {cur_loss.asnumpy():.8f}\"\\\n", + " f\" epoch time: {time.time() - local_time_beg:.2f}s\"\\\n", + " f\" step time: {(time.time() - local_time_beg)/steps_per_epoch:.4f}s\")\n", + "\n", + " if epoch % config['summary']['test_interval'] == 0:\n", + " model.set_train(False)\n", + " print(\"================================Start Evaluation================================\")\n", + " rms_error = problem.get_loss(test_input, test_label)/test_input.shape[0]\n", + " print(f\"mean rms_error: {rms_error}\")\n", + " print(\"=================================End Evaluation=================================\")\n", + " save_checkpoint(model, os.path.join(ckpt_dir, model_params[\"name\"] + '_epoch' + str(epoch)))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "visual(model, epochs=optimizer_params[\"epochs\"], resolution=model_params[\"resolutions\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "jupyter", + "language": "python", + "name": "jupyter" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.23" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/MindFlow/applications/data_driven/burgers/fno1d/FNO1D_CN.ipynb b/MindFlow/applications/data_driven/burgers/fno1d/FNO1D_CN.ipynb new file mode 100644 index 000000000..1ea582480 --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/FNO1D_CN.ipynb @@ -0,0 +1,616 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# 基于Fourier Neural Operator的Burgers' equation求解\n", + "\n", + "[![下载Notebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_notebook.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/mindflow/zh_cn/data_driven/mindspore_burgers_FNO1D.ipynb) [![下载样例代码](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_download_code.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/mindflow/zh_cn/data_driven/mindspore_burgers_FNO1D.py) [![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/master/resource/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/master/docs/mindflow/docs/source_zh_cn/data_driven/burgers_FNO1D.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 概述\n", + "\n", + "计算流体力学是21世纪流体力学领域的重要技术之一,其通过使用数值方法在计算机中对流体力学的控制方程进行求解,从而实现流动的分析、预测和控制。传统的有限元法(finite element method,FEM)和有限差分法(finite difference method,FDM)常用于复杂的仿真流程(物理建模、网格划分、数值离散、迭代求解等)和较高的计算成本,往往效率低下。因此,借助AI提升流体仿真效率是十分必要的。\n", + "\n", + "近年来,随着神经网络的迅猛发展,为科学计算提供了新的范式。经典的神经网络是在有限维度的空间进行映射,只能学习与特定离散化相关的解。与经典神经网络不同,傅里叶神经算子(Fourier Neural Operator,FNO)是一种能够学习无限维函数空间映射的新型深度学习架构。该架构可直接学习从任意函数参数到解的映射,用于解决一类偏微分方程的求解问题,具有更强的泛化能力。更多信息可参考[Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/abs/2010.08895)。\n", + "\n", + "本案例教程介绍利用傅里叶神经算子的1-d Burgers方程求解方法。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 伯格斯方程(Burgers' equation)\n", + "\n", + "一维伯格斯方程(1-d Burgers' equation)是一个非线性偏微分方程,具有广泛应用,包括一维粘性流体流动建模。它的形式如下:\n", + "\n", + "$$\n", + "\\partial_t u(x, t)+\\partial_x (u^2(x, t)/2)=\\nu \\partial_{xx} u(x, t), \\quad x \\in(0,1), t \\in(0, 1]\n", + "$$\n", + "\n", + "$$\n", + "u(x, 0)=u_0(x), \\quad x \\in(0,1)\n", + "$$\n", + "\n", + "其中$u$表示速度场,$u_0$表示初始条件,$\\nu$表示粘度系数。\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 问题描述\n", + "\n", + "本案例利用Fourier Neural Operator学习初始状态到下一时刻状态的映射,实现一维Burgers'方程的求解:\n", + "\n", + "$$\n", + "u_0 \\mapsto u(\\cdot, 1)\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 技术路径\n", + "\n", + "MindFlow求解该问题的具体流程如下:\n", + "\n", + "1. 创建数据集。\n", + "2. 构建模型。\n", + "3. 优化器与损失函数。\n", + "4. 模型训练。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fourier Neural Operator\n", + "\n", + "Fourier Neural Operator模型构架如下图所示。图中$w_0(x)$表示初始涡度,通过Lifting Layer实现输入向量的高维映射,然后将映射结果作为Fourier Layer的输入,进行频域信息的非线性变换,最后由Decoding Layer将变换结果映射至最终的预测结果$w_1(x)$。\n", + "\n", + "Lifting Layer、Fourier Layer以及Decoding Layer共同组成了Fourier Neural Operator。\n", + "\n", + "![Fourier Neural Operator模型构架](images/FNO.png)\n", + "\n", + "Fourier Layer网络结构如下图所示。图中V表示输入向量,上框表示向量经过傅里叶变换后,经过线性变换R,过滤高频信息,然后进行傅里叶逆变换;另一分支经过线性变换W,最后通过激活函数,得到Fourier Layer输出向量。\n", + "\n", + "![Fourier Layer网络结构](images/FNO-2.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "project_root = os.path.abspath(\"../../../../..\")\n", + "sys.path.append(project_root)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:549: UserWarning: The value of the smallest subnormal for type is zero.\n", + " setattr(self, word, getattr(machar, word).flat[0])\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", + " return self._float_to_str(self.smallest_subnormal)\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:549: UserWarning: The value of the smallest subnormal for type is zero.\n", + " setattr(self, word, getattr(machar, word).flat[0])\n", + "/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", + " return self._float_to_str(self.smallest_subnormal)\n" + ] + } + ], + "source": [ + "import os\n", + "import time\n", + "import numpy as np\n", + "\n", + "from mindspore.amp import DynamicLossScaler, auto_mixed_precision, all_finite\n", + "from mindspore import context, nn, Tensor, set_seed, ops, data_sink, jit, save_checkpoint\n", + "from mindspore import dtype as mstype\n", + "from mindscience import FNO1D, RelativeRMSELoss, load_yaml_config, get_warmup_cosine_annealing_lr\n", + "from mindscience.pde import UnsteadyFlowWithLoss" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "下述`src`包可以在[applications/data_driven/burgers/fno1d/src](https://gitee.com/mindspore/mindscience/tree/master/MindFlow/applications/data_driven/burgers/fno1d/src)下载。" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "from src import create_training_dataset, visual\n", + "\n", + "set_seed(0)\n", + "np.random.seed(0)\n", + "\n", + "context.set_context(mode=context.GRAPH_MODE, device_target=\"Ascend\", device_id=0,max_device_memory=\"32GB\")\n", + "use_ascend = context.get_context(attr_key='device_target') == \"Ascend\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "从`config`中获得模型、数据、优化器的参数。" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "config = load_yaml_config('./configs/fno1d.yaml')\n", + "data_params = config[\"data\"]\n", + "model_params = config[\"model\"]\n", + "optimizer_params = config[\"optimizer\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 创建数据集\n", + "\n", + "下载训练与测试数据集: [data_driven/burgers/fno1d/dataset](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/dataset/)。\n", + "\n", + "本案例根据Zongyi Li在 [Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/pdf/2010.08895.pdf) 一文中对数据集的设置生成训练数据集与测试数据集。具体设置如下:\n", + "基于周期性边界,生成满足如下分布的初始条件$u_0(x)$:\n", + "\n", + "$$\n", + "u_0 \\sim \\mu, \\mu=\\mathcal{N}\\left(0,625(-\\Delta+25 I)^{-2}\\right)\n", + "$$\n", + "\n", + "本案例选取粘度系数$\\nu=0.1$,并使用分步法求解方程,其中热方程部分在傅里叶空间中精确求解,然后使用前向欧拉方法求解非线性部分。训练集样本量为1000个,测试集样本量为200个。\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data preparation finished\n", + "input_path: (1000, 1024, 1)\n", + "label_path: (1000, 1024)\n" + ] + } + ], + "source": [ + "# create training dataset\n", + "train_dataset = create_training_dataset(data_params, model_params, shuffle=True)\n", + "\n", + "# create test dataset\n", + "test_input, test_label = np.load(os.path.join(data_params[\"root_dir\"], \"test/inputs.npy\")), \\\n", + " np.load(os.path.join(data_params[\"root_dir\"], \"test/label.npy\"))\n", + "test_input = Tensor(np.expand_dims(test_input, -2), mstype.float32)\n", + "test_label = Tensor(np.expand_dims(test_label, -2), mstype.float32)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 构建模型\n", + "\n", + "网络由1层Lifting layer、1层Decoding layer以及多层Fourier Layer叠加组成:\n", + "\n", + "- Lifting layer对应样例代码中`FNO1D.fc0`,将输出数据$x$映射至高维;\n", + "\n", + "- 多层Fourier Layer的叠加对应样例代码中`FNO1D.fno_seq`,本案例采用离散傅里叶变换实现时域与频域的转换;\n", + "\n", + "- Decoding layer对应代码中`FNO1D.fc1`与`FNO1D.fc2`,获得最终的预测值。\n", + "\n", + "基于上述网络结构,进行模型初始化,其中模型参数可在[配置文件](https://gitee.com/mindspore/mindscience/blob/master/MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml)中修改。" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name:FNO1D_in_channels:1_out_channels:1_modes:16_resolutions:1024_hidden_channels:64_depths:4\n" + ] + } + ], + "source": [ + "model = FNO1D(in_channels=model_params[\"in_channels\"],\n", + " out_channels=model_params[\"out_channels\"],\n", + " n_modes=model_params[\"modes\"],\n", + " resolutions=model_params[\"resolutions\"],\n", + " hidden_channels=model_params[\"hidden_channels\"],\n", + " n_layers=model_params[\"depths\"],\n", + " projection_channels=4*model_params[\"hidden_channels\"],\n", + " )\n", + "model_params_list = []\n", + "for k, v in model_params.items():\n", + " model_params_list.append(f\"{k}:{v}\")\n", + "model_name = \"_\".join(model_params_list)\n", + "print(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 优化器与损失函数\n", + "\n", + "使用相对均方根误差作为网络训练损失函数:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "steps_per_epoch = train_dataset.get_dataset_size()\n", + "lr = get_warmup_cosine_annealing_lr(lr_init=optimizer_params[\"learning_rate\"],\n", + " last_epoch=optimizer_params[\"epochs\"],\n", + " steps_per_epoch=steps_per_epoch,\n", + " warmup_epochs=1)\n", + "optimizer = nn.Adam(model.trainable_params(), learning_rate=Tensor(lr))\n", + "\n", + "if use_ascend:\n", + " from mindspore.amp import DynamicLossScaler, auto_mixed_precision, all_finite\n", + " loss_scaler = DynamicLossScaler(1024, 2, 100)\n", + " auto_mixed_precision(model, 'O3')\n", + "else:\n", + " loss_scaler = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "## 模型训练\n", + "\n", + "使用 **MindSpore version >= 2.0.0**, 我们可以使用函数式编程来训练神经网络。 `MindFlow` 为非稳态问题 `UnsteadyFlowWithLoss` 提供了一个训练接口,用于模型训练和评估." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "./summary/name:FNO1D_in_channels:1_out_channels:1_modes:16_resolutions:1024_hidden_channels:64_depths:4\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[ERROR] CORE(11705,ffff9080b020,python):2025-11-21-12:39:59.699.170 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_11705/2488151560.py]\n", + "[ERROR] CORE(11705,ffff9080b020,python):2025-11-21-12:39:59.701.686 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_11705/2488151560.py]\n", + "[ERROR] CORE(11705,ffff9080b020,python):2025-11-21-12:39:59.701.740 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_11705/2488151560.py]\n", + "[ERROR] CORE(11705,ffff9080b020,python):2025-11-21-12:39:59.701.920 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_11705/2488151560.py]\n", + "[ERROR] CORE(11705,ffff9080b020,python):2025-11-21-12:39:59.701.969 [mindspore/core/utils/file_utils.cc:253] GetRealPath] Get realpath failed, path[/tmp/ipykernel_11705/2488151560.py]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "..epoch: 1 train loss: 0.23573753 epoch time: 19.21s step time: 0.1537s\n", + "epoch: 2 train loss: 0.21564198 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 3 train loss: 0.22462566 epoch time: 2.08s step time: 0.0166s\n", + "epoch: 4 train loss: 0.19544056 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 5 train loss: 0.17717326 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 6 train loss: 0.32128695 epoch time: 2.07s step time: 0.0166s\n", + "epoch: 7 train loss: 0.14024872 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 8 train loss: 0.26638049 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 9 train loss: 0.10469551 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 10 train loss: 0.13028814 epoch time: 2.08s step time: 0.0166s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.020006742\n", + "=================================End Evaluation=================================\n", + "epoch: 11 train loss: 0.14310433 epoch time: 2.02s step time: 0.0161s\n", + "epoch: 12 train loss: 0.08992550 epoch time: 2.05s step time: 0.0164s\n", + "epoch: 13 train loss: 0.14596464 epoch time: 2.08s step time: 0.0167s\n", + "epoch: 14 train loss: 0.19259310 epoch time: 2.08s step time: 0.0166s\n", + "epoch: 15 train loss: 0.11844812 epoch time: 2.15s step time: 0.0172s\n", + "epoch: 16 train loss: 0.23133799 epoch time: 2.17s step time: 0.0173s\n", + "epoch: 17 train loss: 0.10444780 epoch time: 2.18s step time: 0.0174s\n", + "epoch: 18 train loss: 0.18884143 epoch time: 2.20s step time: 0.0176s\n", + "epoch: 19 train loss: 0.09186225 epoch time: 2.19s step time: 0.0175s\n", + "epoch: 20 train loss: 0.09506319 epoch time: 2.16s step time: 0.0173s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0280857\n", + "=================================End Evaluation=================================\n", + "epoch: 21 train loss: 0.12294753 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 22 train loss: 0.16229430 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 23 train loss: 0.14522263 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 24 train loss: 0.13444284 epoch time: 2.12s step time: 0.0169s\n", + "epoch: 25 train loss: 0.14486945 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 26 train loss: 0.19183046 epoch time: 2.11s step time: 0.0168s\n", + "epoch: 27 train loss: 0.13015974 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 28 train loss: 0.13915072 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 29 train loss: 0.27786785 epoch time: 2.13s step time: 0.0171s\n", + "epoch: 30 train loss: 0.22170931 epoch time: 2.13s step time: 0.0170s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.012640592\n", + "=================================End Evaluation=================================\n", + "epoch: 31 train loss: 0.16966642 epoch time: 2.13s step time: 0.0171s\n", + "epoch: 32 train loss: 0.15281333 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 33 train loss: 0.09002480 epoch time: 2.23s step time: 0.0179s\n", + "epoch: 34 train loss: 0.06900214 epoch time: 2.19s step time: 0.0176s\n", + "epoch: 35 train loss: 0.14221093 epoch time: 2.20s step time: 0.0176s\n", + "epoch: 36 train loss: 0.08309147 epoch time: 2.31s step time: 0.0185s\n", + "epoch: 37 train loss: 0.10391256 epoch time: 2.12s step time: 0.0169s\n", + "epoch: 38 train loss: 0.06809868 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 39 train loss: 0.10030124 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 40 train loss: 0.07807523 epoch time: 2.13s step time: 0.0170s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.015541511\n", + "=================================End Evaluation=================================\n", + "epoch: 41 train loss: 0.09839623 epoch time: 2.18s step time: 0.0174s\n", + "epoch: 42 train loss: 0.12183327 epoch time: 2.15s step time: 0.0172s\n", + "epoch: 43 train loss: 0.07499917 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 44 train loss: 0.08465045 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 45 train loss: 0.10229427 epoch time: 2.18s step time: 0.0175s\n", + "epoch: 46 train loss: 0.05824205 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 47 train loss: 0.09787016 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 48 train loss: 0.04827353 epoch time: 2.05s step time: 0.0164s\n", + "epoch: 49 train loss: 0.08133921 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 50 train loss: 0.07289772 epoch time: 2.07s step time: 0.0166s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.008923213\n", + "=================================End Evaluation=================================\n", + "epoch: 51 train loss: 0.07429312 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 52 train loss: 0.04857171 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 53 train loss: 0.03577935 epoch time: 2.06s step time: 0.0165s\n", + "epoch: 54 train loss: 0.06593996 epoch time: 2.12s step time: 0.0169s\n", + "epoch: 55 train loss: 0.07332551 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 56 train loss: 0.07178899 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 57 train loss: 0.05264650 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 58 train loss: 0.03797883 epoch time: 2.13s step time: 0.0171s\n", + "epoch: 59 train loss: 0.05845477 epoch time: 2.16s step time: 0.0173s\n", + "epoch: 60 train loss: 0.06403975 epoch time: 2.18s step time: 0.0174s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.006600254\n", + "=================================End Evaluation=================================\n", + "epoch: 61 train loss: 0.04207995 epoch time: 2.20s step time: 0.0176s\n", + "epoch: 62 train loss: 0.02891058 epoch time: 2.17s step time: 0.0174s\n", + "epoch: 63 train loss: 0.03462276 epoch time: 2.22s step time: 0.0177s\n", + "epoch: 64 train loss: 0.05462021 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 65 train loss: 0.03391835 epoch time: 2.09s step time: 0.0168s\n", + "epoch: 66 train loss: 0.02857728 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 67 train loss: 0.03912133 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 68 train loss: 0.03685917 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 69 train loss: 0.04178163 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 70 train loss: 0.03735603 epoch time: 2.11s step time: 0.0168s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0058275633\n", + "=================================End Evaluation=================================\n", + "epoch: 71 train loss: 0.03040665 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 72 train loss: 0.01874469 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 73 train loss: 0.03120802 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 74 train loss: 0.03042979 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 75 train loss: 0.02004469 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 76 train loss: 0.03624482 epoch time: 2.19s step time: 0.0175s\n", + "epoch: 77 train loss: 0.01205721 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 78 train loss: 0.02248856 epoch time: 2.07s step time: 0.0166s\n", + "epoch: 79 train loss: 0.01789455 epoch time: 2.08s step time: 0.0166s\n", + "epoch: 80 train loss: 0.00859809 epoch time: 2.06s step time: 0.0165s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0018969539\n", + "=================================End Evaluation=================================\n", + "epoch: 81 train loss: 0.01580534 epoch time: 2.10s step time: 0.0168s\n", + "epoch: 82 train loss: 0.01038649 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 83 train loss: 0.00873489 epoch time: 2.18s step time: 0.0174s\n", + "epoch: 84 train loss: 0.01537373 epoch time: 2.19s step time: 0.0175s\n", + "epoch: 85 train loss: 0.00966384 epoch time: 2.14s step time: 0.0171s\n", + "epoch: 86 train loss: 0.01041574 epoch time: 2.17s step time: 0.0174s\n", + "epoch: 87 train loss: 0.01078541 epoch time: 2.18s step time: 0.0174s\n", + "epoch: 88 train loss: 0.00842985 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 89 train loss: 0.01249766 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 90 train loss: 0.00794562 epoch time: 2.15s step time: 0.0172s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.001191039\n", + "=================================End Evaluation=================================\n", + "epoch: 91 train loss: 0.00636647 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 92 train loss: 0.00761471 epoch time: 2.13s step time: 0.0170s\n", + "epoch: 93 train loss: 0.00627219 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 94 train loss: 0.00633444 epoch time: 2.09s step time: 0.0167s\n", + "epoch: 95 train loss: 0.00554313 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 96 train loss: 0.00512293 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 97 train loss: 0.00564081 epoch time: 2.12s step time: 0.0170s\n", + "epoch: 98 train loss: 0.00547583 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 99 train loss: 0.00596039 epoch time: 2.11s step time: 0.0169s\n", + "epoch: 100 train loss: 0.00612691 epoch time: 2.10s step time: 0.0168s\n", + "================================Start Evaluation================================\n", + "mean rms_error: 0.0010852672\n", + "=================================End Evaluation=================================\n" + ] + } + ], + "source": [ + "problem = UnsteadyFlowWithLoss(model, loss_fn=RelativeRMSELoss(), data_format=\"NHWTC\")\n", + "\n", + "summary_dir = os.path.join(config[\"summary\"][\"summary_dir\"], model_name)\n", + "print(summary_dir)\n", + "\n", + "def forward_fn(data, label):\n", + " loss = problem.get_loss(data, label)\n", + " if use_ascend:\n", + " loss = loss_scaler.scale(loss)\n", + " return loss\n", + "\n", + "grad_fn = ops.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=False)\n", + "\n", + "@jit\n", + "def train_step(data, label):\n", + " loss, grads = grad_fn(data, label)\n", + " if use_ascend:\n", + " loss = loss_scaler.unscale(loss)\n", + " if all_finite(grads):\n", + " grads = loss_scaler.unscale(grads)\n", + " loss = ops.depend(loss, optimizer(grads))\n", + " return loss\n", + "\n", + "sink_process = data_sink(train_step, train_dataset, 1)\n", + "ckpt_dir = os.path.join(summary_dir, \"ckpt\")\n", + "if not os.path.exists(ckpt_dir):\n", + " os.makedirs(ckpt_dir)\n", + "\n", + "for epoch in range(1, optimizer_params[\"epochs\"] + 1):\n", + " model.set_train()\n", + " local_time_beg = time.time()\n", + " for _ in range(steps_per_epoch):\n", + " cur_loss = sink_process()\n", + " print(\n", + " f\"epoch: {epoch} train loss: {cur_loss.asnumpy():.8f}\"\\\n", + " f\" epoch time: {time.time() - local_time_beg:.2f}s\"\\\n", + " f\" step time: {(time.time() - local_time_beg)/steps_per_epoch:.4f}s\")\n", + "\n", + " if epoch % config['summary']['test_interval'] == 0:\n", + " model.set_train(False)\n", + " print(\"================================Start Evaluation================================\")\n", + " rms_error = problem.get_loss(test_input, test_label)/test_input.shape[0]\n", + " print(f\"mean rms_error: {rms_error}\")\n", + " print(\"=================================End Evaluation=================================\")\n", + " save_checkpoint(model, os.path.join(ckpt_dir, model_params[\"name\"] + '_epoch' + str(epoch)))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "visual(model, epochs=optimizer_params[\"epochs\"], resolution=model_params[\"resolutions\"])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "jupyter", + "language": "python", + "name": "jupyter" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.23" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/MindFlow/applications/data_driven/burgers/fno1d/README.MD b/MindFlow/applications/data_driven/burgers/fno1d/README.MD new file mode 100644 index 000000000..22559a773 --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/README.MD @@ -0,0 +1,94 @@ +# FNO operator solves Burgers equation + +## Introduction + +### Background + +Computational fluid dynamics is one of the most important techniques in the field of fluid mechanics in the 21st century. The flow analysis, prediction and control can be realized by solving the governing equations of fluid mechanics by numerical method. Traditional finite element method (FEM) and finite difference method (FDM) are inefficient because of the complex simulation process (physical modeling, meshing, numerical discretization, iterative solution, etc.) and high computing costs. Therefore, it is necessary to improve the efficiency of fluid simulation with AI. + +Machine learning methods provide a new paradigm for scientific computing by providing a fast solver similar to traditional methods. Classical neural networks learn mappings between finite dimensional spaces and can only learn solutions related to a specific discretization. Different from traditional neural networks, Fourier Neural Operator (FNO) is a new deep learning architecture that can learn mappings between infinite-dimensional function spaces. It directly learns mappings from arbitrary function parameters to solutions to solve a class of partial differential equations. Therefore, it has a stronger generalization capability. More information can be found in the paper, [Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/abs/2010.08895). + +This tutorial describes how to solve the 1-d Burgers' equation using Fourier neural operator. + +### 问题描述 + +## Burgers' equation + +The 1-d Burgers’ equation is a non-linear PDE with various applications including modeling the one +dimensional flow of a viscous fluid. It takes the form + +$$ +\partial_t u(x, t)+\partial_x (u^2(x, t)/2)=\nu \partial_{xx} u(x, t), \quad x \in(0,1), t \in(0, 1] +$$ + +$$ +u(x, 0)=u_0(x), \quad x \in(0,1) +$$ + +where $u$ is the velocity field, $u_0$ is the initial condition and $\nu$ is the viscosity coefficient. + +We aim to learn the operator mapping the initial condition to the solution at time one: + +$$ +u_0 \mapsto u(\cdot, 1) +$$ + +### Technical path + +The Fourier Neural Operator consists of the Lifting Layer, Fourier Layers, and the Decoding Layer. + +![Fourier Neural Operator model structure](images/FNO.png) + +Fourier layers: Start from input V. On top: apply the Fourier transform $\mathcal{F}$; a linear transform R on the lower Fourier modes and filters out the higher modes; then apply the inverse Fourier transform $\mathcal{F}^{-1}$. On the bottom: apply a local linear transform W. Finally, the Fourier Layer output vector is obtained through the activation function. + +![Fourier Layer structure](images/FNO-2.png) + +## QuickStart + +You can download dataset from [data_driven/burgers/](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/). Save these dataset at `./dataset`. + +### Run Method 1: Call `train.py` from command line + +```shell +export PYTHONPATH=$(cd ../../../../../ && pwd):$PYTHONPATH +python train.py --config_file_path ./configs/fno1d.yaml --mode GRAPH --device_target Ascend --device_id 0 +``` + +where: + +`--config_file_path` indicates the path of the parameter file. Default './configs/fno1d.yaml'; + +`--device_target` indicates the computing platform. You can choose 'Ascend' or 'GPU'. Default 'Ascend'. + +`--device_id` indicates the index of NPU or GPU. Default 0. + +`--mode` is the running mode. 'GRAPH' indicates static graph mode. 'PYNATIVE' indicates dynamic graph mode. + +### Run Method 2: Run Jupyter Notebook + +You can run the training and validation code line by line using the Chinese or English version of the Jupyter Notebook [Chinese Version](./FNO1D_CN.ipynb) and [English Version](./FNO1D.ipynb). + +## Results Display + +![FNO1D Solves Burgers Equation](images/101-result.jpg) + +## Performance + +| Parameter | Ascend | +|:----------------------:|:--------------------------:| +| Hardware | Ascend, 32G | +| MindSpore版本 | 2.7.0 | +| Dataset | [1D Burgers Equation Resolution Dataset](https://download-mindspore.osinfra.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/) | +| Parameters | 5.57e5 | +| Train Config | resolution=1024, modes=16, hidden_channels=64, depth=4, batch_size=8, epoch=100 | +| Evaluation Config | batch_size=8 | +| Optimizer | Adam | +| Train Loss(MSE) | 0.004872 | +| Evaluation Error(RMSE) | 0.001088 | +| Speed(ms/step) | 15 | + +## Contributor + +gitee id:[liulei277](https://gitee.com/liulei277), [yezhenghao2023](https://gitee.com/yezhenghao2023), [huangwangwen2025](https://gitee.com/huangwangwen2025) + +email: liulei2770919@163.com, yezhenghao@isrc.iscas.ac.cn, wangwen@isrc.iscas.ac.cn diff --git a/MindFlow/applications/data_driven/burgers/fno1d/README_CN.md b/MindFlow/applications/data_driven/burgers/fno1d/README_CN.md new file mode 100644 index 000000000..e62603305 --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/README_CN.md @@ -0,0 +1,93 @@ +# FNO算子求解Burgers方程 + +## 概述 + +### 背景 + +计算流体力学是21世纪流体力学领域的重要技术之一,其通过使用数值方法在计算机中对流体力学的控制方程进行求解,从而实现流动的分析、预测和控制。传统的有限元法(finite element method,FEM)和有限差分法(finite difference method,FDM)常用于复杂的仿真流程(物理建模、网格划分、数值离散、迭代求解等)和较高的计算成本,往往效率低下。因此,借助AI提升流体仿真效率是十分必要的。 + +近年来,随着神经网络的迅猛发展,为科学计算提供了新的范式。经典的神经网络是在有限维度的空间进行映射,只能学习与特定离散化相关的解。与经典神经网络不同,傅里叶神经算子(Fourier Neural Operator,FNO)是一种能够学习无限维函数空间映射的新型深度学习架构。该架构可直接学习从任意函数参数到解的映射,用于解决一类偏微分方程的求解问题,具有更强的泛化能力。更多信息可参考[Fourier Neural Operator for Parametric Partial Differential Equations](https://arxiv.org/abs/2010.08895)。 + +本案例教程介绍利用傅里叶神经算子的1-d Burgers方程求解方法。 + +### 问题描述 + +一维伯格斯方程(1-d Burgers' equation)是一个非线性偏微分方程,具有广泛应用,包括一维粘性流体流动建模。它的形式如下: + +$$ +\partial_t u(x, t)+\partial_x (u^2(x, t)/2)=\nu \partial_{xx} u(x, t), \quad x \in(0,1), t \in(0, 1] +$$ + +$$ +u(x, 0)=u_0(x), \quad x \in(0,1) +$$ + +其中$u$表示速度场,$u_0$表示初始条件,$\nu$表示粘度系数。 + +本案例利用Fourier Neural Operator学习初始状态到下一时刻状态的映射,实现一维Burgers'方程的求解: + +$$ +u_0 \mapsto u(\cdot, 1) +$$ + +### 技术路径 + +Fourier Neural Operator模型构架如下图所示。图中$w_0(x)$表示初始涡度,通过Lifting Layer实现输入向量的高维映射,然后将映射结果作为Fourier Layer的输入,进行频域信息的非线性变换,最后由Decoding Layer将变换结果映射至最终的预测结果$w_1(x)$。 + +Lifting Layer、Fourier Layer以及Decoding Layer共同组成了Fourier Neural Operator。 + +![Fourier Neural Operator模型构架](images/FNO.png) + +Fourier Layer网络结构如下图所示。图中V表示输入向量,上框表示向量经过傅里叶变换后,经过线性变换R,过滤高频信息,然后进行傅里叶逆变换;另一分支经过线性变换W,最后通过激活函数,得到Fourier Layer输出向量。 + +![Fourier Layer网络结构](images/FNO-2.png) + +## 快速开始 + +数据集下载地址:[data_driven/burgers/fno1d/dataset](https://download.mindspore.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/dataset/). 将数据集保存在`./dataset`路径下. + +### 训练方式一:在命令行中调用`train.py`脚本 + +```shell +export PYTHONPATH=$(cd ../../../../../ && pwd):$PYTHONPATH +python train.py --config_file_path ./configs/fno1d.yaml --device_target Ascend --device_id 0 --mode GRAPH +``` + +其中, + +`--config_file_path`表示配置文件的路径,默认值'./configs/fno1d.yaml'; + +`--device_target`表示使用的计算平台类型,可以选择'Ascend'或'GPU',默认值'GPU'; + +`--device_id`表示使用的计算卡编号,可按照实际情况填写,默认值 0; + +`--mode`表示运行的模式,'GRAPH'表示静态图模式, 'PYNATIVE'表示动态图模式。 + +### 训练方式二:运行 Jupyter Notebook + +您可以使用[中文版](./FNO1D_CN.ipynb)和[英文版](./FNO1D.ipynb)Jupyter Notebook 逐行运行训练和验证代码。 + +## 结果展示 + +![FNO1D Solves Burgers](images/101-result.jpg) + +## 性能 + +| 参数 | Ascend | +|:----------------------:|:--------------------------:| +| 硬件资源 | Ascend, 显存32G | +| MindSpore版本 | 2.7.0 | | +| 数据集 | [一维Burgers方程分辨率数据集](https://download-mindspore.osinfra.cn/mindscience/mindflow/dataset/applications/data_driven/burgers/) | +| 参数量 | 5.57e5 | +| 训练参数 | resolution=1024, modes=16, hidden_channels=64, depth=4, batch_size=8, epoch=100 | +| 测试参数 | batch_size=8 | +| 优化器 | Adam | +| 训练损失(MSE) | 0.004872 | +| 验证损失(RMSE) | 0.001088 | +| 速度(ms/step) | 15 | + +## Contributor + +gitee id:[liulei277](https://gitee.com/liulei277), [yezhenghao2023](https://gitee.com/yezhenghao2023), [huangwangwen2025](https://gitee.com/huangwangwen2025) + +email: liulei2770919@163.com, yezhenghao@isrc.iscas.ac.cn, wangwen@isrc.iscas.ac.cn diff --git a/MindFlow/applications/data_driven/burgers/fno1d/__init__.py b/MindFlow/applications/data_driven/burgers/fno1d/__init__.py new file mode 100644 index 000000000..03585920b --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2023 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""init""" +from train import train + +__all__ = [ + "train", +] diff --git a/MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml b/MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml new file mode 100644 index 000000000..e3d149fe3 --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/configs/fno1d.yaml @@ -0,0 +1,27 @@ +model: + name: FNO1D + in_channels: 1 + out_channels: 1 + modes: 16 + resolutions: 1024 + hidden_channels: 64 + depths: 4 +data: + name: "burgers1d" + root_dir: "./dataset" + train: + num_samples: 1000 + test: + num_samples: 200 + batch_size: 8 + resolution: 1024 + t_in: 1 + t_out: 1 + step: 8 +optimizer: + learning_rate: 0.001 + epochs: 100 +summary: + test_interval: 10 + summary_dir: "./summary" + ckpt_dir: "./checkpoints" \ No newline at end of file diff --git a/MindFlow/applications/data_driven/burgers/fno1d/images/101-result.jpg b/MindFlow/applications/data_driven/burgers/fno1d/images/101-result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e36b0139dfa03ccf1706b9bccc11ba5a2293c28 GIT binary patch literal 32035 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 T{22T1*ZFZJ{vSQ!e;fN>9S;&+ literal 0 HcmV?d00001 diff --git a/MindFlow/applications/data_driven/burgers/fno1d/images/FNO-2.png b/MindFlow/applications/data_driven/burgers/fno1d/images/FNO-2.png new file mode 100644 index 0000000000000000000000000000000000000000..1556f366ae893a855b37ebe1666fc4fc0e59ef99 GIT binary patch literal 19629 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 A_5c6? literal 0 HcmV?d00001 diff --git a/MindFlow/applications/data_driven/burgers/fno1d/images/FNO.png b/MindFlow/applications/data_driven/burgers/fno1d/images/FNO.png new file mode 100644 index 0000000000000000000000000000000000000000..63c860f85f9a34644202175c2e4da55aaf96ba80 GIT binary patch literal 45253 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ൿIS*#e+DDqVy;`BxW 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 W!G+c#!;s6rEh)*V%hpMohx|VTCnO;N literal 0 HcmV?d00001 diff --git a/MindFlow/applications/data_driven/burgers/fno1d/src/__init__.py b/MindFlow/applications/data_driven/burgers/fno1d/src/__init__.py new file mode 100644 index 000000000..506da79d1 --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/src/__init__.py @@ -0,0 +1,22 @@ +# Copyright 2023 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""init""" +from .dataset import create_training_dataset +from .utils import visual + +__all__ = [ + "create_training_dataset", + "visual", +] diff --git a/MindFlow/applications/data_driven/burgers/fno1d/src/dataset.py b/MindFlow/applications/data_driven/burgers/fno1d/src/dataset.py new file mode 100644 index 000000000..e2932053c --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/src/dataset.py @@ -0,0 +1,94 @@ +# Copyright 2023 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""dataset""" +import os +import numpy as np +from MindFlow.data import Dataset, ExistedDataConfig +from mindscience.utils import print_log +EPS = 1e-8 + + +def create_npy(data_params, step=8): + '''create inputs and label data for trainset and testset''' + data_path = data_params["root_dir"] + train_size = data_params["train"]["num_samples"] + test_size = data_params["test"]["num_samples"] + s = 2 ** 13 // data_params["step"] + + train_path = os.path.join(data_path, "train") + test_path = os.path.join(data_path, "test") + + if not os.path.exists(train_path): + os.makedirs(train_path) + else: + print_log("Data preparation finished") + return + if not os.path.exists(test_path): + os.makedirs(test_path) + + inputs = np.load(os.path.join(train_path, "inputs.npy")) + label = np.load(os.path.join(train_path, "label.npy")) + + x_train = inputs[:train_size, :][:, ::step] + y_train = label[:train_size, :][:, ::step] + x_test = inputs[-test_size:, :][:, ::step] + y_test = label[-test_size:, :][:, ::step] + x_train = x_train.reshape(train_size, s, 1) + x_test = x_test.reshape(test_size, s, 1) + + train_input_path = os.path.join(train_path, "inputs.npy") + train_label_path = os.path.join(train_path, "label.npy") + test_input_path = os.path.join(test_path, "inputs.npy") + test_label_path = os.path.join(test_path, "label.npy") + + with os.fdopen(train_input_path, "wb") as f: + np.save(f, x_train) + with os.fdopen(train_label_path, "wb") as f: + np.save(f, y_train) + with os.fdopen(test_input_path, "wb") as f: + np.save(f, x_test) + with os.fdopen(test_label_path, "wb") as f: + np.save(f, y_test) + + +def create_training_dataset(data_params, model_params, shuffle=True, drop_remainder=True, is_train=True): + """create dataset""" + create_npy(data_params) + data_path = data_params["root_dir"] + if is_train: + train_path = os.path.abspath(os.path.join(data_path, "train")) + input_path = os.path.join(train_path, "inputs.npy") + print_log('input_path: ', np.load(input_path).shape) + label_path = os.path.join(train_path, "label.npy") + print_log('label_path: ', np.load(label_path).shape) + else: + test_path = os.path.abspath(os.path.join(data_path, "test")) + input_path = os.path.join(test_path, "inputs.npy") + label_path = os.path.join(test_path, "label.npy") + burgers_1d_data = ExistedDataConfig(name=data_params["name"], + data_dir=[input_path, label_path], + columns_list=["inputs", "label"], + data_format="npy") + dataset = Dataset(existed_data_list=[burgers_1d_data]) + data_loader = dataset.create_dataset(batch_size=data_params["batch_size"], + shuffle=shuffle, + drop_remainder=drop_remainder) + + operations = [lambda x, y: (x.reshape(-1, data_params["resolution"], + data_params["t_in"], model_params["out_channels"]), + y.reshape(-1, data_params["resolution"], + data_params["t_out"], model_params["out_channels"]))] + data_loader = data_loader.map(operations, input_columns=["burgers1d_inputs", "burgers1d_label"]) + return data_loader diff --git a/MindFlow/applications/data_driven/burgers/fno1d/src/utils.py b/MindFlow/applications/data_driven/burgers/fno1d/src/utils.py new file mode 100644 index 000000000..a6d28575f --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/src/utils.py @@ -0,0 +1,64 @@ +# Copyright 2023 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +""" +utils +""" +import numpy as np +from matplotlib.gridspec import GridSpec +import matplotlib.pyplot as plt +from mindspore import Tensor +from mindspore import dtype as mstype + +def visual(model, epochs=1, resolution=1024): + '''Use the trained model for inference and visualize the results.''' + x = np.linspace(-1, 1, resolution) + input_data = -np.sin(np.pi * x).reshape(1,-1,1).astype(np.float32) + input_tensor = Tensor(input_data, dtype=mstype.float32) + u_predict_list = [] + u_predict_list.append(input_tensor.asnumpy()) + for i in range(3): + u_predict = model(input_tensor) + input_tensor = u_predict + u_predict_list.append(u_predict.asnumpy()) + u_predict_list = np.concatenate(u_predict_list,axis=0).squeeze() + x_flat = np.linspace(-1, 1, resolution) + t_points = np.linspace(0, 3, 4) + gs = GridSpec(2, 3, width_ratios=[1, 1, 1]) + plt.subplot(gs[0, :]) + heatmap_data = np.array(u_predict_list).T + im = plt.imshow( + heatmap_data, + extent=[t_points.min(), t_points.max(), x_flat.min(), x_flat.max()], + aspect='auto', + cmap=plt.cm.rainbow, + origin='lower' + ) + plt.xlabel('t') + plt.ylabel('x') + cbar = plt.colorbar(im, pad=0.05, aspect=10) + cbar.set_label('u(t, x)') + cbar.mappable.set_clim(-1, 1) + + t_cross_sections = [1, 2, 3] + for i, t_cs in enumerate(t_cross_sections): + ax = plt.subplot(gs[1, i]) + ax.plot(x_flat, u_predict_list[t_cs]) + ax.set_title(f't={t_cs}') + ax.set_xlabel('x') + ax.set_ylabel('u(t, x)') + ax.set_xlim(-1, 1) + ax.set_ylim(-1, 1) + plt.tight_layout() + plt.savefig(f'images/{epochs + 1}-result.jpg') diff --git a/MindFlow/applications/data_driven/burgers/fno1d/train.py b/MindFlow/applications/data_driven/burgers/fno1d/train.py new file mode 100644 index 000000000..0ea954e9b --- /dev/null +++ b/MindFlow/applications/data_driven/burgers/fno1d/train.py @@ -0,0 +1,155 @@ +"""train""" +import os +import time +import argparse +import datetime +import numpy as np + +from mindspore import context, nn, Tensor, set_seed, ops, data_sink, jit, save_checkpoint +from mindspore import dtype as mstype +from mindspore.amp import DynamicLossScaler, auto_mixed_precision, all_finite +from src import create_training_dataset, visual +from mindscience import FNO1D, RelativeRMSELoss, load_yaml_config, get_warmup_cosine_annealing_lr +from mindscience.pde import UnsteadyFlowWithLoss +from mindscience.utils import log_config, print_log + + + +set_seed(0) +np.random.seed(0) + + +def parse_args(): + '''Parse input args''' + parser = argparse.ArgumentParser(description='Burgers 1D problem') + parser.add_argument("--mode", type=str, default="GRAPH", choices=["GRAPH", "PYNATIVE"], + help="Context mode, support 'GRAPH', 'PYNATIVE'") + parser.add_argument("--save_graphs", type=bool, default=False, choices=[True, False], + help="Whether to save intermediate compilation graphs") + parser.add_argument("--save_graphs_path", type=str, default="./graphs") + parser.add_argument("--device_target", type=str, default="GPU", choices=["GPU", "Ascend"], + help="The target device to run, support 'Ascend', 'GPU'") + parser.add_argument("--device_id", type=int, default=0, + help="ID of the target device") + parser.add_argument("--config_file_path", type=str, + default="./configs/fno1d.yaml") + input_args = parser.parse_args() + return input_args + + +def train(input_args): + '''Train and evaluate the network''' + use_ascend = context.get_context(attr_key='device_target') == "Ascend" + print_log(f"use_ascend: {use_ascend}") + + config = load_yaml_config(input_args.config_file_path) + data_params = config["data"] + model_params = config["model"] + optimizer_params = config["optimizer"] + + # create training dataset + train_dataset = create_training_dataset(data_params, model_params, shuffle=True) + + # create test dataset + test_input, test_label = np.load(os.path.join(data_params["root_dir"], "test/inputs.npy")), \ + np.load(os.path.join(data_params["root_dir"], "test/label.npy")) + test_input = Tensor(np.expand_dims(test_input, -2), mstype.float32) + test_label = Tensor(np.expand_dims(test_label, -2), mstype.float32) + + model = FNO1D(in_channels=model_params["in_channels"], + out_channels=model_params["out_channels"], + n_modes=model_params["modes"], + resolutions=model_params["resolutions"], + hidden_channels=model_params["hidden_channels"], + n_layers=model_params["depths"], + projection_channels=4*model_params["hidden_channels"], + ) + + steps_per_epoch = train_dataset.get_dataset_size() + lr = get_warmup_cosine_annealing_lr(lr_init=optimizer_params["learning_rate"], + last_epoch=optimizer_params["epochs"], + steps_per_epoch=steps_per_epoch, + warmup_epochs=1) + optimizer = nn.Adam(model.trainable_params(), learning_rate=Tensor(lr)) + + if use_ascend: + loss_scaler = DynamicLossScaler(1024, 2, 100) + auto_mixed_precision(model, 'O3') + else: + loss_scaler = None + + problem = UnsteadyFlowWithLoss( + model, loss_fn=RelativeRMSELoss(), data_format="NHWTC") + + summary_dir = config["summary"]["summary_dir"] + print_log(summary_dir) + + def forward_fn(data, label): + loss = problem.get_loss(data, label) + if use_ascend: + loss = loss_scaler.scale(loss) + return loss + + grad_fn = ops.value_and_grad( + forward_fn, None, optimizer.parameters, has_aux=False) + + @jit + def train_step(data, label): + loss, grads = grad_fn(data, label) + if use_ascend: + loss = loss_scaler.unscale(loss) + if all_finite(grads): + grads = loss_scaler.unscale(grads) + loss = ops.depend(loss, optimizer(grads)) + return loss + + sink_process = data_sink(train_step, train_dataset, 1) + ckpt_dir = "./checkpoints" + if not os.path.exists(ckpt_dir): + os.makedirs(ckpt_dir) + min_train_loss = float('inf') + min_eval_loss = float('inf') + min_step_time = float('inf') + for epoch in range(1, optimizer_params["epochs"] + 1): + model.set_train() + local_time_beg = time.time() + for _ in range(steps_per_epoch): + cur_loss = sink_process() + print_log( + f"epoch: {epoch} train loss: {cur_loss.asnumpy():.8f}"\ + f" epoch time: {time.time() - local_time_beg:.2f}s"\ + f" step time: {(time.time() - local_time_beg)/steps_per_epoch:.4f}s") + min_train_loss = min(min_train_loss, cur_loss) + min_step_time = min(min_step_time, (time.time() - local_time_beg) / steps_per_epoch) + if epoch % config['summary']['test_interval'] == 0: + eval_time_start = time.time() + model.set_train(False) + print_log( + "================================Start Evaluation================================") + rms_error = problem.get_loss( + test_input, test_label)/test_input.shape[0] + print_log(f"mean rms_error: {rms_error}") + min_eval_loss = min(min_eval_loss, rms_error) + print_log( + "=================================End Evaluation=================================") + print_log(f'evaluation time: {time.time() - eval_time_start}s') + save_checkpoint(model, os.path.join( + ckpt_dir, f"{model_params['name']}_epoch{epoch}")) + visual(model, epochs=optimizer_params["epochs"], resolution=model_params["resolutions"]) + print_log( + f"the minimum train mse is {min_train_loss}\n", + f"the minimum step time is {min_step_time}\n", + f"the minimum eval rmse is {min_eval_loss}\n", + ) +if __name__ == '__main__': + log_config('./logs', 'fno1d') + print_log(f"pid: {os.getpid()}") + print_log(datetime.datetime.now()) + args = parse_args() + context.set_context(mode=context.GRAPH_MODE if args.mode.upper().startswith("GRAPH") else context.PYNATIVE_MODE, + save_graphs=args.save_graphs, save_graphs_path=args.save_graphs_path, + device_target=args.device_target, device_id=args.device_id,max_device_memory="32GB") + set_max_mem = context.get_context(attr_key='max_device_memory') + print(f"The maximum amount of video memory set is:{set_max_mem } GB") + print_log(f"device_id: {context.get_context(attr_key='device_id')}") + train(args) diff --git a/MindFlow/data/__init__.py b/MindFlow/data/__init__.py new file mode 100644 index 000000000..dc6da2413 --- /dev/null +++ b/MindFlow/data/__init__.py @@ -0,0 +1,30 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""init""" +from .dataset import Dataset +#from .mind_dataset import MindDataset +from .boundary import BoundaryBC, BoundaryIC, Boundary +from .equation import Equation +from .existed_data import ExistedDataset +from .data_base import ExistedDataConfig, CONSTRAINT_TYPES + +__all__ = ["Dataset", + #"MindDataset", + "Equation", + "BoundaryBC", + "BoundaryIC", + "ExistedDataset", + "ExistedDataConfig", + "CONSTRAINT_TYPES"] diff --git a/MindFlow/data/boundary.py b/MindFlow/data/boundary.py new file mode 100644 index 000000000..4e57a3c53 --- /dev/null +++ b/MindFlow/data/boundary.py @@ -0,0 +1,174 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +#pylint: disable=W0223 +#pylint: disable=W0221 +""" +Sampling data of boundary condition and initial condition. +""" +from __future__ import absolute_import + +import numpy as np +from mindspore import log as logger + + +from mindscience.data.flow.geometry.geometry_base import Geometry, SamplingConfig +from mindscience.utils.check_func import check_param_type + +from .data_base import Data + + +class Boundary(Data): + """ + Base class of boundary condition and initial condition. + + Args: + geometry (Geometry): specifies geometry information of boundary condition and initial condition. + + Raises: + ValueError: if `sampling_config` of geometry is ``None``. + TypeError: if `geometry` is not an instance of Geometry. + """ + + def __init__(self, geometry): + check_param_type(geometry, "geometry", data_type=Geometry) + self.geometry = geometry + check_param_type(geometry.sampling_config, geometry.name, data_type=SamplingConfig) + + self.data = None + self.data_size = None + self.batch_size = 1 + self.shuffle = False + self.batched_data_size = None + self.columns_list = None + self._bound_index = None + self._bound_index_num = 0 + + super().__init__() + + def _get_sampling_data(self, geom_type="BC"): + """get sampling data""" + sample_data = self.geometry.sampling(geom_type=geom_type) + if not isinstance(sample_data, tuple): + sample_data = (sample_data,) + logger.info(f"Get {geom_type} sample_data size: {len(sample_data[0])}") + return sample_data, self.geometry.columns_dict[geom_type] + + def _initialize(self, batch_size=1, shuffle=False, geom_type="BC"): + """initialization: sampling and set attrs.""" + data, self.columns_list = self._get_sampling_data(geom_type=geom_type) + if not isinstance(data, tuple): + data = (data,) + self.data = data + self.data_size = len(self.data[0]) + self.batch_size = batch_size + + if batch_size > self.data_size: + raise ValueError( + f"If prebatch data, batch_size: {batch_size} " + f"should not be larger than data size: {self.data_size}." + ) + + self.batched_data_size = self.data_size // batch_size + self.shuffle = shuffle + self._bound_index = np.arange(self.data_size) + + logger.info( + f"Get {geom_type} dataset: {self.name}, columns: {self.columns_list}, " + f"size: {self.data_size}, batched_size: {self.batched_data_size}, shuffle: {self.shuffle}" + ) + return data + + def _get_index_when_sample_iter(self, index, geom_type="BC"): + if self._bound_index_num == self.batched_data_size: + self.data = self._initialize(self.batch_size, self.shuffle, geom_type=geom_type) + self._bound_index_num = 0 + index = self._bound_index_num + self._bound_index_num += 1 + return index + + def _get_index_when_sample_all(self, index): + data_size = len(self) + if (self._random_merge or self.shuffle) and index % data_size == 0: + self._bound_index = np.random.permutation(self.data_size) + index = index % data_size if index >= data_size else index + return index + + def __len__(self): + return self.batched_data_size + + def _get_item(self, index): + col_data = None + for i in range(len(self.columns_list)): + temp_data = ( + self.data[i][self._bound_index[index]] + if self.batch_size == 1 + else self.data[i][index * self.batch_size : (index + 1) * self.batch_size] + ) + col_data = (temp_data,) if col_data is None else col_data + (temp_data,) + return col_data + + +class BoundaryBC(Boundary): + """Sampling data of boundary condition.""" + + def __init__(self, geometry): + super().__init__(geometry) + self.sampling_config = self.geometry.sampling_config.bc + if not self.sampling_config: + raise ValueError( + f"BC info for the current geometry: {geometry.name} should not be None" + ) + self.name = geometry.name + "_BC" + self.constraint_type = "BC" + self._random_merge = self.sampling_config.random_merge + + def __getitem__(self, bc_index): + if not self.data: + self._initialization() + if self.sampling_config.random_sampling: + bc_index = self._get_index_when_sample_iter(bc_index, geom_type="BC") + else: + bc_index = self._get_index_when_sample_all(bc_index) + return self._get_item(bc_index) + + def _initialization(self, batch_size=1, shuffle=False): + return self._initialize(batch_size=batch_size, shuffle=shuffle, geom_type="BC") + + +class BoundaryIC(Boundary): + """Sampling data of initial condition.""" + + def __init__(self, geometry): + super().__init__(geometry) + self.sampling_config = self.geometry.sampling_config.ic + if not self.sampling_config: + raise ValueError( + f"IC info for the current geometry: {geometry.name} should not be None" + ) + self.name = geometry.name + "_IC" + self.constraint_type = "IC" + self._random_merge = self.sampling_config.random_merge + + def __getitem__(self, ic_index): + if not self.data: + self._initialization() + if self.sampling_config.random_sampling: + ic_index = self._get_index_when_sample_iter(ic_index, geom_type="IC") + else: + ic_index = self._get_index_when_sample_all(ic_index) + return self._get_item(ic_index) + + def _initialization(self, batch_size=1, shuffle=False): + return self._initialize(batch_size=batch_size, shuffle=shuffle, geom_type="IC") diff --git a/MindFlow/data/data_base.py b/MindFlow/data/data_base.py new file mode 100644 index 000000000..96c478513 --- /dev/null +++ b/MindFlow/data/data_base.py @@ -0,0 +1,138 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +This dataset module supports various type of datasets, including .... Some of the operations that are +provided to users to preprocess data include shuffle, batch, repeat, map, and zip. +""" +from __future__ import absolute_import + +import os +import abc + +from mindscience.utils.check_func import ( + check_param_type, + check_param_value, + check_param_type_value +) + +CONSTRAINT_TYPES = ["equation", "bc", "ic", "label", "function", "custom"] +DATA_FORMATS = ["npy"] + + +class Data: + """ + Base class of Dataset, Equation, Boundary and ExistedDataset. + + Args: + name (str): Dataset name. + columns_list (list/tuple): Column names. + constraint_type (str): Type of constraint. + + Supported Platforms: + ``Ascend`` + """ + + def __init__(self, name=None, columns_list=None, constraint_type=None): + none_type = type(None) + + check_param_type(name, "name", data_type=[str, none_type]) + check_param_type(columns_list, "columns_list", data_type=[list, tuple, none_type]) + check_param_type(constraint_type, "constraint_type", data_type=[str, none_type]) + + if constraint_type: + check_param_value(constraint_type.lower(), "constraint_type", CONSTRAINT_TYPES) + + self.name = name + self.columns_list = columns_list + self.constraint_type = constraint_type + self.dataset_type = type(self).__name__ + + def set_constraint_type(self, constraint_type="Equation"): + """Set dataset constraint type.""" + check_param_type(constraint_type, "constraint_type", data_type=str) + check_param_value(constraint_type.lower(), "constraint_type", CONSTRAINT_TYPES) + self.constraint_type = constraint_type + + @abc.abstractmethod + def create_dataset(self): + """Return a dataset (abstract).""" + raise NotImplementedError(f"{self.dataset_type}.create_dataset not implemented") + + @abc.abstractmethod + def _initialization(self): + """Initialize dataset (abstract).""" + raise NotImplementedError(f"{self.dataset_type}._initialization not implemented") + + @abc.abstractmethod + def __getitem__(self, index): + """Return item by index (abstract).""" + raise NotImplementedError(f"{self.dataset_type}.__getitem__ not implemented") + + @abc.abstractmethod + def __len__(self): + """Return dataset length (abstract).""" + raise NotImplementedError(f"{self.dataset_type}.__len__ not implemented") + + +class ExistedDataConfig: + """ + Configuration of ExistedDataset. + + Args: + name (str): Dataset name. + data_dir (str/list): Path(s) to existing data files. + columns_list (str/list): Column names. + data_format (str): File format (supports 'npy'). + constraint_type (str): Constraint type. + random_merge (bool): Whether to randomly merge datasets. + """ + + def __init__( + self, + name, + data_dir, + columns_list, + data_format="npy", + constraint_type="Label", + random_merge=True + ): + check_param_type(name, "name", data_type=str) + self.name = name + + if isinstance(data_dir, str): + data_dir = [data_dir] + + check_param_type(data_dir, "data_dir", data_type=[str, list, tuple]) + for path in data_dir: + if not os.path.exists(path): + raise ValueError(f"ExistedDataset file: {path} does not exist") + + self.data_dir = data_dir + + if isinstance(columns_list, str): + columns_list = [columns_list] + + check_param_type(columns_list, "columns_list", data_type=[str, tuple, list]) + self.columns_list = columns_list + + check_param_type(constraint_type, "constraint_type", data_type=str) + check_param_value(constraint_type.lower(), "constraint_type", CONSTRAINT_TYPES) + self.constraint_type = constraint_type + + check_param_type_value(data_format, "data_format", DATA_FORMATS, data_type=str) + self.data_format = data_format + + check_param_type(random_merge, "random_merge", data_type=bool) + self.random_merge = random_merge diff --git a/MindFlow/data/dataset.py b/MindFlow/data/dataset.py new file mode 100644 index 000000000..1f5ce9a5f --- /dev/null +++ b/MindFlow/data/dataset.py @@ -0,0 +1,340 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +#pylint: disable=W0223 +#pylint: disable=W0221 +#pylint: disable=W0212 +""" +Combine pde/ic/bc datasets together +""" +from __future__ import absolute_import + +import copy + +import mindspore.dataset as ds +from mindspore import log as logger + + +from mindscience.data.flow.geometry import Geometry +from mindscience.utils.check_func import check_param_type, check_dict_type_value + +from .data_base import Data, ExistedDataConfig +from .existed_data import ExistedDataset +from .equation import Equation +from .boundary import BoundaryIC, BoundaryBC + +_geomdata_dict = { + "domain": Equation, + "IC": BoundaryIC, + "BC": BoundaryBC, +} + + +class Dataset(Data): + r""" + Combine datasets together. + """ + + def __init__(self, geometry_dict=None, existed_data_list=None, dataset_list=None): + super().__init__() + if all((geometry_dict is None, existed_data_list is None, dataset_list is None)): + raise ValueError("Dataset should have at least one sub-dataset, but got None") + + if geometry_dict is not None: + check_param_type(geometry_dict, "geometry_dict", data_type=dict) + check_dict_type_value( + geometry_dict, + "geometry_dict", + key_type=Geometry, + value_type=str, + value_value=list(_geomdata_dict.keys()) + ) + + if existed_data_list is not None: + if isinstance(existed_data_list, ExistedDataConfig): + existed_data_list = [existed_data_list] + check_param_type(existed_data_list, "existed_data_list", (list, tuple)) + + for data_config in existed_data_list: + check_param_type(data_config, "element in existed_data_list", ExistedDataConfig) + + if dataset_list is not None: + if isinstance(dataset_list, Data): + dataset_list = [dataset_list] + check_param_type(dataset_list, "dataset_list", (list, tuple)) + for dataset in dataset_list: + check_param_type(dataset, "element in dataset_list", Data) + + self.existed_data_list = existed_data_list + self.geometry_dict = geometry_dict + self.dataset_list = dataset_list + self.all_datasets = dataset_list if dataset_list else [] + self.columns_list = None + self._iterable_datasets = None + + self.num_dataset = len(dataset_list) if dataset_list else 0 + if existed_data_list: + self.num_dataset += len(existed_data_list) + if geometry_dict: + for geom in geometry_dict: + self.num_dataset += len(geometry_dict[geom]) + + logger.info(f"Total datasets number: {self.num_dataset}") + self.dataset_columns_map = {} + self.column_index_map = {} + self.dataset_constraint_map = {} + + def _create_dataset_from_geometry(self, geometry, geom_type="domain"): + """create dataset from geometry.""" + dataset_instance = _geomdata_dict.get(geom_type)(geometry) + return dataset_instance + + def _get_all_datasets(self): + """get all datasets""" + if self.geometry_dict: + for geom, types in self.geometry_dict.items(): + for geom_type in types: + dataset = self._create_dataset_from_geometry(geom, geom_type) + self.all_datasets.append(dataset) + + if self.existed_data_list: + for data_config in self.existed_data_list: + dataset = ExistedDataset(data_config=data_config) + self.all_datasets.append(dataset) + + def create_dataset(self, + batch_size=1, + preprocess_fn=None, + input_output_columns_map=None, + shuffle=True, + drop_remainder=True, + prebatched_data=False, + num_parallel_workers=1, + num_shards=None, + shard_id=None, + python_multiprocessing=False, + sampler=None): + """ + create the final mindspore type dataset to merge all the sub-datasets. + """ + self._get_all_datasets() + check_param_type(prebatched_data, "prebatched_data", data_type=bool) + check_param_type(drop_remainder, "drop_remainder", data_type=bool) + check_param_type(shuffle, "shuffle", data_type=bool) + check_param_type(batch_size, "batch_size", data_type=int, exclude_type=bool) + + if prebatched_data and not drop_remainder: + raise ValueError( + "prebatched_data is not supported when drop_remainder is set to be False" + ) + + for dataset in self.all_datasets: + prebatch_size = batch_size if prebatched_data else 1 + prebatch_shuffle = shuffle if prebatched_data else False + dataset._initialization(batch_size=prebatch_size, shuffle=prebatch_shuffle) + self.columns_list = ( + dataset.columns_list if not self.columns_list + else self.columns_list + dataset.columns_list + ) + logger.info( + f"Check initial all dataset, dataset: {dataset.name}, " + f"columns_list: {dataset.columns_list}, data_size: {len(dataset)}" + ) + + dataset = self._merge_all_datasets( + shuffle=False if prebatched_data else shuffle, + num_parallel_workers=num_parallel_workers, + num_shards=num_shards, + shard_id=shard_id, + python_multiprocessing=python_multiprocessing + ) + logger.info(f"Initial dataset size: {dataset.get_dataset_size()}") + logger.info(f"Get all dataset columns names: {self.columns_list}") + + self.dataset_columns_map, self.dataset_constraint_map, self.column_index_map = \ + self._create_trace_maps() + logger.info(f"Dataset columns map: {self.dataset_columns_map}") + logger.info(f"Dataset column index map: {self.column_index_map}") + logger.info(f"Dataset constraints map: {self.dataset_constraint_map}") + + if sampler: + logger.info("Dataset uses sampler") + dataset.use_sampler(sampler) + + if preprocess_fn: + input_columns = copy.deepcopy(self.columns_list) + check_param_type( + input_output_columns_map, + "input_output_columns_map", + (type(None), dict) + ) + if input_output_columns_map: + new_columns_list, new_dataset_columns_map = self._update_columns_list( + input_output_columns_map + ) + self.columns_list = new_columns_list + self.dataset_columns_map = new_dataset_columns_map + self.column_index_map = {} + for idx, name in enumerate(self.columns_list): + self.column_index_map[name] = idx + logger.info( + f"Dataset columns map after preprocess: {self.dataset_columns_map}" + ) + logger.info( + f"Dataset column index after preprocess: {self.column_index_map}" + ) + logger.info( + f"Dataset constraints after preprocess: {self.dataset_constraint_map}" + ) + output_columns = self.columns_list + + dataset = dataset.map( + operations=preprocess_fn, + input_columns=input_columns, + output_columns=output_columns, + num_parallel_workers=num_parallel_workers, + python_multiprocessing=python_multiprocessing + ) + dataset = dataset.project(output_columns) + logger.info( + f"Get all dataset columns names after preprocess: {self.columns_list}" + ) + + if not prebatched_data: + dataset = dataset.batch( + batch_size=batch_size, + drop_remainder=drop_remainder, + num_parallel_workers=num_parallel_workers + ) + logger.info(f"Final dataset size: {dataset.get_dataset_size()}") + return dataset + + def _merge_all_datasets(self, shuffle=True, num_parallel_workers=1, num_shards=1, + shard_id=0, python_multiprocessing=False): + """merge all datasets""" + self._iterable_datasets = _IterableDatasets(self.all_datasets) + dataset = ds.GeneratorDataset( + source=self._iterable_datasets, + column_names=self.columns_list, + shuffle=shuffle, + num_parallel_workers=num_parallel_workers, + num_shards=num_shards, + shard_id=shard_id, + python_multiprocessing=python_multiprocessing + ) + return dataset + + def _update_columns_list(self, input_output_columns_map): + """update columns list""" + new_dataset_columns_map = {} + for dataset in self.all_datasets: + columns_list = dataset.columns_list + new_dataset_columns_map[dataset.name] = [] + for column in columns_list: + if column in input_output_columns_map.keys(): + new_column = input_output_columns_map[column] + if isinstance(new_column, list): + new_dataset_columns_map[dataset.name] += new_column + else: + new_dataset_columns_map.get(dataset.name).append(new_column) + else: + new_dataset_columns_map.get(dataset.name).append(column) + + new_columns_list = [] + for _, columns in new_dataset_columns_map.items(): + new_columns_list += columns + return new_columns_list, new_dataset_columns_map + + def get_columns_list(self): + """ + get columns list + + Returns: + list[str]. column names list of the final unified dataset. + """ + if not self.columns_list: + raise ValueError( + "Please call create_dataset() first before get final columns list to avoid unexpected error" + ) + return self.columns_list + + def _create_trace_maps(self): + """create trace maps""" + dataset_columns_map = {} + dataset_constraint_map = {} + column_index_map = {} + for dataset in self.all_datasets: + name = dataset.name + dataset_columns_map[name] = dataset.columns_list + dataset_constraint_map[name] = dataset.constraint_type + + for i, column in enumerate(self.columns_list): + column_index_map[column] = i + return dataset_columns_map, dataset_constraint_map, column_index_map + + def __getitem__(self, index): + if not self._iterable_datasets: + raise ValueError( + "Call create_dataset() before getting item by index to avoid unexpected error" + ) + return self._iterable_datasets[index] + + def set_constraint_type(self, constraint_type="Equation"): + """set constraint type of dataset""" + if isinstance(constraint_type, str): + logger.warning( + f"Argument constraint_type: {constraint_type} is str, " + "the same type will be set for all of the sub-datasets" + ) + for datasets in self.all_datasets: + datasets.set_constraint_type(constraint_type) + elif isinstance(constraint_type, dict): + for dataset in constraint_type.keys(): + if dataset not in self.all_datasets: + raise ValueError( + f"Unknown dataset: {dataset}. All sub-dataset are: " + f"{[data.name for data in self.all_datasets]}" + ) + dataset.set_constraint_type(constraint_type[dataset]) + else: + raise TypeError( + f"the type of constraint_type should be dict or str but got {type(constraint_type)}" + ) + + def __len__(self): + if not self._iterable_datasets: + raise ValueError( + "Call create_dataset() before getting item by index to avoid unexpected error" + ) + return len(self._iterable_datasets) + + +class _IterableDatasets: + """get data iteratively""" + + def __init__(self, dataset_list): + self.dataset_list = dataset_list + dataset_size = [len(dataset) for dataset in dataset_list] + logger.info(f"Get all dataset sizes: {dataset_size}") + self.longest = max(dataset_size) + + def __getitem__(self, index): + col_data = None + for dataset_instance in self.dataset_list: + item = dataset_instance[index] + col_data = col_data + item if col_data else item + return col_data + + def __len__(self): + return self.longest diff --git a/MindFlow/data/equation.py b/MindFlow/data/equation.py new file mode 100644 index 000000000..89550855c --- /dev/null +++ b/MindFlow/data/equation.py @@ -0,0 +1,141 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +#pylint: disable=W0223 +#pylint: disable=W0221 +""" +Sampling data of equation domain. +""" +from __future__ import absolute_import + +import numpy as np +from mindspore import log as logger + +from mindscience.data.flow.geometry import Geometry, SamplingConfig +from mindscience.utils.check_func import check_param_type +from .data_base import Data + + +_SPACE = "" + + +class Equation(Data): + """ + Sampling data of equation domain. + """ + + def __init__(self, geometry): + check_param_type(geometry, "geometry", data_type=Geometry) + check_param_type( + geometry.sampling_config, + f"sampling_config of geometry:{geometry.name}", + data_type=SamplingConfig, + ) + + self.sampling_config = geometry.sampling_config.domain + if not self.sampling_config: + raise ValueError(f"domain info of geometry: {geometry.name} should not be None") + + self.geometry = geometry + + self.data = None + self.data_size = None + self.batch_size = 1 + self.shuffle = False + self.batched_data_size = None + self.columns_list = None + + self._domain_index = None + self._domain_index_num = 0 + self._random_merge = self.sampling_config.random_merge + + name = f"{geometry.name}_domain" + columns_list = [f"{geometry.name}_domain_points"] + constraint_type = "Equation" + + super().__init__(name=name, columns_list=columns_list, constraint_type=constraint_type) + + def _get_sampling_data(self): + """Sampling domain points.""" + sample_data = self.geometry.sampling(geom_type="domain") + return sample_data, self.geometry.columns_dict["domain"] + + def _initialization(self, batch_size=1, shuffle=False): + """initialization: sampling and set attrs.""" + data, self.columns_list = self._get_sampling_data() + if not isinstance(data, tuple): + data = (data,) + + self.data = data + self.data_size = len(self.data[0]) + self.batch_size = batch_size + + if batch_size > self.data_size: + raise ValueError( + f"If prebatch data, batch_size: {batch_size} should not be larger than " + f"data size: {self.data_size}." + ) + + self.batched_data_size = self.data_size // batch_size + self.shuffle = shuffle + self._domain_index = np.arange(self.data_size) + + logger.info( + f"Get domain dataset: {self.name}, columns: {self.columns_list}, " + f"size: {self.data_size}, batched_size: {self.batched_data_size}, shuffle: {self.shuffle}" + ) + + def _get_index_when_sample_iter(self, index): + """Index logic for random_sampling=True.""" + if self._domain_index_num == self.batched_data_size: + self._initialization(self.batch_size, self.shuffle) + self._domain_index_num = 0 + index = self._domain_index_num + self._domain_index_num += 1 + return index + + def _get_index_when_sample_all(self, index): + """Index logic for random_sampling=False.""" + data_size = len(self) + if (self._random_merge or self.shuffle) and index % data_size == 0: + self._domain_index = np.random.permutation(self.data_size) + return index % data_size + + def __getitem__(self, index): + if self.data is None: + self._initialization() + + if self.sampling_config.random_sampling: + index = self._get_index_when_sample_iter(index) + else: + index = self._get_index_when_sample_all(index) + + col_data = None + for i in range(len(self.columns_list)): + if self.batch_size == 1: + idx = self._domain_index[index] + else: + idx = self._domain_index[ + index * self.batch_size : (index + 1) * self.batch_size + ] + + temp_data = self.data[i][idx] + col_data = (temp_data,) if col_data is None else col_data + (temp_data,) + + return col_data + + def __len__(self): + if self.data is None: + self._initialization() + return self.batched_data_size diff --git a/MindFlow/data/existed_data.py b/MindFlow/data/existed_data.py new file mode 100644 index 000000000..3f628c209 --- /dev/null +++ b/MindFlow/data/existed_data.py @@ -0,0 +1,161 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +#pylint: disable=W0223 +#pylint: disable=W0221 +""" +This dataset module supports npy type of datasets. Some of the operations that are +provided to users to preprocess data include shuffle, batch, repeat, map, and zip. +""" +from __future__ import absolute_import + +import numpy as np +from mindspore import log as logger + +from mindscience.utils.check_func import check_param_type +from .data_base import Data, ExistedDataConfig + + + +class ExistedDataset(Data): + """ + Load existing dataset (currently supports npy format only). + """ + + def __init__( + self, + name=None, + data_dir=None, + columns_list=None, + data_format="npy", + constraint_type="Label", + random_merge=True, + data_config=None + ): + if data_config is None: + if not name or not data_dir or not columns_list: + raise ValueError( + f"If data_config is None, name/data_dir/columns_list must not be None, " + f"but got name={name}, data_dir={data_dir}, columns_list={columns_list}" + ) + data_config = ExistedDataConfig( + name=name, + data_dir=data_dir, + columns_list=columns_list, + data_format=data_format, + constraint_type=constraint_type, + random_merge=random_merge + ) + + check_param_type(data_config, "data_config", data_type=ExistedDataConfig) + + name = data_config.name + columns_list = [f"{name}_{col}" for col in data_config.columns_list] + constraint_type = data_config.constraint_type + + self.data_dir = data_config.data_dir + self._data_format = data_config.data_format + self._random_merge = data_config.random_merge + + self.data = None + self.data_size = None + self.batch_size = 1 + self.shuffle = False + self.batched_data_size = None + self._index = None + + self.load_data = { + "npy": self._load_npy_data + } + + super().__init__(name=name, columns_list=columns_list, constraint_type=constraint_type) + + def _initialization(self, batch_size=1, shuffle=False): + """Load data once before training starts.""" + loader = self.load_data.get(self._data_format.lower()) + if loader is None: + raise ValueError(f"Unsupported data format: {self._data_format}") + + data = loader() + if not isinstance(data, tuple): + data = (data,) + + self.data = data + self.data_size = len(data[0]) + self.batch_size = batch_size + + if batch_size > self.data_size: + raise ValueError( + f"If prebatch data, batch_size={batch_size} cannot exceed data_size={self.data_size}" + ) + + self.batched_data_size = self.data_size // batch_size + self.shuffle = shuffle + self._index = np.arange(self.data_size) + + logger.info( + f"Loaded existed dataset: {self.name}, columns={self.columns_list}, " + f"size={self.data_size}, batched_size={self.batched_data_size}, shuffle={self.shuffle}" + ) + + def __getitem__(self, index): + if self.data is None: + self._initialization() + + if self._random_merge: + index = ( + np.random.randint(0, self.batched_data_size) + if index >= self.batched_data_size + else index + ) + else: + index = index % self.batched_data_size + + if self.shuffle and index % self.batched_data_size == 0: + self._index = np.random.permutation(self.data_size) + + col_data = None + for i in range(len(self.columns_list)): + + if self.batch_size == 1: + idx = self._index[index] + else: + idx = self._index[ + index * self.batch_size : (index + 1) * self.batch_size + ] + + temp = self.data[i][idx] + col_data = (temp,) if col_data is None else col_data + (temp,) + + return col_data + + def _load_npy_data(self): + """Load data from npy files.""" + results = [] + for path in self.data_dir: + logger.info(f"Loading npy data from: {path}") + arr = np.load(path).astype(np.float32) + + if arr.ndim < 1: + raise ValueError(f"Loaded npy file must have at least 1 dimension: {path}") + + results.append(arr) + + logger.info(f"Loaded npy dataset size: {len(results[0])}") + return tuple(results) + + def __len__(self): + if self.data is None: + self._initialization() + return self.batched_data_size -- Gitee