From b3cd6d59d03108ef3a20c284f74c931f69d54f63 Mon Sep 17 00:00:00 2001 From: Dario Coscia <93731561+dario-coscia@users.noreply.github.com> Date: Tue, 20 Dec 2022 10:15:37 +0100 Subject: [PATCH] Tutorial 1 (#58) --- docs/source/_rst/tutorial1/tutorial.rst | 301 +++++++++++++ .../tutorial_files/tutorial_25_0.png | Bin 0 -> 9567 bytes docs/source/index.rst | 1 + tutorials/README.md | 6 +- tutorials/tutorial1/tutorial.ipynb | 406 ++++++++++++++++++ tutorials/tutorial1/tutorial.py | 223 ++++++++++ 6 files changed, 934 insertions(+), 3 deletions(-) create mode 100644 docs/source/_rst/tutorial1/tutorial.rst create mode 100644 docs/source/_rst/tutorial1/tutorial_files/tutorial_25_0.png create mode 100644 tutorials/tutorial1/tutorial.ipynb create mode 100644 tutorials/tutorial1/tutorial.py diff --git a/docs/source/_rst/tutorial1/tutorial.rst b/docs/source/_rst/tutorial1/tutorial.rst new file mode 100644 index 0000000..e9b1b77 --- /dev/null +++ b/docs/source/_rst/tutorial1/tutorial.rst @@ -0,0 +1,301 @@ +Tutorial 1: Physics Informed Neural Networks on PINA +==================================================== + +In this tutorial we will show the typical use case of PINA on a toy +problem. Specifically, the tutorial aims to introduce the following +topics: + +- Defining a PINA Problem, +- Build a ``pinn`` object, +- Sample points in the domain. + +These are the three main steps needed **before** training a Physics +Informed Neural Network (PINN). We will show in detailed each step, and +at the end we will solve a very simple problem with PINA. + +PINA Problem +------------ + +Initialize the Problem class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The problem definition in the PINA framework is done by building a +phython ``class``, inherited from one or more problem classes +(``SpatialProblem``, ``TimeDependentProblem``, ``ParametricProblem``), +depending on the nature of the problem treated. Let’s see an example to +better understand: #### Simple Ordinary Differential Equation Consider +the following: + +.. math:: + + + \begin{equation} + \begin{cases} + \frac{d}{dx}u(x) &= u(x) \quad x\in(0,1)\\ + u(x=0) &= 1 \\ + \end{cases} + \end{equation} + +with analytical solution :math:`u(x) = e^x`. In this case we have that +our ODE depends only on the spatial variable :math:`x\in(0,1)` , this +means that our problem class is going to be inherited from +``SpatialProblem`` class: + +.. code:: python + + from pina.problem import SpatialProblem + from pina import Span + + class SimpleODE(SpatialProblem): + + output_variables = ['u'] + spatial_domain = Span({'x': [0, 1]}) + + # other stuff ... + +Notice that we define ``output_variables`` as a list of symbols, +indicating the output variables of our equation (in this case only +:math:`u`). The ``spatial_domain`` variable indicates where the sample +points are going to be sampled in the domain, in this case +:math:`x\in(0,1)`. + +What about if we also have a time depencency in the equation? Well in +that case our ``class`` will inherit from both ``SpatialProblem`` and +``TimeDependentProblem``: + +.. code:: python + + from pina.problem import SpatialProblem, TimeDependentProblem + from pina import Span + + class TimeSpaceODE(SpatialProblem, TimeDependentProblem): + + output_variables = ['u'] + spatial_domain = Span({'x': [0, 1]}) + temporal_domain = Span({'x': [0, 1]}) + + # other stuff ... + +where we have included the ``temporal_domain`` variable indicating the +time domain where we want the solution. + +Summarizing, in PINA we can initialize a problem with a class which is +inherited from three base classes: ``SpatialProblem``, +``TimeDependentProblem``, ``ParametricProblem``, depending on the type +of problem we are considering. For reference: \* ``SpatialProblem`` +:math:`\rightarrow` spatial variable(s) presented in the differential +equation \* ``TimeDependentProblem`` :math:`\rightarrow` time +variable(s) presented in the differential equation \* +``ParametricProblem`` :math:`\rightarrow` parameter(s) presented in the +differential equation + +Write the problem class +~~~~~~~~~~~~~~~~~~~~~~~ + +Once the problem class is initialized we need to write the differential +equation in PINA language. For doing this we need to load the pina +operators found in ``pina.operators`` module. Let’s again consider the +Equation (1) and try to write the PINA model class: + +.. code:: ipython3 + + from pina.problem import SpatialProblem + from pina.operators import grad + from pina import Condition, Span + + import torch + + + class SimpleODE(SpatialProblem): + + output_variables = ['u'] + spatial_domain = Span({'x': [0, 1]}) + + # defining the ode equation + def ode_equation(input_, output_): + + # computing the derivative + u_x = grad(output_, input_, components=['u'], d=['x']) + + # extracting u input variable + u = output_.extract(['u']) + + # calculate residual and return it + return u_x - u + + # defining initial condition + def initial_condition(input_, output_): + + # setting initial value + value = 1.0 + + # extracting u input variable + u = output_.extract(['u']) + + # calculate residual and return it + return u - value + + # Conditions to hold + conditions = { + 'x0': Condition(Span({'x': 0.}), initial_condition), + 'D': Condition(Span({'x': [0, 1]}), ode_equation), + } + + # defining true solution + def truth_solution(self, pts): + return torch.exp(pts.extract(['x'])) + + +After the defition of the Class we need to write different class +methods, where each method is a function returning a residual. This +functions are the ones minimized during the PINN optimization, for the +different conditions. For example, in the domain :math:`(0,1)` the ODE +equation (``ode_equation``) must be satisfied, so we write it by putting +all the ODE equation on the right hand side, such that we return the +zero residual. This is done for all the conditions (``ode_equation``, +``initial_condition``). + +Once we have defined the function we need to tell the network where +these methods have to be applied. For doing this we use the class +``Condition``. In ``Condition`` we pass the location points and the +function to be minimized on those points (other possibilities are +allowed, see the documentation for reference). + +Finally, it’s possible to defing the ``truth_solution`` function, which +can be useful if we want to plot the results and see a comparison of +real vs expected solution. Notice that ``truth_solution`` function is a +method of the ``PINN`` class, but it is not mandatory for the problem +definition. + +Build PINN object +----------------- + +The basics requirements for building a PINN model are a problem and a +model. We have already covered the problem definition. For the model one +can use the default models provided in PINA or use a custom model. We +will not go into the details of model definition, Tutorial2 and +Tutorial3 treat the topic in detail. + +.. code:: ipython3 + + from pina.model import FeedForward + from pina import PINN + + # initialize the problem + problem = SimpleODE() + + # build the model + model = FeedForward( + layers=[10, 10], + func=torch.nn.Tanh, + output_variables=problem.output_variables, + input_variables=problem.input_variables + ) + + # create the PINN object + pinn = PINN(problem, model) + + +Creating the pinn object is fairly simple by using the ``PINN`` class, +different optional inputs can be passed: optimizer, batch size, … (see +`documentation `__ for reference). + +Sample points in the domain +--------------------------- + +Once the ``pinn`` object is created, we need to generate the points for +starting the optimization. For doing this we use the ``span_pts`` method +of the ``PINN`` class. Let’s see some methods to sample in +:math:`(0,1 )`. + +.. code:: ipython3 + + # sampling 20 points in (0, 1) with discrite step + pinn.span_pts(20, 'grid', locations=['D']) + + # sampling 20 points in (0, 1) with latin hypercube + pinn.span_pts(20, 'latin', locations=['D']) + + # sampling 20 points in (0, 1) randomly + pinn.span_pts(20, 'random', locations=['D']) + + +We can also use a dictionary for specific variables: + +.. code:: ipython3 + + pinn.span_pts({'variables': ['x'], 'mode': 'grid', 'n': 20}, locations=['D']) + + +We are going to use equispaced points for sampling. We need to sample in +all the conditions domains. In our case we sample in ``D`` and ``x0``. + +.. code:: ipython3 + + # sampling for training + pinn.span_pts(1, 'random', locations=['x0']) + pinn.span_pts(20, 'grid', locations=['D']) + + +Very simple training and plotting +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once we have defined the PINA model, created a network and sampled +points in the domain, we have everything that is necessary for training +a PINN. Here we show a very short training and some method for plotting +the results. + +.. code:: ipython3 + + # simple training + final_loss = pinn.train(stop=3000, frequency_print=1000) + + +.. parsed-literal:: + + sum x0initial_co Dode_equatio + [epoch 00000] 1.933187e+00 1.825489e+00 1.076983e-01 + sum x0initial_co Dode_equatio + [epoch 00001] 1.860870e+00 1.766795e+00 9.407549e-02 + sum x0initial_co Dode_equatio + [epoch 01000] 4.974120e-02 1.635524e-02 3.338596e-02 + sum x0initial_co Dode_equatio + [epoch 02000] 1.099083e-03 3.420736e-05 1.064875e-03 + [epoch 03000] 4.049759e-04 2.937766e-06 4.020381e-04 + + +After the training we have saved the final loss in ``final_loss``, which +we can inspect. By default PINA uses mean square error loss. + +.. code:: ipython3 + + # inspecting final loss + final_loss + + + + + +.. parsed-literal:: + + 0.0004049759008921683 + + + +By using the ``Plotter`` class from PINA we can also do some quatitative +plots of the loss function. + +.. code:: ipython3 + + from pina.plotter import Plotter + + # plotting the loss + plotter = Plotter() + plotter.plot_loss(pinn) + + + +.. image:: tutorial_files/tutorial_25_0.png + + +We have a very smooth loss decreasing! diff --git a/docs/source/_rst/tutorial1/tutorial_files/tutorial_25_0.png b/docs/source/_rst/tutorial1/tutorial_files/tutorial_25_0.png new file mode 100644 index 0000000000000000000000000000000000000000..75df4d4cf9a258e9e54802c6bcfb5a8510f0a616 GIT binary patch literal 9567 zcmZ`<2{@E**B^u|5wa!JAW2O2>=Lp>g^I#R8T&f+eM&{PlxXbP%UWbNCbEwuL>T)r z*~dQi@1Fke_kQ2?UGH~YW;`?Z{oK#F&wbAC{LVQOe#hYEDf+YY5D4Uyj<$v&1VRx2 zu1P0p!29YK^Ahk+-b2&G!^j2U;r-a%8lwN$!`0En!_m&_yqC2*($2+MN?cZ4^2&Kz z4-Z$Q0u1K#?+M~A?l!PXf4Ho`B6P0Wrbq~cfsOp4fFve!K_G%S9gXY8KFQ0Yz5&Kt zb;oOi!aNtQ&<&ZWQ-+=oJAeH&<>h;i*bDN$8DGb<$JxuRE*BmS!58bCTwF%rF26YL z-uZn02Q@o;VL!Fz(9>}ZZODbk40NYuZ)mR{9>#vp5z(`}C~t!p>u!0%hYOIB-`G`{ z-VfMFL1J2xkzrxlGZAam5Ed3YKKcNb*h?R=oGjt2|LfAtg40!Uety2lVX(*|;R2NP ze!RH(!@1-R+utjT*wx{0qI&UH)~Xp>QM0mk6lO0&O(pZFSRYpd`qM?Qc z^d*dHTz)49=^Wth{&g@rEKGwDPJ;;x3r(ejYY6IIB5G=6g#NuGNfMtc>k?KSyd|wy za!x-Gg0hYUaFf3CWlL4K%Ei>ewe<@J_BdJOn-fs{1qVxB=adXPn@4Hu#5Fa3$U!BE z`-Ch=iPfUY?Zwk9YwyAs51j1^ZIifK)~5n35}?p29u{trgoIZ8(wTUd(M3-YC=|mI zi(>9k&E@6s;+^P;wJ+9VVOe{DUbo1Xm!}!+nWKsqkX!pmKKqeL4?)JuA&Gb8DI!=UcJfU7gh1(zbjj&d3vF zfA6cav1us{>xKKS2Evx%VbV?Kf;f)3+HRQl z*iaNxj?0$nV3UORtO@w11iTsnkBy8Bt`eNKYwI@nxQS))GB&3922M-6$8oUCvx}F~ z*#(E}bQHWQ34_7%265eXwEdma-nw?&{pA`O4dzCX#0rX`Ek19iGMSn&-jTPbIWwn$ zytav0IOFEkQfEXwB$8(ATFR-=1=d$|ZW76Tp9-V0C~Vn0{->6G6iD0K45?@W*8YXk)a!c+<}B?Wn#dU)Kc zD`hr$aV2?wi`!A|DW}A0T3>W~=C^`z-}8T*!oxyK!^|H}emFw0mBice!8J0HVN|Md zMhYn--vmXog*INdtKUI~X_MBiCtkVhw%{tS!cum5Iy|Ybu!P%V7!U456Dh?1#CDwwvPJU2;Y)}wqX>H4gcE8E#ng^ z*!O{oU%&eBH~BO9o4*bV6DHrC+DJ=6j7LLi>-=uEmWx1H4gU3QtBxKN*i%CM8KeA6 zPYpqOuxwJh(~X+1ZaWuLdJ`(3tS>Sy6N{HdVws%Sd`Y9H&dx??_pez`$Tgi)=`T#( zeaE6~YHIksO!JX?wG#_V{l?#+BZk;VV3SjWr4SUkgC7(ji3z`=DIKOF|tedNfFvV zBnb!mv$I$f)#zIxy~Ir!4#z8mVut#OI?rt5dxNa}4x39oFEOSf$s_V;jAM;2r4^&UdEm zkrPFi4p&^bV4z6BvX)jtA0Wj;7m4fW;CWzT@<-=Xmwfq(Jg)sM{eWY>rKb+HL-_;O z^f7~Un^(bObel`*DIsA#umZ~C4|C@x2*(=SY4%2c^*!!D{nA3c}h2BT_BV2WgZ}~kfZ^qS% zKhS_iQjXua_v_o0(XR>2a&hZF_eKLS8xTm!TehmrrCTr4`eMIVeNtf6)=*bRtqLe! z#>`}H)u2l;aWM=X*UawS&^1&VzgDB%*gcPZq1>wz8M9S=i-pdJJ|K-O1^!fxug$W- z*uEp@zG%MP`_mmlb&}je#cZ5AxOC0r2DrxARSnd*Qk%hw>cPRqE_3t!56b@f^@9-%ChUc)M=oU0+Y-J@)fI)3??g|7*CDEHv z$0-o@`W^V0lqi2t-c}$S<+6+<{``%NdC77VZamn(u5*`LD#2xXHEnsy_1-B{5iznj z;|pn;-Aqok;&(LyF6W8xO7YN~JCUl)K|*jaI6}m!bVHi3Tni1ya;Ie7ULlL5iT?Bl zeIAvc6TMYOnsBotA0C-#=@$wBmyvk>wQ2!xP}ch~6>5092T4Kti7sirC1-Q8&%hcY}D7ey6)LeU)+YYkIYuXUMwFJFky-%ym; zJ2=<1oLtbTq&X9c;>Wp#n5wzewFsfg+1TG85HVDTG0W$oTjb@2gkgAnopxo5(AQ#IRQBj#Vj=^t)1dBVowA%0$)dC?b~xl> zVRm1c<_ig_Cu#}CG#)y$1Ofkzz1Pc16_yOg5>MFc4@U@*yvdFF)aI>@Nq;;)L8sWh z{}1L;7m5`7u!j#{Y(^rlbB^s!YAOW}TJ90Dh`I8bX2cQZ%Ht;XfbzilL7~@S%qOH8 z{sHBF>O$T^uZ~2q_;|BiSJw zIu+Xtf-P?)mo}{zMZIg6vNhrXC9{$D(=KBIzW9b|a&#}9Mbl)aQt1RkPS*$y(|EHg z@20|z`P-HWymYZ1TkJ*F`!>USkChL!kwk5oJ`U%{?JY?nK>5$`#zB&`oQKF^U-QJU zP!kjCBZ;$WOy8U4>4r-}T6U(N22|~zh2-kipWpm&%|@VG46hzguy$CyGqKH3J#QEV zeOw@7Q2X}en}KR-RsNx*`eR!z>MFZ~IhQo4gUPCZN=*M^uZG|c0M7{7Ufjh8#>PJ8 zy8V9=$6n_;E2h=sy;PVgmthgyJ4>wPtNXp=$r{qx0>@L@o~Mjdcc0xH8&@;C#mF4_ zrzz^Wdzo~p_J^LhXz7O$l_nR>@G14jL4l zoKcw%MZgS`t#bEaVy0$-_Z^}JEi7~ZTi#9gFBf< zx_ZSOtg+MJK_`StouKbr_^>YZqY3YY_PLITX26wKJWZkQXU~v&Z4u3<2n6Ex4h$-n zDYK(opS%w@`~F?vmqo%wXw*apcJLgg-c#SDDboKX@pt5A~>uH?5Goh#bqp6uVE$Sz+ zRgOBds*L*;oZ6D1KWmX71l_ts6v`4`{*{W}u}}kO_$1Bj2a=(}$NX*B2|yM-;|zQj zQ-3CcQLLtN@9q#zMu{|R|M{~6SX*!b#8ohZFz$1%%&v{-_g7-EI8sH2|9aBm-n!eQvvcy-H^5tJfs!&(y5hR} zu*AJn0F7^*(1O&VBoG55xV^-o0-6e(U;SPwc2y^iN9xk67MHVFf+F+Pg*ECkt3S^w zLG`nLqE?fW7^Ia_oloDHNgkO=&Zo416uEU3dUcuqnz(Q88N_hVDPOVVQ~czAsOt`T zTd$#Ymf&GIZ2o)*nfH3}Vke<=`QamYmdJ_yY$uL8W~**G>B|}z+5jppYsAjERl2UK zHozsoA2&Dm^?2b)E6YJ+1YRn%l56^J~O#WJB%6_5?tpY%G zDuIplb@eJ!Iw`j=5(hgKdvAtvs6iIgntJ2j!EW=0E&BALrycZhTY{dbZ78he1Gj4R z(v=`jCw>(Zc>8Z*&abN_1HU-s*)WU&os)VulDAu{-V2KmvE*VJazr3+MKL6j=umGYt&|-Z`B<7G5Htf)bw$6TtdQ` zw7rDC937q|(fg=kV%LqDaXGa)#-_lk_Z5%lv-nmW;M_9V0#U!JHP781^;3DUv-rbh zd^Z{1_3)-sp*noV=byFvVo!56Hpa%3NBKr0dCayekq7P$hu%|WB^sa6}h&N z@Aq!#V(d6(L*AeHfy8_cMcIHgnvAe(VxfTevt+*cw}^MY)+5lg?M3SK5QOEiZ7{7JxCULN(O8+BMgXe zb6(phJSJ*#C$r1Cp=lIdZY#QD@JI_pRuCiXIKc78lCyxPKCq+Jdv#>e=$x`AvM(Cr zXCANp&S*lfzj~v=SxHhM&HB-fdm|fz$S9m_8v42@*xlvCgDa`kTLI;9{8QCB8$)JZ zt>EkvBPrn-Vu&o3zj}9PA_7A2GI-pqd;^$h$DcNa-AdOA zvN4&JQe)65PKcPKAx5dB9BLSN5@I{@D*r{|k$F;+?BnZ}uveBw@B}nif`IxgeZWC$1y- z^sHh-@L4X4`tLX^R5= zNyVmVYh*_*Fbh8!MIS)Gn_1p|K6hO$7U}#BSw!zcQQ0hCF)Z(W@A)G;OGTG!1TDyu z8$)!lcl07-tfzi&RJyq`u@M|+a*K;{sW1lzM{<;&1lOe z1OcDsVUDD2ZEK^;P>P-S(acqkDQBK9WcAs64&6TYdFrRc;a|Ql9V^xA$a&z=6?ih> zcNjrmwv-p!8X6i(p>Lkh+V+8a2R`-=R1lOl=WOr_TShr}g*WBlYap__4?mOFwc&YV zzUoF$d(=}FA5H3X;*`E7x)ajX{+`HoYXU~&_rX+7N)Mt%X)njD zFY7Rv)R;eKrQ@G#Y3#)PQ%i6$d;Iw0;B44IL`E+ae!V~UTwVd9y^u)-t}$V>6UjDOf$m=1_wdg zenIjMyx*$t7m@CirucX&ECkw-%Ro`A{5*}v7HPLZY`&FJfmVJMP26AI6i~VUUP)ImC;pJir&YbpTysY10cb2(mg;l*CouXjgK#MByVNoCAW;2 z{!q?Ok&%}d+`7{IU0zADTD7BmX|`D;;x{s{9r(}znE=LWyL#3e+`?Ex$Q7tPVUChg z^~Ke&3IK4>*r{HNW#4Up$pv{&`=>W{Gj4$)?=vK*zN4%2*}fKb*FV}aWFdz&FE&y`$Py5P8c4`z_X`@G>#UsC;a5TUD5pAodzI5ZTi=d+ zW>oV?S(WBw=cl(Su{|vDK3zN7e{jMdx*1f(c02Aiy3A?atmg2?C;(Bj3Rz}n}|B;+Ja(az@I(T zkdmwy#GzuD0LaBxhx+@&aT7od0roy=Ob4^u8zMvgAMVC<+vru1N2DDll1xRV9$6tC z$PgA^hvqY^_xc8zQP7I!b8nF-2bOzulicUdvj3;5p_b!a~skfb*2P z$an!1rkwrOjZ}t9M!@}yGM9-dNF?fIKGsc5HCu*b`+tBN)Ssd^%nHs>0_K4wQriiiIL0shs$ zLQ4X=WnE_Xoxz*4N417`JPDIb;B*2eM<>v!((cuzyquim`8Mq~%@LbP{F^sD0WZQs z!vRVuAzRLi&!gLpeVt@t0!^~EytD2;qdRa?^Qvn}p>n$3_fP>UJovA;C$pPE8dY4} z-Fmc`_iInlo&I}rX&u*}$B5B9hP|Kto!zvZI4#P*jOmMfY}gs#(EP>Sx#otHw}ZZ- zdD-6|Ozs5l|I6fk+hz{`wFQAEwNgx?ts z%oe_~E`?*?^>2$?tzHmoI^SXwl)RrW*T7G%NO{UbA3q-Wyo^7d(pAVUpP3(_iG3G8 zNk`%swsUXeltTQ^AY)}P-qle6O!jo1NUl0iSu zQx^cQ$nnrn7m#fx&A+$VAfDPSxH8rv1Cp=d<+IwFqF_8Mbl}@z=?Ay&JN7BlfD>*s z>x$KKME&JnEOd34ly|<<7>4;i5%lV9co&eMF?In^j<<*zkW>OE>ua^`dq!wszq*p) ztW;ykFrJ#FC#=OrURgQ?aCg1~gi8)y^39Yo-2GQ(8gYAp7i4{%YA6_Mw)o;`Y6T|v zLKV7jccsi4f80yI{eW+)`ik8@I2Iut)3yB8XLsd7z;UmV1^i3OWN>mvZH6Rx{zZVv zAgR^f&Wg|1>Cb zdJETiGJpaNfacy4bi+{n?rtmfQlMf#W;Uz)TvlJsfL#{L$9Y#~a1tt3j6jVemPosC?#P55C zR=CP68=7(M51rX{lHoC1vm)0YFKVFp2V3E(|Dj|_5KMBzUO&%rNj_>=QxMnmgR8Nt z-4gH^M=@1g+3gDA!dIu-ilVs90Xnm!w5E!#jtuXX@(<|2S*-%CD3EM(c|JZ|`T}}f zCYDsL+@2H|F0LEqq~o0L7gr$d$;fYr0X`C(fd6e&SEL0(cz$&bxtKmTD=X&4@M6c#sOfXaMxp$ef_#7BP2xg5XE|n=KCVUC_ zJhp=XrK^1Bg@tXf@nVn8-b)J$EmnT@*~ejvY0TtH6sSs2TdE~^MgHkSiFVDW6IJ!E z>(3{v{Y|Cmu95_)&1lUivLL}KKR1$*?>Zf6uS#CH@L28iR=D;DZKwCxPHOYYs&od> zz}T4kyj)x5PFwHiz)OAwejS!PNM>SO?Tf9&4Ok@blt1I1jG=s~NLZm)!fLubi`G zNJK}S(3+c_ePM-!vgTB0eX}|!S&8HyNa1CNukU=TA&n&RDs4Yz2~IWAu+Kk%@w!iw zu-X39B*w$=ZCO@h8>n-}WUlCq6dQ4FMxc07kPbKQB?FziEBQ0ulK>q}|-zR6@VWH}t z`eLGbmr0eba{Qd}Uow37Wxhpcg__WK>diLPdc5?wJ-x)_6ds-q3vRqjjQM#IuA%;E zsw4BtZ0~^QsZgJ6J+9o>A+}%VK zUaqlRDhTK_Je|yO2fZAd?>X*>;!$d~hxsRH2z(L>wT9)fuOln37P-yOV`IZ6YPT8S>Te;lLqvPueCsp>&izD-@%vl0RHqwf)iWxTCySU(|NctqjUCLUIi5wzR;QW&KsUR-VgxsKW?1z zhNL+kwZI|feQz=J$9I+>03V?$?w@(F{6(uRA>yBG%9cwM)^s}rBDAw2q9pd73iSz# zqHDz4vvUU4cDoL*;LBdUNNS&db8b%{P`UxM(60)goxHi??goXf-zKkiR~^X1D)jfV zApzxr5a@Jt*VKIR?^ZZ+MW28t&CEO{Hx$FU$dQSKPMNH@IO10L14KE`Z}*q=sZ4P% zO3uMP!H(EO4k24jG0&E7auUq68nxiGL055(ZEIc93^x}djx}^u)dJTta~58K`2OFm zbq)84+uPM4rw!o;!iIYk3laP>I;v1FCb)*ik*Qz|=)q_4{e5CsD}c8D?rKvMFUqq^ z?Kj}=Ck@d{Jz}-hlPUG?EVxQ^sZpyz4`re-g_5NkN=1SHR3QDFa?NYdqi21BKB7k# zGBHX|5hIkVo%B*QU&p$G&i?7}ld8$EsL4%JmFkc!l}Bu&(SmNqjJbg9y<=9q7TvJ8&qMu#%zMj`9}2#n}e-irra6X&4Mvg z90w&6wwVITA3{bc)2d8V1CBzXvA(E%r;t-9zkiD(k?uJWq*QW=IKD4r>5+cuW~n}L zfDx<<9gq=Y+!*ii1IQ4|#p9u?%5p4OHq zjppm84M94Km*igGm>&qDCKe%`W01};qWIO?wg{Jr6M^!v4k4V$$eYQ?BouT(#XmLp zC>g1kjO?J?VTLS*TxlSZRb=p^do=>t#mlG^$&5eeDsokzXABH%2X#U)&WPsleIMOe zF48|utmFf#DOx5{4O&d5bPZL$-Brbl+E+wvqxL6z>iY^uR|BhISAR)t{gyuYxHkbS ztronX_3rT@M*0s!=;Xc?;tQgj@=~@lZ8ghpLUO)}fjRuwIg&5+8?DoU;Xkm_vcO)4ML zq2lOp;D|5*UxYo*`i4Dow3b_141`?kr=F1};PX*)VA<%w1lK@#v|HCQ)g0tTUXpr* z;=iPpykqcJYS0%u34Q>hHk!ce;$zlFhX#q819C~lRlek9bz%%JyPGJ{Z{mmtD~1vf zr4M=S7o4--G(VuiQ;Ek7Qy)p!EA`A((Tv9gL{#t4EbK`txC&~@rPC5cR7eNZbG~Vm zpg46@F&5JR4Eip-VKcDpYH1wyFdl2&A1EwA%f;^VuyJSh_r450={zxED16VyzH?X1 z6FHivxL`@;x$%VA&%9g2zB7A;XXib3_{o-`+=CsJJ11*tv#Nsw`cx%)Dt=+rViwa3 zM$`IleoE4--MD2>yOO@rkqe#h;@_GrMm>B$eROltWObLBX74K>*JMIBzv~6hKAHRw zcGSrGNfIVG3r2otxWe3DOGENaS5BzLic?z9JUlVXMimo5soG6;Gta(WdVpO})?5H0 mGuQqvko_-A`{(jVwS{{O_g;S~1N_(tqN8b`QKWA9?7slxdhne9 literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst index c82690f..1e841bb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,6 +35,7 @@ solve problems in a continuous and nonlinear settings. :numbered: :caption: Tutorials: + Getting start with PINA <_rst/tutorial1/tutorial.rst> Poisson problem <_rst/tutorial2/tutorial.rst> Wave equation <_rst/tutorial3/tutorial.rst> diff --git a/tutorials/README.md b/tutorials/README.md index d1b09e3..dc02905 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -5,8 +5,8 @@ In this folder we collect useful tutorials in order to understand the principles | Name | Description | Type of Problem | |-------|---------------|-------------------| -| Tutorial1 [[.ipynb](), [.py](), [.html]()]| Coming soon | | -| Tutorial2 [[.ipynb](tutorial2/tutorial.ipynb), [.py](tutorial2/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial-1.html)]| Poisson problem on regular domain using extra features | `SpatialProblem` | -| Tutorial3 [[.ipynb](tutorial3/tutorial.ipynb), [.py](tutorial3/tutorial.py), [.html]()]| Wave problem on regular domain using custom pytorch networks. | `SpatialProblem`, `TimeDependentProblem` | +| Tutorial1 [[.ipynb](tutorial1/tutorial.ipynb), [.py](tutorial1/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial1/tutorial-1.html)]| Introduction to PINA features | `SpatialProblem` | +| Tutorial2 [[.ipynb](tutorial2/tutorial.ipynb), [.py](tutorial2/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial2/tutorial-1.html)]| Poisson problem on regular domain using extra features | `SpatialProblem` | +| Tutorial3 [[.ipynb](tutorial3/tutorial.ipynb), [.py](tutorial3/tutorial.py), [.html](http://mathlab.github.io/PINA/_rst/tutorial3/tutorial-1.html)]| Wave problem on regular domain using custom pytorch networks. | `SpatialProblem`, `TimeDependentProblem` | diff --git a/tutorials/tutorial1/tutorial.ipynb b/tutorials/tutorial1/tutorial.ipynb new file mode 100644 index 0000000..b04ad3a --- /dev/null +++ b/tutorials/tutorial1/tutorial.ipynb @@ -0,0 +1,406 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Tutorial 1: Physics Informed Neural Networks on PINA" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "In this tutorial we will show the typical use case of PINA on a toy problem. Specifically, the tutorial aims to introduce the following topics:\n", + "\n", + "* Defining a PINA Problem,\n", + "* Build a `pinn` object,\n", + "* Sample points in the domain.\n", + "\n", + "These are the three main steps needed **before** training a Physics Informed Neural Network (PINN). We will show in detailed each step, and at the end we will solve a very simple problem with PINA." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## PINA Problem" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "### Initialize the Problem class" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "The problem definition in the PINA framework is done by building a phython `class`, inherited from one or more problem classes (`SpatialProblem`, `TimeDependentProblem`, `ParametricProblem`), depending on the nature of the problem treated. Let's see an example to better understand:\n", + "#### Simple Ordinary Differential Equation\n", + "Consider the following:\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "\\begin{cases}\n", + "\\frac{d}{dx}u(x) &= u(x) \\quad x\\in(0,1)\\\\\n", + "u(x=0) &= 1 \\\\\n", + "\\end{cases}\n", + "\\end{equation}\n", + "$$\n", + "\n", + "with analytical solution $u(x) = e^x$. In this case we have that our ODE depends only on the spatial variable $x\\in(0,1)$ , this means that our problem class is going to be inherited from `SpatialProblem` class:\n", + "\n", + "```python\n", + "from pina.problem import SpatialProblem\n", + "from pina import Span\n", + "\n", + "class SimpleODE(SpatialProblem):\n", + " \n", + " output_variables = ['u']\n", + " spatial_domain = Span({'x': [0, 1]})\n", + "\n", + " # other stuff ...\n", + "```\n", + "\n", + "Notice that we define `output_variables` as a list of symbols, indicating the output variables of our equation (in this case only $u$). The `spatial_domain` variable indicates where the sample points are going to be sampled in the domain, in this case $x\\in(0,1)$." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "What about if we also have a time depencency in the equation? Well in that case our `class` will inherit from both `SpatialProblem` and `TimeDependentProblem`:\n", + "```python\n", + "from pina.problem import SpatialProblem, TimeDependentProblem\n", + "from pina import Span\n", + "\n", + "class TimeSpaceODE(SpatialProblem, TimeDependentProblem):\n", + " \n", + " output_variables = ['u']\n", + " spatial_domain = Span({'x': [0, 1]})\n", + " temporal_domain = Span({'x': [0, 1]})\n", + "\n", + " # other stuff ...\n", + "```\n", + "where we have included the `temporal_domain` variable indicating the time domain where we want the solution.\n", + "\n", + "Summarizing, in PINA we can initialize a problem with a class which is inherited from three base classes: `SpatialProblem`, `TimeDependentProblem`, `ParametricProblem`, depending on the type of problem we are considering. For reference:\n", + "* `SpatialProblem` $\\rightarrow$ spatial variable(s) presented in the differential equation\n", + "* `TimeDependentProblem` $\\rightarrow$ time variable(s) presented in the differential equation\n", + "* `ParametricProblem` $\\rightarrow$ parameter(s) presented in the differential equation\n" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "### Write the problem class\n", + "\n", + "Once the problem class is initialized we need to write the differential equation in PINA language. For doing this we need to load the pina operators found in `pina.operators` module. Let's again consider the Equation (1) and try to write the PINA model class:" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 14, + "source": [ + "from pina.problem import SpatialProblem\n", + "from pina.operators import grad\n", + "from pina import Condition, Span\n", + "\n", + "import torch\n", + "\n", + "\n", + "class SimpleODE(SpatialProblem):\n", + "\n", + " output_variables = ['u']\n", + " spatial_domain = Span({'x': [0, 1]})\n", + "\n", + " # defining the ode equation\n", + " def ode_equation(input_, output_):\n", + "\n", + " # computing the derivative\n", + " u_x = grad(output_, input_, components=['u'], d=['x'])\n", + "\n", + " # extracting u input variable\n", + " u = output_.extract(['u'])\n", + "\n", + " # calculate residual and return it\n", + " return u_x - u\n", + "\n", + " # defining initial condition\n", + " def initial_condition(input_, output_):\n", + " \n", + " # setting initial value\n", + " value = 1.0\n", + "\n", + " # extracting u input variable\n", + " u = output_.extract(['u'])\n", + "\n", + " # calculate residual and return it\n", + " return u - value\n", + "\n", + " # Conditions to hold\n", + " conditions = {\n", + " 'x0': Condition(Span({'x': 0.}), initial_condition),\n", + " 'D': Condition(Span({'x': [0, 1]}), ode_equation),\n", + " }\n", + "\n", + " # defining true solution\n", + " def truth_solution(self, pts):\n", + " return torch.exp(pts.extract(['x']))\n" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "After the defition of the Class we need to write different class methods, where each method is a function returning a residual. This functions are the ones minimized during the PINN optimization, for the different conditions. For example, in the domain $(0,1)$ the ODE equation (`ode_equation`) must be satisfied, so we write it by putting all the ODE equation on the right hand side, such that we return the zero residual. This is done for all the conditions (`ode_equation`, `initial_condition`). \n", + "\n", + "Once we have defined the function we need to tell the network where these methods have to be applied. For doing this we use the class `Condition`. In `Condition` we pass the location points and the function to be minimized on those points (other possibilities are allowed, see the documentation for reference).\n", + "\n", + "Finally, it's possible to defing the `truth_solution` function, which can be useful if we want to plot the results and see a comparison of real vs expected solution. Notice that `truth_solution` function is a method of the `PINN` class, but it is not mandatory for the problem definition." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Build PINN object" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "The basics requirements for building a PINN model are a problem and a model. We have already covered the problem definition. For the model one can use the default models provided in PINA or use a custom model. We will not go into the details of model definition, Tutorial2 and Tutorial3 treat the topic in detail." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 31, + "source": [ + "from pina.model import FeedForward\n", + "from pina import PINN\n", + "\n", + "# initialize the problem\n", + "problem = SimpleODE()\n", + "\n", + "# build the model\n", + "model = FeedForward(\n", + " layers=[10, 10],\n", + " func=torch.nn.Tanh,\n", + " output_variables=problem.output_variables,\n", + " input_variables=problem.input_variables\n", + ")\n", + "\n", + "# create the PINN object\n", + "pinn = PINN(problem, model)\n" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "Creating the pinn object is fairly simple by using the `PINN` class, different optional inputs can be passed: optimizer, batch size, ... (see [documentation](https://mathlab.github.io/PINA/) for reference)." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Sample points in the domain " + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "Once the `pinn` object is created, we need to generate the points for starting the optimization. For doing this we use the `span_pts` method of the `PINN` class.\n", + "Let's see some methods to sample in $(0,1 )$." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 32, + "source": [ + "# sampling 20 points in (0, 1) with discrite step\n", + "pinn.span_pts(20, 'grid', locations=['D'])\n", + "\n", + "# sampling 20 points in (0, 1) with latin hypercube\n", + "pinn.span_pts(20, 'latin', locations=['D'])\n", + "\n", + "# sampling 20 points in (0, 1) randomly\n", + "pinn.span_pts(20, 'random', locations=['D'])\n" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "We can also use a dictionary for specific variables:" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 33, + "source": [ + "pinn.span_pts({'variables': ['x'], 'mode': 'grid', 'n': 20}, locations=['D'])\n" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "We are going to use equispaced points for sampling. We need to sample in all the conditions domains. In our case we sample in `D` and `x0`." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 34, + "source": [ + "# sampling for training\n", + "pinn.span_pts(1, 'random', locations=['x0'])\n", + "pinn.span_pts(20, 'grid', locations=['D'])\n" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "### Very simple training and plotting\n", + "\n", + "Once we have defined the PINA model, created a network and sampled points in the domain, we have everything that is necessary for training a PINN. Here we show a very short training and some method for plotting the results." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 35, + "source": [ + "# simple training \n", + "final_loss = pinn.train(stop=3000, frequency_print=1000)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " sum x0initial_co Dode_equatio \n", + "[epoch 00000] 1.933187e+00 1.825489e+00 1.076983e-01 \n", + " sum x0initial_co Dode_equatio \n", + "[epoch 00001] 1.860870e+00 1.766795e+00 9.407549e-02 \n", + " sum x0initial_co Dode_equatio \n", + "[epoch 01000] 4.974120e-02 1.635524e-02 3.338596e-02 \n", + " sum x0initial_co Dode_equatio \n", + "[epoch 02000] 1.099083e-03 3.420736e-05 1.064875e-03 \n", + "[epoch 03000] 4.049759e-04 2.937766e-06 4.020381e-04 \n" + ] + } + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "After the training we have saved the final loss in `final_loss`, which we can inspect. By default PINA uses mean square error loss." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 36, + "source": [ + "# inspecting final loss\n", + "final_loss\n" + ], + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0.0004049759008921683" + ] + }, + "metadata": {}, + "execution_count": 36 + } + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "By using the `Plotter` class from PINA we can also do some quatitative plots of the loss function. " + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 37, + "source": [ + "from pina.plotter import Plotter\n", + "\n", + "# plotting the loss\n", + "plotter = Plotter()\n", + "plotter.plot_loss(pinn)" + ], + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + } + } + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "We have a very smooth loss decreasing!" + ], + "metadata": {} + } + ], + "metadata": { + "kernelspec": { + "name": "python3", + "display_name": "Python 3.9.0 64-bit" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.0" + }, + "interpreter": { + "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/tutorials/tutorial1/tutorial.py b/tutorials/tutorial1/tutorial.py new file mode 100644 index 0000000..188e922 --- /dev/null +++ b/tutorials/tutorial1/tutorial.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # Tutorial 1: Physics Informed Neural Networks on PINA + +# In this tutorial we will show the typical use case of PINA on a toy problem. Specifically, the tutorial aims to introduce the following topics: +# +# * Defining a PINA Problem, +# * Build a `pinn` object, +# * Sample points in the domain. +# +# These are the three main steps needed **before** training a Physics Informed Neural Network (PINN). We will show in detailed each step, and at the end we will solve a very simple problem with PINA. + +# ## PINA Problem + +# ### Initialize the Problem class + +# The problem definition in the PINA framework is done by building a phython `class`, inherited from one or more problem classes (`SpatialProblem`, `TimeDependentProblem`, `ParametricProblem`), depending on the nature of the problem treated. Let's see an example to better understand: +# #### Simple Ordinary Differential Equation +# Consider the following: +# +# $$ +# \begin{equation} +# \begin{cases} +# \frac{d}{dx}u(x) &= u(x) \quad x\in(0,1)\\ +# u(x=0) &= 1 \\ +# \end{cases} +# \end{equation} +# $$ +# +# with analytical solution $u(x) = e^x$. In this case we have that our ODE depends only on the spatial variable $x\in(0,1)$ , this means that our problem class is going to be inherited from `SpatialProblem` class: +# +# ```python +# from pina.problem import SpatialProblem +# from pina import Span +# +# class SimpleODE(SpatialProblem): +# +# output_variables = ['u'] +# spatial_domain = Span({'x': [0, 1]}) +# +# # other stuff ... +# ``` +# +# Notice that we define `output_variables` as a list of symbols, indicating the output variables of our equation (in this case only $u$). The `spatial_domain` variable indicates where the sample points are going to be sampled in the domain, in this case $x\in(0,1)$. + +# What about if we also have a time depencency in the equation? Well in that case our `class` will inherit from both `SpatialProblem` and `TimeDependentProblem`: +# ```python +# from pina.problem import SpatialProblem, TimeDependentProblem +# from pina import Span +# +# class TimeSpaceODE(SpatialProblem, TimeDependentProblem): +# +# output_variables = ['u'] +# spatial_domain = Span({'x': [0, 1]}) +# temporal_domain = Span({'x': [0, 1]}) +# +# # other stuff ... +# ``` +# where we have included the `temporal_domain` variable indicating the time domain where we want the solution. +# +# Summarizing, in PINA we can initialize a problem with a class which is inherited from three base classes: `SpatialProblem`, `TimeDependentProblem`, `ParametricProblem`, depending on the type of problem we are considering. For reference: +# * `SpatialProblem` $\rightarrow$ spatial variable(s) presented in the differential equation +# * `TimeDependentProblem` $\rightarrow$ time variable(s) presented in the differential equation +# * `ParametricProblem` $\rightarrow$ parameter(s) presented in the differential equation +# + +# ### Write the problem class +# +# Once the problem class is initialized we need to write the differential equation in PINA language. For doing this we need to load the pina operators found in `pina.operators` module. Let's again consider the Equation (1) and try to write the PINA model class: + +# In[14]: + + +from pina.problem import SpatialProblem +from pina.operators import grad +from pina import Condition, Span + +import torch + + +class SimpleODE(SpatialProblem): + + output_variables = ['u'] + spatial_domain = Span({'x': [0, 1]}) + + # defining the ode equation + def ode_equation(input_, output_): + + # computing the derivative + u_x = grad(output_, input_, components=['u'], d=['x']) + + # extracting u input variable + u = output_.extract(['u']) + + # calculate residual and return it + return u_x - u + + # defining initial condition + def initial_condition(input_, output_): + + # setting initial value + value = 1.0 + + # extracting u input variable + u = output_.extract(['u']) + + # calculate residual and return it + return u - value + + # Conditions to hold + conditions = { + 'x0': Condition(Span({'x': 0.}), initial_condition), + 'D': Condition(Span({'x': [0, 1]}), ode_equation), + } + + # defining true solution + def truth_solution(self, pts): + return torch.exp(pts.extract(['x'])) + + +# After the defition of the Class we need to write different class methods, where each method is a function returning a residual. This functions are the ones minimized during the PINN optimization, for the different conditions. For example, in the domain $(0,1)$ the ODE equation (`ode_equation`) must be satisfied, so we write it by putting all the ODE equation on the right hand side, such that we return the zero residual. This is done for all the conditions (`ode_equation`, `initial_condition`). +# +# Once we have defined the function we need to tell the network where these methods have to be applied. For doing this we use the class `Condition`. In `Condition` we pass the location points and the function to be minimized on those points (other possibilities are allowed, see the documentation for reference). +# +# Finally, it's possible to defing the `truth_solution` function, which can be useful if we want to plot the results and see a comparison of real vs expected solution. Notice that `truth_solution` function is a method of the `PINN` class, but it is not mandatory for the problem definition. + +# ## Build PINN object + +# The basics requirements for building a PINN model are a problem and a model. We have already covered the problem definition. For the model one can use the default models provided in PINA or use a custom model. We will not go into the details of model definition, Tutorial2 and Tutorial3 treat the topic in detail. + +# In[31]: + + +from pina.model import FeedForward +from pina import PINN + +# initialize the problem +problem = SimpleODE() + +# build the model +model = FeedForward( + layers=[10, 10], + func=torch.nn.Tanh, + output_variables=problem.output_variables, + input_variables=problem.input_variables +) + +# create the PINN object +pinn = PINN(problem, model) + + +# Creating the pinn object is fairly simple by using the `PINN` class, different optional inputs can be passed: optimizer, batch size, ... (see [documentation](https://mathlab.github.io/PINA/) for reference). + +# ## Sample points in the domain + +# Once the `pinn` object is created, we need to generate the points for starting the optimization. For doing this we use the `span_pts` method of the `PINN` class. +# Let's see some methods to sample in $(0,1 )$. + +# In[32]: + + +# sampling 20 points in (0, 1) with discrite step +pinn.span_pts(20, 'grid', locations=['D']) + +# sampling 20 points in (0, 1) with latin hypercube +pinn.span_pts(20, 'latin', locations=['D']) + +# sampling 20 points in (0, 1) randomly +pinn.span_pts(20, 'random', locations=['D']) + + +# We can also use a dictionary for specific variables: + +# In[33]: + + +pinn.span_pts({'variables': ['x'], 'mode': 'grid', 'n': 20}, locations=['D']) + + +# We are going to use equispaced points for sampling. We need to sample in all the conditions domains. In our case we sample in `D` and `x0`. + +# In[34]: + + +# sampling for training +pinn.span_pts(1, 'random', locations=['x0']) +pinn.span_pts(20, 'grid', locations=['D']) + + +# ### Very simple training and plotting +# +# Once we have defined the PINA model, created a network and sampled points in the domain, we have everything that is necessary for training a PINN. Here we show a very short training and some method for plotting the results. + +# In[35]: + + +# simple training +final_loss = pinn.train(stop=3000, frequency_print=1000) + + +# After the training we have saved the final loss in `final_loss`, which we can inspect. By default PINA uses mean square error loss. + +# In[36]: + + +# inspecting final loss +final_loss + + +# By using the `Plotter` class from PINA we can also do some quatitative plots of the loss function. + +# In[37]: + + +from pina.plotter import Plotter + +# plotting the loss +plotter = Plotter() +plotter.plot_loss(pinn) + + +# We have a very smooth loss decreasing!