feat: add document conversion templates

Add md-to-pdf and md-to-docx templates for mytoolkit convert command.
Migrated from workspace/templates.
This commit is contained in:
Zhengshou Lai
2026-05-06 00:11:37 +08:00
parent dea83774a5
commit 2791a785ff
18 changed files with 20018 additions and 0 deletions
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
# 示例文档
这是一份使用默认 Word 模板生成的示例文档,展示基础排版效果。
## 二级标题
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
### 三级标题
- 无序列表项一
- 无序列表项二
- 无序列表项三
1. 有序列表项一
2. 有序列表项二
3. 有序列表项三
**粗体文字***斜体文字* 的混排效果。
> 这是一段引用文本,用于展示引用样式。
---
## 表格
| 姓名 | 单位 | 职称 |
|------|------|------|
| 张三 | 中山大学 | 教授 |
| 李四 | 清华大学 | 副教授 |
| 王五 | 北京大学 | 讲师 |
Binary file not shown.
Binary file not shown.
+41
View File
@@ -0,0 +1,41 @@
# 审稿意见
**稿件编号**: PT-2024-001
**题目**: 颗粒材料剪切带演化规律的离散元模拟研究
**审稿结论**: 修改后发表
---
## 总体评价
本文采用离散元方法(DEM)对颗粒材料在剪切过程中的剪切带演化规律进行了系统研究,选题具有较强的理论意义和工程应用价值。数值模拟方案设计合理,结果分析较为深入。建议在以下方面进行修改完善:
## 具体意见
### 1. 关于模型参数选取
第 3.2 节中颗粒接触参数的选取依据不够充分。建议补充参数标定过程,或引用相关文献说明参数取值的合理性。
### 2. 关于结果讨论
图 5 所示的剪切带厚度随围压变化的趋势与文献 [12] 的实验结果存在差异,作者未对此进行讨论。请补充分析可能的原因。
### 3. 关于格式规范
- 公式编号不连续,请检查并统一
- 部分参考文献格式不符合期刊要求
- 图注缺少数据来源说明
## 次要意见
1. 摘要中"首次提出了"表述过于绝对,建议修改
2. 结论部分建议补充研究的局限性
3. 英文摘要存在语法错误,建议请母语者润色
---
**审稿人**: 匿名审稿人
**日期**: 2024 年 3 月 15 日
Binary file not shown.
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
# 赖正首 Zhengshou Lai
副教授 · 中山大学土木工程学院
laizhengsh@mail.sysu.edu.cn · +86 198-6603-8070
[Google Scholar](https://scholar.google.com) · [ORCID](https://orcid.org/0000-0002-2378-9193)
赖正首博士现任中山大学土木工程学院副教授,香港高等研究院应用数学研究中心双聘教师。研究专注于颗粒介质多尺度计算力学,致力于开发先进的数值算法与高性能计算软件。
## 教育背景
| 时间 | 学位 | 学校 |
| --------- | ----------------------------- | ----------------------- |
| 20152018 | Ph.D. in Civil Engineering | Clemson University, USA |
| 20122014 | Ph.D. Candidate (transferred) | Sun Yat-sen University |
| 20082012 | B.Eng. in Traffic Engineering | Sun Yat-sen University |
## 工作经历
| 时间 | 职位 | 地点 |
| --------- | ------------------------------------------ | --------- |
| 2023–至今 | 副教授,中山大学土木工程学院 | 广州 |
| 2025–至今 | 双聘教师,香港高等研究院应用数学研究中心 | 香港 |
| 20212023 | 博士后("香江学者"计划),香港科技大学/中山大学 | 香港/广州 |
| 2019–2021 | 博士后,中山大学智能工程学院 | 广州 |
## 论文发表(†学生一作,\#通讯)
### 2026
1. **Lai, Z.**, Huang, S., Kong, Y., Zhao, S., Zhao, J., & Huang, L. (2026). Hybrid resolved-unresolved CFD-DEM framework for multiscale fluid-particle systems with irregular-shaped and polydisperse particles. *Journal of Computational Physics*, 554, 114759.
2. Li, C., Huang, L., **Lai, Z.**\#, Huang, S., & Lin, Y. (2026). A diffusion-based generative framework for virtual porous granular media generation. *Powder Technology*, 473, 122230.
3. Li, C., **Lai, Z.**\#, Huang, S., & Huang, L. (2026). Neural network-driven shape representation and computational particle mechanics via signed distance fields. *Engineering Applications of Artificial Intelligence*, 167, 113913.
### 2024
4. Huang, S.†, Wang, P., **Lai, Z.**\#, Yin, Z. Y., Huang, L., & Xu, C. (2024). Machine-learning-enabled discrete element method: The extension to three dimensions and computational issues. *Computer Methods in Applied Mechanics and Engineering*, 432, 117445.
5. **Lai, Z.**, Feng, Y. T., Zhao, J., & Huang, L. (2024). Unifying the contact in signed distance field-based and conventional discrete element methods. *Computers and Geotechnics*, 176, 106764.
6. Lin, Y., **Lai, Z.**, Ma, J., & Huang, L. (2024). A combined weighted Voronoi tessellation and random field approach for modeling heterogeneous rocks with correlated grain structure. *Construction and Building Materials*, 416, 135228.
### 2022
7. **Lai, Z.**, Zhao, S., Zhao, J., & Huang, L. (2022). Signed distance field framework for unified DEM modeling of granular media with arbitrary particle shapes. *Computational Mechanics*, 70(4), 763--783.
8. Xiao, R., Liang, B., Wu, F., Huang, L., & **Lai, Z.** (2022). Biocementation of coral sand under seawater environment and an improved three-stage biogrouting approach. *Construction and Building Materials*, 362, 129758.
## 科研项目
### 纵向项目
- **广东省科学技术厅,面上项目(主持)**;水合物固态流化开采中气—液—固三相流动机理与耦合数值方法研究;2026.01–至今(10万元)
- **国家自然科学基金,青年项目(主持)**;基于CT图像和机器学习的钙质砂宏微观力学试验及多尺度离散元建模;2020.01–2022.1225万元)
- **博士后科学基金,面上项目(主持)**;基于傅里叶级数的不规则颗粒离散元方法研究;2020.01–2021.1212万元)
### 横向项目
- **中国科学院力学研究所(主持)**;非球形颗粒的接触算法开发;2025–至今(18万元)
- **中铁十八局集团市政工程有限公司(主持)**;深厚饱和淤泥地层地铁车站深基坑施工变形规律与预警控制技术;2024–至今(160万元)
## 获奖与荣誉
- **2024**;地下工程裂隙岩体渗流测试装备与防渗关键技术,广东省技术发明奖,一等奖
- **2022**;软土-城市隧道耦合体系性能演化理论与安全控制技术,广东省科技进步奖,一等奖
- **2020**;基于岩溶发育机理的碳酸盐岩地区工程地质调查及塌陷预警关键技术,广东省科技进步奖,二等奖
## 知识产权
### 发明专利
- 一种基于改进浸入边界及有向距离场的任意形状颗粒CFD-DEM处理方法及系统,2024
- 基于粒子群优化与贝塞尔曲线的颗粒形状模拟方法及系统,2021
- 一种不规则颗粒的形状模拟方法、装置及设备,2020
### 软件著作权
- 应用过程分析与模拟平台-基于有向距离场的CFD-DEM流固耦合软件 V1.02024
- NetDEM离散元软件 V1.02022
## 学术服务
### 期刊审稿
- *Computers and Geotechnics*、*Acta Geotechnica*、*Granular Matter*、*Engineering Geology*、*Powder Technology*、*Applied Mathematical Modelling*、*Ocean Engineering* 等
### 学术兼职
- **中国岩石力学与工程学会** — 人工智能技术实用化专业委员会委员(2024–至今)
- **中国颗粒学会** — 颗粒计算专业委员会委员(2025–至今)
- **广东省力学学会** — 委员(2025–至今)
+130
View File
@@ -0,0 +1,130 @@
// CV 模板 —— 简洁学术风格,参考 default 模板
#let serif-stack = ("Cambria", "PingFang SC", "Helvetica Neue", "Arial")
#let sans-stack = ("Cambria", "PingFang SC", "Helvetica Neue", "Arial")
#let mono-stack = ("Courier New", "Menlo", "Monaco", "SimSun")
#let primary = rgb("#1F4E79")
#let accent = rgb("#3467A8")
#let light = rgb("#96A0AA")
#let rule-blue = accent.lighten(46%)
#let _cv-h1-counter = counter("cv-h1")
#let three-line-table(..args) = {
let kwargs = args.named()
let children = args.pos()
let new_children = ()
let header_seen = false
for child in children {
if child.func() == table.header {
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
new_children.push(child)
header_seen = true
} else if header_seen and child.func() == table.hline {
new_children.push(table.hline(stroke: 0.75pt + rule-blue))
header_seen = false
} else {
new_children.push(child)
}
}
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
kwargs.stroke = none
kwargs.inset = (x: 8pt, y: 5pt)
table(..new_children, ..kwargs)
}
#let cv(body) = {
set page(
paper: "a4",
margin: (top: 2.0cm, bottom: 2.0cm, left: 2.0cm, right: 2.0cm),
header: context {
align(right)[
#text(size: 8pt, fill: light)[
Updated on #datetime.today().display("[year]-[month]-[day]")
]
]
},
footer: context {
align(center)[
#text(size: 8pt, fill: light)[
— #counter(page).display("1") —
]
]
},
)
set text(
font: serif-stack,
size: 10pt,
lang: "zh",
)
show raw: set text(font: mono-stack)
set par(
justify: true,
leading: 0.72em,
spacing: 0.92em,
first-line-indent: 0em,
)
set list(tight: true, indent: 1.2em, body-indent: 0.45em, spacing: 0.85em)
set enum(tight: true, indent: 1.2em, body-indent: 0.45em, spacing: 0.85em)
show heading: it => {
set text(font: sans-stack)
it
}
show heading.where(level: 1): it => {
_cv-h1-counter.step()
context {
let n = _cv-h1-counter.get().first()
if n == 1 {
align(center)[
#text(size: 18pt, weight: "bold")[#it.body]
]
v(0.8em)
} else {
v(1.2em)
text(size: 13pt, weight: "bold", fill: primary, it.body)
v(0.45em)
}
}
}
show heading.where(level: 2): it => {
v(0.8em)
stack(spacing: 0.5em,
text(size: 11pt, weight: "bold", fill: accent, it.body),
line(length: 100%, stroke: 0.7pt + rule-blue),
)
v(0.2em)
}
show heading.where(level: 3): it => {
v(0.5em)
text(size: 10pt, weight: "bold", fill: light, it.body)
v(0.2em)
}
set heading(numbering: none)
show link: it => text(fill: accent, it)
show strong: it => {
text(font: sans-stack, weight: "bold", it.body)
}
set table(
inset: (x: 8pt, y: 5pt),
stroke: 0.5pt + rule-blue,
)
body
}
Binary file not shown.
+27
View File
@@ -0,0 +1,27 @@
# 默认模板示例
这是一个使用默认模板的简单 Markdown 文档。
## 基本排版
默认模板只做最基础的排版设置:
- A4 纸张
- Cambria + SimSun 字体
- 两端对齐
- 无额外样式
## 代码示例
```python
def hello():
print("Hello, World!")
```
## 表格
| 项目 | 说明 |
|------|------|
| 字体 | Cambria / SimSun |
| 字号 | 11pt |
| 页边距 | 2.5cm |
+115
View File
@@ -0,0 +1,115 @@
// 默认空模板 —— 基础排版 + 简洁样式 + 封面页 + 三线表
#let primary = rgb("#1F4E79")
#let accent = rgb("#3467A8")
#let light = rgb("#96A0AA")
#let rule-blue = accent.lighten(46%)
#let cover-page(title, subtitle, date) = {
page(margin: (top: 3cm, bottom: 3cm, left: 3cm, right: 3cm))[
#align(center + horizon)[
#v(2cm)
#text(size: 28pt, weight: "bold", fill: primary, title)
#if subtitle != none {
v(0.8cm)
text(size: 16pt, fill: accent, subtitle)
}
#if date != none {
v(2cm)
text(size: 12pt, fill: light, date)
}
]
]
}
#let three-line-table(..args) = {
let kwargs = args.named()
let children = args.pos()
let new_children = ()
let header_seen = false
for child in children {
if child.func() == table.header {
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
new_children.push(child)
header_seen = true
} else if header_seen and child.func() == table.hline {
new_children.push(table.hline(stroke: 0.75pt + rule-blue))
header_seen = false
} else {
new_children.push(child)
}
}
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
kwargs.stroke = none
kwargs.inset = (x: 8pt, y: 5pt)
table(..new_children, ..kwargs)
}
#let default(
title: none,
subtitle: none,
date: none,
body,
) = {
if title != none {
cover-page(title, subtitle, date)
pagebreak()
}
set page(
paper: "a4",
margin: (top: 2.5cm, bottom: 2.5cm, left: 2.5cm, right: 2.5cm),
footer: context {
align(center)[
#text(size: 9pt, fill: light)[
#counter(page).display("1")
]
]
},
)
set text(
font: (
"Cambria",
"PingFang SC",
"Helvetica Neue",
"Arial",
),
size: 11pt,
lang: "zh",
)
// Body rhythm: spacing > leading; first-line indent = Typst default (none)
set par(justify: true, leading: 0.78em, spacing: 1.12em)
set list(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
set enum(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
// Headings: slightly tighter than long-form (manual/textbook) because H1 is 16pt here
show heading.where(level: 1): it => {
v(1.15em)
text(size: 16pt, weight: "bold", fill: primary, it)
v(0.5em)
}
show heading.where(level: 2): it => {
v(0.9em)
text(size: 13pt, weight: "bold", fill: accent, it)
v(0.34em)
}
show heading.where(level: 3): it => {
v(0.62em)
text(size: 11pt, weight: "bold", fill: light, it)
v(0.24em)
}
set heading(numbering: none)
show link: it => text(fill: accent, it)
body
}
+203
View File
@@ -0,0 +1,203 @@
---
title: Phynexis Python API 参考手册
author: Phynexis 开发团队
version: 2.1.0
lang: zh
date: 2025 年 5 月
---
# 快速开始
## 安装
通过 pip 安装 Phynexis
```bash
pip install phynexis
```
#tip-box(title: "提示")[
建议使用虚拟环境安装,避免与系统 Python 包冲突。
```bash
python -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
```
]
## 第一个示例
```python
from phynexis import Model, Mesh
# 创建简单网格
mesh = Mesh.rectangle(width=1.0, height=1.0, nx=20, ny=20)
# 定义材料模型
model = Model(mesh)
model.set_material(young=210e9, nu=0.3, rho=7850)
# 求解
result = model.solve()
print(f"最大位移: {result.max_displacement:.6f} m")
```
---
# 核心 API
## Mesh 类
网格是有限元分析的基础。`Mesh` 类提供多种工厂方法创建常见几何。
### 类方法
#info-box(title: "Mesh.rectangle")[
**签名**: `Mesh.rectangle(width, height, nx, ny)`
**参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| `width` | `float` | 矩形宽度 |
| `height` | `float` | 矩形高度 |
| `nx` | `int` | x 方向单元数 |
| `ny` | `int` | y 方向单元数 |
**返回**: `Mesh` 实例
]
### 属性
```python
mesh.nodes # ndarray, shape (n_nodes, 2) — 节点坐标
mesh.elements # ndarray, shape (n_elements, 4) — 四边形单元拓扑
mesh.n_nodes # int — 节点总数
mesh.n_elements # int — 单元总数
```
## Model 类
`Model` 封装了物理场求解的完整流程。
### 核心方法
```python
model = Model(mesh)
# 设置材料参数
model.set_material(young=210e9, nu=0.3)
# 施加边界条件
model.fix_boundary(tag="left") # 固定左侧边界
model.apply_force(tag="right", fx=1e6) # 右侧施加力
# 求解
result = model.solve(solver="direct")
```
#warn-box(title: "注意")[
`solve()` 方法默认使用直接求解器。对于大规模问题(> 100,000 DOF),建议使用 `"cg"` 或 `"gmres"` 迭代求解器:
```python
result = model.solve(solver="cg", preconditioner="ilu")
```
]
---
# 高级用法
## 并行计算
Phynexis 支持 MPI 并行和 OpenMP 多线程。
```python
from phynexis.parallel import ParallelContext
with ParallelContext(nprocs=4) as ctx:
model = Model(mesh, comm=ctx.comm)
result = model.solve()
```
#danger-box(title: "危险")[
在 `ParallelContext` 内部分配大规模数组时,务必确保每个进程只分配自己的局部部分。全局数组会导致内存溢出:
```python
# ❌ 错误:每个进程都分配了全局矩阵
global_K = np.zeros((n_global, n_global))
# ✅ 正确:只分配局部矩阵
local_K = np.zeros((n_local, n_local))
```
]
## 自定义材料模型
通过继承 `Material` 基类实现自定义本构:
```python
from phynexis.material import Material
class HyperelasticMaterial(Material):
def __init__(self, C10, C01):
self.C10 = C10
self.C01 = C01
def stress(self, F):
# F: 变形梯度
# 返回: PK1 应力张量
...
def tangent(self, F):
# 返回: 切线模量
...
```
---
# 故障排除
## 常见问题
**Q: 导入时报错 `ImportError: cannot import name 'Model' from 'phynexis'`**
说明你安装的是旧版本。请升级:
```bash
pip install --upgrade phynexis
```
**Q: 求解器收敛失败**
检查以下事项:
1. 边界条件是否充分约束了刚体位移
2. 材料参数是否合理(特别是泊松比接近 0.5 时)
3. 网格质量是否太差
**Q: 并行计算结果与串行不一致**
确保所有随机操作(如网格重编号)使用了相同的种子:
```python
import numpy as np
np.random.seed(42)
```
---
# 版本历史
## v2.1.0 (2025-05-01)
- 新增 GPU 加速求解器(CUDA 后端)
- 支持非线性材料模型
- 改进 mesh 生成器,支持任意多边形
## v2.0.0 (2025-01-15)
- 重构核心 API,向后兼容 v1.x
- 引入 `Model` 类统一求解接口
- 新增 Python 类型注解
File diff suppressed because it is too large Load Diff
+185
View File
@@ -0,0 +1,185 @@
// 手册模板 —— 代码高亮、提示框 + 封面页 + 三线表
#import "@preview/codly:1.3.0": *
#import "@preview/showybox:2.0.4": showybox
#let primary = rgb("#1F4E79")
#let secondary = rgb("#2C3E50")
#let accent = rgb("#3467A8")
#let light = rgb("#96A0AA")
#let rule-blue = accent.lighten(46%)
#let info-color = rgb("#0EA5E9")
#let tip-color = rgb("#10B981")
#let warn-color = rgb("#F59E0B")
#let danger-color = rgb("#EF4444")
#let cover-page(title, subtitle, date) = {
page(margin: (top: 3cm, bottom: 3cm, left: 3cm, right: 3cm))[
#align(center + horizon)[
#v(2cm)
#text(size: 28pt, weight: "bold", fill: primary, title)
#if subtitle != none {
v(0.8cm)
text(size: 16pt, fill: accent, subtitle)
}
#if date != none {
v(2cm)
text(size: 12pt, fill: light, date)
}
]
]
}
#let three-line-table(..args) = {
let kwargs = args.named()
let children = args.pos()
let new_children = ()
let header_seen = false
for child in children {
if child.func() == table.header {
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
new_children.push(child)
header_seen = true
} else if header_seen and child.func() == table.hline {
new_children.push(table.hline(stroke: 0.75pt + rule-blue))
header_seen = false
} else {
new_children.push(child)
}
}
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
kwargs.stroke = none
kwargs.inset = (x: 8pt, y: 5pt)
table(..new_children, ..kwargs)
}
#let manual(
title: none,
subtitle: none,
date: none,
body,
) = {
if title != none {
cover-page(title, subtitle, date)
pagebreak()
}
set text(
font: (
"Cambria",
"PingFang SC",
),
size: 10pt,
lang: "zh",
)
show raw: set text(font: (
"Courier New",
"Menlo",
"Monaco",
"SimSun",
))
// 10pt + code blocks: a touch denser than default 11pt, still spacing > leading
set par(justify: true, leading: 0.76em, spacing: 1.08em)
show: codly-init
codly(
stroke: 0.5pt + rule-blue,
fill: rgb("#F8F9FA"),
zebra-fill: none,
lang-stroke: none,
lang-fill: light.lighten(60%),
radius: 4pt,
number-format: n => text(fill: rgb("#6E7681"), str(n)),
number-align: right,
)
set list(tight: true, indent: 1.2em, body-indent: 0.45em, spacing: 0.52em)
set enum(tight: true, indent: 1.2em, body-indent: 0.45em, spacing: 0.52em)
// Same heading vertical scale as textbook (long-form); H sizes differ by template
show heading.where(level: 1): it => {
v(1.45em)
text(size: 18pt, weight: "bold", fill: primary, it)
v(0.55em)
}
show heading.where(level: 2): it => {
v(1.1em)
text(size: 13pt, weight: "bold", fill: accent, it)
v(0.5em)
}
show heading.where(level: 3): it => {
v(0.8em)
text(size: 11pt, weight: "bold", fill: secondary, it)
v(0.4em)
}
set heading(numbering: none)
set page(
paper: "a4",
margin: (top: 2.0cm, bottom: 2.0cm, left: 2.0cm, right: 2.0cm),
header: context {
let h = query(selector(heading.where(level: 1)))
.filter(it => it.location().page() <= here().page())
if h.len() > 0 {
let current = h.last()
align(right)[
#text(size: 8pt, fill: light)[
#current.body
]
]
}
},
footer: context {
align(center)[
#text(size: 8pt, fill: light)[
#counter(page).display("1")
]
]
},
)
show link: it => text(fill: accent, it)
outline(title: "目录", depth: 2)
pagebreak()
body
}
#let info-box(title: "Info", body) = showybox(
title: title,
title-style: (color: white, weight: "bold"),
frame: (border-color: info-color, title-color: info-color, thickness: 1pt, radius: 4pt),
body
)
#let tip-box(title: "Tip", body) = showybox(
title: title,
title-style: (color: white, weight: "bold"),
frame: (border-color: tip-color, title-color: tip-color, thickness: 1pt, radius: 4pt),
body
)
#let warn-box(title: "Warning", body) = showybox(
title: title,
title-style: (color: white, weight: "bold"),
frame: (border-color: warn-color, title-color: warn-color, thickness: 1pt, radius: 4pt),
body
)
#let danger-box(title: "Danger", body) = showybox(
title: title,
title-style: (color: white, weight: "bold"),
frame: (border-color: danger-color, title-color: danger-color, thickness: 1pt, radius: 4pt),
body
)
+113
View File
@@ -0,0 +1,113 @@
---
title: 计算力学导论
author: 赖正首
lang: zh
date: 2025
---
# 引言
计算力学是利用数值方法求解力学问题的学科。本章将介绍有限元方法的基本概念。
## 历史背景
有限元方法起源于 20 世纪 50 年代的航空工业。Turner 等人于 1956 年首次发表了关于有限元方法的系统性论文。
## 本章概述
本书将按以下结构组织:
- **第一章** 介绍有限元方法的基本原理
- **第二章** 讨论变分原理与弱形式
- **第三章** 探讨高阶有限元与谱方法
---
# 有限元方法基础
## 强形式与弱形式
考虑一维泊松方程:
$$-u''(x) = f(x), \quad x \in (0, 1)$$
边界条件为 $u(0) = u(1) = 0$。
以下定理框使用 Typst 语法;`mytoolkit` 会在转 Typst 前自动包一层 raw,避免行首 `#` 被 Pandoc 转义。
#theorem(title: "解的存在唯一性")[
设 $f \in L^2(0,1)$,则上述边值问题存在唯一弱解 $u \in H_0^1(0,1)$。
]
#proof[
根据 Lax-Milgram 定理,双线性形式 $a(u, v) = \int_0^1 u' v' \, \mathrm{d}x$ 在 $H_0^1(0,1)$ 上是连续且强制的,线性泛函 $L(v) = \int_0^1 f v \, \mathrm{d}x$ 是连续的。因此存在唯一解。
]
## 伽辽金方法
#example(title: "一维泊松方程")[
取试探空间 $V_h = \mathrm{span}\{\phi_1, \phi_2, \dots, \phi_n\}$,其中 $\phi_i$ 为分段线性帽函数。则近似解可写为:
$$u_h(x) = \sum_{j=1}^n u_j \phi_j(x)$$
代入弱形式并取测试函数 $v = \phi_i$,得到线性方程组 $K u = F$。
]
---
# 变分原理
## 极小位能原理
#lemma(title: "能量泛函的凸性")[
定义能量泛函 $J(v) = \frac{1}{2} a(v, v) - L(v)$,则 $J$ 是严格凸的。
]
## 拉格朗日乘子法
对于有约束的优化问题,引入拉格朗日乘子:
$$\mathcal{L}(u, \lambda) = J(u) + \lambda^T (B u - g)$$
---
# 高级主题
## 代码示例
以下是用 Python 实现的简单有限元求解器:
```python
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import spsolve
def assemble_stiffness(n):
"""组装一维刚度矩阵"""
h = 1.0 / (n + 1)
data = np.array([1/h] * (n-1) + [2/h] * n + [1/h] * (n-1))
rows = np.array(list(range(n-1)) + list(range(n)) + list(range(1, n)))
cols = np.array(list(range(1, n)) + list(range(n)) + list(range(n-1)))
return csr_matrix((data, (rows, cols)), shape=(n, n))
def solve_poisson_1d(f, n):
"""求解一维泊松方程"""
K = assemble_stiffness(n)
h = 1.0 / (n + 1)
F = h * f(np.linspace(h, 1-h, n))
return spsolve(K, F)
```
## 练习
#exercise(title: "3.1")[
证明一维线性有限元方法的收敛阶为 $O(h^2)$,即:
$$|u - u_h|_1 \leq C h |u|_2$$
其中 $C$ 为与 $h$ 无关的常数。
]
#exercise(title: "3.2")[
编写程序计算二维正方形区域上的泊松方程,并与解析解比较收敛阶。
]
+194
View File
@@ -0,0 +1,194 @@
// 教材模板 —— 定理/代码/目录 + 封面页 + 三线表
#import "@preview/bookly:3.1.0": *
#import "@preview/great-theorems:0.1.2": *
#import "@preview/codly:1.3.0": *
#import "@preview/showybox:2.0.4": showybox
#let primary = rgb("#1F4E79")
#let secondary = rgb("#2C3E50")
#let accent = rgb("#3467A8")
#let light = rgb("#96A0AA")
#let rule-blue = accent.lighten(46%)
// Filled mathblocks: inset so fill does not hug text; radius slightly larger than codly (4pt)
#let _mathblock-inset = (x: 14pt, y: 12pt)
#let _mathblock-radius = 5pt
// Theorem-like environments (great-theorems)
#let theorem = mathblock(
blocktitle: "Theorem",
fill: rgb("#E8F0FE"),
inset: _mathblock-inset,
radius: _mathblock-radius,
)
#let lemma = mathblock(
blocktitle: "Lemma",
fill: rgb("#F3E8FF"),
inset: _mathblock-inset,
radius: _mathblock-radius,
)
#let proof = proofblock(
inset: _mathblock-inset,
radius: _mathblock-radius,
)
#let example = mathblock(
blocktitle: "Example",
fill: rgb("#F0FDF4"),
inset: _mathblock-inset,
radius: _mathblock-radius,
)
// Uncounted block: titles must use `title: ...`, not a positional string (see great-theorems README).
#let exercise = mathblock(
blocktitle: "Exercise",
prefix: [],
titlix: title => [*习题*~#title~#h(0.25em)],
fill: rgb("#FEF3C7"),
inset: (x: 14pt, y: 13pt),
radius: _mathblock-radius,
)
#let cover-page(title, subtitle, date) = {
page(margin: (top: 3cm, bottom: 3cm, left: 3cm, right: 3cm))[
#align(center + horizon)[
#v(2cm)
#text(size: 28pt, weight: "bold", fill: primary, title)
#if subtitle != none {
v(0.8cm)
text(size: 16pt, fill: accent, subtitle)
}
#if date != none {
v(2cm)
text(size: 12pt, fill: light, date)
}
]
]
}
#let three-line-table(..args) = {
let kwargs = args.named()
let children = args.pos()
let new_children = ()
let header_seen = false
for child in children {
if child.func() == table.header {
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
new_children.push(child)
header_seen = true
} else if header_seen and child.func() == table.hline {
new_children.push(table.hline(stroke: 0.75pt + rule-blue))
header_seen = false
} else {
new_children.push(child)
}
}
new_children.push(table.hline(stroke: 1.25pt + rule-blue))
kwargs.stroke = none
kwargs.inset = (x: 8pt, y: 5pt)
table(..new_children, ..kwargs)
}
#let textbook(
title: none,
subtitle: none,
date: none,
body,
) = {
if title != none {
cover-page(title, subtitle, date)
pagebreak()
}
set text(
font: (
"Cambria",
"PingFang SC",
),
size: 11pt,
lang: "zh",
)
show raw: set text(font: (
"Courier New",
"Menlo",
"Monaco",
"SimSun",
))
// Long-form reading: most generous body rhythm in this family
set par(
justify: true,
leading: 0.86em,
spacing: 1.2em,
first-line-indent: 2em,
)
show: codly-init
codly(
stroke: 0.5pt + rule-blue,
fill: rgb("#F8F9FA"),
zebra-fill: none,
lang-stroke: none,
lang-fill: light.lighten(60%),
radius: 4pt,
)
show: great-theorems-init
set heading(numbering: "1.1")
// Same 11pt body as default: match list rhythm to default template
set list(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
set enum(indent: 1.2em, body-indent: 0.45em, spacing: 0.62em)
// Vertical rhythm matches manual (long-form); font sizes differ
show heading.where(level: 1): it => {
v(1.45em)
text(size: 18pt, weight: "bold", fill: primary, it)
v(0.55em)
}
show heading.where(level: 2): it => {
v(1.1em)
text(size: 14pt, weight: "bold", fill: accent, it)
v(0.5em)
}
show heading.where(level: 3): it => {
v(0.8em)
text(size: 12pt, weight: "bold", fill: secondary, it)
v(0.4em)
}
set page(
paper: "a4",
margin: (top: 2.5cm, bottom: 2.5cm, left: 2.5cm, right: 2.5cm),
header: context {
let h = query(selector(heading.where(level: 1))).find(it => it.location().page() == here().page())
if h != none {
align(right)[
#text(size: 9pt, fill: light)[
#h.body
]
]
}
},
footer: context {
align(center)[
#text(size: 9pt, fill: light)[
#counter(page).display("1")
]
]
},
)
show link: it => text(fill: accent, it)
outline(title: "目录", depth: 2)
pagebreak()
body
}
File diff suppressed because it is too large Load Diff