Next, we will explain how to convert the problem into PaddleScience code step by step and solve the problem using deep learning methods.
In order to quickly understand PaddleScience, only key steps such as model construction, equation construction, and computational domain construction are described below, while other details please refer to API Documentation.
In the Euler Beam problem, each known coordinate point \(x\) has a corresponding unknown quantity \(u\) to be solved. We use a relatively simple MLP (Multilayer Perceptron) here to represent the mapping function \(f: \mathbb{R}^1 \to \mathbb{R}^1\) from \(x\) to \(u\), i.e.:
\[
u = f(x)
\]
In the above formula, \(f\) is the MLP model itself, expressed in PaddleScience code as follows:
Then by specifying the number of layers and neurons of MLP, we instantiated a neural network model model with 3 hidden layers and 20 neurons per layer.
The equation construction of Euler Beam can directly use the Biharmonic built in PaddleScience, specifying the parameter dim of this class as 1, q as -1, and D as 1.
In this article, the Euler Beam problem acts on a one-dimensional area of (0.0, 1.0), so the spatial geometry Interval built in PaddleScience can be used directly as the computational domain.
In this case, we used two constraints to guide the training of the model in the computational domain, namely the equation constraint acting on the sampling point and the constraint acting on the boundary point.
Before defining constraints, you need to specify the number of sampling points for each constraint, indicating the number of sampled data for each constraint in its corresponding computational domain, as well as general sampling configuration.
# set dataloader configdataloader_cfg={"dataset":"IterableNamedArrayDataset","iters_per_epoch":cfg.TRAIN.iters_per_epoch,}# set constraintpde_constraint=ppsci.constraint.InteriorConstraint(equation["biharmonic"].equations,{"biharmonic":0},geom["interval"],{**dataloader_cfg,"batch_size":cfg.TRAIN.batch_size.pde},ppsci.loss.MSELoss(),random="Hammersley",name="EQ",)
Similarly, we also need to construct boundary constraints. However, unlike constructing InteriorConstraint, since the action area is the boundary, we use the BoundaryConstraint class, code as follows:
Next, we need to specify the number of training epochs in the configuration file. Here, based on experimental experience, we use 10,000 training epochs, with an evaluation interval of 1,000 epochs.
Usually during the training process, the training status of the current model is evaluated using the validation set (test set) at a certain epoch interval, so ppsci.validate.GeometryValidator is used to construct the validator.
# set visualizer(optional)visu_points=geom["interval"].sample_interior(cfg.EVAL.total_size,evenly=True)visualizer={"visualize_u":ppsci.visualize.VisualizerScatter1D(visu_points,("x",),{"u_label":lambdad:u_solution_func(d),"u_pred":lambdad:d["u"],},num_timestamps=1,prefix="result_u",)}
After completing the above settings, you only need to pass the instantiated objects to ppsci.solver.Solver in order, and then start training, evaluation, and visualization.
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.importhydrafromomegaconfimportDictConfigimportppscifromppsci.autodiffimporthessianfromppsci.autodiffimportjacobiandeftrain(cfg:DictConfig):# set modelmodel=ppsci.arch.MLP(**cfg.MODEL)# set geometrygeom={"interval":ppsci.geometry.Interval(0,1)}# set equation(s)equation={"biharmonic":ppsci.equation.Biharmonic(dim=1,q=cfg.q,D=cfg.D)}# set dataloader configdataloader_cfg={"dataset":"IterableNamedArrayDataset","iters_per_epoch":cfg.TRAIN.iters_per_epoch,}# set constraintpde_constraint=ppsci.constraint.InteriorConstraint(equation["biharmonic"].equations,{"biharmonic":0},geom["interval"],{**dataloader_cfg,"batch_size":cfg.TRAIN.batch_size.pde},ppsci.loss.MSELoss(),random="Hammersley",name="EQ",)bc=ppsci.constraint.BoundaryConstraint({"u0":lambdad:d["u"][0:1],"u__x":lambdad:jacobian(d["u"],d["x"])[1:2],"u__x__x":lambdad:hessian(d["u"],d["x"])[2:3],"u__x__x__x":lambdad:jacobian(hessian(d["u"],d["x"]),d["x"])[3:4],},{"u0":0,"u__x":0,"u__x__x":0,"u__x__x__x":0},geom["interval"],{**dataloader_cfg,"batch_size":cfg.TRAIN.batch_size.bc},ppsci.loss.MSELoss("sum"),evenly=True,name="BC",)# wrap constraints togetherconstraint={pde_constraint.name:pde_constraint,bc.name:bc,}# set optimizeroptimizer=ppsci.optimizer.Adam(cfg.TRAIN.learning_rate)(model)# set validatordefu_solution_func(out):"""compute ground truth for u as label data"""x=out["x"]return-(x**4)/24+x**3/6-x**2/4l2_rel_metric=ppsci.validate.GeometryValidator({"u":lambdaout:out["u"]},{"u":u_solution_func},geom["interval"],{"dataset":"IterableNamedArrayDataset","total_size":cfg.EVAL.total_size,},ppsci.loss.MSELoss(),evenly=True,metric={"L2Rel":ppsci.metric.L2Rel()},name="L2Rel_Metric",)validator={l2_rel_metric.name:l2_rel_metric}# set visualizer(optional)visu_points=geom["interval"].sample_interior(cfg.EVAL.total_size,evenly=True)visualizer={"visualize_u":ppsci.visualize.VisualizerScatter1D(visu_points,("x",),{"u_label":lambdad:u_solution_func(d),"u_pred":lambdad:d["u"],},num_timestamps=1,prefix="result_u",)}# initialize solversolver=ppsci.solver.Solver(model,constraint,cfg.output_dir,optimizer,epochs=cfg.TRAIN.epochs,iters_per_epoch=cfg.TRAIN.iters_per_epoch,eval_during_train=cfg.TRAIN.eval_during_train,eval_freq=cfg.TRAIN.eval_freq,seed=cfg.seed,equation=equation,geom=geom,validator=validator,visualizer=visualizer,pretrained_model_path=cfg.TRAIN.pretrained_model_path,checkpoint_path=cfg.TRAIN.checkpoint_path,eval_with_no_grad=cfg.EVAL.eval_with_no_grad,to_static=cfg.to_static,)# train modelsolver.train()# evaluate after finished trainingsolver.eval()# visualize prediction after finished trainingsolver.visualize()defevaluate(cfg:DictConfig):# set modelmodel=ppsci.arch.MLP(**cfg.MODEL)# set geometrygeom={"interval":ppsci.geometry.Interval(0,1)}# set equation(s)equation={"biharmonic":ppsci.equation.Biharmonic(dim=1,q=cfg.q,D=cfg.D)}# set validatordefu_solution_func(out):"""compute ground truth for u as label data"""x=out["x"]return-(x**4)/24+x**3/6-x**2/4l2_rel_metric=ppsci.validate.GeometryValidator({"u":lambdaout:out["u"]},{"u":u_solution_func},geom["interval"],{"dataset":"IterableNamedArrayDataset","total_size":cfg.EVAL.total_size,},ppsci.loss.MSELoss(),evenly=True,metric={"L2Rel":ppsci.metric.L2Rel()},name="L2Rel_Metric",)validator={l2_rel_metric.name:l2_rel_metric}# set visualizer(optional)visu_points=geom["interval"].sample_interior(cfg.EVAL.total_size,evenly=True)visualizer={"visualize_u":ppsci.visualize.VisualizerScatter1D(visu_points,("x",),{"u_label":lambdad:u_solution_func(d),"u_pred":lambdad:d["u"],},num_timestamps=1,prefix="result_u",)}# initialize solversolver=ppsci.solver.Solver(model,None,cfg.output_dir,None,seed=cfg.seed,equation=equation,geom=geom,validator=validator,visualizer=visualizer,pretrained_model_path=cfg.EVAL.pretrained_model_path,eval_with_no_grad=cfg.EVAL.eval_with_no_grad,to_static=cfg.to_static,)# evaluate after finished trainingsolver.eval()# visualize prediction after finished trainingsolver.visualize()defexport(cfg:DictConfig):# set modelmodel=ppsci.arch.MLP(**cfg.MODEL)# initialize solversolver=ppsci.solver.Solver(model,pretrained_model_path=cfg.INFER.pretrained_model_path,)# export modelfrompaddle.staticimportInputSpecinput_spec=[{key:InputSpec([None,1],"float32",name=key)forkeyinmodel.input_keys},]solver.export(input_spec,cfg.INFER.export_path)definference(cfg:DictConfig):fromdeploy.python_inferimportpinn_predictorpredictor=pinn_predictor.PINNPredictor(cfg)# set geometrygeom={"interval":ppsci.geometry.Interval(0,1)}input_dict=geom["interval"].sample_interior(cfg.INFER.total_size,evenly=True)output_dict=predictor.predict({"x":input_dict["x"]},cfg.INFER.batch_size)# mapping data to cfg.INFER.output_keysoutput_dict={store_key:output_dict[infer_key]forstore_key,infer_keyinzip(cfg.MODEL.output_keys,output_dict.keys())}defu_solution_func(out):"""compute ground truth for u as label data"""x=out["x"]return-(x**4)/24+x**3/6-x**2/4ppsci.visualize.save_plot_from_1d_dict("./euler_beam_pred",{**input_dict,**output_dict,"u_label":u_solution_func(input_dict)},("x",),("u","u_label"),)@hydra.main(version_base=None,config_path="./conf",config_name="euler_beam.yaml")defmain(cfg:DictConfig):ifcfg.mode=="train":train(cfg)elifcfg.mode=="eval":evaluate(cfg)elifcfg.mode=="export":export(cfg)elifcfg.mode=="infer":inference(cfg)else:raiseValueError(f"cfg.mode should in ['train', 'eval', 'export', 'infer'], but got '{cfg.mode}'")if__name__=="__main__":main()
The trained model is used to predict a total of NPOINT_TOTAL points \(x_i\) uniformly taken from the above computational domain. The prediction results are shown below. In the image, the abscissa is \(x\), and the ordinate is the corresponding predicted result \(u\).