├── examples ├── README.md └── example01_geomprep_usage.ipynb ├── gallery ├── header.png ├── header3.png ├── profiles.png ├── model_plot.png ├── results_compare.png ├── synthwave-heatpinn.png ├── heat_pinn_doughnotts.png ├── 2023-11-08-19-00-36_EDIT.org.png ├── 2023-11-08-19-00-39_EDIT.org.png ├── 2023-11-08-19-07-16_EDIT.org.png └── 2023-11-08-19-09-09_EDIT.org.png ├── model └── heat-man.h5 ├── LICENSE ├── codes └── pinn-nn-comparison │ └── README.md ├── README.md └── utils └── geomprep.py /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | ## Example 01: Usage of the `geomprep.py` -------------------------------------------------------------------------------- /gallery/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/header.png -------------------------------------------------------------------------------- /gallery/header3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/header3.png -------------------------------------------------------------------------------- /model/heat-man.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/model/heat-man.h5 -------------------------------------------------------------------------------- /gallery/profiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/profiles.png -------------------------------------------------------------------------------- /gallery/model_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/model_plot.png -------------------------------------------------------------------------------- /gallery/results_compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/results_compare.png -------------------------------------------------------------------------------- /gallery/synthwave-heatpinn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/synthwave-heatpinn.png -------------------------------------------------------------------------------- /gallery/heat_pinn_doughnotts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/heat_pinn_doughnotts.png -------------------------------------------------------------------------------- /gallery/2023-11-08-19-00-36_EDIT.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/2023-11-08-19-00-36_EDIT.org.png -------------------------------------------------------------------------------- /gallery/2023-11-08-19-00-39_EDIT.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/2023-11-08-19-00-39_EDIT.org.png -------------------------------------------------------------------------------- /gallery/2023-11-08-19-07-16_EDIT.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/2023-11-08-19-07-16_EDIT.org.png -------------------------------------------------------------------------------- /gallery/2023-11-08-19-09-09_EDIT.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/314arhaam/heat-pinn/HEAD/gallery/2023-11-08-19-09-09_EDIT.org.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Parham Abbasi 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 | -------------------------------------------------------------------------------- /examples/example01_geomprep_usage.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from utils.geomprep import Geometry2D\n", 10 | "from shapely import Polygon, Point" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "# create a polygon\n", 20 | "p1 = Polygon([[-1, -1], [-1, +1], [+1, +1], [+1, -1], [-1, -1]])\n", 21 | "# create a circle\n", 22 | "c1 = Point(0, -0.5).buffer(0.3)\n", 23 | "# bore the circle inside the polygon\n", 24 | "geom = p1.difference(c1)\n", 25 | "# show the *.svg\n", 26 | "geom\n", 27 | "# create Geometry2D object\n", 28 | "G = Geometry2D(geom)\n", 29 | "# make boundary; 100 points\n", 30 | "G.makeBoundary(100)\n", 31 | "# make domain; 500 points\n", 32 | "G.makeDomain(500)\n", 33 | "# show scatter plot\n", 34 | "G.plot()" 35 | ] 36 | } 37 | ], 38 | "metadata": { 39 | "language_info": { 40 | "name": "python" 41 | } 42 | }, 43 | "nbformat": 4, 44 | "nbformat_minor": 2 45 | } 46 | -------------------------------------------------------------------------------- /codes/pinn-nn-comparison/README.md: -------------------------------------------------------------------------------- 1 | # PINN and simple NN performance comparison 2 | ## Introduction 3 | In this work, 2 neural networks with the same architectures, one with pysics-informed loss functions and the other using simple MSE loss were compared. The model properties for the simple neural network (SNN) and physics-informed neural network (PINN) are presented in the following table: 4 | |Model|Loss functions|Data| 5 | |-|-|-| 6 | |**PINN**|Physics-Informed & MSE|(x, y, T) for BCs
(x, y) for Colloc.| 7 | |**SNN**|MSE|(x, y, T)| 8 | ## Performance 9 | Performance comparison between PINN and a simple NN (a neural network without MSE loss) are presented in the following table: 10 | |Method|Training time|Loss|Data Size| 11 | |-|-|-|-| 12 | |**PINN**|66.35 (s)|0.019|BC points: (100, 3)
Colloc. points: (10000, 2)| 13 | |**SNN**|21.0 (min.)|0.020 (noisy)|Training data: (7000, 3)
Test data: (1800, 3)
Val. data: (1200, 3)| 14 | 15 | ## Conclusion 16 | 1. SNN takes a relatively long time to be trained with comparable loss. This makes SNN unfeasible compared to both of the FDM solver and the PINN. 17 | 2. For a 100*100 grid, 7000 data points was used and the computed loss value for the test data, was 0.25 which means poor performance for the unseen data. Also, 7000 data points simply means the grid is almost solved by the FDM method. With fewer training data, the performance becomes worse. 18 | 3. Unlike the data required for the SNN, there is no need to have the value of dependent variable (T) in collocation points used in PINNs. It could be said that these points indicate the domain in which the equation is solved and the non-homogenous terms. 19 | 20 | In summary, not only it is not feasible to use SNNs instead of PINNs for this example, it is not reasonable as a huge amount of data is needed for the SNN to be trained. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # 🔥 $\textbf{Heat-PINN}$ 🔥 6 | 7 |

A Physics-Informed Neural Network, to solve 2D steady-state heat equation based on the methodology, introduced in: Physics Informed Deep Learning (Part I): Data-driven Solutions of Nonlinear Partial Differential Equations.

8 | 9 | ## **Table of Contents** 10 | - [Introduction](#intro) 11 | - [Results](#res) 12 | 13 | 14 | ## Introduction 15 | In this project, a PINN is trained to solve a 2D heat equation and the final results is compared to a solution based on FDM method. 16 | For more detailts about the project read [this](https://github.com/314arhaam/burger-pinn). 17 | ### Problem 18 | The governing equation: 19 | 20 | $$ 21 | \Theta = \frac{T - T_{\textbf{min}}}{T_{\textbf{max}}-T_{\textbf{min}}} 22 | $$ 23 | 24 | $$ 25 | \nabla^2{\Theta} = (\partial_{xx}+\partial_{yy})\Theta=0 26 | $$ 27 | 28 | in the following domain: 29 | 30 | 31 | $$ 32 | D = \\{ (x, y)|-1\le x \le +1 \land -1\le y \le +1 \\} 33 | $$ 34 | 35 | With the following boundary conditions: 36 | 37 | 38 | $$ 39 | \begin{equation} 40 | \begin{cases} 41 | T(-1, y) = 75.0 \degree{C}\\ 42 | T(+1, y) = 0.0 \degree{C}\\ 43 | T(x, -1) = 50.0 \degree{C}\\ 44 | T(x, +1) = 0.0 \degree{C}\\ 45 | \end{cases} 46 | \end{equation} 47 | $$ 48 | 49 | When normalized: 50 | 51 | $$ 52 | \begin{equation} 53 | \begin{cases} 54 | \Theta(-1, y) = 1\\ 55 | \Theta(+1, y) = 0\\ 56 | \Theta(x, -1) = \frac{2}{3}\\ 57 | \Theta(x, +1) = 0\\ 58 | \end{cases} 59 | \end{equation} 60 | $$ 61 | 62 | ## Results 63 | 64 | ### Square geometry 65 |

66 | 67 |

68 | Temperature profiles: 69 |

70 | 71 |

72 | 73 | ### Doughnut geometry 74 |

75 | 76 |

77 | 78 | 79 | ## Performance Comparison 80 | Results obtained from a [9 layered DNN](https://github.com/314arhaam/heat-pinn/blob/main/gallery/model_plot.png) (1000 epochs) and FDM code on a 100×100 grid. The FDM code is written in Python. 81 | |**Method**|**Computation time (s)**| 82 | |-|-| 83 | |PINN|66.35| 84 | |FDM|77.60| 85 | 86 | 87 | ## Note 88 | This implementation is based on [Tensorflow 2.0](https://www.tensorflow.org/guide/effective_tf2) package and made possible by [Google Colabratory](https://colab.research.google.com) GPU. 89 | -------------------------------------------------------------------------------- /utils/geomprep.py: -------------------------------------------------------------------------------- 1 | import shapely 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | import shapely 6 | from shapely import MultiPoint 7 | # import shapely.geometry as geometry 8 | # from matplotlib.path import Path 9 | # from shapely.geometry import GeometryCollection 10 | 11 | 12 | """ 13 | WARNING: 14 | Currently it only works fine with multi geometry shapely objects, have bugs when 15 | there are simple shapely geometries. 16 | """ 17 | 18 | class Geometry: 19 | """Geometry object 20 | The purpose of this class is to convert a shapely.geometry object to a 21 | point cloud of boundary points and domain points that could be used for 22 | our PINN algorithm. 23 | """ 24 | def __init__(self, geometry: shapely.geometry) -> None: 25 | """Initialize the object 26 | 27 | Args: 28 | geometry (shapely.geometry): The shapely freindly geometry object 29 | to convert to set of points. 30 | """ 31 | self.geometry = geometry 32 | 33 | 34 | class Geometry2D(Geometry): 35 | """2-dimensional geometry 36 | """ 37 | def __init__(self, geometry: shapely.geometry) -> None: 38 | """Initialize the object 39 | 40 | Args: 41 | geometry (shapely.geometry): The shapely freindly geometry object 42 | to convert to set of points. 43 | """ 44 | super().__init__(geometry) 45 | 46 | def makeBoundary(self, n: int) -> None: 47 | """This method is used to generate random points on all of the geometry 48 | boundaries. 49 | 50 | Args: 51 | n (int): total number of points on all boundaries. 52 | 53 | Returns: 54 | None 55 | """ 56 | geometry = self.geometry 57 | n += 1 58 | # initialize data lists 59 | N, data = [], [] 60 | # measure the total perimeter of the geometry boundary 61 | total_perim = geometry.length 62 | # create n evenly distributed points on the whole perimeter 63 | distances = np.linspace(0, total_perim, n) 64 | # number of points on each boundary side must be porpotional to its 65 | # length. In other words, the percentage of the length it has. 66 | for geom in list(geometry.boundary.geoms): 67 | perim = geom.length 68 | N.append(int(n*perim/total_perim)) 69 | # walk on the perimeter 70 | for n, geom in zip(N, list(geometry.boundary.geoms)): 71 | distances = np.linspace(0, geom.length, 5*n) 72 | distances = distances[list(np.random.randint(0, 5*n, n))] 73 | for d in distances: 74 | data.append(*geom.interpolate(d).coords) 75 | # generate padnas.DataFrame object for boundary points 76 | self.boundaryDataFrame = pd.DataFrame(data, columns=["x", "y"]) 77 | # set the BC value for each point to default: 0. 78 | self.boundaryDataFrame['value'] = 0. 79 | return None 80 | 81 | def makeDomain(self, n: int, tolcoef: int = 4) -> None: 82 | """This method is used to randomly distribute points on the main domain 83 | of the geometry. 84 | 85 | Args: 86 | n (int): total number of the points inside the geometry domain. 87 | tolcoef (int, optional): For cases with a hole or subtracted geometry, 88 | use this parameter to recover the number of points. Defaults to 4. 89 | 90 | Returns: 91 | None 92 | """ 93 | geometry = self.geometry 94 | # find bounds or envelope corners 95 | xmin, ymin, xmax, ymax = geometry.bounds 96 | random_domain = np.random.rand(tolcoef*n, 2) 97 | # defining the `envelope` as the biggest rectangle that covers all over 98 | # the shape, here we distribute the random numbers over the envelope 99 | # that covers the shapely.geometry object. 100 | random_domain[:, 0] = random_domain[:, 0] * (xmax - xmin) + xmin 101 | random_domain[:, 1] = random_domain[:, 1] * (ymax - ymin) + ymin 102 | # covert the `numpy.array` random points to `shapely.MultiPoint` object 103 | # so they could be easily intersected with the desired geometry 104 | random_domain = MultiPoint(random_domain) 105 | domain = shapely.intersection(geometry, random_domain) 106 | # covert to `numpy.array`, maybe a `pandas.DataFrame` in future 107 | self.domain = np.array(list(map(lambda p: [p.x, p.y], list(domain.geoms)))) 108 | return None 109 | 110 | def plot(self): 111 | """Plot the boundary and domain points, as an scatter plot. Useful to 112 | check if everything is generated correctly, according to the desired 113 | shapely.geometry. 114 | 115 | Returns: 116 | None 117 | """ 118 | # just making the xs & ys easier to call. 119 | xb, yb = self.boundaryDataFrame['x'], self.boundaryDataFrame['y'] 120 | xd, yd = self.domain[:, 0], self.domain[:, 1] 121 | # scatter plot 122 | plt.scatter(xb, yb, c='k', marker='x') # black 'X's for boundary points 123 | plt.scatter(xd, yd, c='r', marker='.') # red dots for domain points 124 | return None 125 | --------------------------------------------------------------------------------