{"id":19132496,"url":"https://github.com/taichi-dev/mls_mpm_88_extensions","last_synced_at":"2025-10-28T03:14:51.301Z","repository":{"id":105044422,"uuid":"371594395","full_name":"taichi-dev/mls_mpm_88_extensions","owner":"taichi-dev","description":null,"archived":false,"fork":false,"pushed_at":"2021-06-21T08:02:09.000Z","size":34530,"stargazers_count":16,"open_issues_count":1,"forks_count":4,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-19T13:44:40.136Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taichi-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-28T05:52:08.000Z","updated_at":"2025-03-21T03:09:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"cae35efd-1b4b-48f7-bebe-e19a7334f4c1","html_url":"https://github.com/taichi-dev/mls_mpm_88_extensions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taichi-dev%2Fmls_mpm_88_extensions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taichi-dev%2Fmls_mpm_88_extensions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taichi-dev%2Fmls_mpm_88_extensions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taichi-dev%2Fmls_mpm_88_extensions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taichi-dev","download_url":"https://codeload.github.com/taichi-dev/mls_mpm_88_extensions/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252741458,"owners_count":21797027,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T06:18:27.435Z","updated_at":"2025-10-28T03:14:51.226Z","avatar_url":"https://github.com/taichi-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mpm88.py 与各种扩展\n\n## 原版原理讲解\n\n效果：\n\n\u003cimg src='mpm88.gif'\u003e\n\n- 程序中\n\n- Clear Grid\n\n  清空网格上的物理量\n\n  ```python\n  for i, j in grid_m:\n          grid_v[i, j] = [0, 0]\n          grid_m[i, j] = 0\n  ```\n\n- P2G\n\n  P2G中包含将物理参数从粒子上传输至网格上，并进行这个过程中计算压力的影响。\n\n  需要传输的物理参数包括质量与动量。\n\n  ```python\n  for p in x:\n      Xp = x[p] / dx\t\t\t\t\t\t\t\t#将坐标转换为网格单位\n      base = int(Xp - 0.5)\t\t\t\t\t\t\t#计算粒子左下方网格坐标，因为网格偏移为0，1，2，与左下方网格坐标相加后即可得到周围3*3网格\n      fx = Xp - base\t\t\t\t\t\t\t\t#计算坐标相对网格偏移\n      w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]\t\t#计算权重\n      stress = -dt * 4 * E * p_vol * (J[p] - 1) / dx**2\t\t\t\t#计算压力造成的affine动量\n      affine = ti.Matrix([[stress, 0], [0, stress]]) + p_mass * C[p]\t\t#压力与affine动量\n      for i, j in ti.static(ti.ndrange(3, 3)):\t\t\t\t\t#将粒子信息传输至周围3*3的网格\n          offset = ti.Vector([i, j])\t\t\t\t\t\t#网格偏移\n          dpos = (offset - fx) * dx\t\t\t\t\t\t#网格相对粒子坐标\n          weight = w[i].x * w[j].y\t\t\t\t\t\t#网格权重\n          grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)\t#将动量投影至网格\n          grid_m[base + offset] += weight * p_mass\t\t\t\t#将质量投影至网格\n  ```\n\n  \n\n- Grid\n\n  在这个过程中计算需要在grid上处理的操作，包括通过动量计算速度，施加重力与边界条件处理。\n\n  ```python\n  for i, j in grid_m:\n          if grid_m[i, j] \u003e 0:\n              grid_v[i, j] /= grid_m[i, j]\t#通过动量计算速度\n          grid_v[i, j].y -= dt * gravity\t#施加重力\n          #可分离边界条件\n          if i \u003c bound and grid_v[i, j].x \u003c 0:\n              grid_v[i, j].x = 0\n          if i \u003e n_grid - bound and grid_v[i, j].x \u003e 0:\n              grid_v[i, j].x = 0\n          if j \u003c bound and grid_v[i, j].y \u003c 0:\n              grid_v[i, j].y = 0\n          if j \u003e n_grid - bound and grid_v[i, j].y \u003e 0:\n              grid_v[i, j].y = 0\n  ```\n\n  \n\n- G2P\n\n  在这个过程中完成将网格上的参数收集回粒子，并完成粒子参数的更新。\n\n  收集的参数包括：C仿射速度，v速度\n\n  更新的参数包括：x位置，J粒子体积\n  \n  ```python\n  for p in x:\n          Xp = x[p] / dx\t\t\t\t\t\t\t\t#与p2g相同\n          base = int(Xp - 0.5)\t\t\t\t\t\t\t#与p2g相同\n          fx = Xp - base\t\t\t\t\t\t\t\t#与p2g相同\n          w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]\t#与p2g相同\n          new_v = ti.Vector.zero(float, 2)\t\t\t\t\t#定义新的速度，用来从网格收集\n          new_C = ti.Matrix.zero(float, 2, 2)\t\t\t\t\t#定义新的仿射速度，用来从网格收集\n          for i, j in ti.static(ti.ndrange(3, 3)):\t\t\t\t#遍历周围3*3所有网格\n              offset = ti.Vector([i, j])\t\t\t\t\t\t#网格偏移\n              dpos = (offset - fx) * dx\t\t\t\t\t\t#网格相对粒子位置\n              weight = w[i].x * w[j].y\t\t\t\t\t\t#网格权重\n              g_v = grid_v[base + offset]\t\t\t\t\t\t#获取网格速度\n              new_v += weight * g_v\t\t\t\t\t\t#将网格速度传输至粒子\n              new_C += 4 * weight * g_v.outer_product(dpos) / dx**2\t\t#将网格仿射速度传输至粒子\n          v[p] = new_v\t\t\t\t\t\t\t\t#更新粒子速度\n          x[p] += dt * v[p]\t\t\t\t\t\t\t#更新粒子位置\n          J[p] *= 1 + dt * new_C.trace()\t\t\t\t\t\t#更新粒子体积\n          C[p] = new_C\t\t\t\t\t\t\t\t#更新粒子仿射速度\n  ```\n  \n  通过以上这些部分即可求解无粘的自由面弱可压缩液体，我们可以看到比较真实的液体滴落效果\n  \n  ### 应力矩阵\n  \n  对于流体，与固体，其应力需要写为矩阵形式，在2维中为2 * 2矩阵，3维中为3 * 3矩阵对应了每个方向切面上的力矢量：\n  $$\n  σ_{ij}=\\lim_{A\\to 0} \\frac{ΔF_j}{ΔA_i}\n  $$\n  在流体（液体或者气体）中，压力（pressure）通常是其中最重要的力，而该力方向总是与切面方向相同，因此压力部分的应力矩阵形式很简单，如下为3维，二维形式：\n  $$\n  σ_p=\\begin{pmatrix}\n  p\u00260\u00260\\\\\n  0\u0026p\u00260\\\\\n  0\u00260\u0026p\\\\\n  \\end{pmatrix}\n  $$\n  在mpm88的p2g过程中可以看到该计算。\n  \n  关于应力矩阵带来的affine动量为什么是：\n  \n  $$\n  affine +=\\sigma*4*\\Delta t/dx^2\n  $$\n  \n  大家可以在胡老师的mls-mpm文章中看到：\n  \n  [A Moving Least Squares Material Point Method with Displacement Discontinuity and Two-Way Rigid Body Coupling | Yuanming Hu (taichi.graphics)](https://yuanming.taichi.graphics/publication/2018-mlsmpm/)\n\n## 粘性\n\n效果：\n\n\u003cimg src='mpm88_vis.gif'\u003e\n\n根据N-S方程：\n$$\nρ\\frac{D\\vec v}{Dt}=-\\nabla P+\\nabla\\cdot[μ(\\nabla \\vec v+(\\nabla \\vec v)^T-\\frac{2}{3}(\\nabla\\cdot \\vec v)I)]+\\nabla \\cdot[ζ(\\nabla\\cdot \\vec v)]+ρ\\vec g\n$$\n\n其中，等式左端为动量变化率，等式右端依次为\n$$\n压力项：-\\nabla P，剪切粘性项：\\nabla\\cdot[μ(\\nabla \\vec v+(\\nabla \\vec v)^T-\\frac{2}{3}(\\nabla\\cdot \\vec v)I)]，体积粘性项：\\nabla \\cdot[ζ(\\nabla\\cdot \\vec v)]，重力项ρ\\vec g\n$$\n对于mpm，重力项在grid上处理，弱可压缩流体计算粘性时可以忽略速度散度，因此在p2g过程中仅需要考虑粘性项与压力项，粘性应力矩阵可简化为：\n$$\nμ(\\nabla \\vec v+(\\nabla \\vec v)^T)\n$$\n考虑到mpm中C为速度梯度的近似，因此\n\n### p2g时affine动量的计算\n\n```python\n\tmu = 0.1  \t\t\t\t\t#粘度\n        stressMu = -(C[p] + C[p].transpose()) * mu  \t#粘性应力矩阵\n        stressMu *= dt * p_vol * 4 / dx**2\n        affine = ti.Matrix([[stress, 0], [0, stress]\n                            ]) + p_mass * C[p] + stressMu\n```\n\n为了保证收敛，需要将时间步长适当调小，从2e-4调整至1e-4：\n\n```python\ndt = 1e-4\n```\n\n即可完成粘性的添加。\n\n## 等温气体\n\n效果：\n\n\u003cimg src='mpm88_gas.gif'\u003e\n\n气体通常满足理想气体状态方程：\n$$\np=ρRT\n$$\n其中p为压力单位Pa，ρ为密度单位为kg/m3，T为热力学温度，单位为K（开尔文），R为气体常数，对空气约为287J/kg。\n\n对于可压缩气体，其密度不再是常数，而是会有较大幅度的变化，因此，其压力计算中不应再采用固定的粒子体积，而是采用粒子质量除以粒子密度得到粒子体积。\n\n因此其一个时间步内的affine动量计算可以写为：\n$$\n\\mathbf{I}_D\\cdot ρ_p\\cdot R\\cdot T_p\\cdot \\Delta t \\cdot 4\\cdot \\frac{p_{mass}}{ρ_p}/\\Delta x^2=\\mathbf{I}_D\\cdot R\\cdot T_p\\cdot \\Delta t\\cdot 4\\cdot p_{mass}/\\Delta x^2\n$$\n\n### 在p2g中\n\n```\n\tstress = p_mass * RT * dt * 4 / dx**2\n        affine = ti.Matrix([[stress, 0], [0, stress]]) + p_mass * C[p]\n```\n\n为了简化计算可以假设温度为常数，通常在压力变化不太大的情况下，这是符合假设的。而且不需要求解能量方程。此外，等温气体的声速为：\n$$\nc=\\sqrt{\\frac{dp}{dρ}}=\\sqrt{RT}\n$$\n通常对于流速小于0.3倍声速即Mach=v/c\u003c0.3时，流体的密度变化范围小于5%，此时弱可压缩可以近似不可压缩。\n\n可以看到气体膨胀并产生冲击波，但随着冲击波反射，气体逐渐趋于稳定，并填满整个空间。\n\n## 两相流\n\n效果：\n\n\u003cimg src='mpm88_2phase.gif'\u003e\n\n在两相流模拟中，可以定义一个粒子类型type_p，每个粒子存储一个粒子类型，为0则为气体，为1则为液体。为了保证粒子分布均匀，先进行一段时间的气体模拟，然后将对应部分的粒子更改为液体粒子进行计算。\n\n粒子的质量取决于类型，对于我们生活中常见的空气与水来说，密度分别为1.3kg/m3与1000kg/m3密度相差大约770倍，如果压力计算方法与气体相同，则水中声速下降为：\n$$\nc_{water}=c_{air}(\\frac{\\rho_{water}}{\\rho_{air}})^{-0.5}\n$$\n为了加快求解速度，以及增强不可压缩性，可以令液体的声速与气体相同，在液体中额外计算体积变化导致的压力，并保证声速相同即可。如上为气液密度比10，粘度0.1，声速20的仿真结果。\n\n但在上面可以看到，在气液交界面出明显出现了气液混合现象，这是因为没有添加表面张力，而表面张力是保证液体表面平滑的重要原因。\n\n## 表面张力\n\n在夏天荷叶上，我们经常能看到圆滚滚的水珠在荷叶上滚动，而促使水珠形成圆形的原因就是表面张力\n\n\u003cimg src='waterdrop.png'\u003e\n\n仿真效果：\n\n\u003cimg src='mpm88_2phasecss.gif'\u003e\n\n密度比为10，表面张力为1，空气声速为20，粘度为0.1的水滴落。\n\ncss模型：\n$$\nσ_{css}=-\\sigma \\left(|\\nabla \\alpha|\\mathbf{I}-\\nabla a \\otimes \\left(\\frac{\\nabla \\alpha}{|\\nabla \\alpha|}\\right)\\right)\n$$\n其中α为某种流体所占比例，σ为表面张力系数，对水在空气中为0.07N/m。在MPM中，可以在网格上存储流体比例，在粒子上存储流体比例梯度：\n\n```python\ngrid_alpha = ti.field(float, (n_grid, n_grid))#流体比例\ngrad_alpha = ti.Vector.field(2, float, n_particles)#流体比例梯度\n```\n\n### 在p2g时\n\n计算应力表面张力：\n\n```python\nif grad_alpha[p].norm()\u003e0:\n            alphanorm=grad_alpha[p].normalized()\n            affine +=-dt * p_vol *sigma*4 * grad_alpha[p].norm()*(ti.Matrix.identity(ti.f32,2)-alphanorm.outer_product(alphanorm))/ dx**2\n        \n```\n\n并且将液体质量存储至网格：\n\n```python\n        for i, j in ti.static(ti.ndrange(3, 3)):\n            offset = ti.Vector([i, j])\n            dpos = (offset - fx) * dx\n            weight = w[i].x * w[j].y\n            grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos)\n            grid_m[base + offset] += weight * p_mass\n            grid_alpha[base + offset] += weight * p_mass *type_p[p]#存储液体质量\n```\n\n### 网格操作时\n\n将液体质量转换为液体质量分数：\n\n```python\n    for i, j in grid_m:\n        if grid_m[i, j] \u003e 0:\n            grid_v[i, j] /= grid_m[i, j]\n            grid_alpha[i, j] /= grid_m[i, j]#将液体质量转换为液体质量分数\n```\n\n### g2p时\n\n```python\n    for p in x:\n        Xp = x[p] / dx\n        base = int(Xp - 0.5)\n        fx = Xp - base\n        w = [0.5 * (1.5 - fx)**2, 0.75 - (fx - 1)**2, 0.5 * (fx - 0.5)**2]\n        new_v = ti.Vector.zero(float, 2)\n        new_alpha = ti.Vector.zero(float, 2)\n        new_C = ti.Matrix.zero(float, 2, 2)\n        for i, j in ti.static(ti.ndrange(3, 3)):\n            offset = ti.Vector([i, j])\n            dpos = (offset - fx) * dx\n            weight = w[i].x * w[j].y\n            g_v = grid_v[base + offset]\n            g_alpha = grid_alpha[base + offset]\n            new_v += weight * g_v\n            new_alpha += 4 * weight * g_alpha * dpos / dx**2\n            if use_C[None]:\n                new_C += 4 * weight * g_v.outer_product(dpos) / dx**2\n        v[p] = new_v\n        x[p] += dt * v[p]\n        J[p] *= 1 + dt * new_C.trace()\n        grad_alpha[p] = new_alpha\n        C[p] = new_C\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaichi-dev%2Fmls_mpm_88_extensions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaichi-dev%2Fmls_mpm_88_extensions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaichi-dev%2Fmls_mpm_88_extensions/lists"}