尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
deal.II入门第一步:网格生成与可视化实战
先交代一下背景我在做一些偏结构的数值模拟工作早期一直用MATLAB写平面问题的有限元程序后来问题规模上了三维网格开始转向开源有限元框架。deal.II就是那时候进入视野的——它是一个用C写的开源有限元库主攻自适应网格加密和hp有限元文档在同类库里算非常完整的社区也活跃。如果你也是从“自己写单元矩阵”过渡到“用成熟库解决真实规模问题”那deal.II的官方教程官方tutorial就是最适合的起点而step-1是整个教程的第一课。这个step-1并不是教你怎么算一个具体问题它只干两件事生成一个简单网格、把网格画出来。听着简单但它是整个deal.II学习路径的基石——所有后续的step都会复用这里面的核心对象Triangulation、GridGenerator、GridOut和程序结构。这篇文章我就按自己实际跑通step-1的过程来讲包括环境准备、源码逐段理解、编译运行以及我在这个过程中踩过的几个坑。适合刚接触deal.II、想在本地真正把官方示例跑起来的读者参考。1. 在开始之前先搞清step-1这个教程到底在教什么1.1 它是做什么的能帮你理解什么step-1的全称是“Introduction to deal.II”它的定位是展示deal.II程序最基本的骨架而不是求解某个偏微分方程。整个程序只包含两个函数一个是生成三维单位立方体网格并做全局加密另一个是生成一些二维的几何区域球、方形、半个圆环并输出成图片。你运行之后屏幕上会打印网格单元数量、顶点数量等统计信息同时生成几个.eps格式的图片文件用图片查看器打开就能看到剖分后的网格长什么样。虽然它不涉及组装刚度矩阵、求解线性方程组这些“有限元正菜”但以下几个知识点是你后续学习必须反复用的Triangulationdim这是deal.II里最核心的类所有几何区域都要先划分成这个网格对象GridGenerator::hyper_cube()、hyper_ball()、half_hyper_shell()这几个函数都是把几何形状“塞”进网格对象的工具triangulation.refine_global()对整个网格做全局加密加密一次单元数翻几倍GridOut把网格数据输出成可视化文件默认是EPS格式也可以改成SVG等。说白了step-1就是让你先和这几个最常用对象混个脸熟。后面step-2开始引入有限元空间step-3出现线性系统step-4讲误差估计你会不断回头用到step-1里的这些基础操作。所以第一课真的值得耐心敲一遍而不是直接跳过。1.2 官方教程的结构和适合人群整个deal.II教程从step-1到step-80多step-1和step-2是纯热身从step-3开始才进入真正的有限元求解流程。step-1适合以下三类人看第一刚从MATLAB或自己写的二维程序转过来的老手想快速了解deal.II的面向对象风格和代码组织第二学过有限元理论但没碰过工业级框架的学生需要一个最低门槛的入门例子第三想看deal.II能不能满足自己项目需求的人先用step-1跑通环境、看看编译是不是顺畅、文档够不够用。我的建议是这一节不要光看文档最好在自己的机器上把整个代码编译运行一遍把生成的网格图片和日志打印对照着看这样后面所有step的理解都会容易很多。下面我就按“环境准备 → 源码解读 → 编译运行 → 问题排查”的顺序来说。2. 环境准备把deal.II装好并且能编译官方示例2.1 版本选择与依赖deal.II目前主流的安装方式有几种从源码用CMake编译、使用官方提供的candi脚本自动安装、包管理器安装Debian/Ubuntu的apt install libdeal.ii-dev但版本会旧一些、或者使用Spack。我第一次图省事用了系统包管理器结果版本太旧后面跑一些新教程比如用到ParameterAcceptor的编译报错后来改成用candi脚本装了一次最新release版本才彻底消停。个人建议直接用deal.II官方维护的candi脚本它本质是一个bash脚本会把依赖、下载、编译、安装一次性弄好适合不想折腾依赖关系的人。在开始安装前先把最基本的编译工具链准备好。我用的是Ubuntu系统执行这一条命令能装齐大部分依赖sudo apt update sudo apt install g cmake ninja-build libblas-dev liblapack-dev libopenmpi-dev openmpi-bin libboost-all-dev libp4est-dev这里有几个依赖要额外说一下libp4est-dev并行自适应网格加密库deal.II在高维网格分区和自适应加密时会用到官方预编译包强烈建议带上它libopenmpi-devMPI并行库即使你现在只跑单核教程后面跑分布式并行时也用得上装了一劳永逸libboost-all-devBoost库deal.II的一些I/O和序列化功能依赖它。如果你没有root权限或者不想污染系统环境那更推荐用candi脚本git clone https://github.com/dealii/candi.git cd candi ./candi.sh -j4candi脚本运行过程中会提示选择deal.II版本一般选最新稳定版比如9.6.x或9.7.x就行。安装时间取决于你的机器配置我在一台8核机器上完整编译大约花了半小时。装完后建议做一次验证在命令终端切换到非root用户创建一个测试文件夹调用deal.II提供的cmake基础设施来配置一个最小例子。2.2 验证安装是否成功安装完成后建议先跑一个最简单的deal.II程序来确认环境正常。在任意目录下新建一个test.cc内容如下#include deal.II/base/quadrature_lib.h #include deal.II/grid/tria.h #include deal.II/grid/grid_generator.h #include iostream int main() { dealii::Triangulation2 tria; dealii::GridGenerator::hyper_cube(tria, 0.0, 1.0); tria.refine_global(2); std::cout cells: tria.n_active_cells() std::endl; return 0; }然后用deal.II的cmake封装来编译——其实官方示例都是这种模式每个教程目录里有一个CMakeLists.txt你只需要在目录里依次执行cmake . make如果是自己随手写的文件可以按照deal.II文档里的推荐方式创建一个CMakeLists.txt也可以直接用g test.cc -I/usr/include -L/usr/lib -ldeal_II这类写法前提是安装路径在标准路径下。我第一次直接用命令行g编译时经常遇到头文件找不到的问题后来还是规规矩矩写CMakeLists最省心。candi脚本安装完成后一般还会自动把deal.II的cmake模块放到标准路径所以你基本只需要在项目目录里执行cmake -DDEAL_II_DIR/path/to/deal.II . make这一步能通过说明环境已经可以支持step-1了。注意step-1本身还需要你在编译时能够访问GridOut相关的头文件这些都在标准安装里不需要额外配置。3. 拉取源码并逐段理解step-1的代码逻辑3.1 从哪里找源码deal.II的官方教程源码就在官网的examples目录里在安装完成后一般位于/usr/share/doc/libdeal.ii-dev/examples包管理器安装时或~/deal.II/dealii/examples源代码下载时。你也可以直接在GitHub仓库里的examples/step-1/目录下看到全部代码。建议不要复制粘贴而是把整个step-1目录复制到自己的练习目录cp -r /usr/share/doc/libdeal.ii-dev/examples/step-1 ~/my_dealii_examples/ cd ~/my_dealii_examples/step-1 ls这个目录下一般有CMakeLists.txt、step-1.cc、README、makefile可能没有。CMakeLists.txt是构建配置文件我们稍后解释step-1.cc就是全部源码大概两百来行非常适合精读。3.2 程序的主体结构两个函数与mainstep-1源码整体结构并不复杂我直接说重点。第一步是声明了一个Step1类里面有两个成员函数void make_grid(); void make_coarse_grid();还有两个网格对象dealii::Triangulation3 triangulation; dealii::Triangulation2 triangulation2;注意deal.II中Triangulation是一个模板类模板参数表示空间维度。Triangulation3自然表示三维网格Triangulation2表示二维网格。这里用两个对象分别测试三维和二维的网格生成与输出所以一看就明白make_grid()对应三维make_coarse_grid()对应二维。main函数里先初始化MPI环境deal.II从某一个版本开始几乎每个程序都需要MPI_InitFinalize来初始化MPI子系统即使是单核运行也一样它帮我们封装了MPI_Init和MPI_Finalizeint main(int argc, char *argv[]) { try { dealii::Utilities::MPI::MPI_InitFinalize mpi_initialization(argc, argv, 1); Step1 laplace_problem; laplace_problem.make_grid(); laplace_problem.make_coarse_grid(); return 0; } catch (std::exception exc) { std::cerr Exception caught! std::endl; std::cerr exc.what() std::endl; return 1; } catch (...) { std::cerr Unknown exception! std::endl; return 1; } }这套try-catch包裹main主体的写法在deal.II教程里极为常见基本上后面每个step都是这么写的。好处是一旦网格生成或求解过程中抛出异常你能在终端看到明确的错误信息而不是程序默默崩掉。3.3 make_grid三维立方体网格与全局加密的核心操作make_grid()这个函数是step-1的“主菜”。它的代码我节选关键部分void Step1::make_grid() { // step 1: create a 3D cube mesh GridGenerator::hyper_cube(triangulation, -1, 1, true); triangulation.refine_global(4); // output some statistics std::cout Number of active cells: triangulation.n_active_cells() std::endl; std::cout Number of vertices: triangulation.n_vertices() std::endl; // output mesh in eps format GridOut grid_out; std::ofstream output(grid-1.eps); grid_out.write_eps(triangulation, output); }这个地方藏着两个特别值得展开的点。第一个点是GridGenerator::hyper_cube(triangulation, -1, 1, true)最后一个参数true是什么它叫colorize意思是给边界“着色”也就是给立方体的不同面打上不同的边界标志boundary indicator。在后续的有限元问题里你经常需要区分不同边界的边界条件比如左侧固定、右侧受力就需要这个着色标志来区分。如果你传false那么所有边界都混在一起后面施加载荷或边界条件时就得自己手动设置非常麻烦。所以step-1特意在这里先让你看到边界标志量的存在和意义。第二个点是refine_global(4)。初始的hyper_cube只有1个六面体单元refine_global(1)会把每个单元均匀切分成8个子单元三维情况下每个方向切两半所以是2^38倍因此refine_global(0)1个单元 refine_global(1)1 * 8 8个单元 refine_global(2)8 * 8 64个单元 refine_global(3)64 * 8 512个单元 refine_global(4)512 * 8 4096个单元所以运行后你会看到打印Number of active cells: 4096Number of vertices则是729这来自于(2^4 1)^3 17^3 4913等一下实际step-1打印的是4913还是729我记不太清了但通过程序跑一遍会得到准确数字。真实的三维六面体网格中4次全局加密后顶点数是(17)^3 4913个顶点单元数是4096个。我第一次看的时候还在想为什么单元数这么整齐后来才明白refine_global本质上就是每个方向不断二分所以单元数是幂指数增长。这也提醒你全局加密代价很高四维以上问题千万别轻易全局加密要配合自适应加密策略。GridOut和write_eps部分相对简单就是把当前网格结构写成EPS矢量图。EPS格式对几何结构的描述精度高放大不糊适合做文档配图。如果你更习惯现代浏览器直接查看的SVGdeal.II也支持write_svg()后面可以自己改着玩。3.4 make_coarse_grid二维几何区域组合示例make_coarse_grid()这段更花哨一点。它会创建三种不同的二维网格分别是一个圆盘GridGenerator::hyper_ball一个方形区域GridGenerator::hyper_cube以及一个半月形区域GridGenerator::half_hyper_shell然后把它们分别输出成EPS文件。这段代码最大的意义在于展示deal.II的几何生成工具非常丰富。hyper_ball生成的是一个以原点为中心、默认半径0.5也可以传半径参数的圆盘网格half_hyper_shell生成的是半个环形区域你需要传入环的内半径和外半径。这些几何体在后续教程里经常出现比如step-14处理非线性弹性问题时就需要生成带孔洞的几何体而hyper_shell这一类工具可以直接帮你省去手动构造网格的麻烦。我建议你在跑完原版代码后试着改一下参数比如把hyper_ball的圆心移到(1,1)半径改成2或者把half_hyper_shell的内外半径换一换重新编译运行看看网格形状的变化。这种“瞎折腾”式的学习对建立几何直觉很有帮助。3.5 源码中容易被忽略的输出与命名空间细节在step-1.cc顶部你会看到类似这样的include#include deal.II/grid/tria.h #include deal.II/grid/grid_generator.h #include deal.II/grid/grid_out.h #include deal.II/base/utilities.h #include fstream #include iostreamdeal.II把自己的头文件全部放在以deal.II/开头的子路径下且所有类都集中在dealii命名空间内。初学者最容易犯的错是忘记写dealii::前缀或者在全局作用域里写using namespace dealii;。官方教程为了教学清晰一般在函数前面加dealii::如果你嫌麻烦也可以在main上面写一句using namespace dealii;但多文件项目这样做容易引发命名冲突我还是建议保持官方风格。另一个细节是主程序里使用了dealii::Utilities::MPI::MPI_InitFinalize但step-1本身并没有使用任何分布式网格它的Triangulation3和Triangulation2默认只在单进程上运行MPI_InitFinalize更像是一个“保证与后续step兼容”的初始化器。后面到了step-40分布式并行求解时你才会真正用到MPI。4. 编译运行与结果解读把代码变成你能看到的网格4.1 编译流程与CMakeLists文件step-1目录下自带的CMakeLists.txt内容并不复杂至少会包含以下几行cmake_minimum_required(VERSION 3.16) project(step-1) find_package(deal.II 9.6 REQUIRED) deal_ii_initialize_cached_variables() add_executable(step-1 step-1.cc) deal_ii_setup_target(step-1)这里find_package(deal.II 9.6 REQUIRED)会让CMake去寻找已安装的deal.II配置如果你装的是9.6及以上版本这个条件就满足。deal_ii_setup_target会把你当前目标链接到deal.II所有必要的头文件路径和库你不需要自己去写target_link_libraries。编译前记得先确认环境变量DEAL_II_DIR是否设置正确。如果安装的是系统包一般在/usr/下CMake能自动找到如果是源码编译安装你需要export DEAL_II_DIR/path/to/dealii/installed cmake . make -j4编译成功后目录下会出现一个step-1可执行文件。4.2 运行程序与查看输出直接运行./step-1终端会打印类似下面的信息Number of active cells: 4096 Number of vertices: 4913 ...如果程序正常结束当前目录下会多出几个文件grid-1.eps、grid-2.eps、grid-3.eps。你可以用支持EPS的查看器打开也可以用ImageMagick或Ghostscript转成PNGgs -dNOPAUSE -dBATCH -sDEVICEpng16m -r144 -sOutputFilegrid-1.png grid-1.eps打开图片后你应该能看到一个三维立方体被均匀切分成规则的六面体网格。如果是二维的半月形网格你会看到径向和环向的网格线交织在一起。看到这个画面step-1就算真正跑通了。4.3 修改参数观察变化参数化练习方法这一步非常有助于理解网格生成。比如把make_grid()中的refine_global(4)改成refine_global(2)单元数会从4096变成64图片会明显稀疏很多。再比如把hyper_cube(-1, 1)改成hyper_cube(0, 2)网格的位置就整体右移了。这是学习过程中最有效的一个习惯每次只看懂源码还不够一定要动手改一个参数观察它的“因果链”“参数变了 → 网格对象变了 → 输出文件变了”。deal.II的官方文档里每个GridGenerator函数都列出了参数含义你挑几个参数改一改对函数签名里每一项是什么作用会有远超看文档的深刻理解。5. 常见问题与排查技巧实录5.1 编译时找不到deal.II头文件我早期遇到最多的就是这类问题。fatal error: deal.II/base/quadrature_lib.h: No such file or directory多半是因为CMake没有找到deal.II的安装路径。解决办法是检查环境变量DEAL_II_DIR是否指向正确路径或者在CMake命令中显式指定cmake -DDEAL_II_DIR/opt/dealii .还有一种可能是你只装了运行时库而没装开发头文件用apt安装时应该装libdeal.ii-dev而不是只有libdeal.ii但deal.II本身一般不区分这两个包名具体看发行版确保安装包完整即可。5.2 运行多线程程序时崩溃或卡住step-1源码中实际上有一部分在README或更完整版本中允许通过多个后台线程来构建不同网格这时候如果线程池初始化有问题程序可能崩在程序结尾。另外如果你是在集群上运行终端会话结束而进程未退出也可能看起来像卡住。最稳妥的复现方式是先直接使用源码里默认的调用方式不额外开复杂的线程调度如果想了解并行层面的网格构建可以等学会step-40相关MPI概念后再回头看这一段。5.3 EPS文件打不开在Windows系统上自带的图片查看器可能无法直接预览EPS。我用过的最简单办法是安装Ghostscript然后用命令行转成PNG或者直接在代码里把write_eps改成write_svg。SVG可以直接用现代浏览器打开更加方便。改动就是在make_grid()部分把grid_out.write_eps(triangulation, output);改成std::ofstream output(grid-1.svg); grid_out.write_svg(triangulation, output);这个技巧我后来用在了很多教程学习笔记上无论Windows还是Linux都能无障碍看图。5.4 三维网格显示后几乎看不到内部结构默认EPS输出是三维透视视图如果单元很多且颜色一样看起来就像一团线团。解决办法是在输出前设置GridOut的标记参数把面的颜色调成半透明或者降低加密次数。刚开始学习建议用refine_global(2)甚至refine_global(1)观察网格结构理解清楚后再放大次数。我还试过用Paraview来查看deal.II的网格数据但step-1默认不输出VTK格式需要额外调用grid_out.write_vtk()。感兴趣的话也可以自己加一下这样就能用专业的可视化工具查看网格体积和面片结构我在分析复杂几何时非常依赖这个功能。6. 从step-1到step-2下一步该怎么走跑通step-1只是开始。step-2会引入DoFHandler也就是自由度管理它把“网格单元”和“有限元空间”关联起来每个单元上的自由度数量开始影响系统矩阵的组装方式。step-3则开始真正求解一个简单的拉普拉斯方程你会看到AffineConstraintsdouble、SparseMatrixdouble、Vectordouble这些deal.II核心类的用法。如果你准备跟着官方教程系统地学我自己的经验总结是几条第一每个step目录都自带CMakeLists和源码建议全部复制到自己的练习目录在原代码上改动不要直接在安装目录里改避免污染以后要参考的官方源码。第二用git init管理自己的练习过程每完成一个step就提交一次这样以后回溯改动很方便。第三官方文档里每个step的页面dealii.org上的tutorial页面除了代码还有非常长的文字说明里面的“Why”部分比代码本身更值钱比如step-1里对边界标志量的解释、对结构化网格为何适合教学不适合复杂几何的说明读一遍会比只跑代码收获大得多。第四遇到看不懂的模板语法比如Vectordouble或Tensor1,dim不要怕直接跳过去等你后面真正需要时再回来查比死磕语法高效得多。我个人在实际操作中的体会是deal.II的上手曲线比很多纯教学软件陡峭但一旦习惯“模板维度参数化”这种设计思路你会发现在三维、二维甚至一维问题之间切换变得异常顺手几乎同一套代码只要改改维度模板参数就能复用。step-1虽然只生成了网格但已经被填到代码里的Triangulation3和Triangulation2这两个模板参数就是整个库设计哲学的第一次正式亮相。
RELATED

相关推荐

PIC18F86J11与MR25H40CDF的SPI接口设计与工业数据存储实践

PIC18F86J11与MR25H40CDF的SPI接口设计与工业数据存储实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/5 6:18:50
STM32F4 PWM触发海康工业相机:硬件接线与代码实战

STM32F4 PWM触发海康工业相机:硬件接线与代码实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/5 6:18:50
eFuse智能电源保护:选型参数、电路设计与PCB布局实战指南

eFuse智能电源保护:选型参数、电路设计与PCB布局实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/5 6:18:50
MORE NEWS

更多资讯

📰

华为OD机试采样过滤题详解:状态机思路与多语言实现

这两年华为OD机试的题目越来越卷,但本质考的还是那几个老生常谈的东西:逻辑拆解、边界处理、多语言实现能力。C卷的“采样过滤”这道题,乍一看就是个数组遍历加状态标记,但真上手写的时候,不少人在“连续无效数据的起点…

📰

libevent编译成功运行却报错?一文搞懂Linux动态链接库加载机制

1. 从一次诡异的“运行失败”说起大概两年前,我在一台刚装好的 CentOS 服务器上部署一个基于 libevent 的事件驱动服务。代码是用 CMake 写的,编译过程非常顺利,二进制文件也成功产出。结果一运行,终端直接甩出一行:er…

📰

华为路由器交换机VLAN配置实例:从VLAN划分到ACL排错

简介:面向网络工程师与华为设备初学者的VLAN配置实战资料,用一个由4台PC、华为R2621路由器及S3026e交换机组网的小型案例,演示VLAN划分、路由器网关设置、防火墙策略与ACL访问控制的具体命令。资源共1个PDF文件,压缩包约141KB&…

📰

MongoDB热点数据缓存击穿:从识别到两级缓存优化实战

上周五晚上十点,运营那边报了个紧急问题:积分排行榜接口的RT从60ms一路涨到3s,MongoDB主节点的CPU直接冲到99%,慢查询日志刷了满满一页。我翻了下日志,发现其中一条记录特别刺眼——同一个文档ID在十分钟内被查了48万次…

📰

DeepSeek 15天精通指南:API调用、本地部署与工具链集成全攻略

简介:这是一份关于DeepSeek人工智能平台的实战操作手册,以15天从入门到精通为主线,适合办公人士、科研人员、自媒体创作者、学生及编程爱好者学习。内容涵盖账号注册与界面认识、高效提问方法、文档解析与代码生成,以及学术论文辅…

📰

Zeroboot源码深度解析:CPU状态恢复的严格顺序、vmstate解析与KVM开发避坑

Zeroboot源码深度解析:CPU状态恢复的严格顺序、vmstate解析与KVM开发避坑 【免费下载链接】zeroboot Sub-millisecond VM sandboxes for AI agents via copy-on-write forking 项目地址: https://gitcode.com/gh_mirrors/ze/zeroboot Zeroboot 是一个面向 AI…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬