1. 背景简介
拉普拉斯方程由法国数学家拉普拉斯首先提出而得名,该方程在许多领域都有重要应用,例如电磁学、天文学和流体力学等。在实际应用中,拉普拉斯方程的求解往往是一个复杂的数学问题。对于一些具有特定边界条件和初始条件的实际问题,可以通过特定的数值方法(如有限元方法、有限差分方法等)来求解拉普拉斯方程。对于一些复杂的问题,可能需要采用更高级的数值方法或者借助高性能计算机进行计算。
本案例通过深度学习的方式对拉普拉斯方程的2维形式进行求解。
2. 问题定义
拉普拉斯方程(2维形式):
\dfrac{\partial^{2} u}{\partial x^{2}} + \dfrac{\partial^{2} u}{\partial y^{2}} = 0, x \in (0, 1), y \in (0, 1)
3. 问题求解
接下来开始讲解如何将问题一步一步地转化为 PaddleScience 代码,用深度学习的方法求解该问题。
为了快速理解 PaddleScience,接下来仅对模型构建、方程构建、计算域构建等关键步骤进行阐述,而其余细节请参考
API文档
。
3.1 模型构建
在 2D-Laplace 问题中,每一个已知的坐标点
\((x, y)\)
都有对应的待求解的未知量
\(u\)
,我们在这里使用比较简单的 MLP(Multilayer Perceptron, 多层感知机) 来表示
\((x, y)\)
到
\((u)\)
的映射函数
\(f: \mathbb{R}^2 \to \mathbb{R}^1\)
,即:
u = f(x, y)
上式中
\(f\)
即为 MLP 模型本身,用 PaddleScience 代码表示如下
24
# set validator
mse_metric = ppsci.validate.GeometryValidator(
{"u": lambda out: out["u"]},
{"u": u_solution_func},
geom["rect"],
"dataset"
: "IterableNamedArrayDataset",
"total_size": NPOINT_TOTAL,
ppsci.loss.MSELoss(),
evenly=True,
metric={"MSE": ppsci.metric.MSE()},
with_initial=True,
name="MSE_Metric",
validator = {mse_metric.name: mse_metric}
在模型评估时,如果评估结果是可以可视化的数据,我们可以选择合适的可视化器来对输出结果进行可视化。
本文中的输出数据是一个区域内的二维点集,因此我们只需要将评估的输出数据保存成 vtu格式 文件,最后用可视化软件打开查看即可。代码如下:
103
# initialize solver
solver = 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,
equation=equation,
geom=geom,
validator=validator,
visualizer=visualizer,
# train model
solver.train()
# evaluate after finished training
solver.eval()
# visualize prediction after finished training
solver.visualize()
laplace2d.py |
---|
1
# 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.
import hydra
import numpy as np
from omegaconf import DictConfig
import ppsci
def train(cfg: DictConfig):
# set model
model = ppsci.arch.MLP(**cfg.MODEL)
# set equation
equation = {"laplace": ppsci.equation.Laplace(dim=2)}
# set geometry
geom = {
"rect": ppsci.geometry.Rectangle(
cfg.DIAGONAL_COORD.xmin, cfg.DIAGONAL_COORD.xmax
# compute ground truth function
def u_solution_func(out):
"""compute ground truth for u as label data"""
x, y = out["x"], out["y"]
return np.cos(x) * np.cosh(y)
# set train dataloader config
train_dataloader_cfg = {
"dataset": "IterableNamedArrayDataset",
"iters_per_epoch": cfg.TRAIN.iters_per_epoch,
NPOINT_TOTAL = cfg.NPOINT_INTERIOR + cfg.NPOINT_BC
# set constraint
pde_constraint = ppsci.constraint.InteriorConstraint(
equation["laplace"].equations,
{"laplace": 0},
geom["rect"],
{**train_dataloader_cfg, "batch_size": NPOINT_TOTAL},
ppsci.loss.MSELoss("sum"),
evenly
=True,
name="EQ",
bc = ppsci.constraint.BoundaryConstraint(
{"u": lambda out: out["u"]},
{"u": u_solution_func},
geom["rect"],
{**train_dataloader_cfg, "batch_size": cfg.NPOINT_BC},
ppsci.loss.MSELoss("sum"),
name="BC",
# wrap constraints together
constraint = {
pde_constraint.name: pde_constraint,
bc.name: bc,
# set optimizer
optimizer = ppsci.optimizer.Adam(learning_rate=cfg.TRAIN.learning_rate)(model)
# set validator
mse_metric = ppsci.validate.GeometryValidator(
{"u": lambda out: out["u"]},
{"u": u_solution_func},
geom["rect"],
"dataset": "IterableNamedArrayDataset",
"total_size": NPOINT_TOTAL,
ppsci.loss.MSELoss(),
evenly=True,
metric={"MSE": ppsci.metric.MSE()},
with_initial=True,
name="MSE_Metric",
validator = {mse_metric.name: mse_metric}
# set visualizer(optional)
vis_points = geom["rect"].sample_interior(NPOINT_TOTAL, evenly=True)
visualizer = {
"visualize_u": ppsci.visualize.VisualizerVtu(
vis_points,
{"u": lambda d: d["u"]},
num_timestamps=1,
prefix="result_u",
# initialize solver
solver = 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,
equation=equation,
geom=geom,
validator=validator,
visualizer=visualizer,
# train model
solver.train()
# evaluate after finished training
solver.eval()
# visualize prediction after finished training
solver.visualize()
def evaluate(cfg: DictConfig):
# set model
model = ppsci.arch.MLP(**cfg.MODEL)
# set equation
equation = {"laplace": ppsci.equation.Laplace(dim=2)}
# set geometry
geom = {
"rect": ppsci.geometry.Rectangle(
cfg.DIAGONAL_COORD.xmin, cfg.DIAGONAL_COORD.xmax
# compute ground truth function
def u_solution_func(out):
"""compute ground truth for u as label data"""
x, y = out["x"], out["y"]
return np.cos(x) * np.cosh(y)
NPOINT_TOTAL = cfg.NPOINT_INTERIOR + cfg.NPOINT_BC
# set validator
mse_metric = ppsci.validate.GeometryValidator(
{"u": lambda out: out["u"]},
{"u": u_solution_func},
geom["rect"],
"dataset": "IterableNamedArrayDataset"
,
"total_size": NPOINT_TOTAL,
ppsci.loss.MSELoss(),
evenly=True,
metric={"MSE": ppsci.metric.MSE()},
with_initial=True,
name="MSE_Metric",
validator = {mse_metric.name: mse_metric}
# set visualizer(optional)
vis_points = geom["rect"].sample_interior(NPOINT_TOTAL, evenly=True)
visualizer = {
"visualize_u": ppsci.visualize.VisualizerVtu(
vis_points,
{"u": lambda d: d["u"]},
num_timestamps=1,
prefix="result_u",
# initialize solver
solver = ppsci.solver.Solver(
model,
output_dir=cfg.output_dir,
seed=cfg.seed,
equation=equation,
geom=geom,
validator=validator,
visualizer=visualizer,
pretrained_model_path=cfg.EVAL.pretrained_model_path,
solver.eval()
# visualize prediction
solver.visualize()
def export(cfg: DictConfig):
# set model
model = ppsci.arch.MLP(**cfg.MODEL)
# initialize solver
solver = ppsci.solver.Solver(
model,
pretrained_model_path=cfg.INFER.pretrained_model_path,
# export model
from paddle.static import InputSpec
input_spec = [
{key: InputSpec([None, 1], "float32", name=key) for key in model.input_keys},
solver.export(input_spec, cfg.INFER.export_path)
def inference(cfg: DictConfig):
from deploy.python_infer import pinn_predictor
predictor = pinn_predictor.PINNPredictor(cfg)
# set geometry
geom = {
"rect": ppsci.geometry.Rectangle(
cfg.DIAGONAL_COORD.xmin, cfg.DIAGONAL_COORD.xmax
NPOINT_TOTAL = cfg.NPOINT_INTERIOR + cfg.NPOINT_BC
input_dict = geom["rect"].sample_interior(NPOINT_TOTAL, evenly=True)
output_dict = predictor.predict(
{key: input_dict[key] for key in cfg.MODEL.input_keys}, cfg.INFER.batch_size
# mapping data to cfg.INFER.output_keys
output_dict = {
store_key: output_dict[infer_key]
for store_key, infer_key in zip(cfg.MODEL.output_keys, output_dict.keys())
# save result
ppsci.visualize.save_vtu_from_dict(
"./laplace2d_pred.vtu",
{**input_dict, **output_dict},
input_dict.keys(),
cfg.MODEL.output_keys,
@hydra.main(version_base=None, config_path="./conf", config_name="laplace2d.yaml")
def main(cfg: DictConfig):
if cfg.mode == "train":
train(cfg)
elif cfg.mode == "eval":
evaluate(cfg)
elif cfg.mode == "export":
export(cfg)
elif cfg.mode == "infer":
inference(cfg)
else:
raise ValueError(
f"cfg.mode should in ['train', 'eval', 'export', 'infer'], but got '{cfg.mode}'"
if __name__ == "__main__":
main()
使用训练得到的模型对上述计算域中均匀取的共 NPOINT_TOTAL 个点 \((x_i,y_i)\) 进行预测,预测结果如下所示。图像中每个点 \((x_i,y_i)\) 的值代表对应坐标上模型对 2D-Laplace 问题预测的解 \(u(x_i,y_i)\)。
模型预测结果
|