├── src ├── ex16 ├── ex9 ├── sindy_utils.py ├── error_utils.py ├── ex16.cpp ├── autoencoder.py └── ex9.cpp ├── examples ├── MFEMex16 │ ├── batch_job.bsub │ ├── ref-square.mesh │ ├── README.md │ └── train_model.ipynb ├── MFEMex9 │ ├── batch_job.bsub │ ├── periodic-square.mesh │ ├── README.md │ └── train_model.ipynb ├── 1DBurgerEqn │ ├── batch_job.bsub │ ├── README.md │ ├── plot_heatmap.ipynb │ ├── generate_data.ipynb │ └── train_model.ipynb └── 2DBurgerEqn │ ├── batch_job.bsub │ ├── README.md │ ├── train_model.ipynb │ └── generate_data.ipynb ├── LICENSE ├── setup.sh └── README.md /src/ex16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLNL/gLaSDI/HEAD/src/ex16 -------------------------------------------------------------------------------- /src/ex9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLNL/gLaSDI/HEAD/src/ex9 -------------------------------------------------------------------------------- /examples/MFEMex16/batch_job.bsub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #BSUB -nnodes 1 3 | #BSUB -J ex16 4 | #BSUB -W 12:00 5 | #BSUB -q pbatch 6 | #BSUB -o job_%J.out 7 | #BSUB -e job_%J.err 8 | # -wa 'KILL 9 | 10 | eval "$(conda shell.bash hook)" 11 | conda activate tfvenv 12 | jupyter nbconvert --to script train_model.ipynb 13 | sed -i '/ipython/d' ./*.py 14 | sed -i '/plt.show()/d' ./*.py 15 | jsrun -r 1 python -u train_model.py -------------------------------------------------------------------------------- /examples/MFEMex9/batch_job.bsub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #BSUB -nnodes 1 3 | #BSUB -J ex9 4 | #BSUB -W 12:00 5 | #BSUB -q pbatch 6 | #BSUB -o job_%J.out 7 | #BSUB -e job_%J.err 8 | # -wa 'KILL 9 | 10 | eval "$(conda shell.bash hook)" 11 | conda activate tfvenv 12 | jupyter nbconvert --to script train_model.ipynb 13 | sed -i '/ipython/d' ./*.py 14 | sed -i '/plt.show()/d' ./*.py 15 | jsrun -r 1 python -u train_model.py -------------------------------------------------------------------------------- /examples/1DBurgerEqn/batch_job.bsub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #BSUB -nnodes 1 3 | #BSUB -J 1dBg 4 | #BSUB -W 12:00 5 | #BSUB -q pbatch 6 | #BSUB -o job_%J.out 7 | #BSUB -e job_%J.err 8 | # -wa 'KILL 9 | 10 | eval "$(conda shell.bash hook)" 11 | conda activate tfvenv 12 | jupyter nbconvert --to script train_model.ipynb 13 | sed -i '/ipython/d' ./*.py 14 | sed -i '/plt.show()/d' ./*.py 15 | jsrun -r 1 python -u train_model.py -------------------------------------------------------------------------------- /examples/2DBurgerEqn/batch_job.bsub: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #BSUB -nnodes 1 3 | #BSUB -J 2dBg 4 | #BSUB -W 12:00 5 | #BSUB -q pbatch 6 | #BSUB -o job_%J.out 7 | #BSUB -e job_%J.err 8 | # -wa 'KILL 9 | 10 | eval "$(conda shell.bash hook)" 11 | conda activate tfvenv 12 | jupyter nbconvert --to script train_model.ipynb 13 | sed -i '/ipython/d' ./*.py 14 | sed -i '/plt.show()/d' ./*.py 15 | jsrun -r 1 python -u train_model.py -------------------------------------------------------------------------------- /examples/MFEMex16/ref-square.mesh: -------------------------------------------------------------------------------- 1 | MFEM mesh v1.0 2 | 3 | # 4 | # MFEM Geometry Types (see mesh/geom.hpp): 5 | # 6 | # POINT = 0 7 | # SEGMENT = 1 8 | # TRIANGLE = 2 9 | # SQUARE = 3 10 | # TETRAHEDRON = 4 11 | # CUBE = 5 12 | # PRISM = 6 13 | # 14 | 15 | dimension 16 | 2 17 | 18 | elements 19 | 1 20 | 1 3 0 1 2 3 21 | 22 | boundary 23 | 4 24 | 1 1 0 1 25 | 2 1 1 2 26 | 3 1 2 3 27 | 4 1 3 0 28 | 29 | vertices 30 | 4 31 | 2 32 | 0 0 33 | 1 0 34 | 1 1 35 | 0 1 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Lawrence Livermore National Laboratory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -x 3 | 4 | installdir=`pwd`/anaconda 5 | 6 | ################################### 7 | # Python 3 users, run this line: 8 | ################################## 9 | bash /collab/usr/gapps/python/blueos_3_ppc64le_ib/conda/Anaconda3-2021.05-Linux-ppc64le.sh -b -f -p $installdir 10 | 11 | source $installdir/bin/activate 12 | 13 | conda config --add default_channels https://repo.anaconda.com/pkgs/main 14 | conda config --add default_channels https://repo.anaconda.com/pkgs/r 15 | 16 | ################################## 17 | # https://www.ibm.com/support/knowledgecenter/SS5SF7_1.6.0/navigation/pai_install.html 18 | ################################## 19 | 20 | conda config --set ssl_verify /etc/pki/tls/cert.pem 21 | 22 | #conda config --prepend channels \ 23 | #https://public.dhe.ibm.com/ibmdl/export/pub/software/server/ibm-ai/conda/ 24 | 25 | export IBM_POWERAI_LICENSE_ACCEPT=yes 26 | conda config --prepend channels https://public.dhe.ibm.com/ibmdl/export/pub/software/server/ibm-ai/conda-early-access/ 27 | conda create -n tfvenv python=3.7 28 | conda activate ~/.conda/envs/tfvenv 29 | conda install -y scipy opencv absl-py cudatoolkit-dev tensorflow-gpu 30 | -------------------------------------------------------------------------------- /examples/MFEMex9/periodic-square.mesh: -------------------------------------------------------------------------------- 1 | MFEM mesh v1.0 2 | 3 | # 4 | # MFEM Geometry Types (see mesh/geom.hpp): 5 | # 6 | # POINT = 0 7 | # SEGMENT = 1 8 | # TRIANGLE = 2 9 | # SQUARE = 3 10 | # TETRAHEDRON = 4 11 | # CUBE = 5 12 | # 13 | 14 | dimension 15 | 2 16 | 17 | # format: ... 18 | elements 19 | 9 20 | 1 3 0 1 4 3 21 | 2 3 1 2 5 4 22 | 3 3 2 0 3 5 23 | 4 3 3 4 7 6 24 | 5 3 4 5 8 7 25 | 6 3 5 3 6 8 26 | 7 3 6 7 1 0 27 | 8 3 7 8 2 1 28 | 9 3 8 6 0 2 29 | 30 | boundary 31 | 0 32 | 33 | vertices 34 | 9 35 | 36 | nodes 37 | FiniteElementSpace 38 | FiniteElementCollection: L2_T1_2D_P1 39 | VDim: 2 40 | Ordering: 1 41 | 42 | -1 -1 43 | -0.333333333 -1 44 | -1 -0.333333333 45 | -0.333333333 -0.333333333 46 | 47 | -0.333333333 -1 48 | +0.333333333 -1 49 | -0.333333333 -0.333333333 50 | +0.333333333 -0.333333333 51 | 52 | +0.333333333 -1 53 | +1 -1 54 | +0.333333333 -0.333333333 55 | +1 -0.333333333 56 | 57 | -1 -0.333333333 58 | -0.333333333 -0.333333333 59 | -1 +0.333333333 60 | -0.333333333 +0.333333333 61 | 62 | -0.333333333 -0.333333333 63 | +0.333333333 -0.333333333 64 | -0.333333333 +0.333333333 65 | +0.333333333 +0.333333333 66 | 67 | +0.333333333 -0.333333333 68 | +1 -0.333333333 69 | +0.333333333 +0.333333333 70 | +1 +0.333333333 71 | 72 | -1 +0.333333333 73 | -0.333333333 +0.333333333 74 | -1 +1 75 | -0.333333333 +1 76 | 77 | -0.333333333 +0.333333333 78 | +0.333333333 +0.333333333 79 | -0.333333333 +1 80 | +0.333333333 +1 81 | 82 | +0.333333333 +0.333333333 83 | +1 +0.333333333 84 | +0.333333333 +1 85 | +1 +1 86 | -------------------------------------------------------------------------------- /examples/2DBurgerEqn/README.md: -------------------------------------------------------------------------------- 1 | The proposed framework is applied to construct a non-intrusive reduced-order model of the **2D Burgers' Equation**. 2 | 3 | 4 | **Procedure**: 5 | - run `generate_data.ipynb` to generate dataset 6 | 1. generate initial training parameter cases on a given parameter space 7 | 2. generate a dataset of discrete parameter space for adaptive greedy sampling and evaluation 8 | - run `train_model.ipynb` to train a model by submitting a job using `batch_job.bsub` 9 | - If the training time exceeds the maximum allowable wall time, the training can be continued by 10 | updating the following parameters in `train_model.ipynb` and submitting additoinal jobs: 11 | - `params['retrain'] = True` 12 | - `save_name` (file name of the trained model) 13 | - run `test_model.ipynb` to evalute the trained model 14 | 15 | 16 | The data can be generated by running `generate_data.ipynb`. For example, to generate n x n discrete parameter cases with the width in [0.9,1.1] and the amplitude in [0.7,0.9], one can set 17 | - `width = np.linspace(0.9,1.1,n)` 18 | - `amp = np.linspace(0.7,0.9,n)` 19 | 20 | To reproduce the numerical results in the paper, one can generate the following datasets for training and testing: 21 | - An initial training set: 2 x 2 parameter cases on the given parameter space, which are initial parameter cases located at the corners of the parameter space 22 | - An evaluation set: 21 x 21 parameter cases on the given parameter space; a dataset of discrete parameter space for adaptive greedy sampling and evaluation 23 | 24 | 25 | Note that to enhance the training efficiency, the dataset of a discrete parameter space (e.g., 21 x 21 parameter cases) for adaptive greedy sampling is generated before training starts, which means the solution of the optimal parameter case (with the maximum error indicator) is retrieved from the dataset without calling the solver of the full-order model during training. The proposed framework can be adapted to a continuous parameter space and the solution of sampled parameter cases can be generated by running full-order model simulations during training. 26 | -------------------------------------------------------------------------------- /examples/1DBurgerEqn/README.md: -------------------------------------------------------------------------------- 1 | The proposed framework is applied to construct a non-intrusive reduced-order model of the **1D Burgers' Equation**. 2 | 3 | 4 | **Procedure**: 5 | - run `generate_data.ipynb` to generate dataset: 6 | 1. generate initial training parameter cases on a given parameter space 7 | 2. generate a dataset of discrete parameter space for adaptive greedy sampling and evaluation 8 | - run `train_model.ipynb` to train a model by submitting a job using `batch_job.bsub` 9 | - If the training time exceeds the maximum allowable wall time, the training can be continued by 10 | updating the following parameters in `train_model.ipynb` and submitting additoinal jobs: 11 | - `params['retrain'] = True` 12 | - `save_name` (file name of the trained model) 13 | - run `test_model.ipynb` to evalute the trained model 14 | - run `plot_heatmap.ipynb` to plot the greedy sampling process in terms of the error indicator 15 | 16 | 17 | The data can be generated by running `generate_data.ipynb`. For example, to generate n x n discrete parameter cases with the width in [0.9,1.1] and the amplitude in [0.7,0.9], one can set 18 | - `width = np.linspace(0.9,1.1,n)` 19 | - `amp = np.linspace(0.7,0.9,n)` 20 | 21 | One can generate the following datasets for training and testing: 22 | - An initial training set: 2 x 2 parameter cases on the given parameter space, which are initial parameter cases located at the corners of the parameter space 23 | - An evaluation set: 21 x 21 parameter cases on the given parameter space; a dataset of discrete parameter space for adaptive greedy sampling and evaluation 24 | 25 | 26 | Note that to enhance the training efficiency, the dataset of a discrete parameter space (e.g., 21 x 21 parameter cases) for adaptive greedy sampling is generated before training starts, which means the solution of the optimal parameter case (with the maximum error indicator) is retrieved from the dataset without calling the solver of the full-order model during training. The proposed framework can be adapted to a continuous parameter space and the solution of sampled parameter cases can be generated by running full-order model simulations during training. 27 | -------------------------------------------------------------------------------- /examples/MFEMex16/README.md: -------------------------------------------------------------------------------- 1 | The proposed framework is applied to construct a non-intrusive reduced-order model of the [**MFEM Example 16 Time-Dependent Heat Conduction**](https://github.com/mfem/mfem/blob/master/examples/ex16.cpp). 2 | 3 | 4 | The [ex16.cpp](https://github.com/mfem/mfem/blob/master/examples/ex16.cpp) is modified to include the parameterized initial condition and an option to compute the residual given the predicted solutions from the model. The modified `ex16.cpp` and the corresponding executable (`ex16`) are located at `glasdi/src/`. The executable (`ex16`) can be used to generate data and will be called to compute residual-based error indicator during training of the model. 5 | 6 | 7 | **Procedure**: 8 | - run `generate_data.ipynb` to generate dataset; A mesh file ([`ref-square.mesh`](https://github.com/mfem/mfem/blob/master/data/ref-square.mesh)) is included in `glasdi/examples/MFEMex16/`. 9 | 1. generate initial training parameter cases on a given parameter space 10 | 2. generate a dataset of discrete parameter space for adaptive greedy sampling and evaluation 11 | - run `train_model.ipynb` to train a model by submitting a job using `batch_job.bsub` 12 | - If the training time exceeds the maximum allowable wall time, the training can be continued by 13 | updating the following parameters in `train_model.ipynb` and submitting additoinal jobs: 14 | - `params['retrain'] = True` 15 | - `save_name` (file name of the trained model) 16 | - run `test_model.ipynb` to evalute the trained model 17 | 18 | 19 | The data can be generated by running `generate_data.ipynb`. For example, to generate n x n discrete parameter cases with w in [4.0,4.3] and a in [1.0,1.4], one can set 20 | - `p1_arr = np.linspace(4.0,4.3,n)` 21 | - `p2_arr = np.linspace(1.0,1.4,n)` 22 | 23 | To reproduce the numerical results in the paper, one can generate the following datasets for training and testing: 24 | - An initial training set: 2 x 2 parameter cases on the given parameter space, which are initial parameter cases located at the corners of the parameter space 25 | - An evaluation set: 21 x 21 parameter cases on the given parameter space; a dataset of discrete parameter space for adaptive greedy sampling and evaluation 26 | 27 | Note that to enhance the training efficiency, the dataset of a discrete parameter space (e.g., 21 x 21 parameter cases) for adaptive greedy sampling is generated before training starts, which means the solution of the optimal parameter case (with the maximum error indicator) is retrieved from the dataset without calling the solver of the full-order model during training. The proposed framework can be adapted to a continuous parameter space and the solution of sampled parameter cases can be generated by running full-order model simulations during training. -------------------------------------------------------------------------------- /examples/MFEMex9/README.md: -------------------------------------------------------------------------------- 1 | The proposed framework is applied to construct a non-intrusive reduced-order model of the [**MFEM Example 9 Time-Dependent Radial Advection**](https://github.com/mfem/mfem/blob/master/examples/ex9.cpp). 2 | 3 | 4 | The [ex9.cpp](https://github.com/mfem/mfem/blob/master/examples/ex9.cpp) is modified to include the parameterized initial condition and an option to compute the residual given the predicted solutions from the model. The modified `ex9.cpp` and the corresponding executable (`ex9`) are located at `glasdi/src/`. The executable (`ex9`) can be used to generate data and will be called to compute residual-based error indicator during training of the model. 5 | 6 | 7 | **Procedure**: 8 | - run `generate_data.ipynb` to generate dataset; A mesh file ([`periodic-square.mesh`](https://github.com/mfem/mfem/blob/master/data/periodic-square.mesh)) is included in `glasdi/examples/MFEMex9/`. 9 | 1. generate initial training parameter cases on a given parameter space 10 | 2. generate a dataset of discrete parameter space for adaptive greedy sampling and evaluation 11 | - run `train_model.ipynb` to train a model by submitting a job using `batch_job.bsub` 12 | - If the training time exceeds the maximum allowable wall time, the training can be continued by 13 | updating the following parameters in `train_model.ipynb` and submitting additoinal jobs: 14 | - `params['retrain'] = True` 15 | - `save_name` (file name of the trained model) 16 | - run `test_model.ipynb` to evalute the trained model 17 | 18 | 19 | The data can be generated by running `generate_data.ipynb`. For example, to generate n x n discrete parameter cases with w1 in [1.5,2.0] and w2 in [2.0,2.5], one can set 20 | - `p1_arr = np.linspace(1.5,2.0,n)` 21 | - `p2_arr = np.linspace(2.0,2.5,n)` 22 | 23 | To reproduce the numerical results in the paper, one can generate the following datasets for training and testing: 24 | - An initial training set: 2 x 2 parameter cases on the given parameter space, which are initial parameter cases located at the corners of the parameter space 25 | - A training set for adaptive sampling: 11 x 11 parameter cases on the given parameter space; a dataset of discrete parameter space for adaptive greedy sampling 26 | - A testing set: 21 x 21 parameter cases on the given parameter space; a dataset of discrete parameter space for evaluation 27 | 28 | Note that to enhance the training efficiency, the dataset of a discrete parameter space (e.g., 11 x 11 parameter cases) for adaptive greedy sampling is generated before training starts, which means the solution of the optimal parameter case (with the maximum error indicator) is retrieved from the dataset without calling the solver of the full-order model during training. The proposed framework can be adapted to a continuous parameter space and the solution of sampled parameter cases can be generated by running full-order model simulations during training. 29 | -------------------------------------------------------------------------------- /src/sindy_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.special import binom 3 | from scipy.integrate import odeint 4 | from scipy.sparse import spdiags 5 | from scipy.sparse.linalg import spsolve 6 | 7 | def library_size(n, poly_order, use_sine=False, use_cosine=False, include_constant=True): 8 | l = 0 9 | for k in range(poly_order+1): 10 | l += int(binom(n+k-1,k)) 11 | if use_sine: 12 | l += n 13 | if use_cosine: 14 | l += n 15 | if not include_constant: 16 | l -= 1 17 | return l 18 | 19 | 20 | def sindy_library(X, poly_order, include_sine=False, include_cosine=False): 21 | m,n = X.shape 22 | l = library_size(n, poly_order, include_sine, include_cosine, True) 23 | library = np.ones((m,l)) 24 | index = 1 25 | 26 | if poly_order > 0: 27 | for i in range(n): 28 | library[:,index] = X[:,i] 29 | index += 1 30 | 31 | if poly_order > 1: 32 | for i in range(n): 33 | for j in range(i,n): 34 | library[:,index] = X[:,i]*X[:,j] 35 | index += 1 36 | 37 | if poly_order > 2: 38 | for i in range(n): 39 | for j in range(i,n): 40 | for k in range(j,n): 41 | library[:,index] = X[:,i]*X[:,j]*X[:,k] 42 | index += 1 43 | 44 | if poly_order > 3: 45 | for i in range(n): 46 | for j in range(i,n): 47 | for k in range(j,n): 48 | for q in range(k,n): 49 | library[:,index] = X[:,i]*X[:,j]*X[:,k]*X[:,q] 50 | index += 1 51 | 52 | if poly_order > 4: 53 | for i in range(n): 54 | for j in range(i,n): 55 | for k in range(j,n): 56 | for q in range(k,n): 57 | for r in range(q,n): 58 | library[:,index] = X[:,i]*X[:,j]*X[:,k]*X[:,q]*X[:,r] 59 | index += 1 60 | 61 | if include_sine: 62 | for i in range(n): 63 | library[:,index] = np.sin(X[:,i]) 64 | index += 1 65 | 66 | if include_cosine: 67 | for i in range(n): 68 | library[:,index] = np.cos(X[:,i]) 69 | index += 1 70 | 71 | return library 72 | 73 | 74 | def sindy_fit(RHS, LHS, coefficient_threshold): 75 | m,n = LHS.shape 76 | Xi = np.linalg.lstsq(RHS,LHS, rcond=None)[0] 77 | 78 | for k in range(10): 79 | small_inds = (np.abs(Xi) < coefficient_threshold) 80 | Xi[small_inds] = 0 81 | for i in range(n): 82 | big_inds = ~small_inds[:,i] 83 | if np.where(big_inds)[0].size == 0: 84 | continue 85 | Xi[big_inds,i] = np.linalg.lstsq(RHS[:,big_inds], LHS[:,i], rcond=None)[0] 86 | return Xi 87 | 88 | 89 | def sindy_simulate(x0, t, Xi, poly_order, include_sine, include_cosine=False): 90 | m = t.size 91 | n = x0.size 92 | f = lambda x,t : np.dot(sindy_library(np.array(x).reshape((1,n)), poly_order, include_sine, include_cosine), Xi).reshape((n,)) 93 | 94 | x = odeint(f, x0, t) 95 | return x 96 | 97 | 98 | ### Compact Finite Difference Method ### 99 | def D_Lele(N,h): 100 | d=[-1, 0, 1]; 101 | B1=3/8*np.ones(N) 102 | B2=3/8*np.ones(N) 103 | B1[-2]=3 104 | B1[-3]=1/4 105 | B1[-4]=1/3 106 | B1[0]=1/4 107 | B1[1]=1/3 108 | B2[1]=3 109 | B2[2]=1/4 110 | B2[3]=1/3 111 | B2[-1]=1/4 112 | B2[-2]=1/3 113 | A=spdiags([B1, np.ones(N), B2],d, N,N) 114 | 115 | alf=25/32/h 116 | bet=1/20/h 117 | gam=-1/480/h 118 | d=np.arange(-3,4) 119 | 120 | # d=-3 121 | B1=-gam*np.ones(N) 122 | B1[-4]=1/6/h 123 | B1[-5]=0 124 | B1[-6]=0 125 | # d=-2 126 | B2=-bet*np.ones(N) 127 | B2[0]=-1/36/h 128 | B2[-3]=-3/2/h 129 | B2[-4]=0 130 | B2[-5]=-1/36/h 131 | # d = -1 132 | B3=-alf*np.ones(N) 133 | B3[0]=-3/4/h 134 | B3[1]=-7/9/h 135 | B3[-2]=-3/2/h 136 | B3[-3]=-3/4/h 137 | B3[-4]=-7/9/h 138 | # d = 0 139 | B4=np.zeros(N) 140 | B4[0]=-17/6/h 141 | B4[-1]=17/6/h 142 | # d = 1 143 | B5=alf*np.ones(N) 144 | B5[1]=3/2/h 145 | B5[2]=3/4/h 146 | B5[3]=7/9/h 147 | B5[-1]=3/4/h 148 | B5[-2]=7/9/h 149 | # d = 2 150 | B6=bet*np.ones(N) 151 | B6[2]=3/2/h 152 | B6[3]=0 153 | B6[4]=1/36/h 154 | B6[-1]=1/36/h 155 | # d = 3 156 | B7=gam*np.ones(N) 157 | B7[3]=-1/6/h 158 | B7[4]=0 159 | B7[5]=0 160 | B=spdiags([B1, B2, B3, B4, B5, B6, B7],d,N,N) 161 | return spsolve(A.tocsc(),B.tocsc()) 162 | 163 | 164 | def derivative(x, tstop): 165 | dxdt = np.empty(x.shape) 166 | D = D_Lele(x.shape[0], tstop/x.shape[0]) 167 | for i in range(x.shape[1]): 168 | dxdt[:,i] = np.dot(D.toarray(), x[:,i]) 169 | del D 170 | return dxdt -------------------------------------------------------------------------------- /examples/1DBurgerEqn/plot_heatmap.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "3bab78be-8f00-4550-8993-34279e6d005b", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import numpy as np\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "import pickle \n", 13 | "from matplotlib.patches import Rectangle\n", 14 | "import matplotlib.patches as patches\n", 15 | "import seaborn as sns\n", 16 | "from copy import deepcopy\n", 17 | "from matplotlib.ticker import FormatStrFormatter" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "id": "f7c9de63-65cd-4733-bbc0-0a30be614080", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "data_path = \"./fig/nCase441_k1_MRN_ld5_subsize50_upfreq2000/\"\n", 28 | "save_name = 'burger_2021_11_26_21_50_45'\n", 29 | "params = pickle.load(open(data_path + save_name + '_params.pkl', 'rb'))" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "id": "aa5e40e3-187b-41fb-a5a2-795aa882edd2", 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "# heat map of max relative errors\n", 40 | "sns.set(font_scale=1.3)\n", 41 | "def max_err_heatmap(max_err, sindy_idx, idx_list, idx_param, dtype='int', scale=1, mask=None):\n", 42 | " if dtype == 'int':\n", 43 | " max_err = max_err.astype(int)\n", 44 | " rect = []\n", 45 | " rect.append(patches.Rectangle((0, 0), 1, 1, linewidth=2, edgecolor='k', facecolor='none'))\n", 46 | " rect.append(patches.Rectangle((amp_test.size-1, 0), 1, 1, linewidth=2, edgecolor='k', facecolor='none'))\n", 47 | " rect.append(patches.Rectangle((amp_test.size-1, width_test.size-1), 1, 1, linewidth=2, edgecolor='k', facecolor='none'))\n", 48 | " rect.append(patches.Rectangle((0, width_test.size-1), 1, 1, linewidth=2, edgecolor='k', facecolor='none'))\n", 49 | " for i in range(len(idx_param)):\n", 50 | " print(f\"idx: {idx_param[i][0]}, param: {idx_param[i][1]}\")\n", 51 | " idd = idx_param[i][0]\n", 52 | " rect.append(patches.Rectangle((idx_list[idd,0], idx_list[idd,1]), 1, 1, \n", 53 | " linewidth=2, edgecolor='k', facecolor='none'))\n", 54 | " idx_max_err = np.argmax(max_err)\n", 55 | " rect.append(patches.Rectangle((idx_list[idx_max_err,0], idx_list[idx_max_err,1]), 1, 1, \n", 56 | " linewidth=3, edgecolor='lime', facecolor='none'))\n", 57 | " rect2 = deepcopy(rect)\n", 58 | " \n", 59 | " if max_err.size < 100:\n", 60 | " fig = plt.figure(figsize=(10,5))\n", 61 | " else:\n", 62 | " fig = plt.figure(figsize=(18,9))\n", 63 | " \n", 64 | " # local DI indices\n", 65 | " ax = fig.add_subplot(121)\n", 66 | " sindy_idx = sindy_idx.astype(int)\n", 67 | " sns.heatmap(sindy_idx, ax=ax, square=True, xticklabels=width_test, yticklabels=amp_test, \n", 68 | " annot=True, fmt='d', annot_kws={'size':14}, \n", 69 | " cbar=False, cmap='Spectral', robust=True, vmin=1, vmax=15, mask=mask)\n", 70 | " for i in rect:\n", 71 | " ax.add_patch(i)\n", 72 | " \n", 73 | " # format text labels\n", 74 | " fmt = '{:0.2f}'\n", 75 | " xticklabels = []\n", 76 | " for item in ax.get_xticklabels():\n", 77 | " item.set_text(fmt.format(float(item.get_text())))\n", 78 | " xticklabels += [item]\n", 79 | " yticklabels = []\n", 80 | " for item in ax.get_yticklabels():\n", 81 | " item.set_text(fmt.format(float(item.get_text())))\n", 82 | " yticklabels += [item]\n", 83 | " ax.set_xticklabels(xticklabels)\n", 84 | " ax.set_yticklabels(yticklabels)\n", 85 | " ax.set_xlabel('Width', fontsize=16)\n", 86 | " ax.set_ylabel('Amplitude', fontsize=16)\n", 87 | " ax.set_title('Index of Selected Local DI', fontsize=16)\n", 88 | " ax.set_xticklabels(ax.get_xticklabels(), rotation=30) \n", 89 | "\n", 90 | " # heatmap of residual-based error indicator\n", 91 | " ax = fig.add_subplot(122)\n", 92 | " cbar_ax = fig.add_axes([0.99, 0.19, 0.018, 0.7])\n", 93 | " sns.heatmap(max_err*scale, ax=ax, square=True, xticklabels=width_test, yticklabels=amp_test, \n", 94 | " annot=True, annot_kws={'size':14}, fmt='.1f', \n", 95 | " cbar_ax=cbar_ax, cbar=True, cmap='vlag', robust=True, vmin=0, vmax=max_err.max()*scale, mask=mask)\n", 96 | " for i in rect2:\n", 97 | " ax.add_patch(i)\n", 98 | " \n", 99 | " # format text labels\n", 100 | " fmt = '{:0.2f}'\n", 101 | " xticklabels = []\n", 102 | " for item in ax.get_xticklabels():\n", 103 | " item.set_text(fmt.format(float(item.get_text())))\n", 104 | " xticklabels += [item]\n", 105 | " yticklabels = []\n", 106 | " for item in ax.get_yticklabels():\n", 107 | " item.set_text(fmt.format(float(item.get_text())))\n", 108 | " yticklabels += [item]\n", 109 | " ax.set_xticklabels(xticklabels)\n", 110 | " ax.set_yticklabels(yticklabels)\n", 111 | " ax.set_xlabel('Width', fontsize=16)\n", 112 | " ax.set_ylabel('Amplitude', fontsize=16)\n", 113 | " ax.set_xticklabels(ax.get_xticklabels(), rotation=30) \n", 114 | " \n", 115 | " plt.tight_layout()\n", 116 | " plt.savefig(data_path + f'heatmap_{len(idx_param)}.png', bbox_inches='tight')\n", 117 | " plt.show()" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "id": "c563f5d8-a4c3-46a7-943b-658240e4ed57", 124 | "metadata": { 125 | "scrolled": false 126 | }, 127 | "outputs": [], 128 | "source": [ 129 | "# plot heatmaps of residual-based errors at all greedy sampling steps\n", 130 | "amp_test = params['test_param'][:,0]\n", 131 | "width_test = params['test_param'][:,1]\n", 132 | "a_grid, w_grid = np.meshgrid(np.arange(amp_test.size), np.arange(width_test.size))\n", 133 | "idx_list = np.hstack([a_grid.flatten().reshape(-1,1), w_grid.flatten().reshape(-1,1)])\n", 134 | "scale = 10\n", 135 | "for i in range(len(params['err_array'])):\n", 136 | " idx_param = params['max_err_idx_param'][i]\n", 137 | " mask = params['sindy_idx'][i]==-1\n", 138 | " max_err_heatmap(params['err_array'][i], params['sindy_idx'][i], idx_list, \n", 139 | " idx_param, dtype='float', scale=scale, mask=mask)" 140 | ] 141 | } 142 | ], 143 | "metadata": { 144 | "kernelspec": { 145 | "display_name": "tfvenv", 146 | "language": "python", 147 | "name": "tfvenv" 148 | }, 149 | "language_info": { 150 | "codemirror_mode": { 151 | "name": "ipython", 152 | "version": 3 153 | }, 154 | "file_extension": ".py", 155 | "mimetype": "text/x-python", 156 | "name": "python", 157 | "nbconvert_exporter": "python", 158 | "pygments_lexer": "ipython3", 159 | "version": "3.7.10" 160 | } 161 | }, 162 | "nbformat": 4, 163 | "nbformat_minor": 5 164 | } 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gLaSDI: Parametric Physics-informed Greedy Latent Space Dynamics Identification 2 | - In the proposed framework, an autoencoder discovers intrinsic nonlinear latent representations of high-dimensional data, while dynamics identification (DI) models capture local latent-space dynamics. 3 | - An interactive training algorithm is adopted for the autoencoder and local DI models, which enables identification of simple latent-space dynamics and enhances accuracy and efficiency of data-driven reduced-order modeling. 4 | - To maximize and accelerate the exploration of the parameter space for the optimal model performance, an adaptive greedy sampling algorithm integrated with a physics-informed residual-based error indicator and random-subset evaluation is introduced to search for the optimal training samples on-the-fly. 5 | - Further, to exploit local latent-space dynamics captured by the local DI models for an improved modeling accuracy with a minimum number of local DI models in the parameter space, an efficient k-nearest neighbor convex interpolation scheme is employed. 6 | - The effectiveness of the proposed framework is demonstrated by modeling various nonlinear dynamical problems, including Burgers equations, nonlinear heat conduction, and radial advection. 7 | - The proposed adaptive greedy sampling outperforms the conventional predefined uniform sampling in terms of accuracy. Compared with the high-fidelity models, the proposed method achieves 17 to 2,658x speed-up with 1 to 5% relative errors. 8 | 9 | 10 | ## Required Packages 11 | The following versions of packages have been verified to work. Other versions may also work. 12 | - Python: 3.7.10 13 | - TensorFlow: 2.2.0 14 | - Numpy: 1.17.4 15 | - Scipy: 1.4.1 16 | - Sklearn: 0.23.2 17 | - Pandas: 1.1.3 18 | - Matplotlib: 3.4.2 19 | - Seaborn: 0.11.0 20 | - Pickle: 0.7.5 21 | 22 | *For LC users*: 23 | 1. Configure the `conda` directory to the workspace: 24 | `conda config --add pkgs_dirs /usr/workspace/{OUN}/cache/pkgs` 25 | 2. Create a TensorFlow (GPU-version) virtual environment: `bash setup.sh` 26 | 3. Activate the TensorFlow virtual environment: `conda activate ~/.conda/envs/tfvenv` 27 | 4. If TensorFlow wasn't installed successfually in Step 2, try: `conda install -y tensorflow-gpu` 28 | 29 | 30 | ## Examples 31 | Three examples are provided, including 32 | - 1D Burgers Equation 33 | - 2D Burgers Equation 34 | - Time-Dependent Radial Advection ([MFEM Example 9](https://github.com/mfem/mfem/blob/master/examples/ex9.cpp)) 35 | - Time-Dependent Heat Conduction ([MFEM Example 16](https://github.com/mfem/mfem/blob/master/examples/ex16.cpp)) 36 | 37 | The Jupyter notebooks for data generation, model training and evaluation are provided in `glasdi/examples/`. 38 | 39 | 40 | ## Description of Parameters: 41 | - `seed` - int, seed for random number generators; To ensure reproducibility, use the same seed. 42 | - `config` - configuration of TensorFlow; see `tensorflow.ConfigProto` 43 | - `num_sindy` - int, the number of existing local dynamics identification (DI) models in the parameter space 44 | - `param` - the parameter set of sampled parameter cases 45 | - `train_idx` - list, the indices of sampled parameter cases in the discrete parameter space 46 | - `input_dim` - int, input dimension of the auto-encoder 47 | - `latent_dim` - int, latent-space dimension of the auto-encoder 48 | - `poly_order` - int, from 1-5, maximum polynomial order to which to build the DI library 49 | - `include_sine` - bool, whether or not to include sine functions in the DI library 50 | - `include_cosine` - bool, whether or not to include cosine functions in the DI library 51 | - `include_constant` - bool, whether or not to include constant in the DI library 52 | - `library_dim` - int, total number of basis functions; this is determined based on the `latent_dim`, `model_order`, `poly_order`, `include_sine`, `include_cosine`, and `include_constant`, and can be calculated using the function `library_size` in `sindy_utils.py` 53 | - `sequential_thresholding` - bool, whether or not to perform sequential thresholding on the DI coefficient matrix 54 | - `coefficient_threshold` - float, minimum magnitude of coefficients to keep in the DI coefficient matrix when performing thresholding 55 | - `threshold_frequency` - int, the number of epochs after which to perform thresholding 56 | - `coefficient_mask` - numpy.array, matrix of ones and zeros that determines which coefficients are still included in the DI model; typically initialized to all ones and will be modified by the sequential thresholding procedure 57 | - `coefficient_initialization` - str, how to initialize the DI coefficient matrix; options are 'constant' (initialize as all 1s), 'xavier' (initialize using the xavier initialization approach), 'specified' (pass in an additional parameter init_coefficients that has the values to use to initialize the DI coefficient matrix) 58 | - `loss_weight_decoder` - float, weighting of the auto-encoder reconstruction in the loss function (should keep this at 1.0 and adjust the other weightings proportionally) 59 | - `loss_weight_sindy_z`- float, weighting of the DI prediction in the latent space in the loss function 60 | - `loss_weight_sindy_x` - float, weighting of the DI prediction passed back to the input space in the loss function 61 | - `loss_weight_sindy_regularization` - float, weighting of the L1 regularization on the DI coefficients in the loss function; default: zero 62 | - `diff` - str, 'symb': symbolic differentiation (only for fully connected Autoencoder), 'auto': automatic differentiation; default: 'symb' 63 | - `activation` - str, activation function to be used in the network; options are 'sigmoid', 'relu', 'linear', or 'elu' 64 | - `widths` - list of integers specifying the number of units for each hidden layer of the encoder; decoder widths will be the reverse order of these widths 65 | - `epoch_size` - int, the number of training samples in an epoch 66 | - `batch_size` - int, the number of samples to use in a batch of training 67 | - `learning rate` - float, initial learning rate passed to the Adam optimizer 68 | - `fig_path` - str, path specifying where to save the resulting models 69 | - `print_progress` - bool, whether or not to print updates during training 70 | - `print_frequency` - int, print progress at intervals of this many epochs 71 | - `max_epochs` - int, the maximum number of training epochs 72 | - `update_epoch` - int, greedy sampling frequency, update training set at intervals of this many epochs 73 | - `tol` - float, initial tolerance of the maximum error indicator in the parameter space; it will be updated during training using the prescribed `adaptive` method 74 | - `tol2` - float, initial tolerance of the maximum relative error in the parameter space 75 | - `sindy_max` - int or `None`, the maximum number of local DIs; if tolerance is used as a termination criterion; set it as `None` 76 | - `convex_knn` - int, the number nearest local DIs used for convex interpolation of coefficient matrices during Greedy sampling 77 | - `test_data` - dict, dataset of the discrete parameter space 78 | - `test_param` - numpy.array, parameters of the discrete parameter space 79 | - `num_test` - int, the number of parameter cases of the discrete parameter space 80 | - `coeff_exist` - bool, whether or not to initialize model coefficients with pescribed values; set as `False` 81 | - `retrain` - bool, whether or not to retrain the model; set as `False` for training a new model 82 | - `err_type` - int, 1: max relative error (if test data is available); 2: residual norm for 1D Burgers eqn; 3: residual norm for 2D Burgers eqn; 4: residual norm for time dependent heat conduction (MFEM example 16); 5: residual norm for radial advection (MFEM example 9) 83 | - `subsize` - int, initial random subset size, the number of randomly selected cases for Greedy sampling 84 | - `subsize_max` - int, maximum random subset size in percentage 85 | - `adapative` - str, the method used to update the error tolerance; 'mean': use mean ratios between error indicator and max relative errors; 'reg_mean': use linear regression line; 'reg_max': use linear regression line shifted by std to upper bound; 'reg_min': use linear regression line shifted by std to lower bound, more conservative; 86 | - `pde` - dict, stores the parameters related to the PDE 87 | 88 | 89 | ## Citation 90 | [He, X., Choi, Y., Fries, W. D., Belof, J., & Chen, J. S. (2023). gLaSDI: Parametric Physics-informed Greedy Latent Space Dynamics Identification. Journal of Computational Physics, 489, 112267.](https://www.sciencedirect.com/science/article/pii/S0021999123003625?dgcid=author) 91 | 92 | ## Aknowledgement 93 | Y. Choi was also supported for this work by the CHaRMNET Mathematical Multifaceted Integrated Capability Center (MMICC). 94 | 95 | ## License 96 | gLaSDI is distributed under the terms of the MIT license. All new contributions must be made under the MIT. See 97 | [LICENSE-MIT](https://github.com/LLNL/gLaSDI/blob/main/LICENSE) 98 | 99 | LLNL Release Nubmer: LLNL-CODE-842327 100 | -------------------------------------------------------------------------------- /examples/1DBurgerEqn/generate_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "4af6b8f8", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import sys\n", 11 | "sys.path.append('../../src')\n", 12 | "import os\n", 13 | "import numpy as np\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import pickle\n", 16 | "from time import time\n", 17 | "from scipy import sparse\n", 18 | "from scipy.sparse import spdiags\n", 19 | "from scipy.sparse.linalg import spsolve\n", 20 | "from sindy_utils import D_Lele\n", 21 | "\n", 22 | "def get_cmap(n, name='tab20'):\n", 23 | " return plt.cm.get_cmap(name, n)\n", 24 | "cmap = get_cmap(10)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "a85365b9", 31 | "metadata": { 32 | "tags": [] 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "x0 = -3\n", 37 | "x1 = 3\n", 38 | "tstop = 1\n", 39 | "nx = 1001\n", 40 | "nt = 1000\n", 41 | "amp = np.array([0.7])\n", 42 | "width = np.array([0.9])\n", 43 | "save_data = False\n", 44 | "\n", 45 | "# amp = np.linspace(0.7,0.9,21)\n", 46 | "# width = np.linspace(0.9,1.1,21)" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "id": "b32fe39e", 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "def kth_diag_indices(a, k):\n", 57 | " rows, cols = np.diag_indices_from(a)\n", 58 | " if k < 0:\n", 59 | " return rows[-k:], cols[:k]\n", 60 | " elif k > 0:\n", 61 | " return rows[:-k], cols[k:]\n", 62 | " else:\n", 63 | " return rows, cols\n", 64 | " \n", 65 | "def sine_wave(amp, width):\n", 66 | " u0 = np.zeros(nx)\n", 67 | " u0[1:int(width/dx+1)] =amp/2*(np.sin(2*np.pi/(x[int(width/dx+1)]-x[1])*x[1:int(width/dx+1)]-np.pi/2)+1)\n", 68 | " u0[-1] = u0[0]\n", 69 | " return u0\n", 70 | "\n", 71 | "def gaussian(amp, width, x):\n", 72 | " u0 = amp*np.exp(-(x-0.0)**2/(2*width**2))\n", 73 | " u0[-1] = u0[0]\n", 74 | " return u0\n", 75 | "\n", 76 | "def residual(un, uw, c, idxn1):\n", 77 | " # r = -u^{n} + u^{n+1} -dt*f(u^{n+1})\n", 78 | " f = c*(uw**2 - uw*uw[idxn1]) \n", 79 | " r = -un + uw + f\n", 80 | " return r\n", 81 | "\n", 82 | "def jacobian(u, c, idxn1, nx):\n", 83 | " diag_comp = 1.0 + c*(2*u - u[idxn1])\n", 84 | " subdiag_comp = np.ones(nx-1)\n", 85 | " subdiag_comp[:-1] = -c*u[1:]\n", 86 | " data = np.array([diag_comp, subdiag_comp])\n", 87 | " J = spdiags(data,[0,-1],nx-1,nx-1,format='csr')\n", 88 | " J[0,-1] = -c*u[0]\n", 89 | " return J\n", 90 | "\n", 91 | "def solve(u0, maxk, convergence_threshold, nt, nx, idxn1, c):\n", 92 | " u = np.zeros((nt+1,nx))\n", 93 | " u_inter=np.array([])\n", 94 | " u[0] = u0\n", 95 | " u_inter=np.append(u_inter,u0[:-1])\n", 96 | " I = sparse.eye(nx,format='csr')\n", 97 | " for n in range(nt): \n", 98 | " uw = u[n,:-1].copy()\n", 99 | " r = residual(u[n,:-1], uw, c, idxn1)\n", 100 | " \n", 101 | " for k in range(maxk):\n", 102 | " J = jacobian(uw, c, idxn1, nx)\n", 103 | " duw = spsolve(J, -r)\n", 104 | " uw = uw + duw\n", 105 | " r = residual(u[n,:-1], uw, c, idxn1)\n", 106 | " u_inter = np.append(u_inter, uw)\n", 107 | "\n", 108 | " rel_residual = np.linalg.norm(r)/np.linalg.norm(u[n,:-1])\n", 109 | " if rel_residual < convergence_threshold:\n", 110 | " u[n+1,:-1] = uw.copy()\n", 111 | " u[n+1,-1] = u[n+1,0]\n", 112 | " break\n", 113 | " return u,u_inter.reshape((-1,nx-1))\n", 114 | "\n", 115 | " \n", 116 | "def gen_data(amp_arr, width_arr, x0, x1, tstop, nx, nt):\n", 117 | " maxk = 10\n", 118 | " convergence_threshold = 1.0e-8\n", 119 | " x = np.linspace(x0, x1, nx)\n", 120 | " dx = (x1-x0) / (nx - 1)\n", 121 | " t = np.linspace(0, tstop, nt)\n", 122 | " dt = tstop / nt \n", 123 | " c = dt/dx\n", 124 | " degree = 1 \n", 125 | " thres = 1\n", 126 | "\n", 127 | " idxn1 = np.zeros(nx-1,dtype='int')\n", 128 | " idxn1[1:] = np.arange(nx-2)\n", 129 | " idxn1[0] = nx-2\n", 130 | " timer = []\n", 131 | " \n", 132 | " # compute x\n", 133 | " timer.append(time())\n", 134 | " num_amp = amp_arr.shape[0]\n", 135 | " num_width = width_arr.shape[0]\n", 136 | " soln = []\n", 137 | " for i in range(num_amp):\n", 138 | " for j in range(num_width):\n", 139 | " u0 = gaussian(amp_arr[i],width_arr[j],x)\n", 140 | "# u0=sine_wave(amp_arr[i],width_arr[j])\n", 141 | " u,_ = solve(u0, maxk, convergence_threshold, nt, nx, idxn1, c)\n", 142 | " soln.append(u)\n", 143 | " soln = np.vstack(soln) \n", 144 | " \n", 145 | " # compute dx/dt\n", 146 | " timer.append(time())\n", 147 | " data_x = soln.reshape(-1,nt+1,nx)\n", 148 | " dxdt_full = []\n", 149 | " t_full = []\n", 150 | " for num in range(len(data_x)):\n", 151 | " X = data_x[num] # exclude the last time step\n", 152 | " dxdt = np.empty(X.shape)\n", 153 | " D = D_Lele(X.shape[0],tstop/X.shape[0]) \n", 154 | " for i in range(X.shape[1]):\n", 155 | " dxdt[:,i] = np.dot(D.toarray(), X[:,i])\n", 156 | " dxdt_full.extend(dxdt)\n", 157 | " t_full.append(np.linspace(0,tstop,nt+1).reshape(-1,1))\n", 158 | " del D\n", 159 | " dxdt = np.array(dxdt_full)\n", 160 | " t = np.vstack(t_full)\n", 161 | " timer.append(time())\n", 162 | " \n", 163 | " time_fom = [timer[2]-timer[1], timer[1]-timer[0]]\n", 164 | " data = {}\n", 165 | " data['t'] = t\n", 166 | " data['x'] = soln\n", 167 | " data['dx'] = dxdt\n", 168 | " data['time_fom'] = time_fom\n", 169 | " return data" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "id": "60b4e444", 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "data = []\n", 180 | "param = []\n", 181 | "for i in amp:\n", 182 | " for j in width:\n", 183 | " tmp = gen_data(np.array([i]), np.array([j]), x0, x1, tstop, nx, nt)\n", 184 | " data.append(tmp)\n", 185 | " param.append(np.array([i, j]))\n", 186 | "data_all = {}\n", 187 | "data_all['data'] = data\n", 188 | "data_all['param'] = param\n", 189 | " \n", 190 | "\n", 191 | "num_case = len(data_all['data'])\n", 192 | "param = data_all['param']\n", 193 | "time1 = 0\n", 194 | "time2 = 0\n", 195 | "time3 = 0\n", 196 | "print(f'number of cases: {num_case}')\n", 197 | "for i in range(num_case):\n", 198 | " print(f\"Case {i+1}: A: {param[i][0]:.2f}, W: {param[i][1]:.2f}, t: {data_all['data'][i]['t'].shape}, x: {data_all['data'][i]['x'].shape}, dx: {data_all['data'][i]['dx'].shape}\")\n", 199 | " time1 += data_all['data'][i]['time_fom'][0]\n", 200 | " time2 += data_all['data'][i]['time_fom'][1]\n", 201 | " time3 = time1 + time2\n", 202 | " \n", 203 | "print(f\"time for computing x: {time1/num_case:.2f} s\")\n", 204 | "print(f\"time for computing dx: {time2/num_case:.2f} s\")\n", 205 | "print(f'total time: {time3/num_case:.2f} s')" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "id": "94264eb2-c7fe-480e-9734-690bb83188ca", 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "t = np.linspace(0,tstop,nt+1)\n", 216 | "x = np.linspace(x0,x1,nx)\n", 217 | "step_list = np.linspace(0,nt,4,dtype=int)\n", 218 | "\n", 219 | "fig1 = plt.figure(figsize=(9,6))\n", 220 | "idx = np.arange(0,t.size,50)\n", 221 | "ax = fig1.add_subplot(111)\n", 222 | "for i,step in enumerate(step_list):\n", 223 | " ax.plot(x, data_all['data'][0]['x'][step,:], '-', lw=2, c=cmap(i), label=f't={t[step]:.1f} s')\n", 224 | "\n", 225 | "ax.set_xlabel('x')\n", 226 | "ax.set_ylabel('u')\n", 227 | "ax.set_xlim(x.min(),x.max())\n", 228 | "ax.set_ylim(bottom=0)\n", 229 | "ax.tick_params(axis='both', labelsize=24)\n", 230 | "ax.legend(loc='upper left', frameon=False, fontsize=18)\n", 231 | "plt.tight_layout()\n", 232 | "# plt.savefig(f\"./1Dburger_physical_dynamics.png\",bbox_inches='tight')" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "id": "9353d185", 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "# save data\n", 243 | "if save_data:\n", 244 | " data_path = '/g/g92/he10/Research/data/1DBurgerEqn'\n", 245 | " if not os.path.exists(data_path):\n", 246 | " os.makedirs(data_path)\n", 247 | " if num_case > 1:\n", 248 | " pickle.dump(data_all, open(data_path + f\"/local{num_case}.p\", \"wb\"))\n", 249 | " else:\n", 250 | " pickle.dump(data_all, open(data_path + f\"/local{num_case}_A{amp[0]:.2f}_W{width[0]:.2f}.p\", \"wb\"))" 251 | ] 252 | } 253 | ], 254 | "metadata": { 255 | "kernelspec": { 256 | "display_name": "tfvenv", 257 | "language": "python", 258 | "name": "tfvenv" 259 | }, 260 | "language_info": { 261 | "codemirror_mode": { 262 | "name": "ipython", 263 | "version": 3 264 | }, 265 | "file_extension": ".py", 266 | "mimetype": "text/x-python", 267 | "name": "python", 268 | "nbconvert_exporter": "python", 269 | "pygments_lexer": "ipython3", 270 | "version": "3.7.10" 271 | } 272 | }, 273 | "nbformat": 4, 274 | "nbformat_minor": 5 275 | } 276 | -------------------------------------------------------------------------------- /src/error_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy import sparse as sp 3 | import subprocess 4 | from scipy.sparse import spdiags 5 | from scipy.sparse.linalg import spsolve 6 | 7 | 8 | def err_indicator(u_sim, params, data=None, err_type=1): 9 | """ 10 | This function computes errors using a speciffied error indicator. 11 | inputs: 12 | data: dict, data of the evalution case 13 | err_type: int, types of error indicator 14 | 1: max relative error (if test data is available) 15 | 2: residual norm (mean), 1D Burger's eqn 16 | 3: residual norm (mean), 2D Burger's eqn 17 | 4: MFEM example 16: Time dependent heat conduction 18 | 5: MFEM example 9: DG advection 19 | outputs: 20 | err: float, error 21 | """ 22 | if err_type == 1: 23 | err = (np.linalg.norm(data - u_sim, axis=1) / np.linalg.norm(data, axis=1)*100).max() 24 | elif err_type == 2: 25 | res = [] 26 | for k in range(u_sim.shape[0]-1): 27 | res.append(residual_1Dburger(u_sim[k,:], u_sim[k+1,:], params)) 28 | err = np.stack(res).mean() 29 | elif err_type == 3: 30 | res = [] 31 | for k in range(u_sim.shape[0]-1): 32 | res.append(residual_2Dburger(u_sim[k,:], u_sim[k+1,:], params)) 33 | err = np.stack(res).mean() 34 | elif err_type == 4: 35 | u_file = params['pde']['u_file'] 36 | np.savetxt(u_file, np.array([[u_sim.shape[0], u_sim.shape[1]]]), fmt='%d') 37 | with open(u_file, 'ab') as f: 38 | np.savetxt(f, u_sim, fmt='%.18f') 39 | err = residual_MFEMex16(params) / int(u_sim.shape[0]*params['pde']['res_ns']-1) 40 | elif err_type == 5: 41 | u_file = params['pde']['u_file'] 42 | np.savetxt(u_file, np.array([[u_sim.shape[0], u_sim.shape[1]]]), fmt='%d') 43 | with open(u_file, 'ab') as f: 44 | np.savetxt(f, u_sim, fmt='%.18f') 45 | err = residual_MFEMex9(params) / int(u_sim.shape[0]*params['pde']['res_ns']-1) 46 | return err 47 | 48 | 49 | def residual_1Dburger(u0, u1, params): 50 | """ 51 | r = -u^{n} + u^{n+1} -dt*f(u^{n+1}) 52 | """ 53 | nx = params['pde']['nx'] 54 | nt = params['pde']['nt'] 55 | tstop = params['pde']['tstop'] 56 | dx = 6 / (nx - 1) 57 | dt = tstop / nt 58 | c = dt / dx 59 | 60 | idxn1 = np.zeros(nx,dtype='int') 61 | idxn1[1:] = np.arange(nx-1) 62 | idxn1[0] = nx-1 63 | 64 | f = c*(u1**2 - u1*u1[idxn1]) 65 | r = -u0 + u1 + f 66 | return np.linalg.norm(r) 67 | 68 | 69 | def residual_2Dburger(x_prev, x, params): 70 | Re = params['pde']['Re'] 71 | nx = params['pde']['nx'] 72 | ny = nx 73 | nt = params['pde']['nt'] 74 | tstop = params['pde']['tstop'] 75 | ic = params['pde']['ic'] # initial condition, 1: Sine, 2: Gaussian 76 | u_prev = x_prev[:nx*ny] 77 | u = x[:nx*ny] 78 | v_prev = x_prev[nx*ny:] 79 | v = x[nx*ny:] 80 | 81 | dt=tstop/nt 82 | t = np.linspace(0, tstop, nt+1) 83 | nxy = (nx-2)*(ny-2) 84 | dx = 1/(nx-1) 85 | dy = 1/(ny-1) 86 | 87 | if ic == 1: # sine 88 | xmin = 0 89 | xmax = 1 90 | ymin = 0 91 | ymax = 1 92 | elif ic == 2: # Gaussian 93 | xmin = -3 94 | xmax = 3 95 | ymin = -3 96 | ymax = 3 97 | x0 = 0 # Gaussian center 98 | y0 = 0 # Gaussian center 99 | else: 100 | print('wrong values for IC!') 101 | I=sp.eye(nxy,format='csr') 102 | 103 | # full indices, free indices, fixed indices 104 | [xv,yv] = np.meshgrid(np.linspace(xmin,xmax,nx),np.linspace(ymin,ymax,ny),indexing='xy') 105 | x = xv.flatten() 106 | y = yv.flatten() 107 | 108 | multi_index_i,multi_index_j = np.meshgrid(np.arange(nx),np.arange(ny),indexing='xy') 109 | full_multi_index = (multi_index_j.flatten(),multi_index_i.flatten()) 110 | free_multi_index = (multi_index_j[1:-1,1:-1].flatten(),multi_index_i[1:-1,1:-1].flatten()) 111 | x0_multi_index = (multi_index_j[1:-1,0].flatten(),multi_index_i[1:-1,0].flatten()) 112 | x1_multi_index = (multi_index_j[1:-1,-1].flatten(),multi_index_i[1:-1,-1].flatten()) 113 | y0_multi_index = (multi_index_j[0,1:-1].flatten(),multi_index_i[0,1:-1].flatten()) 114 | y1_multi_index = (multi_index_j[-1,1:-1].flatten(),multi_index_i[-1,1:-1].flatten()) 115 | 116 | dims=(ny,nx) 117 | full_raveled_indices = np.ravel_multi_index(full_multi_index,dims) 118 | free_raveled_indices = np.ravel_multi_index(free_multi_index,dims) 119 | x0_raveled_indices = np.ravel_multi_index(x0_multi_index,dims) 120 | x1_raveled_indices = np.ravel_multi_index(x1_multi_index,dims) 121 | x01_raveled_indices = np.concatenate((x0_raveled_indices,x1_raveled_indices)) 122 | y0_raveled_indices = np.ravel_multi_index(y0_multi_index,dims) 123 | y1_raveled_indices = np.ravel_multi_index(y1_multi_index,dims) 124 | y01_raveled_indices = np.concatenate((y0_raveled_indices,y1_raveled_indices)) 125 | fixed_raveled_indices = np.setdiff1d(full_raveled_indices,free_raveled_indices) 126 | 127 | # boundary one-hot vector 128 | x0_one_hot = np.eye(nx-2)[0] 129 | y0_one_hot = np.eye(ny-2)[0] 130 | x1_one_hot = np.eye(nx-2)[-1] 131 | y1_one_hot = np.eye(ny-2)[-1] 132 | 133 | # inner grid 134 | inner_multi_index_i,inner_multi_index_j=np.meshgrid(np.arange(nx-2),np.arange(ny-2),indexing='xy') 135 | inner_x_multi_index = (np.concatenate((inner_multi_index_j[:,0].flatten(),inner_multi_index_j[:,-1].flatten())), 136 | np.concatenate((inner_multi_index_i[:,0].flatten(),inner_multi_index_i[:,-1].flatten()))) 137 | inner_y_multi_index = (np.concatenate((inner_multi_index_j[0,:].flatten(),inner_multi_index_j[-1,:].flatten())), 138 | np.concatenate((inner_multi_index_i[0,:].flatten(),inner_multi_index_i[-1,:].flatten()))) 139 | 140 | inner_dims = (ny-2,nx-2) 141 | inner_x_raveled_indices = np.ravel_multi_index(inner_x_multi_index,inner_dims) 142 | inner_y_raveled_indices = np.ravel_multi_index(inner_y_multi_index,inner_dims) 143 | 144 | 145 | # first order derivative 146 | # central 147 | Mcb = sp.diags([np.zeros(nx-2),-np.ones(nx-2),np.ones(nx-2)],[0,-1,1],(nx-2,nx-2)) 148 | Mc = sp.kron(sp.eye(ny-2),Mcb,format="csr") 149 | 150 | Ib = sp.eye(nx-2) 151 | Nc = sp.kron(sp.diags([np.zeros(ny-2),-np.ones(ny-2),np.ones(ny-2)],[0,-1,1],(ny-2,ny-2)),Ib,format="csr") 152 | 153 | # forward 154 | Mfb = sp.diags([-np.ones(nx-2),np.ones(nx-2)],[0,1],(nx-2,nx-2)) 155 | Mf = sp.kron(sp.eye(ny-2),Mfb,format="csr") 156 | 157 | Ib = sp.eye(nx-2) 158 | Nf = sp.kron(sp.diags([-np.ones(ny-2),np.ones(ny-2)],[0,1],(ny-2,ny-2)),Ib,format="csr") 159 | 160 | # backward 161 | Mbb = sp.diags([np.ones(nx-2),-np.ones(nx-2)],[0,-1],(nx-2,nx-2)) 162 | Mb = sp.kron(sp.eye(ny-2),Mbb,format="csr") 163 | 164 | Ib = sp.eye(nx-2) 165 | Nb = sp.kron(sp.diags([np.ones(ny-2),-np.ones(ny-2)],[0,-1],(ny-2,ny-2)),Ib,format="csr") 166 | 167 | # laplacian operator 168 | Dxb = sp.diags([-2*np.ones(nx-2),np.ones(nx-2),np.ones(nx-2)],[0,-1,1],(nx-2,nx-2)) 169 | Dx = sp.kron(sp.eye(ny-2),Dxb,format="csr") 170 | 171 | Ib = sp.eye(nx-2) 172 | Dy = sp.kron(sp.diags([-2*np.ones(ny-2),np.ones(ny-2),np.ones(ny-2)],[0,-1,1],(ny-2,ny-2)),Ib,format="csr") 173 | 174 | # Initial condition 175 | amp = params['pde']['param'][0] 176 | width = params['pde']['param'][1] 177 | if ic == 1: # IC: sine 178 | zv = amp*np.sin(2*np.pi*xv)*np.sin(2*np.pi*yv) 179 | zv[np.nonzero(xv>0.5)] = 0.0 180 | zv[np.nonzero(yv>0.5)] = 0.0 181 | elif ic == 2: # IC: Gaussian 182 | zv = amp * np.exp(-((xv-x0)**2 + (yv-y0)**2) / width) 183 | z = zv.flatten() 184 | u0 = z.copy() 185 | v0 = z.copy() 186 | 187 | 188 | # boundary for first order derivative term 189 | Bdudx0_cur=np.kron(u0[x0_raveled_indices],x0_one_hot) 190 | Bdudy0_cur=np.kron(y0_one_hot,u0[y0_raveled_indices]) 191 | Bdvdx0_cur=np.kron(v0[x0_raveled_indices],x0_one_hot) 192 | Bdvdy0_cur=np.kron(y0_one_hot,v0[y0_raveled_indices]) 193 | Bdudx1_cur=np.kron(u0[x1_raveled_indices],x1_one_hot) 194 | Bdudy1_cur=np.kron(y1_one_hot,u0[y1_raveled_indices]) 195 | Bdvdx1_cur=np.kron(v0[x1_raveled_indices],x1_one_hot) 196 | Bdvdy1_cur=np.kron(y1_one_hot,v0[y1_raveled_indices]) 197 | 198 | # boundary for second order derivative term 199 | bxu_cur=np.zeros(nxy) 200 | byu_cur=np.zeros(nxy) 201 | bxv_cur=np.zeros(nxy) 202 | byv_cur=np.zeros(nxy) 203 | 204 | bxu_cur[inner_x_raveled_indices]=u0[x01_raveled_indices] 205 | byu_cur[inner_y_raveled_indices]=u0[y01_raveled_indices] 206 | bxv_cur[inner_x_raveled_indices]=v0[x01_raveled_indices] 207 | byv_cur[inner_y_raveled_indices]=v0[y01_raveled_indices] 208 | 209 | u_free_prev=np.copy(u_prev[free_raveled_indices]) 210 | v_free_prev=np.copy(v_prev[free_raveled_indices]) 211 | 212 | u_free=np.copy(u[free_raveled_indices]) 213 | v_free=np.copy(v[free_raveled_indices]) 214 | 215 | Mu_free=Mb.dot(u_free) 216 | Mv_free=Mb.dot(v_free) 217 | Nu_free=Nb.dot(u_free) 218 | Nv_free=Nb.dot(v_free) 219 | 220 | 221 | f_u=(-1/dx*(u_free*(Mu_free - Bdudx0_cur)) 222 | -1/dy*(v_free*(Nu_free - Bdudy0_cur)) 223 | +1/(Re*dx**2)*(Dx.dot(u_free) + bxu_cur) 224 | +1/(Re*dy**2)*(Dy.dot(u_free) + byu_cur)) 225 | 226 | f_v=(-1/dx*(u_free*(Mv_free - Bdvdx0_cur)) 227 | -1/dy*(v_free*(Nv_free - Bdvdy0_cur)) 228 | +1/(Re*dx**2)*(Dx.dot(v_free) + bxv_cur) 229 | +1/(Re*dy**2)*(Dy.dot(v_free) + byv_cur)) 230 | 231 | r_u = u_free - u_free_prev-dt*f_u 232 | r_v = v_free - v_free_prev-dt*f_v 233 | 234 | return np.linalg.norm(r_u)+np.linalg.norm(r_v) 235 | 236 | 237 | def residual_MFEMex16(params): 238 | """ 239 | This function calculates the residual error of the 240 | heat conduction problem (MFEM example 16). 241 | The initial condition is parameterized by w0 and h0. 242 | Their values are stored in `params['pde']['param']` 243 | """ 244 | subprocess.call([params['pde']['exe_file'], 245 | '-m', params['pde']['m_file'], 246 | '-uf', params['pde']['u_file'], 247 | '-rf', params['pde']['res_file'], 248 | '-r', str(params['pde']['rl']), 249 | '-o', str(params['pde']['order']), 250 | '-s', str(params['pde']['ODEsolver']), 251 | '-tf', str(params['pde']['tstop']), 252 | '-dt', str(params['pde']['dt']), 253 | '-w0', str(params['pde']['param'][0]), 254 | '-h0', str(params['pde']['param'][1]), 255 | '-x1', str(params['pde']['x1']), 256 | '-x2', str(params['pde']['x2']), 257 | '-res_ns', str(params['pde']['res_ns']), 258 | '-Tmax_iter', str(params['pde']['Tmax_iter']), 259 | '-no-vis', 260 | '-no-visit', 261 | '-res']) 262 | res = np.loadtxt(params['pde']['res_file']) 263 | return res 264 | 265 | 266 | def residual_MFEMex9(params): 267 | """ 268 | This function calculates the residual error of the 269 | radial advection problem (MFEM example 9). 270 | The initial condition is parameterized by w1 and w2. 271 | Their values are stored in `params['pde']['param']` 272 | """ 273 | subprocess.call([params['pde']['exe_file'], 274 | '-m', params['pde']['m_file'], 275 | '-uf', params['pde']['u_file'], 276 | '-rf', params['pde']['res_file'], 277 | '-p', str(params['pde']['prob']), 278 | '-r', str(params['pde']['rl']), 279 | '-o', str(params['pde']['order']), 280 | '-tf', str(params['pde']['tstop']), 281 | '-dt', str(params['pde']['dt']), 282 | '-w1', str(params['pde']['param'][0]), 283 | '-w2', str(params['pde']['param'][1]), 284 | '-res_ns', str(params['pde']['res_ns']), 285 | '-Mmax_iter', str(params['pde']['Mmax_iter']), 286 | '-no-vis', 287 | '-no-visit', 288 | '-res']) 289 | res = np.loadtxt(params['pde']['res_file']) 290 | return res -------------------------------------------------------------------------------- /examples/1DBurgerEqn/train_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.append(\"../../src\")\n", 11 | "import os\n", 12 | "import datetime\n", 13 | "import pandas as pd\n", 14 | "import numpy as np\n", 15 | "from sindy_utils import library_size\n", 16 | "from training import train_network\n", 17 | "import tensorflow.compat.v1 as tf\n", 18 | "tf.compat.v1.disable_v2_behavior()\n", 19 | "from time import time\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "from matplotlib.ticker import FormatStrFormatter\n", 22 | "import pickle \n", 23 | "import subprocess as sp" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "def get_gpu_memory():\n", 33 | " _output_to_list = lambda x: x.decode('ascii').split('\\n')[:-1]\n", 34 | "\n", 35 | " ACCEPTABLE_AVAILABLE_MEMORY = 1024\n", 36 | " COMMAND = \"nvidia-smi --query-gpu=memory.free --format=csv\"\n", 37 | " memory_free_info = _output_to_list(sp.check_output(COMMAND.split()))[1:]\n", 38 | " memory_free_values = [int(x.split()[0]) for i, x in enumerate(memory_free_info)]\n", 39 | " return memory_free_values\n", 40 | "\n", 41 | "device_list = tf.config.list_physical_devices('GPU')\n", 42 | "free_mem = get_gpu_memory()\n", 43 | "for i,gpu in enumerate(device_list):\n", 44 | " print(f'{gpu}: free memory: {free_mem[i]}')" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# specify which GPU to use\n", 54 | "config = tf.ConfigProto(log_device_placement=False, gpu_options=tf.GPUOptions(allow_growth=True,\n", 55 | " visible_device_list='0'))" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Load data" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "# load initial training dataset\n", 72 | "amp_train = np.linspace(0.7,0.9,2)\n", 73 | "width_train = np.linspace(0.9,1.1,2)\n", 74 | "num_train = amp_train.size * width_train.size # initial number of training data\n", 75 | "nx = 1001\n", 76 | "nt = 1000\n", 77 | "tstop = 1.0\n", 78 | "train_data = pickle.load(open(f\"/g/g92/he10/Research/data/1DBurgerEqn/local{num_train}.p\", \"rb\")) # A:[0.7,0.9], W:[0.9,1.1]\n", 79 | "num_sindy = len(train_data['data'])\n", 80 | "input_dim = train_data['data'][0]['x'].shape[1]\n", 81 | "\n", 82 | "for i in range(num_sindy):\n", 83 | " print(f\"case {i}: params: {train_data['param'][i]}, x shape: {train_data['data'][i]['x'].shape}\")\n", 84 | " \n", 85 | "# load dataset of discrete parameter space for greedy sampling\n", 86 | "amp_test = np.linspace(0.7,0.9,21)\n", 87 | "width_test = np.linspace(0.9,1.1,21)\n", 88 | "num_test = amp_test.size * width_test.size # number of cases in the discrete parameter space\n", 89 | "test_data = pickle.load(open(f\"/g/g92/he10/Research/data/1DBurgerEqn/local{num_test}.p\", \"rb\")) # A:[0.7,0.9], W:[0.9,1.1]" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "grid1, grid2 = np.meshgrid(amp_train, width_train)\n", 99 | "train_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 100 | "grid1, grid2 = np.meshgrid(amp_test, width_test)\n", 101 | "test_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 102 | "\n", 103 | "train_idx = []\n", 104 | "for i in range(num_test):\n", 105 | " for j in range(num_train):\n", 106 | " if np.abs(test_param[i,0]-train_param[j,0]) < 1e-8 and \\\n", 107 | " np.abs(test_param[i,1]-train_param[j,1]) < 1e-8:\n", 108 | " train_idx.append(i)\n", 109 | "print(train_idx)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## Set up model and training parameters" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "params = {}\n", 126 | "\n", 127 | "params['seed'] = 1 # random seed\n", 128 | "params['config'] = config\n", 129 | "params['num_sindy'] = num_sindy\n", 130 | "params['param'] = train_data['param']\n", 131 | "params['train_idx'] = train_idx\n", 132 | "params['input_dim'] = input_dim\n", 133 | "params['latent_dim'] = 5\n", 134 | "params['poly_order'] = 1\n", 135 | "params['include_sine'] = False\n", 136 | "params['include_cosine'] = False\n", 137 | "params['include_costant'] = True\n", 138 | "params['library_dim'] = library_size(params['latent_dim'], params['poly_order'], \n", 139 | " params['include_sine'], params['include_cosine'], \n", 140 | " params['include_costant'])\n", 141 | "\n", 142 | "# sequential thresholding parameters\n", 143 | "params['sequential_thresholding'] = False\n", 144 | "params['coefficient_threshold'] = 0.1\n", 145 | "params['threshold_frequency'] = 500\n", 146 | "params['coefficient_mask'] = np.ones((params['library_dim'], params['latent_dim']))\n", 147 | "params['coefficient_initialization'] = 'constant'\n", 148 | "\n", 149 | "# loss function weighting\n", 150 | "params['loss_weight_decoder'] = 1.0\n", 151 | "params['loss_weight_sindy_x'] = 1e-1\n", 152 | "params['loss_weight_sindy_z'] = 1e-1\n", 153 | "params['loss_weight_sindy_regularization'] = 0\n", 154 | "params['diff'] = 'symb' # 'symb': symbolic diff (only for fully connected Autoencoder), 'auto': automatic diff\n", 155 | "params['activation'] = 'sigmoid'\n", 156 | "params['widths'] = [100]\n", 157 | "\n", 158 | "# training parameters\n", 159 | "params['epoch_size'] = train_data['data'][0]['x'].shape[0]\n", 160 | "params['batch_size'] = 100\n", 161 | "params['learning_rate'] = 1e-3\n", 162 | "\n", 163 | "params['fig_path'] = os.getcwd() + '/fig/test_errTol/'\n", 164 | "if not os.path.exists(params['fig_path']):\n", 165 | " os.makedirs(params['fig_path'])\n", 166 | "params['print_progress'] = True\n", 167 | "params['print_frequency'] = 100\n", 168 | "params['save_frequency'] = 100\n", 169 | "\n", 170 | "# training epochs\n", 171 | "params['max_epochs'] = 100000 # max number of training epochs\n", 172 | "\n", 173 | "# Greedy sampling algorithm\n", 174 | "params['update_epoch'] = 2000 # Greedy sampling frequency\n", 175 | "params['tol'] = 0.001 # initial tolerance of the maximum error indicator in the parameter space; it will be updated during training using the prescribed `adaptive` method\n", 176 | "params['tol2'] = 2 # initial tolerance of the maximum relative error in the parameter space\n", 177 | "params['sindy_max'] = None # max number of local DIs; if tolerance is used as a termination criterior, set it as None\n", 178 | "params['convex_knn'] = 1 # the number nearest local DIs used for convex interpolation during Greedy sampling\n", 179 | "params['test_data'] = test_data # dataset of the discrete parameter space\n", 180 | "params['test_param'] = np.hstack((amp_test.reshape(-1,1), width_test.reshape(-1,1))) # parameters of the discrete parameter space\n", 181 | "params['num_test'] = num_test # the number of parameter cases of the discrete parameter space\n", 182 | "params['coeff_exist'] = False # whether to initialize model coefficients with pescribed values, set as False\n", 183 | "params['retrain'] = False # whether to retrain the model; set as False for training a new model\n", 184 | "\n", 185 | "# Error indicator:\n", 186 | "# 1: max relative error (if test data is available); \n", 187 | "# 2: residual norm for 1D Burgers eqn; \n", 188 | "# 3: residual norm for 2D Burgers eqn; \n", 189 | "# 4: residual norm for time dependent heat conduction (MFEM example 16); \n", 190 | "# 5: residual norm for radial advection (MFEM example 9)\n", 191 | "params['err_type'] = 2 \n", 192 | "params['subsize'] = int(0.5 * num_test) # initial random subset size, the number of randomly selected cases for Greedy sampling\n", 193 | "params['subsize_max'] = 80 # maximum random subset size in percentage\n", 194 | "\n", 195 | "# Adaptive approach for tol of error indicator:\n", 196 | "# 'mean': use mean ratios between error indicator and max relative errors\n", 197 | "# 'reg_mean': use linear regression line\n", 198 | "# 'reg_max': use linear regression line shifted by std to upper bound\n", 199 | "# 'reg_min': use linear regression line shifted by std to lower bound, more conservative\n", 200 | "params['adaptive'] = 'reg_max' \n", 201 | "\n", 202 | "# PDE parameters\n", 203 | "params['pde'] = {}\n", 204 | "params['pde']['nx'] = nx\n", 205 | "params['pde']['nt'] = nt\n", 206 | "params['pde']['tstop'] = tstop" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "if params['retrain']:\n", 216 | " save_name = 'burger_2021_11_26_10_02_50'\n", 217 | " params = pickle.load(open(params['fig_path'] + save_name + '_params.pkl', 'rb'))\n", 218 | " params['retrain'] = True\n", 219 | " params['coeff_exist'] = True # flag to indicate whether to initialize model coefficients with pescribed values\n", 220 | " params['save_name'] = save_name\n", 221 | " params['max_epochs'] = 5000\n", 222 | " params['update_epoch'] = 5000\n", 223 | " params['save_frequency'] = 1000\n", 224 | " \n", 225 | " for i in params['train_idx'][4:]:\n", 226 | " train_data['data'].append(test_data['data'][i])\n", 227 | " train_data['param'].append(test_data['param'][i])" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "## Training" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": { 241 | "collapsed": true, 242 | "jupyter": { 243 | "outputs_hidden": true 244 | }, 245 | "tags": [] 246 | }, 247 | "outputs": [], 248 | "source": [ 249 | "df = pd.DataFrame()\n", 250 | "timer = []\n", 251 | "timer.append(time())\n", 252 | "\n", 253 | "if not params['retrain']:\n", 254 | " params['save_name'] = 'burger_' + datetime.datetime.now().strftime(\"%Y_%m_%d_%H_%M_%S\")\n", 255 | "tf.reset_default_graph()\n", 256 | "results_dict = train_network(train_data, params)\n", 257 | "df = df.append({**results_dict, **params}, ignore_index=True)\n", 258 | " \n", 259 | "timer.append(time())\n", 260 | "print(f'training time: {(timer[-1]-timer[0])/60:.2f} mins, {(timer[-1]-timer[0])/3600:.2f} hours')" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [ 269 | "# history of validation loss\n", 270 | "train_loss = np.array(df['training_losses'][0]).squeeze()\n", 271 | "test_loss = np.array(df['testing_losses'][0]).squeeze()\n", 272 | "\n", 273 | "fig, ax1 = plt.subplots(figsize=(9,5))\n", 274 | "xt = np.linspace(1,df['epoch_count'],train_loss.shape[0])\n", 275 | "ax1.plot(xt, train_loss[:,0], 'r', label='Train')\n", 276 | "ax1.set_yscale('log')\n", 277 | "ax1.xaxis.set_major_formatter(FormatStrFormatter('%.f'))\n", 278 | "ax1.set_xlabel('Epochs', fontsize=16)\n", 279 | "ax1.set_ylabel('Loss', color='r', fontsize=16)\n", 280 | "ax1.set_xlim(0, params['epoch_count'])\n", 281 | "ax1.tick_params(axis='x', labelsize=14)\n", 282 | "ax1.tick_params(axis='y', labelsize=16)\n", 283 | "ax1.tick_params(axis='y', labelcolor='r')\n", 284 | "\n", 285 | "ax2 = ax1.twinx()\n", 286 | "xt = np.linspace(1,df['epoch_count'],test_loss.shape[0])\n", 287 | "ax2.plot(xt, test_loss, 'b-o', label='Val')\n", 288 | "ax2.set_ylabel('Max Error', color='b', fontsize=16)\n", 289 | "ax2.tick_params(axis='both', labelsize=16)\n", 290 | "ax2.tick_params(axis='y', labelcolor='b')\n", 291 | "\n", 292 | "plt.grid()\n", 293 | "plt.tight_layout()\n", 294 | "plt.savefig(f\"{params['fig_path']}/loss.png\")" 295 | ] 296 | } 297 | ], 298 | "metadata": { 299 | "kernelspec": { 300 | "display_name": "tfvenv", 301 | "language": "python", 302 | "name": "tfvenv" 303 | }, 304 | "language_info": { 305 | "codemirror_mode": { 306 | "name": "ipython", 307 | "version": 3 308 | }, 309 | "file_extension": ".py", 310 | "mimetype": "text/x-python", 311 | "name": "python", 312 | "nbconvert_exporter": "python", 313 | "pygments_lexer": "ipython3", 314 | "version": "3.7.10" 315 | }, 316 | "toc": { 317 | "base_numbering": 1, 318 | "nav_menu": {}, 319 | "number_sections": true, 320 | "sideBar": true, 321 | "skip_h1_title": false, 322 | "title_cell": "Table of Contents", 323 | "title_sidebar": "Contents", 324 | "toc_cell": false, 325 | "toc_position": {}, 326 | "toc_section_display": true, 327 | "toc_window_display": false 328 | }, 329 | "varInspector": { 330 | "cols": { 331 | "lenName": 16, 332 | "lenType": 16, 333 | "lenVar": 40 334 | }, 335 | "kernels_config": { 336 | "python": { 337 | "delete_cmd_postfix": "", 338 | "delete_cmd_prefix": "del ", 339 | "library": "var_list.py", 340 | "varRefreshCmd": "print(var_dic_list())" 341 | }, 342 | "r": { 343 | "delete_cmd_postfix": ") ", 344 | "delete_cmd_prefix": "rm(", 345 | "library": "var_list.r", 346 | "varRefreshCmd": "cat(var_dic_list()) " 347 | } 348 | }, 349 | "types_to_exclude": [ 350 | "module", 351 | "function", 352 | "builtin_function_or_method", 353 | "instance", 354 | "_Feature" 355 | ], 356 | "window_display": false 357 | } 358 | }, 359 | "nbformat": 4, 360 | "nbformat_minor": 4 361 | } 362 | -------------------------------------------------------------------------------- /examples/MFEMex16/train_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.append(\"../../src\")\n", 11 | "import os\n", 12 | "import datetime\n", 13 | "import pandas as pd\n", 14 | "import numpy as np\n", 15 | "from sindy_utils import library_size\n", 16 | "from training import train_network\n", 17 | "from error_utils import *\n", 18 | "import tensorflow.compat.v1 as tf\n", 19 | "tf.compat.v1.disable_v2_behavior()\n", 20 | "from time import time\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "import pickle \n", 23 | "import subprocess as sp\n", 24 | "from matplotlib.ticker import FormatStrFormatter" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "def get_gpu_memory():\n", 34 | " _output_to_list = lambda x: x.decode('ascii').split('\\n')[:-1]\n", 35 | "\n", 36 | " ACCEPTABLE_AVAILABLE_MEMORY = 1024\n", 37 | " COMMAND = \"nvidia-smi --query-gpu=memory.free --format=csv\"\n", 38 | " memory_free_info = _output_to_list(sp.check_output(COMMAND.split()))[1:]\n", 39 | " memory_free_values = [int(x.split()[0]) for i, x in enumerate(memory_free_info)]\n", 40 | " return memory_free_values\n", 41 | "\n", 42 | "device_list = tf.config.list_physical_devices('GPU')\n", 43 | "free_mem = get_gpu_memory()\n", 44 | "for i,gpu in enumerate(device_list):\n", 45 | " print(f'{gpu}: free memory: {free_mem[i]}')" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# specify which GPU to use\n", 55 | "config = tf.ConfigProto(log_device_placement=False, gpu_options=tf.GPUOptions(allow_growth=True,visible_device_list='1'))" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Load data" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "# load initial training dataset\n", 72 | "p1_train = np.linspace(4, 4.3, 2)\n", 73 | "p2_train = np.linspace(1, 1.4, 2)\n", 74 | "x0 = 0.5 # center coordinate of initial condition\n", 75 | "num_train = p1_train.size * p2_train.size # number of training cases\n", 76 | "tstop = 0.3\n", 77 | "dt = 5e-3\n", 78 | "data_path = './data/'\n", 79 | "train_data = pickle.load(open(data_path + f\"/local{num_train}_tstop{tstop:.1f}c.p\", \"rb\"))\n", 80 | "num_sindy = len(train_data['data'])\n", 81 | "input_dim = train_data['data'][0]['x'].shape[1]\n", 82 | "\n", 83 | "for i in range(num_sindy):\n", 84 | " print(f\"case {i}: params: {train_data['param'][i]}, x shape: {train_data['data'][i]['x'].shape}\")\n", 85 | "\n", 86 | "\n", 87 | "# load dataset of discrete parameter space for greedy sampling\n", 88 | "p1_test = np.linspace(4, 4.3, 21)\n", 89 | "p2_test = np.linspace(1, 1.4, 21)\n", 90 | "num_test = p1_test.size * p2_test.size\n", 91 | "test_data = pickle.load(open(data_path + f\"/local{num_test}_tstop{tstop:.1f}c.p\", \"rb\"))" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "grid1, grid2 = np.meshgrid(p1_train, p2_train)\n", 101 | "train_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 102 | "grid1, grid2 = np.meshgrid(p1_test, p2_test)\n", 103 | "test_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 104 | "\n", 105 | "train_idx = []\n", 106 | "for i in range(num_test):\n", 107 | " for j in range(num_train):\n", 108 | " if np.abs(test_param[i,0]-train_param[j,0]) < 1e-8 and \\\n", 109 | " np.abs(test_param[i,1]-train_param[j,1]) < 1e-8:\n", 110 | " train_idx.append(i)\n", 111 | "print(train_idx)" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "metadata": {}, 117 | "source": [ 118 | "## Set up model and training parameters" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "params = {}\n", 128 | "\n", 129 | "params['seed'] = 1 # random seed\n", 130 | "params['config'] = config\n", 131 | "params['num_sindy'] = num_sindy\n", 132 | "params['param'] = train_data['param']\n", 133 | "params['train_idx'] = train_idx\n", 134 | "params['input_dim'] = input_dim\n", 135 | "params['latent_dim'] = 3\n", 136 | "params['model_order'] = 1\n", 137 | "params['poly_order'] = 1\n", 138 | "params['include_sine'] = False\n", 139 | "params['include_cosine'] = False\n", 140 | "params['include_costant'] = True\n", 141 | "params['library_dim'] = library_size(params['latent_dim'], params['poly_order'], \n", 142 | " params['include_sine'], params['include_cosine'], \n", 143 | " params['include_costant'])\n", 144 | "\n", 145 | "# sequential thresholding parameters\n", 146 | "params['sequential_thresholding'] = False\n", 147 | "params['coefficient_threshold'] = 0.1\n", 148 | "params['threshold_frequency'] = 500\n", 149 | "params['coefficient_mask'] = np.ones((params['library_dim'], params['latent_dim']))\n", 150 | "params['coefficient_initialization'] = 'constant'\n", 151 | "\n", 152 | "# loss function weighting\n", 153 | "params['loss_weight_decoder'] = 1.0\n", 154 | "params['loss_weight_sindy_x'] = 1e-4\n", 155 | "params['loss_weight_sindy_z'] = 1e-4\n", 156 | "params['loss_weight_sindy_regularization'] = 0\n", 157 | "params['diff'] = 'symb' # 'symb': symbolic diff (only for fully connected Autoencoder), 'auto': automatic diff\n", 158 | "params['activation'] = 'sigmoid'\n", 159 | "params['widths'] = [100]\n", 160 | "\n", 161 | "# training parameters\n", 162 | "params['epoch_size'] = train_data['data'][0]['x'].shape[0]\n", 163 | "params['batch_size'] = train_data['data'][0]['x'].shape[0]\n", 164 | "params['learning_rate'] = 5e-4\n", 165 | "\n", 166 | "params['fig_path'] = os.getcwd() + '/fig/test/'\n", 167 | "if not os.path.exists(params['fig_path']):\n", 168 | " os.makedirs(params['fig_path'])\n", 169 | "params['print_progress'] = True\n", 170 | "params['print_frequency'] = 100\n", 171 | "params['save_frequency'] = 100\n", 172 | "\n", 173 | "# training epochs\n", 174 | "params['max_epochs'] = 3000000 # max number of training epochs\n", 175 | "\n", 176 | "# Greedy sampling algorithm\n", 177 | "params['update_epoch'] = 100000 # Greedy sampling frequency\n", 178 | "params['tol'] = 0.001 # initial tolerance of the maximum error indicator in the parameter space; it will be updated during training using the prescribed `adaptive` method\n", 179 | "params['tol2'] = 2 # initial tolerance of the maximum relative error in the parameter space\n", 180 | "params['sindy_max'] = 25 # max number of local DIs; if tolerance is used as a termination criterior, set it as None\n", 181 | "params['convex_knn'] = 1 # the number nearest local DIs used for convex interpolation during Greedy sampling\n", 182 | "params['test_data'] = test_data # dataset of the discrete parameter space\n", 183 | "params['test_param'] = np.hstack((p1_test.reshape(-1,1), p2_test.reshape(-1,1))) # parameters of the discrete parameter space\n", 184 | "params['num_test'] = num_test # the number of parameter cases of the discrete parameter space\n", 185 | "params['coeff_exist'] = False # whether to initialize model coefficients with pescribed values, set as False\n", 186 | "params['retrain'] = True # whether to retrain the model; set as False for training a new model\n", 187 | "\n", 188 | "# Error indicator:\n", 189 | "# 1: max relative error (if test data is available); \n", 190 | "# 2: residual norm for 1D Burgers eqn; \n", 191 | "# 3: residual norm for 2D Burgers eqn; \n", 192 | "# 4: residual norm for time dependent heat conduction (MFEM example 16); \n", 193 | "# 5: residual norm for radial advection (MFEM example 9)\n", 194 | "params['err_type'] = 4 \n", 195 | "params['subsize'] = int(0.3 * num_test) # initial random subset size, the number of randomly selected cases for Greedy sampling\n", 196 | "params['subsize_max'] = 50 # maximum random subset size in percentage\n", 197 | "\n", 198 | "# Adaptive approach for tol of error indicator:\n", 199 | "# 'mean': use mean ratios between error indicator and max relative errors\n", 200 | "# 'reg_mean': use linear regression line\n", 201 | "# 'reg_max': use linear regression line shifted by std to upper bound\n", 202 | "# 'reg_min': use linear regression line shifted by std to lower bound, more conservative\n", 203 | "params['adaptive'] = 'reg_max' \n", 204 | "\n", 205 | "# PDE parameters\n", 206 | "params['pde'] = {}\n", 207 | "params['pde']['exe_file'] = '../../src/ex16'\n", 208 | "params['pde']['m_file'] = './ref-square.mesh'\n", 209 | "params['pde']['u_file'] = './ex16-u_pred1.gf'\n", 210 | "params['pde']['res_file'] = \"./ex16-residual1.gf\"\n", 211 | "params['pde']['rl'] = 5\n", 212 | "params['pde']['order'] = 1\n", 213 | "params['pde']['ODEsolver'] = 1 # 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3\n", 214 | "params['pde']['tstop'] = tstop\n", 215 | "params['pde']['dt'] = dt\n", 216 | "params['pde']['x1'] = x0\n", 217 | "params['pde']['x2'] = x0\n", 218 | "params['pde']['res_ns'] = 0.1 # percentage of time steps for residual evaluation\n", 219 | "params['pde']['Tmax_iter'] = 30 # max number of CG iterations in Tsolver" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "if params['retrain']:\n", 229 | " save_name = 'ex16_2023_05_23_00_08_40'\n", 230 | " params = pickle.load(open(params['fig_path'] + save_name + '_params.pkl', 'rb'))\n", 231 | " params['retrain'] = True\n", 232 | " params['coeff_exist'] = True # flag to indicate whether to initialize model coefficients with pescribed values\n", 233 | " params['save_name'] = save_name\n", 234 | " params['max_epochs'] = 3000000\n", 235 | " params['update_epoch'] = 100000 # update training set for every 2000 epochs\n", 236 | " params['save_frequency'] = 5000\n", 237 | " \n", 238 | " for i in params['train_idx'][4:]:\n", 239 | " train_data['data'].append(test_data['data'][i])\n", 240 | " train_data['param'].append(test_data['param'][i])" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": { 246 | "tags": [] 247 | }, 248 | "source": [ 249 | "## Training" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": { 256 | "tags": [] 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "df = pd.DataFrame()\n", 261 | "timer = []\n", 262 | "timer.append(time())\n", 263 | "\n", 264 | "if not params['retrain']:\n", 265 | " params['save_name'] = 'ex16_' + datetime.datetime.now().strftime(\"%Y_%m_%d_%H_%M_%S\")\n", 266 | "tf.reset_default_graph()\n", 267 | "results_dict = train_network(train_data, params)\n", 268 | "df = df.append({**results_dict, **params}, ignore_index=True)\n", 269 | " \n", 270 | "timer.append(time())\n", 271 | "print(f'training time: {(timer[-1]-timer[0])/60:.2f} mins, {(timer[-1]-timer[0])/3600:.2f} hours')" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": null, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "# history of validation loss\n", 281 | "train_loss = np.array(df['training_losses'][0]).squeeze()\n", 282 | "test_loss = np.array(df['testing_losses'][0]).squeeze()\n", 283 | "\n", 284 | "fig, ax1 = plt.subplots(figsize=(9,5))\n", 285 | "xt = np.linspace(1,df['num_epochs'][0],train_loss.shape[0])\n", 286 | "ax1.plot(xt, train_loss[:,0], 'r', label='Train')\n", 287 | "ax1.set_yscale('log')\n", 288 | "ax1.xaxis.set_major_formatter(FormatStrFormatter('%.f'))\n", 289 | "ax1.set_xlabel('Epochs', fontsize=16)\n", 290 | "ax1.set_ylabel('Loss', color='r', fontsize=16)\n", 291 | "ax1.set_xlim(0, df['num_epochs'][0])\n", 292 | "ax1.tick_params(axis='x', labelsize=14)\n", 293 | "ax1.tick_params(axis='y', labelsize=16)\n", 294 | "ax1.tick_params(axis='y', labelcolor='r')\n", 295 | "\n", 296 | "ax2 = ax1.twinx()\n", 297 | "xt = np.linspace(1,df['num_epochs'],test_loss.shape[0])\n", 298 | "ax2.plot(xt, test_loss, 'b-o', label='Val')\n", 299 | "ax2.set_ylabel('Max Error', color='b', fontsize=16)\n", 300 | "ax2.tick_params(axis='both', labelsize=16)\n", 301 | "ax2.tick_params(axis='y', labelcolor='b')\n", 302 | "\n", 303 | "plt.grid()\n", 304 | "plt.tight_layout()\n", 305 | "plt.savefig(f\"{params['fig_path']}/loss.png\")" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "# remove *.gf temporary files for calculation of residual-based error indicator\n", 315 | "import glob\n", 316 | "files = glob.glob('*.gf')\n", 317 | "subprocess.call(['rm', '-r'] + files)" 318 | ] 319 | } 320 | ], 321 | "metadata": { 322 | "kernelspec": { 323 | "display_name": "tfvenv", 324 | "language": "python", 325 | "name": "tfvenv" 326 | }, 327 | "language_info": { 328 | "codemirror_mode": { 329 | "name": "ipython", 330 | "version": 3 331 | }, 332 | "file_extension": ".py", 333 | "mimetype": "text/x-python", 334 | "name": "python", 335 | "nbconvert_exporter": "python", 336 | "pygments_lexer": "ipython3", 337 | "version": "3.7.10" 338 | }, 339 | "toc": { 340 | "base_numbering": 1, 341 | "nav_menu": {}, 342 | "number_sections": true, 343 | "sideBar": true, 344 | "skip_h1_title": false, 345 | "title_cell": "Table of Contents", 346 | "title_sidebar": "Contents", 347 | "toc_cell": false, 348 | "toc_position": {}, 349 | "toc_section_display": true, 350 | "toc_window_display": false 351 | }, 352 | "varInspector": { 353 | "cols": { 354 | "lenName": 16, 355 | "lenType": 16, 356 | "lenVar": 40 357 | }, 358 | "kernels_config": { 359 | "python": { 360 | "delete_cmd_postfix": "", 361 | "delete_cmd_prefix": "del ", 362 | "library": "var_list.py", 363 | "varRefreshCmd": "print(var_dic_list())" 364 | }, 365 | "r": { 366 | "delete_cmd_postfix": ") ", 367 | "delete_cmd_prefix": "rm(", 368 | "library": "var_list.r", 369 | "varRefreshCmd": "cat(var_dic_list()) " 370 | } 371 | }, 372 | "types_to_exclude": [ 373 | "module", 374 | "function", 375 | "builtin_function_or_method", 376 | "instance", 377 | "_Feature" 378 | ], 379 | "window_display": false 380 | } 381 | }, 382 | "nbformat": 4, 383 | "nbformat_minor": 4 384 | } 385 | -------------------------------------------------------------------------------- /examples/MFEMex9/train_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.append(\"../../src\")\n", 11 | "import os\n", 12 | "import datetime\n", 13 | "import pandas as pd\n", 14 | "import numpy as np\n", 15 | "from sindy_utils import library_size\n", 16 | "from training import train_network\n", 17 | "from error_utils import *\n", 18 | "import tensorflow.compat.v1 as tf\n", 19 | "tf.compat.v1.disable_v2_behavior()\n", 20 | "from time import time\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "import pickle \n", 23 | "import subprocess as sp\n", 24 | "from matplotlib.ticker import FormatStrFormatter" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "def get_gpu_memory():\n", 34 | " _output_to_list = lambda x: x.decode('ascii').split('\\n')[:-1]\n", 35 | "\n", 36 | " ACCEPTABLE_AVAILABLE_MEMORY = 1024\n", 37 | " COMMAND = \"nvidia-smi --query-gpu=memory.free --format=csv\"\n", 38 | " memory_free_info = _output_to_list(sp.check_output(COMMAND.split()))[1:]\n", 39 | " memory_free_values = [int(x.split()[0]) for i, x in enumerate(memory_free_info)]\n", 40 | " return memory_free_values\n", 41 | "\n", 42 | "device_list = tf.config.list_physical_devices('GPU')\n", 43 | "free_mem = get_gpu_memory()\n", 44 | "for i,gpu in enumerate(device_list):\n", 45 | " print(f'{gpu}: free memory: {free_mem[i]}')" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "# specify which GPU to use\n", 55 | "config = tf.ConfigProto(log_device_placement=False, gpu_options=tf.GPUOptions(allow_growth=True,\n", 56 | " visible_device_list='2'))" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "metadata": {}, 62 | "source": [ 63 | "## Load data" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "# load initial training dataset\n", 73 | "p1_train = np.linspace(1.5,2,2) # w1 of initial condition\n", 74 | "p2_train = np.linspace(2,2.5,2) # w2 of initial condition\n", 75 | "num_train = p1_train.size * p2_train.size # initial number of training data\n", 76 | "tstop = 3\n", 77 | "dt = 1e-2\n", 78 | "train_data = pickle.load(open(f\"/g/g92/he10/Research/data/MFEMex9/local{num_train}_tstop{tstop:.1f}.p\", \"rb\"))\n", 79 | "num_sindy = len(train_data['data'])\n", 80 | "input_dim = train_data['data'][0]['x'].shape[1]\n", 81 | "\n", 82 | "for i in range(num_sindy):\n", 83 | " print(f\"case {i}: params: {train_data['param'][i]}, x shape: {train_data['data'][i]['x'].shape}\")\n", 84 | " \n", 85 | "# load dataset of discrete parameter space for greedy sampling\n", 86 | "p1_test = np.linspace(1.5,2,11) # w1 of initial condition\n", 87 | "p2_test = np.linspace(2,2.5,11) # w2 of initial condition\n", 88 | "num_test = p1_test.size * p2_test.size # number of cases in the discrete parameter space\n", 89 | "test_data = pickle.load(open(f\"/g/g92/he10/Research/data/MFEMex9/local{num_test}_tstop{tstop:.1f}.p\", \"rb\"))" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "grid1, grid2 = np.meshgrid(p1_train, p2_train)\n", 99 | "train_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 100 | "grid1, grid2 = np.meshgrid(p1_test, p2_test)\n", 101 | "test_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 102 | "\n", 103 | "train_idx = []\n", 104 | "for i in range(num_test):\n", 105 | " for j in range(num_train):\n", 106 | " if np.abs(test_param[i,0]-train_param[j,0]) < 1e-8 and \\\n", 107 | " np.abs(test_param[i,1]-train_param[j,1]) < 1e-8:\n", 108 | " train_idx.append(i)\n", 109 | "print(train_idx)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "## Set up model and training parameters" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "params = {}\n", 126 | "\n", 127 | "params['seed'] = 1 # random seed\n", 128 | "params['config'] = config\n", 129 | "params['num_sindy'] = num_sindy\n", 130 | "params['param'] = train_data['param']\n", 131 | "params['train_idx'] = train_idx\n", 132 | "params['input_dim'] = input_dim\n", 133 | "params['latent_dim'] = 3\n", 134 | "params['poly_order'] = 1\n", 135 | "params['include_sine'] = False\n", 136 | "params['include_cosine'] = False\n", 137 | "params['include_costant'] = True\n", 138 | "params['library_dim'] = library_size(params['latent_dim'], params['poly_order'], \n", 139 | " params['include_sine'], params['include_cosine'], \n", 140 | " params['include_costant'])\n", 141 | "\n", 142 | "# sequential thresholding parameters\n", 143 | "params['sequential_thresholding'] = False\n", 144 | "params['coefficient_threshold'] = 0.1\n", 145 | "params['threshold_frequency'] = 500\n", 146 | "params['coefficient_mask'] = np.ones((params['library_dim'], params['latent_dim']))\n", 147 | "params['coefficient_initialization'] = 'constant'\n", 148 | "\n", 149 | "# loss function weighting\n", 150 | "params['loss_weight_decoder'] = 1.0\n", 151 | "params['loss_weight_sindy_x'] = 1e-2\n", 152 | "params['loss_weight_sindy_z'] = 1e-2\n", 153 | "params['loss_weight_sindy_regularization'] = 0\n", 154 | "params['diff'] = 'symb' # 'symb': symbolic diff (only for fully connected Autoencoder), 'auto': automatic diff\n", 155 | "params['activation'] = 'sigmoid'\n", 156 | "params['widths'] = [100]\n", 157 | "\n", 158 | "# training parameters\n", 159 | "params['epoch_size'] = train_data['data'][0]['x'].shape[0]\n", 160 | "params['batch_size'] = train_data['data'][0]['x'].shape[0]\n", 161 | "params['learning_rate'] = 1e-3\n", 162 | "\n", 163 | "params['fig_path'] = os.getcwd() + '/fig/test/'\n", 164 | "if not os.path.exists(params['fig_path']):\n", 165 | " os.makedirs(params['fig_path'])\n", 166 | "params['print_progress'] = True\n", 167 | "params['print_frequency'] = 100\n", 168 | "params['save_frequency'] = 500\n", 169 | "\n", 170 | "# training epochs\n", 171 | "params['max_epochs'] = 600000 # max number of training epochs\n", 172 | "\n", 173 | "# Greedy sampling algorithm\n", 174 | "params['update_epoch'] = 20000 # Greedy sampling frequency\n", 175 | "params['tol'] = 0.001 # initial tolerance of the maximum error indicator in the parameter space; it will be updated during training using the prescribed `adaptive` method\n", 176 | "params['tol2'] = 5 # initial tolerance of the maximum relative error in the parameter space\n", 177 | "params['sindy_max'] = 25 # max number of local DIs; if tolerance is used as a termination criterior, set it as None\n", 178 | "params['convex_knn'] = 1 # the number nearest local DIs used for convex interpolation during Greedy sampling\n", 179 | "params['test_data'] = test_data # dataset of the discrete parameter space\n", 180 | "params['test_param'] = np.hstack((p1_test.reshape(-1,1), p2_test.reshape(-1,1))) # parameters of the discrete parameter space\n", 181 | "params['num_test'] = num_test # the number of parameter cases of the discrete parameter space\n", 182 | "params['coeff_exist'] = False # whether to initialize model coefficients with pescribed values, set as False\n", 183 | "params['retrain'] = True # whether to retrain the model; set as False for training a new model\n", 184 | "\n", 185 | "# Error indicator:\n", 186 | "# 1: max relative error (if test data is available); \n", 187 | "# 2: residual norm for 1D Burgers eqn; \n", 188 | "# 3: residual norm for 2D Burgers eqn; \n", 189 | "# 4: residual norm for time dependent heat conduction (MFEM example 16); \n", 190 | "# 5: residual norm for radial advection (MFEM example 9)\n", 191 | "params['err_type'] = 5 \n", 192 | "params['subsize'] = int(0.3 * num_test) # initial random subset size, the number of randomly selected cases for Greedy sampling\n", 193 | "params['subsize_max'] = 50 # maximum random subset size in percentage\n", 194 | "\n", 195 | "# Adaptive approach for tol of error indicator:\n", 196 | "# 'mean': use mean ratios between error indicator and max relative errors\n", 197 | "# 'reg_mean': use linear regression line\n", 198 | "# 'reg_max': use linear regression line shifted by std to upper bound\n", 199 | "# 'reg_min': use linear regression line shifted by std to lower bound, more conservative\n", 200 | "params['adaptive'] = 'reg_max' \n", 201 | "\n", 202 | "# PDE parameters\n", 203 | "params['pde'] = {}\n", 204 | "params['pde']['exe_file'] = '../../src/ex9'\n", 205 | "params['pde']['m_file'] = './periodic-square.mesh'\n", 206 | "params['pde']['u_file'] = './ex9-u_pred1.gf'\n", 207 | "params['pde']['res_file'] = \"./ex9-residual1.gf\"\n", 208 | "params['pde']['prob'] = 3\n", 209 | "params['pde']['rl'] = 4\n", 210 | "params['pde']['order'] = 1\n", 211 | "params['pde']['tstop'] = tstop\n", 212 | "params['pde']['dt'] = dt\n", 213 | "params['pde']['res_ns'] = 0.1 # percentage of time steps for residual evaluation\n", 214 | "params['pde']['Mmax_iter'] = 20 # max iterations in Tsolver" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "if params['retrain']:\n", 224 | " save_name = 'ex9_2022_11_20_16_45_41'\n", 225 | " params = pickle.load(open(params['fig_path'] + save_name + '_params.pkl', 'rb'))\n", 226 | " params['retrain'] = True\n", 227 | " params['coeff_exist'] = True # flag to indicate whether to initialize model coefficients with pescribed values\n", 228 | " params['save_name'] = save_name\n", 229 | " params['max_epochs'] = 600000\n", 230 | " params['update_epoch'] = 20000\n", 231 | " params['save_frequency'] = 500\n", 232 | " \n", 233 | " for i in params['train_idx'][4:]:\n", 234 | " train_data['data'].append(test_data['data'][i])\n", 235 | " train_data['param'].append(test_data['param'][i])" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": { 241 | "tags": [] 242 | }, 243 | "source": [ 244 | "## Training" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": { 251 | "collapsed": true, 252 | "jupyter": { 253 | "outputs_hidden": true 254 | }, 255 | "tags": [] 256 | }, 257 | "outputs": [], 258 | "source": [ 259 | "df = pd.DataFrame()\n", 260 | "timer = []\n", 261 | "timer.append(time())\n", 262 | "\n", 263 | "if not params['retrain']:\n", 264 | " params['save_name'] = 'ex9_' + datetime.datetime.now().strftime(\"%Y_%m_%d_%H_%M_%S\")\n", 265 | "tf.reset_default_graph()\n", 266 | "results_dict = train_network(train_data, params)\n", 267 | "df = df.append({**results_dict, **params}, ignore_index=True)\n", 268 | " \n", 269 | "timer.append(time())\n", 270 | "print(f'training time: {(timer[-1]-timer[0])/60:.2f} mins, {(timer[-1]-timer[0])/3600:.2f} hours')" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "metadata": {}, 277 | "outputs": [], 278 | "source": [ 279 | "# history of validation loss\n", 280 | "train_loss = np.array(df['training_losses'][0]).squeeze()\n", 281 | "test_loss = np.array(df['testing_losses'][0]).squeeze()\n", 282 | "\n", 283 | "fig, ax1 = plt.subplots(figsize=(9,5))\n", 284 | "xt = np.linspace(1,df['num_epochs'][0],train_loss.shape[0])\n", 285 | "ax1.plot(xt, train_loss[:,0], 'r', label='Train')\n", 286 | "ax1.set_yscale('log')\n", 287 | "ax1.xaxis.set_major_formatter(FormatStrFormatter('%.f'))\n", 288 | "ax1.set_xlabel('Epochs', fontsize=16)\n", 289 | "ax1.set_ylabel('Loss', color='r', fontsize=16)\n", 290 | "ax1.set_xlim(0, df['num_epochs'][0])\n", 291 | "ax1.tick_params(axis='x', labelsize=14)\n", 292 | "ax1.tick_params(axis='y', labelsize=16)\n", 293 | "ax1.tick_params(axis='y', labelcolor='r')\n", 294 | "\n", 295 | "ax2 = ax1.twinx()\n", 296 | "xt = np.linspace(1,df['num_epochs'],test_loss.shape[0])\n", 297 | "ax2.plot(xt, test_loss, 'b-o', label='Val')\n", 298 | "ax2.set_ylabel('Max Error', color='b', fontsize=16)\n", 299 | "ax2.tick_params(axis='both', labelsize=16)\n", 300 | "ax2.tick_params(axis='y', labelcolor='b')\n", 301 | "\n", 302 | "plt.grid()\n", 303 | "plt.tight_layout()\n", 304 | "plt.savefig(f\"{params['fig_path']}/loss.png\")" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": null, 310 | "metadata": {}, 311 | "outputs": [], 312 | "source": [ 313 | "# remove *.gf temporary files for calculation of residual-based error indicator\n", 314 | "import glob\n", 315 | "files = glob.glob('*.gf')\n", 316 | "subprocess.call(['rm', '-r'] + files)" 317 | ] 318 | } 319 | ], 320 | "metadata": { 321 | "kernelspec": { 322 | "display_name": "tfvenv", 323 | "language": "python", 324 | "name": "tfvenv" 325 | }, 326 | "language_info": { 327 | "codemirror_mode": { 328 | "name": "ipython", 329 | "version": 3 330 | }, 331 | "file_extension": ".py", 332 | "mimetype": "text/x-python", 333 | "name": "python", 334 | "nbconvert_exporter": "python", 335 | "pygments_lexer": "ipython3", 336 | "version": "3.7.10" 337 | }, 338 | "toc": { 339 | "base_numbering": 1, 340 | "nav_menu": {}, 341 | "number_sections": true, 342 | "sideBar": true, 343 | "skip_h1_title": false, 344 | "title_cell": "Table of Contents", 345 | "title_sidebar": "Contents", 346 | "toc_cell": false, 347 | "toc_position": {}, 348 | "toc_section_display": true, 349 | "toc_window_display": false 350 | }, 351 | "varInspector": { 352 | "cols": { 353 | "lenName": 16, 354 | "lenType": 16, 355 | "lenVar": 40 356 | }, 357 | "kernels_config": { 358 | "python": { 359 | "delete_cmd_postfix": "", 360 | "delete_cmd_prefix": "del ", 361 | "library": "var_list.py", 362 | "varRefreshCmd": "print(var_dic_list())" 363 | }, 364 | "r": { 365 | "delete_cmd_postfix": ") ", 366 | "delete_cmd_prefix": "rm(", 367 | "library": "var_list.r", 368 | "varRefreshCmd": "cat(var_dic_list()) " 369 | } 370 | }, 371 | "types_to_exclude": [ 372 | "module", 373 | "function", 374 | "builtin_function_or_method", 375 | "instance", 376 | "_Feature" 377 | ], 378 | "window_display": false 379 | } 380 | }, 381 | "nbformat": 4, 382 | "nbformat_minor": 4 383 | } 384 | -------------------------------------------------------------------------------- /examples/2DBurgerEqn/train_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.append(\"../../src\")\n", 11 | "import os\n", 12 | "import datetime\n", 13 | "import pandas as pd\n", 14 | "import numpy as np\n", 15 | "from sindy_utils import library_size\n", 16 | "from training import train_network\n", 17 | "import tensorflow.compat.v1 as tf\n", 18 | "tf.compat.v1.disable_v2_behavior()\n", 19 | "from time import time\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "from matplotlib.ticker import FormatStrFormatter\n", 22 | "import pickle \n", 23 | "import subprocess as sp" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "def get_gpu_memory():\n", 33 | " _output_to_list = lambda x: x.decode('ascii').split('\\n')[:-1]\n", 34 | "\n", 35 | " ACCEPTABLE_AVAILABLE_MEMORY = 1024\n", 36 | " COMMAND = \"nvidia-smi --query-gpu=memory.free --format=csv\"\n", 37 | " memory_free_info = _output_to_list(sp.check_output(COMMAND.split()))[1:]\n", 38 | " memory_free_values = [int(x.split()[0]) for i, x in enumerate(memory_free_info)]\n", 39 | " return memory_free_values\n", 40 | "\n", 41 | "device_list = tf.config.list_physical_devices('GPU')\n", 42 | "free_mem = get_gpu_memory()\n", 43 | "for i,gpu in enumerate(device_list):\n", 44 | " print(f'{gpu}: free memory: {free_mem[i]}')" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "# specify which GPU to use\n", 54 | "config = tf.ConfigProto(log_device_placement=False, gpu_options=tf.GPUOptions(allow_growth=True,\n", 55 | " visible_device_list='1'))" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Load data" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "def preprocess_data(data, vel):\n", 72 | " for i in range(len(data['data'])):\n", 73 | " if vel == 1:\n", 74 | " data['data'][i]['x'] = data['data'][i].pop('u')\n", 75 | " data['data'][i]['dx'] = data['data'][i].pop('du')\n", 76 | " data['data'][i].pop('v')\n", 77 | " data['data'][i].pop('dv')\n", 78 | " elif vel == 2:\n", 79 | " data['data'][i]['x'] = data['data'][i].pop('v')\n", 80 | " data['data'][i]['dx'] = data['data'][i].pop('dv')\n", 81 | " data['data'][i].pop('u')\n", 82 | " data['data'][i].pop('du')\n", 83 | " elif vel == 3:\n", 84 | " data['data'][i]['x'] = np.hstack((data['data'][i]['u'], data['data'][i]['v']))\n", 85 | " data['data'][i]['dx'] = np.hstack((data['data'][i]['du'], data['data'][i]['dv']))\n", 86 | " return data" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "# load initial training dataset\n", 96 | "vel = 3 # 1: u; 2: v; 3: u and v\n", 97 | "option = 2 # 1: mean=0, std=1; 2: max=1; 3: [0,1]\n", 98 | "Re = 10000\n", 99 | "ic = 2 # initial condition, 1: Sine; 2: Gaussian\n", 100 | "nx = 60\n", 101 | "ny = nx\n", 102 | "nt = 200\n", 103 | "tstop = 1\n", 104 | "amp_train = np.linspace(0.7, 0.9, 2)\n", 105 | "width_train = np.linspace(0.9, 1.1, 2)\n", 106 | "num_train = amp_train.size * width_train.size # initial number of training data\n", 107 | "train_data = pickle.load(open(f\"/g/g92/he10/Research/data/2DBurgerEqn/local{num_train}_Re{Re}_tstop{tstop:.1f}_nt{nt}_nx{nx}.p\", \"rb\"))\n", 108 | "train_data = preprocess_data(train_data, vel)\n", 109 | "num_sindy = len(train_data['data'])\n", 110 | "input_dim = train_data['data'][0]['x'].shape[1]\n", 111 | "\n", 112 | "for i in range(num_sindy):\n", 113 | " print(f\"train case {i}: params: {train_data['param'][i]}, x shape: {train_data['data'][i]['x'].shape}\")\n", 114 | "\n", 115 | "# load dataset of discrete parameter space for greedy sampling\n", 116 | "amp_test = np.linspace(0.7, 0.9, 21)\n", 117 | "width_test = np.linspace(0.9, 1.1, 21)\n", 118 | "num_test = amp_test.size * width_test.size # number of cases in the discrete parameter space\n", 119 | "test_data = pickle.load(open(f\"/g/g92/he10/Research/data/2DBurgerEqn/local{num_test}_Re{Re}_tstop{tstop:.1f}_nt{nt}_nx{nx}.p\", \"rb\"))\n", 120 | "test_data = preprocess_data(test_data, vel)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "grid1, grid2 = np.meshgrid(amp_train, width_train)\n", 130 | "train_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 131 | "grid1, grid2 = np.meshgrid(amp_test, width_test)\n", 132 | "test_param = np.hstack((grid1.flatten().reshape(-1,1), grid2.flatten().reshape(-1,1)))\n", 133 | "\n", 134 | "train_idx = []\n", 135 | "for i in range(num_test):\n", 136 | " for j in range(num_train):\n", 137 | " if np.abs(test_param[i,0]-train_param[j,0]) < 1e-8 and \\\n", 138 | " np.abs(test_param[i,1]-train_param[j,1]) < 1e-8:\n", 139 | " train_idx.append(i)\n", 140 | "print(train_idx)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "## Set up model and training parameters" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "params = {}\n", 157 | "\n", 158 | "params['seed'] = 1 # random seed\n", 159 | "params['config'] = config\n", 160 | "params['num_sindy'] = num_sindy\n", 161 | "params['param'] = train_data['param']\n", 162 | "params['train_idx'] = train_idx\n", 163 | "params['input_dim'] = input_dim\n", 164 | "params['latent_dim'] = 5\n", 165 | "params['poly_order'] = 2\n", 166 | "params['include_sine'] = False\n", 167 | "params['include_cosine'] = False\n", 168 | "params['include_costant'] = True\n", 169 | "params['library_dim'] = library_size(params['latent_dim'], params['poly_order'], \n", 170 | " params['include_sine'], params['include_cosine'], \n", 171 | " params['include_costant'])\n", 172 | "\n", 173 | "# sequential thresholding parameters\n", 174 | "params['sequential_thresholding'] = False\n", 175 | "params['coefficient_threshold'] = 0.1\n", 176 | "params['threshold_frequency'] = 500\n", 177 | "params['coefficient_mask'] = np.ones((params['library_dim'], params['latent_dim']))\n", 178 | "params['coefficient_initialization'] = 'constant'\n", 179 | "\n", 180 | "# loss function weighting\n", 181 | "params['loss_weight_decoder'] = 1.0\n", 182 | "params['loss_weight_sindy_x'] = 1e0\n", 183 | "params['loss_weight_sindy_z'] = 1e-1\n", 184 | "params['loss_weight_sindy_regularization'] = 0\n", 185 | "params['diff'] = 'symb' # 'symb': symbolic diff (only for fully connected Autoencoder), 'auto': automatic diff\n", 186 | "params['activation'] = 'sigmoid'\n", 187 | "params['widths'] = [100]\n", 188 | "\n", 189 | "# training parameters\n", 190 | "params['epoch_size'] = train_data['data'][0]['x'].shape[0]\n", 191 | "params['batch_size'] = 100\n", 192 | "params['learning_rate'] = 1e-4\n", 193 | "\n", 194 | "params['fig_path'] = os.getcwd() + '/fig/test/'\n", 195 | "if not os.path.exists(params['fig_path']):\n", 196 | " os.makedirs(params['fig_path'])\n", 197 | "params['print_progress'] = True\n", 198 | "params['print_frequency'] = 100\n", 199 | "params['save_frequency'] = 1000\n", 200 | "\n", 201 | "# training epochs\n", 202 | "params['max_epochs'] = 600000\n", 203 | "\n", 204 | "# Greedy sampling algorithm\n", 205 | "params['update_epoch'] = 20000 # Greedy sampling frequency\n", 206 | "params['tol'] = 0.02 # initial tolerance of the maximum error indicator in the parameter space; it will be updated during training using the prescribed `adaptive` method\n", 207 | "params['tol2'] = 5 # initial tolerance of the maximum relative error in the parameter space\n", 208 | "params['sindy_max'] = 36 # max number of local DIs; if tolerance is used as a termination criterior, set it as None\n", 209 | "params['convex_knn'] = 1 # the number nearest local DIs used for convex interpolation if used\n", 210 | "params['test_data'] = test_data # dataset of the discrete parameter space\n", 211 | "params['test_param'] = np.hstack((amp_test.reshape(-1,1), width_test.reshape(-1,1))) # parameters of the discrete parameter space\n", 212 | "params['num_test'] = num_test # the number of parameter cases of the discrete parameter space\n", 213 | "params['coeff_exist'] = False # whether to initialize model coefficients with pescribed values, set as False\n", 214 | "params['retrain'] = True # whether to retrain the model; set as False for training a new model\n", 215 | "\n", 216 | "# Error indicator:\n", 217 | "# 1: max relative error (if test data is available); \n", 218 | "# 2: residual norm for 1D Burgers eqn; \n", 219 | "# 3: residual norm for 2D Burgers eqn; \n", 220 | "# 4: residual norm for time dependent heat conduction (MFEM example 16); \n", 221 | "# 5: residual norm for radial advection (MFEM example 9)\n", 222 | "params['err_type'] = 3 \n", 223 | "params['subsize'] = int(0.3 * num_test) # initial random subset size, the number of randomly selected cases for Greedy sampling\n", 224 | "params['subsize_max'] = 50 # maximum random subset size in percentage\n", 225 | "\n", 226 | "# Adaptive approach for tol of error indicator:\n", 227 | "# 'mean': use mean ratios between error indicator and max relative errors\n", 228 | "# 'reg_mean': use linear regression line\n", 229 | "# 'reg_max': use linear regression line shifted by std to upper bound\n", 230 | "# 'reg_min': use linear regression line shifted by std to lower bound, more conservative\n", 231 | "params['adaptive'] = 'reg_max' \n", 232 | "\n", 233 | "\n", 234 | "# PDE parameters\n", 235 | "params['pde'] = {}\n", 236 | "params['pde']['Re'] = Re\n", 237 | "params['pde']['nx'] = nx\n", 238 | "params['pde']['nt'] = nt\n", 239 | "params['pde']['tstop'] = tstop\n", 240 | "params['pde']['ic'] = ic" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "if params['retrain']: # for retraining the model\n", 250 | " save_name = 'burger_2022_11_20_16_47_35'\n", 251 | " params = pickle.load(open(params['fig_path'] + save_name + '_params.pkl', 'rb'))\n", 252 | " params['retrain'] = True\n", 253 | " params['coeff_exist'] = True # flag to indicate whether to initialize model coefficients with pescribed values\n", 254 | " params['save_name'] = save_name\n", 255 | " params['max_epochs'] = 600000\n", 256 | " params['update_epoch'] = 20000\n", 257 | " params['save_frequency'] = 500\n", 258 | " \n", 259 | " for i in params['train_idx'][4:]:\n", 260 | " train_data['data'].append(test_data['data'][i])\n", 261 | " train_data['param'].append(test_data['param'][i])" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "## Training" 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "metadata": { 275 | "collapsed": true, 276 | "jupyter": { 277 | "outputs_hidden": true 278 | }, 279 | "tags": [] 280 | }, 281 | "outputs": [], 282 | "source": [ 283 | "df = pd.DataFrame()\n", 284 | "timer = []\n", 285 | "timer.append(time())\n", 286 | "\n", 287 | "if not params['retrain']:\n", 288 | " params['save_name'] = 'burger_' + datetime.datetime.now().strftime(\"%Y_%m_%d_%H_%M_%S\")\n", 289 | "tf.reset_default_graph()\n", 290 | "results_dict = train_network(train_data, params)\n", 291 | "df = df.append({**results_dict, **params}, ignore_index=True)\n", 292 | " \n", 293 | "timer.append(time())\n", 294 | "print(f'training time: {(timer[-1]-timer[0])/60:.2f} mins, {(timer[-1]-timer[0])/3600:.2f} hours')" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "# history of validation loss\n", 304 | "train_loss = np.array(df['training_losses'][0]).squeeze()\n", 305 | "test_loss = np.array(df['testing_losses'][0]).squeeze()\n", 306 | "\n", 307 | "fig, ax1 = plt.subplots(figsize=(9,5))\n", 308 | "xt = np.linspace(1,df['epoch_count'],train_loss.shape[0])\n", 309 | "ax1.plot(xt, train_loss[:,0], 'r', label='Train')\n", 310 | "ax1.set_yscale('log')\n", 311 | "ax1.xaxis.set_major_formatter(FormatStrFormatter('%.f'))\n", 312 | "ax1.set_xlabel('Epochs', fontsize=16)\n", 313 | "ax1.set_ylabel('Loss', color='r', fontsize=16)\n", 314 | "ax1.set_xlim(0, params['epoch_count'])\n", 315 | "ax1.tick_params(axis='x', labelsize=14)\n", 316 | "ax1.tick_params(axis='y', labelsize=16)\n", 317 | "ax1.tick_params(axis='y', labelcolor='r')\n", 318 | "\n", 319 | "ax2 = ax1.twinx()\n", 320 | "xt = np.linspace(1,df['epoch_count'],test_loss.shape[0])\n", 321 | "ax2.plot(xt, test_loss, 'b-o', label='Val')\n", 322 | "ax2.set_ylabel('Max Error', color='b', fontsize=16)\n", 323 | "ax2.tick_params(axis='both', labelsize=16)\n", 324 | "ax2.tick_params(axis='y', labelcolor='b')\n", 325 | "\n", 326 | "plt.grid()\n", 327 | "plt.tight_layout()\n", 328 | "plt.savefig(f\"{params['fig_path']}/loss.png\")" 329 | ] 330 | } 331 | ], 332 | "metadata": { 333 | "kernelspec": { 334 | "display_name": "tfvenv", 335 | "language": "python", 336 | "name": "tfvenv" 337 | }, 338 | "language_info": { 339 | "codemirror_mode": { 340 | "name": "ipython", 341 | "version": 3 342 | }, 343 | "file_extension": ".py", 344 | "mimetype": "text/x-python", 345 | "name": "python", 346 | "nbconvert_exporter": "python", 347 | "pygments_lexer": "ipython3", 348 | "version": "3.7.10" 349 | }, 350 | "toc": { 351 | "base_numbering": 1, 352 | "nav_menu": {}, 353 | "number_sections": true, 354 | "sideBar": true, 355 | "skip_h1_title": false, 356 | "title_cell": "Table of Contents", 357 | "title_sidebar": "Contents", 358 | "toc_cell": false, 359 | "toc_position": {}, 360 | "toc_section_display": true, 361 | "toc_window_display": false 362 | }, 363 | "varInspector": { 364 | "cols": { 365 | "lenName": 16, 366 | "lenType": 16, 367 | "lenVar": 40 368 | }, 369 | "kernels_config": { 370 | "python": { 371 | "delete_cmd_postfix": "", 372 | "delete_cmd_prefix": "del ", 373 | "library": "var_list.py", 374 | "varRefreshCmd": "print(var_dic_list())" 375 | }, 376 | "r": { 377 | "delete_cmd_postfix": ") ", 378 | "delete_cmd_prefix": "rm(", 379 | "library": "var_list.r", 380 | "varRefreshCmd": "cat(var_dic_list()) " 381 | } 382 | }, 383 | "types_to_exclude": [ 384 | "module", 385 | "function", 386 | "builtin_function_or_method", 387 | "instance", 388 | "_Feature" 389 | ], 390 | "window_display": false 391 | } 392 | }, 393 | "nbformat": 4, 394 | "nbformat_minor": 4 395 | } 396 | -------------------------------------------------------------------------------- /src/ex16.cpp: -------------------------------------------------------------------------------- 1 | // MFEM Example 16 2 | // 3 | // Compile with: make ex16 4 | // 5 | // Sample runs: ex16 6 | // ex16 -m ../data/inline-tri.mesh 7 | // ex16 -m ../data/disc-nurbs.mesh -tf 2 8 | // ex16 -s 1 -a 0.0 -k 1.0 9 | // ex16 -s 2 -a 1.0 -k 0.0 10 | // ex16 -s 3 -a 0.5 -k 0.5 -o 4 11 | // ex16 -s 14 -dt 1.0e-4 -tf 4.0e-2 -vs 40 12 | // ex16 -m ../data/fichera-q2.mesh 13 | // ex16 -m ../data/fichera-mixed.mesh 14 | // ex16 -m ../data/escher.mesh 15 | // ex16 -m ../data/beam-tet.mesh -tf 10 -dt 0.1 16 | // ex16 -m ../data/amr-quad.mesh -o 4 -r 0 17 | // ex16 -m ../data/amr-hex.mesh -o 2 -r 0 18 | // 19 | // Description: This example solves a time dependent nonlinear heat equation 20 | // problem of the form du/dt = C(u), with a non-linear diffusion 21 | // operator C(u) = \nabla \cdot (\kappa + \alpha u) \nabla u. 22 | // 23 | // The example demonstrates the use of nonlinear operators (the 24 | // class ConductionOperator defining C(u)), as well as their 25 | // implicit time integration. Note that implementing the method 26 | // ConductionOperator::ImplicitSolve is the only requirement for 27 | // high-order implicit (SDIRK) time integration. 28 | // 29 | // We recommend viewing examples 2, 9 and 10 before viewing this 30 | // example. 31 | 32 | #include "mfem.hpp" 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | using namespace mfem; 38 | 39 | 40 | // parameters for initial condition 41 | double h = 0.5; // height of initial condition 42 | double w = 0.2; // width of initial condition 43 | double x1 = 0.5, x2 = 0.5; // center coordinates of initial condition 44 | 45 | // parameters related to residual calculation 46 | bool res_flag = false; // flag to indicate whether residual is calculated from given solutions 47 | double res_ns = 0.1; // percentage of the time steps for residual evaluation 48 | int Tmax_iter = 100; // maximum CG iterations in Tsolver 49 | const char *u_file = "./ex16-u_pred.gf"; // file path of given solutions 50 | const char *res_file = "./ex16-residual.gf"; // file path to save calculated residual 51 | 52 | 53 | /** After spatial discretization, the conduction model can be written as: 54 | * 55 | * du/dt = M^{-1}(-Ku) 56 | * 57 | * where u is the vector representing the temperature, M is the mass matrix, 58 | * and K is the diffusion operator with diffusivity depending on u: 59 | * (\kappa + \alpha u). 60 | * 61 | * Class ConductionOperator represents the right-hand side of the above ODE. 62 | */ 63 | class ConductionOperator : public TimeDependentOperator 64 | { 65 | protected: 66 | FiniteElementSpace &fespace; 67 | Array ess_tdof_list; // this list remains empty for pure Neumann b.c. 68 | 69 | BilinearForm *M; 70 | BilinearForm *K; 71 | 72 | SparseMatrix Mmat, Kmat; 73 | SparseMatrix *T; // T = M + dt K 74 | double current_dt; 75 | 76 | CGSolver M_solver; // Krylov solver for inverting the mass matrix M 77 | DSmoother M_prec; // Preconditioner for the mass matrix M 78 | 79 | CGSolver T_solver; // Implicit solver for T = M + dt K 80 | DSmoother T_prec; // Preconditioner for the implicit solver 81 | 82 | double alpha, kappa; 83 | 84 | mutable Vector z; // auxiliary vector 85 | 86 | public: 87 | ConductionOperator(FiniteElementSpace &f, double alpha, double kappa, 88 | const Vector &u); 89 | 90 | virtual void Mult(const Vector &u, Vector &du_dt) const; 91 | /** Solve the Backward-Euler equation: k = f(u + dt*k, t), for the unknown k. 92 | This is the only requirement for high-order SDIRK implicit integration.*/ 93 | virtual void ImplicitSolve(const double dt, const Vector &u, Vector &k); 94 | 95 | /// Update the diffusion BilinearForm K using the given true-dof vector `u`. 96 | void SetParameters(const Vector &u); 97 | 98 | virtual ~ConductionOperator(); 99 | }; 100 | 101 | double InitialTemperature(const Vector &x); 102 | 103 | int main(int argc, char *argv[]) 104 | { 105 | // 1. Parse command-line options. 106 | const char *mesh_file = "../data/star.mesh"; 107 | int ref_levels = 2; 108 | int order = 2; 109 | int ode_solver_type = 3; 110 | double t_final = 0.5; 111 | double dt = 1.0e-2; 112 | double alpha = 1.0e-2; 113 | double kappa = 0.5; 114 | bool visualization = false; 115 | bool visit = false; 116 | int vis_steps = 5; 117 | 118 | int precision = 18; 119 | cout.precision(precision); 120 | 121 | OptionsParser args(argc, argv); 122 | args.AddOption(&mesh_file, "-m", "--mesh", 123 | "Mesh file to use."); 124 | args.AddOption(&u_file, "-uf", "--sol_file", 125 | "Input file of predicted solution"); 126 | args.AddOption(&res_file, "-rf", "--res_file", 127 | "Output file of residual"); 128 | args.AddOption(&ref_levels, "-r", "--refine", 129 | "Number of times to refine the mesh uniformly."); 130 | args.AddOption(&order, "-o", "--order", 131 | "Order (degree) of the finite elements."); 132 | args.AddOption(&ode_solver_type, "-s", "--ode-solver", 133 | "ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t" 134 | "\t 11 - Forward Euler, 12 - RK2, 13 - RK3 SSP, 14 - RK4."); 135 | args.AddOption(&t_final, "-tf", "--t-final", 136 | "Final time; start time is 0."); 137 | args.AddOption(&dt, "-dt", "--time-step", 138 | "Time step."); 139 | args.AddOption(&alpha, "-a", "--alpha", 140 | "Alpha coefficient."); 141 | args.AddOption(&kappa, "-k", "--kappa", 142 | "Kappa coefficient offset."); 143 | args.AddOption(&h, "-h0", "--height", 144 | "Height of initial condition"); 145 | args.AddOption(&w, "-w0", "--width", 146 | "Width of initial condition"); 147 | args.AddOption(&x1, "-x1", "--x1", 148 | "x coordinate of the center of initial condition"); 149 | args.AddOption(&x2, "-x2", "--x2", 150 | "y coordinate of the center of initial condition"); 151 | args.AddOption(&res_ns, "-res_ns", "--residual_num_steps", 152 | "Percentage of the time steps for residual evaluation"); 153 | args.AddOption(&Tmax_iter, "-Tmax_iter", "--Tsolver_max_iter", 154 | "Maximum CG iterations in Tsolver"); 155 | args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", 156 | "--no-visualization", 157 | "Enable or disable GLVis visualization."); 158 | args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit", 159 | "--no-visit-datafiles", 160 | "Save data files for VisIt (visit.llnl.gov) visualization."); 161 | args.AddOption(&vis_steps, "-vs", "--visualization-steps", 162 | "Visualize every n-th timestep."); 163 | args.AddOption(&res_flag, "-res", "--residual", "-no-res", "--no-residual", 164 | "Calculate residual from given solutions"); 165 | args.Parse(); 166 | if (!args.Good()) 167 | { 168 | args.PrintUsage(cout); 169 | return 1; 170 | } 171 | // args.PrintOptions(cout); 172 | 173 | // 2. Read the mesh from the given mesh file. We can handle triangular, 174 | // quadrilateral, tetrahedral and hexahedral meshes with the same code. 175 | Mesh *mesh = new Mesh(mesh_file, 1, 1); 176 | int dim = mesh->Dimension(); 177 | 178 | // 3. Define the ODE solver used for time integration. Several implicit 179 | // singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as 180 | // explicit Runge-Kutta methods are available. 181 | ODESolver *ode_solver; 182 | switch (ode_solver_type) 183 | { 184 | // Implicit L-stable methods 185 | case 1: ode_solver = new BackwardEulerSolver; break; 186 | case 2: ode_solver = new SDIRK23Solver(2); break; 187 | case 3: ode_solver = new SDIRK33Solver; break; 188 | // Explicit methods 189 | case 11: ode_solver = new ForwardEulerSolver; break; 190 | case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method 191 | case 13: ode_solver = new RK3SSPSolver; break; 192 | case 14: ode_solver = new RK4Solver; break; 193 | case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break; 194 | // Implicit A-stable methods (not L-stable) 195 | case 22: ode_solver = new ImplicitMidpointSolver; break; 196 | case 23: ode_solver = new SDIRK23Solver; break; 197 | case 24: ode_solver = new SDIRK34Solver; break; 198 | default: 199 | cout << "Unknown ODE solver type: " << ode_solver_type << '\n'; 200 | delete mesh; 201 | return 3; 202 | } 203 | 204 | // 4. Refine the mesh to increase the resolution. In this example we do 205 | // 'ref_levels' of uniform refinement, where 'ref_levels' is a 206 | // command-line parameter. 207 | for (int lev = 0; lev < ref_levels; lev++) 208 | { 209 | mesh->UniformRefinement(); 210 | } 211 | 212 | // 5. Define the vector finite element space representing the current and the 213 | // initial temperature, u_ref. 214 | H1_FECollection fe_coll(order, dim); 215 | FiniteElementSpace fespace(mesh, &fe_coll); 216 | 217 | int fe_size = fespace.GetTrueVSize(); 218 | // cout << "Number of temperature unknowns: " << fe_size << endl; 219 | 220 | GridFunction u_gf(&fespace); 221 | 222 | // 6. Set the initial conditions for u. All boundaries are considered 223 | // natural. 224 | FunctionCoefficient u_0(InitialTemperature); 225 | u_gf.ProjectCoefficient(u_0); 226 | Vector u; 227 | u_gf.GetTrueDofs(u); 228 | 229 | // 7. Initialize the conduction operator and the visualization. 230 | ConductionOperator oper(fespace, alpha, kappa, u); 231 | 232 | u_gf.SetFromTrueDofs(u); 233 | 234 | if (!res_flag) 235 | { 236 | ofstream omesh("ex16.mesh"); 237 | omesh.precision(precision); 238 | mesh->Print(omesh); 239 | ofstream osol("ex16-init.gf"); 240 | osol.precision(precision); 241 | u_gf.Save(osol); 242 | } 243 | 244 | VisItDataCollection visit_dc("Example16", mesh); 245 | visit_dc.RegisterField("temperature", &u_gf); 246 | if (visit) 247 | { 248 | visit_dc.SetCycle(0); 249 | visit_dc.SetTime(0.0); 250 | visit_dc.Save(); 251 | } 252 | 253 | 254 | socketstream sout; 255 | if (visualization) 256 | { 257 | char vishost[] = "localhost"; 258 | int visport = 19916; 259 | sout.open(vishost, visport); 260 | if (!sout) 261 | { 262 | cout << "Unable to connect to GLVis server at " 263 | << vishost << ':' << visport << endl; 264 | visualization = false; 265 | cout << "GLVis visualization disabled.\n"; 266 | } 267 | else 268 | { 269 | sout.precision(precision); 270 | sout << "solution\n" << *mesh << u_gf; 271 | sout << "pause\n"; 272 | sout << flush; 273 | cout << "GLVis visualization paused." 274 | << " Press space (in the GLVis window) to resume it.\n"; 275 | } 276 | } 277 | 278 | ode_solver->Init(oper); 279 | 280 | 281 | if (!res_flag) 282 | { 283 | // output the initial condition of u and dudt 284 | GridFunction tmp_u(&fespace), tmp_dudt(&fespace); 285 | tmp_u.SetFromTrueDofs(u); 286 | ofstream osol("ex16-u_"+to_string(0)+".gf"); 287 | osol.precision(precision); 288 | tmp_u.Save(osol); 289 | osol.close(); 290 | 291 | oper.Mult(u, tmp_dudt); 292 | osol.open("ex16-du_"+to_string(0)+".gf"); 293 | osol.precision(precision); 294 | tmp_dudt.Save(osol); 295 | osol.close(); 296 | 297 | 298 | // 8. Perform time-integration (looping over the time iterations, ti, with a 299 | // time-step dt). 300 | double t = 0.0; 301 | bool last_step = false; 302 | 303 | for (int ti = 1; !last_step; ti++) 304 | { 305 | if (t + dt >= t_final - dt/2) 306 | { 307 | last_step = true; 308 | } 309 | 310 | ode_solver->Step(u, t, dt); 311 | 312 | if (last_step || (ti % vis_steps) == 0) 313 | { 314 | cout << "step " << ti << ", t = " << t << endl; 315 | 316 | u_gf.SetFromTrueDofs(u); 317 | if (visualization) 318 | { 319 | sout << "solution\n" << *mesh << u_gf << flush; 320 | } 321 | 322 | if (visit) 323 | { 324 | visit_dc.SetCycle(ti); 325 | visit_dc.SetTime(t); 326 | visit_dc.Save(); 327 | } 328 | } 329 | oper.SetParameters(u); 330 | 331 | // output u and dudt at every time step 332 | GridFunction tmp_u(&fespace), tmp_dudt(&fespace); 333 | tmp_u.SetFromTrueDofs(u); 334 | ofstream osol("ex16-u_"+to_string(ti)+".gf"); 335 | osol.precision(precision); 336 | tmp_u.Save(osol); 337 | osol.close(); 338 | 339 | oper.Mult(u, tmp_dudt); 340 | osol.open("ex16-du_"+to_string(ti)+".gf"); 341 | osol.precision(precision); 342 | tmp_dudt.Save(osol); 343 | osol.close(); 344 | 345 | } 346 | 347 | // 9. Save the final solution. This output can be viewed later using GLVis: 348 | // "glvis -m ex16.mesh -g ex16-final.gf". 349 | { 350 | ofstream osol("ex16-final_u.gf"); 351 | osol.precision(precision); 352 | u_gf.Save(osol); 353 | osol.close(); 354 | 355 | GridFunction dudt(&fespace); 356 | oper.Mult(u, dudt); 357 | osol.open("ex16-final_dudt.gf"); 358 | osol.precision(precision); 359 | dudt.Save(osol); 360 | osol.close(); 361 | } 362 | } 363 | else // calculate residual from given solution 364 | { 365 | ifstream infile(u_file); 366 | infile.precision(precision); 367 | int d1, d2; 368 | infile >> d1; 369 | infile >> d2; 370 | // cout << d1 << ' ' << d2 << endl; 371 | 372 | GridFunction res(&fespace), u1(&fespace), u2(&fespace); 373 | double t = 0, res_norm = 0.0, res_norm_tmp = 0.0; 374 | u1.Load(infile, d2); // u at t=0 375 | for (int i = 0; i < int(d1*res_ns)-1; i++){ 376 | oper.SetParameters(u1); 377 | ode_solver->Step(u1, t, dt); // true next u 378 | u2.Load(infile, d2); // predicted next step 379 | subtract(u1, u2, res); 380 | res_norm_tmp = res.Norml2(); 381 | res_norm += res_norm_tmp; 382 | cout << i+1 << " - " << "t: " << t << " residual: " << res_norm_tmp << endl; 383 | u1 = u2; 384 | } 385 | cout << "final residual: " << res_norm << endl; 386 | 387 | // output the residual norm 388 | ofstream outfile(res_file); 389 | outfile.precision(precision); 390 | outfile << res_norm << endl; 391 | // cout << "output done" << endl; 392 | } 393 | 394 | // 10. Free the used memory. 395 | delete ode_solver; 396 | delete mesh; 397 | 398 | return 0; 399 | } 400 | 401 | ConductionOperator::ConductionOperator(FiniteElementSpace &f, double al, 402 | double kap, const Vector &u) 403 | : TimeDependentOperator(f.GetTrueVSize(), 0.0), fespace(f), M(NULL), K(NULL), 404 | T(NULL), current_dt(0.0), z(height) 405 | { 406 | const double rel_tol = 1e-8; 407 | 408 | M = new BilinearForm(&fespace); 409 | M->AddDomainIntegrator(new MassIntegrator()); 410 | M->Assemble(); 411 | M->FormSystemMatrix(ess_tdof_list, Mmat); 412 | 413 | M_solver.iterative_mode = false; 414 | M_solver.SetRelTol(rel_tol); 415 | M_solver.SetAbsTol(0.0); 416 | M_solver.SetMaxIter(30); 417 | M_solver.SetPrintLevel(0); 418 | M_solver.SetPreconditioner(M_prec); 419 | M_solver.SetOperator(Mmat); 420 | 421 | alpha = al; 422 | kappa = kap; 423 | 424 | T_solver.iterative_mode = false; 425 | T_solver.SetRelTol(rel_tol); 426 | T_solver.SetAbsTol(0.0); 427 | T_solver.SetMaxIter(Tmax_iter); 428 | T_solver.SetPrintLevel(0); 429 | T_solver.SetPreconditioner(T_prec); 430 | 431 | SetParameters(u); 432 | } 433 | 434 | void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const 435 | { 436 | // Compute: 437 | // du_dt = M^{-1}*-K(u) 438 | // for du_dt 439 | Kmat.Mult(u, z); 440 | z.Neg(); // z = -z 441 | M_solver.Mult(z, du_dt); 442 | } 443 | 444 | void ConductionOperator::ImplicitSolve(const double dt, 445 | const Vector &u, Vector &du_dt) 446 | { 447 | // Solve the equation: 448 | // du_dt = M^{-1}*[-K(u + dt*du_dt)] 449 | // for du_dt 450 | if (!T) 451 | { 452 | T = Add(1.0, Mmat, dt, Kmat); 453 | current_dt = dt; 454 | T_solver.SetOperator(*T); 455 | } 456 | MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt 457 | Kmat.Mult(u, z); 458 | z.Neg(); 459 | T_solver.Mult(z, du_dt); 460 | } 461 | 462 | void ConductionOperator::SetParameters(const Vector &u) 463 | { 464 | GridFunction u_alpha_gf(&fespace); 465 | u_alpha_gf.SetFromTrueDofs(u); 466 | for (int i = 0; i < u_alpha_gf.Size(); i++) 467 | { 468 | u_alpha_gf(i) = kappa + alpha*u_alpha_gf(i); 469 | } 470 | 471 | delete K; 472 | K = new BilinearForm(&fespace); 473 | 474 | GridFunctionCoefficient u_coeff(&u_alpha_gf); 475 | 476 | K->AddDomainIntegrator(new DiffusionIntegrator(u_coeff)); 477 | K->Assemble(); 478 | K->FormSystemMatrix(ess_tdof_list, Kmat); 479 | delete T; 480 | T = NULL; // re-compute T on the next ImplicitSolve 481 | } 482 | 483 | ConductionOperator::~ConductionOperator() 484 | { 485 | delete T; 486 | delete M; 487 | delete K; 488 | } 489 | 490 | double InitialTemperature(const Vector &x) 491 | { 492 | // double rd = sqrt(pow(x(0)-x1,2) + pow(x(1)-x2,2)); 493 | // if (rd < w) 494 | // { 495 | // return h; 496 | // } 497 | // else 498 | // { 499 | // return 0.0; 500 | // } 501 | 502 | // return h * sin(w*(x(0)+x(1))) + h; 503 | return h * sin(w*x.Norml2()) + h; 504 | // return h * sin(w*x(0)) + h; 505 | // return h * (sin(w*x(0)) + cos(w*x(1))) + h; 506 | } 507 | -------------------------------------------------------------------------------- /examples/2DBurgerEqn/generate_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "5a7e648d", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import sys\n", 11 | "sys.path.append('../../src')\n", 12 | "import numpy as np\n", 13 | "from scipy import sparse as sp\n", 14 | "from scipy.sparse.linalg import spsolve\n", 15 | "from scipy.sparse import spdiags\n", 16 | "from matplotlib import pyplot as plt\n", 17 | "from matplotlib import cm\n", 18 | "from mpl_toolkits.mplot3d import Axes3D\n", 19 | "import pickle\n", 20 | "from time import time\n", 21 | "import os\n", 22 | "from sindy_utils import *\n", 23 | "%matplotlib inline \n", 24 | "np.set_printoptions(threshold=sys.maxsize)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "id": "3328b23b", 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "plot_fig = False\n", 35 | "save_data = True\n", 36 | "Re = 10000\n", 37 | "nx = 60\n", 38 | "ny = nx\n", 39 | "ic = 2 # initial condition, 1: Sine, 2: Gaussian\n", 40 | "nt = 200\n", 41 | "tstop = 1.0\n", 42 | "dt = tstop/nt\n", 43 | "t = np.linspace(0, tstop, nt+1)\n", 44 | "nxy = (nx-2)*(ny-2)\n", 45 | "dx = 1/(nx-1)\n", 46 | "dy = 1/(ny-1)\n", 47 | "maxitr = 10\n", 48 | "tol = 1e-8\n", 49 | "\n", 50 | "# parameters\n", 51 | "amp_arr = np.array([0.7])\n", 52 | "width_arr = np.array([0.9])\n", 53 | "\n", 54 | "# amp_arr = np.linspace(0.7, 0.9, 5)\n", 55 | "# width_arr = np.linspace(0.9, 1.1, 5)" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "id": "30182399", 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "if ic == 1: # sine\n", 66 | " xmin = 0\n", 67 | " xmax = 1\n", 68 | " ymin = 0\n", 69 | " ymax = 1\n", 70 | "elif ic == 2: # Gaussian\n", 71 | " xmin = -3\n", 72 | " xmax = 3\n", 73 | " ymin = -3\n", 74 | " ymax = 3\n", 75 | " x0 = 0 # Gaussian center\n", 76 | " y0 = 0 # Gaussian center\n", 77 | "else: \n", 78 | " print('wrong values for IC!')\n", 79 | "I=sp.eye(nxy,format='csr')\n", 80 | "\n", 81 | "# full indicies, free indicies, fixed indicies\n", 82 | "[xv,yv]=np.meshgrid(np.linspace(xmin,xmax,nx),np.linspace(ymin,ymax,ny),indexing='xy')\n", 83 | "x=xv.flatten()\n", 84 | "y=yv.flatten()\n", 85 | "\n", 86 | "multi_index_i,multi_index_j=np.meshgrid(np.arange(nx),np.arange(ny),indexing='xy')\n", 87 | "full_multi_index=(multi_index_j.flatten(),multi_index_i.flatten())\n", 88 | "free_multi_index=(multi_index_j[1:-1,1:-1].flatten(),multi_index_i[1:-1,1:-1].flatten())\n", 89 | "x0_multi_index=(multi_index_j[1:-1,0].flatten(),multi_index_i[1:-1,0].flatten())\n", 90 | "x1_multi_index=(multi_index_j[1:-1,-1].flatten(),multi_index_i[1:-1,-1].flatten())\n", 91 | "y0_multi_index=(multi_index_j[0,1:-1].flatten(),multi_index_i[0,1:-1].flatten())\n", 92 | "y1_multi_index=(multi_index_j[-1,1:-1].flatten(),multi_index_i[-1,1:-1].flatten())\n", 93 | "\n", 94 | "dims=(ny,nx)\n", 95 | "full_raveled_indicies=np.ravel_multi_index(full_multi_index,dims)\n", 96 | "free_raveled_indicies=np.ravel_multi_index(free_multi_index,dims)\n", 97 | "x0_raveled_indicies=np.ravel_multi_index(x0_multi_index,dims)\n", 98 | "x1_raveled_indicies=np.ravel_multi_index(x1_multi_index,dims)\n", 99 | "x01_raveled_indicies=np.concatenate((x0_raveled_indicies,x1_raveled_indicies))\n", 100 | "y0_raveled_indicies=np.ravel_multi_index(y0_multi_index,dims)\n", 101 | "y1_raveled_indicies=np.ravel_multi_index(y1_multi_index,dims)\n", 102 | "y01_raveled_indicies=np.concatenate((y0_raveled_indicies,y1_raveled_indicies))\n", 103 | "fixed_raveled_indicies=np.setdiff1d(full_raveled_indicies,free_raveled_indicies)\n", 104 | "\n", 105 | "# boundary one-hot vector\n", 106 | "x0_one_hot=np.eye(nx-2)[0]\n", 107 | "y0_one_hot=np.eye(ny-2)[0]\n", 108 | "x1_one_hot=np.eye(nx-2)[-1]\n", 109 | "y1_one_hot=np.eye(ny-2)[-1]\n", 110 | "\n", 111 | "# inner grid\n", 112 | "inner_multi_index_i,inner_multi_index_j=np.meshgrid(np.arange(nx-2),np.arange(ny-2),indexing='xy')\n", 113 | "inner_x_multi_index=(np.concatenate((inner_multi_index_j[:,0].flatten(),inner_multi_index_j[:,-1].flatten())),\n", 114 | " np.concatenate((inner_multi_index_i[:,0].flatten(),inner_multi_index_i[:,-1].flatten())))\n", 115 | "inner_y_multi_index=(np.concatenate((inner_multi_index_j[0,:].flatten(),inner_multi_index_j[-1,:].flatten())),\n", 116 | " np.concatenate((inner_multi_index_i[0,:].flatten(),inner_multi_index_i[-1,:].flatten())))\n", 117 | "\n", 118 | "inner_dims=(ny-2,nx-2)\n", 119 | "inner_x_raveled_indicies=np.ravel_multi_index(inner_x_multi_index,inner_dims)\n", 120 | "inner_y_raveled_indicies=np.ravel_multi_index(inner_y_multi_index,inner_dims)\n", 121 | "\n", 122 | "\n", 123 | "# first order derivative\n", 124 | "# central\n", 125 | "Mcb=sp.diags([np.zeros(nx-2),-np.ones(nx-2),np.ones(nx-2)],[0,-1,1],(nx-2,nx-2))\n", 126 | "Mc=sp.kron(sp.eye(ny-2),Mcb,format=\"csr\")\n", 127 | "\n", 128 | "Ib=sp.eye(nx-2)\n", 129 | "Nc=sp.kron(sp.diags([np.zeros(ny-2),-np.ones(ny-2),np.ones(ny-2)],[0,-1,1],(ny-2,ny-2)),Ib,format=\"csr\")\n", 130 | "\n", 131 | "# forward\n", 132 | "Mfb=sp.diags([-np.ones(nx-2),np.ones(nx-2)],[0,1],(nx-2,nx-2))\n", 133 | "Mf=sp.kron(sp.eye(ny-2),Mfb,format=\"csr\")\n", 134 | "\n", 135 | "Ib=sp.eye(nx-2)\n", 136 | "Nf=sp.kron(sp.diags([-np.ones(ny-2),np.ones(ny-2)],[0,1],(ny-2,ny-2)),Ib,format=\"csr\")\n", 137 | "\n", 138 | "# backward\n", 139 | "Mbb=sp.diags([np.ones(nx-2),-np.ones(nx-2)],[0,-1],(nx-2,nx-2))\n", 140 | "Mb=sp.kron(sp.eye(ny-2),Mbb,format=\"csr\")\n", 141 | "\n", 142 | "Ib=sp.eye(nx-2)\n", 143 | "Nb=sp.kron(sp.diags([np.ones(ny-2),-np.ones(ny-2)],[0,-1],(ny-2,ny-2)),Ib,format=\"csr\")\n", 144 | "\n", 145 | "# laplacian operator\n", 146 | "Dxb=sp.diags([-2*np.ones(nx-2),np.ones(nx-2),np.ones(nx-2)],[0,-1,1],(nx-2,nx-2))\n", 147 | "Dx=sp.kron(sp.eye(ny-2),Dxb,format=\"csr\")\n", 148 | "\n", 149 | "Ib=sp.eye(nx-2)\n", 150 | "Dy=sp.kron(sp.diags([-2*np.ones(ny-2),np.ones(ny-2),np.ones(ny-2)],[0,-1,1],(ny-2,ny-2)),Ib,format=\"csr\")" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "id": "9208f250", 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "def generate_dataset(ic, amp, width, plot_fig=False):\n", 161 | " timer = []\n", 162 | " timer.append(time())\n", 163 | "\n", 164 | " # compute u_full and v_full\n", 165 | " if ic == 1: # IC: sine\n", 166 | " zv=amp*np.sin(2*np.pi*xv)*np.sin(2*np.pi*yv)\n", 167 | " zv[np.nonzero(xv>0.5)]=0.0\n", 168 | " zv[np.nonzero(yv>0.5)]=0.0\n", 169 | " elif ic == 2: # IC: Gaussian\n", 170 | " zv = amp * np.exp(-((xv-x0)**2 + (yv-y0)**2) / width)\n", 171 | " z = zv.flatten()\n", 172 | " u0 = z.copy()\n", 173 | " v0 = z.copy()\n", 174 | "\n", 175 | " # plot IC\n", 176 | " if plot_fig:\n", 177 | " fig = plt.figure(figsize=(15,3))\n", 178 | " ax_u = fig.add_subplot(141)\n", 179 | " p_u=ax_u.pcolor(x.reshape(ny,nx), y.reshape(ny,nx), u0.reshape(ny,nx))\n", 180 | " cb_u=fig.colorbar(p_u, ax=ax_u)\n", 181 | " ax_u.set_xlabel('$x$')\n", 182 | " ax_u.set_ylabel('$y$')\n", 183 | " ax_u.set_title('$u$')\n", 184 | " \n", 185 | " ax_v = fig.add_subplot(143)\n", 186 | " p_v=ax_v.pcolor(x.reshape(ny,nx), y.reshape(ny,nx), v0.reshape(ny,nx))\n", 187 | " cb_v=fig.colorbar(p_v, ax=ax_v)\n", 188 | " ax_v.set_xlabel('$x$')\n", 189 | " ax_v.set_ylabel('$y$')\n", 190 | " ax_v.set_title('$v$')\n", 191 | "\n", 192 | " # boundary for first order derivative term\n", 193 | " Bdudx0_cur=np.kron(u0[x0_raveled_indicies],x0_one_hot)\n", 194 | " Bdudy0_cur=np.kron(y0_one_hot,u0[y0_raveled_indicies])\n", 195 | " Bdvdx0_cur=np.kron(v0[x0_raveled_indicies],x0_one_hot)\n", 196 | " Bdvdy0_cur=np.kron(y0_one_hot,v0[y0_raveled_indicies])\n", 197 | " Bdudx1_cur=np.kron(u0[x1_raveled_indicies],x1_one_hot)\n", 198 | " Bdudy1_cur=np.kron(y1_one_hot,u0[y1_raveled_indicies])\n", 199 | " Bdvdx1_cur=np.kron(v0[x1_raveled_indicies],x1_one_hot)\n", 200 | " Bdvdy1_cur=np.kron(y1_one_hot,v0[y1_raveled_indicies])\n", 201 | "\n", 202 | " # boundary for second order derivative term\n", 203 | " bxu_cur=np.zeros(nxy)\n", 204 | " byu_cur=np.zeros(nxy)\n", 205 | " bxv_cur=np.zeros(nxy)\n", 206 | " byv_cur=np.zeros(nxy)\n", 207 | "\n", 208 | " bxu_cur[inner_x_raveled_indicies]=u0[x01_raveled_indicies]\n", 209 | " byu_cur[inner_y_raveled_indicies]=u0[y01_raveled_indicies]\n", 210 | " bxv_cur[inner_x_raveled_indicies]=v0[x01_raveled_indicies]\n", 211 | " byv_cur[inner_y_raveled_indicies]=v0[y01_raveled_indicies]\n", 212 | "\n", 213 | " def r(u_free,v_free,u_free_prev,v_free_prev,Mu_free,Mv_free,Nu_free,Nv_free,\n", 214 | " Bdudx0_cur,Bdvdx0_cur,Bdudx1_cur,Bdvdx1_cur,Bdudy0_cur,Bdvdy0_cur,Bdudy1_cur,Bdvdy1_cur,\n", 215 | " bxu_cur,bxv_cur,byu_cur,byv_cur):\n", 216 | "\n", 217 | " f_u=(-1/dx*(u_free*(Mu_free - Bdudx0_cur))\n", 218 | " -1/dy*(v_free*(Nu_free - Bdudy0_cur))\n", 219 | " +1/(Re*dx**2)*(Dx.dot(u_free) + bxu_cur)\n", 220 | " +1/(Re*dy**2)*(Dy.dot(u_free) + byu_cur))\n", 221 | " \n", 222 | " f_v=(-1/dx*(u_free*(Mv_free - Bdvdx0_cur))\n", 223 | " -1/dy*(v_free*(Nv_free - Bdvdy0_cur))\n", 224 | " +1/(Re*dx**2)*(Dx.dot(v_free) + bxv_cur)\n", 225 | " +1/(Re*dy**2)*(Dy.dot(v_free) + byv_cur))\n", 226 | "\n", 227 | " r_u=u_free-u_free_prev-dt*f_u\n", 228 | " r_v=v_free-v_free_prev-dt*f_v\n", 229 | "\n", 230 | " return np.concatenate((r_u,r_v))\n", 231 | "\n", 232 | "\n", 233 | "\n", 234 | " def J(u_free,v_free,Mu_free,Mv_free,Nu_free,Nv_free,\n", 235 | " Bdudx0_cur,Bdvdx0_cur,Bdudx1_cur,Bdvdx1_cur,Bdudy0_cur,Bdvdy0_cur,Bdudy1_cur,Bdvdy1_cur):\n", 236 | "\n", 237 | " df_udu = (-1/dx*(sp.diags(Mu_free - Bdudx0_cur,0,(nxy,nxy),format=\"csr\") \n", 238 | " + sp.diags(u_free,0,(nxy,nxy),format=\"csr\").dot(Mb))\n", 239 | " -1/dy*sp.diags(v_free,0,(nxy,nxy),format=\"csr\").dot(Nb)\n", 240 | " +1/(Re*dx**2)*Dx\n", 241 | " +1/(Re*dy**2)*Dy)\n", 242 | " df_udv = -1/dy*sp.diags(Nu_free - Bdudy0_cur,0,(nxy,nxy),format=\"csr\")\n", 243 | " df_vdu = -1/dx*sp.diags(Mv_free - Bdvdx0_cur,0,(nxy,nxy),format=\"csr\")\n", 244 | " df_vdv = (-1/dx*sp.diags(u_free,0,(nxy,nxy),format=\"csr\").dot(Mb)\n", 245 | " -1/dy*(sp.diags(Nv_free - Bdvdy0_cur,0,(nxy,nxy),format=\"csr\")\n", 246 | " + sp.diags(v_free,0,(nxy,nxy),format=\"csr\").dot(Nb))\n", 247 | " +1/(Re*dx**2)*Dx\n", 248 | " +1/(Re*dy**2)*Dy)\n", 249 | "\n", 250 | " return sp.bmat([[I-dt*df_udu,-dt*df_udv],[-dt*df_vdu,I-dt*df_vdv]],format='csr')\n", 251 | "\n", 252 | " # solution snapshot\n", 253 | " u_full=np.zeros(((nt+1),ny*nx))\n", 254 | " v_full=np.zeros(((nt+1),ny*nx))\n", 255 | " \n", 256 | " # solution + intermediate snapshot\n", 257 | " u_full_inter=np.array([])\n", 258 | " v_full_inter=np.array([])\n", 259 | "\n", 260 | " # IC\n", 261 | " u_full[0]=np.copy(u0)\n", 262 | " v_full[0]=np.copy(v0)\n", 263 | " u0_free=u0[free_raveled_indicies]\n", 264 | " v0_free=v0[free_raveled_indicies]\n", 265 | "\n", 266 | " for k in range(nt):\n", 267 | " u_free_prev=np.copy(u_full[k,free_raveled_indicies])\n", 268 | " v_free_prev=np.copy(v_full[k,free_raveled_indicies])\n", 269 | "\n", 270 | " u_free=np.copy(u_full[k,free_raveled_indicies])\n", 271 | " v_free=np.copy(v_full[k,free_raveled_indicies])\n", 272 | "\n", 273 | " Mu_free=Mb.dot(u_free)\n", 274 | " Mv_free=Mb.dot(v_free)\n", 275 | " Nu_free=Nb.dot(u_free)\n", 276 | " Nv_free=Nb.dot(v_free)\n", 277 | "\n", 278 | " residual=r(u_free,v_free,u_free_prev,v_free_prev,Mu_free,Mv_free,Nu_free,Nv_free,\n", 279 | " Bdudx0_cur,Bdvdx0_cur,Bdudx1_cur,Bdvdx1_cur,Bdudy0_cur,Bdvdy0_cur,Bdudy1_cur,Bdvdy1_cur,\n", 280 | " bxu_cur,bxv_cur,byu_cur,byv_cur)\n", 281 | "\n", 282 | " for itr in range(maxitr):\n", 283 | " jacobian=J(u_free,v_free,Mu_free,Mv_free,Nu_free,Nv_free,\n", 284 | " Bdudx0_cur,Bdvdx0_cur,Bdudx1_cur,Bdvdx1_cur,Bdudy0_cur,Bdvdy0_cur,Bdudy1_cur,Bdvdy1_cur)\n", 285 | "\n", 286 | " delta_free=spsolve(jacobian, -residual)\n", 287 | "\n", 288 | " u_free+=delta_free[:nxy]\n", 289 | " v_free+=delta_free[nxy:]\n", 290 | "\n", 291 | " Mu_free=Mb.dot(u_free)\n", 292 | " Mv_free=Mb.dot(v_free)\n", 293 | " Nu_free=Nb.dot(u_free)\n", 294 | " Nv_free=Nb.dot(v_free)\n", 295 | "\n", 296 | " residual=r(u_free,v_free,u_free_prev,v_free_prev,Mu_free,Mv_free,Nu_free,Nv_free,\n", 297 | " Bdudx0_cur,Bdvdx0_cur,Bdudx1_cur,Bdvdx1_cur,Bdudy0_cur,Bdvdy0_cur,Bdudy1_cur,Bdvdy1_cur,\n", 298 | " bxu_cur,bxv_cur,byu_cur,byv_cur)\n", 299 | " \n", 300 | " # store itermediate values\n", 301 | " R = np.linalg.norm(residual)\n", 302 | "\n", 303 | " if R=tol:\n", 312 | " print(\"\\n non converged after {}th iteration\".format(maxitr))\n", 313 | " break \n", 314 | " timer.append(time())\n", 315 | " time_fom = timer[1]-timer[0]\n", 316 | " \n", 317 | " # plot solution at final time step\n", 318 | " if plot_fig:\n", 319 | " ax_u = fig.add_subplot(142)\n", 320 | " ax_u = plt.gca()\n", 321 | " p_u=ax_u.pcolor(xv, yv, (u_full[-1]).reshape(ny,nx))\n", 322 | " cb_u=fig.colorbar(p_u, ax=ax_u)\n", 323 | " ax_u.set_xlabel('$x$')\n", 324 | " ax_u.set_ylabel('$y$')\n", 325 | " ax_u.set_title('$u$')\n", 326 | "\n", 327 | " ax_v = fig.add_subplot(144)\n", 328 | " ax_v = plt.gca()\n", 329 | " p_v=ax_v.pcolor(xv, yv, (v_full[-1]).reshape(ny,nx))\n", 330 | " cb_v=fig.colorbar(p_v, ax=ax_v)\n", 331 | " ax_v.set_xlabel('$x$')\n", 332 | " ax_v.set_ylabel('$y$')\n", 333 | " ax_v.set_title('$v$')\n", 334 | " plt.tight_layout()\n", 335 | " plt.show() \n", 336 | " return u_full.reshape(-1,ny*nx), v_full.reshape(-1,ny*nx),time_fom" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "id": "32c73531", 342 | "metadata": { 343 | "tags": [] 344 | }, 345 | "source": [ 346 | "### Generate and save data in one file" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "id": "5d6d1dd1", 353 | "metadata": { 354 | "tags": [] 355 | }, 356 | "outputs": [], 357 | "source": [ 358 | "# generate training set\n", 359 | "data = []\n", 360 | "param = []\n", 361 | "time_fom = 0\n", 362 | "for i in amp_arr:\n", 363 | " for j in width_arr:\n", 364 | " snapshot = {}\n", 365 | " param.append(np.array([i, j]))\n", 366 | "\n", 367 | " # compute u, v\n", 368 | " u, v, t_fom = generate_dataset(ic, i, j, plot_fig=plot_fig)\n", 369 | " print(f'amplitude: {i}, width: {j}, shape: {u.shape}, {v.shape}')\n", 370 | " snapshot['u'] = u\n", 371 | " snapshot['v'] = v\n", 372 | " snapshot['t'] = t\n", 373 | " snapshot['time_fom'] = t_fom\n", 374 | " time_fom += t_fom\n", 375 | " \n", 376 | " # compute du/dt, dv/dt\n", 377 | " dudt = derivative(u,tstop)\n", 378 | " dvdt = derivative(v,tstop)\n", 379 | " snapshot['du'] = dudt\n", 380 | " snapshot['dv'] = dvdt\n", 381 | "\n", 382 | " data.append(snapshot)\n", 383 | "\n", 384 | "data_all = {}\n", 385 | "data_all['data'] = data\n", 386 | "data_all['param'] = param\n", 387 | "print(f\"time for computing x: {time_fom/len(data_all['data']):.2f} s\")" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": null, 393 | "id": "7350c92a-353a-4479-971e-75e5ae807695", 394 | "metadata": {}, 395 | "outputs": [], 396 | "source": [ 397 | "nplot = 8\n", 398 | "step_list = np.linspace(0,nt,nplot,dtype=int)\n", 399 | "vmin = data[0]['u'].min()\n", 400 | "vmax = data[0]['u'].max()\n", 401 | "\n", 402 | "fig = plt.figure(figsize=(18,5))\n", 403 | "for i,step in enumerate(step_list):\n", 404 | " ax_u = fig.add_subplot(2,nplot,i+1)\n", 405 | " ax_u = plt.gca()\n", 406 | " p_u=ax_u.pcolor(xv, yv, (data[0]['u'][step]).reshape(ny,nx))\n", 407 | " ax_u.set_title(f'$u$ - t: {step/nt*tstop:.1f} s',fontsize=16)\n", 408 | " \n", 409 | "for i,step in enumerate(step_list):\n", 410 | " ax_u = fig.add_subplot(2,nplot,i+1+nplot)\n", 411 | " ax_u = plt.gca()\n", 412 | " p_u=ax_u.pcolor(xv, yv, (data[0]['v'][step]).reshape(ny,nx))\n", 413 | " ax_u.set_title(f'$v$ - t: {step/nt*tstop:.1f} s',fontsize=16)\n", 414 | "plt.tight_layout()\n", 415 | "# plt.savefig('./2dBurger_u_multisteps.png')" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": null, 421 | "id": "79b4b64c", 422 | "metadata": {}, 423 | "outputs": [], 424 | "source": [ 425 | "# save data\n", 426 | "if save_data:\n", 427 | " num_case = len(data)\n", 428 | " data_path = '/g/g92/he10/Research/data/2DBurgerEqn'\n", 429 | " if not os.path.exists(data_path):\n", 430 | " os.makedirs(data_path)\n", 431 | " if num_case > 1:\n", 432 | " pickle.dump(data_all, open(data_path+f\"/local{num_case}_Re{Re}_tstop{tstop:.1f}_nt{nt}_nx{nx}.p\", \"wb\"))\n", 433 | " else:\n", 434 | " pickle.dump(data_all, open(data_path+f\"/local{num_case}_Re{Re}_A{amp_arr[0]:.2f}_W{width_arr[0]:.2f}_tstop{tstop:.1f}_nt{nt}_nx{nx}.p\", \"wb\"))" 435 | ] 436 | } 437 | ], 438 | "metadata": { 439 | "kernelspec": { 440 | "display_name": "tfvenv", 441 | "language": "python", 442 | "name": "tfvenv" 443 | }, 444 | "language_info": { 445 | "codemirror_mode": { 446 | "name": "ipython", 447 | "version": 3 448 | }, 449 | "file_extension": ".py", 450 | "mimetype": "text/x-python", 451 | "name": "python", 452 | "nbconvert_exporter": "python", 453 | "pygments_lexer": "ipython3", 454 | "version": "3.7.10" 455 | } 456 | }, 457 | "nbformat": 4, 458 | "nbformat_minor": 5 459 | } 460 | -------------------------------------------------------------------------------- /src/autoencoder.py: -------------------------------------------------------------------------------- 1 | import tensorflow.compat.v1 as tf 2 | tf.compat.v1.disable_v2_behavior() 3 | from tensorflow.python.ops.parallel_for.gradients import jacobian, batch_jacobian 4 | from time import time 5 | import pickle 6 | import numpy as np 7 | 8 | def full_network(params): 9 | """ 10 | Define the full network architecture. 11 | 12 | Arguments: 13 | params - Dictionary object containing the parameters that specify the training. 14 | See README file for a description of the parameters. 15 | 16 | Returns: 17 | network - Dictionary containing the tensorflow objects that make up the network. 18 | """ 19 | input_dim = params['input_dim'] 20 | latent_dim = params['latent_dim'] 21 | activation = params['activation'] 22 | poly_order = params['poly_order'] 23 | num_sindy = params['num_sindy'] # number of local SINDys 24 | model_params = [] 25 | if params['coeff_exist']: 26 | model_params = pickle.load(open(params['fig_path'] + params['save_name'] + '_params.pkl', 'rb'))['model_params'] 27 | if 'include_sine' in params.keys(): 28 | include_sine = params['include_sine'] 29 | else: 30 | include_sine = False 31 | if 'include_cosine' in params.keys(): 32 | include_cosine = params['include_cosine'] 33 | else: 34 | include_cosine = False 35 | library_dim = params['library_dim'] 36 | 37 | network = {} 38 | x = tf.placeholder(tf.float32, shape=[None, num_sindy, input_dim], name='x') 39 | dx = tf.placeholder(tf.float32, shape=[None, num_sindy, input_dim], name='dx') 40 | 41 | # Autoencoder 42 | if activation == 'linear': 43 | z, x_decode, encoder_weights, encoder_biases, decoder_weights, decoder_biases = linear_autoencoder(x, input_dim, latent_dim, model_params) 44 | else: 45 | z, x_decode, encoder_weights, encoder_biases, decoder_weights, decoder_biases = nonlinear_autoencoder(x, input_dim, latent_dim, 46 | params['widths'], 47 | model_params, 48 | activation=activation) 49 | 50 | # compute dz/dt 51 | if params['diff'] == 'symb': # symbolic differentiation 52 | dz = z_derivative(x, dx, encoder_weights, encoder_biases, activation=activation) # [batch,num_sindy,latent_dim] 53 | elif params['diff'] == 'auto': # automatic differentiation 54 | dzdx_batch = batch_jacobian(z, x) 55 | dzdx = [] 56 | for i in range(num_sindy): 57 | dzdx.append(dzdx_batch[:,i,:,i,:]) # [batch,output_dim,input_dim] 58 | dzdx = tf.stack(dzdx, axis=1) # [batch,num_sindy,output_dim] 59 | dz = tf.matmul(dzdx, dx[:,:,:,None])[:,:,:,0] # [batch,num_sindy,latent_dim] 60 | 61 | # SINDy 62 | Theta = [] 63 | sindy_coefficients = [] 64 | sindy_predict = [] # [batch,num_sindy,latent_dim] 65 | 66 | if params['sequential_thresholding']: # 1 mask for all local SINDys 67 | coefficient_mask = tf.placeholder(tf.float32, shape=[library_dim,latent_dim], name='coefficient_mask') 68 | network['coefficient_mask'] = coefficient_mask 69 | 70 | 71 | for i in range(num_sindy): 72 | Theta.append(sindy_library_tf(z[:,i,:], latent_dim, poly_order, include_sine, include_cosine)) 73 | 74 | if params['coeff_exist']: 75 | if i < len(model_params[0]): 76 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', initializer=model_params[0][i])) 77 | else: 78 | print(f" Existing SINDys: {len(model_params[0])}, Create new SINDy: {i+1}") 79 | 80 | # initialize the new local SINDy with the coefficients of the nearest SINDy 81 | all_param = np.stack(params['param']) 82 | idx = np.argmin(np.linalg.norm(all_param[:-1]-all_param[-1], axis=1)) 83 | 84 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', initializer=model_params[0][idx])) 85 | else: 86 | if params['coefficient_initialization'] == 'xavier': 87 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', shape=[library_dim,latent_dim], 88 | initializer=tf.truncated_normal_initializer())) 89 | elif params['coefficient_initialization'] == 'specified': 90 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', 91 | initializer=params['init_coefficients'])) 92 | elif params['coefficient_initialization'] == 'constant': 93 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', shape=[library_dim,latent_dim], 94 | initializer=tf.constant_initializer(1.0))) 95 | elif params['coefficient_initialization'] == 'normal': 96 | sindy_coefficients.append(tf.get_variable(f'sindy_coefficients{i}', shape=[library_dim,latent_dim], 97 | initializer=tf.initializers.random_normal())) 98 | 99 | if params['sequential_thresholding']: 100 | sindy_predict.append(tf.matmul(Theta[i], coefficient_mask * sindy_coefficients[i])) 101 | else: 102 | sindy_predict.append(tf.matmul(Theta[i], sindy_coefficients[i])) # [batch,input_dim] 103 | 104 | sindy_predict = tf.stack(sindy_predict, axis=1) # [batch,num_sindy,latent_dim] 105 | 106 | # compute dx/dt 107 | if params['loss_weight_sindy_x'] > 0: 108 | if params['diff'] == 'symb': # symbolic differentiation 109 | dx_decode = z_derivative(z, sindy_predict, decoder_weights, decoder_biases, activation=activation) # [batch,num_sindy,input_dim] 110 | elif params['diff'] == 'auto': # automatic differentiation 111 | dx_decode = tf.linalg.matmul(dxdz_decode, sindy_predict[:,:,:,None])[:,:,:,0] # [batch,num_sindy,input_dim] 112 | else: 113 | dx_decode = tf.zeros(tf.shape(dx)) 114 | 115 | network['x'] = x # [batch,num_sindy,input_dim] 116 | network['dx'] = dx # [batch,num_sindy,input_dim] 117 | network['z'] = z # [batch,num_sindy,latent_dim] 118 | network['dz'] = dz # [batch,num_sindy,latent_dim] 119 | network['x_decode'] = x_decode # [batch,num_sindy,input_dim] 120 | network['dx_decode'] = dx_decode # [batch,num_sindy,input_dim] 121 | network['encoder_weights'] = encoder_weights 122 | network['encoder_biases'] = encoder_biases 123 | network['decoder_weights'] = decoder_weights 124 | network['decoder_biases'] = decoder_biases 125 | network['Theta'] = Theta # list 126 | network['sindy_coefficients'] = sindy_coefficients # list 127 | network['dz_predict'] = sindy_predict # [batch,num_sindy,latent_dim] 128 | return network 129 | 130 | 131 | def define_loss(network, params): 132 | """ 133 | Create the loss function. 134 | 135 | Arguments: 136 | network - Dictionary object containing the elements of the network architecture. 137 | This will be the output of the full_network() function. 138 | """ 139 | x = network['x'] # [batch,num_sindy,input_dim] 140 | x_decode = network['x_decode'] # [batch,num_sindy,input_dim] 141 | num_sindy = params['num_sindy'] # number of local SINDys 142 | sindy_coefficients = network['sindy_coefficients'] 143 | dz = network['dz'] # [batch,num_sindy,latent_dim] 144 | dz_predict = network['dz_predict'] # [batch,num_sindy,latent_dim] 145 | dx = network['dx'] # [batch,num_sindy,input_dim] 146 | dx_decode = network['dx_decode'] # [batch,num_sindy,input_dim] 147 | 148 | losses = {} 149 | losses['decoder'] = tf.zeros((1)) 150 | losses['sindy_x'] = tf.zeros((1)) 151 | losses['sindy_z'] = tf.zeros((1)) 152 | losses['sindy_regularization'] = tf.zeros((1)) 153 | 154 | for i in range(num_sindy): 155 | losses['decoder'] += tf.reduce_mean((x[:,i,:] - x_decode[:,i,:])**2) 156 | losses['sindy_x'] += tf.reduce_mean((dx[:,i,:] - dx_decode[:,i,:])**2) 157 | losses['sindy_z'] += tf.reduce_mean((dz[:,i,:] - dz_predict[:,i,:])**2) 158 | losses['sindy_regularization'] += tf.reduce_mean(tf.abs(sindy_coefficients[i])) 159 | 160 | loss = params['loss_weight_decoder'] * losses['decoder'] \ 161 | + params['loss_weight_sindy_z'] * losses['sindy_z'] \ 162 | + params['loss_weight_sindy_x'] * losses['sindy_x'] \ 163 | + params['loss_weight_sindy_regularization'] * losses['sindy_regularization'] 164 | 165 | loss_refinement = params['loss_weight_decoder'] * losses['decoder'] \ 166 | + params['loss_weight_sindy_z'] * losses['sindy_z'] \ 167 | + params['loss_weight_sindy_x'] * losses['sindy_x'] 168 | return loss, losses, loss_refinement 169 | 170 | 171 | def linear_autoencoder(x, input_dim, latent_dim, model_params): 172 | """ 173 | Construct a linear autoencoder. 174 | 175 | Arguments: 176 | x - 2D tensorflow array, input to the network (shape is [batch,num_sindy,input_dim]) 177 | input_dim - Integer, number of state variables in the input to the first layer 178 | latent_dim - Integer, number of latent variables in the embedding layer 179 | model_params - List, exsiting model parameters 180 | 181 | Returns: 182 | z - Tensorflow array, output of the embedding layer (shape is [batch,num_sindy,latent_dim]) 183 | x_decode - Tensorflow array, output of the output layer (shape is [batch,num_sindy,output_dim]) 184 | encoder_weights - List of tensorflow arrays containing the encoder weights 185 | encoder_biases - List of tensorflow arrays containing the encoder biases 186 | decoder_weights - List of tensorflow arrays containing the decoder weights 187 | decoder_biases - List of tensorflow arrays containing the decoder biases 188 | """ 189 | z,encoder_weights,encoder_biases = build_network_layers(x, input_dim, latent_dim, [], None, 'encoder', model_params) 190 | x_decode,decoder_weights,decoder_biases = build_network_layers(z, latent_dim, input_dim, [], None, 'decoder', model_params) 191 | return z, x_decode, encoder_weights, encoder_biases,decoder_weights,decoder_biases 192 | 193 | 194 | def silu(x): 195 | """ 196 | This function calculates the output of the SiLU activation 197 | given an input x. 198 | """ 199 | return x * tf.sigmoid(x) 200 | 201 | 202 | def nonlinear_autoencoder(x, input_dim, latent_dim, widths, model_params, activation='elu'): 203 | """ 204 | Construct a nonlinear autoencoder. 205 | 206 | Arguments: 207 | x - 2D tensorflow array, input to the network (shape is [batch,num_sindy,input_dim]) 208 | input_dim - Integer, number of state variables in the input to the first layer 209 | latent_dim - Integer, number of latent variables in the embedding layer 210 | widths - List of integers representing how many units are in each network layer 211 | model_params - List, exsiting model parameters 212 | activation - Tensorflow function to be used as the activation function at each layer 213 | 214 | Returns: 215 | z - Tensorflow array, output of the embedding layer (shape is [batch,num_sindy,latent_dim]) 216 | x_decode - Tensorflow array, output of the output layer (shape is [batch,num_sindy,output_dim]) 217 | encoder_weights - List of tensorflow arrays containing the encoder weights 218 | encoder_biases - List of tensorflow arrays containing the encoder biases 219 | decoder_weights - List of tensorflow arrays containing the decoder weights 220 | decoder_biases - List of tensorflow arrays containing the decoder biases 221 | """ 222 | if activation == 'relu': 223 | activation_function = tf.nn.relu 224 | elif activation == 'elu': 225 | activation_function = tf.nn.elu 226 | elif activation == 'sigmoid': 227 | activation_function = tf.sigmoid 228 | elif activation == 'tanh': 229 | activation_function = tf.tanh 230 | elif activation == 'silu': 231 | activation_function = silu 232 | else: 233 | raise ValueError('invalid activation function') 234 | z,encoder_weights,encoder_biases = build_network_layers(x, input_dim, latent_dim, widths, activation_function, 'encoder', model_params) 235 | x_decode,decoder_weights,decoder_biases = build_network_layers(z, latent_dim, input_dim, widths[::-1], activation_function, 'decoder', model_params) 236 | return z, x_decode, encoder_weights, encoder_biases, decoder_weights, decoder_biases 237 | 238 | 239 | def build_network_layers(input, input_dim, output_dim, widths, activation, name, model_params): 240 | """ 241 | Construct one portion of the network (either encoder or decoder). 242 | 243 | Arguments: 244 | input - 2D tensorflow array, input to the network (shape is [batch,num_sindy,input_dim]) 245 | input_dim - Integer, number of state variables in the input to the first layer 246 | output_dim - Integer, number of state variables to output from the final layer 247 | widths - List of integers representing how many units are in each network layer 248 | activation - Tensorflow function to be used as the activation function at each layer 249 | name - String, prefix to be used in naming the tensorflow variables 250 | model_params - List, exsiting model parameters 251 | 252 | Returns: 253 | input - Tensorflow array, output of the network layers (shape is [batch,num_sindy,output_dim]) 254 | weights - List of tensorflow arrays containing the network weights 255 | biases - List of tensorflow arrays containing the network biases 256 | """ 257 | weights = [] 258 | biases = [] 259 | last_width = input_dim # output width of last (previous) layer 260 | 261 | if len(model_params) > 0: 262 | # build hidden layers 263 | for i,n_units in enumerate(widths): 264 | if name == 'encoder': 265 | W = tf.get_variable(name+'_W'+str(i), initializer=model_params[1][i]) 266 | b = tf.get_variable(name+'_b'+str(i), initializer=model_params[2][i]) 267 | elif name == 'decoder': 268 | W = tf.get_variable(name+'_W'+str(i), initializer=model_params[3][i]) 269 | b = tf.get_variable(name+'_b'+str(i), initializer=model_params[4][i]) 270 | last_width = n_units 271 | weights.append(W) 272 | biases.append(b) 273 | 274 | # build last layer 275 | if name == 'encoder': 276 | W = tf.get_variable(name+'_W'+str(len(widths)), initializer=model_params[1][-1]) 277 | b = tf.get_variable(name+'_b'+str(len(widths)), initializer=model_params[2][-1]) 278 | elif name == 'decoder': 279 | W = tf.get_variable(name+'_W'+str(len(widths)), initializer=model_params[3][-1]) 280 | b = tf.get_variable(name+'_b'+str(len(widths)), initializer=model_params[4][-1]) 281 | weights.append(W) 282 | biases.append(b) 283 | 284 | else: 285 | # build hidden layers 286 | for i,n_units in enumerate(widths): 287 | W = tf.get_variable(name+'_W'+str(i), shape=[last_width,n_units]) 288 | b = tf.get_variable(name+'_b'+str(i), shape=[n_units], initializer=tf.constant_initializer(0.0)) 289 | last_width = n_units 290 | weights.append(W) 291 | biases.append(b) 292 | 293 | # build last layer 294 | W = tf.get_variable(name+'_W'+str(len(widths)), shape=[last_width,output_dim]) 295 | b = tf.get_variable(name+'_b'+str(len(widths)), shape=[output_dim],initializer=tf.constant_initializer(0.0)) 296 | weights.append(W) 297 | biases.append(b) 298 | 299 | # forward pass 300 | output = [] 301 | for j in range(input.shape[1]): 302 | input_j = input[:,j,:] 303 | for i in range(len(weights)-1): 304 | input_j = tf.matmul(input_j, weights[i]) + biases[i] 305 | if activation is not None: 306 | input_j = activation(input_j) 307 | output.append(tf.matmul(input_j, weights[-1]) + biases[-1]) # last layer, [batch,output_dim] 308 | output = tf.stack(output, axis=1) # [batch,num_sindy,output_dim] 309 | return output, weights, biases 310 | 311 | 312 | def sindy_library_tf(z, latent_dim, poly_order, include_sine=False, include_cosine=False): 313 | """ 314 | Build the SINDy library. 315 | 316 | Arguments: 317 | z - 2D tensorflow array of the snapshots on which to build the library. Shape is number of 318 | time points by the number of state variables. 319 | latent_dim - Integer, number of state variable in z. 320 | poly_order - Integer, polynomial order to which to build the library. Max value is 5. 321 | include_sine - Boolean, whether or not to include sine terms in the library. Default False. 322 | 323 | Returns: 324 | 2D tensorflow array containing the constructed library. Shape is number of time points by 325 | number of library functions. The number of library functions is determined by the number 326 | of state variables of the input, the polynomial order, and whether or not sines are included. 327 | """ 328 | library = [tf.ones(tf.shape(z)[0])] 329 | 330 | if poly_order > 0: 331 | for i in range(latent_dim): 332 | library.append(z[:,i]) 333 | 334 | if poly_order > 1: 335 | for i in range(latent_dim): 336 | for j in range(i,latent_dim): 337 | library.append(tf.multiply(z[:,i], z[:,j])) 338 | 339 | if poly_order > 2: 340 | for i in range(latent_dim): 341 | for j in range(i,latent_dim): 342 | for k in range(j,latent_dim): 343 | library.append(z[:,i]*z[:,j]*z[:,k]) 344 | 345 | if poly_order > 3: 346 | for i in range(latent_dim): 347 | for j in range(i,latent_dim): 348 | for k in range(j,latent_dim): 349 | for p in range(k,latent_dim): 350 | library.append(z[:,i]*z[:,j]*z[:,k]*z[:,p]) 351 | 352 | if poly_order > 4: 353 | for i in range(latent_dim): 354 | for j in range(i,latent_dim): 355 | for k in range(j,latent_dim): 356 | for p in range(k,latent_dim): 357 | for q in range(p,latent_dim): 358 | library.append(z[:,i]*z[:,j]*z[:,k]*z[:,p]*z[:,q]) 359 | 360 | if include_sine: 361 | for i in range(latent_dim): 362 | library.append(tf.sin(z[:,i])) 363 | 364 | if include_cosine: 365 | for i in range(latent_dim): 366 | library.append(tf.cos(z[:,i])) 367 | 368 | return tf.stack(library, axis=1) 369 | 370 | 371 | def z_derivative(input, dx, weights, biases, activation='elu'): 372 | """ 373 | Compute the first order time derivatives by propagating through the network. 374 | 375 | Arguments: 376 | input - 2D tensorflow array, input to the network. Dimensions are number of time points 377 | by number of state variables. 378 | dx - First order time derivatives of the input to the network. 379 | weights - List of tensorflow arrays containing the network weights 380 | biases - List of tensorflow arrays containing the network biases 381 | activation - String specifying which activation function to use. Options are 382 | 'elu' (exponential linear unit), 'relu' (rectified linear unit), 'sigmoid', 383 | or linear. 384 | 385 | Returns: 386 | dz - Tensorflow array, first order time derivatives of the network output. 387 | """ 388 | num_sindy = input.shape[1] 389 | dz = [] 390 | 391 | if activation == 'elu': 392 | for j in range(num_sindy): 393 | input_j = input[:,j,:] 394 | dz_j = dx[:,j,:] 395 | for i in range(len(weights)-1): 396 | input_j = tf.matmul(input_j, weights[i]) + biases[i] 397 | input_j = tf.nn.elu(input_j) 398 | dz_j = tf.multiply(tf.minimum(tf.exp(input_j),1.0), 399 | tf.matmul(dz_j, weights[i])) 400 | dz.append(tf.matmul(dz_j, weights[-1])) # [batch,output_dim] 401 | dz = tf.stack(dz, axis=1) # [batch,num_sindy,output_dim] 402 | 403 | elif activation == 'relu': 404 | for j in range(num_sindy): 405 | input_j = input[:,j,:] 406 | dz_j = dx[:,j,:] 407 | for i in range(len(weights)-1): 408 | input_j = tf.matmul(input_j, weights[i]) + biases[i] 409 | input_j = tf.nn.relu(input_j) 410 | dz_j = tf.multiply(tf.to_float(input_j > 0), tf.matmul(dz_j, weights[i])) 411 | dz.append(tf.matmul(dz_j, weights[-1])) # [batch,output_dim] 412 | dz = tf.stack(dz, axis=1) # [batch,num_sindy,output_dim] 413 | 414 | elif activation == 'sigmoid': 415 | for j in range(num_sindy): 416 | input_j = input[:,j,:] 417 | dz_j = dx[:,j,:] 418 | for i in range(len(weights)-1): 419 | input_j = tf.matmul(input_j, weights[i]) + biases[i] 420 | input_j = tf.sigmoid(input_j) 421 | dz_j = tf.multiply(tf.multiply(input_j, 1-input_j), tf.matmul(dz_j, weights[i])) 422 | dz.append(tf.matmul(dz_j, weights[-1])) # [batch,output_dim] 423 | dz = tf.stack(dz, axis=1) # [batch,num_sindy,output_dim] 424 | 425 | else: 426 | for j in range(num_sindy): 427 | input_j = input[:,j,:] 428 | dz_j = dx[:,j,:] 429 | for i in range(len(weights)-1): 430 | input_j = tf.matmul(input_j, weights[i]) + biases[i] 431 | dz.append(tf.matmul(dz_j, weights[-1])) # [batch,output_dim] 432 | dz = tf.stack(dz, axis=1) # [batch,num_sindy,output_dim] 433 | return dz -------------------------------------------------------------------------------- /src/ex9.cpp: -------------------------------------------------------------------------------- 1 | // MFEM Example 9 2 | // 3 | // Compile with: make ex9 4 | // 5 | // Sample runs: 6 | // ex9 -m ../data/periodic-segment.mesh -p 0 -r 2 -dt 0.005 7 | // ex9 -m ../data/periodic-square.mesh -p 0 -r 2 -dt 0.01 -tf 10 8 | // ex9 -m ../data/periodic-hexagon.mesh -p 0 -r 2 -dt 0.01 -tf 10 9 | // ex9 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9 10 | // ex9 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9 11 | // ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9 12 | // ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 13 -tf 9 13 | // ex9 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.005 -tf 9 14 | // ex9 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9 15 | // ex9 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9 16 | // ex9 -m ../data/disc-nurbs.mesh -p 2 -r 3 -dt 0.005 -tf 9 17 | // ex9 -m ../data/periodic-square.mesh -p 3 -r 4 -dt 0.0025 -tf 9 -vs 20 18 | // ex9 -m ../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.02 -tf 8 19 | // ex9 -m ../data/periodic-square.msh -p 0 -r 2 -dt 0.005 -tf 2 20 | // ex9 -m ../data/periodic-cube.msh -p 0 -r 1 -o 2 -tf 2 21 | // 22 | // Device sample runs: 23 | // ex9 -pa 24 | // ex9 -ea 25 | // ex9 -fa 26 | // ex9 -pa -m ../data/periodic-cube.mesh 27 | // ex9 -pa -m ../data/periodic-cube.mesh -d cuda 28 | // ex9 -ea -m ../data/periodic-cube.mesh -d cuda 29 | // ex9 -fa -m ../data/periodic-cube.mesh -d cuda 30 | // 31 | // Description: This example code solves the time-dependent advection equation 32 | // du/dt + v.grad(u) = 0, where v is a given fluid velocity, and 33 | // u0(x)=u(0,x) is a given initial condition. 34 | // 35 | // The example demonstrates the use of Discontinuous Galerkin (DG) 36 | // bilinear forms in MFEM (face integrators), the use of implicit 37 | // and explicit ODE time integrators, the definition of periodic 38 | // boundary conditions through periodic meshes, as well as the use 39 | // of GLVis for persistent visualization of a time-evolving 40 | // solution. The saving of time-dependent data files for external 41 | // visualization with VisIt (visit.llnl.gov) and ParaView 42 | // (paraview.org) is also illustrated. 43 | 44 | #include "mfem.hpp" 45 | #include 46 | #include 47 | #include 48 | 49 | using namespace std; 50 | using namespace mfem; 51 | 52 | // Choice for the problem setup. The fluid velocity, initial condition and 53 | // inflow boundary condition are chosen based on this parameter. 54 | int problem; 55 | 56 | // Velocity coefficient 57 | void velocity_function(const Vector &x, Vector &v); 58 | 59 | // Initial condition 60 | double u0_function(const Vector &x); 61 | 62 | // Inflow boundary condition 63 | double inflow_function(const Vector &x); 64 | 65 | // Mesh bounding box 66 | Vector bb_min, bb_max; 67 | 68 | // parameters for initial condition of Problem 3 69 | double w1 = 1; 70 | double w2 = 1; 71 | 72 | // parameters related to residual calculation 73 | bool res_flag = false; // flag to indicate whether residual is calculated from given solutions 74 | double res_ns = 0.1; // percentage of the time steps for residual evaluation 75 | int Mmax_iter = 100; // maximum iterations in Msolver 76 | const char *u_file = "./ex9-u_pred.gf"; // file path of given solutions 77 | const char *res_file = "./ex9-residual.gf"; // file path to save calculated residual 78 | 79 | class DG_Solver : public Solver 80 | { 81 | private: 82 | SparseMatrix &M, &K, A; 83 | GMRESSolver linear_solver; 84 | BlockILU prec; 85 | double dt; 86 | public: 87 | DG_Solver(SparseMatrix &M_, SparseMatrix &K_, const FiniteElementSpace &fes) 88 | : M(M_), 89 | K(K_), 90 | prec(fes.GetFE(0)->GetDof(), 91 | BlockILU::Reordering::MINIMUM_DISCARDED_FILL), 92 | dt(-1.0) 93 | { 94 | linear_solver.iterative_mode = false; 95 | linear_solver.SetRelTol(1e-9); 96 | linear_solver.SetAbsTol(0.0); 97 | linear_solver.SetMaxIter(100); 98 | linear_solver.SetPrintLevel(0); 99 | linear_solver.SetPreconditioner(prec); 100 | } 101 | 102 | void SetTimeStep(double dt_) 103 | { 104 | if (dt_ != dt) 105 | { 106 | dt = dt_; 107 | // Form operator A = M - dt*K 108 | A = K; 109 | A *= -dt; 110 | A += M; 111 | 112 | // this will also call SetOperator on the preconditioner 113 | linear_solver.SetOperator(A); 114 | } 115 | } 116 | 117 | void SetOperator(const Operator &op) 118 | { 119 | linear_solver.SetOperator(op); 120 | } 121 | 122 | virtual void Mult(const Vector &x, Vector &y) const 123 | { 124 | linear_solver.Mult(x, y); 125 | } 126 | }; 127 | 128 | /** A time-dependent operator for the right-hand side of the ODE. The DG weak 129 | form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass 130 | and advection matrices, and b describes the flow on the boundary. This can 131 | be written as a general ODE, du/dt = M^{-1} (K u + b), and this class is 132 | used to evaluate the right-hand side. */ 133 | class FE_Evolution : public TimeDependentOperator 134 | { 135 | private: 136 | BilinearForm &M, &K; 137 | const Vector &b; 138 | Solver *M_prec; 139 | CGSolver M_solver; 140 | DG_Solver *dg_solver; 141 | 142 | mutable Vector z; 143 | 144 | public: 145 | FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_); 146 | 147 | virtual void Mult(const Vector &x, Vector &y) const; 148 | virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k); 149 | 150 | virtual ~FE_Evolution(); 151 | }; 152 | 153 | 154 | int main(int argc, char *argv[]) 155 | { 156 | // 1. Parse command-line options. 157 | problem = 0; 158 | const char *mesh_file = "../data/periodic-hexagon.mesh"; 159 | int ref_levels = 2; 160 | int order = 3; 161 | bool pa = false; 162 | bool ea = false; 163 | bool fa = false; 164 | const char *device_config = "cpu"; 165 | int ode_solver_type = 4; 166 | double t_final = 10.0; 167 | double dt = 0.01; 168 | bool visualization = true; 169 | bool visit = false; 170 | bool paraview = false; 171 | bool binary = false; 172 | int vis_steps = 5; 173 | 174 | int precision = 18; 175 | cout.precision(precision); 176 | 177 | OptionsParser args(argc, argv); 178 | args.AddOption(&mesh_file, "-m", "--mesh", 179 | "Mesh file to use."); 180 | args.AddOption(&u_file, "-uf", "--sol_file", 181 | "Input file of predicted solution"); 182 | args.AddOption(&res_file, "-rf", "--res_file", 183 | "Output file of residual"); 184 | args.AddOption(&problem, "-p", "--problem", 185 | "Problem setup to use. See options in velocity_function()."); 186 | args.AddOption(&ref_levels, "-r", "--refine", 187 | "Number of times to refine the mesh uniformly."); 188 | args.AddOption(&order, "-o", "--order", 189 | "Order (degree) of the finite elements."); 190 | args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa", 191 | "--no-partial-assembly", "Enable Partial Assembly."); 192 | args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea", 193 | "--no-element-assembly", "Enable Element Assembly."); 194 | args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa", 195 | "--no-full-assembly", "Enable Full Assembly."); 196 | args.AddOption(&device_config, "-d", "--device", 197 | "Device configuration string, see Device::Configure()."); 198 | args.AddOption(&ode_solver_type, "-s", "--ode-solver", 199 | "ODE solver: 1 - Forward Euler,\n\t" 200 | " 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t" 201 | " 11 - Backward Euler,\n\t" 202 | " 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t" 203 | " 22 - Implicit Midpoint Method,\n\t" 204 | " 23 - SDIRK23 (A-stable), 24 - SDIRK34"); 205 | args.AddOption(&t_final, "-tf", "--t-final", 206 | "Final time; start time is 0."); 207 | args.AddOption(&dt, "-dt", "--time-step", 208 | "Time step."); 209 | args.AddOption(&w1, "-w1", "--w1", 210 | "w1 of initial condition of Problem 3"); 211 | args.AddOption(&w2, "-w2", "--w2", 212 | "w2 of initial condition of Problem 3"); 213 | args.AddOption(&res_ns, "-res_ns", "--residual_num_steps", 214 | "Percentage of the time steps for residual evaluation"); 215 | args.AddOption(&Mmax_iter, "-Mmax_iter", "--Msolver_max_iter", 216 | "Maximum iterations in Msolver"); 217 | args.AddOption(&visualization, "-vis", "--visualization", "-no-vis", 218 | "--no-visualization", 219 | "Enable or disable GLVis visualization."); 220 | args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit", 221 | "--no-visit-datafiles", 222 | "Save data files for VisIt (visit.llnl.gov) visualization."); 223 | args.AddOption(¶view, "-paraview", "--paraview-datafiles", "-no-paraview", 224 | "--no-paraview-datafiles", 225 | "Save data files for ParaView (paraview.org) visualization."); 226 | args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii", 227 | "--ascii-datafiles", 228 | "Use binary (Sidre) or ascii format for VisIt data files."); 229 | args.AddOption(&vis_steps, "-vs", "--visualization-steps", 230 | "Visualize every n-th timestep."); 231 | args.AddOption(&res_flag, "-res", "--residual", "-no-res", "--no-residual", 232 | "Calculate residual from given solutions"); 233 | args.Parse(); 234 | if (!args.Good()) 235 | { 236 | args.PrintUsage(cout); 237 | return 1; 238 | } 239 | 240 | Device device(device_config); 241 | 242 | if (!res_flag){ 243 | args.PrintOptions(cout); 244 | device.Print(); 245 | } 246 | 247 | // 2. Read the mesh from the given mesh file. We can handle geometrically 248 | // periodic meshes in this code. 249 | Mesh mesh(mesh_file, 1, 1); 250 | int dim = mesh.Dimension(); 251 | 252 | // 3. Define the ODE solver used for time integration. Several explicit 253 | // Runge-Kutta methods are available. 254 | ODESolver *ode_solver = NULL; 255 | switch (ode_solver_type) 256 | { 257 | // Explicit methods 258 | case 1: ode_solver = new ForwardEulerSolver; break; 259 | case 2: ode_solver = new RK2Solver(1.0); break; 260 | case 3: ode_solver = new RK3SSPSolver; break; 261 | case 4: ode_solver = new RK4Solver; break; 262 | case 6: ode_solver = new RK6Solver; break; 263 | // Implicit (L-stable) methods 264 | case 11: ode_solver = new BackwardEulerSolver; break; 265 | case 12: ode_solver = new SDIRK23Solver(2); break; 266 | case 13: ode_solver = new SDIRK33Solver; break; 267 | // Implicit A-stable methods (not L-stable) 268 | case 22: ode_solver = new ImplicitMidpointSolver; break; 269 | case 23: ode_solver = new SDIRK23Solver; break; 270 | case 24: ode_solver = new SDIRK34Solver; break; 271 | 272 | default: 273 | cout << "Unknown ODE solver type: " << ode_solver_type << '\n'; 274 | return 3; 275 | } 276 | 277 | // 4. Refine the mesh to increase the resolution. In this example we do 278 | // 'ref_levels' of uniform refinement, where 'ref_levels' is a 279 | // command-line parameter. If the mesh is of NURBS type, we convert it to 280 | // a (piecewise-polynomial) high-order mesh. 281 | for (int lev = 0; lev < ref_levels; lev++) 282 | { 283 | mesh.UniformRefinement(); 284 | } 285 | if (mesh.NURBSext) 286 | { 287 | mesh.SetCurvature(max(order, 1)); 288 | } 289 | mesh.GetBoundingBox(bb_min, bb_max, max(order, 1)); 290 | 291 | // 5. Define the discontinuous DG finite element space of the given 292 | // polynomial order on the refined mesh. 293 | DG_FECollection fec(order, dim, BasisType::GaussLobatto); 294 | FiniteElementSpace fes(&mesh, &fec); 295 | 296 | if (!res_flag){ 297 | cout << "Number of unknowns: " << fes.GetVSize() << endl; 298 | } 299 | 300 | // 6. Set up and assemble the bilinear and linear forms corresponding to the 301 | // DG discretization. The DGTraceIntegrator involves integrals over mesh 302 | // interior faces. 303 | VectorFunctionCoefficient velocity(dim, velocity_function); 304 | FunctionCoefficient inflow(inflow_function); 305 | FunctionCoefficient u0(u0_function); 306 | 307 | BilinearForm m(&fes); 308 | BilinearForm k(&fes); 309 | if (pa) 310 | { 311 | m.SetAssemblyLevel(AssemblyLevel::PARTIAL); 312 | k.SetAssemblyLevel(AssemblyLevel::PARTIAL); 313 | } 314 | else if (ea) 315 | { 316 | m.SetAssemblyLevel(AssemblyLevel::ELEMENT); 317 | k.SetAssemblyLevel(AssemblyLevel::ELEMENT); 318 | } 319 | else if (fa) 320 | { 321 | m.SetAssemblyLevel(AssemblyLevel::FULL); 322 | k.SetAssemblyLevel(AssemblyLevel::FULL); 323 | } 324 | m.AddDomainIntegrator(new MassIntegrator); 325 | constexpr double alpha = -1.0; 326 | k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha)); 327 | k.AddInteriorFaceIntegrator( 328 | new NonconservativeDGTraceIntegrator(velocity, alpha)); 329 | k.AddBdrFaceIntegrator( 330 | new NonconservativeDGTraceIntegrator(velocity, alpha)); 331 | 332 | LinearForm b(&fes); 333 | b.AddBdrFaceIntegrator( 334 | new BoundaryFlowIntegrator(inflow, velocity, alpha)); 335 | 336 | m.Assemble(); 337 | int skip_zeros = 0; 338 | k.Assemble(skip_zeros); 339 | b.Assemble(); 340 | m.Finalize(); 341 | k.Finalize(skip_zeros); 342 | 343 | // 7. Define the initial conditions, save the corresponding grid function to 344 | // a file and (optionally) save data in the VisIt format and initialize 345 | // GLVis visualization. 346 | GridFunction u(&fes); 347 | u.ProjectCoefficient(u0); 348 | 349 | if (!res_flag){ 350 | ofstream omesh("ex9.mesh"); 351 | omesh.precision(precision); 352 | mesh.Print(omesh); 353 | ofstream osol("ex9-init.gf"); 354 | osol.precision(precision); 355 | u.Save(osol); 356 | } 357 | 358 | // Create data collection for solution output: either VisItDataCollection for 359 | // ascii data files, or SidreDataCollection for binary data files. 360 | DataCollection *dc = NULL; 361 | if (visit) 362 | { 363 | if (binary) 364 | { 365 | #ifdef MFEM_USE_SIDRE 366 | dc = new SidreDataCollection("Example9", &mesh); 367 | #else 368 | MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output."); 369 | #endif 370 | } 371 | else 372 | { 373 | dc = new VisItDataCollection("Example9", &mesh); 374 | dc->SetPrecision(precision); 375 | } 376 | dc->RegisterField("solution", &u); 377 | dc->SetCycle(0); 378 | dc->SetTime(0.0); 379 | dc->Save(); 380 | } 381 | 382 | ParaViewDataCollection *pd = NULL; 383 | if (paraview) 384 | { 385 | pd = new ParaViewDataCollection("Example9", &mesh); 386 | pd->SetPrefixPath("ParaView"); 387 | pd->RegisterField("solution", &u); 388 | pd->SetLevelsOfDetail(order); 389 | pd->SetDataFormat(VTKFormat::BINARY); 390 | pd->SetHighOrderOutput(true); 391 | pd->SetCycle(0); 392 | pd->SetTime(0.0); 393 | pd->Save(); 394 | } 395 | 396 | socketstream sout; 397 | if (visualization) 398 | { 399 | char vishost[] = "localhost"; 400 | int visport = 19916; 401 | sout.open(vishost, visport); 402 | if (!sout) 403 | { 404 | cout << "Unable to connect to GLVis server at " 405 | << vishost << ':' << visport << endl; 406 | visualization = false; 407 | cout << "GLVis visualization disabled.\n"; 408 | } 409 | else 410 | { 411 | sout.precision(precision); 412 | sout << "solution\n" << mesh << u; 413 | sout << "pause\n"; 414 | sout << flush; 415 | cout << "GLVis visualization paused." 416 | << " Press space (in the GLVis window) to resume it.\n"; 417 | } 418 | } 419 | 420 | 421 | FE_Evolution adv(m, k, b); 422 | double t = 0.0; 423 | adv.SetTime(t); 424 | ode_solver->Init(adv); 425 | 426 | 427 | if (!res_flag) 428 | { 429 | // output the initial condition of u and dudt 430 | GridFunction tmp_u(&fes); 431 | tmp_u.SetFromTrueDofs(u); 432 | ofstream osol("ex9-u_"+to_string(0)+".gf"); 433 | osol.precision(precision); 434 | tmp_u.Save(osol); 435 | osol.close(); 436 | 437 | // 8. Define the time-dependent evolution operator describing the ODE 438 | // right-hand side, and perform time-integration (looping over the time 439 | // iterations, ti, with a time-step dt). 440 | 441 | bool done = false; 442 | for (int ti = 0; !done; ) 443 | { 444 | double dt_real = min(dt, t_final - t); 445 | ode_solver->Step(u, t, dt_real); 446 | ti++; 447 | 448 | done = (t >= t_final - 1e-8*dt); 449 | 450 | if (done || ti % vis_steps == 0) 451 | { 452 | cout << "time step: " << ti << ", time: " << t << endl; 453 | 454 | if (visualization) 455 | { 456 | sout << "solution\n" << mesh << u << flush; 457 | } 458 | 459 | if (visit) 460 | { 461 | dc->SetCycle(ti); 462 | dc->SetTime(t); 463 | dc->Save(); 464 | } 465 | 466 | if (paraview) 467 | { 468 | pd->SetCycle(ti); 469 | pd->SetTime(t); 470 | pd->Save(); 471 | } 472 | } 473 | 474 | // output u and dudt at every time step 475 | GridFunction tmp_u(&fes); 476 | tmp_u.SetFromTrueDofs(u); 477 | ofstream osol("ex9-u_"+to_string(ti)+".gf"); 478 | osol.precision(precision); 479 | tmp_u.Save(osol); 480 | osol.close(); 481 | } 482 | 483 | // 9. Save the final solution. This output can be viewed later using GLVis: 484 | // "glvis -m ex9.mesh -g ex9-final.gf". 485 | { 486 | ofstream osol("ex9-final.gf"); 487 | osol.precision(precision); 488 | u.Save(osol); 489 | } 490 | } 491 | else // calculate residual from given solution 492 | { 493 | ifstream infile(u_file); 494 | infile.precision(precision); 495 | int d1, d2; 496 | infile >> d1; 497 | infile >> d2; 498 | // cout << d1 << ' ' << d2 << endl; 499 | 500 | GridFunction res(&fes), u1(&fes), u2(&fes); 501 | double res_norm = 0.0, res_norm_tmp = 0.0; 502 | u1.Load(infile, d2); // u at t=0 503 | 504 | for (int i = 0; i < int(d1*res_ns)-1; i++){ 505 | double dt_real = min(dt, t_final - t); 506 | ode_solver->Step(u1, t, dt_real); // true next u 507 | u2.Load(infile, d2); // predicted next step 508 | subtract(u1, u2, res); 509 | res_norm_tmp = res.Norml2(); 510 | res_norm += res_norm_tmp; 511 | // cout << i+1 << " - " << "t: " << t << " residual: " << res_norm_tmp << endl; 512 | u1 = u2; 513 | } 514 | // cout << "final residual: " << res_norm << endl; 515 | 516 | // output the residual norm 517 | ofstream outfile(res_file); 518 | outfile.precision(precision); 519 | outfile << res_norm << endl; 520 | } 521 | 522 | // 10. Free the used memory. 523 | delete ode_solver; 524 | delete pd; 525 | delete dc; 526 | 527 | return 0; 528 | } 529 | 530 | 531 | // Implementation of class FE_Evolution 532 | FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_) 533 | : TimeDependentOperator(M_.Height()), M(M_), K(K_), b(b_), z(M_.Height()) 534 | { 535 | Array ess_tdof_list; 536 | if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY) 537 | { 538 | M_prec = new DSmoother(M.SpMat()); 539 | M_solver.SetOperator(M.SpMat()); 540 | dg_solver = new DG_Solver(M.SpMat(), K.SpMat(), *M.FESpace()); 541 | } 542 | else 543 | { 544 | M_prec = new OperatorJacobiSmoother(M, ess_tdof_list); 545 | M_solver.SetOperator(M); 546 | dg_solver = NULL; 547 | } 548 | M_solver.SetPreconditioner(*M_prec); 549 | M_solver.iterative_mode = false; 550 | M_solver.SetRelTol(1e-9); 551 | M_solver.SetAbsTol(0.0); 552 | M_solver.SetMaxIter(100); 553 | M_solver.SetPrintLevel(0); 554 | } 555 | 556 | void FE_Evolution::Mult(const Vector &x, Vector &y) const 557 | { 558 | // y = M^{-1} (K x + b) 559 | K.Mult(x, z); 560 | z += b; 561 | M_solver.Mult(z, y); 562 | } 563 | 564 | void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k) 565 | { 566 | MFEM_VERIFY(dg_solver != NULL, 567 | "Implicit time integration is not supported with partial assembly"); 568 | K.Mult(x, z); 569 | z += b; 570 | dg_solver->SetTimeStep(dt); 571 | dg_solver->Mult(z, k); 572 | } 573 | 574 | FE_Evolution::~FE_Evolution() 575 | { 576 | delete M_prec; 577 | delete dg_solver; 578 | } 579 | 580 | // Velocity coefficient 581 | void velocity_function(const Vector &x, Vector &v) 582 | { 583 | int dim = x.Size(); 584 | 585 | // map to the reference [-1,1] domain 586 | Vector X(dim); 587 | for (int i = 0; i < dim; i++) 588 | { 589 | double center = (bb_min[i] + bb_max[i]) * 0.5; 590 | X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]); 591 | } 592 | 593 | switch (problem) 594 | { 595 | case 0: 596 | { 597 | // Translations in 1D, 2D, and 3D 598 | switch (dim) 599 | { 600 | case 1: v(0) = 1.0; break; 601 | case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break; 602 | case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.); 603 | break; 604 | } 605 | break; 606 | } 607 | case 1: 608 | case 2: 609 | { 610 | // Clockwise rotation in 2D around the origin 611 | const double w = M_PI/2; 612 | switch (dim) 613 | { 614 | case 1: v(0) = 1.0; break; 615 | case 2: v(0) = w*X(1); v(1) = -w*X(0); break; 616 | case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break; 617 | } 618 | break; 619 | } 620 | case 3: 621 | { 622 | // Clockwise twisting rotation in 2D around the origin 623 | const double w = M_PI/2; 624 | double d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.); 625 | d = d*d; 626 | switch (dim) 627 | { 628 | case 1: v(0) = 1.0; break; 629 | case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break; 630 | case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break; 631 | } 632 | break; 633 | } 634 | } 635 | } 636 | 637 | // Initial condition 638 | double u0_function(const Vector &x) 639 | { 640 | int dim = x.Size(); 641 | 642 | // map to the reference [-1,1] domain 643 | Vector X(dim); 644 | for (int i = 0; i < dim; i++) 645 | { 646 | double center = (bb_min[i] + bb_max[i]) * 0.5; 647 | X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]); 648 | } 649 | 650 | switch (problem) 651 | { 652 | case 0: 653 | case 1: 654 | { 655 | switch (dim) 656 | { 657 | case 1: 658 | return exp(-40.*pow(X(0)-0.5,2)); 659 | case 2: 660 | case 3: 661 | { 662 | double rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.; 663 | if (dim == 3) 664 | { 665 | const double s = (1. + 0.25*cos(2*M_PI*X(2))); 666 | rx *= s; 667 | ry *= s; 668 | } 669 | return ( erfc(w*(X(0)-cx-rx))*erfc(-w*(X(0)-cx+rx)) * 670 | erfc(w*(X(1)-cy-ry))*erfc(-w*(X(1)-cy+ry)) )/16; 671 | } 672 | } 673 | } 674 | case 2: 675 | { 676 | double x_ = X(0), y_ = X(1), rho, phi; 677 | rho = hypot(x_, y_); 678 | phi = atan2(y_, x_); 679 | return pow(sin(M_PI*rho),2)*sin(3*phi); 680 | } 681 | case 3: 682 | { 683 | // const double f = M_PI; 684 | // return sin(f*X(0))*sin(f*X(1)); 685 | return sin(w1*X(0))*sin(w2*X(1)); 686 | } 687 | } 688 | return 0.0; 689 | } 690 | 691 | // Inflow boundary condition (zero for the problems considered in this example) 692 | double inflow_function(const Vector &x) 693 | { 694 | switch (problem) 695 | { 696 | case 0: 697 | case 1: 698 | case 2: 699 | case 3: return 0.0; 700 | } 701 | return 0.0; 702 | } 703 | --------------------------------------------------------------------------------