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:
@@ -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")[
|
||||
编写程序计算二维正方形区域上的泊松方程,并与解析解比较收敛阶。
|
||||
]
|
||||
@@ -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
Reference in New Issue
Block a user