├── fealpy ├── scipy.md ├── problem.md ├── fealpy-mesh-1.md ├── sagemath-jian-jie.md ├── zhong-xin-zuo-biao.md ├── .gitignore ├── lagrange-cha-zhi-xiang-liang-hua-shi-xian.md ├── figures ├── int.png ├── mplot.png ├── shell.png ├── vect.gif ├── linux.jpeg ├── ndarray.png ├── python.jpeg ├── twotri.png ├── unvect.gif ├── zhuyi.jpeg ├── bytecode.png ├── fourquad.png ├── localedge.png ├── trianglemesh.png ├── array_vs_list.png ├── numpy-mesh-edge.png ├── twotri.py ├── test_trianglemesh.py ├── localedge.py ├── fourquad.py ├── numpy-mesh-edge.py ├── bytecode.svg └── vectorized.svg ├── book.json ├── xian-xing-you-xian-yuan.md ├── mumps-jian-jie.md ├── hypre.md ├── SUMMARY.md ├── python-rong-qi-ff1a-tuble.md ├── numpy.md ├── python-han-shu.md ├── python-zhong-de-ji-ben-shu-ju-jie-gou-yu-liu-cheng-kong-zhi.md ├── numpy-mesh-node-cell.md ├── python.md ├── matlab-engine-for-python.md ├── xi-shu-ju-zhen.md ├── fealpy.md ├── fealpy-win.md ├── install_python.md ├── README.md ├── win-open-dev.md ├── ubuntu-xia-bing-xing-mumps-de-an-zhuang.md ├── mumps.md ├── san-du-ding-li.md ├── fealpy-mesh-0.md └── numpy-mesh-edge.md /fealpy: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scipy.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /problem.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fealpy-mesh-1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sagemath-jian-jie.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zhong-xin-zuo-biao.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _book/* 2 | node_modules/* 3 | -------------------------------------------------------------------------------- /lagrange-cha-zhi-xiang-liang-hua-shi-xian.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /figures/int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/int.png -------------------------------------------------------------------------------- /figures/mplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/mplot.png -------------------------------------------------------------------------------- /figures/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/shell.png -------------------------------------------------------------------------------- /figures/vect.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/vect.gif -------------------------------------------------------------------------------- /figures/linux.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/linux.jpeg -------------------------------------------------------------------------------- /figures/ndarray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/ndarray.png -------------------------------------------------------------------------------- /figures/python.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/python.jpeg -------------------------------------------------------------------------------- /figures/twotri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/twotri.png -------------------------------------------------------------------------------- /figures/unvect.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/unvect.gif -------------------------------------------------------------------------------- /figures/zhuyi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/zhuyi.jpeg -------------------------------------------------------------------------------- /figures/bytecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/bytecode.png -------------------------------------------------------------------------------- /figures/fourquad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/fourquad.png -------------------------------------------------------------------------------- /figures/localedge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/localedge.png -------------------------------------------------------------------------------- /figures/trianglemesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/trianglemesh.png -------------------------------------------------------------------------------- /figures/array_vs_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/array_vs_list.png -------------------------------------------------------------------------------- /figures/numpy-mesh-edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weihuayi/femprogramming/HEAD/figures/numpy-mesh-edge.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["mathjax", "image-captions", "fontsettings"], 3 | "pluginsConfig": { 4 | "fontSettings": { 5 | "size": 1 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /xian-xing-you-xian-yuan.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [1] 极限的思想 https://blog.csdn.net/lengxue/article/details/14209459 6 | [2] 割圆术 https://zh.wikipedia.org/wiki/%E5%89%B2%E5%9C%86%E6%9C%AF_(%E5%88%98%E5%BE%BD) 7 | [3] 成语与数学思想 http://blog.sina.com.cn/s/blog_e9eb50700102veqc.html -------------------------------------------------------------------------------- /figures/twotri.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from fealpy.mesh import TriangleMesh 3 | import matplotlib.pyplot as plt 4 | 5 | node = np.array([[0.0, 0.0], 6 | [1.0, 0.0], 7 | [1.0, 1.0], 8 | [0.0, 1.0]], dtype=np.float) 9 | cell = np.array([[1, 2, 0], 10 | [3, 0, 2]], dtype=np.int32) 11 | 12 | mesh = TriangleMesh(node, cell) 13 | fig = plt.figure() 14 | axes = fig.gca() 15 | mesh.add_plot(axes) 16 | mesh.find_node(axes, showindex=True) 17 | mesh.find_cell(axes, showindex=True) 18 | plt.show() 19 | -------------------------------------------------------------------------------- /figures/test_trianglemesh.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from fealpy.mesh import TriangleMesh 4 | 5 | node = np.array([ 6 | (0, 0), 7 | (1, 0), 8 | (1, 1), 9 | (0, 1)], dtype=np.float) 10 | cell = np.array([ 11 | (1, 2, 0), 12 | (3, 0, 2)], dtype=np.int) 13 | 14 | tmesh = TriangleMesh(node, cell) 15 | tmesh.uniform_refine(2) 16 | node = tmesh.entity('node') 17 | cell = tmesh.entity('cell') 18 | fig = plt.figure() 19 | axes = fig.gca() 20 | tmesh.add_plot(axes) 21 | tmesh.find_node(axes, showindex=True) 22 | tmesh.find_edge(axes, showindex=True) 23 | tmesh.find_cell(axes, showindex=True) 24 | plt.show() 25 | -------------------------------------------------------------------------------- /figures/localedge.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from fealpy.mesh import TriangleMesh 3 | import matplotlib.pyplot as plt 4 | 5 | node = np.array([[0.0, 0.0], 6 | [1.0, 0.0], 7 | [0.5, np.sqrt(3)/2.0]], dtype=np.float) 8 | cell = np.array([[0, 1, 2]], dtype=np.int32) 9 | 10 | localEdge = np.array([[1, 2], [2, 0], [0, 1]], dtype=np.int32) 11 | 12 | bc = (node[localEdge[:, 0]] + node[localEdge[:, 1]])/2.0 13 | 14 | mesh = TriangleMesh(node, cell) 15 | fig = plt.figure() 16 | axes = fig.gca() 17 | mesh.add_plot(axes) 18 | mesh.find_node(axes, showindex=True) 19 | #mesh.find_cell(axes, showindex=True) 20 | mesh.find_node(axes, node=bc, index=np.array([0, 1, 2], dtype=np.int32), showindex=True, markersize=150, color='g') 21 | plt.savefig('localedge.png') 22 | plt.show() 23 | -------------------------------------------------------------------------------- /figures/fourquad.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from fealpy.mesh import QuadrangleMesh 3 | import matplotlib.pyplot as plt 4 | 5 | node = np.array([[0.0, 0.0], 6 | [0.5, 0.0], 7 | [1.0, 0.0], 8 | [0.0, 0.5], 9 | [0.5, 0.5], 10 | [1.0, 0.5], 11 | [0.0, 1.0], 12 | [0.5, 1.0], 13 | [1.0, 1.0]], dtype=np.float) 14 | 15 | cell = np.array([[0, 1, 4, 3], 16 | [3, 4, 7, 6], 17 | [1, 2, 5, 4], 18 | [4, 5, 8, 7]], dtype=np.int32) 19 | 20 | mesh = QuadrangleMesh(node, cell) 21 | fig = plt.figure() 22 | axes = fig.gca() 23 | mesh.add_plot(axes) 24 | mesh.find_node(axes, showindex=True) 25 | mesh.find_cell(axes, showindex=True) 26 | plt.show() 27 | -------------------------------------------------------------------------------- /mumps-jian-jie.md: -------------------------------------------------------------------------------- 1 | # MUMPS 2 | 3 | 最近在算一个三维线弹性问题, 自由度规模有 50 多万, 结果发现没有什么好用的解法器。 Matlab 的直接解法器根本算不出结果。 用了几个代数多重网格解法器, 发现都不怎么稳健, 要么收敛速度慢,要么算的快但结果对不上。 理论总是很美好, 但遇到实际问题还是需要一翻尝试和探索。 4 | 5 | 最终还是找到 [MUMPS(MUltifrontal Massively Parallel sparse direct Solver)](http://mumps.enseeiht.fr/) 是一款高效的并行稀疏矩阵直接解法器, 用的算法是[多波前方法](https://en.wikipedia.org/wiki/Frontal_solver)。 当然, 它还是一款由欧洲科学计算研究中心(CERFACS)、 法国图卢兹计算机研究所(IRIT-ENSEEIHT) 和法国国立计算机及自动化研究院(INRIA)联合支持的自由软件, 它的软件许可协议为 [CeCILL-C](https://en.wikipedia.org/wiki/CeCILL), 该协议是和 GPL 协议兼容。 6 | 7 | 测试了一下, 50 多万的自由度, 用 4 个线程, 40 秒左右就可以解出结果。 如果手头没有现成的快速解法器, 可以考虑用下这个解法器。 8 | 9 | 10 | ## Ubuntu 下的安装 11 | 12 | Ubuntu 下, 用如下两条命令即可安装好 mumps: 13 | 14 | ``` 15 | $ sudo apt install libmumps-dev # 安装 mumps 的库文件和头文件 16 | $ sudo -H pip3 install PyMUMPS # 安装 mumps 的 Python 3 接口 17 | ``` 18 | 19 | 调用也很简单 20 | 21 | ``` 22 | from mumps import spsolve # 导入求解接口 23 | #....... 生成你的矩阵和右端 24 | x = spsolve(A, b) # 求解 25 | ``` 26 | 27 | 在后续的文章中, 我会找机会介绍一些更复杂的安装与使用方式, 如: 28 | * 并行版本的编译与安装 29 | * Matlab 接口的安装 30 | 31 | ## 32 | -------------------------------------------------------------------------------- /hypre.md: -------------------------------------------------------------------------------- 1 | # Hypre 安装笔记 2 | 3 | 求解线性代数系统 $Ax =b$ 是科学计算中很重要的组成部分。 对于困难一点的问题, 可能大部分时间都花在线性代数系统求解上面了。 以前都是算一些很简单的小问题, 没什么太多体会。 最近算三维高次元和线弹性问题, 才知道经典的代数多重网格方法求解这些问题根本不管用, 但手头熟悉的 Matlab 和 Python 软件包基本都是经典的方法, 根本满足不了实际的需要。 本来想自己重新学习实现, 但苦于能力和精力有限, 最后只能求助于更加专业强大的软件包, 比如 Hypre。 这里记录下自己学习使用的过程, 分享给你, 希望对你有点用处。 4 | 5 | ## Hypre 简介 6 | 7 | [Hypre](https://computation.llnl.gov/projects/hypre-scalable-linear-solvers-multigrid-methods/software) 为美国劳伦斯利佛摩国家实验室(英语:Lawrence Livermore National Laboratory,缩写: LLNL)开发的并行稀疏代数系统解法器, 主要特色是并行多重网格, 实现语言为 C 语言, 并行用 MPI 实现。 另外, 它也是跨平台的, 在主流系统下都可以安装。 8 | 9 | Hypre 遵循的开源协议为 [LGPL](https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License), 该协议比原来的 [GPL](https://en.wikipedia.org/wiki/GNU_General_Public_License) 协议要宽松。 如果以库的形式调用 Hypre, 你的程序也可以是不开源的。 10 | 11 | ## 在 Windows 下安装 Hypre 12 | 13 | 在 Linux 下安装 Hypre 很容易, 下面主要介绍在 Widnows 如何安装 Hypre 14 | 15 | * 下载安装 [MicroSoft .Net Framework 4.7](https://www.microsoft.com/zh-CN/download/details.aspx?id=55170) 16 | * 下载安装 [Visual Studio IDE Community 2017](https://visualstudio.microsoft.com/zh-hans/) 17 | * 安装 [mpi](https://www.microsoft.com/en-us/download/details.aspx?id=56727) 18 | * 安装 [CMake](https://cmake.org/download/) 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [导言](./README.md) 4 | * [Python 基础] 5 | * [Python 环境搭建](./install_python.md) 6 | * [Python 基本算术运算和变量](./python.md) 7 | * [Python 容器: list](python-zhong-de-ji-ben-shu-ju-jie-gou-yu-liu-cheng-kong-zhi.md) 8 | * [Python 容器:tuple](python-rong-qi-ff1a-tuble.md) 9 | * [Python 函数:function](python-han-shu.md) 10 | * [NumPy 基础] 11 | * [NumPy 简介](./numpy.md) 12 | * [SciPy 基础] 13 | * [SciPy 基础](./scipy.md) 14 | * [编程环境与通用软件] 15 | * [Mumps 简介](mumps-jian-jie.md) 16 | * [Ubuntu 下并行 Mumps 的安装](ubuntu-xia-bing-xing-mumps-de-an-zhuang.md) 17 | * [Matlab Engine for Python](matlab-engine-for-python.md) 18 | * [Windows 下开源开发环境的搭建](win-open-dev.md) 19 | * [有限元编程与 FEALPy] 20 | * [Fealpy 简介](./fealpy.md) 21 | * [Windows 下 FEALPy 的安装](./fealpy-win.md) 22 | * [FEALPY中的网格对象](./fealpy-mesh-0.md) 23 | * [FEALPY中的网格示例](./fealpy-mesh-1.md) 24 | * [网格数据结构的数组化表示:node 和 cell](numpy-mesh-node-cell.md) 25 | * [网格数据结构的数组化表示:edge](numpy-mesh-edge.md) 26 | * [重心坐标](zhong-xin-zuo-biao.md) 27 | * [线性有限元](xian-xing-you-xian-yuan.md) 28 | * [通用数学算法基础] 29 | * [稀疏矩阵](xi-shu-ju-zhen.md) 30 | * [散度定理](san-du-ding-li.md) 31 | 32 | * [问题集](./problem.md) 33 | 34 | -------------------------------------------------------------------------------- /python-rong-qi-ff1a-tuble.md: -------------------------------------------------------------------------------- 1 | # Python 容器: tuple 2 | 3 | 元组(tuple)容器是 Python 中另一个常用的数据结构, 在使用 Numpy 的过程中你会经常用到它。 它有以下两个特点 4 | 5 | * 有序 6 | * 不可修改 7 | 8 | ## 基本语法 9 | 10 | Python 中用 `()` 来创建元组, 如: 11 | ```python 12 | thistuple = (`cat`, `dog`, 1) 13 | print(thistuple) 14 | ``` 15 | 注意元组中的元素可以是任何 Python 对象, 包括元组本身。 16 | 17 | 创建只含一个元素的元组语法如下: 18 | ```python 19 | one_tuble = ('cat',) 20 | print(one_tuple) 21 | ``` 22 | **注意**其中的 **`,`** 不能省略。 23 | 24 | 创建一个空元组的语法如下: 25 | ```python 26 | empty_tuple = tuple() 27 | print(empty_tuple) 28 | ``` 29 | 30 | 也可以从列表创建元组: 31 | ```python 32 | test_tuple = tuple([2, 3, 4]) 33 | print(test_tuple) 34 | ``` 35 | 36 | 访问元组第 i 个元素的语法为: 37 | ```python 38 | thistuple[i] # i 可以是负值, 表示倒数第 |i| 个元素 39 | ``` 40 | 另外, 可以用**切片**语法得到元组的一个**子元组**: 41 | ```python 42 | thistuple[start:stop:step] 43 | ``` 44 | **注意**, 无论切片中包含多少个元素, 返回的都是**元组**对象, 如: 45 | ```python 46 | thistuple[:0] # 返回一个空元组 () 47 | thistuple[:1] # 返回只包含一个元素的元组, 包含元素为 thistuple 的第 0 个元素 48 | ``` 49 | **注意**, 对**列表**的切片操作, 也是得到列表的一个**子列表**。 50 | 51 | 虽然元组不能修改, 即你不能修改某个元素的值或者插入一个新的元素, 但与**列表**一样, 你可以把两个元组用 `+` 拼接起来: 52 | ```python 53 | tuple_0 = (1, 2) 54 | tuple_1 = (3, 4) 55 | print(tuple_0 + tuple_1) # 返回 (1, 2, 3, 4) 56 | ``` 57 | 58 | 最后, 获得元组长度的语法为 59 | ```python 60 | len(thistuple) 61 | ``` 62 | 63 | ## 总结 64 | 65 | 元组在 Python 函数的定义中经常用到, 主要是用于多个参数的输入和返回; 在 Numpy 中 , 多维数组的形状 `shape` 就是一个元组。 上面强调**注意**的地方, 是我自己在编程过程中经常会出错的地方,你也要多加**注意**哦。 -------------------------------------------------------------------------------- /numpy.md: -------------------------------------------------------------------------------- 1 | # Numpy 2 | 3 | 4 | Python 是一门**高级计算机编程语言(high-level computer language)**, 它让编程变的非常容易, 这是它能流行的重要原因之一。 当你执行 Python 代码时, Python 解释器首先把代码转化为计算机可理解的**字节码(bytecode)**, 然后再执行。 5 | 6 | ![](./figures/bytecode.svg) 7 | 8 | 用 Python 写程序你基本不用关心内存管理的问题, 也不用关心 CPU 具体要做什么操作, 这一切都由 Python 解释器负责处理, 所以用 Python 编码速度会很快, 但执行的速度快不快是另一会儿事儿。 前面讲过 Python 里面一切皆是对象, 就连最基本的整型和浮点型都是对象。 Python 在它们上面附加了很多其它的数据, 这些数据是 Python 能够替你管理内存和 CPU 操作必须要的数据, 而这些额外数据的处理会增加程序的负担。 因此, 获得编码容易的好处, 就需要付出执行速度的代价, 天下没有免费的东西。 9 | 10 | 而在科学计算中, 往往要对大量的整数或浮点数进行运算, 而且对程序的执行速度要求很高。 这时直接用 Python 的整型和浮点型(比如用前面讲的列表存储)就会大大拖慢程序的执行速度, 从这个角度来说, Python 语言本身是不适合科学计算的, 其实 Guido van Rossum 设计 Python 的初衷就不是为了科学计算。 但是 Python 的易用性吸引了科学计算圈子里的人。 11 | 12 |
13 | 14 | 15 |
16 | 17 | 18 | > “Python 这么好用! 没有我想要的功能?! 那我们就来做一个吧!” 只有在开源世界, 你才可以这么随意和任性。 19 | 20 | 于是就有了后来的 [Numpy](http://www.numpy.org/)。 Numpy 是专门为 Python 设计的,使得 Python 可以支持大型多维数及其相关的运算, 从此科学计算人的日子更好过一点了。 21 | 22 | Numpy 提供的多维数组 `ndarray` 和 Python 内置的列表(list)类型表面上看起来很相似, 但它们存储数据的方式完全不同, 见下图, 它很好的展示了 `ndarray` 的存储模型与列表的不同 23 | 24 | ![](./figures/array_vs_list.png) 25 | 26 | 因此相比于列表, Numpy 的 `ndarray` 提供了更加高效的存储和数据操作, 更适用于科学计算和数据科学, 它现在已经是整个 Python 科学计算和数据科学工具生态系统的核心。 27 | 28 | Numpy 数组的操作可以很快,也可以很慢, 关键是有没有利用**向量化**的操作, 下面两图生动地展示了两列数对应元素相加时的两种操作方式: 第一种是**标量化**的方式, CPU 一次只计算1 组数的加法; 第二种是**向量化**的方式, 一次计算 4 组数的加法, 速度轻松提高 4 倍! 29 | 30 | ![](./figures/unvect.gif) 31 | ![](./figures/vect.gif) 32 | 33 | 其中向量化的方式很好的利用了 CPU 的**单指令流多数据流(SIMD)** 功能, 所以更加高效。 而 Numpy 的向量化运算是通过**通用函数(ufunc)** 实现的。 34 | 35 | 后续的文章中,我会结合科学计算编程来介绍 Numpy 的多维数组对象 `ndarray` 和通用函数 `ufunc`的相关知识和用法。 36 | -------------------------------------------------------------------------------- /python-han-shu.md: -------------------------------------------------------------------------------- 1 | # Python 函数:function 2 | 3 | 代码**重用**是编程实践中需要着重考虑的内容, 因为重用是提高编程效率的根本手段。 你花了两个小时编程序, 编写的代码一种情况是只能使用一次, 另一种情况是能用很多次, 当然在后一种情况下, 那两个小时的价值才是最大化的。 4 | 5 | 编写合适的**函数(function)**就可以大大提高代码的重用性。 另外, 函数还可以带来如下的好处: 6 | 7 | * 允许用户更方便使用他人编写的代码, 而且不需要深入了解其代码是怎样编写的。 这就叫**信息隐藏(information hiding)**。 8 | * 可以把复杂的逻辑拆分为很多小模块。 这样不同的功能用不同的函数来实现, 可以避免编写冗长复杂的代码。 这就叫**模块化(modularization)**。 9 | * 可以精简所写的代码, 使代码更易阅读和维护。 程序员可能会在一个软件工程的很多场景下**重用**一个函数, 那么他必须把这个函数写的尽量**一般化**。 这个叫**抽象(abstration)**。 10 | 11 | ## 基本语法 12 | 13 | 下面通过一个简单的例子来说明 Python 定义函数的语法。 14 | 15 | ```python 16 | def add(a, b, c=10): 17 | d = a + b + c 18 | e = a - b + c 19 | return(d, e) 20 | ``` 21 | 其中 22 | * `def` 是定义函数的关键词; 23 | * `add` 是函数的名字; 24 | * `a`, `b` 和 `c` 是三个输入参数, 其中参数 `c` 有一个默认值 10。 **注意**, 定义函数时**没有默认值**的参数都必须放在**有默认值参数**的**前面**; 25 | * `:` 表示函数代码块的开始, 下面函数代码块中的代码都要**缩进(至少) 4 个空格**; 26 | * `return` 是返回语句, 返回一个包含两个元素 `d` 和 `e` 的元组。 27 | + 注意这里的 `()` 可以省略, 可以写成 `return d, e`。 28 | + 如果函数有多个值需要返回, 可以写成`return(r1, r2, ..., rn)`。 29 | 30 | 下面给出 `add` 函数的一些调用示例 31 | 32 | ```python 33 | r0, r1 = add(10, 3) # 其中 r0 = 23, r1 = 17 34 | ``` 35 | 36 | ```python 37 | r = add(10, 3) # 其中 r = (23, 17) 38 | ``` 39 | 40 | ```python 41 | r0, r1 = add(10, 3, c=20) # 其中 r0 = 33, r1 = 27, 这里的第三个参数 `c=20` 42 | ``` 43 | 44 | 下面给出另一个函数定义: 45 | 46 | ```python 47 | def myprint(a): 48 | print("the argument is ", a) 49 | ``` 50 | 注意这个函数没有返回语句 `return`, 但实际上它会返回一个默认值 `None`, 这个值是 Python 的一个内置对象, 代表**什么对象都不是**。 这个值我们以后还要经常用到。 51 | 52 | ## 延伸阅读 53 | 54 | 关于函数的参数, 还有更多内容,比如输入参数个数是可变的, 可参见下面的文档: 55 | 56 | http://book.pythontips.com/en/latest/args_and_kwargs.html 57 | 58 | 下面的网站可以输入代码测试你对函数定义的理解: 59 | 60 | https://www.learnpython.org/en/Multiple_Function_Arguments 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /python-zhong-de-ji-ben-shu-ju-jie-gou-yu-liu-cheng-kong-zhi.md: -------------------------------------------------------------------------------- 1 | # Python 容器: list 2 | --- 3 | 4 | 前一篇介绍了 Python 中的变量和基本数据类型: 5 | 6 | * 整型 7 | * 浮点型 8 | * 字符串型 9 | 10 | 但这并不能很好满足实际编程应用的需要, 我们还需要复杂一点的数据结构, 如容器(container), 就是用来**存放**其它对象的一种数据结构, 一般称它里面放的对象为**元素**. Python 中有如下内置的容器: 11 | 12 | * **list**: 列表, 是一个有序, 可修改, 可**用整数索引**的容器, 允许包含重复的元素. 13 | * **tuple**: 元组, 是一个有序, 不可修改, 可**用整数索引**的容器, 允许包含重复的元素. 14 | * **set**: 集合, 是一个无序, **不能**索引的容器, 没有重复的元素. 15 | * **dict**: 字典, 是一个无序, 可修改, 且可以**索引**的容器, 注意它的索引值不限整数 16 | 17 | 这次仅介绍 `list`. 首先, 介绍列表的**创建**. Python 是用 `[]` 语法来创建列表的, 如: 18 | ``` 19 | empty = [] #创建一个空列表 20 | pets = ['dog', 'cat', 'fish'] #创建一个长度为 3 的列表 21 | mixed_list = [3, 'flower', [2, 3, 4]] # 创建一个长度为 3 的列表, 注意最后一个元素是一个列表 22 | ``` 23 | 注意一个列表中的元素可以是不同类型的对象, 甚至是另外一个列表对象. 24 | 25 | 下面介绍列表元素的**索引**, 最基本的用法是索引某个元素, 基本语法: 26 | ``` 27 | list_val[i] # 获取第 i 个元素, i 可以是负整数值, 表示索引倒数第 i 个元素 28 | ``` 29 | 如 30 | ``` 31 | number = [1, 3, 4, 5, 0] 32 | number[0] # 取出第 0 个元素, 其值为 1 33 | ``` 34 | 35 | 另外一种索引用法, 是获取一个列表的**子列表**, 叫做**切片(slice)**索引, 语法如下: 36 | ``` 37 | list_val[start:stop:step] 38 | ``` 39 | 其中 `start` 表示切片的索引起始位置, 如果是 0, 就可以省略; `stop` 是表示切片的索引结束位置, 如果是一直结尾, 也可以省略不写; `step` 表示切片的步长, 如果是 1, 也可以省略; 而且它们都可以是负值, 表示**倒着数**, 40 | 41 | 注意在 Python 中整数索引是从 0 开始计数的, 索引放在方括号 `[]` 中, 而 Matlab 的索引是从 1 开始的, 且索引值放在圆括号 `()` 中. 42 | 43 | ``` 44 | number[1:3] # 取出第 1 个到第 3 个元素之前的元素, 不包括第 3 个元素 45 | number[1:-1] # 取出第 1 个元素到倒数第 1 个之前的元素, 不包括倒数第 1 个元素 46 | number[0::2] # 从第 0 个元素开始到最后一个元素, 每隔 2 个元素取一个 47 | number[-1::-1] # 从倒数第 1 个元素一直取到第 0 个元素, 相当于把列表反过来 48 | ``` 49 | 50 | 下面给出一些常用的其它操作: 51 | 52 | ``` 53 | len(number) # 返回列表的长度 54 | number.append(10) # 在列表末尾加上一个元素 10 55 | number.insert(2, 11) # 在第 2 个位置之前插入整数 11, 之后 11 就变成了第 2 个数 56 | number + [7, 8, 9] # 把两个列表拼成一个列表 57 | ``` 58 | 59 | ## 总结 60 | 61 | 这里最关键的是灵活理解和运用**切片**索引, 因为后面我们用 Numpy 中多维数组编程时, 会大量用到**切片**操作, 这是向量化编程的一项基本功. 62 | 63 | 64 | -------------------------------------------------------------------------------- /figures/numpy-mesh-edge.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from fealpy.mesh import TriangleMesh 4 | import matplotlib.pyplot as plt 5 | # 网格节点数组 6 | node = np.array([[0.0, 0.0], 7 | [1.0, 0.0], 8 | [1.0, 1.0], 9 | [0.0, 1.0]], dtype=np.float) 10 | # 网格单元数组 11 | cell = np.array([[1, 2, 0], 12 | [3, 0, 2]], dtype=np.int32) 13 | NN = node.shape[0] # 网格中的节点个数 14 | NC = cell.shape[0] # 网格中的单元个数 15 | 16 | localEdge = np.array([[1, 2], 17 | [2, 0], 18 | [0, 1]], dtype=np.int32) 19 | 20 | totalEdge = cell[:, localEdge] # shape 为 (NC, 3, 2) 21 | totalEdge = totalEdge.reshape(-1, 2) # shape 变为 (3*NC, 2) 22 | print('totalEdge:\n', totalEdge) 23 | 24 | totalEdge0 = np.sort(totalEdge, axis=1) 25 | print('totalEdge0:\n', totalEdge0) 26 | 27 | edge0, i0, j = np.unique(totalEdge0, return_index=True, return_inverse=True, axis=0) 28 | print('j:\n', j) 29 | 30 | cell2edge = j.reshape(NC, 3) 31 | print('cell2edge:\n', cell2edge) 32 | 33 | edge = totalEdge[i0] # 最终的边数组 34 | print('edge:\n', edge) 35 | 36 | E = 3 # 每个三角形有 3 条边 37 | NE = edge.shape[0] # 获得网格中边的个数, 即 `edge` 的行数 38 | i1 = np.zeros(NE, dtype=np.int32) # 分配空间 39 | i1[j] = range(3*NC) # totalEdge0 的行数是 3*NC, j 的长度也是 3*NC 40 | 41 | print('i0:\n', i0) 42 | print('i1:\n', i1) 43 | 44 | edge2cell = np.zeros((NE, 4), dtype=np.int32) 45 | edge2cell[:, 0] = i0//E # 得到每条边的左边单元 46 | edge2cell[:, 1] = i1//E # 得到每条边的右边单元 47 | edge2cell[:, 2] = i0%E # 得到每条边的在左边单元中的局部编号 48 | edge2cell[:, 3] = i1%E # 得到每条边在其右边单元中的局部编号 49 | 50 | print('edge2cell:\n', edge2cell) 51 | 52 | 53 | 54 | mesh = TriangleMesh(node, cell) 55 | mesh.print() 56 | fig = plt.figure() 57 | axes = fig.gca() 58 | mesh.add_plot(axes) 59 | mesh.find_node(axes, showindex=True) 60 | mesh.find_cell(axes, showindex=True) 61 | mesh.find_edge(axes, showindex=True) 62 | plt.savefig('numpy-mesh-edge.png') 63 | plt.show() 64 | -------------------------------------------------------------------------------- /numpy-mesh-node-cell.md: -------------------------------------------------------------------------------- 1 | # 网格数据结构的数组化表示:node 和 cell 2 | 3 | 本文将介绍如何用 Numpy 中的多维数组对象 `ndarray` 来表示存储一个三角形网格。 4 | 5 | 首先, 表示一个三角形网格需要两个基本数组 6 | 7 | * `node`: 网格节点坐标数组, 形状为 `(NN, 2)`, 即一个 `NN` 行 `2` 列的二维矩阵。 `node[i, j]` 存储的是第 `i` 个节点的第 `j` 个坐标分量, 其中 `0<= i < NN`, `0<= j < 2`。 8 | * `cell`: 网格单元顶点编号数组, 形状为 `(NC, 3)`, 即一个 `NC` 行 `2` 列的二维矩阵。 `cell[i, j]` 存储的是第 `i` 个单元的第 `j` 个顶点编号(即 `node` 的行号), 其中 `0<= i < NC`, `0 <= j < 3`。注意, **这里约定三个顶点编号在 `cell[i, :]` 中存储顺序保证对应三个顶点在二维空间中是按逆时针排列的**, 这样可以保证以后计算三角形面积时一定为正的。 9 | 10 | 下面给出一个 [0,1]^2 区域的三角形网格(见下图), 其中有 4 个顶点、 2 个单元 11 | 12 | ![](./figures/twotri.png) 13 | 14 | 下面代码给出用 `numpy` 数组表示上面网格的示例, 15 | 16 | ``` 17 | import numpy as np 18 | node = np.array([[0.0, 0.0], 19 | [1.0, 0.0], 20 | [1.0, 1.0], 21 | [0.0, 1.0]], dtype=np.float) 22 | cell = np.array([[1, 2, 0], 23 | [3, 0, 2]], dtype=np.int32) 24 | ``` 25 | 26 | 代码第一行导入 `numpy` 模块, 并且用 `as` 语法重新把模块名字简写为 `np`。 这是一个**约定俗成的习惯**, 你平时最好也这样写, 方便大家阅读你的代码。 27 | 28 | 接着用数组创建函数 `np.array` 创建两个数组: `node` 和 `cell`, 存储的数据类型分别指定为 `np.float`(64 位浮点型) 和 `np.int32` (32 位整型). 29 | 30 | 在 IPython 中用运行上述代码后, 用 `type` 函数可以检查 `node` 和 `cell` 的数据类型, 都为 `numpy.ndarray` 多维数组对象。 31 | 32 | ``` 33 | In [10]: type(node) 34 | Out[10]: numpy.ndarray 35 | 36 | In [11]: type(cell) 37 | Out[11]: numpy.ndarray 38 | ``` 39 | 40 | `ndarray` 对象有很多属性, 常用的有 `shape` 属性, 它是一个 `tuple` 对象, 存储 41 | 了 `ndarray` 对象的形状。 `shape[i]` 就存储的是 `ndarray` 对象的第 `i` 轴 42 | (axis) 长度。 另外, `ndarray` 还有另一个属性 `ndim`, 存储了多维数组的维数。 43 | 44 | ``` 45 | In [12]: print(node.ndim, node.shape) 46 | 2 (4, 2) # node 是 2 维数组, 第 0 轴长度为 4 , 第 1 轴长度为 2 47 | 48 | In [13]: print(cell.shape) 49 | 2 (2, 3) # cell 是 2 维数组, 第 0 轴长度为 2, 第 1 轴长度为 3 50 | ``` 51 | 52 | 下面上一个 `ndarray` 的图片(来自[1] ), 让大家直观感受一下多维数组中**轴**的概念。 53 | 54 | ![](./figures/ndarray.png) 55 | 56 | 这次就到这里, 下次介绍如何从 `node` 和 `cell` 数组出发, 用向量化计算的方法得到关于网格的更多数据。 57 | 58 | 最后给一个习题, 请用 `node` 和 `cell` 两个数组表示下面的四边形网格, 区域为 $$[0, 1]^2$$, 59 | 60 | ![](./figures/fourquad.png) 61 | 62 | 63 | [1] https://www.oreilly.com/library/view/elegant-scipy/9781491922927/assets/elsp_0105.png 64 | -------------------------------------------------------------------------------- /python.md: -------------------------------------------------------------------------------- 1 | # Python 基本算术运算和变量 2 | 3 | --- 4 | 5 | 今天开始介绍 Python 中的基本语法和相关概念. 6 | 7 | ## 常用算术运算符 8 | 9 | **算术运算**是每种编程语言的必备, Python 中常用的算术运算符有: 10 | 11 | * 加法 `+`: `6 + 5` 12 | * 减法 `-`: `8 - 4` 13 | * 乘法 `*`: `5 * 4` 14 | * 浮点除法 `/`: `6 / 3` 15 | * 幂次 `**`: `2**3` 16 | * 求商`//`: `3//2` 17 | * 求余 `%`: `6%3` 18 | * 圆括号 `()`: `(5 + 3)/4` 19 | 20 | 注意不同编程语言的基本算术运算符存在差异. 如 Matlab 中 `%` 是用来做注释的, 幂次运算符是 `^`, 且没有单独的求商与求余的运算符, 这导致了很多 Matlab 用户刚切换到 Python 时, 可能很不习惯. 另外 Matlab 还针对向量运算定义了点乘 `.*`, 点除 `./`和 点幂次 `.^`, 这其实是编程体验很不好语法设计, 经常忘记加点的同学请举手! 21 | 22 | ## 变量 23 | 24 | Python 中有三种最基本的变量: 25 | 26 | ```python 27 | integer_val = 5 # 整型 28 | float_val = 5.0 # 浮点型 29 | string_val = "5.0" # 字符串型 30 | ``` 31 | 注意在 Python 中 `#` 是用来标记注释的. 32 | 33 | 前面讲过, Python 中一切皆是对象, 都有它自己的属性和方法. 在 `ipython` 中定义整型变量 `integer_val`, 变量后面加`.`, 然后按下 `Tab` 键补全, 你会看到它里面还有很多东西, 而且实际上比图片中所显示的还要多. 34 | 35 | ![](/figures/int.png) 36 | 37 | 所以整型变量在 Python 中不仅仅代表一个整型的值(这里值是 5), 举个例子, 其中: 38 | 39 | ``` 40 | integer_val.real # 整型的实部, 等于 5 41 | integer_val.imag # 整型的虚部, 等于 0 42 | ``` 43 | 对浮点型和字符串型变量也是同样. 这样的变量设计当然在内存中会占用更多的空间, 而且降低程序运行效率. 但这带来了**动态类型**的好处, 即不用显式声明一个变量的类型, 也可以随意更换变量的类型, 如: 44 | 45 | ``` 46 | In [18]: a = 5.0 47 | In [19]: type(a) # type 函数用来查看变量的类型 48 | Out[19]: float 49 | In [20]: a = 'string' 50 | In [21]: type(a) 51 | Out[21]: str 52 | ``` 53 | 54 | Matlab 也是动态类型的语言; 而 C/C++ 就是**静态类型**的语言, 你要显式定义一个变量, 并指明类型, 你还不能随便把不同类型的值赋值给其它类型的变量, 如: 55 | 56 | ``` 57 | int a = 5; // 定义一个整型变量, 值为 5 58 | a = "5.0"; // 错误 59 | ``` 60 | 61 | 另外, Python 中没有变量指针的概念(被指针虐过的同学请举手). 其实, Python 中每个变量都有一个**identity**属性, 它是一块内存的唯一标识, 也相当于指针啦, 可以用 `id()` 函数来查看变量的 identity 值. 下面定义一个整型的变量 `a`, 然后把它赋值给另一个变量 `b`, 用 `id()` 函数来检查 `a` 和 `b` 的标签值, 你会发现它们有一样的 identity 值: 62 | 63 | 64 | ``` 65 | In [40]: a = 5 66 | 67 | In [41]: b = a 68 | 69 | In [42]: id(a) 70 | Out[42]: 10919552 71 | 72 | In [43]: id(b) 73 | Out[43]: 10919552 74 | ``` 75 | 也就是说此时 `a` 和 `b` 其实是指向同一块内存的. 语句 `b = a` 并没有真正为 `b` 重新开辟新的内存. 76 | 77 | ## `print` 78 | 79 | Python 中用于打印显示的函数是 `print`. 在编程过程中, 把有用的信息打印出来, 是测试和调试程序的最基本方法. 下面给出一个例子: 80 | 81 | ``` 82 | In [1]: integer_val = 5 83 | 84 | In [2]: print(integer_val) # 打印变量的值 85 | 5 86 | 87 | In [3]: print(type(integer_val)) # 打印变量的类型 88 | 89 | ``` 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /matlab-engine-for-python.md: -------------------------------------------------------------------------------- 1 | # Matlab Engine for Python 2 | 3 | 因为种种原因, 有时候需要在 Python 和 Matlab 之间传递数据。 这时可以用 `scipy` 中的 `io` 模块, 具体用法如下: 4 | 5 | ``` 6 | import scipy.io as sio 7 | # 1. 读入 8 | # 假设 mesh.mat 是 Matlab 保存的网格数据, 包括两个变量 `node` 和 `elem` 9 | data = sio.loadmat(‘mesh.mat’) # 读入 mat 文件 10 | node = data['node'] 11 | cell = data['elem'] - 1 # Matlab 中编号是从 1 开始的, 读为 numpy 数组要减 1 12 | 13 | # 2. 写出 14 | data = {'node':node, 'elem': cell+1} # 生成一个 dict 数据 data 15 | sio.savemat('python-mesh.mat', data) # 把 data 写入 mat 文件 16 | ``` 17 | 18 | 但这种用法违背了**尽可能在一个环境下完成任务**的原则, 用 Python 保存完数据你还要打开 Matlab, 再读取数据处理, 这样的工作效率显然不是最高的。 做事情一定要有效率意识, 换个说法就是**偷懒**意识, 这是我们人类科技不断进步的主要动力, 也是一个人学习的最大动力来源。 当然在**学习上**我们不能**偷懒**, 因为学习的目的就是为了**更好地偷懒**。 19 | 20 | 但有时候还是需要用 Matlab 做点事情(毕竟大家习惯用了), 比如发现 matplotlib 有时画出的图不满意, 但你的论文又急需一张更满意的图时。 在这种情形下, 你首先要问自己: 能否省点事不离开 Python 环境, 又能用到 Matlab? 有了这样的问题, 你就可以行动了, 比如 google 和问别人。 21 | 22 | Matlab 提供了一个 Python 编程接口 Matlab Engine for Python。 在 Matlab 的根目录下, 进入 `extern/engines/python`, 执行一个简单的安装命令即可, 示例代码如下: 23 | 24 | ``` 25 | cd /opt/matlab/R2018a/extern/engines/python/ 26 | sudo python3 setup.py install 27 | ``` 28 | 29 | 下面给出一些简单的用法, 其它情形请自己脑补和 google: 30 | 31 | ``` 32 | import numpy as np 33 | import matlab.engine 34 | eng = matlab.engine.start_matlab() # 启动一个 Matlab 的 session 35 | 36 | a = np.random.rand(10) # 生成一个长度为 10 的随机矩阵, 元素在 0 和 1 之间 37 | A = matlab.double(a.tolist()) # 转化为 matlab 的矩阵 38 | eng.sqrt(A) # 计算矩阵的开方 39 | eng.plot(A) # 画图 40 | eng.max(A, nargout=1) # 返回 A 最大值 41 | eng.max(A, nargout=2) # 返回 A 的最大值及其位置 42 | ``` 43 | 画出的图如下: 44 | 45 | ![](./figures/mplot.png) 46 | 47 | 当然, 如果做了足够的搜索,你会发现 Matlab Engine for Python 不是唯一的选择, 你的选择还有 [Transplant](https://github.com/bastibe/transplant), 接口做的比 Matlab Engine for Python 更简洁一点, 可以直接用 numpy 数组做参数。 安装很简单: 48 | 49 | ``` 50 | sudo -H pip3 install transplant 51 | ``` 52 | 53 | 下面给出一个两种接口的代码比较: 54 | 55 | ``` 56 | Matlab Engine for Python | Transplant 57 | ---------------------------------------|--------------------------------------- 58 | import numpy as np | import numpy as np 59 | import matlab | import transplant 60 | import matlab.engine | 61 | | 62 | eng = matlab.engine.start_matlab() | eng = transplant.Matlab() 63 | numpy_data = np.random.randn(100) | numpy_data = np.random.randn(100) 64 | list_data = numpy_data.tolist() | 65 | matlab_data = matlab.double(list_data) | 66 | data_sum = eng.sum(matlab_data) | data_sum = eng.sum(numpy_data) 67 | ``` 68 | 69 | 更多内容请参见 https://bastibe.de/2015-11-03-matlab-engine-performance.html。 70 | -------------------------------------------------------------------------------- /xi-shu-ju-zhen.md: -------------------------------------------------------------------------------- 1 | # 稀疏矩阵 2 | 3 | 给定一个矩阵 A, 如果其非零元个数比较少, 我们就称其为**稀疏矩阵**。 稀疏矩阵是数值计算中经常用到的矩阵类型, 如有限元、有限差分、有限体积等常用的偏微分方程数值求解方法,最后得到的都是稀疏矩阵。 因此, 学习数值计算很有必要了解这种矩阵在计算机中的存储格式。 4 | 5 | 在计算机中存储稀疏矩阵时, 如果把每个元素都存下来, 是很浪费存储空间的。 比如一个 10 万阶的方阵, 包含 10 亿个元素, 但假设其中只有 20 万个非零元。 在计算机中按双精度存储的话, 每个矩阵元素要占 8 个字节。 如果把所有元素都存储下来, 大概需要 74.5 GB 的内存空间, 其计算公式如下: 6 | 7 | 8 | $$ 9 | \frac{\text{10 万阶矩阵的字节数}}{\text{1 GB 的字节数}} = \frac{10^5\times 10^5\times 8}{2^{10}\times 2^{10}\times 2^{10}} \approx 74.5 (GB), 10 | $$ 11 | 12 | 13 | 对一般的中小规模计算机, 在内存中直接存储 10 万阶的矩阵是不可能的事情。 唯一的办法就是利用稀疏矩阵大部分元素为 0 的这个特点。 因此, 我们需要为稀疏矩阵设计不同的存储格式。 常用的稀疏矩阵存储格式有: 14 | 15 | * Coordinate format Matrix \(COO\): 坐标格式稀疏矩阵, 用相同长度的三个一维数组 16 | `data`, `I` 和 `J` 分别存**非零元**及其对应的**行列指标**, 三个数组长度都为矩阵中非零元的个数。 17 | * Compressed Sparse Row Matrix \(CSR\): 压缩稀疏行存储矩阵, 用两个相同长度的一维数组 `data` 和 `indices` 分别存储**非零元**和其对应的**列指标**, 这两个数组的长度为非零元的个数; 用另外一个数组 `indptr` 存储**每一行**的非零元和其列指标在 `data` 和 `indices` 中的**起始**和**终止**位置, 长度要比**矩阵行数**多 1。 18 | * Compressed Sparse Column Matrix \(CSC\):压缩稀疏列存储矩阵,用两个相同长度的一维数组 `data` 和 `indices` 分别存储**非零元**和其对应的**行指标**, 这两个数组的长度为非零元的个数; 用另外一个数组 `indptr` 存储**每一列**的非零元和其列指标在 `data` 和 `indices` 中的**起始**和**终止位置**, 长度要比**矩阵列数**多 1。 19 | 20 | ## 稀疏矩阵示例 21 | 22 | 下面以一个具体的矩阵为例, 对上面三种格式进行说明: 23 | 24 | $$ 25 | A = \begin{pmatrix} 26 | 0 & 0 & 0 & 10 \\ 27 | 21 & 0 & 33 & 0 \\ 28 | 0 & 0 & 3 & 0 \\ 29 | 12 & 1 & 0 & 4 30 | \end{pmatrix} 31 | $$ 32 | 33 | **注意**下面我们采用 C\C++ 和 Python 的习惯, 矩阵行列编号都从 **0** 开始. 首先是 COO 格式, 可以**行优先存储**: 34 | 35 | $$ 36 | \begin{aligned} 37 | data &= [10, 21,33,3,12,1,4], \\ 38 | I &= [0, 1, 1, 2, 3, 3, 3], \\ 39 | J &= [3, 0, 2, 2, 0, 1, 3]. 40 | \end{aligned} 41 | $$ 42 | 43 | 也可以**列优先存储**: 44 | 45 | $$ 46 | \begin{aligned} 47 | data &=[21, 12, 1, 33, 3, 10, 4], \\ 48 | I &=[1, 3, 3, 1, 2, 0, 3], \\ 49 | J &=[0, 0, 1, 2, 2,, 3, 3]. 50 | \end{aligned} 51 | $$ 52 | 53 | 可以看到上面的两种格式, 同一行或同一列的指标存储有点冗余, 这里仍有改进空间。 54 | 55 | 比如, CSR 用下面三个数组来表示矩阵 A: 56 | 57 | $$ 58 | \begin{aligned} 59 | data &= [10,21,33,3,12,1,4], \\ 60 | indices &= [3,0,2,2,0,1,3], \\ 61 | indptr &= [0,1,3,4,7]. 62 | \end{aligned} 63 | $$ 64 | 65 | 其中, `data[indptr[i]:indptr[i+1]]` 是第 i 行所有非零元, `indices[indptr[i]:indptr[i+1]]` 是第 i 行所有非零元的列指标。 注意, 这里用了 Python 中的**切片**语法 `start:stop:step`。 66 | 67 | CSC 可以用下面三个数组来表示矩阵 A: 68 | 69 | $$ 70 | \begin{aligned} 71 | data &= [21,12,1,33,3,10,4], \\ 72 | indices &= [1,3,3,1,2,0,3], \\ 73 | indptr &= [0,2,3,5,7]. 74 | \end{aligned} 75 | $$ 76 | 77 | 其中, `data[indptr[i]:indptr[i+1]]` 是**第 i 列**所有非零元, `indices[indptr[i]:indptr[i+1]]` 是**第 i 列**所有非零元的**行指标**。 78 | 79 | 还以上面的 10 万阶方矩阵为例, 假设有 20 万个非零元, 用双精度存储, 指标数组用 32 位整型存储, 在 CSR 模式下, 它占用内存为\(以 MB 为单位\): 80 | 81 | $$ 82 | \frac{2 \times 10^5 \times 8 + 10^5 \times 4 + (10^5+1) \times 4}{2^{10}\times 2^{10}} \approx 2.29 (MB). 83 | $$ 84 | 85 | 哈哈, 内存节省了大概 33333 倍。 86 | 87 | 上面只是讲了稀疏矩阵的存储格式, 下次再写点稀疏矩阵的运算。 88 | 89 | -------------------------------------------------------------------------------- /fealpy.md: -------------------------------------------------------------------------------- 1 | # FEALPy: Finite Element Analysis Library in Python 2 | 3 | 在学习陈龙老师 IFEM 软件包基础上, 我开发了一个 Python 有限元软件包 FEALPy。 4 | FEALPy 借助 Python 中的多维数组模块 Numpy, 完全继承了 IFEM 面向数组的编程模式 5 | , 同样可以用简短的代码写出比较高效的数值计算程序。 与 IFEM 不同的是, FEALPy 6 | 采用了面向对象的编程模式来组织程序,与面向过程的编程模式相比, 大大提高了程序的 7 | 重用性和可扩展性。 8 | 9 | FEALPy 开源托管在 Github 上,主页为: github.com/weihuayi/fealpy, 欢迎大家试用 10 | 。目前 FEALPy 核心 框架已经搭建完成, 主要实现了下面的模块 11 | 12 | * 丰富的网格数据结构类型, 包括二维、三维的常见网格类型。 13 | * 简单区域上的网格生成。 14 | * 任意维的高次拉格朗日有限元空间。 15 | * 高次曲面有限元空间。 16 | * 多边形上的高次虚单元空间。 17 | * 自适应算法,包括三角形网格的二分法, 四叉树和八叉树。 18 | 19 | 20 | 今天,我将介绍如何在 Ubuntu 下安装 FEALPy, 后续的文章中我也会介绍在 Windows 和 21 | Mac 系统下的安装配置。 22 | 23 | 在 FEALPy 的主页上, 点击 `clone or download` 按钮, 在弹出的小页面中点击 24 | `Download Zip` 直接下载得到压缩文件 `fealpy-master.zip`, 然后 25 | 26 | ``` 27 | $ unzip fealpy-master.zip # 解压得到 fealpy-master 文件夹 28 | $ cd fealpy-master/ # 进行 fealpy 主目录 29 | ``` 30 | 31 | 或者在 Ubuntu 命令行终端中直接用 git 克隆一份: 32 | 33 | ``` 34 | $ git clone https://github.com/weihuayi/fealpy.git 35 | $ cd fealpy/ # 进入 fealpy 主目录 36 | ``` 37 | 38 | 39 | 下面首先安装 Python 环境 40 | 41 | ``` 42 | $ sudo apt install python3 # python 3.x 解释器 43 | $ sudo apt install python3-pip # python 软件包管理器 44 | $ sudo apt install python3-tk # matplotlib 需要的后 GUI 程序 45 | $ sudo apt install ipython3 # python 3.x 的高级交互环境 46 | ``` 47 | 48 | 当然, 也可以直接安装 Anaconda 集成开发环境。但我建议还是自己来配置,这样可以帮 49 | 助你更好理解编程环境运行机制。 50 | 51 | 在 FEALPy 的主目录里运行下面安装命令, 就可以把 FEALPy 安装到 Ubuntu 系统中: 52 | 53 | ``` 54 | $ sudo pip3 install -e ./ 55 | ``` 56 | 57 | 注意这里的 `-e` 表示**软安装**, 即安装一个软链接到系统目录下, Ubuntu 58 | 默认的安装目录是 `/usr/local/lib/python3.6/dist-packages`。 59 | 这样安装的好处是当更新 FEALPy 后, 你马上可以在系统的其它位置调用最新的 60 | FEALPy。特别是用 `git clone` 命令克隆的版本,在 `fealpy` 目录下, 运行命令: 61 | 62 | ``` 63 | $ git pull 64 | ``` 65 | 66 | 就可以更新 FEALPy 到最新版本。 67 | 68 | 接着就可以进行测试了, 把下面代码拷贝到一个文本文件中,命名为 `test_trianglemesh.py`: 69 | 70 | ``` 71 | import numpy as np 72 | import matplotlib.pyplot as plt 73 | from fealpy.mesh import TriangleMesh 74 | 75 | # [0, 1]^2 区域上的网格 76 | node = np.array([ 77 | (0, 0), 78 | (1, 0), 79 | (1, 1), 80 | (0, 1)], dtype=np.float) 81 | cell = np.array([ 82 | (1, 2, 0), 83 | (3, 0, 2)], dtype=np.int) 84 | 85 | tmesh = TriangleMesh(node, cell) # 建立三角形网格对象 86 | tmesh.uniform_refine(2) # 一致加密两次 87 | node = tmesh.entity('node') # 获得节点数组 88 | cell = tmesh.entity('cell') # 获得单元数组 89 | 90 | # 画图 91 | fig = plt.figure() # 建立画图对象 92 | axes = fig.gca() # 获得坐标系 93 | tmesh.add_plot(axes) # 在坐标系中画网格 94 | tmesh.find_node(axes, showindex=True) # 显示所有节点编号 95 | tmesh.find_edge(axes, showindex=True) # 显示所有边的编号 96 | tmesh.find_cell(axes, showindex=True) # 显示所有单元的编号 97 | plt.show() 98 | ``` 99 | 100 | 最后在命令行中运行 101 | 102 | ``` 103 | $ python3 test_trianglemesh.py 104 | ``` 105 | 106 | 就可以显示下面的图形 107 | 108 | ![](./figures/trianglemesh.png) 109 | 110 | 在后面的文章中,我会对 FEALPy 中已有的各个模块进行详细的介绍。 我真心希望有更多 111 | 的人来使用 FEALPy, 这样它才能变的更好,也会对大家更有用。 112 | -------------------------------------------------------------------------------- /fealpy-win.md: -------------------------------------------------------------------------------- 1 | # Windows 系统下 FEALPy 的安装 2 | 3 | 首先下载安装 Visual Studio IDE, https://visualstudio.microsoft.com 。 在安装时 4 | ,只要选择安装 C++ 模块就可以了。这里主要是为了安装编译器,在安装一些 Python 软 5 | 件时用到。很多 Python 软件包核心代码是 C\C++ 写的,安装时需要首先编译出库文件才 6 | 能安装。 7 | 8 | 9 | 然后下载最新版的 Anaconda3, 下载地址为: https://repo.continuum.io/archive/Anaconda3-2018.12-Windows-x86_64.exe , 10 | 默认安装即可。关于 Anaconda3 的介绍,请见 11 | 12 | * [Python 环境搭建](./install_python.md) 13 | 14 | 因为在 Windows 系统下, FEALPy 用到的 meshpy 和 pyamg 默认安装有问题,所以只能 15 | 采用手工方式安装。美国加州大学欧文分校生物医学工程系的 Christoph Gohlke 提供了 16 | 一个非官方的 Python 软件包下载服务, 网址为 https://www.lfd.uci.edu/~gohlke/pythonlibs/ 。 17 | 上面一般都有最新版本的软件包下载, meshpy 和 pyamg 的下载链接为: 18 | 19 | * https://www.lfd.uci.edu/~gohlke/pythonlibs/#meshpy 20 | * https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyamg 21 | 22 | 这里需要下载最新的适合 Python 3.7 的版本。下载之后,在 Windows 桌面搜索框输入 23 | `Anaconda Prompt`, 回车即可以打开一个命令行, 显示如下内容: 24 | 25 | ``` 26 | (base) C:\Users\username> 27 | ``` 28 | 29 | 注意上面 `username` 实际上会显示为你自己的用户名。 30 | 31 | 比如下载的目录为: `D:\dowloads`, 依次输入以下命令, 并回车: 32 | 33 | ``` 34 | (base) C:\Users\username>D: 35 | (base) D:\>cd dowloads 36 | (base) D:\downloads> pip install MeshPy‑2018.2.1‑cp37‑cp37m‑win_amd64.whl # For 64 bit win system with python 3.7 37 | (base) D:\downloads> pip install pyamg‑4.0.0‑cp37‑cp37m‑win_amd64.whl # For 64 bit win system with python 3.7 38 | ``` 39 | 40 | 注意上面命令中的 `install` 后的文件名,都是适用于 64 位 Windows 系统,对应的 Python 版本是 3.7。 41 | 。 42 | 43 | 最后在 FEALPy 的 `https://github.com/weihuayi/fealpy` 主页上, 点击 `clone or 44 | download` 按钮, 在弹出的小页面中点击 `Download Zip` 直接下载得到压缩文件 45 | `fealpy-master.zip`, 然后解压。 比如解压到目录 `D:\fealpy-master` 下面, 则在命 46 | 令行中首先切换到该目录下,然后安装 fealpy 47 | 48 | ``` 49 | (base) D:\downloads> cd ../fealpy-master 50 | (base) D:\fealpy-master> pip install -e ./ 51 | ``` 52 | 53 | 如果没有报什么错误,就说明安装成功了。下面就可以进行测试了, 比如可以进入 54 | `example` 目录,运行命令 `python PoissonFEMRate.py 2 1 3 4` 进行测试, 这里的 `2 55 | 1 3 4` 分别代表 2 维问题, 用 1 次元求解, 初始网格首先加密 3 次, 再迭代加密网 56 | 格 4 次并求解。 具体测试结果如下 57 | 58 | 59 | ``` 60 | (base) D:\fealpy-master> cd example 61 | (base) D:\fealpy-master\example>python PoissonFEMRate.py 2 1 3 4 62 | Can not import spsolve from mumps!Using spsolve in scipy! 63 | Can not import spsolve from mumps!Using spsolve in scipy! 64 | Construct linear system time: 0.01743110000000314 65 | Solve time: 0.006151400000000251 66 | Construct linear system time: 0.006938899999997972 67 | Solve time: 0.0007587999999998374 68 | Construct linear system time: 0.042748799999998255 69 | Solve time: 0.004435099999998471 70 | Construct linear system time: 0.08573089999999794 71 | Solve time: 0.02161789999999897 72 | \begin{table}[!htdp] 73 | \begin{tabular}[c]{|c|c|c|c|c|}\\\hline 74 | Dof & 81 & 289 & 1089 & 4225 75 | $|| u - u_h||_0$ & 1.9408e-02 & 4.9543e-03 & 1.2452e-03 & 3.1173e-04 76 | Order & -- & 1.97 & 1.99 & 2. 77 | $||\nabla u - \nabla u_h||_0$ & 4.3180e-01 & 2.1754e-01 & 1.0898e-01 & 5.4514e-02 78 | Order & -- & 0.99 & 1. & 1. 79 | \end{tabular} 80 | \end{table} 81 | ``` 82 | 83 | 注意,上面的测试中,报出信息 `Can not import spsolve from mumps!Using spsolve in 84 | scipy!`, 是因为上面的安装默认没有安装并行直接解法器 pymumps (它是 mpi 版本的, 目前 85 | fealpy 还不能充分发挥它的潜力), 程序就转为调用 scipy 中的的解法器。`scipy` 中的解法器 86 | 求解 100 万规模左右的问题还可以,但更大的时间上就有点受不了啦。这里也没有默 87 | 认用 `pyamg`, 因为高次元还是解不了, 不过线性元还是比较高效的。 88 | 89 | 总的来说, fealpy 的解法器还有待加强。我计划自己开发一个 mumps 多线程版本的 90 | python 接口,以便能充分利用我的 48 核工作站。当然也非常欢迎你去尝试把 pymumps 在 91 | Windows下的安装搞定, 如果搞定请把安装笔记发给我。 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /install_python.md: -------------------------------------------------------------------------------- 1 | # Python 编程环境的安装 2 | 3 | 编程是一项实践性很强的活动。 经常写是学好编程的最基本保证。 在学习 Python 编程之前, 4 | 你首先要给自己搭建一个环境, 让自己随时都能写上一点程序。 5 | 6 | 下面主要介绍几种 Python 编程环境的搭建过程。 7 | 8 | ## [Anaconda](https://www.anaconda.com/) 9 | 10 | 首先介绍最简单的一种方式, 就是安装 Anaconda, 这是当前最流行的跨平台 Python 数 11 | 据科学平台软件包, 下载地址: https://www.anaconda.com/download/ , 其中有 12 | Windows、macOS 和 Linux 三种系统的 安装包, 最新版本是 5.2, 内置的 Python 版本 13 | 是 3.6。 Anaconda 中已经集成了大部 分我们平时最常用的 Python 软件模块。 另外, 14 | 也提供了很多第三方 的网上软件模块仓 库, 可以利用 `conda` 命令来安装你需要的软 15 | 件包。 16 | 17 | Windows 安装 Anaconda 之后, 就会在开始菜单中出现一个 Anaconda 文件夹, 下面有 18 | 各种工具的启动链接: 19 | 20 | * **Anaconda Cloud**: 21 | Anaconda 的云平台,就是一个网上编程环境, 它提供软件包、 笔记和环境的管理服务, 22 | 你可以和别人共享自己的程序和笔记。用 Cloud 的好处是, 它会帮你你完成软件包和环 23 | 境的更新管理任务, 你只用专注编程就可以啦。 24 | * **Anaconda Navigator**: 就是一个导览啦, 告诉你 Anaconda 中有什么。 25 | * **Anaconda Prompt**: 就是 Anaconda 的命令终端, 用 `conda` 命令管理本地 Python 软件包的安装和卸载。 26 | * **IPython**: Python 的高级交互终端, 可以在里面快速获得帮助,测试代码的运行效果等。 27 | * **Jupyter Notebook**: 一个交互计算的 Web 应用, 启动后会在浏览器中打开, 你可 28 | 以写包含代码的文档笔记, 注意其中的代码是可以直接运行的哦。 29 | * **Jupyter QTConsole**: 和 IPython 其实是一个东西, 只不过用 QT 实现的界面。 30 | * **spyder**: Python 的集成开发环境, 有与 Matlab 类似的界面。 31 | 32 | 而 Linux 和 MacOS 系统中安装 Anaconda 之后, 在你的主目录中就会出现一个 Anaconda 的文件夹, 33 | 并且会自动屏蔽系统中已经安装的 Python。 你需要打开你的命令行终端来运行上面的命 34 | 令。 35 | 36 | ## Ubuntu 16.04 系统 37 | 38 | 我在这一节首先介绍如何手工配置 Linux 系统下的 Python 编程环境, 然后介绍怎么调 39 | 用相关的程序。 Linux 下安装 Anaconda 后, 也是同样的方式调用的。 40 | 41 | 首先按下 `Ctrl + Alt + T` 快捷键打开命令行终端, 类似下面的界面: 42 | ![命令行终端](./figures/shell.png) 43 | 44 | 45 | 下面安装 Python 语言基础软件包 `python3` 和 Python 软件模块管理器 `pip3`。 46 | 47 | ``` 48 | $ sudo apt install python3 # 语言基础软件包 49 | $ sudo apt install python3-pip # python 软件模块管理器 50 | ``` 51 | 注意这里安装的是 Python 3。 52 | 53 | 安装完成, 可以打印版本信息测试一下: 54 | 55 | ``` 56 | $ python3 -V 57 | Python 3.5.2 58 | $ pip3 -V 59 | pip 18.0 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5) 60 | ``` 61 | 62 | 进一步安装 Python 的高级交互终端 IPython: 63 | 64 | ``` 65 | $ sudo apt install ipython3 66 | ``` 67 | 68 | 在命令行终端中运行 `ipython3`: 69 | 70 | ``` 71 | $ ipython3 72 | Python 3.5.2 (default, Nov 23 2017, 16:37:01) 73 | Type "copyright", "credits" or "license" for more information. 74 | 75 | IPython 5.1.0 -- An enhanced Interactive Python. 76 | ? -> Introduction and overview of IPython's features. 77 | %quickref -> Quick reference. 78 | help -> Python's own help system. 79 | object? -> Details about 'object', use 'object??' for extra details. 80 | 81 | In [1]: print("Hello Python World!") 82 | Hello Python World! 83 | 84 | In [2]: 85 | ``` 86 | 87 | IPython 是交互式学习 Python 的好工具。 你可以很方便的在里面获得帮助文档, 和快速 88 | 测试自己的想法。 89 | 90 | 下面安装 Python 最基本的科学计算软件模块: 91 | 92 | ``` 93 | $ sudo -H pip3 install numpy 94 | $ sudo -H pip3 install scipy 95 | $ sudo -H pip3 install matplotlib 96 | $ sudo -H pip3 install sympy 97 | ``` 98 | 99 | 经过以上步骤, 你已经完成 Python 编程环境的配置了。 100 | 下面就是找一款好的编辑器写 Python 程序了, 我推荐使用 101 | [vim](https://www.vim.org/)。 102 | 103 | 104 | 当然, 很多人更喜欢用集成开发环境来写程序, 这里推荐使用[spyder](https://www.spyder-ide.org/): 105 | 106 | ``` 107 | $ sudo apt install spyder 108 | ``` 109 | 110 | 它的界面类似 Matlab, 所以很适合从 Matlab 迁移过来的用户。 111 | 112 | ## 我很懒, 上面都不适合我 113 | 114 | 这没关系, 我推荐你一个数据科学学习网站 Dataquest: https://www.dataquest.io 。 115 | 116 | 你只要注册一个用户, 选择一个学习路径, 就可以开始学习 Python 基础知识了。 这个 117 | 学习网站的好处是, 你只要按照它设计的学习路径学就可以了。 一次只学一个小知 118 | 识点, 知识点的结束是编程练习, 你通过测试就可以学习下面的内容。 一切都在网上 119 | 完成, 你完全不用装什么环境, 而且随时随地都可以学习。 120 | 121 | 当然内容是全英语的。 你可能会说, 我英语不好, 这个推荐还不好。 没关系, 我还有 122 | 一个中文的网站推荐, 就是**数据嗨客平台**: http://hackdata.cn/ , 里面有很多数 123 | 据科学的课程, 包括 Python 的基础课程, 但可能要**收费**才能用哦。 124 | 125 | 唉, 这个世界不总那么完美啊! 126 | 127 | ## 总结说明 128 | 129 | 上面的搭建过程介绍的很简单, 有很多细节还需要你多查一查网络, 130 | 或者在使用过程中再更深入的了解。 131 | 132 | 再强调一点, 在学习使用工具的过程中, 你一定要有效率意识, 133 | 要不时的想一想, 是不是还有更好的操作方法, 并且多查多问, 134 | 你的水平自然就会不断提高, 最终成为一个顶尖高手。 135 | 136 | 祝 Python 能够成为你学习、工作和生活的好朋友。 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 我为什么选择 Python 2 | 3 | 对计算数学专业的科研工作者和学生来说, 编程是一项很重要的工作。 如何快速写出高 4 | 效的数值实验程序, 并且能反复使用, 是大家非常关心的事情。 5 | 6 | 我希望通过一系列的文章, 系统介绍如何基于 Python 中常用的科学计算模块, 利用向 7 | 量化和面向对象的编程技术, 快速编写有限元、有限差分、有限体积、虚单元等常见的数 8 | 值方法程序。 9 | 10 | 我个人对 Matlab、 C++ 和 Python 都比较熟悉, 有很多相关的编程经验,对这几种编程 11 | 语言的优劣有比较丰富的体会。 下面我想谈谈我为什么选择 Python 做为我现在的主要编 12 | 程语言。 13 | 14 | 你可能会说, Python 是大数据时代的热门语言, 我只是在追赶潮流。 确实, 当下机器 15 | 学习和人工智能的主流语言是 Python。 但我向来认为一种东西流行, 只是表象, 而我 16 | 也不会仅仅因为它流行而去用它。 我用它 最根本的原因在于它**真的好用**, 可以极大 17 | 的提高编程效率, 能大量节约用户的时间。 而对每个人来说, 最宝贵的资源就是时间。 18 | 所以, 这里我首先把理由列出来: 19 | 20 | * 支持面向对象编程的解释性语言,无需编译,易学易用。 21 | * 语法设计简洁清爽, 写出的代码可读性强, 可以做到 “Code what we think”。 22 | * 有庞大的用户社群,包括 NASA, ANL, Google 等国际知名的科研机构和公司都选择 Python 23 | 做为高性能计算的开发语言。Python 的初学者和开发者很容易从社群中获得帮助和开发文档。 24 | * 有丰富的科学计算基础软件包, 如: 25 | + NumPy: http://numpy.scipy.org - Numerical Python,主要提供多维数组及相关运算功能。 26 | + SciPy: http://www.scipy.org - Scientific Python,提供高效的优化、FFT、稀疏矩阵等科学计算模块。 27 | + Matplotlib: http://www.matplotlib.org - Graphics library,提供成熟 2D 和 3D 画图软件功能。 28 | + SymPy: http://www.sympy.org - 数学符号计算。 29 | * 还可以做很多其它的事情, 如系统管理, 网站开发。 30 | * 和其它编译语言的接口灵活,功能很容易扩展。 31 | * 有 mpi4py, pycuda 等并行软件包, 使得并行编程更加容易。 32 | * 开源免费 33 | 34 | 35 | 36 | 下面我给出一些解释说明。 首先, Python 是一门解释性语言, 无需编译即可运行。 解 37 | 释性带来的好处是交互性, 即你能即时得到反馈。 你在编程过程中, 可以更频繁的测试 38 | 程序, 马上知道自己有没有犯语法或逻辑错误, 这样就可快速迭代自己的想法, 大大 39 | 提高编程的效率。 对有点 C/C++、 Fortran 等编译语言使用经验的人来说(特别是没有 40 | 养成良好编程习惯的初学者 ), 往往要花大量时间在编译调错上, 这是很让人沮丧的编程体验。 41 | 42 | Python 的语言设计是非常简介清爽, 没有很多不必要的语法要求, 写同样功能的程序, 43 | 你需要敲击键盘的次数更少, 从而大大提高了编辑代码的效率。 比如下面的三段代码 44 | 45 | * C++ 46 | 47 | ```c++ 48 | int sum = 0; 49 | for(int i = 0; i < 10; i++) 50 | { 51 | sum += i; 52 | std::cout<< sum << std::endl; 53 | } 54 | ``` 55 | 56 | * Matlab 57 | 58 | ```matlab 59 | sum = 0; 60 | for i = 0:9 61 | sum = sum + i; 62 | disp(sum); 63 | end 64 | ``` 65 | 66 | * Python 67 | 68 | ```python 69 | sum = 0 70 | for i in range(10): 71 | sum += i 72 | print(sum) 73 | ``` 74 | 75 | Python 的代码行末尾不强制要求加 `;`。 如果 C++ 不加, 就会报语法错误; 而 Matlab 76 | 行末不加 `;`, 就会打印本行的计算结果。 Python 是用缩进来区分代码块的开始和结 77 | 束, 而且严格要求 4 个缩进空格。 同级缩进的就属于同一个代码块。 这样的缩进强制 78 | 要求, 可以让你养成很好程序排版习惯。 这是不同于 C++ 的 `{}`, 以及 Matlab 的 79 | `end` 关键词的, 这些语法要求本质上是不必要的。 Matlab 还有更多这样的要求, 比 80 | 如每个文件只能有一个主函数或者类的定义, 而且还必须和文件名同名。 81 | 82 | 做为解释性的语言, Python 对面向对象编程支持的非常好。 在 Python 中, **一切皆是 83 | 对象, 都有它固有的属性和方法**。 相比于 C++ 的面向对象, Python 的面向对象语法更 84 | 加灵活易用。 而 Matlab 的面向对象, 我就只能说“呵呵”了。 85 | 86 | 由于 Python 语法设计的考虑的非常全面, 没有那些不必要的语法, 所以可以写出结构 87 | 性很好的 代码, 更容易阅读。 因此在 Python 的语言世界里, 是可以做到 "code what we 88 | think" 的。 89 | 90 | 另外, Python 有着庞大的用户社群, 包括 NASA、 ANL 和 Google 等国际知名的科研机 91 | 构 和公司都选择 Python 做为主要的高性能计算开发语言。 而且 Python 社群是非常开 92 | 放的, 开放带来的好处是初学者和开发者很容易从社群中获得帮助和开发文档。 93 | 94 | 有的人会说, Python 是解释性的语言, 它的类型是动态的, 虽然灵活, 提高了编写代 95 | 码的效率, 但运行速度太慢, 不适合做科学计算。 确实 Python 里的每个变量都是一个 96 | 对象, 包括整数、 字符串和浮点数这些基本数据类型都是对象, 它们包含的不仅仅是 97 | 一种类型的值, 而且都附加了一些额外的东西, 这才带来了你所体验到的灵活性, 但 98 | 付出了运行效率的代价。 但这没关系, Python 还有另外一个灵活性,就是 Python 与 99 | 其它语言的接口很灵活, 那些需要高效率执行的代码, 可以用 C/C++ 或者 Fortran 实 100 | 现, 然后再接到 Python 里用, 这样就可以兼顾灵活性和效率。 Python 还有一个扩展 101 | 叫 Cython, 可以直接翻译成 C 代码, 编译后效率也很高。 102 | 103 | Python 中很多用于科学计算基础软件包, 它们的内核就是用编译语言实现的, 然后加了一个 104 | Python 接口。 如前面提到的 NumPy、 Scipy等都是这样实现的。 105 | 106 | Python 的另一个优点在于,它的内核很小, 只有二十几兆, 其它的功能都是通过模块的 107 | 形式加上去了。 除了科学计算, 开发网站、系统管理什么的 Python 也都能胜任。 对比 108 | 一下, Matlab 最新的安装文件有十几个 G, 而且很多科学计算之外的事情还干不了。 109 | 110 | 最后, Python 还是开源免费的, 注意重点在**开源**。 开放性对于计算数学的科研 人 111 | 员和学生来说, 其实是非常重要的。 我们科研人员有一项重要的工作任务, 就 是设计 112 | 更高效的算法。 但新算法必须变成程序, 运行出让人信服的实验结果, 才能发表 推广 113 | 。 但一般的新算法都是在旧算法的基础上改进而来。 如果手头有旧算法的开源代码 , 114 | 就可以直接在代码上做改进, 这样研究效率就会大大提高。 对学生来说, 他们的主要 115 | 任务是学习已有的优秀算法, 并且理解它们为什么可以工作的那么好。 如果有开源的算 116 | 法代码, 通过仔细地研读分析, 就可以更好地理解和掌握这些算法。 相反, 如果只用 117 | 商业软件, 看不到源代码, 学习和研究计算数学的效率就会大大折扣。 118 | 119 | 但很多人更喜欢**免费**的商业软件。 在中国, 免费往往是通过盗版方式实现的。 但这 120 | 个世界上所谓“免费"的东西, 深究之后基本上都不是免费的, 因为你不出钱, 总会有其 121 | 他人来出钱。 正是我们每一个习惯了使用盗版商业软件的人, 免费为国际商业软件巨头 122 | 培养了大量的用户, 帮助它们几乎完全占领了中国的工业软件市场, 中国的制造业每 123 | 年要花费大量的金钱来购买这些软件。 中国改革开放的历程, 也是国产工业软件坠入深 124 | 渊的历程, 我们的计算机和互联网教育, 以及我们每一个“钟情”盗版软件的人, 都负有 125 | 不可推卸的责任。 126 | 127 | 我们的工业界在付出代价, 而我们每个人也在付出代价。 某一天其它专业的同事来找我 128 | , 因为他的论文数值模拟是用商业软件算的, 但他根本不懂里面的有限元算法,无法回 129 | 答审稿人的问题。 还有, 他也不敢在论文里面提他是用商业软件算的, 因为他用的是 130 | 盗版。 请问,这样的论文出再多有什么用啊? 131 | 132 | 我们的教育系统也在付出代价。 设想一下, 现在如果我们的学校没有 Windows 系统用了 133 | , 整个学校就可能没法正常运转了, 没有 PPT 就几乎没法上课了。 而我在 UCI 访问的 134 | 时候,看到他们公共机房的机器都是开源的 Ubuntu 系统。 135 | 136 | 很多时候, 无论是大到国家, 还是小到我们自己, 都需要尽量准备一些额外的选项, 137 | 因为只有这样才有可能拥有真正的自由。 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /win-open-dev.md: -------------------------------------------------------------------------------- 1 | # Windows 下开源开发环境的搭建 2 | 3 | 因工作需要在 Windows 下测试开发的程序, 所以研究了一下如何在 Windows 下搭建基于 gcc 的开源开发环境。 4 | 5 | 我们这里使用 MSYS2, 是 Windows 下的一款软件构建和发布平台, 网址为 https://www.msys2.org。 它提供了和 Linux 系统几乎一样开发接口和环境, 而且它自带了一个软件包的管理工具 `pacman`, 可以像 Ubutnu 中 `apt` 一样管理需要的开源软件包。 6 | 7 | MSYS2 可以在其官网下载, 有 32 位和 64位版本的。 因为现在大部分机器和系统都是 64 位的, 所以下面只介绍 64 位版本安装情况。 8 | 9 | 首先下载 [msys2-x86_64-latest.exe](http://repo.msys2.org/distrib/msys2-x86_64-latest.exe) 运行安装, 默认安装在 `C:\msys64`。 10 | 11 | 在目录 `C:\msys64` 下, 点击运行 `msys2`, 启动 MSYS2 命令行, 多次输入下面命令把 MSYS2 更新到最新。 注意每次更新完, 可能需要关闭命令行, 再打开才能重新执行下面的命令。 12 | 13 | ``` 14 | $ pacman -Syuu 15 | ``` 16 | 17 | 下面安装安装编译器和开发工具, 注意这里只安装 64 位的版本: 18 | 19 | ``` 20 | pacman -S --needed base-devel mingw-w64-x86_64-toolchain git mingw-w64-x86_64-cmake 21 | ``` 22 | 23 | 安装完成后, `C:\msys64\` 目录下会出现 `mingw64` 的命令行程序, 点击打开, 就可以命令行中调用 `gcc`, `g++`等编译器和工具了。 24 | 25 | MSYS2 有很多已经编译好的开源软件包, 可以通过 `pacman` 从网上的仓库中搜索、下载和安装。 比如要安装 `boost` 26 | 27 | ``` 28 | $ pacman -Ss boost # 搜索 boost 29 | mingw32/mingw-w64-i686-boost 1.60.0-2 30 | Free peer-reviewed portable C++ source libraries (mingw-w64) 31 | mingw64/mingw-w64-x86_64-boost 1.60.0-2 32 | Free peer-reviewed portable C++ source libraries (mingw-w64) 33 | $ pacman -Sy mingw-w64-x86-64-boost # 安装 64位 boost 34 | ``` 35 | 36 | ## Windows 下 Mumps 的安装 37 | 38 | 有了上面的环境, 下面将介绍如何利用 mingw64 来编译安装 Mumps。 39 | 40 | 首先, 安装依赖软件包 metis 和 openblas 41 | 42 | ``` 43 | $ pacman -Sy mingw-w64-x86-64-metis 44 | $ pacman -Sy mingw-w64-x86_64-openblas 45 | ``` 46 | 47 | 下面安装 MUMPS, 但它的安装包需要到官网(http://mumps.enseeiht.fr/)填个表, 开发人员会把包发到你的邮箱。 48 | 49 | ``` 50 | $ tar -xvf MUMPS_5.1.2.tar.gz 51 | $ cd MUMPS_5.1.2/ 52 | $ cp Make.inc/Makefile.inc.generic.SEQ ./Makefile.inc 53 | ``` 54 | 55 | 下面要对 `Makefile.inc` 针对 Windows 系统做一些修改, 主要就是告诉 make, MUMPS 依赖包的头文件和库文件在 Windows 系统的什么地方, 具体内容如下: 56 | ``` 57 | # Makefile.inc 58 | LPORDDIR = $(topdir)/PORD/lib/ 59 | IPORD = -I$(topdir)/PORD/include/ 60 | LPORD = -L$(LPORDDIR) -lpord 61 | LMETISDIR = /C/msys64/mingw64/lib 62 | IMETIS = -I/C/msys64/mingw64/include 63 | LMETIS = -L$(LMETISDIR) -lmetis 64 | ORDERINGSF = -Dmetis -Dpord 65 | ORDERINGSC = $(ORDERINGSF) 66 | 67 | PLAT = 68 | LIBEXT = .a 69 | OUTC = -o 70 | OUTF = -o 71 | RM = /bin/rm -f 72 | CC = gcc 73 | FC = gfortran 74 | FL = gfortran 75 | AR = ar vr 76 | RANLIB = ranlib 77 | 78 | INCSEQ = -I$(topdir)/libseq 79 | LIBSEQ = $(LAPACK) -L$(topdir)/libseq -lmpiseq 80 | 81 | LIBBLAS = -lopenblas 82 | LIBOTHERS = -lpthread 83 | 84 | CDEFS = -DAdd_ 85 | OPTF = -O -fPIC 86 | OPTC = -O -fPIC 87 | OPTL = -O -fPIC 88 | 89 | INCS = $(INCSEQ) 90 | LIBS = $(LIBSEQ) 91 | LIBSEQNEEDED = libseqneeded 92 | ``` 93 | 94 | 另外, 在 `MUMPS_5.1.2/example` 下的 `Makefile` 中, 也要做些修改: 95 | ``` 96 | ``` 97 | 98 | 打开 `mingw64` 的命令行程序, 并进行 `MUMPS` 的代码目录 99 | 100 | ``` 101 | $ make 102 | ``` 103 | 104 | ## Windows 下安装 Mumps Interface for Matlab 105 | 106 | 首先, 在 Matlab 命令行中设置环境变量, 107 | ``` 108 | >> setenv('MW_MINGW64_LOC','C:\msys64\mingw64') 109 | ``` 110 | 或者直接在 Windows 系统中添加系统环境变量 `MW_MINGW64_LOC`, 其值设为 `C:\msys64\mingw64`, 如何添加系统环境变量请自行网络搜索网络。 111 | 112 | 在 Matlab 命令行中运行: 113 | ``` 114 | >> mex -setup 115 | ``` 116 | 就可以让 Matlab 的 `mex` 命令识别到 `mingw64`, 然后就可以用 `mingw64` 提供的编译器来编译 mex 程序, 并被 Matlab 的程序调用。 117 | 118 | 但实际上要编译出在 Matlab 中可以直接调用的 `mex` 程序, 不用 `mex` 命令, 甚至不用打开 Matlab, 也是可以的做到的。 因为 `mex` 生成的可执行文件本质是上**动态联接库文件**, 而且 Matlab 为 Matlab 与 C\C++ 的混合编程提供了相应的函数库, 如: 119 | 120 | * libmx.lib 121 | * libmex.lib 122 | * libmat.lib 123 | * libeng.lib 124 | 125 | 直接把 `MUMPS_5.1.2/MATLAB/` 中 `Makefile` 文件直接替换为下面在内容 126 | ``` 127 | # MUMPS_5.1.2/MATLAB 下的 Makefile 文件 128 | MATLABROOT = /C/MATLAB/R2016b 129 | MUMPSROOT = /C/msys64/home/why/software/MUMPS_5.1.2 130 | MINGWROOT = /C/msys64/mingw64 131 | 132 | MUMPSLIBS = -L${MUMPSROOT}/lib -ldmumps -lmumps_common -lpord -L${MUMPSROOT}/libseq -lmpiseq 133 | LIBS = -L${MINGWROOT}/usr/lib -lmetis -lopenblas -lgfortran # 注意 134 | MEXLIBS = -L${MATLABROOT}/extern/lib/win64/microsoft -llibmex -llibeng -llibmat -llibmx -llibut # 这两行中引用软件包名字的区别 135 | CC = gcc 136 | CCFLAGS = -I${MATLABROOT}/extern/include -I${MUMPSROOT}/include -I${MUMPSROOT}/libseq 137 | 138 | .PHONY: all 139 | 140 | all: dmumpsmex.mexw64 141 | 142 | dmumpsmex.mexw64: mumpsmex.c 143 | @${CC} ${CCFLAGS} -shared -fPIC -DMUMPS_ARITH=MUMPS_ARITH_d -O2 -o $@ $< ${MUMPSLIBS} ${LIBS} ${MEXLIBS} 144 | @strip $@ 145 | 146 | clean: 147 | @-rm -f *.o *.mexw64 148 | ``` 149 | 150 | **注意**, 上面 `MEXLIBS` 变量中索引 Matlab 的库文件时, 用的是 `-llibmex -llibeng -llibmat -llibmx -llibut`, 而 `MUMPSLIBS` 和 `LIBS` 变量中用的是 Linux 下常用的方式, 如 `-lmetis`, 没有 `lib` 字符串。 我猜 `mingw64` 用了两种完全方式来 151 | -------------------------------------------------------------------------------- /ubuntu-xia-bing-xing-mumps-de-an-zhuang.md: -------------------------------------------------------------------------------- 1 | # Ubuntu 下的 MUMPS 安装过程 2 | 3 | 因为 Ubuntu 系统中自带的 MUMPS 版本比较旧, 所以想用比较新的版本, 就需要学会自己编译安装 MUMPS。 本文将介绍 Ubuntu 下的串行 MUMPS 及其依赖软件包的编译安装过程, 其它的 Linux 发行版本类似可以安装。 4 | 5 | MUMPS 能调用 `metis` 和 `scotch` 来做稀疏矩阵元素的重新排序, 可以大大减少矩阵分解产生的非零元,从而节省矩阵分解需要的内存开销。 下面首先介绍 `metis` 的安装过程: 6 | 7 | ``` 8 | $ wget http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz 9 | $ tar -xvf metis-5.1.0.tar 10 | $ cd metis-5.1.0/ 11 | ``` 12 | 13 | 如果需要 `metis` 支持 64 位的整型和浮点型, 可以把 `include/metis.h` 中的 14 | 15 | ``` 16 | #define IDXTYPEWIDTH 32 17 | #define REALTYPEWIDTH 32 18 | ``` 19 | 20 | 修改为 21 | 22 | ``` 23 | #define IDXTYPEWIDTH 64 24 | #define REALTYPEWIDTH 64 25 | ``` 26 | 27 | 然后编译安装: 28 | 29 | ``` 30 | $ make config prefix=/home/why/local/multicore # 替换为你的安装路径 31 | $ make -j8 # 并行编译, 8 个核 32 | $ make install 33 | ``` 34 | 35 | 下面安装 scotch, 36 | 37 | ``` 38 | $ wget https://gforge.inria.fr/frs/download.php/file/37622/scotch_6.0.6.tar.gz 39 | $ tar -xvf scotch_6.0.6.tar.gz 40 | $ cd scotch_6.0.6/src 41 | $ cp Make.inc/Makefile.inc.x86-64_pc_linux2 ./Makefile.inc 42 | ``` 43 | 44 | 注意要修改 `Makefile.inc` 中的 `CFLAGS` 行, `=` 后面加 `-fPIC`。 然后编译安装 45 | 46 | ``` 47 | $ make -j8 48 | $ make esmumps 49 | $ make install prefix=/home/why/local/multicore 50 | $ cp ../lib/libesmumps.a /home/why/local/multicore/lib/ 51 | $ cp ../include/esmumps.h /home/why/local/multicore/include/ 52 | ``` 53 | 54 | 下面安装 OpenBLAS, 它是一个优化的 BLAS 包, 支持多线程, 运行速度更快。目前大部分的个人电脑的 CPU 都是多核的, 所以为了充分利用多核资源, 获得更快的计算速度, 强烈建议安装 55 | 56 | ``` 57 | $ git clone https://github.com/xianyi/OpenBLAS.git 58 | $ cd OpenBLAS/ 59 | $ make -j8 NO_SHARED=1 60 | $ make PREFIX=/home/why/local/multicore NO_SHARED=1 install 61 | ``` 62 | 63 | 下面安装 MUMPS, 但它的安装包需要到官网(http://mumps.enseeiht.fr/)填个表, 开发人员会把包发到你的邮箱。 64 | 65 | ``` 66 | $ tar -xvf MUMPS_5.1.2.tar.gz 67 | $ cd MUMPS_5.1.2/ 68 | $ cp Make.inc/Makefile.inc.generic.SEQ ./Makefile.inc 69 | ``` 70 | 71 | 下面要对 `Makefile.inc` 做修改, 主要就是告诉 make, MUMPS 依赖的包的头文件和库文件在什么地方, 我这里修改的内容如下: 72 | 73 | 74 | ``` 75 | SCOTCHDIR = ${HOME}/local/multicore 76 | ISCOTCH = -I$(SCOTCHDIR)/include 77 | 78 | LSCOTCH = -L$(SCOTCHDIR)/lib -lesmumps -lscotch -lscotcherr 79 | 80 | LPORDDIR = $(topdir)/PORD/lib/ 81 | IPORD = -I$(topdir)/PORD/include 82 | LPORD = -L$(LPORDDIR) -lpord 83 | 84 | LMETISDIR = ${HOME}/local/multicore/lib 85 | IMETIS = -I${HOME}/local/multicore/include 86 | 87 | LMETIS = -L$(LMETISDIR) -lmetis 88 | 89 | 90 | ORDERINGSF = -Dpord -Dscotch -Dmetis 91 | ORDERINGSC = $(ORDERINGSF) 92 | 93 | LORDERINGS = $(LMETIS) $(LPORD) $(LSCOTCH) 94 | IORDERINGSF = $(ISCOTCH) 95 | IORDERINGSC = $(IMETIS) $(IPORD) $(ISCOTCH) 96 | 97 | 98 | PLAT = 99 | LIBEXT = .a 100 | OUTC = -o 101 | OUTF = -o 102 | RM = /bin/rm -f 103 | CC = gcc 104 | FC = gfortran 105 | FL = gfortran 106 | AR = ar vr 107 | RANLIB = ranlib 108 | 109 | INCSEQ = -I$(topdir)/libseq 110 | LIBSEQ = $(LAPACK) -L$(topdir)/libseq -lmpiseq 111 | 112 | LIBBLAS = -L${HOME}/local/multicore/lib -lopenblas 113 | 114 | LIBOTHERS = -lpthread 115 | 116 | CDEFS = -DAdd_ 117 | 118 | OPTF = -O -fPIC 119 | OPTC = -O -fPIC 120 | OPTL = -O -fPIC 121 | 122 | INCS = $(INCSEQ) 123 | LIBS = $(LIBSEQ) 124 | LIBSEQNEEDED = libseqneeded 125 | ``` 126 | 127 | 然后编译安装 128 | 129 | 130 | ``` 131 | $ make -j8 132 | $ cp include/* /home/why/local/multicore/include/ 133 | $ cp lib/* /home/why/local/multicore/lib/ 134 | $ cp libseq/libmpiseq.a /home/why/local/multicore/lib/ 135 | $ cp libseq/*.h /home/why/local/multicore/include/ 136 | ``` 137 | 138 | 下面安装 MUMPS 的 Matlab 接口 139 | 140 | ``` 141 | $ cd MATLAB/ # 在 MUMPS 的主目录下, 进入 MATLAB 文件夹。 142 | ``` 143 | 144 | 修改 `make.inc` 文件如下, 注意其中的目录要替换为你自己的目录哦。 145 | 146 | ``` 147 | MEX = /opt/matlab/R2018a/bin/mex -g -largeArrayDims # 替换为你的 mex 位置 148 | 149 | # Main MUMPS_DIR 150 | MUMPS_DIR = ${HOME}/local/multicore 151 | 152 | # Orderings (see main Makefile.inc file from MUMPS) 153 | LSCOTCHDIR = ${HOME}/local/multicore/lib 154 | LSCOTCH = -L$(LSCOTCHDIR)/lib -lesmumps -lscotch -lscotcherr 155 | LMETISDIR = ${HOME}/local/multicore/lib 156 | LMETIS = -L$(LMETISDIR) -lmetis 157 | LPORDDIR = $(MUMPS_DIR)/PORD/lib 158 | LPORD = -L$(LPORDDIR) -lpord 159 | LORDERINGS = $(LPORD) $(LMETIS) $(LSCOTCH) 160 | 161 | LIBFORT = -lgfortran 162 | 163 | LIBBLAS = -L${HOME}/local/multicore/lib -lopenblas 164 | 165 | OPTC = -DINTSIE64 -g 166 | ``` 167 | 168 | 然后编译 169 | 170 | ``` 171 | $ make 172 | $ ls *.mexa64 173 | dmumpsmex.mexa64 zmumpsmex.mexa64 # 实数和复数版本的 174 | ``` 175 | 176 | 在 `MATLAB` 目录下, 有一个 `simple_example.m` 文件, 打开 matlab, 运行就可以测试有没有问题了。 177 | 178 | 如果以上编译过程有问题, 可以给我发邮件 weihuayi@xtu.edu.cn。 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /mumps.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | For what it’s worth, I’ve put my entire build process below. It’s probably too much information, but the short version is that I’m using Intel’s complier on Linux. The Kwant-specific stuff is near the end. The earlier stuff is compiling Python, SCOTCH, METIS, and MUMPS. 8 | 9 | Any advice? 10 | 11 | Thanks. 12 | -Leon 13 | 14 | # folders to hold everything 15 | 16 | cd 17 | mkdir local 18 | mkdir build 19 | 20 | cd build 21 | 22 | # install python and packages 23 | 24 | wget [https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tar.xz](https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tar.xz) 25 | tar xf Python-3.5.2.tar.xz 26 | 27 | cd Python-3.5.2 28 | ./configure --prefix=~/local 29 | make 30 | make install 31 | export PATH=~/local/bin:$PATH 32 | cd .. 33 | 34 | # install important python packages 35 | 36 | pip3 install scipy tinyarray matplotlib cython 37 | 38 | # get SCOTCH, METIS, MUMPS 39 | 40 | wget [http://gforge.inria.fr/frs/download.php/file/34618/scotch\_6.0.4.tar.gz](http://gforge.inria.fr/frs/download.php/file/34618/scotch_6.0.4.tar.gz) 41 | wget [http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz](http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/metis-5.1.0.tar.gz) 42 | wget [http://graal.ens-lyon.fr/MUMPS/MUMPS\_5.0.2.tar.gz](http://graal.ens-lyon.fr/MUMPS/MUMPS_5.0.2.tar.gz) 43 | 44 | tar xf scotch\_6.0.4.tar.gz 45 | tar xf metis-5.1.0.tar.gz 46 | tar xf MUMPS\_5.0.2.tar.gz 47 | 48 | # install METIS 49 | 50 | cd metis-5.1.0 51 | make config shared=1 prefix=~/local/ 52 | make 53 | make install 54 | cd .. 55 | 56 | # install SCOTCH 57 | 58 | cd scotch\_6.0.4/src/ 59 | cp Make.inc/Makefile.inc.x86-64\_pc\_linux2.icc Makefile.inc 60 | 61 | # need to tweak the makefile 62 | 63 | nano Makefile.inc 64 | 65 | Add "-fPIC” to the line that starts with "CFLAGS" 66 | 67 | make 68 | make esmumps 69 | make install prefix=~/local 70 | 71 | # make install won't install esmumps... copy files manually 72 | 73 | cd ../.. 74 | cp scotch\_6.0.4/lib/libesmumps.a ~/local/lib/ 75 | cp scotch\_6.0.4/include/esmumps.h ~/local/include/ 76 | 77 | # install MUMPS 78 | 79 | cd MUMPS\_5.0.2 80 | cp Make.inc/Makefile.INTEL.SEQ Makefile.inc 81 | 82 | # need to make a bunch of changes to Makefile.inc 83 | 84 | nano Makefile.inc 85 | 86 | 1\) change "\#SCOTCHDIR = ${HOME}/scotch\_6.0" 87 | to "SCOTCHDIR = ${HOME}/local/lib" 88 | 89 | 2\) uncomment the line "LSCOTCH = -L$\(SCOTCHDIR\)/lib -lesmumps -lscotch -lscotcherr" 90 | 91 | 3\) change the line "\#LMETISDIR = /local/metis/" 92 | to "LMETISDIR = ${HOME}/local/lib" 93 | 94 | 4\) uncomment the line "LMETIS = -L$\(LMETISDIR\) -lmetis" 95 | 96 | 5\) change the line "ORDERINGSF = -Dpord" 97 | to "ORDERINGSF = -Dpord -Dscotch -Dmetis" 98 | 99 | 6\) change "MKLROOT=/opt/intel/mkl/lib/intel64" 100 | to "MKLROOT=/opt/intel-12.1/mkl/lib/intel64" 101 | 102 | 7\) add the "-fPIC" compiler flag to the lines that begin "OPTF", "OPTL", and "OPTC" 103 | 104 | # about damn time! 105 | 106 | make z 107 | 108 | # there's no automatic installation... 109 | 110 | cp include/_ ~/local/include/ 111 | cp lib/_ ~/local/lib/ 112 | cp libseq/libmpiseq.a ~/local/lib/ 113 | cp libseq/\*.h ~/local/include/ 114 | 115 | cd .. 116 | 117 | # now for kwant 118 | 119 | git clone --branch v1.2.2 [http://gitlab.kwant-project.org/kwant/kwant.git](http://gitlab.kwant-project.org/kwant/kwant.git) 120 | cd kwant 121 | 122 | # need to make a new configuration file 123 | 124 | nano build.conf 125 | 126 | #### START OF FILE 127 | 128 | \[lapack\] 129 | libraries = mkl\_intel\_lp64 mkl\_sequential mkl\_core mkl\_def 130 | library\_dirs = /opt/intel-12.1/mkl/lib/intel64 131 | extra\_link\_args = -Wl,-rpath=/opt/intel-12.1/mkl/lib/intel64 132 | 133 | \[mumps\] 134 | libraries = zmumps mumps\_common pord metis esmumps scotch scotcherr mpiseq gfortran 135 | library\_dirs = /home/lmaurer/local/lib/ 136 | include\_dirs = /home/lmaurer/local/include 137 | 138 | #### END OF FILE 139 | 140 | # finally make and install kwant 141 | 142 | python3 setup.py build 143 | python3 setup.py install 144 | 145 | # need to do the following or kwant won't be able to find libmetis.so 146 | 147 | export LD\_LIBRARY\_PATH=~/local/lib/:$LD\_LIBRARY\_PATH 148 | 149 | For reference, here are my libs and headers 150 | $ ls local/lib/ 151 | libesmumps.a libmpiseq.a libpord.a libscotch.a 152 | libscotcherrexit.a libzmumps.a python3.5 libmetis.so libmumps\_common.a 153 | libpython3.5m.a libscotcherr.a libscotchmetis.a pkgconfig 154 | $ ls local/include/ 155 | cmumps\_c.h cmumps\_struc.h dmumps\_root.h elapse.h metis.h mpi.h 156 | mumps\_c\_types.h scotchf.h smumps\_c.h smumps\_struc.h zmumps\_root.h 157 | cmumps\_root.h dmumps\_c.h dmumps\_struc.h esmumps.h mpif.h 158 | mumps\_compat.h python3.5m scotch.h smumps\_root.h zmumps\_c.h 159 | zmumps\_struc.h 160 | 161 | -------------------------------------------------------------------------------- /san-du-ding-li.md: -------------------------------------------------------------------------------- 1 | # 散度定理 2 | 3 | 散度定理是有限元理论和算法中经常用到一个定理。 但很多学过数学分析的学生, 你如果问他这个定理, 很少有人能答出来。 4 | 5 | 给定向量函数 $$\mathbf F(x)$$, 其定义域为 $$\Omega\in\mathbb R^n$$, $$\mathbf n$$ 是 $$\Omega$$ 边界 $$\partial \Omega$$ 上的单位外法线向量. 6 | 7 | $$ 8 | \int_{\Omega} \nabla\cdot\mathbf F~ \mathrm d \mathbf x = \int_{\partial \Omega}\mathbf F\cdot\mathbf n ~\mathrm d s 9 | $$ 10 | 11 | 在一维情形下, 散度定理等价于微积分第二基本定理或“牛顿-莱布尼茨公式”。 在二维情形下, 散度定理等价于格林公式。 12 | 13 | 散度定理在数值计算的理论和算法中非常有用, 你一定要牢记它。 14 | 15 | 比如, 它可以用来计算多边形的面积和多面体的体积。 以多边形的面积计算为例, 给定一个多边形 $$\Omega$$, 假设它有 $$n$$ 个按逆时针排序的顶点 $$\{\mathbf x_i \}_{i=0}^{n-1}$$, $$n$$ 条边 $$\{e_i:=(\mathbf x_i, \mathbf x_{i+1})\}_{i=0}^{n-1}$$(注意这里假定 $$\mathbf x_n = \mathbf x_0$$), 第 $$i$$ 条边 $$e_i$$ 的单位外法线向量记为 $$\mathbf n_i$$。 下面利用散度定理给出多边形面积的计算公式: 16 | 17 | $$ 18 | \begin{align} 19 | \int_\Omega \mathrm d\mathbf x = &\frac{1}{2}\int_\Omega\nabla\cdot\mathbf x \mathrm d\mathbf x \\ 20 | =& \frac{1}{2}\int_{\partial\Omega} \mathbf x\cdot \mathbf n \mathrm ds\\ 21 | =& \frac{1}{2}\sum_{i=0}^{n-1}\int_{e_i} \mathbf x\cdot \mathbf n_i \mathrm ds\\ 22 | =& \frac{1}{2}\sum_{i=0}^{n-1}\int_{e_i} (\mathbf x - \mathbf x_i + \mathbf x_i)\cdot \mathbf n_i\mathrm ds\\ 23 | =& \frac{1}{2}\sum_{i=0}^{n-1}\int_{e_i} \mathbf x_i\cdot \mathbf n_i \mathrm ds\\ 24 | =& \frac{1}{2}\sum_{i=0}^{n-1}\mathbf x_i\cdot \mathbf n_i \int_{e_i} \mathrm ds\\ 25 | \end{align} 26 | $$ 27 | 28 | 推广一下, 还可以把一些特殊一点的函数在高维区域函数积分, 转化为低维区域积分的问题。 如**$$q$$ 次齐次函数** 29 | 30 | $$ 31 | f(\lambda\mathbf x) = \lambda^qf(\mathbf x),\quad \forall \lambda > 0 32 | $$ 33 | 34 | 记 $$\mathbf y = \lambda \mathbf x $$, 上式对 $$\lambda$$ 求导 35 | 36 | $$ 37 | q\lambda^{q-1}f(\mathbf x) = \nabla_{\mathbf y}f \cdot\frac{\partial\mathbf y}{\partial \lambda} =\nabla_{\mathbf y}f \cdot \mathbf x 38 | $$ 39 | 40 | 取 $$\lambda = 1$$, 可得: 41 | 42 | $$ 43 | qf(\mathbf x) = \nabla f(\mathbf x)\cdot\mathbf x 44 | $$ 45 | 46 | 给定一个定义在多边形 $$\Omega$$ 上的齐次函数 $$f(\mathbf x)$$, 记 47 | 48 | $$ 49 | \mathbf F: = \mathbf x f(\mathbf x) 50 | $$ 51 | 52 | 代入散度定理公式可得 53 | 54 | $$ 55 | \begin{align} 56 | & \int_\Omega\nabla\cdot[\mathbf x f(\mathbf x)]\mathrm d \mathbf x \\ 57 | =& \int_\Omega (\nabla\cdot\mathbf x)f(\mathbf x)\mathrm d \mathbf x + 58 | \int_\Omega \mathbf x\cdot \nabla f(\mathbf x)\mathrm d \mathbf x\\ 59 | =& 2 \int_\Omega f(\mathbf x)\mathrm d \mathbf x + 60 | \int_\Omega qf(\mathbf x)\mathrm d \mathbf x\\ 61 | =& (q+2) \int_\Omega f(\mathbf x)\mathrm d \mathbf x \\ 62 | \end{align} 63 | $$ 64 | 65 | $$ 66 | \begin{align} 67 | & \int_{\partial\Omega} (\mathbf x\cdot \mathbf n) f(\mathbf x)\mathrm ds\\ 68 | =& \sum_{i=0}^{n-1} \int_{e_i} (\mathbf x\cdot \mathbf n_i) f(\mathbf x)\mathrm ds\\ 69 | =& \sum_{i=0}^{n-1}\int_{e_i} [(\mathbf x - \mathbf x_i + \mathbf x_i)\cdot \mathbf n_i] f(\mathbf x)\mathrm ds\\ 70 | =& \sum_{i=0}^{n-1}\int_{e_i} (\mathbf x_i\cdot \mathbf n_i) f(\mathbf x)\mathrm ds\\ 71 | =& \sum_{i=0}^{n-1}(\mathbf x_i\cdot \mathbf n_i)\int_{e_i} f(\mathbf x)\mathrm ds\\ 72 | \end{align} 73 | $$ 74 | 75 | 最后可得: 76 | $$ 77 | \int_\Omega f(\mathbf x)\mathrm d \mathbf x = \frac{1}{q+2}\sum_{i=0}^{n-1}(\mathbf x_i\cdot \mathbf n_i)\int_{e_i} f(\mathbf x)\mathrm ds 78 | $$ 79 | 80 | 用好散度定理的一个关键是, 你要问自己**手头的向量函数 $$\mathbf F$$ 的具体形式是什么?** 比如: 81 | 82 | $$ 83 | \int_\Omega v_x \mathrm d \mathbf x = \int_\Omega \nabla\cdot \begin{pmatrix} 84 | v\\0 85 | \end{pmatrix} \mathrm d \mathbf x = 86 | \int_{\partial \Omega} v\mathbf n_x \mathrm d \mathbf s 87 | $$ 88 | 89 | $$ 90 | \int_\Omega v_y \mathrm d \mathbf x = \int_\Omega \nabla\cdot \begin{pmatrix} 91 | 0\\v 92 | \end{pmatrix} \mathrm d \mathbf x = 93 | \int_{\partial \Omega} v\mathbf n_y \mathrm d \mathbf s 94 | $$ 95 | 96 | 还可以把高阶的微分算子变成低阶的微分算子, 给定一个偏微分方程,要变成**弱形式**就要用到这个定理。 比如给定一个适当光滑的函数 $$u$$, 其 Laplace 算子定义为: 97 | $$ 98 | \Delta u(x, y) =\nabla\cdot\nabla u = \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2}~~~(2D), 99 | $$ 100 | 101 | $$ 102 | \Delta u(x, y, z) =\nabla\cdot\nabla u = \frac{\partial^2 u}{\partial x^2} + \frac{\partial^2 u}{\partial y^2} + \frac{\partial^2 u}{\partial z^2}~~~(3D), 103 | $$ 104 | 给定另一个适当光滑的函数 $$v$$, 假定 $$v|_{\partial\Omega} = 0$$, 可得: 105 | 106 | $$ 107 | \int_{\Omega} \nabla\cdot(v\nabla u)~\mathrm d \mathbf x =\int_{\Omega} v\Delta u~\mathrm d \mathbf x + \int_{\Omega}\nabla u\cdot\nabla v~\mathrm d \mathbf x= \int_{\partial\Omega} v\nabla u\cdot\mathbf n~\mathrm d \mathbf s 108 | $$ 109 | 110 | 进而可得: 111 | $$ 112 | -\int_{\Omega} v\Delta u~\mathrm d \mathbf x = \int_{\Omega}\nabla u\cdot\nabla v~\mathrm d \mathbf x 113 | $$ 114 | 这个形式的神奇之处在于, 左边是对 $$u$$ 求二阶导数, 右边 $$u$$ 的导数就只剩下一阶, 相当于把另外一阶导数转给函数 $$v$$ 了, 这样对 $$u$$ 的光滑性要求降低了。 115 | 116 | ## 写在后面 117 | 通常大家都说, 数学学的好, 编程也会好。 但实际情况并非如此, 这里面除了编程教育环境的问题,也有数学推理与编程过程之间存在的思维方式差异问题。 118 | 119 | 很多学生在学习数分高代时, 对于一个定理、一个公式, 他能想到的用处就是用来做题和考试,产生了思维固化, 一旦换个情景他就不会了。 120 | 121 | 仔细去比较数学推理和编程, 你会发现编程过程和数学推理过程往往是相反的, 比如下面的公式: 122 | 123 | $$ 124 | \int_\Omega f(\mathbf x)\mathrm d \mathbf x = \frac{1}{q+2}\sum_{i=0}^{n-1}(\mathbf x_i\cdot \mathbf n_i)\int_{e_i} f(\mathbf x)\mathrm ds 125 | $$ 126 | 127 | 我们的目标是编程计算等式的右端, 但直接不好算。 但通过数学推导, 我们找到了更好算的右端形式。 编程的时候是计算右端, 从而得到左端的值。 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /fealpy-mesh-0.md: -------------------------------------------------------------------------------- 1 | # FEALPy 中的网格对象 2 | 3 | 在偏微分方程数值计算程序设计中, 网格是最核心的数据结构,是下一步实现数值离散方法的 4 | 基础。用节点数组 `node` 和单元数组 `cell`, 就可以表示一个网格,其它的如边数 5 | 组 `edge`、 面数组 `face` 及拓扑关系数组都可以由 `cell` 生成。在这里我们把 6 | `node`、`edge`、`face` 和 `cell` 通称为网格的实体 `entity`。 关于网格数据结构的 7 | 数组化表示与生成核心算法,请参见下面的两篇文章: 8 | 9 | * [网格数据结构的数组化表示:node 和 cell]() 10 | * [网格数据结构的数组化表示:edge]() 11 | 12 | 下面将着重介绍 FEALPy 中的 `mesh` 模块。`mesh` 模块中实现了偏微分方程数值计算 13 | 中常见网格类型,包括: 14 | 15 | | 网格类名 |代表网格类型 | 16 | | :----------- | :-----| 17 | | `IntervalMesh` | 区间网格| 18 | | `TriangleMesh` | 三角形网格| 19 | | `QuadrangleMesh` | 四边形网格| 20 | | `TetrahedronMesh` | 四面体网格| 21 | | `HexahedronMesh` | 六面体网格| 22 | | `PolygonMesh` | 多边形网格| 23 | | `PolyhedronMesh` | 多面体网格| 24 | | `StructureQuadMesh`| 结构四边形网格| 25 | | `StructureHexMesh` | 结构六面体体网格| 26 | | `Tritree` | 三角形树结构网格| 27 | | `Quadtree` | 四边形树结构网格,即四叉树| 28 | | `Octree` | 六面体树结构网格,即八叉树| 29 | 30 | 31 | 上面的网格类都可以通过类似于下面的语法导入: 32 | 33 | ``` 34 | from fealpy.mesh import TriangleMesh 35 | from fealpy.mesh import QuadrangleMesh 36 | from fealpy.mesh import Tritree 37 | ``` 38 | 39 | 注意上面的 `StructureQuadMesh` 和 `StructureHexMesh` 的实现与其它非结构网格类的 40 | 内部实现是完全不同的,它们的实现充分考虑了结构网格的特点。但它们的接口和其它非结 41 | 构网格基本是一样的,在使用过程中用户不用关心底层的实现。 42 | 43 | 44 | FEALPy 中的所有网格类的成员变量和成员函数都遵循同样的命名约定,尽量做到与维数和 45 | 网格类型无关。基本成员变量的约定为: 46 | 47 | | 变量名 | 含义 | 48 | | :----- | :----- | 49 | | `NN` | 节点的个数 50 | | `NC` | 单元的个数 51 | | `NE` | 边的个数 52 | | `NF` | 面的个数 53 | | `NCV` | 单元顶点的个数 54 | | `NFV` | 面的顶点的个数 55 | | `GD` | 空间维数 56 | | `TD` | 拓扑维数 57 | | `node` | 节点数组, 形状为 `(NN, GD)` 58 | | `cell` | 单元数组, 形状为 `(NC, NCV)` 59 | | `edge` | 边数组, 形状为 `(NE, 2)` 60 | | `face` | 面数组, 形状为 `(NF, NFV)` 61 | | `ds` | 网格的拓扑数据结构对象, 所有的拓扑关系数据都由其管理和获取 62 | | `nodedata` | 字典数组, 存储定义在节点上的数据, 默认为空字典 `{}` 63 | | `celldata` | 字典数组, 存储定义在单元上的数据, 默认为空字典 `{}` 64 | | `edgedata` | 字典数组, 存储定义在边上的数据, 默认为空字典 `{}` 65 | | `facedata` | 字典数组, 存储定义在面上的数据, 默认为空字典 `{}` 66 | | `itype` | 存储网格所用的整型数据类型 67 | | `ftype` | 存储网格所有的浮点型数据类型 68 | 69 | 因为多边形网格的每个单元顶点个数不一样,所以 `PolygonMesh` 中的 `cell` 数组 70 | `cell` 数组是一个一维数组, 另外还多增加了一个长度为 `NC+1` 一维数组 71 | `cellLocation`, `cell[cellLocation[i]:cellLocation[i+1]]` 存储第 `i` 个单元的顶 72 | 点编号, 注意这里的顶点编号是按逆时针排序。 73 | 74 | 75 | 假设有网格对象 `mesh`, 它一般都有如下接口: 76 | 77 | | 成员函数名 | 功能 78 | | :--------- | :------ 79 | | `mesh.geo_dimension()` | 获得网格的几何维数 80 | | `mesh.top_dimension()` | 获得网格的拓扑维数 81 | | `mesh.number_of_nodes()` | 获得网格的节点个数 82 | | `mesh.number_of_cells()` | 获得网格的单元个数 83 | | `mesh.number_of_edges()` | 获得网格的边个数 84 | | `mesh.number_of_faces()` | 获得网格的面的个数 85 | | `mesh.number_of_entities(etype)` | 获得 `etype` 类型实体的个数 86 | | `mesh.entity(etype)` | 获得 `etype` 类型的实体 87 | | `mesh.entity_measure(etype)` | 获得 `etype` 类型的实体的测度 88 | | `mesh.entity_barycenter(etype)` | 获得 `etype` 类型的实体的重心 89 | | `mesh.integrator(i)` | 获得该网格上的第 `i` 个积分公式 90 | | `mesh.bc_to_points(bc)` | 转换重心坐标 `bc` 到实际单元上的点 91 | 92 | 93 | 上面的 `etype` 可以取 0, 1, 2, 3 或者字符串 `cell`, `node`, `edge`, `face` 94 | , 当然对于二维网格 `etype` 就不能取 3 或者 `face`。 95 | 96 | 网格对象 `mesh` 的成员变量 `ds` 有如下的接口: 97 | 98 | | 成员函数名 | 功能 99 | | :--------- | :------ 100 | | `cell2cell = mesh.ds.cell_to_cell(...)` | 单元与单元的邻接关系 101 | | `cell2face = mesh.ds.cell_to_face(...)` | 单元与面的邻接关系 102 | | `cell2edge = mesh.ds.cell_to_edge(...)` | 单元与边的邻接关系 103 | | `cell2node = mesh.ds.cell_to_node(...)` | 单元与节点的邻接关系 104 | | `face2cell = mesh.ds.face_to_cell(...)` | 面与单元的邻接关系 105 | | `face2face = mesh.ds.face_to_face(...)` | 面与面的邻接关系 106 | | `face2edge = mesh.ds.face_to_edge(...)` | 面与边的邻接关系 107 | | `face2node = mesh.ds.face_to_node(...)` | 面与节点的邻接关系 108 | | `edge2cell = mesh.ds.edge_to_cell(...)` | 边与单元的邻接关系 109 | | `edge2face = mesh.ds.edge_to_face(...)` | 边与面的邻接关系 110 | | `edge2edge = mesh.ds.edge_to_edge(...)` | 边与边的邻接关系 111 | | `edge2node = mesh.ds.edge_to_node(...)` | 边与节点的邻接关系 112 | | `node2cell = mesh.ds.node_to_cell(...)` | 节点与单元的邻接关 113 | | `node2face = mesh.ds.node_to_face(...)` | 节点与面的邻接关系 114 | | `node2edge = mesh.ds.node_to_edge(...)` | 节点与边的邻接关系 115 | | `node2node = mesh.ds.node_to_node(...)` | 节点与节点的邻接关 116 | | `isBdNode = mesh.ds.boundary_node_flag()` | 一维逻辑数组,标记边界节点 117 | | `isBdEdge = mesh.ds.boundary_edge_flag()` | 一维逻辑数组,标记边界边 118 | | `isBdFace = mesh.ds.boundary_face_flag()` | 一维逻辑数组,标记边界面 119 | | `isBdCell = mesh.ds.boundary_cell_flag()` | 一维逻辑数组,标记边界单元 120 | | `bdNodeIdx = mesh.ds.boundary_node_index()` | 一维整数数组,边界节点全局编号 121 | | `bdEdgeIdx = mesh.ds.boundary_edge_index()` | 一维整数数组,边界边全局编号 122 | | `bdFaceIdx = mesh.ds.boundary_face_index()` | 一维整数数组,边界面全局编号 123 | | `bdCellIdx = mesh.ds.boundary_cell_index()` | 一维整数数组,边界单元全局编号 124 | 125 | 注意,上面接口中的 `...` 表示有默认的参数,可以根据需要来设定。也要注意返回的 126 | 实体间邻接关系, 默认可能是二维数组, 也可能是稀疏矩阵。 在使用过程中,用户要根 127 | 据网格的类型或者实际需要, 来控制返回邻接关系的数据类型。 128 | 129 | 如果 `mesh` 是一个三角形网格类 `TriangleMesh` 的对象, 则 130 | 131 | ``` 132 | # 下面的语句 cell2cell 为形状为 (NC, 3) 133 | # 的数组。 注意, 如果 cell2cell[i, j] == i, 134 | # 则表示第 i 个单元的第 j 个邻居是区域外部 135 | cell2cell = mesh.ds.cell_to_cell() 136 | 137 | # 下面语句 cell2cell 为形状为 (NC, NC) 的稀疏矩阵 138 | cell2cell = mesh.ds.cell_to_cell(return_sparse=True) 139 | 140 | # 下面的语句返回的 cell2cell 是一个一维数组 141 | # 再加一个 cell2cellLocation, 则第 i 个单元 142 | # 邻接单元的编号为 cell2cell[cell2cellLocation[i]:cell2cellLocation[i+1]] 143 | cell2cell, cell2cellLocation = mesh.ds.cell_to_cell(return_array=True) 144 | ``` 145 | 146 | 当然上面介绍的接口只是一部分常用接口,更多接口可以自己去代码中探索发现。 147 | 148 | 最后, 还是以一个例子为结尾, 你可以在 ipython3 中一行行测试下 面的命令, 可以先 149 | 把网格画出来, 再获取网格中的各种数据,并和网格图进行对比。 这样可以帮助你更快 150 | 更好理解 FEALPy 中网格的用法。 151 | 152 | ``` 153 | import numpy as np 154 | import matplotlib.pyplot as plt 155 | from fealpy.mesh import TriangleMesh 156 | 157 | # [0, 1]^2 区域上的网格 158 | node = np.array([ 159 | (0, 0), 160 | (1, 0), 161 | (1, 1), 162 | (0, 1)], dtype=np.float) 163 | cell = np.array([ 164 | (1, 2, 0), 165 | (3, 0, 2)], dtype=np.int) 166 | 167 | # 建立三角形网格对象 168 | tmesh = TriangleMesh(node, cell) 169 | 170 | # 一致加密一次 171 | tmesh.uniform_refine() 172 | 173 | 174 | # 获得节点数组 175 | node = tmesh.entity('node') 176 | 177 | # 获得边数组 178 | edge = tmesh.entity('edge') 179 | 180 | # 获得单元数组 181 | cell = tmesh.entity('cell') 182 | 183 | # 获得单元的面积 184 | cellArea = tmesh.entity_measure('cell') 185 | 186 | # 获得边的长度 187 | edgeLength = tmesh.entity_measure('edge') 188 | 189 | # 获得单元的重心 190 | cellBC = tmesh.entity_barycenter('cell') 191 | 192 | # 获得边的重心 193 | edgeBC = tmesh.entity_barycenter('edge') 194 | 195 | # 获得每条边的单位法向和切向 196 | n, t = tmesh.edge_frame() 197 | 198 | # 获得单元的邻接关系数组, 形状为 (NE, 3) 199 | cell2cell = tmesh.ds.cell_to_cell() 200 | 201 | # 获得边与单元的邻接关系数组,形状为 (NE, 4) 202 | edge2cell = tmesh.ds.edge_to_cell() 203 | 204 | # 打印网格的信息 205 | tmesh.print() 206 | 207 | # 建立画图对象 208 | fig = plt.figure() 209 | # 获得坐标系 210 | axes = fig.gca() 211 | # 在坐标系中画网格 212 | tmesh.add_plot(axes) 213 | # 显示所有节点编号 214 | tmesh.find_node(axes, showindex=True) 215 | # 显示所有边的编号 216 | tmesh.find_edge(axes, showindex=True) 217 | # 显示所有单元的编号 218 | tmesh.find_cell(axes, showindex=True) 219 | plt.show() 220 | ``` 221 | -------------------------------------------------------------------------------- /numpy-mesh-edge.md: -------------------------------------------------------------------------------- 1 | # 网格数据结构的数组化表示: edge 2 | 3 | 本文将介绍如何用 **数组化(array-oriented)** 的思考和编程方式构造网格数据结构中的 `edge` 数组。 4 | 5 | 所谓**数组化**, 就是从已知或者简单数组出发, 利用数组操作和运算, 获得目标数组的过程。 与其对应的就是**标量化**, 这是很多人常用到且更习惯的方式。 注意本文提到的**数组**指的是**一般的多维数组**。 而我们常用的**向量**这个词, 在数学的语境下通常是指**一维数组**, 所以为了避免语言对思维的限制, 在以后的文章中我会尽量不使用**向量化**这个词。 6 | 7 | 数组化的编程需要数组化的思维习惯。 要养成数组化的思维习惯, 当你面对一个编程问题时, 首先要养成问自己三个问题的习惯: 8 | 9 | 1. 我想获得的目标数组是什么? 10 | 2. 我手头有什么已知的数组? 11 | 3. 我需要生成什么样的中间的数组? 12 | 13 | 还是以 $[0, 1]^2$ 区域上有两个三角形单元的网格为例, 对应的 Python 代码为: 14 | 15 | ``` 16 | import numpy as np 17 | # 网格节点数组 18 | node = np.array([[0.0, 0.0], 19 | [1.0, 0.0], 20 | [1.0, 1.0], 21 | [0.0, 1.0]], dtype=np.float) 22 | # 网格单元数组 23 | cell = np.array([[1, 2, 0], 24 | [3, 0, 2]], dtype=np.int32) 25 | NN = node.shape[0] # 网格中的节点个数 26 | NC = cell.shape[0] # 网格中的单元个数 27 | ``` 28 | **注意**, 上面的 `node`, `cell`, `NN` 和 `NC` 在以后的文章和程序表示意义都是固定的。 进一步, 我会用 `edge` 表示网格边数组, 用 `NE` 表示网格中边的个数。 记住这个约定, 可以帮助你更好的理解以后文章和程序代码。 后面我也会专门写一篇文章讲这些命名的约定。 29 | 30 |

31 |

32 |
图 1, 四个顶点、两个单元的三角形网格。
33 |

34 | 35 | 对于第一个问题, 观察图中网格共有 5 条边, 每条边 2 个顶点, 而且可以分为两类: 36 | 37 | * **内部边:** 被两个三角形单元共用的边。 38 | * **边界边:** 只属于一个三角形单元。 39 | 40 | 下面首先定义清楚目标数组 `edge`, 并做一些适当的假定, 这样就可以回答第一个问题: 41 | * `edge` 形状应为 `(NE, 2)`, 存储 `NE` 条**不重复的**边, 每条边有 2 个顶点。 42 | * `edge[i, 0]` 和 `edge[i, 1]` 分别存储第 `i` 条边两个顶点的全局编号, 注意它们是 **`node` 数组的行号**。 43 | * 如果第 `i` 条边是一条边界边, 则从`edge[i, 0]` 看向 `edge[i, 1]` 的左手边是区域内部。 44 | 45 | 对于第二个问题, 显然 `cell` 数组中已经包含了所有边的信息, 只要从它出发就可以提取出目标数组 `edge`。 46 | 47 | 对于第 3 个问题, 即需要什么样的中间数组? 容易想到, 要首先把每个三角形单元的 3 条边提取出来。 当然这里存在一个边的局部编号问题。 给定一个三角形单元, 可以通过定义一个 `localEdge` 数组来定义每个单元的局部边: 48 | 49 | ``` 50 | localEdge = np.array([[1, 2], 51 | [2, 0], 52 | [0, 1]], dtype=np.int32) 53 | ``` 54 | 上面生成的 `localEdge` 用下面的约定构造: 55 | 56 | * 与单元局部第 `i` 个顶点相对的局部边局部编号设为 `i, 0<=i<3`。 57 | * 每条局部边的的左手边是**单元的内部**。 58 | 59 | 见下图: 60 | 61 | 62 |
63 |
图 2, 三角形单元顶点和边的局部编号。
64 | 65 | 66 | 当然上面关于局部边的约定不是强制的, 你可根据需要给出自己的约定。 67 | 68 | 有了上面的 `localEdge`, 就可以把网格中所有单元的三个边都拿出来组成一个新的数组 `totalEdge` 69 | ``` 70 | totalEdge = cell[:, localEdge] # shape 为 (NC, 3, 2) 71 | totalEdge = totalEdge.reshape(-1, 2) # shape 变为 (3*NC, 2) 72 | ``` 73 | `totalEdge` 是一个 `ndarray` 对象, 它的 `reshape` 成员函数可以改变数组的形状, 其中第 0 个参数 `-1`, 表示让 `reshape` 自己计算新数组的第 `0` 轴的长度。 最后的 `totalEdge` 数组, 就是把每个三角形单元的三条局部边先按单元顺序, 再按局部顺序排列的形状为 `(3*NC, 2)` 的二维数组。 74 | 75 | 这时 `totalEdge` 中已经包含 `edge` 数组需要的所有信息, 但是里面还有重复的边。 内部边会在 `totalEdge` 出现两次, 但注意因为 `localEdge` 的定义方法, 这两条重复边的方向是不一样的, 即两个顶点的排序刚好相反。 为了得到想要的 `edge`, 需要把重复的边去掉。 这里要用到 `sort` 和 `unique` 函数, 用法如下: 76 | 77 | ``` 78 | totalEdge0 = np.sort(totalEdge, axis=1) 79 | edge0, i0, j = np.unique(totalEdge0, return_index=True, return_inverse=True, axis=0) 80 | ``` 81 | 其中 `np.sort(totalEdge, axis=1)` 是把 `totalEdge` 每一行的两个值重新排序, 默认从小到大。 这样重复边两个顶点编号排序就一致了。 **注意**, 这里没有改变 `totalEdge` **行的排列顺序**。 下面解释 `np.unique` 的用法: 82 | 83 | * 第 0 个参数为边顶点排序后的所有局部边数组 `totalEdge0`。 84 | * 第 1 个参数 `return_index` 设为 `True`, 目的是为了返回 `i0`。 85 | * 第 2 个参数 `return_inverse` 设为 `True`, 目的是为了返回 `j`。 86 | * `edge0` 就是没有重复的边数组, 它和 `totalEdge0` 满足如下关系: 87 | + `edge0 == totalEdge0[i0]` 88 | + `totalEdge0 == edge0[j]` 89 | 90 | **注意**, 上面的 `totalEdge0[i0]` 和 `edge[j]` 是指索引两个数组的行, 即第 0 轴, 此时后面的轴可以省略不写, 这和 Matlab 的索引语法是不同的, 所以 Numpy 数组的索引更加方便和灵活。 91 | 92 | 但 `edge0` 数组并不是我们想要的最终边数组, 因为它不能保证满足前面对边数组存储的约定, 特别是**边界边左手边是区域内部**的约定。 实际上, 我们可以利用**没有做边顶点排序**的 `totalEdge` 数组及 `i0` 数组来得到最终的边数组: 93 | 94 | ``` 95 | edge = totalEdge[i0] # 最终的边数组 96 | NE = edge.shape[0] # 获得网格中边的个数, 即 `edge` 的行数 97 | ``` 98 | 99 | ## 关系数组: cell2edge 和 edge2cell 100 | 101 | 网格中节点、边和单元之间的拓扑关系在有有限元编程中也经常用到, 因此也需要生成这些数组。 上面生成 `edge` 数组的过程中得到的 `i0` 和 `j` 实际上已经包含了边与单元之间的拓扑关系, 下面介绍如何生成 `cell2edge` 和 `edge2cell` 数组。 102 | 103 | 首先, 先定义好这两个数组: 104 | 105 | * `cell2edge` 的形状为 `(NC, 3)`, 其第 `i` 行的三个值 `cell2edge[i, 0]`, `cell2edge[i, 1]` 和 `cell2edge[i, 2]` 分别存储第 `i` 个单元三个局部边的全局编号, 即在 `edge` 数组中的行号。 106 | * `edge2cell` 的形状为 `(NE, 4)`, 其中第 `i` 行有四个值, 其中 `edge2cell[i, 0]` 和 `edge2cell[i, 1]` 分别存储第 `i` 条边的左手边单元和右手边单元的全局编号, 而 `edge2cell[i, 2]` 和 `edge2cell[i, 3]` 分别存储第 i 条边在其左右单元中的局部编号。 107 | 108 | 利用 `j`, 很容易得到 `cell2edge` 数组, 代码如下: 109 | 110 | ``` 111 | cell2edge = j.reshape(NC, 3) 112 | ``` 113 | 114 | 最后讨论如何得到 `edge2cell`。 首先利用 `j`, 可以得到另外一个一维数组 `i1`, 它和 `i0` 的长度是一样的, 都 是 `NE`, 而且也有 `edge == totalEdge[i1, :]`。 但与 `i0` 的不同之处在于, 对于第 `i` 条边: 115 | 116 | * 如果是内部边, 则 `i0[i] != i1[i]`, 它们是同一条内部边在 `totalEdge0` 中出现两次的行号。 117 | * 如果是边界边, 则 `i0[i] == i1[i]`, 它们是同一条边界边在 `totalEdge0` 中出现一次的行号。 118 | 119 | 下面代码给出用 `j`, 得到 `i1` 的方法 120 | 121 | ``` 122 | E = 3 # 每个三角形有 3 条边 123 | i1 = np.zeros(NE, dtype=self.itype) # 分配空间 124 | i1[j] = range(3*NC) # totalEdge0 的行数是 3*NC, j 的长度也是 3*NC 125 | ``` 126 | 上面的最后一行代码中, `i1` 数组的第 `i` 个位置会赋值 1 次(边界边)或者 2 次(内部边)。 内部边第 2 次赋值时, 就把第 1 次的赋值覆盖了, 从而达到得到内部边在 `totalEdge0` 中另一个行号的目的。 127 | 128 | 最后, 利用下面的代码, 就可以得到 `edge2cell`数组 129 | 130 | ``` 131 | edge2cell = np.zeros((NE, 4), dtype=np.int32) 132 | edge2cell[:, 0] = i0//E # 每条边的左边单元的编号 133 | edge2cell[:, 1] = i1//E # 每条边的右边单元的编号 134 | edge2cell[:, 2] = i0%E # 每条边在其左边单元中的局部编号 135 | edge2cell[:, 3] = i1%E # 每条边在其右边单元中的局部编号 136 | ``` 137 | **注意**, `//` 是求商运算符, `%`是求余运算符。 如果第 `i` 条边是边界边, 则 `edge2cell[i, 0] == edge2cell[i, 1]`, 同样 `edge2cell[i, 2] == edge2cell[i, 3]`。 这样我们就得到了一种判断边界边的方法: 138 | 139 | ``` 140 | isBdEdge = edge2cell[i, 0] == edge2cell[i, 1] 141 | ``` 142 | 在我们处理边界条件时会用到这行代码。 143 | 144 | 下面给出生成算法过程中生成的数组, 以便你更好的理解上面的算法。 145 | 146 |
147 | ``` 148 | totalEdge: 149 | [[2 0] 150 | [0 1] 151 | [1 2] 152 | [0 2] 153 | [2 3] 154 | [3 0]] 155 | totalEdge0: 156 | [[0 2] 157 | [0 1] 158 | [1 2] 159 | [0 2] 160 | [2 3] 161 | [0 3]] 162 | i0: 163 | [1 0 5 2 4] 164 | i1: 165 | [1 3 5 2 4] 166 | j: 167 | [1 0 3 1 4 2] 168 | edge: 169 | [[0 1] 170 | [2 0] 171 | [3 0] 172 | [1 2] 173 | [2 3]] 174 | edge2cell: 175 | [[0 0 1 1] 176 | [0 1 0 0] 177 | [1 1 2 2] 178 | [0 0 2 2] 179 | [1 1 1 1]] 180 | cell2edge: 181 | [[1 0 3] 182 | [1 4 2]] 183 | ``` 184 | 185 | ## 写在后面 186 | 187 | 你读过文章后, 强烈建议在自己的机器测试一下上面的代码, 以加深对算法的理解。 因为这个算法是网格数据结构中生成网格实体拓扑关系核心算法, 你理解了它, 就可以推广到其它类型的网格上的去, 包括三维的网格。 我把所有的代码贴到这里, 方便你去测试。 188 | 189 | 有问题想和我讨论, 可以发邮件给我: weihuayi@xtu.edu.cn. 190 | 191 | ``` 192 | import numpy as np 193 | 194 | from fealpy.mesh import TriangleMesh 195 | import matplotlib.pyplot as plt 196 | # 网格节点数组 197 | node = np.array([[0.0, 0.0], 198 | [1.0, 0.0], 199 | [1.0, 1.0], 200 | [0.0, 1.0]], dtype=np.float) 201 | # 网格单元数组 202 | cell = np.array([[1, 2, 0], 203 | [3, 0, 2]], dtype=np.int32) 204 | NN = node.shape[0] # 网格中的节点个数 205 | NC = cell.shape[0] # 网格中的单元个数 206 | 207 | localEdge = np.array([[1, 2], 208 | [2, 0], 209 | [0, 1]], dtype=np.int32) 210 | 211 | totalEdge = cell[:, localEdge] # shape 为 (NC, 3, 2) 212 | totalEdge = totalEdge.reshape(-1, 2) # shape 变为 (3*NC, 2) 213 | print('totalEdge:\n', totalEdge) 214 | 215 | totalEdge0 = np.sort(totalEdge, axis=1) 216 | print('totalEdge0:\n', totalEdge0) 217 | 218 | edge0, i0, j = np.unique(totalEdge0, return_index=True, return_inverse=True, axis=0) 219 | print('j:\n', j) 220 | 221 | cell2edge = j.reshape(NC, 3) 222 | print('cell2edge:\n', cell2edge) 223 | 224 | edge = totalEdge[i0] # 最终的边数组 225 | print('edge:\n', edge) 226 | 227 | E = 3 # 每个三角形有 3 条边 228 | NE = edge.shape[0] # 获得网格中边的个数, 即 `edge` 的行数 229 | i1 = np.zeros(NE, dtype=np.int32) # 分配空间 230 | i1[j] = range(3*NC) # totalEdge0 的行数是 3*NC, j 的长度也是 3*NC 231 | 232 | print('i0:\n', i0) 233 | print('i1:\n', i1) 234 | 235 | edge2cell = np.zeros((NE, 4), dtype=np.int32) 236 | edge2cell[:, 0] = i0//E # 得到每条边的左边单元 237 | edge2cell[:, 1] = i1//E # 得到每条边的右边单元 238 | edge2cell[:, 2] = i0%E # 得到每条边的在左边单元中的局部编号 239 | edge2cell[:, 3] = i1%E # 得到每条边在其右边单元中的局部编号 240 | 241 | print('edge2cell:\n', edge2cell) 242 | 243 | 244 | 245 | mesh = TriangleMesh(node, cell) 246 | mesh.print() 247 | fig = plt.figure() 248 | axes = fig.gca() 249 | mesh.add_plot(axes) 250 | mesh.find_node(axes, showindex=True) 251 | mesh.find_cell(axes, showindex=True) 252 | mesh.find_edge(axes, showindex=True) 253 | plt.savefig('numpy-mesh-edge.png') 254 | plt.show() 255 | ``` 256 | -------------------------------------------------------------------------------- /figures/bytecode.svg: -------------------------------------------------------------------------------- 1 | 2 | Frame 3 | Created using Figma 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /figures/vectorized.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Frame 7 | 8 | 9 | Created using Figma 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | --------------------------------------------------------------------------------