├── README.md ├── example ├── cavity │ ├── 0 │ │ ├── T │ │ ├── U │ │ └── p │ ├── constant │ │ ├── g │ │ ├── polyMesh │ │ │ ├── boundary │ │ │ ├── faces │ │ │ ├── neighbour │ │ │ ├── owner │ │ │ └── points │ │ └── transportProperties │ ├── convergence │ │ └── convergenceUp.out │ ├── foam.foam │ ├── pyFVMScript.py │ └── system │ │ ├── blockMeshDict │ │ ├── controlDict │ │ ├── fvSchemes │ │ └── fvSolution └── flange │ ├── 0 │ └── T │ ├── constant │ ├── polyMesh │ │ ├── boundary │ │ ├── faceZones │ │ ├── faces │ │ ├── neighbour │ │ ├── owner │ │ └── points │ └── transportProperties │ ├── convergence │ └── convergenceUp.out │ ├── foam.foam │ ├── pyFVMScript.py │ └── system │ ├── controlDict │ ├── fvSchemes │ └── fvSolution ├── src ├── __pycache__ │ └── config.cpython-310.pyc ├── cfdtool │ ├── IO.py │ ├── Interpolate.py │ ├── Math.py │ ├── Solve.py │ ├── Time.py │ ├── __pycache__ │ │ ├── IO.cpython-310.pyc │ │ ├── Interpolate.cpython-310.pyc │ │ ├── Math.cpython-310.pyc │ │ ├── Solve.cpython-310.pyc │ │ ├── Time.cpython-310.pyc │ │ ├── base.cpython-310.pyc │ │ ├── cfdPlot.cpython-310.pyc │ │ ├── dimensions.cpython-310.pyc │ │ └── quantities.cpython-310.pyc │ ├── base.py │ ├── cfdPlot.py │ ├── decorators.py │ ├── dimensions.py │ └── quantities.py ├── config.py └── pyFVM │ ├── Assemble.py │ ├── Coefficients.py │ ├── Equation.py │ ├── Field.py │ ├── Fluxes.py │ ├── FoamDictionaries.py │ ├── Gradient.py │ ├── Model.py │ ├── Polymesh.py │ ├── Region.py │ ├── __pycache__ │ ├── Assemble.cpython-310.pyc │ ├── Coefficients.cpython-310.pyc │ ├── Equation.cpython-310.pyc │ ├── Field.cpython-310.pyc │ ├── Fluxes.cpython-310.pyc │ ├── FoamDictionaries.cpython-310.pyc │ ├── Gradient.cpython-310.pyc │ ├── Model.cpython-310.pyc │ ├── Polymesh.cpython-310.pyc │ ├── Region.cpython-310.pyc │ ├── cfdGetTools.cpython-310.pyc │ └── cfdfunction.cpython-310.pyc │ ├── cfdGetTools.py │ └── cfdfunction.py └── test ├── PCG_big_test.py ├── PCG_test.py ├── Pint_test.py ├── Quantity_test.py └── test.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | # pyOpenFOAM: Python Finite Volume Method Solver Like OpenFoam 3 | 4 | pyOpenFOAM 是一个基于有限体积方法(FVM)的计算流体动力学(CFD)库,用于模拟流体流动和热传递。该库提供了一套完整的工具来处理网格拓扑关系、方程求解、边界条件设置和结果可视化。该求解器库采用OpenFOAM网格格式文件,主要离散格式和求解也参考了书籍《The Finite Volume Method in Computational Fluid Dynamics》。pyOpenFOAM目前完全基于Python平台开发,尽管也用到了Numpy库,但计算性能依然受限,亟待大家的共同开发和加入来提高程序的性能,但该代码库依然具有教育意义,对于从事CFD行业理解CFD数值离散以及从事科研学术依然具有重要意义。 5 | 6 | ## 主要特性 7 | 8 | - 支持多维网格和多种边界条件 9 | - 多种求解器选项(待验证准确性),包括AMG(代数多重网格法)、PCG(带预分解器共轭梯度法)、ILU(不完全LU分解)和SOR(高斯赛德尔迭代法) 10 | - 稳态和瞬态模拟支持 11 | - 丰富的插值和残差计算工具 12 | - 支持自定义方程和系数设置 13 | - 本项目实现了一个量纲管理机制,用于在计算流体力学(CFD)模拟过程中自动检查和维护物理量的量纲一致性。通过集成该机制,可以确保各种物理运算(如加减乘除)中的量纲关系正确,提升代码的健壮性和可靠性。 14 | 15 | ## 安装 16 | 17 | 可以通过以下方式安装 pyOpenFOAM: 18 | 19 | ```bash 20 | git clone https://github.com/sherlockyzd/pyOpenFOAM.git 21 | cd pyOpenFOAM 22 | ``` 23 | 24 | ## 使用方法 25 | 26 | 1. **初始化模拟区域**: 创建 `Region` 对象,设置案例路径。 27 | 28 | ```python 29 | from pyFVM.Region import Region 30 | 31 | case_path = "/path/to/case" 32 | cfd_case = Region(case_path) 33 | ``` 34 | 35 | 2. **运行案例**: 调用 `RunCase` 方法启动模拟。 36 | 37 | ```python 38 | cfd_case.RunCase() 39 | ``` 40 | 41 | 3. **结果可视化**: 使用内置的可视化工具或导出数据到ParaView。 42 | 43 | ## 文件结构及调用关系 44 | ### 1. pyFVMScript.py 45 | - **角色**: 主执行脚本。 46 | - **功能**: 创建 `Region` 实例并运行案例。 47 | - **调用**: 48 | - `Region.Region(os.getcwd())`: 创建区域实例。 49 | - `cfdCase.RunCase()`: 运行案例。 50 | 51 | ### 2. Region.py 52 | - **角色**: 模拟区域类。 53 | - **功能**: 管理整个CFD模拟案例。 54 | - **调用**: 55 | - `Polymesh.Polymesh(self)`: 创建网格实例。 56 | - `FoamDictionaries.FoamDictionaries(self)`: 创建字典实例。 57 | - `Model.Model(self)`: 创建模型实例。 58 | - `Time.Time(self)`: 创建时间实例。 59 | - `Coefficients.Coefficients(self)`: 创建系数实例。 60 | - `Fluxes.Fluxes(self)`: 创建通量实例。 61 | - `Assemble.Assemble(self, iTerm)`: 创建组装实例。 62 | - `Solve.cfdSolveEquation(self, theEquationName, iComponent)`: 求解方程。 63 | 64 | ### 3. Polymesh.py 65 | - **角色**: 网格类。 66 | - **功能**: 处理网格相关操作。 67 | - **调用**: 68 | - `self.cfdReadPointsFile()`: 读取点文件。 69 | - `self.cfdReadFacesFile()`: 读取面文件。 70 | - `self.cfdReadOwnerFile()`: 读取所有者文件。 71 | - `self.cfdReadNeighbourFile()`: 读取邻居文件。 72 | - `self.cfdReadBoundaryFile()`: 读取边界文件。 73 | 74 | ### 4. FoamDictionaries.py 75 | - **角色**: 字典类。 76 | - **功能**: 读取和操作OpenFOAM字典文件。 77 | - **调用**: 78 | - `self.cfdReadControlDictFile()`: 读取控制字典文件。 79 | - `self.cfdReadFvSchemesFile()`: 读取有限体积方案文件。 80 | - `self.cfdReadFvSolutionFile()`: 读取有限体积解决方案文件。 81 | - `self.cfdReadGravity()`: 读取重力文件。 82 | - `self.cfdReadTurbulenceProperties()`: 读取湍流属性文件。 83 | 84 | ### 5. Model.py 85 | - **角色**: 模型类。 86 | - **功能**: 定义CFD模拟的模型。 87 | - **调用**: 88 | - `Equation.Equation(fieldName)`: 创建方程实例。 89 | - `self.DefineMomentumEquation()`: 定义动量方程。 90 | - `self.DefineContinuityEquation()`: 定义连续性方程。 91 | - `self.DefineEnergyEquation()`: 定义能量方程。 92 | 93 | ### 6. Equation.py 94 | - **角色**: 方程类。 95 | - **功能**: 表示CFD模拟中的一个方程。 96 | - **调用**: 97 | - `self.initializeResiduals()`: 初始化残差。 98 | - `self.setTerms(terms)`: 设置方程项。 99 | 100 | ### 7. Coefficients.py 101 | - **角色**: 系数类。 102 | - **功能**: 设置求解方程组所需的系数。 103 | - **调用**: 104 | - `self.setupCoefficients()`: 设置系数数组。 105 | 106 | ### 8. Fluxes.py 107 | - **角色**: 通量类。 108 | - **功能**: 管理CFD模拟中的通量计算。 109 | - **调用**: 110 | - `self.cfdZeroCoefficients()`: 将系数数组置零。 111 | 112 | ### 9. Assemble.py 113 | - **角色**: 组装类。 114 | - **功能**: 组装CFD模拟中的方程。 115 | - **调用**: 116 | - `self.cfdAssembleEquation()`: 组装方程。 117 | 118 | ### 10. Solve.py 119 | - **角色**: 求解器类。 120 | - **功能**: 求解方程。 121 | - **调用**: 122 | - `self.cfdSolveEquation()`: 求解方程。 123 | 124 | ### 11. Field.py 125 | - **角色**: 场类。 126 | - **功能**: 创建和初始化CFD场数据结构。 127 | - **调用**: 128 | - `self.cfdUpdateScale()`: 更新场的比例尺。 129 | 130 | ### 12. Gradient.py 131 | - **角色**: 梯度类。 132 | - **功能**: 计算指定场的梯度。 133 | - **调用**: 134 | - `self.cfdComputeGradientGaussLinear0()`: 使用高斯线性方法计算梯度。 135 | 136 | ### 13. cfdGetTools.py 137 | - **角色**: 工具函数类。 138 | - **功能**: 提供辅助函数,如插值、残差计算等。 139 | 140 | ### 14. Interpolate.py 141 | - **角色**: 插值函数类。 142 | - **功能**: 提供插值函数,用于场数据的插值计算。 143 | 144 | ### 15. Math.py 145 | - **角色**: 数学函数类。 146 | - **功能**: 提供数学工具,如向量运算、单位向量计算等。 147 | 148 | ### 16. IO.py 149 | - **角色**: 输入输出类。 150 | - **功能**: 提供输入输出工具,用于文件读写和错误处理。 151 | 152 | ## 贡献 153 | 154 | 我们欢迎任何形式的贡献,包括代码、文档、测试和反馈。请通过GitHub提交Pull Requests。 155 | 156 | ## 许可 157 | 158 | pyFVM 是开源软件,采用 [MIT 许可证](LICENSE)。 159 | 160 | ## 联系方式 161 | 邮箱:yuzd17@tsinghua.org.cn 162 | 如有任何问题或建议,也欢迎通过GitHub Issues与我联系。 163 | 164 | ## code structure tree 165 | ``` 166 | pyOpenFOAM 167 | ├─ example 168 | │ ├─ cavity 169 | │ │ ├─ 0 170 | │ │ │ ├─ p 171 | │ │ │ ├─ T 172 | │ │ │ └─ U 173 | │ │ ├─ constant 174 | │ │ │ ├─ g 175 | │ │ │ ├─ polyMesh 176 | │ │ │ │ ├─ boundary 177 | │ │ │ │ ├─ faces 178 | │ │ │ │ ├─ neighbour 179 | │ │ │ │ ├─ owner 180 | │ │ │ │ └─ points 181 | │ │ │ └─ transportProperties 182 | │ │ ├─ convergence 183 | │ │ │ └─ convergenceUp.out 184 | │ │ ├─ foam.foam 185 | │ │ ├─ pyFVMScript.py 186 | │ │ └─ system 187 | │ │ ├─ blockMeshDict 188 | │ │ ├─ controlDict 189 | │ │ ├─ fvSchemes 190 | │ │ └─ fvSolution 191 | │ └─ flange 192 | │ ├─ 0 193 | │ │ └─ T 194 | │ ├─ constant 195 | │ │ ├─ polyMesh 196 | │ │ │ ├─ boundary 197 | │ │ │ ├─ faces 198 | │ │ │ ├─ faceZones 199 | │ │ │ ├─ neighbour 200 | │ │ │ ├─ owner 201 | │ │ │ └─ points 202 | │ │ └─ transportProperties 203 | │ ├─ convergence 204 | │ │ └─ convergenceUp.out 205 | │ ├─ foam.foam 206 | │ ├─ pyFVMScript.py 207 | │ └─ system 208 | │ ├─ controlDict 209 | │ ├─ fvSchemes 210 | │ └─ fvSolution 211 | ├─ README.md 212 | ├─ src 213 | │ ├─ cfdtool 214 | │ │ ├─ base.py 215 | │ │ ├─ cfdPlot.py 216 | │ │ ├─ decorators.py 217 | │ │ ├─ dimensions.py 218 | │ │ ├─ Interpolate.py 219 | │ │ ├─ IO.py 220 | │ │ ├─ Math.py 221 | │ │ ├─ quantities.py 222 | │ │ ├─ Solve.py 223 | │ │ ├─ Time.py 224 | │ ├─ config.py 225 | │ ├─ pyFVM 226 | │ │ ├─ Assemble.py 227 | │ │ ├─ cfdfunction.py 228 | │ │ ├─ cfdGetTools.py 229 | │ │ ├─ Coefficients.py 230 | │ │ ├─ Equation.py 231 | │ │ ├─ Field.py 232 | │ │ ├─ Fluxes.py 233 | │ │ ├─ FoamDictionaries.py 234 | │ │ ├─ Gradient.py 235 | │ │ ├─ Model.py 236 | │ │ ├─ Polymesh.py 237 | │ │ ├─ Region.py 238 | └─ test 239 | ├─ Quantity_test.py 240 | └─ test.py 241 | ``` -------------------------------------------------------------------------------- /example/cavity/0/T: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class volScalarField; 13 | object p; 14 | } 15 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 16 | 17 | dimensions [0 0 0 1 0 0 0]; 18 | 19 | internalField uniform 300; 20 | 21 | boundaryField 22 | { 23 | movingWall 24 | { 25 | type fixedValue; 26 | value uniform 350; 27 | } 28 | 29 | fixedWalls 30 | { 31 | type fixedValue; 32 | value uniform 300; 33 | } 34 | 35 | frontAndBack 36 | { 37 | type empty; 38 | } 39 | } 40 | 41 | // ************************************************************************* // 42 | -------------------------------------------------------------------------------- /example/cavity/0/U: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class volVectorField; 13 | object U; 14 | } 15 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 16 | 17 | dimensions [0 1 -1 0 0 0 0]; 18 | 19 | internalField uniform (0 0 0); 20 | 21 | boundaryField 22 | { 23 | movingWall 24 | { 25 | type fixedValue; 26 | value uniform (1 0 0); 27 | } 28 | 29 | fixedWalls 30 | { 31 | type noSlip; 32 | } 33 | 34 | frontAndBack 35 | { 36 | type empty; 37 | } 38 | } 39 | 40 | // ************************************************************************* // 41 | -------------------------------------------------------------------------------- /example/cavity/0/p: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class volScalarField; 13 | object p; 14 | } 15 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 16 | 17 | dimensions [0 2 -2 0 0 0 0]; 18 | 19 | internalField uniform 0; 20 | 21 | boundaryField 22 | { 23 | movingWall 24 | { 25 | type zeroGradient; 26 | } 27 | 28 | fixedWalls 29 | { 30 | type zeroGradient; 31 | } 32 | 33 | frontAndBack 34 | { 35 | type empty; 36 | } 37 | } 38 | 39 | // ************************************************************************* // 40 | -------------------------------------------------------------------------------- /example/cavity/constant/g: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \ / O peration | Version: 4.1 | 5 | | \ / A nd | Web: www.OpenFOAM.org | 6 | | \/ M anipulation | | 7 | *---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class uniformDimensionedVectorField; 13 | location "constant"; 14 | object g; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | dimensions [0 1 -2 0 0 0 0]; 19 | value (0 -9.81 0); 20 | 21 | 22 | // ************************************************************************* // -------------------------------------------------------------------------------- /example/cavity/constant/polyMesh/boundary: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5.x | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class polyBoundaryMesh; 13 | location "constant/polyMesh"; 14 | object boundary; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | 3 19 | ( 20 | movingWall 21 | { 22 | type wall; 23 | inGroups 1(wall); 24 | nFaces 20; 25 | startFace 760; 26 | } 27 | fixedWalls 28 | { 29 | type wall; 30 | inGroups 1(wall); 31 | nFaces 60; 32 | startFace 780; 33 | } 34 | frontAndBack 35 | { 36 | type empty; 37 | inGroups 1(empty); 38 | nFaces 800; 39 | startFace 840; 40 | } 41 | ) 42 | 43 | // ************************************************************************* // 44 | -------------------------------------------------------------------------------- /example/cavity/constant/polyMesh/neighbour: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5.x | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class labelList; 13 | note "nPoints: 882 nCells: 400 nFaces: 1640 nInternalFaces: 760"; 14 | location "constant/polyMesh"; 15 | object neighbour; 16 | } 17 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 18 | 19 | 20 | 760 21 | ( 22 | 1 23 | 20 24 | 2 25 | 21 26 | 3 27 | 22 28 | 4 29 | 23 30 | 5 31 | 24 32 | 6 33 | 25 34 | 7 35 | 26 36 | 8 37 | 27 38 | 9 39 | 28 40 | 10 41 | 29 42 | 11 43 | 30 44 | 12 45 | 31 46 | 13 47 | 32 48 | 14 49 | 33 50 | 15 51 | 34 52 | 16 53 | 35 54 | 17 55 | 36 56 | 18 57 | 37 58 | 19 59 | 38 60 | 39 61 | 21 62 | 40 63 | 22 64 | 41 65 | 23 66 | 42 67 | 24 68 | 43 69 | 25 70 | 44 71 | 26 72 | 45 73 | 27 74 | 46 75 | 28 76 | 47 77 | 29 78 | 48 79 | 30 80 | 49 81 | 31 82 | 50 83 | 32 84 | 51 85 | 33 86 | 52 87 | 34 88 | 53 89 | 35 90 | 54 91 | 36 92 | 55 93 | 37 94 | 56 95 | 38 96 | 57 97 | 39 98 | 58 99 | 59 100 | 41 101 | 60 102 | 42 103 | 61 104 | 43 105 | 62 106 | 44 107 | 63 108 | 45 109 | 64 110 | 46 111 | 65 112 | 47 113 | 66 114 | 48 115 | 67 116 | 49 117 | 68 118 | 50 119 | 69 120 | 51 121 | 70 122 | 52 123 | 71 124 | 53 125 | 72 126 | 54 127 | 73 128 | 55 129 | 74 130 | 56 131 | 75 132 | 57 133 | 76 134 | 58 135 | 77 136 | 59 137 | 78 138 | 79 139 | 61 140 | 80 141 | 62 142 | 81 143 | 63 144 | 82 145 | 64 146 | 83 147 | 65 148 | 84 149 | 66 150 | 85 151 | 67 152 | 86 153 | 68 154 | 87 155 | 69 156 | 88 157 | 70 158 | 89 159 | 71 160 | 90 161 | 72 162 | 91 163 | 73 164 | 92 165 | 74 166 | 93 167 | 75 168 | 94 169 | 76 170 | 95 171 | 77 172 | 96 173 | 78 174 | 97 175 | 79 176 | 98 177 | 99 178 | 81 179 | 100 180 | 82 181 | 101 182 | 83 183 | 102 184 | 84 185 | 103 186 | 85 187 | 104 188 | 86 189 | 105 190 | 87 191 | 106 192 | 88 193 | 107 194 | 89 195 | 108 196 | 90 197 | 109 198 | 91 199 | 110 200 | 92 201 | 111 202 | 93 203 | 112 204 | 94 205 | 113 206 | 95 207 | 114 208 | 96 209 | 115 210 | 97 211 | 116 212 | 98 213 | 117 214 | 99 215 | 118 216 | 119 217 | 101 218 | 120 219 | 102 220 | 121 221 | 103 222 | 122 223 | 104 224 | 123 225 | 105 226 | 124 227 | 106 228 | 125 229 | 107 230 | 126 231 | 108 232 | 127 233 | 109 234 | 128 235 | 110 236 | 129 237 | 111 238 | 130 239 | 112 240 | 131 241 | 113 242 | 132 243 | 114 244 | 133 245 | 115 246 | 134 247 | 116 248 | 135 249 | 117 250 | 136 251 | 118 252 | 137 253 | 119 254 | 138 255 | 139 256 | 121 257 | 140 258 | 122 259 | 141 260 | 123 261 | 142 262 | 124 263 | 143 264 | 125 265 | 144 266 | 126 267 | 145 268 | 127 269 | 146 270 | 128 271 | 147 272 | 129 273 | 148 274 | 130 275 | 149 276 | 131 277 | 150 278 | 132 279 | 151 280 | 133 281 | 152 282 | 134 283 | 153 284 | 135 285 | 154 286 | 136 287 | 155 288 | 137 289 | 156 290 | 138 291 | 157 292 | 139 293 | 158 294 | 159 295 | 141 296 | 160 297 | 142 298 | 161 299 | 143 300 | 162 301 | 144 302 | 163 303 | 145 304 | 164 305 | 146 306 | 165 307 | 147 308 | 166 309 | 148 310 | 167 311 | 149 312 | 168 313 | 150 314 | 169 315 | 151 316 | 170 317 | 152 318 | 171 319 | 153 320 | 172 321 | 154 322 | 173 323 | 155 324 | 174 325 | 156 326 | 175 327 | 157 328 | 176 329 | 158 330 | 177 331 | 159 332 | 178 333 | 179 334 | 161 335 | 180 336 | 162 337 | 181 338 | 163 339 | 182 340 | 164 341 | 183 342 | 165 343 | 184 344 | 166 345 | 185 346 | 167 347 | 186 348 | 168 349 | 187 350 | 169 351 | 188 352 | 170 353 | 189 354 | 171 355 | 190 356 | 172 357 | 191 358 | 173 359 | 192 360 | 174 361 | 193 362 | 175 363 | 194 364 | 176 365 | 195 366 | 177 367 | 196 368 | 178 369 | 197 370 | 179 371 | 198 372 | 199 373 | 181 374 | 200 375 | 182 376 | 201 377 | 183 378 | 202 379 | 184 380 | 203 381 | 185 382 | 204 383 | 186 384 | 205 385 | 187 386 | 206 387 | 188 388 | 207 389 | 189 390 | 208 391 | 190 392 | 209 393 | 191 394 | 210 395 | 192 396 | 211 397 | 193 398 | 212 399 | 194 400 | 213 401 | 195 402 | 214 403 | 196 404 | 215 405 | 197 406 | 216 407 | 198 408 | 217 409 | 199 410 | 218 411 | 219 412 | 201 413 | 220 414 | 202 415 | 221 416 | 203 417 | 222 418 | 204 419 | 223 420 | 205 421 | 224 422 | 206 423 | 225 424 | 207 425 | 226 426 | 208 427 | 227 428 | 209 429 | 228 430 | 210 431 | 229 432 | 211 433 | 230 434 | 212 435 | 231 436 | 213 437 | 232 438 | 214 439 | 233 440 | 215 441 | 234 442 | 216 443 | 235 444 | 217 445 | 236 446 | 218 447 | 237 448 | 219 449 | 238 450 | 239 451 | 221 452 | 240 453 | 222 454 | 241 455 | 223 456 | 242 457 | 224 458 | 243 459 | 225 460 | 244 461 | 226 462 | 245 463 | 227 464 | 246 465 | 228 466 | 247 467 | 229 468 | 248 469 | 230 470 | 249 471 | 231 472 | 250 473 | 232 474 | 251 475 | 233 476 | 252 477 | 234 478 | 253 479 | 235 480 | 254 481 | 236 482 | 255 483 | 237 484 | 256 485 | 238 486 | 257 487 | 239 488 | 258 489 | 259 490 | 241 491 | 260 492 | 242 493 | 261 494 | 243 495 | 262 496 | 244 497 | 263 498 | 245 499 | 264 500 | 246 501 | 265 502 | 247 503 | 266 504 | 248 505 | 267 506 | 249 507 | 268 508 | 250 509 | 269 510 | 251 511 | 270 512 | 252 513 | 271 514 | 253 515 | 272 516 | 254 517 | 273 518 | 255 519 | 274 520 | 256 521 | 275 522 | 257 523 | 276 524 | 258 525 | 277 526 | 259 527 | 278 528 | 279 529 | 261 530 | 280 531 | 262 532 | 281 533 | 263 534 | 282 535 | 264 536 | 283 537 | 265 538 | 284 539 | 266 540 | 285 541 | 267 542 | 286 543 | 268 544 | 287 545 | 269 546 | 288 547 | 270 548 | 289 549 | 271 550 | 290 551 | 272 552 | 291 553 | 273 554 | 292 555 | 274 556 | 293 557 | 275 558 | 294 559 | 276 560 | 295 561 | 277 562 | 296 563 | 278 564 | 297 565 | 279 566 | 298 567 | 299 568 | 281 569 | 300 570 | 282 571 | 301 572 | 283 573 | 302 574 | 284 575 | 303 576 | 285 577 | 304 578 | 286 579 | 305 580 | 287 581 | 306 582 | 288 583 | 307 584 | 289 585 | 308 586 | 290 587 | 309 588 | 291 589 | 310 590 | 292 591 | 311 592 | 293 593 | 312 594 | 294 595 | 313 596 | 295 597 | 314 598 | 296 599 | 315 600 | 297 601 | 316 602 | 298 603 | 317 604 | 299 605 | 318 606 | 319 607 | 301 608 | 320 609 | 302 610 | 321 611 | 303 612 | 322 613 | 304 614 | 323 615 | 305 616 | 324 617 | 306 618 | 325 619 | 307 620 | 326 621 | 308 622 | 327 623 | 309 624 | 328 625 | 310 626 | 329 627 | 311 628 | 330 629 | 312 630 | 331 631 | 313 632 | 332 633 | 314 634 | 333 635 | 315 636 | 334 637 | 316 638 | 335 639 | 317 640 | 336 641 | 318 642 | 337 643 | 319 644 | 338 645 | 339 646 | 321 647 | 340 648 | 322 649 | 341 650 | 323 651 | 342 652 | 324 653 | 343 654 | 325 655 | 344 656 | 326 657 | 345 658 | 327 659 | 346 660 | 328 661 | 347 662 | 329 663 | 348 664 | 330 665 | 349 666 | 331 667 | 350 668 | 332 669 | 351 670 | 333 671 | 352 672 | 334 673 | 353 674 | 335 675 | 354 676 | 336 677 | 355 678 | 337 679 | 356 680 | 338 681 | 357 682 | 339 683 | 358 684 | 359 685 | 341 686 | 360 687 | 342 688 | 361 689 | 343 690 | 362 691 | 344 692 | 363 693 | 345 694 | 364 695 | 346 696 | 365 697 | 347 698 | 366 699 | 348 700 | 367 701 | 349 702 | 368 703 | 350 704 | 369 705 | 351 706 | 370 707 | 352 708 | 371 709 | 353 710 | 372 711 | 354 712 | 373 713 | 355 714 | 374 715 | 356 716 | 375 717 | 357 718 | 376 719 | 358 720 | 377 721 | 359 722 | 378 723 | 379 724 | 361 725 | 380 726 | 362 727 | 381 728 | 363 729 | 382 730 | 364 731 | 383 732 | 365 733 | 384 734 | 366 735 | 385 736 | 367 737 | 386 738 | 368 739 | 387 740 | 369 741 | 388 742 | 370 743 | 389 744 | 371 745 | 390 746 | 372 747 | 391 748 | 373 749 | 392 750 | 374 751 | 393 752 | 375 753 | 394 754 | 376 755 | 395 756 | 377 757 | 396 758 | 378 759 | 397 760 | 379 761 | 398 762 | 399 763 | 381 764 | 382 765 | 383 766 | 384 767 | 385 768 | 386 769 | 387 770 | 388 771 | 389 772 | 390 773 | 391 774 | 392 775 | 393 776 | 394 777 | 395 778 | 396 779 | 397 780 | 398 781 | 399 782 | ) 783 | 784 | 785 | // ************************************************************************* // 786 | -------------------------------------------------------------------------------- /example/cavity/constant/polyMesh/owner: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5.x | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class labelList; 13 | note "nPoints: 882 nCells: 400 nFaces: 1640 nInternalFaces: 760"; 14 | location "constant/polyMesh"; 15 | object owner; 16 | } 17 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 18 | 19 | 20 | 1640 21 | ( 22 | 0 23 | 0 24 | 1 25 | 1 26 | 2 27 | 2 28 | 3 29 | 3 30 | 4 31 | 4 32 | 5 33 | 5 34 | 6 35 | 6 36 | 7 37 | 7 38 | 8 39 | 8 40 | 9 41 | 9 42 | 10 43 | 10 44 | 11 45 | 11 46 | 12 47 | 12 48 | 13 49 | 13 50 | 14 51 | 14 52 | 15 53 | 15 54 | 16 55 | 16 56 | 17 57 | 17 58 | 18 59 | 18 60 | 19 61 | 20 62 | 20 63 | 21 64 | 21 65 | 22 66 | 22 67 | 23 68 | 23 69 | 24 70 | 24 71 | 25 72 | 25 73 | 26 74 | 26 75 | 27 76 | 27 77 | 28 78 | 28 79 | 29 80 | 29 81 | 30 82 | 30 83 | 31 84 | 31 85 | 32 86 | 32 87 | 33 88 | 33 89 | 34 90 | 34 91 | 35 92 | 35 93 | 36 94 | 36 95 | 37 96 | 37 97 | 38 98 | 38 99 | 39 100 | 40 101 | 40 102 | 41 103 | 41 104 | 42 105 | 42 106 | 43 107 | 43 108 | 44 109 | 44 110 | 45 111 | 45 112 | 46 113 | 46 114 | 47 115 | 47 116 | 48 117 | 48 118 | 49 119 | 49 120 | 50 121 | 50 122 | 51 123 | 51 124 | 52 125 | 52 126 | 53 127 | 53 128 | 54 129 | 54 130 | 55 131 | 55 132 | 56 133 | 56 134 | 57 135 | 57 136 | 58 137 | 58 138 | 59 139 | 60 140 | 60 141 | 61 142 | 61 143 | 62 144 | 62 145 | 63 146 | 63 147 | 64 148 | 64 149 | 65 150 | 65 151 | 66 152 | 66 153 | 67 154 | 67 155 | 68 156 | 68 157 | 69 158 | 69 159 | 70 160 | 70 161 | 71 162 | 71 163 | 72 164 | 72 165 | 73 166 | 73 167 | 74 168 | 74 169 | 75 170 | 75 171 | 76 172 | 76 173 | 77 174 | 77 175 | 78 176 | 78 177 | 79 178 | 80 179 | 80 180 | 81 181 | 81 182 | 82 183 | 82 184 | 83 185 | 83 186 | 84 187 | 84 188 | 85 189 | 85 190 | 86 191 | 86 192 | 87 193 | 87 194 | 88 195 | 88 196 | 89 197 | 89 198 | 90 199 | 90 200 | 91 201 | 91 202 | 92 203 | 92 204 | 93 205 | 93 206 | 94 207 | 94 208 | 95 209 | 95 210 | 96 211 | 96 212 | 97 213 | 97 214 | 98 215 | 98 216 | 99 217 | 100 218 | 100 219 | 101 220 | 101 221 | 102 222 | 102 223 | 103 224 | 103 225 | 104 226 | 104 227 | 105 228 | 105 229 | 106 230 | 106 231 | 107 232 | 107 233 | 108 234 | 108 235 | 109 236 | 109 237 | 110 238 | 110 239 | 111 240 | 111 241 | 112 242 | 112 243 | 113 244 | 113 245 | 114 246 | 114 247 | 115 248 | 115 249 | 116 250 | 116 251 | 117 252 | 117 253 | 118 254 | 118 255 | 119 256 | 120 257 | 120 258 | 121 259 | 121 260 | 122 261 | 122 262 | 123 263 | 123 264 | 124 265 | 124 266 | 125 267 | 125 268 | 126 269 | 126 270 | 127 271 | 127 272 | 128 273 | 128 274 | 129 275 | 129 276 | 130 277 | 130 278 | 131 279 | 131 280 | 132 281 | 132 282 | 133 283 | 133 284 | 134 285 | 134 286 | 135 287 | 135 288 | 136 289 | 136 290 | 137 291 | 137 292 | 138 293 | 138 294 | 139 295 | 140 296 | 140 297 | 141 298 | 141 299 | 142 300 | 142 301 | 143 302 | 143 303 | 144 304 | 144 305 | 145 306 | 145 307 | 146 308 | 146 309 | 147 310 | 147 311 | 148 312 | 148 313 | 149 314 | 149 315 | 150 316 | 150 317 | 151 318 | 151 319 | 152 320 | 152 321 | 153 322 | 153 323 | 154 324 | 154 325 | 155 326 | 155 327 | 156 328 | 156 329 | 157 330 | 157 331 | 158 332 | 158 333 | 159 334 | 160 335 | 160 336 | 161 337 | 161 338 | 162 339 | 162 340 | 163 341 | 163 342 | 164 343 | 164 344 | 165 345 | 165 346 | 166 347 | 166 348 | 167 349 | 167 350 | 168 351 | 168 352 | 169 353 | 169 354 | 170 355 | 170 356 | 171 357 | 171 358 | 172 359 | 172 360 | 173 361 | 173 362 | 174 363 | 174 364 | 175 365 | 175 366 | 176 367 | 176 368 | 177 369 | 177 370 | 178 371 | 178 372 | 179 373 | 180 374 | 180 375 | 181 376 | 181 377 | 182 378 | 182 379 | 183 380 | 183 381 | 184 382 | 184 383 | 185 384 | 185 385 | 186 386 | 186 387 | 187 388 | 187 389 | 188 390 | 188 391 | 189 392 | 189 393 | 190 394 | 190 395 | 191 396 | 191 397 | 192 398 | 192 399 | 193 400 | 193 401 | 194 402 | 194 403 | 195 404 | 195 405 | 196 406 | 196 407 | 197 408 | 197 409 | 198 410 | 198 411 | 199 412 | 200 413 | 200 414 | 201 415 | 201 416 | 202 417 | 202 418 | 203 419 | 203 420 | 204 421 | 204 422 | 205 423 | 205 424 | 206 425 | 206 426 | 207 427 | 207 428 | 208 429 | 208 430 | 209 431 | 209 432 | 210 433 | 210 434 | 211 435 | 211 436 | 212 437 | 212 438 | 213 439 | 213 440 | 214 441 | 214 442 | 215 443 | 215 444 | 216 445 | 216 446 | 217 447 | 217 448 | 218 449 | 218 450 | 219 451 | 220 452 | 220 453 | 221 454 | 221 455 | 222 456 | 222 457 | 223 458 | 223 459 | 224 460 | 224 461 | 225 462 | 225 463 | 226 464 | 226 465 | 227 466 | 227 467 | 228 468 | 228 469 | 229 470 | 229 471 | 230 472 | 230 473 | 231 474 | 231 475 | 232 476 | 232 477 | 233 478 | 233 479 | 234 480 | 234 481 | 235 482 | 235 483 | 236 484 | 236 485 | 237 486 | 237 487 | 238 488 | 238 489 | 239 490 | 240 491 | 240 492 | 241 493 | 241 494 | 242 495 | 242 496 | 243 497 | 243 498 | 244 499 | 244 500 | 245 501 | 245 502 | 246 503 | 246 504 | 247 505 | 247 506 | 248 507 | 248 508 | 249 509 | 249 510 | 250 511 | 250 512 | 251 513 | 251 514 | 252 515 | 252 516 | 253 517 | 253 518 | 254 519 | 254 520 | 255 521 | 255 522 | 256 523 | 256 524 | 257 525 | 257 526 | 258 527 | 258 528 | 259 529 | 260 530 | 260 531 | 261 532 | 261 533 | 262 534 | 262 535 | 263 536 | 263 537 | 264 538 | 264 539 | 265 540 | 265 541 | 266 542 | 266 543 | 267 544 | 267 545 | 268 546 | 268 547 | 269 548 | 269 549 | 270 550 | 270 551 | 271 552 | 271 553 | 272 554 | 272 555 | 273 556 | 273 557 | 274 558 | 274 559 | 275 560 | 275 561 | 276 562 | 276 563 | 277 564 | 277 565 | 278 566 | 278 567 | 279 568 | 280 569 | 280 570 | 281 571 | 281 572 | 282 573 | 282 574 | 283 575 | 283 576 | 284 577 | 284 578 | 285 579 | 285 580 | 286 581 | 286 582 | 287 583 | 287 584 | 288 585 | 288 586 | 289 587 | 289 588 | 290 589 | 290 590 | 291 591 | 291 592 | 292 593 | 292 594 | 293 595 | 293 596 | 294 597 | 294 598 | 295 599 | 295 600 | 296 601 | 296 602 | 297 603 | 297 604 | 298 605 | 298 606 | 299 607 | 300 608 | 300 609 | 301 610 | 301 611 | 302 612 | 302 613 | 303 614 | 303 615 | 304 616 | 304 617 | 305 618 | 305 619 | 306 620 | 306 621 | 307 622 | 307 623 | 308 624 | 308 625 | 309 626 | 309 627 | 310 628 | 310 629 | 311 630 | 311 631 | 312 632 | 312 633 | 313 634 | 313 635 | 314 636 | 314 637 | 315 638 | 315 639 | 316 640 | 316 641 | 317 642 | 317 643 | 318 644 | 318 645 | 319 646 | 320 647 | 320 648 | 321 649 | 321 650 | 322 651 | 322 652 | 323 653 | 323 654 | 324 655 | 324 656 | 325 657 | 325 658 | 326 659 | 326 660 | 327 661 | 327 662 | 328 663 | 328 664 | 329 665 | 329 666 | 330 667 | 330 668 | 331 669 | 331 670 | 332 671 | 332 672 | 333 673 | 333 674 | 334 675 | 334 676 | 335 677 | 335 678 | 336 679 | 336 680 | 337 681 | 337 682 | 338 683 | 338 684 | 339 685 | 340 686 | 340 687 | 341 688 | 341 689 | 342 690 | 342 691 | 343 692 | 343 693 | 344 694 | 344 695 | 345 696 | 345 697 | 346 698 | 346 699 | 347 700 | 347 701 | 348 702 | 348 703 | 349 704 | 349 705 | 350 706 | 350 707 | 351 708 | 351 709 | 352 710 | 352 711 | 353 712 | 353 713 | 354 714 | 354 715 | 355 716 | 355 717 | 356 718 | 356 719 | 357 720 | 357 721 | 358 722 | 358 723 | 359 724 | 360 725 | 360 726 | 361 727 | 361 728 | 362 729 | 362 730 | 363 731 | 363 732 | 364 733 | 364 734 | 365 735 | 365 736 | 366 737 | 366 738 | 367 739 | 367 740 | 368 741 | 368 742 | 369 743 | 369 744 | 370 745 | 370 746 | 371 747 | 371 748 | 372 749 | 372 750 | 373 751 | 373 752 | 374 753 | 374 754 | 375 755 | 375 756 | 376 757 | 376 758 | 377 759 | 377 760 | 378 761 | 378 762 | 379 763 | 380 764 | 381 765 | 382 766 | 383 767 | 384 768 | 385 769 | 386 770 | 387 771 | 388 772 | 389 773 | 390 774 | 391 775 | 392 776 | 393 777 | 394 778 | 395 779 | 396 780 | 397 781 | 398 782 | 380 783 | 381 784 | 382 785 | 383 786 | 384 787 | 385 788 | 386 789 | 387 790 | 388 791 | 389 792 | 390 793 | 391 794 | 392 795 | 393 796 | 394 797 | 395 798 | 396 799 | 397 800 | 398 801 | 399 802 | 0 803 | 20 804 | 40 805 | 60 806 | 80 807 | 100 808 | 120 809 | 140 810 | 160 811 | 180 812 | 200 813 | 220 814 | 240 815 | 260 816 | 280 817 | 300 818 | 320 819 | 340 820 | 360 821 | 380 822 | 19 823 | 39 824 | 59 825 | 79 826 | 99 827 | 119 828 | 139 829 | 159 830 | 179 831 | 199 832 | 219 833 | 239 834 | 259 835 | 279 836 | 299 837 | 319 838 | 339 839 | 359 840 | 379 841 | 399 842 | 0 843 | 1 844 | 2 845 | 3 846 | 4 847 | 5 848 | 6 849 | 7 850 | 8 851 | 9 852 | 10 853 | 11 854 | 12 855 | 13 856 | 14 857 | 15 858 | 16 859 | 17 860 | 18 861 | 19 862 | 0 863 | 20 864 | 40 865 | 60 866 | 80 867 | 100 868 | 120 869 | 140 870 | 160 871 | 180 872 | 200 873 | 220 874 | 240 875 | 260 876 | 280 877 | 300 878 | 320 879 | 340 880 | 360 881 | 380 882 | 1 883 | 21 884 | 41 885 | 61 886 | 81 887 | 101 888 | 121 889 | 141 890 | 161 891 | 181 892 | 201 893 | 221 894 | 241 895 | 261 896 | 281 897 | 301 898 | 321 899 | 341 900 | 361 901 | 381 902 | 2 903 | 22 904 | 42 905 | 62 906 | 82 907 | 102 908 | 122 909 | 142 910 | 162 911 | 182 912 | 202 913 | 222 914 | 242 915 | 262 916 | 282 917 | 302 918 | 322 919 | 342 920 | 362 921 | 382 922 | 3 923 | 23 924 | 43 925 | 63 926 | 83 927 | 103 928 | 123 929 | 143 930 | 163 931 | 183 932 | 203 933 | 223 934 | 243 935 | 263 936 | 283 937 | 303 938 | 323 939 | 343 940 | 363 941 | 383 942 | 4 943 | 24 944 | 44 945 | 64 946 | 84 947 | 104 948 | 124 949 | 144 950 | 164 951 | 184 952 | 204 953 | 224 954 | 244 955 | 264 956 | 284 957 | 304 958 | 324 959 | 344 960 | 364 961 | 384 962 | 5 963 | 25 964 | 45 965 | 65 966 | 85 967 | 105 968 | 125 969 | 145 970 | 165 971 | 185 972 | 205 973 | 225 974 | 245 975 | 265 976 | 285 977 | 305 978 | 325 979 | 345 980 | 365 981 | 385 982 | 6 983 | 26 984 | 46 985 | 66 986 | 86 987 | 106 988 | 126 989 | 146 990 | 166 991 | 186 992 | 206 993 | 226 994 | 246 995 | 266 996 | 286 997 | 306 998 | 326 999 | 346 1000 | 366 1001 | 386 1002 | 7 1003 | 27 1004 | 47 1005 | 67 1006 | 87 1007 | 107 1008 | 127 1009 | 147 1010 | 167 1011 | 187 1012 | 207 1013 | 227 1014 | 247 1015 | 267 1016 | 287 1017 | 307 1018 | 327 1019 | 347 1020 | 367 1021 | 387 1022 | 8 1023 | 28 1024 | 48 1025 | 68 1026 | 88 1027 | 108 1028 | 128 1029 | 148 1030 | 168 1031 | 188 1032 | 208 1033 | 228 1034 | 248 1035 | 268 1036 | 288 1037 | 308 1038 | 328 1039 | 348 1040 | 368 1041 | 388 1042 | 9 1043 | 29 1044 | 49 1045 | 69 1046 | 89 1047 | 109 1048 | 129 1049 | 149 1050 | 169 1051 | 189 1052 | 209 1053 | 229 1054 | 249 1055 | 269 1056 | 289 1057 | 309 1058 | 329 1059 | 349 1060 | 369 1061 | 389 1062 | 10 1063 | 30 1064 | 50 1065 | 70 1066 | 90 1067 | 110 1068 | 130 1069 | 150 1070 | 170 1071 | 190 1072 | 210 1073 | 230 1074 | 250 1075 | 270 1076 | 290 1077 | 310 1078 | 330 1079 | 350 1080 | 370 1081 | 390 1082 | 11 1083 | 31 1084 | 51 1085 | 71 1086 | 91 1087 | 111 1088 | 131 1089 | 151 1090 | 171 1091 | 191 1092 | 211 1093 | 231 1094 | 251 1095 | 271 1096 | 291 1097 | 311 1098 | 331 1099 | 351 1100 | 371 1101 | 391 1102 | 12 1103 | 32 1104 | 52 1105 | 72 1106 | 92 1107 | 112 1108 | 132 1109 | 152 1110 | 172 1111 | 192 1112 | 212 1113 | 232 1114 | 252 1115 | 272 1116 | 292 1117 | 312 1118 | 332 1119 | 352 1120 | 372 1121 | 392 1122 | 13 1123 | 33 1124 | 53 1125 | 73 1126 | 93 1127 | 113 1128 | 133 1129 | 153 1130 | 173 1131 | 193 1132 | 213 1133 | 233 1134 | 253 1135 | 273 1136 | 293 1137 | 313 1138 | 333 1139 | 353 1140 | 373 1141 | 393 1142 | 14 1143 | 34 1144 | 54 1145 | 74 1146 | 94 1147 | 114 1148 | 134 1149 | 154 1150 | 174 1151 | 194 1152 | 214 1153 | 234 1154 | 254 1155 | 274 1156 | 294 1157 | 314 1158 | 334 1159 | 354 1160 | 374 1161 | 394 1162 | 15 1163 | 35 1164 | 55 1165 | 75 1166 | 95 1167 | 115 1168 | 135 1169 | 155 1170 | 175 1171 | 195 1172 | 215 1173 | 235 1174 | 255 1175 | 275 1176 | 295 1177 | 315 1178 | 335 1179 | 355 1180 | 375 1181 | 395 1182 | 16 1183 | 36 1184 | 56 1185 | 76 1186 | 96 1187 | 116 1188 | 136 1189 | 156 1190 | 176 1191 | 196 1192 | 216 1193 | 236 1194 | 256 1195 | 276 1196 | 296 1197 | 316 1198 | 336 1199 | 356 1200 | 376 1201 | 396 1202 | 17 1203 | 37 1204 | 57 1205 | 77 1206 | 97 1207 | 117 1208 | 137 1209 | 157 1210 | 177 1211 | 197 1212 | 217 1213 | 237 1214 | 257 1215 | 277 1216 | 297 1217 | 317 1218 | 337 1219 | 357 1220 | 377 1221 | 397 1222 | 18 1223 | 38 1224 | 58 1225 | 78 1226 | 98 1227 | 118 1228 | 138 1229 | 158 1230 | 178 1231 | 198 1232 | 218 1233 | 238 1234 | 258 1235 | 278 1236 | 298 1237 | 318 1238 | 338 1239 | 358 1240 | 378 1241 | 398 1242 | 19 1243 | 39 1244 | 59 1245 | 79 1246 | 99 1247 | 119 1248 | 139 1249 | 159 1250 | 179 1251 | 199 1252 | 219 1253 | 239 1254 | 259 1255 | 279 1256 | 299 1257 | 319 1258 | 339 1259 | 359 1260 | 379 1261 | 399 1262 | 0 1263 | 20 1264 | 40 1265 | 60 1266 | 80 1267 | 100 1268 | 120 1269 | 140 1270 | 160 1271 | 180 1272 | 200 1273 | 220 1274 | 240 1275 | 260 1276 | 280 1277 | 300 1278 | 320 1279 | 340 1280 | 360 1281 | 380 1282 | 1 1283 | 21 1284 | 41 1285 | 61 1286 | 81 1287 | 101 1288 | 121 1289 | 141 1290 | 161 1291 | 181 1292 | 201 1293 | 221 1294 | 241 1295 | 261 1296 | 281 1297 | 301 1298 | 321 1299 | 341 1300 | 361 1301 | 381 1302 | 2 1303 | 22 1304 | 42 1305 | 62 1306 | 82 1307 | 102 1308 | 122 1309 | 142 1310 | 162 1311 | 182 1312 | 202 1313 | 222 1314 | 242 1315 | 262 1316 | 282 1317 | 302 1318 | 322 1319 | 342 1320 | 362 1321 | 382 1322 | 3 1323 | 23 1324 | 43 1325 | 63 1326 | 83 1327 | 103 1328 | 123 1329 | 143 1330 | 163 1331 | 183 1332 | 203 1333 | 223 1334 | 243 1335 | 263 1336 | 283 1337 | 303 1338 | 323 1339 | 343 1340 | 363 1341 | 383 1342 | 4 1343 | 24 1344 | 44 1345 | 64 1346 | 84 1347 | 104 1348 | 124 1349 | 144 1350 | 164 1351 | 184 1352 | 204 1353 | 224 1354 | 244 1355 | 264 1356 | 284 1357 | 304 1358 | 324 1359 | 344 1360 | 364 1361 | 384 1362 | 5 1363 | 25 1364 | 45 1365 | 65 1366 | 85 1367 | 105 1368 | 125 1369 | 145 1370 | 165 1371 | 185 1372 | 205 1373 | 225 1374 | 245 1375 | 265 1376 | 285 1377 | 305 1378 | 325 1379 | 345 1380 | 365 1381 | 385 1382 | 6 1383 | 26 1384 | 46 1385 | 66 1386 | 86 1387 | 106 1388 | 126 1389 | 146 1390 | 166 1391 | 186 1392 | 206 1393 | 226 1394 | 246 1395 | 266 1396 | 286 1397 | 306 1398 | 326 1399 | 346 1400 | 366 1401 | 386 1402 | 7 1403 | 27 1404 | 47 1405 | 67 1406 | 87 1407 | 107 1408 | 127 1409 | 147 1410 | 167 1411 | 187 1412 | 207 1413 | 227 1414 | 247 1415 | 267 1416 | 287 1417 | 307 1418 | 327 1419 | 347 1420 | 367 1421 | 387 1422 | 8 1423 | 28 1424 | 48 1425 | 68 1426 | 88 1427 | 108 1428 | 128 1429 | 148 1430 | 168 1431 | 188 1432 | 208 1433 | 228 1434 | 248 1435 | 268 1436 | 288 1437 | 308 1438 | 328 1439 | 348 1440 | 368 1441 | 388 1442 | 9 1443 | 29 1444 | 49 1445 | 69 1446 | 89 1447 | 109 1448 | 129 1449 | 149 1450 | 169 1451 | 189 1452 | 209 1453 | 229 1454 | 249 1455 | 269 1456 | 289 1457 | 309 1458 | 329 1459 | 349 1460 | 369 1461 | 389 1462 | 10 1463 | 30 1464 | 50 1465 | 70 1466 | 90 1467 | 110 1468 | 130 1469 | 150 1470 | 170 1471 | 190 1472 | 210 1473 | 230 1474 | 250 1475 | 270 1476 | 290 1477 | 310 1478 | 330 1479 | 350 1480 | 370 1481 | 390 1482 | 11 1483 | 31 1484 | 51 1485 | 71 1486 | 91 1487 | 111 1488 | 131 1489 | 151 1490 | 171 1491 | 191 1492 | 211 1493 | 231 1494 | 251 1495 | 271 1496 | 291 1497 | 311 1498 | 331 1499 | 351 1500 | 371 1501 | 391 1502 | 12 1503 | 32 1504 | 52 1505 | 72 1506 | 92 1507 | 112 1508 | 132 1509 | 152 1510 | 172 1511 | 192 1512 | 212 1513 | 232 1514 | 252 1515 | 272 1516 | 292 1517 | 312 1518 | 332 1519 | 352 1520 | 372 1521 | 392 1522 | 13 1523 | 33 1524 | 53 1525 | 73 1526 | 93 1527 | 113 1528 | 133 1529 | 153 1530 | 173 1531 | 193 1532 | 213 1533 | 233 1534 | 253 1535 | 273 1536 | 293 1537 | 313 1538 | 333 1539 | 353 1540 | 373 1541 | 393 1542 | 14 1543 | 34 1544 | 54 1545 | 74 1546 | 94 1547 | 114 1548 | 134 1549 | 154 1550 | 174 1551 | 194 1552 | 214 1553 | 234 1554 | 254 1555 | 274 1556 | 294 1557 | 314 1558 | 334 1559 | 354 1560 | 374 1561 | 394 1562 | 15 1563 | 35 1564 | 55 1565 | 75 1566 | 95 1567 | 115 1568 | 135 1569 | 155 1570 | 175 1571 | 195 1572 | 215 1573 | 235 1574 | 255 1575 | 275 1576 | 295 1577 | 315 1578 | 335 1579 | 355 1580 | 375 1581 | 395 1582 | 16 1583 | 36 1584 | 56 1585 | 76 1586 | 96 1587 | 116 1588 | 136 1589 | 156 1590 | 176 1591 | 196 1592 | 216 1593 | 236 1594 | 256 1595 | 276 1596 | 296 1597 | 316 1598 | 336 1599 | 356 1600 | 376 1601 | 396 1602 | 17 1603 | 37 1604 | 57 1605 | 77 1606 | 97 1607 | 117 1608 | 137 1609 | 157 1610 | 177 1611 | 197 1612 | 217 1613 | 237 1614 | 257 1615 | 277 1616 | 297 1617 | 317 1618 | 337 1619 | 357 1620 | 377 1621 | 397 1622 | 18 1623 | 38 1624 | 58 1625 | 78 1626 | 98 1627 | 118 1628 | 138 1629 | 158 1630 | 178 1631 | 198 1632 | 218 1633 | 238 1634 | 258 1635 | 278 1636 | 298 1637 | 318 1638 | 338 1639 | 358 1640 | 378 1641 | 398 1642 | 19 1643 | 39 1644 | 59 1645 | 79 1646 | 99 1647 | 119 1648 | 139 1649 | 159 1650 | 179 1651 | 199 1652 | 219 1653 | 239 1654 | 259 1655 | 279 1656 | 299 1657 | 319 1658 | 339 1659 | 359 1660 | 379 1661 | 399 1662 | ) 1663 | 1664 | 1665 | // ************************************************************************* // 1666 | -------------------------------------------------------------------------------- /example/cavity/constant/polyMesh/points: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5.x | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class vectorField; 13 | location "constant/polyMesh"; 14 | object points; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | 19 | 882 20 | ( 21 | (0 0 0) 22 | (0.005 0 0) 23 | (0.01 0 0) 24 | (0.015 0 0) 25 | (0.02 0 0) 26 | (0.025 0 0) 27 | (0.03 0 0) 28 | (0.035 0 0) 29 | (0.04 0 0) 30 | (0.045 0 0) 31 | (0.05 0 0) 32 | (0.055 0 0) 33 | (0.06 0 0) 34 | (0.065 0 0) 35 | (0.07 0 0) 36 | (0.075 0 0) 37 | (0.08 0 0) 38 | (0.085 0 0) 39 | (0.09 0 0) 40 | (0.095 0 0) 41 | (0.1 0 0) 42 | (0 0.005 0) 43 | (0.005 0.005 0) 44 | (0.01 0.005 0) 45 | (0.015 0.005 0) 46 | (0.02 0.005 0) 47 | (0.025 0.005 0) 48 | (0.03 0.005 0) 49 | (0.035 0.005 0) 50 | (0.04 0.005 0) 51 | (0.045 0.005 0) 52 | (0.05 0.005 0) 53 | (0.055 0.005 0) 54 | (0.06 0.005 0) 55 | (0.065 0.005 0) 56 | (0.07 0.005 0) 57 | (0.075 0.005 0) 58 | (0.08 0.005 0) 59 | (0.085 0.005 0) 60 | (0.09 0.005 0) 61 | (0.095 0.005 0) 62 | (0.1 0.005 0) 63 | (0 0.01 0) 64 | (0.005 0.01 0) 65 | (0.01 0.01 0) 66 | (0.015 0.01 0) 67 | (0.02 0.01 0) 68 | (0.025 0.01 0) 69 | (0.03 0.01 0) 70 | (0.035 0.01 0) 71 | (0.04 0.01 0) 72 | (0.045 0.01 0) 73 | (0.05 0.01 0) 74 | (0.055 0.01 0) 75 | (0.06 0.01 0) 76 | (0.065 0.01 0) 77 | (0.07 0.01 0) 78 | (0.075 0.01 0) 79 | (0.08 0.01 0) 80 | (0.085 0.01 0) 81 | (0.09 0.01 0) 82 | (0.095 0.01 0) 83 | (0.1 0.01 0) 84 | (0 0.015 0) 85 | (0.005 0.015 0) 86 | (0.01 0.015 0) 87 | (0.015 0.015 0) 88 | (0.02 0.015 0) 89 | (0.025 0.015 0) 90 | (0.03 0.015 0) 91 | (0.035 0.015 0) 92 | (0.04 0.015 0) 93 | (0.045 0.015 0) 94 | (0.05 0.015 0) 95 | (0.055 0.015 0) 96 | (0.06 0.015 0) 97 | (0.065 0.015 0) 98 | (0.07 0.015 0) 99 | (0.075 0.015 0) 100 | (0.08 0.015 0) 101 | (0.085 0.015 0) 102 | (0.09 0.015 0) 103 | (0.095 0.015 0) 104 | (0.1 0.015 0) 105 | (0 0.02 0) 106 | (0.005 0.02 0) 107 | (0.01 0.02 0) 108 | (0.015 0.02 0) 109 | (0.02 0.02 0) 110 | (0.025 0.02 0) 111 | (0.03 0.02 0) 112 | (0.035 0.02 0) 113 | (0.04 0.02 0) 114 | (0.045 0.02 0) 115 | (0.05 0.02 0) 116 | (0.055 0.02 0) 117 | (0.06 0.02 0) 118 | (0.065 0.02 0) 119 | (0.07 0.02 0) 120 | (0.075 0.02 0) 121 | (0.08 0.02 0) 122 | (0.085 0.02 0) 123 | (0.09 0.02 0) 124 | (0.095 0.02 0) 125 | (0.1 0.02 0) 126 | (0 0.025 0) 127 | (0.005 0.025 0) 128 | (0.01 0.025 0) 129 | (0.015 0.025 0) 130 | (0.02 0.025 0) 131 | (0.025 0.025 0) 132 | (0.03 0.025 0) 133 | (0.035 0.025 0) 134 | (0.04 0.025 0) 135 | (0.045 0.025 0) 136 | (0.05 0.025 0) 137 | (0.055 0.025 0) 138 | (0.06 0.025 0) 139 | (0.065 0.025 0) 140 | (0.07 0.025 0) 141 | (0.075 0.025 0) 142 | (0.08 0.025 0) 143 | (0.085 0.025 0) 144 | (0.09 0.025 0) 145 | (0.095 0.025 0) 146 | (0.1 0.025 0) 147 | (0 0.03 0) 148 | (0.005 0.03 0) 149 | (0.01 0.03 0) 150 | (0.015 0.03 0) 151 | (0.02 0.03 0) 152 | (0.025 0.03 0) 153 | (0.03 0.03 0) 154 | (0.035 0.03 0) 155 | (0.04 0.03 0) 156 | (0.045 0.03 0) 157 | (0.05 0.03 0) 158 | (0.055 0.03 0) 159 | (0.06 0.03 0) 160 | (0.065 0.03 0) 161 | (0.07 0.03 0) 162 | (0.075 0.03 0) 163 | (0.08 0.03 0) 164 | (0.085 0.03 0) 165 | (0.09 0.03 0) 166 | (0.095 0.03 0) 167 | (0.1 0.03 0) 168 | (0 0.035 0) 169 | (0.005 0.035 0) 170 | (0.01 0.035 0) 171 | (0.015 0.035 0) 172 | (0.02 0.035 0) 173 | (0.025 0.035 0) 174 | (0.03 0.035 0) 175 | (0.035 0.035 0) 176 | (0.04 0.035 0) 177 | (0.045 0.035 0) 178 | (0.05 0.035 0) 179 | (0.055 0.035 0) 180 | (0.06 0.035 0) 181 | (0.065 0.035 0) 182 | (0.07 0.035 0) 183 | (0.075 0.035 0) 184 | (0.08 0.035 0) 185 | (0.085 0.035 0) 186 | (0.09 0.035 0) 187 | (0.095 0.035 0) 188 | (0.1 0.035 0) 189 | (0 0.04 0) 190 | (0.005 0.04 0) 191 | (0.01 0.04 0) 192 | (0.015 0.04 0) 193 | (0.02 0.04 0) 194 | (0.025 0.04 0) 195 | (0.03 0.04 0) 196 | (0.035 0.04 0) 197 | (0.04 0.04 0) 198 | (0.045 0.04 0) 199 | (0.05 0.04 0) 200 | (0.055 0.04 0) 201 | (0.06 0.04 0) 202 | (0.065 0.04 0) 203 | (0.07 0.04 0) 204 | (0.075 0.04 0) 205 | (0.08 0.04 0) 206 | (0.085 0.04 0) 207 | (0.09 0.04 0) 208 | (0.095 0.04 0) 209 | (0.1 0.04 0) 210 | (0 0.045 0) 211 | (0.005 0.045 0) 212 | (0.01 0.045 0) 213 | (0.015 0.045 0) 214 | (0.02 0.045 0) 215 | (0.025 0.045 0) 216 | (0.03 0.045 0) 217 | (0.035 0.045 0) 218 | (0.04 0.045 0) 219 | (0.045 0.045 0) 220 | (0.05 0.045 0) 221 | (0.055 0.045 0) 222 | (0.06 0.045 0) 223 | (0.065 0.045 0) 224 | (0.07 0.045 0) 225 | (0.075 0.045 0) 226 | (0.08 0.045 0) 227 | (0.085 0.045 0) 228 | (0.09 0.045 0) 229 | (0.095 0.045 0) 230 | (0.1 0.045 0) 231 | (0 0.05 0) 232 | (0.005 0.05 0) 233 | (0.01 0.05 0) 234 | (0.015 0.05 0) 235 | (0.02 0.05 0) 236 | (0.025 0.05 0) 237 | (0.03 0.05 0) 238 | (0.035 0.05 0) 239 | (0.04 0.05 0) 240 | (0.045 0.05 0) 241 | (0.05 0.05 0) 242 | (0.055 0.05 0) 243 | (0.06 0.05 0) 244 | (0.065 0.05 0) 245 | (0.07 0.05 0) 246 | (0.075 0.05 0) 247 | (0.08 0.05 0) 248 | (0.085 0.05 0) 249 | (0.09 0.05 0) 250 | (0.095 0.05 0) 251 | (0.1 0.05 0) 252 | (0 0.055 0) 253 | (0.005 0.055 0) 254 | (0.01 0.055 0) 255 | (0.015 0.055 0) 256 | (0.02 0.055 0) 257 | (0.025 0.055 0) 258 | (0.03 0.055 0) 259 | (0.035 0.055 0) 260 | (0.04 0.055 0) 261 | (0.045 0.055 0) 262 | (0.05 0.055 0) 263 | (0.055 0.055 0) 264 | (0.06 0.055 0) 265 | (0.065 0.055 0) 266 | (0.07 0.055 0) 267 | (0.075 0.055 0) 268 | (0.08 0.055 0) 269 | (0.085 0.055 0) 270 | (0.09 0.055 0) 271 | (0.095 0.055 0) 272 | (0.1 0.055 0) 273 | (0 0.06 0) 274 | (0.005 0.06 0) 275 | (0.01 0.06 0) 276 | (0.015 0.06 0) 277 | (0.02 0.06 0) 278 | (0.025 0.06 0) 279 | (0.03 0.06 0) 280 | (0.035 0.06 0) 281 | (0.04 0.06 0) 282 | (0.045 0.06 0) 283 | (0.05 0.06 0) 284 | (0.055 0.06 0) 285 | (0.06 0.06 0) 286 | (0.065 0.06 0) 287 | (0.07 0.06 0) 288 | (0.075 0.06 0) 289 | (0.08 0.06 0) 290 | (0.085 0.06 0) 291 | (0.09 0.06 0) 292 | (0.095 0.06 0) 293 | (0.1 0.06 0) 294 | (0 0.065 0) 295 | (0.005 0.065 0) 296 | (0.01 0.065 0) 297 | (0.015 0.065 0) 298 | (0.02 0.065 0) 299 | (0.025 0.065 0) 300 | (0.03 0.065 0) 301 | (0.035 0.065 0) 302 | (0.04 0.065 0) 303 | (0.045 0.065 0) 304 | (0.05 0.065 0) 305 | (0.055 0.065 0) 306 | (0.06 0.065 0) 307 | (0.065 0.065 0) 308 | (0.07 0.065 0) 309 | (0.075 0.065 0) 310 | (0.08 0.065 0) 311 | (0.085 0.065 0) 312 | (0.09 0.065 0) 313 | (0.095 0.065 0) 314 | (0.1 0.065 0) 315 | (0 0.07 0) 316 | (0.005 0.07 0) 317 | (0.01 0.07 0) 318 | (0.015 0.07 0) 319 | (0.02 0.07 0) 320 | (0.025 0.07 0) 321 | (0.03 0.07 0) 322 | (0.035 0.07 0) 323 | (0.04 0.07 0) 324 | (0.045 0.07 0) 325 | (0.05 0.07 0) 326 | (0.055 0.07 0) 327 | (0.06 0.07 0) 328 | (0.065 0.07 0) 329 | (0.07 0.07 0) 330 | (0.075 0.07 0) 331 | (0.08 0.07 0) 332 | (0.085 0.07 0) 333 | (0.09 0.07 0) 334 | (0.095 0.07 0) 335 | (0.1 0.07 0) 336 | (0 0.075 0) 337 | (0.005 0.075 0) 338 | (0.01 0.075 0) 339 | (0.015 0.075 0) 340 | (0.02 0.075 0) 341 | (0.025 0.075 0) 342 | (0.03 0.075 0) 343 | (0.035 0.075 0) 344 | (0.04 0.075 0) 345 | (0.045 0.075 0) 346 | (0.05 0.075 0) 347 | (0.055 0.075 0) 348 | (0.06 0.075 0) 349 | (0.065 0.075 0) 350 | (0.07 0.075 0) 351 | (0.075 0.075 0) 352 | (0.08 0.075 0) 353 | (0.085 0.075 0) 354 | (0.09 0.075 0) 355 | (0.095 0.075 0) 356 | (0.1 0.075 0) 357 | (0 0.08 0) 358 | (0.005 0.08 0) 359 | (0.01 0.08 0) 360 | (0.015 0.08 0) 361 | (0.02 0.08 0) 362 | (0.025 0.08 0) 363 | (0.03 0.08 0) 364 | (0.035 0.08 0) 365 | (0.04 0.08 0) 366 | (0.045 0.08 0) 367 | (0.05 0.08 0) 368 | (0.055 0.08 0) 369 | (0.06 0.08 0) 370 | (0.065 0.08 0) 371 | (0.07 0.08 0) 372 | (0.075 0.08 0) 373 | (0.08 0.08 0) 374 | (0.085 0.08 0) 375 | (0.09 0.08 0) 376 | (0.095 0.08 0) 377 | (0.1 0.08 0) 378 | (0 0.085 0) 379 | (0.005 0.085 0) 380 | (0.01 0.085 0) 381 | (0.015 0.085 0) 382 | (0.02 0.085 0) 383 | (0.025 0.085 0) 384 | (0.03 0.085 0) 385 | (0.035 0.085 0) 386 | (0.04 0.085 0) 387 | (0.045 0.085 0) 388 | (0.05 0.085 0) 389 | (0.055 0.085 0) 390 | (0.06 0.085 0) 391 | (0.065 0.085 0) 392 | (0.07 0.085 0) 393 | (0.075 0.085 0) 394 | (0.08 0.085 0) 395 | (0.085 0.085 0) 396 | (0.09 0.085 0) 397 | (0.095 0.085 0) 398 | (0.1 0.085 0) 399 | (0 0.09 0) 400 | (0.005 0.09 0) 401 | (0.01 0.09 0) 402 | (0.015 0.09 0) 403 | (0.02 0.09 0) 404 | (0.025 0.09 0) 405 | (0.03 0.09 0) 406 | (0.035 0.09 0) 407 | (0.04 0.09 0) 408 | (0.045 0.09 0) 409 | (0.05 0.09 0) 410 | (0.055 0.09 0) 411 | (0.06 0.09 0) 412 | (0.065 0.09 0) 413 | (0.07 0.09 0) 414 | (0.075 0.09 0) 415 | (0.08 0.09 0) 416 | (0.085 0.09 0) 417 | (0.09 0.09 0) 418 | (0.095 0.09 0) 419 | (0.1 0.09 0) 420 | (0 0.095 0) 421 | (0.005 0.095 0) 422 | (0.01 0.095 0) 423 | (0.015 0.095 0) 424 | (0.02 0.095 0) 425 | (0.025 0.095 0) 426 | (0.03 0.095 0) 427 | (0.035 0.095 0) 428 | (0.04 0.095 0) 429 | (0.045 0.095 0) 430 | (0.05 0.095 0) 431 | (0.055 0.095 0) 432 | (0.06 0.095 0) 433 | (0.065 0.095 0) 434 | (0.07 0.095 0) 435 | (0.075 0.095 0) 436 | (0.08 0.095 0) 437 | (0.085 0.095 0) 438 | (0.09 0.095 0) 439 | (0.095 0.095 0) 440 | (0.1 0.095 0) 441 | (0 0.1 0) 442 | (0.005 0.1 0) 443 | (0.01 0.1 0) 444 | (0.015 0.1 0) 445 | (0.02 0.1 0) 446 | (0.025 0.1 0) 447 | (0.03 0.1 0) 448 | (0.035 0.1 0) 449 | (0.04 0.1 0) 450 | (0.045 0.1 0) 451 | (0.05 0.1 0) 452 | (0.055 0.1 0) 453 | (0.06 0.1 0) 454 | (0.065 0.1 0) 455 | (0.07 0.1 0) 456 | (0.075 0.1 0) 457 | (0.08 0.1 0) 458 | (0.085 0.1 0) 459 | (0.09 0.1 0) 460 | (0.095 0.1 0) 461 | (0.1 0.1 0) 462 | (0 0 0.01) 463 | (0.005 0 0.01) 464 | (0.01 0 0.01) 465 | (0.015 0 0.01) 466 | (0.02 0 0.01) 467 | (0.025 0 0.01) 468 | (0.03 0 0.01) 469 | (0.035 0 0.01) 470 | (0.04 0 0.01) 471 | (0.045 0 0.01) 472 | (0.05 0 0.01) 473 | (0.055 0 0.01) 474 | (0.06 0 0.01) 475 | (0.065 0 0.01) 476 | (0.07 0 0.01) 477 | (0.075 0 0.01) 478 | (0.08 0 0.01) 479 | (0.085 0 0.01) 480 | (0.09 0 0.01) 481 | (0.095 0 0.01) 482 | (0.1 0 0.01) 483 | (0 0.005 0.01) 484 | (0.005 0.005 0.01) 485 | (0.01 0.005 0.01) 486 | (0.015 0.005 0.01) 487 | (0.02 0.005 0.01) 488 | (0.025 0.005 0.01) 489 | (0.03 0.005 0.01) 490 | (0.035 0.005 0.01) 491 | (0.04 0.005 0.01) 492 | (0.045 0.005 0.01) 493 | (0.05 0.005 0.01) 494 | (0.055 0.005 0.01) 495 | (0.06 0.005 0.01) 496 | (0.065 0.005 0.01) 497 | (0.07 0.005 0.01) 498 | (0.075 0.005 0.01) 499 | (0.08 0.005 0.01) 500 | (0.085 0.005 0.01) 501 | (0.09 0.005 0.01) 502 | (0.095 0.005 0.01) 503 | (0.1 0.005 0.01) 504 | (0 0.01 0.01) 505 | (0.005 0.01 0.01) 506 | (0.01 0.01 0.01) 507 | (0.015 0.01 0.01) 508 | (0.02 0.01 0.01) 509 | (0.025 0.01 0.01) 510 | (0.03 0.01 0.01) 511 | (0.035 0.01 0.01) 512 | (0.04 0.01 0.01) 513 | (0.045 0.01 0.01) 514 | (0.05 0.01 0.01) 515 | (0.055 0.01 0.01) 516 | (0.06 0.01 0.01) 517 | (0.065 0.01 0.01) 518 | (0.07 0.01 0.01) 519 | (0.075 0.01 0.01) 520 | (0.08 0.01 0.01) 521 | (0.085 0.01 0.01) 522 | (0.09 0.01 0.01) 523 | (0.095 0.01 0.01) 524 | (0.1 0.01 0.01) 525 | (0 0.015 0.01) 526 | (0.005 0.015 0.01) 527 | (0.01 0.015 0.01) 528 | (0.015 0.015 0.01) 529 | (0.02 0.015 0.01) 530 | (0.025 0.015 0.01) 531 | (0.03 0.015 0.01) 532 | (0.035 0.015 0.01) 533 | (0.04 0.015 0.01) 534 | (0.045 0.015 0.01) 535 | (0.05 0.015 0.01) 536 | (0.055 0.015 0.01) 537 | (0.06 0.015 0.01) 538 | (0.065 0.015 0.01) 539 | (0.07 0.015 0.01) 540 | (0.075 0.015 0.01) 541 | (0.08 0.015 0.01) 542 | (0.085 0.015 0.01) 543 | (0.09 0.015 0.01) 544 | (0.095 0.015 0.01) 545 | (0.1 0.015 0.01) 546 | (0 0.02 0.01) 547 | (0.005 0.02 0.01) 548 | (0.01 0.02 0.01) 549 | (0.015 0.02 0.01) 550 | (0.02 0.02 0.01) 551 | (0.025 0.02 0.01) 552 | (0.03 0.02 0.01) 553 | (0.035 0.02 0.01) 554 | (0.04 0.02 0.01) 555 | (0.045 0.02 0.01) 556 | (0.05 0.02 0.01) 557 | (0.055 0.02 0.01) 558 | (0.06 0.02 0.01) 559 | (0.065 0.02 0.01) 560 | (0.07 0.02 0.01) 561 | (0.075 0.02 0.01) 562 | (0.08 0.02 0.01) 563 | (0.085 0.02 0.01) 564 | (0.09 0.02 0.01) 565 | (0.095 0.02 0.01) 566 | (0.1 0.02 0.01) 567 | (0 0.025 0.01) 568 | (0.005 0.025 0.01) 569 | (0.01 0.025 0.01) 570 | (0.015 0.025 0.01) 571 | (0.02 0.025 0.01) 572 | (0.025 0.025 0.01) 573 | (0.03 0.025 0.01) 574 | (0.035 0.025 0.01) 575 | (0.04 0.025 0.01) 576 | (0.045 0.025 0.01) 577 | (0.05 0.025 0.01) 578 | (0.055 0.025 0.01) 579 | (0.06 0.025 0.01) 580 | (0.065 0.025 0.01) 581 | (0.07 0.025 0.01) 582 | (0.075 0.025 0.01) 583 | (0.08 0.025 0.01) 584 | (0.085 0.025 0.01) 585 | (0.09 0.025 0.01) 586 | (0.095 0.025 0.01) 587 | (0.1 0.025 0.01) 588 | (0 0.03 0.01) 589 | (0.005 0.03 0.01) 590 | (0.01 0.03 0.01) 591 | (0.015 0.03 0.01) 592 | (0.02 0.03 0.01) 593 | (0.025 0.03 0.01) 594 | (0.03 0.03 0.01) 595 | (0.035 0.03 0.01) 596 | (0.04 0.03 0.01) 597 | (0.045 0.03 0.01) 598 | (0.05 0.03 0.01) 599 | (0.055 0.03 0.01) 600 | (0.06 0.03 0.01) 601 | (0.065 0.03 0.01) 602 | (0.07 0.03 0.01) 603 | (0.075 0.03 0.01) 604 | (0.08 0.03 0.01) 605 | (0.085 0.03 0.01) 606 | (0.09 0.03 0.01) 607 | (0.095 0.03 0.01) 608 | (0.1 0.03 0.01) 609 | (0 0.035 0.01) 610 | (0.005 0.035 0.01) 611 | (0.01 0.035 0.01) 612 | (0.015 0.035 0.01) 613 | (0.02 0.035 0.01) 614 | (0.025 0.035 0.01) 615 | (0.03 0.035 0.01) 616 | (0.035 0.035 0.01) 617 | (0.04 0.035 0.01) 618 | (0.045 0.035 0.01) 619 | (0.05 0.035 0.01) 620 | (0.055 0.035 0.01) 621 | (0.06 0.035 0.01) 622 | (0.065 0.035 0.01) 623 | (0.07 0.035 0.01) 624 | (0.075 0.035 0.01) 625 | (0.08 0.035 0.01) 626 | (0.085 0.035 0.01) 627 | (0.09 0.035 0.01) 628 | (0.095 0.035 0.01) 629 | (0.1 0.035 0.01) 630 | (0 0.04 0.01) 631 | (0.005 0.04 0.01) 632 | (0.01 0.04 0.01) 633 | (0.015 0.04 0.01) 634 | (0.02 0.04 0.01) 635 | (0.025 0.04 0.01) 636 | (0.03 0.04 0.01) 637 | (0.035 0.04 0.01) 638 | (0.04 0.04 0.01) 639 | (0.045 0.04 0.01) 640 | (0.05 0.04 0.01) 641 | (0.055 0.04 0.01) 642 | (0.06 0.04 0.01) 643 | (0.065 0.04 0.01) 644 | (0.07 0.04 0.01) 645 | (0.075 0.04 0.01) 646 | (0.08 0.04 0.01) 647 | (0.085 0.04 0.01) 648 | (0.09 0.04 0.01) 649 | (0.095 0.04 0.01) 650 | (0.1 0.04 0.01) 651 | (0 0.045 0.01) 652 | (0.005 0.045 0.01) 653 | (0.01 0.045 0.01) 654 | (0.015 0.045 0.01) 655 | (0.02 0.045 0.01) 656 | (0.025 0.045 0.01) 657 | (0.03 0.045 0.01) 658 | (0.035 0.045 0.01) 659 | (0.04 0.045 0.01) 660 | (0.045 0.045 0.01) 661 | (0.05 0.045 0.01) 662 | (0.055 0.045 0.01) 663 | (0.06 0.045 0.01) 664 | (0.065 0.045 0.01) 665 | (0.07 0.045 0.01) 666 | (0.075 0.045 0.01) 667 | (0.08 0.045 0.01) 668 | (0.085 0.045 0.01) 669 | (0.09 0.045 0.01) 670 | (0.095 0.045 0.01) 671 | (0.1 0.045 0.01) 672 | (0 0.05 0.01) 673 | (0.005 0.05 0.01) 674 | (0.01 0.05 0.01) 675 | (0.015 0.05 0.01) 676 | (0.02 0.05 0.01) 677 | (0.025 0.05 0.01) 678 | (0.03 0.05 0.01) 679 | (0.035 0.05 0.01) 680 | (0.04 0.05 0.01) 681 | (0.045 0.05 0.01) 682 | (0.05 0.05 0.01) 683 | (0.055 0.05 0.01) 684 | (0.06 0.05 0.01) 685 | (0.065 0.05 0.01) 686 | (0.07 0.05 0.01) 687 | (0.075 0.05 0.01) 688 | (0.08 0.05 0.01) 689 | (0.085 0.05 0.01) 690 | (0.09 0.05 0.01) 691 | (0.095 0.05 0.01) 692 | (0.1 0.05 0.01) 693 | (0 0.055 0.01) 694 | (0.005 0.055 0.01) 695 | (0.01 0.055 0.01) 696 | (0.015 0.055 0.01) 697 | (0.02 0.055 0.01) 698 | (0.025 0.055 0.01) 699 | (0.03 0.055 0.01) 700 | (0.035 0.055 0.01) 701 | (0.04 0.055 0.01) 702 | (0.045 0.055 0.01) 703 | (0.05 0.055 0.01) 704 | (0.055 0.055 0.01) 705 | (0.06 0.055 0.01) 706 | (0.065 0.055 0.01) 707 | (0.07 0.055 0.01) 708 | (0.075 0.055 0.01) 709 | (0.08 0.055 0.01) 710 | (0.085 0.055 0.01) 711 | (0.09 0.055 0.01) 712 | (0.095 0.055 0.01) 713 | (0.1 0.055 0.01) 714 | (0 0.06 0.01) 715 | (0.005 0.06 0.01) 716 | (0.01 0.06 0.01) 717 | (0.015 0.06 0.01) 718 | (0.02 0.06 0.01) 719 | (0.025 0.06 0.01) 720 | (0.03 0.06 0.01) 721 | (0.035 0.06 0.01) 722 | (0.04 0.06 0.01) 723 | (0.045 0.06 0.01) 724 | (0.05 0.06 0.01) 725 | (0.055 0.06 0.01) 726 | (0.06 0.06 0.01) 727 | (0.065 0.06 0.01) 728 | (0.07 0.06 0.01) 729 | (0.075 0.06 0.01) 730 | (0.08 0.06 0.01) 731 | (0.085 0.06 0.01) 732 | (0.09 0.06 0.01) 733 | (0.095 0.06 0.01) 734 | (0.1 0.06 0.01) 735 | (0 0.065 0.01) 736 | (0.005 0.065 0.01) 737 | (0.01 0.065 0.01) 738 | (0.015 0.065 0.01) 739 | (0.02 0.065 0.01) 740 | (0.025 0.065 0.01) 741 | (0.03 0.065 0.01) 742 | (0.035 0.065 0.01) 743 | (0.04 0.065 0.01) 744 | (0.045 0.065 0.01) 745 | (0.05 0.065 0.01) 746 | (0.055 0.065 0.01) 747 | (0.06 0.065 0.01) 748 | (0.065 0.065 0.01) 749 | (0.07 0.065 0.01) 750 | (0.075 0.065 0.01) 751 | (0.08 0.065 0.01) 752 | (0.085 0.065 0.01) 753 | (0.09 0.065 0.01) 754 | (0.095 0.065 0.01) 755 | (0.1 0.065 0.01) 756 | (0 0.07 0.01) 757 | (0.005 0.07 0.01) 758 | (0.01 0.07 0.01) 759 | (0.015 0.07 0.01) 760 | (0.02 0.07 0.01) 761 | (0.025 0.07 0.01) 762 | (0.03 0.07 0.01) 763 | (0.035 0.07 0.01) 764 | (0.04 0.07 0.01) 765 | (0.045 0.07 0.01) 766 | (0.05 0.07 0.01) 767 | (0.055 0.07 0.01) 768 | (0.06 0.07 0.01) 769 | (0.065 0.07 0.01) 770 | (0.07 0.07 0.01) 771 | (0.075 0.07 0.01) 772 | (0.08 0.07 0.01) 773 | (0.085 0.07 0.01) 774 | (0.09 0.07 0.01) 775 | (0.095 0.07 0.01) 776 | (0.1 0.07 0.01) 777 | (0 0.075 0.01) 778 | (0.005 0.075 0.01) 779 | (0.01 0.075 0.01) 780 | (0.015 0.075 0.01) 781 | (0.02 0.075 0.01) 782 | (0.025 0.075 0.01) 783 | (0.03 0.075 0.01) 784 | (0.035 0.075 0.01) 785 | (0.04 0.075 0.01) 786 | (0.045 0.075 0.01) 787 | (0.05 0.075 0.01) 788 | (0.055 0.075 0.01) 789 | (0.06 0.075 0.01) 790 | (0.065 0.075 0.01) 791 | (0.07 0.075 0.01) 792 | (0.075 0.075 0.01) 793 | (0.08 0.075 0.01) 794 | (0.085 0.075 0.01) 795 | (0.09 0.075 0.01) 796 | (0.095 0.075 0.01) 797 | (0.1 0.075 0.01) 798 | (0 0.08 0.01) 799 | (0.005 0.08 0.01) 800 | (0.01 0.08 0.01) 801 | (0.015 0.08 0.01) 802 | (0.02 0.08 0.01) 803 | (0.025 0.08 0.01) 804 | (0.03 0.08 0.01) 805 | (0.035 0.08 0.01) 806 | (0.04 0.08 0.01) 807 | (0.045 0.08 0.01) 808 | (0.05 0.08 0.01) 809 | (0.055 0.08 0.01) 810 | (0.06 0.08 0.01) 811 | (0.065 0.08 0.01) 812 | (0.07 0.08 0.01) 813 | (0.075 0.08 0.01) 814 | (0.08 0.08 0.01) 815 | (0.085 0.08 0.01) 816 | (0.09 0.08 0.01) 817 | (0.095 0.08 0.01) 818 | (0.1 0.08 0.01) 819 | (0 0.085 0.01) 820 | (0.005 0.085 0.01) 821 | (0.01 0.085 0.01) 822 | (0.015 0.085 0.01) 823 | (0.02 0.085 0.01) 824 | (0.025 0.085 0.01) 825 | (0.03 0.085 0.01) 826 | (0.035 0.085 0.01) 827 | (0.04 0.085 0.01) 828 | (0.045 0.085 0.01) 829 | (0.05 0.085 0.01) 830 | (0.055 0.085 0.01) 831 | (0.06 0.085 0.01) 832 | (0.065 0.085 0.01) 833 | (0.07 0.085 0.01) 834 | (0.075 0.085 0.01) 835 | (0.08 0.085 0.01) 836 | (0.085 0.085 0.01) 837 | (0.09 0.085 0.01) 838 | (0.095 0.085 0.01) 839 | (0.1 0.085 0.01) 840 | (0 0.09 0.01) 841 | (0.005 0.09 0.01) 842 | (0.01 0.09 0.01) 843 | (0.015 0.09 0.01) 844 | (0.02 0.09 0.01) 845 | (0.025 0.09 0.01) 846 | (0.03 0.09 0.01) 847 | (0.035 0.09 0.01) 848 | (0.04 0.09 0.01) 849 | (0.045 0.09 0.01) 850 | (0.05 0.09 0.01) 851 | (0.055 0.09 0.01) 852 | (0.06 0.09 0.01) 853 | (0.065 0.09 0.01) 854 | (0.07 0.09 0.01) 855 | (0.075 0.09 0.01) 856 | (0.08 0.09 0.01) 857 | (0.085 0.09 0.01) 858 | (0.09 0.09 0.01) 859 | (0.095 0.09 0.01) 860 | (0.1 0.09 0.01) 861 | (0 0.095 0.01) 862 | (0.005 0.095 0.01) 863 | (0.01 0.095 0.01) 864 | (0.015 0.095 0.01) 865 | (0.02 0.095 0.01) 866 | (0.025 0.095 0.01) 867 | (0.03 0.095 0.01) 868 | (0.035 0.095 0.01) 869 | (0.04 0.095 0.01) 870 | (0.045 0.095 0.01) 871 | (0.05 0.095 0.01) 872 | (0.055 0.095 0.01) 873 | (0.06 0.095 0.01) 874 | (0.065 0.095 0.01) 875 | (0.07 0.095 0.01) 876 | (0.075 0.095 0.01) 877 | (0.08 0.095 0.01) 878 | (0.085 0.095 0.01) 879 | (0.09 0.095 0.01) 880 | (0.095 0.095 0.01) 881 | (0.1 0.095 0.01) 882 | (0 0.1 0.01) 883 | (0.005 0.1 0.01) 884 | (0.01 0.1 0.01) 885 | (0.015 0.1 0.01) 886 | (0.02 0.1 0.01) 887 | (0.025 0.1 0.01) 888 | (0.03 0.1 0.01) 889 | (0.035 0.1 0.01) 890 | (0.04 0.1 0.01) 891 | (0.045 0.1 0.01) 892 | (0.05 0.1 0.01) 893 | (0.055 0.1 0.01) 894 | (0.06 0.1 0.01) 895 | (0.065 0.1 0.01) 896 | (0.07 0.1 0.01) 897 | (0.075 0.1 0.01) 898 | (0.08 0.1 0.01) 899 | (0.085 0.1 0.01) 900 | (0.09 0.1 0.01) 901 | (0.095 0.1 0.01) 902 | (0.1 0.1 0.01) 903 | ) 904 | 905 | 906 | // ************************************************************************* // 907 | -------------------------------------------------------------------------------- /example/cavity/constant/transportProperties: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "constant"; 14 | object transportProperties; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | nu [0 2 -1 0 0 0 0] 0.01; 19 | kappa [0 2 -1 0 0 0 0] 0.001; 20 | rho [1 -3 0 0 0 0 0] 1000; 21 | 22 | // ************************************************************************* // 23 | -------------------------------------------------------------------------------- /example/cavity/convergence/convergenceUp.out: -------------------------------------------------------------------------------- 1 | noIter Time[s] UxResInit UyResInit UzResInit pResInit kResInit epsilonResInit omegaResInit TResInit -------------------------------------------------------------------------------- /example/cavity/foam.foam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/example/cavity/foam.foam -------------------------------------------------------------------------------- /example/cavity/pyFVMScript.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time as time 4 | # 获取当前脚本文件所在的目录 5 | current_file_path = os.path.abspath(__file__) 6 | current_dir = os.path.dirname(current_file_path) 7 | # Add the path to the pyFVM directory 8 | src_path = os.path.abspath(os.path.join(current_dir, '..', '..', 'src')) 9 | print(f"src 路径: {src_path}") # 打印路径,确保正确 10 | sys.path.insert(0, src_path) 11 | import pyFVM.Region as Rg 12 | 13 | start=time.time() 14 | cfdCase=Rg.Region(current_dir) 15 | cfdCase.RunCase() 16 | # region_vars=vars(cfdCase) 17 | print(time.time()-start) -------------------------------------------------------------------------------- /example/cavity/system/blockMeshDict: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | object blockMeshDict; 14 | } 15 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 16 | 17 | convertToMeters 0.1; 18 | 19 | vertices 20 | ( 21 | (0 0 0) 22 | (1 0 0) 23 | (1 1 0) 24 | (0 1 0) 25 | (0 0 0.1) 26 | (1 0 0.1) 27 | (1 1 0.1) 28 | (0 1 0.1) 29 | ); 30 | 31 | blocks 32 | ( 33 | hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1) 34 | ); 35 | 36 | edges 37 | ( 38 | ); 39 | 40 | boundary 41 | ( 42 | movingWall 43 | { 44 | type wall; 45 | faces 46 | ( 47 | (3 7 6 2) 48 | ); 49 | } 50 | fixedWalls 51 | { 52 | type wall; 53 | faces 54 | ( 55 | (0 4 7 3) 56 | (2 6 5 1) 57 | (1 5 4 0) 58 | ); 59 | } 60 | frontAndBack 61 | { 62 | type empty; 63 | faces 64 | ( 65 | (0 3 2 1) 66 | (4 5 6 7) 67 | ); 68 | } 69 | ); 70 | 71 | mergePatchPairs 72 | ( 73 | ); 74 | 75 | // ************************************************************************* // 76 | -------------------------------------------------------------------------------- /example/cavity/system/controlDict: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object controlDict; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | application icoFoam; 19 | 20 | startFrom latestTime; 21 | 22 | startTime 0; 23 | 24 | stopAt endTime; 25 | 26 | endTime 2.0; 27 | 28 | deltaT 0.005; 29 | 30 | writeControl timeStep; 31 | 32 | writeInterval 0.025; 33 | 34 | purgeWrite 0; 35 | 36 | writeFormat ascii; 37 | 38 | writePrecision 6; 39 | 40 | writeCompression off; 41 | 42 | timeFormat general; 43 | 44 | timePrecision 6; 45 | 46 | runTimeModifiable true; 47 | 48 | 49 | // ************************************************************************* // 50 | -------------------------------------------------------------------------------- /example/cavity/system/fvSchemes: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object fvSchemes; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | ddtSchemes 19 | { 20 | default Euler; 21 | } 22 | 23 | gradSchemes 24 | { 25 | default Gauss linear; 26 | grad(p) Gauss linear; 27 | } 28 | 29 | divSchemes 30 | { 31 | default none; 32 | div(phi,U) Gauss linear; 33 | div(phi,T) Gauss upwind; 34 | } 35 | 36 | laplacianSchemes 37 | { 38 | default Gauss linear orthogonal; 39 | laplacian(kappa,T) Gauss linear corrected; 40 | } 41 | 42 | interpolationSchemes 43 | { 44 | default linear; 45 | } 46 | 47 | snGradSchemes 48 | { 49 | default orthogonal; 50 | } 51 | 52 | 53 | // ************************************************************************* // 54 | -------------------------------------------------------------------------------- /example/cavity/system/fvSolution: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: 5 | 5 | | \\ / A nd | Web: www.OpenFOAM.org | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object fvSolution; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | solvers 19 | { 20 | p 21 | { 22 | solver PCG; 23 | preconditioner Jacobi; 24 | tolerance 1e-06; 25 | relTol 0.05; 26 | } 27 | 28 | pFinal 29 | { 30 | $p; 31 | relTol 0; 32 | } 33 | 34 | U 35 | { 36 | solver smoothSolver; 37 | smoother DILU; 38 | tolerance 1e-05; 39 | relTol 0; 40 | } 41 | 42 | T 43 | { 44 | solver smoothSolver; 45 | smoother symGaussSeidel; 46 | tolerance 1e-06; 47 | relTol 0; 48 | } 49 | } 50 | 51 | PISO 52 | { 53 | nCorrectors 2; 54 | nNonOrthogonalCorrectors 0; 55 | pRefCell 0; 56 | pRefValue 0; 57 | } 58 | 59 | relaxationFactors 60 | { 61 | fields 62 | { 63 | p 0.3; 64 | } 65 | equations 66 | { 67 | U 0.7; 68 | } 69 | } 70 | 71 | // ************************************************************************* // 72 | -------------------------------------------------------------------------------- /example/flange/0/T: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: plus | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class volScalarField; 13 | object T; 14 | } 15 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 16 | 17 | dimensions [0 0 0 1 0 0 0]; 18 | 19 | internalField uniform 273; 20 | 21 | boundaryField 22 | { 23 | wall1 24 | { 25 | type zeroGradient; 26 | } 27 | 28 | top 29 | { 30 | type fixedValue; 31 | value uniform 273; 32 | } 33 | 34 | wall2 35 | { 36 | type zeroGradient; 37 | } 38 | 39 | bottom 40 | { 41 | type fixedValue; 42 | value uniform 573; 43 | } 44 | } 45 | 46 | // ************************************************************************* // 47 | -------------------------------------------------------------------------------- /example/flange/constant/polyMesh/boundary: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: v1706 | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class polyBoundaryMesh; 13 | location "constant/polyMesh"; 14 | object boundary; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | 4 19 | ( 20 | wall1 21 | { 22 | type wall; 23 | nFaces 2440; 24 | startFace 15316; 25 | } 26 | top 27 | { 28 | type wall; 29 | nFaces 348; 30 | startFace 17756; 31 | } 32 | wall2 33 | { 34 | type wall; 35 | nFaces 96; 36 | startFace 18104; 37 | } 38 | bottom 39 | { 40 | type wall; 41 | nFaces 384; 42 | startFace 18200; 43 | } 44 | ) 45 | 46 | // ************************************************************************* // 47 | -------------------------------------------------------------------------------- /example/flange/constant/transportProperties: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: plus | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "constant"; 14 | object transportProperties; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | rho [1 -3 0 0 0 0 0] 2000; 19 | kappa [0 2 -1 0 0 0 0] 4e-05; 20 | 21 | 22 | // ************************************************************************* // 23 | -------------------------------------------------------------------------------- /example/flange/convergence/convergenceUp.out: -------------------------------------------------------------------------------- 1 | noIter Time[s] UxResInit UyResInit UzResInit pResInit kResInit epsilonResInit omegaResInit TResInit -------------------------------------------------------------------------------- /example/flange/foam.foam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/example/flange/foam.foam -------------------------------------------------------------------------------- /example/flange/pyFVMScript.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time as time 4 | # 获取当前脚本文件所在的目录 5 | current_file_path = os.path.abspath(__file__) 6 | current_dir = os.path.dirname(current_file_path) 7 | # Add the path to the pyFVM directory 8 | src_path = os.path.abspath(os.path.join(current_dir, '..', '..', 'src')) 9 | print(f"src 路径: {src_path}") # 打印路径,确保正确 10 | sys.path.insert(0, src_path) 11 | import pyFVM.Region as Rg 12 | 13 | start=time.time() 14 | cfdCase=Rg.Region(current_dir) 15 | cfdCase.RunCase() 16 | # region_vars=vars(cfdCase) 17 | print(time.time()-start) 18 | -------------------------------------------------------------------------------- /example/flange/system/controlDict: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: plus | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object controlDict; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | application SIMPLE; 19 | 20 | startFrom latestTime; 21 | 22 | startTime 0; 23 | 24 | stopAt endTime; 25 | 26 | endTime 3; 27 | 28 | deltaT 0.005; 29 | 30 | writeControl runTime; 31 | 32 | writeInterval 0.1; 33 | 34 | purgeWrite 0; 35 | 36 | writeFormat ascii; 37 | 38 | writePrecision 6; 39 | 40 | writeCompression off; 41 | 42 | timeFormat general; 43 | 44 | timePrecision 6; 45 | 46 | runTimeModifiable true; 47 | 48 | 49 | // ************************************************************************* // 50 | -------------------------------------------------------------------------------- /example/flange/system/fvSchemes: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: plus | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object fvSchemes; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | ddtSchemes 19 | { 20 | default Euler; 21 | } 22 | 23 | gradSchemes 24 | { 25 | default Gauss linear; 26 | grad(T) Gauss linear; 27 | } 28 | 29 | divSchemes 30 | { 31 | default none; 32 | } 33 | 34 | laplacianSchemes 35 | { 36 | default none; 37 | laplacian(kappa,T) Gauss linear corrected; 38 | } 39 | 40 | interpolationSchemes 41 | { 42 | default linear; 43 | } 44 | 45 | snGradSchemes 46 | { 47 | default corrected; 48 | } 49 | 50 | 51 | // ************************************************************************* // 52 | -------------------------------------------------------------------------------- /example/flange/system/fvSolution: -------------------------------------------------------------------------------- 1 | /*--------------------------------*- C++ -*----------------------------------*\ 2 | | ========= | | 3 | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | 4 | | \\ / O peration | Version: plus | 5 | | \\ / A nd | Web: www.OpenFOAM.com | 6 | | \\/ M anipulation | | 7 | \*---------------------------------------------------------------------------*/ 8 | FoamFile 9 | { 10 | version 2.0; 11 | format ascii; 12 | class dictionary; 13 | location "system"; 14 | object fvSolution; 15 | } 16 | // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 17 | 18 | solvers 19 | { 20 | T 21 | { 22 | solver PCG; 23 | preconditioner DIC; 24 | tolerance 1e-06; 25 | relTol 0; 26 | } 27 | } 28 | 29 | SIMPLE 30 | { 31 | nNonOrthogonalCorrectors 2; 32 | } 33 | 34 | relaxationFactors 35 | { 36 | equations 37 | { 38 | T 0.7; 39 | } 40 | } 41 | // ************************************************************************* // 42 | -------------------------------------------------------------------------------- /src/__pycache__/config.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/__pycache__/config.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/Interpolate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cfdtool.IO as io 3 | import cfdtool.Math as mth 4 | from cfdtool.quantities import Quantity as Q_ 5 | 6 | def cfdInterpolateFromElementsToFaces(Region,theInterpolationScheme,field,*args): 7 | return Q_(cfdInterpolateFromElementsToFaces_tool(Region,theInterpolationScheme,field.value,*args),field.dimension) 8 | 9 | def cfdInterpolateFromElementsToFaces_tool(Region,theInterpolationScheme,field,*args): 10 | ''' 11 | 将单元格(元素)上的场数据插值到网格的面(faces)上。 12 | 13 | 参数: 14 | - Region: 包含流体区域信息的对象。 15 | - theInterpolationScheme: 插值方案的名称(例如 'linear', 'harmonic mean' 等)。 16 | - field: 要插值的场的数据,形状为 (numberOfElements, numberOfComponents)。 17 | 18 | 返回: 19 | - phi_f: 插值后的面场数据,形状为 (numberOfFaces, numberOfComponents)。 20 | ''' 21 | # theInterpolationScheme=scheme 22 | if field.shape[0]!=Region.mesh.numberOfElements+Region.mesh.numberOfBElements: 23 | raise ValueError("The shape of gradPhi does not match the number of elements in the mesh.") 24 | try: 25 | theNumberOfComponents = field.shape[1] 26 | except: 27 | theNumberOfComponents =1 28 | 29 | numberOfInteriorFaces = Region.mesh.numberOfInteriorFaces 30 | numberOfFaces = Region.mesh.numberOfFaces 31 | phi_f=np.zeros((numberOfFaces,theNumberOfComponents)) 32 | 33 | # 插值内部面 34 | phi_f[:numberOfInteriorFaces,:]=cfdInterpolateFromElementsToInteriorFaces_tool(Region,theInterpolationScheme,field[:Region.mesh.numberOfElements]) 35 | ''' **边界面插值**: 36 | - 对于边界面(从 `numberOfInteriorFaces` 到 `numberOfFaces`),将单元格中心的值直接赋给 `phi_f`,因为边界面没有邻居单元格。 37 | ''' 38 | boundaryFaceOwners = Region.mesh.owners[numberOfInteriorFaces:] 39 | neighbourFaceOwners= Region.mesh.owners_b 40 | # 处理边界面 41 | phi_f[numberOfInteriorFaces:numberOfFaces, :] = 0.5*(field[boundaryFaceOwners, :]+field[neighbourFaceOwners, :])# TODO check theInterpolation 42 | 43 | return phi_f 44 | 45 | 46 | def cfdInterpolateFromElementsToInteriorFaces(Region,theInterpolationScheme,field, *args): 47 | if args: 48 | return Q_(cfdInterpolateFromElementsToInteriorFaces_tool(Region,theInterpolationScheme,field.value, args[0].value),field.dimension) 49 | else: 50 | return Q_(cfdInterpolateFromElementsToInteriorFaces_tool(Region,theInterpolationScheme,field.value),field.dimension) 51 | 52 | def cfdInterpolateFromElementsToInteriorFaces_tool(Region,theInterpolationScheme,field, *args): 53 | ''' 54 | 根据指定的插值方案,将场数据从单元格插值到内部面上。 55 | 56 | 参数: 57 | - Region: 包含流体区域信息的对象。 58 | - theInterpolationScheme: 插值方案的名称(例如 'linear', 'harmonic mean' 等)。 59 | - field: 要插值的场的数据,形状为 (numberOfElements, numberOfComponents)。 60 | - *args: 其他参数(例如 'linearUpwind' 方案需要的流量数据)。 61 | 62 | 返回: 63 | - phi_f: 插值后的内部面场数据,形状为 (numberOfInteriorFaces, numberOfComponents)。 64 | ''' 65 | if field.shape[0]!=Region.mesh.numberOfElements: 66 | raise ValueError("The shape of gradPhi does not match the number of elements in the mesh.") 67 | try: 68 | theNumberOfComponents = field.shape[1] 69 | except: 70 | theNumberOfComponents =1 71 | numberOfInteriorFaces = Region.mesh.numberOfInteriorFaces 72 | owners = Region.mesh.interiorFaceOwners 73 | neighbours = Region.mesh.interiorFaceNeighbours 74 | 75 | #interpolation factor p.160 in Moukalled 76 | g_f=Region.mesh.interiorFaceWeights 77 | phi_f=np.zeros((numberOfInteriorFaces,theNumberOfComponents)) 78 | 79 | if theInterpolationScheme == 'linear': 80 | # Calculate face-based interpolation weights 81 | # w = dN / (dO + dN), where dO is distance from owner to face, dN is distance from face to neighbor 82 | # phi_f = w * phi_O + (1 - w) * phi_N 83 | phi_f[:numberOfInteriorFaces, :] = g_f[:, None] * field[owners, :] + (1 - g_f)[:, None] * field[neighbours, :] 84 | 85 | elif theInterpolationScheme == 'harmonic mean': 86 | # 谐和平均插值: phi_f = 1 / ((1 - w)/phi_O + w/phi_N) 87 | phi_f[:numberOfInteriorFaces,:] = 1 / ((1 - g_f)[:, None] / field[owners, :] + g_f[:, None] / field[neighbours, :]) 88 | # for iComponent in range(theNumberOfComponents): 89 | # phi_f[0:numberOfInteriorFaces,iComponent]=1/((1-g_f)/field[owners][:,iComponent]+g_f/field[neighbours][:,iComponent]) 90 | 91 | elif theInterpolationScheme == 'vanLeerV': 92 | vol = Region.mesh.elementVolumes 93 | phi_f[:numberOfInteriorFaces,:] = ((vol[owners] + vol[neighbours]) * field[owners, :] * field[neighbours, :]) / \ 94 | (vol[neighbours] * field[owners, :] + vol[owners] * field[neighbours, :]) 95 | # for iComponent in range(theNumberOfComponents): 96 | # phi_f[0:numberOfInteriorFaces,iComponent] = (vol[owners]+vol[neighbours])*field[owners,iComponent]*field[neighbours,iComponent]/(vol[neighbours]*field[owners,iComponent]+vol[owners]*field[neighbours,iComponent]) 97 | 98 | elif theInterpolationScheme == 'linearUpwind': 99 | if args: 100 | mdot_f=args[0] 101 | pos = (mdot_f > 0).astype(int)[:numberOfInteriorFaces,:] 102 | # 插值: phi_f = pos * phi_O + (1 - pos) * phi_N 103 | phi_f[:numberOfInteriorFaces,:] = pos * field[owners, :] + (1 - pos) * field[neighbours, :] 104 | # for iComponent in range(theNumberOfComponents): 105 | # phi_f[0:numberOfInteriorFaces,iComponent] = field[owners,iComponent]*pos + field[neighbours,iComponent]*(1 - pos) 106 | else: 107 | io.cfdError("linearUpwind is implemented in interpolateFromElementsToFaces Error!!!") 108 | else: 109 | io.cfdError(theInterpolationScheme+" is not yet implemented in interpolateFromElementsToFaces") 110 | return phi_f 111 | 112 | def cfdInterpolateGradientsFromElementsToInteriorFaces(Region,gradPhi,scheme,*args): 113 | if args: 114 | return Q_(cfdInterpolateGradientsFromElementsToInteriorFaces_tool(Region,gradPhi.value,scheme,args[0].value),gradPhi.dimension) 115 | else: 116 | return Q_(cfdInterpolateGradientsFromElementsToInteriorFaces_tool(Region,gradPhi.value,scheme),gradPhi.dimension) 117 | 118 | def cfdInterpolateGradientsFromElementsToInteriorFaces_tool(Region,gradPhi,scheme,*args): 119 | ''' 120 | 将单元中心计算的梯度插值到内部面上。 121 | 122 | 参数: 123 | - Region: 包含流体区域信息的对象。 124 | - gradPhi: 在单元格中心计算得到的梯度,形状为 (numberOfElements, 3, numberOfComponents)。 125 | - scheme: 插值方案的名称(例如 'linear', 'Gauss linear' 等)。 126 | - *args: 其他参数(例如 'Gauss linear corrected' 需要的场数据 phi)。 127 | 128 | 返回: 129 | - grad_f: 插值后的内部面梯度数据,形状为 (numberOfInteriorFaces, 3, numberOfComponents)。 130 | ''' 131 | if gradPhi.shape[0]!=Region.mesh.numberOfElements: 132 | raise ValueError("The shape of gradPhi does not match the number of elements in the mesh.") 133 | numberOfInteriorFaces = Region.mesh.numberOfInteriorFaces 134 | # owners of elements of faces 135 | owners_f=Region.mesh.interiorFaceOwners 136 | # neighbour elements of faces 137 | neighbours_f=Region.mesh.interiorFaceNeighbours 138 | # face weights 139 | g_f=Region.mesh.interiorFaceWeights 140 | # vector formed between owner (C) and neighbour (f) elements 141 | CF = Region.mesh.interiorFaceCF.value 142 | # vector of ones 143 | # face gradient matrix 144 | grad_f= np.zeros((numberOfInteriorFaces, 3),dtype='float') 145 | 146 | if scheme == 'linear' or scheme == 'Gauss linear': 147 | # for i in range(theNumberOfComponents): 148 | """ 149 | Example of what is going one in one of the lines below: 150 | 151 | a = ones - g_f <--- returns a 1D (:,) array (1 - faceWeight) 152 | b = gradPhi[self.neighbours_f][:,0,i] <--- grabs only rows in self.neighbours, in column 0, for component 'i' 153 | c = g_f*self.gradPhi[self.owners_f][:,0,i] <--- multiplies faceWeight by owners in column 0, for component 'i' 154 | 155 | grad_f[:,0,i] = a*self.b + c <--- fills (:) column '0' for component 'i' with the result of self.a*self.b + self.c 156 | 157 | In words, what is going is: 158 | 159 | gradient of phi at face = (1 - faceWeight for cell)*neighbouring element's gradient + faceWeight*owner element's gradient 160 | 161 | If the faceWeight (g_f) of the owner cell is high, then its gradient contributes more to the face's gradient than the neighbour cell. 162 | """ 163 | # 线性插值: grad_f = (1 - w) * gradPhi_N + w * gradPhi_O 164 | grad_f[:numberOfInteriorFaces,:]=(1-g_f)[:,None]*gradPhi[neighbours_f,:]+g_f[:,None]*gradPhi[owners_f,:] 165 | 166 | elif scheme == 'Gauss linear corrected': 167 | #书籍《The Finite Volume Method in Computational Fluid Dynamics》Page 289页 168 | grad_f[:numberOfInteriorFaces,:]=(1-g_f)[:,None]*gradPhi[neighbours_f,:]+g_f[:,None]*gradPhi[owners_f,:] 169 | # ScfdUrface-normal gradient 170 | dcfdMag = mth.cfdMag(CF) 171 | e_CF = mth.cfdUnit(CF) 172 | 173 | # local_grad=np.zeros((numberOfInteriorFaces, 3)) 174 | if args: 175 | phi=args[0] 176 | local_grad_cfdMag_f = (phi[neighbours_f]-phi[owners_f])/dcfdMag 177 | local_grad=local_grad_cfdMag_f[:,None]*e_CF 178 | else: 179 | io.cfdError('No phi provided for Gauss linear corrected interpolation') 180 | 181 | local_avg_grad = mth.cfdDot(grad_f, e_CF)[:, None] * e_CF 182 | 183 | # Corrected gradient 184 | grad_f += (local_grad- local_avg_grad) 185 | 186 | elif scheme == 'Gauss upwind': 187 | #not yet implemented, but it will have to be 188 | local_grad_f=(1-g_f)[:,None]*gradPhi[neighbours_f,:]+g_f[:,None]*gradPhi[owners_f,:] 189 | if args: 190 | mdot_f=args[1] 191 | else: 192 | io.cfdError('Gauss upwind requires flow rate (mdot_f) as a second argument') 193 | 194 | # 根据流量方向选择插值方向 195 | pos = (mdot_f > 0).astype(int)[:numberOfInteriorFaces,:] 196 | grad_f[:numberOfInteriorFaces,:] = pos*local_grad_f[owners_f,:] + (1-pos)*local_grad_f[neighbours_f,:] 197 | else: 198 | io.cfdError(f"{scheme} is not yet implemented, but it will have to be") 199 | 200 | return grad_f 201 | -------------------------------------------------------------------------------- /src/cfdtool/Math.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from cfdtool.quantities import Quantity as Q_ 3 | 4 | def cfdMag(valueVector): 5 | if isinstance(valueVector,Q_) : 6 | return Q_(cfdMag_np(valueVector.value),valueVector.dimension) 7 | elif isinstance(valueVector,np.ndarray): 8 | return cfdMag_np(valueVector) 9 | else: 10 | raise TypeError("valueVector must be a Quantity or a numpy array") 11 | 12 | def cfdMag_np(valueVector): 13 | 14 | """Returns the magnitude of a vector or list of vectors 15 | 16 | Attributes: 17 | 18 | valueVector 19 | 这段代码是一个Python程序片段,使用NumPy库来计算向量的点积和模(或长度)。下面是对这段代码的详细解释: 20 | 1. `try`块:程序首先尝试执行`try`块中的代码。如果`try`块中的代码执行过程中没有出现异常,那么`except`块将被跳过。 21 | 2. `iter(valueVector[0])`:这行代码尝试对`valueVector[0]`进行迭代。如果`valueVector[0]`是一个可迭代对象(例如列表或元组),则`iter`函数将返回一个迭代器。如果`valueVector[0]`不是一个可迭代对象,将引发`TypeError`异常。 22 | 3. `result = []`:初始化一个空列表`result`,用来存储计算结果。 23 | 4. `for iVector in valueVector:`:这个循环遍历`valueVector`中的每个元素。如果`valueVector`是一个列表或元组,每个`iVector`将是`valueVector`中的一个元素。 24 | 5. `dotProduct = np.vdot(iVector, iVector)`:使用NumPy的`vdot`函数计算`iVector`和自身的点积。点积是两个向量的对应元素相乘后求和的结果。 25 | 6. `magnitude = np.sqrt(dotProduct)`:计算点积的平方根,得到`iVector`的模(或长度)。 26 | 7. `result.append(magnitude)`:将计算得到的模添加到结果列表`result`中。 27 | 8. `except TypeError:`:如果`try`块中的代码抛出了`TypeError`异常(例如`valueVector[0]`不可迭代),则执行`except`块中的代码。 28 | 9. `dotProduct = np.vdot(valueVector, valueVector)`:如果`valueVector`本身是一个向量,那么使用`vdot`函数计算`valueVector`和自身的点积。 29 | 10. `magnitude = np.sqrt(dotProduct)`:计算点积的平方根,得到`valueVector`的模。 30 | 11. `result = magnitude`:由于`valueVector`是一个单一向量,所以结果是一个单一数值,直接赋值给`result`。 31 | 12. `return result`:函数返回`result`,它可能是一个包含多个模的列表,或者是一个单一的模数值。 32 | 总结来说,这段代码的目的是计算一个向量列表中每个向量的模,或者如果只给定了一个向量,则计算该向量的模。如果输入是一个向量列表,它会返回一个包含每个向量模的列表;如果输入是一个单一向量,它会返回该向量的模。 33 | """ 34 | if not isinstance(valueVector, np.ndarray): 35 | raise TypeError("输入参数必须是一个 NumPy 数组") 36 | if valueVector.ndim == 1: 37 | # 输入是单个向量,返回单一模数 38 | return np.linalg.norm(valueVector) 39 | elif valueVector.ndim == 2: 40 | # 输入是向量列表,返回每个向量的模 41 | return np.linalg.norm(valueVector, axis=1) 42 | else: 43 | raise ValueError("valueVector 必须是一维或二维的 NumPy 数组。") 44 | 45 | def cfdDot(Sf, U_f): 46 | if isinstance(Sf,Q_) and isinstance(U_f,Q_): 47 | return Q_(cfdDot_np(Sf.value, U_f.value),Sf.dimension*U_f.dimension) 48 | 49 | elif isinstance(Sf,Q_) and isinstance(U_f,np.ndarray): 50 | return Q_(cfdDot_np(Sf.value, U_f), Sf.dimension) 51 | 52 | elif isinstance(Sf,np.ndarray) and isinstance(U_f,np.ndarray): 53 | return cfdDot_np(Sf, U_f) 54 | 55 | else: 56 | raise ValueError("输入参数必须是 Quantity 或 NumPy 数组。") 57 | 58 | def cfdDot_np(Sf, U_f): 59 | """ 60 | 计算每个面的面积向量 Sf 与速度向量 U_f 的点积。 61 | 62 | 参数: 63 | - Sf: 面面积向量数组,形状为 (..., dim)。 64 | - U_f: 面上的速度向量数组,形状为 (..., dim)。 65 | 66 | 返回: 67 | - flux: 每个面的通量值数组,形状为 (...,)。 68 | 69 | 支持一维、二维和三维向量。 70 | 71 | 示例: 72 | >>> Sf = np.array([[1, 2, 3], [4, 5, 6]]) 73 | >>> U_f = np.array([[7, 8, 9], [10, 11, 12]]) 74 | >>> cfdDot(Sf, U_f) 75 | array([ 50, 154]) 76 | """ 77 | # 检查输入数组的形状是否匹配 78 | if Sf.shape != U_f.shape: 79 | raise ValueError(f"Shape mismatch: Sf.shape={Sf.shape} and U_f.shape={U_f.shape} must be the same.") 80 | return np.einsum('...i,...i->...', Sf, U_f) 81 | 82 | def cfdUnit(vector): 83 | """ 84 | 这段Python代码定义了一个名为`cfdUnit`的函数,它接收一个名为`vector`的参数,该参数应该是一个NumPy数组。这个函数的目的是将输入的数组`vector`中的每个向量标准化,使其成为单位向量(即模为1的向量)。下面是对这段代码的详细解释: 85 | 1. `def cfdUnit(vector):`:定义一个函数`cfdUnit`,它接收一个参数`vector`。 86 | 2. `return vector/np.linalg.norm(vector,axis=1)[:,None]`:函数的返回语句执行了以下操作: 87 | - `np.linalg.norm(vector,axis=1)`:使用NumPy的`linalg.norm`函数计算`vector`中每个向量的模(长度)。参数`axis=1`指定了沿着第二个轴(即行方向)计算每个向量的模。 88 | - `[:,None]`:这个操作将上一步得到的模数组增加一个维度,使得它可以与`vector`进行广播(broadcasting)操作。`[:,None]`相当于`np.newaxis`,它在数组的形状中添加了一个维度,位置在第二个轴(列)上。 89 | - `vector/...`:将`vector`中的每个向量除以对应的模,从而得到单位向量。由于之前通过`[:,None]`增加了维度,所以这个除法操作可以沿着行进行广播,即每个行向量都除以其对应的模。 90 | 总结来说,`cfdUnit`函数通过计算输入数组中每个向量的模,然后将每个向量除以其模,来返回一个包含单位向量的数组。这样,每个输出向量的模都是1,它们的方向与输入向量相同。 91 | 举个例子,如果输入的`vector`是这样的二维数组: 92 | ```python 93 | vector = np.array([[4, 0], [0, 3]]) 94 | ``` 95 | 那么`cfdUnit`函数将返回: 96 | ```python 97 | cfdUnit(vector) = np.array([[1, 0], [0, 1]]) 98 | ``` 99 | 因为第一个向量的模是4,第二个向量的模是3,分别除以它们的模后,我们得到了单位向量。 100 | """ 101 | if not isinstance(vector, np.ndarray): 102 | raise TypeError("输入参数必须是一个 NumPy 数组") 103 | # 计算范数,并避免除以0 104 | epsilon = 1e-10 # 设置一个小的数,避免除以0 105 | norms = np.linalg.norm(vector, axis=1) 106 | norms[norms == 0] = epsilon # 将0范数替换为epsilon 107 | 108 | # 进行除法操作,得到单位向量 109 | return vector / norms[:, np.newaxis] 110 | # return vector/np.linalg.norm(vector,axis=1)[:,None] 111 | 112 | def cfdScalarList(*args): 113 | """ 114 | Initializes a scalar list. 115 | 116 | Parameters: 117 | - *args: Variable length argument list. 118 | 119 | Returns: 120 | - the_scalar_list: A NumPy array initialized based on the input arguments. 121 | """ 122 | 123 | # 如果没有提供参数或提供的参数为空,返回空列表 124 | if not args: 125 | return [] 126 | 127 | # 如果提供了一个参数,假设这是一个长度 n,初始化为0的列表 128 | if len(args) == 1: 129 | n = args[0] 130 | the_scalar_list = np.zeros(n, dtype=float) 131 | 132 | # 如果提供了两个参数,假设这是长度 n 和一个值,用该值初始化列表 133 | elif len(args) == 2: 134 | n = args[0] 135 | value = args[1] 136 | the_scalar_list = np.full(n, value, dtype=float) 137 | 138 | return the_scalar_list 139 | 140 | # # 使用示例 141 | # # 初始化一个长度为5的列表,所有元素为0 142 | # scalar_list1 = cfd_scalar_list(5) 143 | 144 | # # 初始化一个长度为3的列表,所有元素为1.0 145 | # scalar_list2 = cfd_scalar_list(3, 1.0) 146 | 147 | def cfdResidual(rc, method='norm'): 148 | if method == 'RMS': 149 | rc_res = np.sqrt(np.mean(rc ** 2)) 150 | elif method == 'norm': 151 | rc_res = np.linalg.norm(rc) 152 | elif method == 'mean': 153 | rc_res = np.mean(np.abs(rc)) 154 | elif method == 'max': 155 | rc_res = np.max(np.abs(rc)) 156 | elif method =='sum': 157 | rc_res = np.sum(np.abs(rc)) 158 | return rc_res -------------------------------------------------------------------------------- /src/cfdtool/Time.py: -------------------------------------------------------------------------------- 1 | import time as timer 2 | 3 | class Time(): 4 | """Manages simulation's time related properties 5 | 这段Python代码定义了一个名为 `Time` 的类,用于管理模拟的时间相关属性,特别是在执行瞬态(即随时间变化的)模拟时。以下是对类的构造器和方法的详细解释: 6 | 1. **类定义**: 7 | - `class Time():` 定义了一个名为 `Time` 的类,用于跟踪和更新模拟时间。 8 | 9 | 2. **构造器**: 10 | - `def __init__(self, Region):` 构造器接收一个参数 `Region`,它是一个包含模拟区域信息的对象。 11 | 12 | 3. **时间初始化**: 13 | - `self.tic = timer.time()`:记录模拟开始时的时间戳。 14 | - `self.startTime`:根据 `Region` 对象中的控制字典(`controlDict`)设置模拟的起始时间。 15 | - `self.currentTime`:初始化为起始时间,并将在模拟过程中更新。 16 | 17 | 4. **时间设置逻辑**: 18 | - 根据 `controlDict['startFrom']` 的值,确定模拟的起始时间: 19 | - `'startTime'`:使用 `controlDict` 中指定的起始时间。 20 | - `'latestTime'`:使用 `Region.timeSteps` 中的最大值作为当前时间,即从最新的时间步开始。 21 | - `'firstTime'`:使用 `Region.timeSteps` 中的最小值作为当前时间,即从最早的时间步开始。 22 | 23 | 5. **打印起始时间**: 24 | - `print('Start time: %.2f' % self.currentTime)`:打印模拟的起始时间。 25 | 26 | 6. **结束时间设置**: 27 | - `self.endTime`:从 `controlDict` 中获取模拟的结束时间。 28 | 29 | 7. **更新运行时间方法**: 30 | - `def cfdUpdateRunTime(self):` 更新模拟的当前运行时间和CPU时间。 31 | 32 | 8. **打印当前时间方法**: 33 | - `def cfdPrintCurrentTime(self):` 打印模拟的当前时间。 34 | 35 | 9. **执行瞬态循环方法**: 36 | - `def cfdDoTransientLoop(self):` 检查模拟的运行时间是否已达到结束时间,并返回相应的布尔值。 37 | 38 | ### 注意事项: 39 | - 类使用了 `time` 模块,并将其重命名为 `timer`,以便调用 `time()` 函数获取当前时间戳。 40 | - `Region` 对象预期包含 `dictionaries.controlDict` 和 `timeSteps` 属性,这些属性包含控制模拟的配置信息和时间步目录列表。 41 | - `self.cpuTime` 用于跟踪瞬态循环开始以来的CPU时间。 42 | `Time` 类是模拟过程中时间管理的关键组件,它提供了一种机制来跟踪和更新模拟时间,确保模拟按照预定的时间步长运行,并在达到设定的结束时间时停止。 43 | """ 44 | def __init__(self,Region): 45 | 46 | ##Time at beginning of transient loop 47 | self.tic=timer.time() 48 | ##Instance of simulation's region class 49 | # self.region=Region 50 | if Region.dictionaries.controlDict['startFrom']=='startTime': 51 | 52 | ##Specified start time of the simulation 53 | self.startTime=float(Region.dictionaries.controlDict['startTime']) 54 | 55 | ## The current time elapsed since the start of the simulation 56 | self.currentTime = self.startTime 57 | 58 | elif Region.dictionaries.controlDict['startFrom']=='latestTime': 59 | self.currentTime = float(max(Region.timeDictionary)) 60 | 61 | elif Region.dictionaries.controlDict['startFrom']=='firstTime': 62 | self.currentTime = float(min(Region.timeDictionary)) 63 | 64 | print('Start time: %.2f' % self.currentTime) 65 | 66 | ##Specified end time of the simulation 67 | self.endTime = float(Region.dictionaries.controlDict['endTime']) 68 | self.deltaT = Region.dictionaries.controlDict['deltaT'] 69 | self.writeInterval=Region.dictionaries.controlDict['writeInterval'] 70 | 71 | def cfdUpdateRunTime(self): 72 | """Increments the simulation's runTime, updates cpu time 73 | """ 74 | self.currentTime = self.currentTime + self.deltaT 75 | ## The elapsed cpu clock time since starting the transient loop 76 | self.cpuTime=timer.time()-self.tic 77 | print('cpu time: %0.4f [s]\n' % self.cpuTime) 78 | 79 | 80 | def cfdPrintCurrentTime(self): 81 | """Prints the current runTime""" 82 | print('\n\n Time: %0.4f [s]\n' % self.currentTime) 83 | 84 | def cfdDoTransientLoop(self): 85 | """ 86 | Checks too see if simulation runTime has reached the simulation's end time 87 | """ 88 | if self.currentTime < self.endTime: 89 | return True 90 | else: 91 | return False 92 | 93 | def cfdDoWriteTime(self): 94 | """ 95 | 检查每隔writeInterval时间步长是否需要写入数据 96 | """ 97 | if int(self.currentTime/self.deltaT) % int(self.writeInterval/self.deltaT)== 0: 98 | return True 99 | else: 100 | return False -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/IO.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/IO.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/Interpolate.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/Interpolate.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/Math.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/Math.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/Solve.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/Solve.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/Time.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/Time.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/base.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/base.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/cfdPlot.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/cfdPlot.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/dimensions.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/dimensions.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/__pycache__/quantities.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/cfdtool/__pycache__/quantities.cpython-310.pyc -------------------------------------------------------------------------------- /src/cfdtool/base.py: -------------------------------------------------------------------------------- 1 | # base.py 2 | 3 | # from dataclasses import dataclass, fields,field 4 | from config import ENABLE_DIMENSION_CHECK 5 | from cfdtool.quantities import Quantity 6 | 7 | # @dataclass 8 | class DimensionChecked: 9 | ''' 10 | DimensionChecked 使用了 @dataclass 装饰器,但没有显式定义 __init__ 方法。 11 | @dataclass 会自动生成一个 __init__ 方法,该方法初始化所有定义的字段。 12 | DimensionChecked 定义了 __post_init__ 方法,用于在初始化后执行量纲检查。 13 | ''' 14 | # 定义一个内部字典,用于存储字段名到预期量纲的映射 15 | # expected_dimensions: dict = field(default_factory=dict, init=False, repr=False) 16 | # _class_field_map: dict = field(init=False, default_factory=dict, repr=False) 17 | def __init__(self): 18 | # 如果 `expected_dimensions` 尚未设置,则初始化为空字典 19 | if not hasattr(self, 'expected_dimensions'): 20 | raise ValueError("No expected_dimensions") 21 | # 如果启用了量纲检查,进行量纲检查 22 | if ENABLE_DIMENSION_CHECK: 23 | self.check_dimensions() 24 | 25 | def check_dimensions(self): 26 | """ 27 | 在初始化之后进行量纲检查。 28 | 仅检查 expected_dimensions 中指定的字段。 29 | """ 30 | for name, expected_dim in self.expected_dimensions.items(): 31 | try: 32 | value = getattr(self, name) 33 | except AttributeError: 34 | raise AttributeError(f"'{self.__class__.__name__}' 对象没有属性 '{name}'") 35 | # print(f"Checking dimension for '{name}': expected {expected_dim}, got {value.dimension}") # 调试信息 36 | 37 | if isinstance(value, Quantity) and expected_dim is not None: 38 | if value.dimension != expected_dim: 39 | raise ValueError( 40 | f"量纲不匹配在字段 '{name}': 预期 {expected_dim}, 但得到 {value.dimension}" 41 | ) 42 | 43 | def __setattr__(self, name, value): 44 | """ 45 | 在属性赋值时进行量纲检查。 46 | """ 47 | if name == 'expected_dimensions': 48 | # 不对 expected_dimensions 本身进行量纲检查 49 | super().__setattr__(name, value) 50 | return 51 | 52 | # 如果启用了量纲检查,进行量纲检查 53 | if ENABLE_DIMENSION_CHECK and name in self.__dict__.get('expected_dimensions', {}): 54 | expected_dim = self.expected_dimensions[name] 55 | if isinstance(value, Quantity) and expected_dim is not None: 56 | if value.dimension != expected_dim: 57 | raise ValueError( 58 | f"赋值时量纲不匹配 '{name}': 预期 {expected_dim}, 但得到 {value.dimension}" 59 | ) 60 | super().__setattr__(name, value) 61 | 62 | -------------------------------------------------------------------------------- /src/cfdtool/cfdPlot.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import matplotlib.pyplot as plt 4 | 5 | def cfdPlotRes(path,equations): 6 | # Read data from file 7 | data = np.loadtxt(path+os.sep+'convergence'+os.sep+'convergenceUp.out', skiprows=1) 8 | 9 | # Return if no data yet is stored 10 | if data.size == 0: 11 | return 12 | 13 | # Extract data columns 14 | iters = data[:, 0] 15 | currentTime = data[:, 1] 16 | UxResInit = data[:, 2] 17 | UyResInit = data[:, 3] 18 | UzResInit = data[:, 4] 19 | pResInit = data[:, 5] 20 | TResInit = data[:, 9] 21 | 22 | # Clear previous plot 23 | plt.clf() 24 | 25 | # Plot residuals for each equation 26 | legendStringArray = [] 27 | # theEquationNames = model.equations 28 | 29 | for iEquation in equations: 30 | if iEquation.name == 'U': 31 | legendStringArray.append('Ux') 32 | legendStringArray.append('Uy') 33 | legendStringArray.append('Uz') 34 | 35 | plt.semilogy(iters, UxResInit, '-xr') 36 | plt.hold(True) 37 | plt.semilogy(iters, UyResInit, '-og') 38 | plt.hold(True) 39 | plt.semilogy(iters, UzResInit, '-+b') 40 | plt.hold(True) 41 | elif iEquation.name == 'p': 42 | legendStringArray.append('p-mass') 43 | plt.semilogy(iters, pResInit, '-c') 47 | 48 | # Set plot labels and legend 49 | plt.xlabel('Global Iterations') 50 | plt.ylabel('Scaled RMS Residuals') 51 | plt.legend(legendStringArray) 52 | 53 | # Set plot grid and axis limits 54 | plt.grid(True) 55 | plt.axis('tight') 56 | plt.ylim(1e-6, 1e2) 57 | 58 | # Pause to show the plot 59 | plt.pause(0.01) -------------------------------------------------------------------------------- /src/cfdtool/decorators.py: -------------------------------------------------------------------------------- 1 | # decorators.py 2 | from functools import wraps 3 | from config import ENABLE_DIMENSION_CHECK 4 | from quantities import Quantity 5 | 6 | def enforce_dimensions(func): 7 | """ 8 | 装饰器,用于在函数执行前后强制检查量纲一致性。 9 | """ 10 | def wrapper(*args, **kwargs): 11 | if not ENABLE_DIMENSION_CHECK: 12 | return func(*args, **kwargs) 13 | result = func(*args, **kwargs) 14 | return result 15 | return wrapper 16 | 17 | def check_input_dimension(expected_dimension): 18 | """ 19 | 装饰器,用于检查输入 Quantity 的量纲是否匹配。 20 | 21 | 参数: 22 | expected_dimension (Dimension): 期望的量纲。 23 | """ 24 | def decorator(func): 25 | @wraps(func) 26 | def wrapper(*args, **kwargs): 27 | for arg in args: 28 | if isinstance(arg, Quantity): 29 | if arg.dimension != expected_dimension: 30 | raise ValueError(f"参数 {arg} 的量纲不匹配。期望量纲为 {expected_dimension},实际量纲为 {arg.dimension}") 31 | for key, value in kwargs.items(): 32 | if isinstance(value, Quantity): 33 | if value.dimension != expected_dimension: 34 | raise ValueError(f"参数 {key} 的量纲不匹配。期望量纲为 {expected_dimension},实际量纲为 {value.dimension}") 35 | return func(*args, **kwargs) 36 | return wrapper 37 | return decorator 38 | 39 | def check_output_dimension(expected_dimension): 40 | """ 41 | 装饰器,用于检查输出 Quantity 的量纲是否匹配。 42 | 43 | 参数: 44 | expected_dimension (Dimension): 期望的量纲。 45 | """ 46 | def decorator(func): 47 | @wraps(func) 48 | def wrapper(*args, **kwargs): 49 | result = func(*args, **kwargs) 50 | if isinstance(result, Quantity): 51 | if result.dimension != expected_dimension: 52 | raise ValueError(f"输出的量纲不匹配。期望量纲为 {expected_dimension},实际量纲为 {result.dimension}") 53 | return result 54 | return wrapper 55 | return decorator 56 | 57 | def auto_calculate_dimension(operation): 58 | """ 59 | 装饰器,用于自动计算输出量纲,适用于乘法或除法运算。 60 | 61 | 参数: 62 | operation (str): 'multiply' 或 'divide',表示要执行的操作。 63 | """ 64 | def decorator(func): 65 | @wraps(func) 66 | def wrapper(*args, **kwargs): 67 | # 获取输入的 Quantity 对象 68 | quantities = [arg for arg in args if isinstance(arg, Quantity)] 69 | if len(quantities) < 2: 70 | raise ValueError("函数需要至少两个 Quantity 对象进行计算。") 71 | 72 | # 自动计算量纲 73 | if operation == 'multiply': 74 | result_dimension = quantities[0].dimension * quantities[1].dimension 75 | elif operation == 'divide': 76 | result_dimension = quantities[0].dimension / quantities[1].dimension 77 | else: 78 | raise ValueError("未知的操作类型:仅支持 'multiply' 或 'divide'") 79 | 80 | # 执行原始函数 81 | result_value = func(*args, **kwargs) 82 | 83 | return Quantity(result_value, result_dimension) 84 | 85 | return wrapper 86 | return decorator -------------------------------------------------------------------------------- /src/cfdtool/dimensions.py: -------------------------------------------------------------------------------- 1 | # value.py 2 | import numpy as np 3 | 4 | class Dimension: 5 | ''' 6 | 在 OpenFOAM 中,`value` 用于定义物理量的维度。每个物理量的维度是一个具有七个分量的数组,分别表示它在以下七个基础单位下的幂次[M, L, T, Θ, I, N, J]: 7 | 1. 质量(mass) 8 | 2. 长度(length) 9 | 3. 时间(time) 10 | 4. 温度(temperature) 11 | 5. 电流(electric current) 12 | 6. 物质的量(amount of substance) 13 | 7. 发光强度(luminous intensity) 14 | 这些单位遵循国际单位制(SI)。`value` 主要用于确保单位的正确性,并帮助 OpenFOAM 在计算过程中执行单位检查。 15 | value [M L T Θ I N J] 每个字母表示一个单位的幂次,按照如下解释: 16 | - `M`: 质量,单位 kg 17 | - `L`: 长度,单位 m 18 | - `T`: 时间,单位 s 19 | - `Θ`: 温度,单位 K 20 | - `I`: 电流,单位 A 21 | - `N`: 物质的量,单位 mol 22 | - `J`: 发光强度,单位 cd 23 | 在 OpenFOAM 中,物理量的维度通常写在文件开头。 24 | 速度的维度(单位 m/s)value [0 1 -1 0 0 0 0]; 25 | 压力的维度(单位 N/m² 或者 kg/(m·s²))value [1 -1 -2 0 0 0 0]; 26 | 体积:`[0 3 0 0 0 0 0]` (m³) 27 | 力:`[1 1 -2 0 0 0 0]` (N 或 kg·m/s²) 28 | 密度:`[1 -3 0 0 0 0 0]` (kg/m³) 29 | 通过这种方式,OpenFOAM 可以确保所有物理量的单位在计算中保持一致。如果在模拟过程中出现单位不匹配的问题,OpenFOAM 将给出错误信息。 30 | ''' 31 | __slots__ = ['_value'] 32 | 33 | def __init__(self, *args, **kwargs): 34 | if len(args) == 1 and isinstance(args[0], (list, tuple, np.ndarray)) and len(args[0]) == 7: 35 | dim_list = args[0] 36 | elif 'dim_list' in kwargs and isinstance(kwargs['dim_list'], (list, tuple, np.ndarray)) and len(kwargs['dim_list']) == 7: 37 | dim_list = kwargs['dim_list'] 38 | else: 39 | dim_list = [kwargs.get(key, 0) for key in ['M', 'L', 'T', 'Theta', 'I', 'N', 'J']] 40 | 41 | if len(dim_list) != 7: 42 | raise ValueError("dim_list must be a length-7 list, tuple, or NumPy array representing [M, L, T, Θ, I, N, J].") 43 | 44 | # 设置属性时确保只允许在初始化时赋值,后续不可修改 45 | value=np.array(dim_list, dtype=np.float64) 46 | # 将数组设置为只读,确保不可变性 47 | value.flags.writeable = False 48 | object.__setattr__(self, '_value', value) 49 | # self._value.flags.writeable = False 50 | 51 | @property 52 | def value(self): 53 | '''只读属性,返回维度数组''' 54 | return self._value 55 | 56 | def __dir__(self): 57 | '''控制 dir() 输出的属性,隐藏 _value,只暴露 value''' 58 | return ['value'] 59 | # def __dir__(self): 60 | # '''控制 dir() 输出的属性,隐藏 _value,只暴露 value 和其他属性''' 61 | # not_visible_attr = ['_value','special variables'] # 不可见属性列表 62 | # base_attrs = super().__dir__() # 获取所有现有属性 63 | # return [attr for attr in base_attrs if attr not in not_visible_attr] # 过滤掉 _value 64 | 65 | def __setattr__(self, name, value): 66 | # 禁止修改现有属性 67 | if name == 'value': 68 | raise AttributeError(f"Cannot modify the '{name}' attribute, it is read-only.") 69 | super().__setattr__(name, value) 70 | 71 | def __mul__(self, other)-> 'Dimension': 72 | if not isinstance(other, Dimension): 73 | raise TypeError("Multiplication is only supported between Dimension instances.") 74 | # new_dims = self.value + other.value 75 | return Dimension(self.value + other.value) 76 | 77 | def __truediv__(self, other)-> 'Dimension': 78 | if not isinstance(other, Dimension): 79 | raise TypeError("Division is only supported between Dimension instances.") 80 | # new_dims = self.value - other.value 81 | return Dimension(self.value - other.value) 82 | 83 | def __pow__(self, power)-> 'Dimension': 84 | if not isinstance(power, (int, float)): 85 | raise TypeError("Power must be an integer or float.") 86 | # new_dims = self.value * power 87 | return Dimension(self.value * power) 88 | 89 | def __eq__(self, other)-> bool: 90 | # if not isinstance(other, Dimension): 91 | # return False 92 | return isinstance(other, Dimension) and np.allclose(self.value, other.value, atol=1e-10) 93 | 94 | def __str__(self) -> str: 95 | return self.__repr__() 96 | 97 | def __repr__(self)-> str: 98 | labels = ['M', 'L', 'T', 'Θ', 'I', 'N', 'J'] 99 | # components = [] 100 | # for label, power in zip(labels, self.value): 101 | # if np.isclose(power, 0.0): 102 | # continue 103 | # elif np.isclose(power, 1.0): 104 | # components.append(f"{label}") 105 | # elif np.isclose(power, -1.0): 106 | # components.append(f"{label}^-1") 107 | # else: 108 | # components.append(f"{label}^{power}") 109 | # return " ".join(components) if components else "Dimensionless" 110 | # return f"Dimension({self.value.tolist()})" 111 | # return "Dimension(" + " ".join(self.__str__) + ")" 112 | components = [f"{label}^{power}" if not np.isclose(power, 1.0) else label 113 | for label, power in zip(labels, self.value) if not np.isclose(power, 0.0)] 114 | return " ".join(components) if components else "Dimensionless" 115 | 116 | def copy(self)-> 'Dimension': 117 | return Dimension(self.value.copy()) 118 | 119 | def grad(self)-> 'Dimension': 120 | # 复制 dimensions 对象,确保可修改 121 | value_copy = self.value.copy() 122 | value_copy[1] -= 1 123 | return Dimension(value_copy) 124 | 125 | # Define common dimensions 126 | 127 | dimless = Dimension() # Dimensionless 128 | 129 | mass_dim = Dimension(M=1) # Mass [kg] 130 | length_dim = Dimension(dim_list=[0, 1, 0, 0, 0, 0, 0]) # Length [m] 131 | time_dim = Dimension(T=1) # Time [s] 132 | temperature_dim = Dimension(dim_list=[0, 0, 0, 1, 0, 0, 0]) # Temperature [K] 133 | current_dim = Dimension(I=1) # Electric Current [A] 134 | amount_dim = Dimension(dim_list=[0, 0, 0, 0, 0, 1, 0]) # Amount of Substance [mol] 135 | luminous_intensity_dim = Dimension(J=1) # Luminous Intensity [cd] 136 | 137 | # Composite dimensions 138 | 139 | velocity_dim = length_dim / time_dim # Velocity [m/s] 140 | acceleration_dim = length_dim / time_dim**2 # Acceleration [m/s²] 141 | force_dim = mass_dim * acceleration_dim # Force [kg·m/s²] 142 | pressure_dim = force_dim / length_dim**2 # Pressure [kg/(m·s²)] 143 | density_dim = mass_dim / length_dim**3 # Density [kg/m³] 144 | volume_dim = length_dim**3 # Volume [m³] 145 | area_dim = length_dim**2 # Area [m²] 146 | flux_dim = mass_dim / time_dim # Flux [kg/s] -------------------------------------------------------------------------------- /src/cfdtool/quantities.py: -------------------------------------------------------------------------------- 1 | # quantities.py 2 | import numpy as np 3 | from config import ENABLE_DIMENSION_CHECK 4 | from cfdtool.dimensions import Dimension,dimless 5 | from typing import Callable, Any 6 | 7 | class Quantity: 8 | """ 9 | 表示带有量纲的物理量。 10 | 11 | Attributes: 12 | value (numpy.ndarray): 数值部分,可变的。 13 | dimension (Dimension): 量纲部分,不可变的。 14 | """ 15 | __array_priority__ = 20 # 设置优先级高于 numpy.ndarray 16 | __slots__ = ['__value', '__dimension'] 17 | 18 | def __init__(self, value, dimension=Dimension(),copy=True): 19 | """ 20 | 初始化 Quantity 对象。 21 | 22 | 参数: 23 | value (int, float, list, tuple, numpy.ndarray): 数值,可以是标量或数组。 24 | dimension (Dimension): 量纲对象,表示物理量的量纲。 25 | 在 __init__ 和 value 的 setter 中,对于标量值(int 和 float),建议统一存储为 float 类型。这有助于避免在后续的算术运算中出现类型不一致的问题。 26 | """ 27 | if isinstance(value, np.ndarray): 28 | if copy: 29 | self.__value = value.astype(float).copy() 30 | else: 31 | self.__value = value 32 | elif isinstance(value, (float, int)): 33 | self.__value = np.array([float(value)], dtype=float) # 统一为 float 34 | elif isinstance(value, (list, tuple)): 35 | self.__value = np.array(value, dtype=float) # 统一为 float 36 | else: 37 | raise TypeError("value 必须是 int、float、list、tuple 或 numpy.ndarray 类型。") 38 | # self.__value = np.array(value, dtype=float) if isinstance(value, (list, tuple, np.ndarray)) else np.array([float(value)], dtype=float) 39 | 40 | if not isinstance(dimension, Dimension): 41 | raise TypeError("dimension 必须是 Dimension 类的实例。") 42 | 43 | self.__dimension = dimension # Dimension 已经是不可变的 44 | 45 | def __dir__(self): 46 | """ 47 | 重写 __dir__ 方法,隐藏内部属性,使其不出现在 dir() 的结果中。 48 | """ 49 | # return [attr for attr in super().__dir__() if not attr.startswith('_Quantity__')] 50 | return ['value', 'dimension'] 51 | 52 | @property 53 | def value(self)-> np.ndarray: 54 | """ 55 | 公开只读属性,便于访问修改数值部分。 56 | 获取数值部分。 57 | 58 | 返回: 59 | numpy.ndarray: 数值数组。 60 | """ 61 | return self.__value 62 | 63 | @value.setter 64 | def value(self, new_value): 65 | """ 66 | 设置数值部分。 67 | 68 | 参数: 69 | new_value (int, float, list, tuple, numpy.ndarray): 新的数值,可以是标量或数组。 70 | """ 71 | if isinstance(new_value, np.ndarray): 72 | self.__value = new_value.astype(float).copy() # 确保数值为 float 73 | elif isinstance(new_value, (int, float)): 74 | self.__value = np.array([float(new_value)], dtype=float) # 统一为 float 75 | elif isinstance(new_value, (list, tuple)): 76 | self.__value = np.array(new_value, dtype=float) # 统一为 float 77 | else: 78 | raise TypeError("new_value 必须是 int、float、list、tuple 或 numpy.ndarray 类型。") 79 | 80 | @property 81 | def dimension(self)-> Dimension: 82 | """ 83 | 此为只读属性,便于访问,不可修改! 84 | 获取量纲部分。 85 | 86 | 返回: 87 | Dimension: 量纲对象。 88 | """ 89 | return self.__dimension 90 | 91 | # def __array__(self, dtype=None): 92 | # """ 93 | # 重载 __array__ 方法,以便在使用 NumPy 函数时返回数值部分。 94 | # """ 95 | # if not ENABLE_DIMENSION_CHECK: 96 | # return np.asarray(self.value, dtype=dtype) 97 | 98 | def _check_dimension(self, other): 99 | if ENABLE_DIMENSION_CHECK and self.dimension != other.dimension: 100 | raise ValueError( 101 | f"无法与不同量纲的 Quantity 对象相加/相减:{self.dimension} 和 {other.dimension}。" 102 | ) 103 | 104 | def _apply_operation(self, other, op)-> 'Quantity': 105 | if isinstance(other, Quantity): 106 | # Add and subtract operations require identical dimensions 107 | if op in (np.add, np.subtract): 108 | self._check_dimension(other) 109 | new_value = op(self.value, other.value) 110 | return Quantity(new_value, self.dimension) 111 | # Multiplication and division allow different dimensions 112 | elif op in (np.multiply, np.divide): 113 | new_value = op(self.value, other.value) 114 | new_dimension = self.dimension * other.dimension if op == np.multiply else self.dimension / other.dimension 115 | return Quantity(new_value, new_dimension) 116 | elif isinstance(other, (int, float, np.ndarray)): 117 | new_value = op(self.value, other) 118 | if op in (np.add, np.subtract): 119 | if ENABLE_DIMENSION_CHECK and self.dimension != dimless: 120 | raise ValueError(f"Cannot add or subtract a scalar to a Quantity with dimensions: {self.dimension}") 121 | return Quantity(new_value, self.dimension) 122 | elif op in (np.multiply, np.divide): 123 | return Quantity(new_value, self.dimension) 124 | else: 125 | raise TypeError("Operation is only supported between Quantity and scalar values.") 126 | 127 | # 二元操作符重载 128 | def __add__(self, other): return self._apply_operation(other, np.add) 129 | def __sub__(self, other): return self._apply_operation(other, np.subtract) 130 | def __mul__(self, other): return self._apply_operation(other, np.multiply) 131 | def __truediv__(self, other): return self._apply_operation(other, np.divide) 132 | 133 | # 左操作符重载 134 | def __radd__(self, other): return self.__add__(other) 135 | def __rsub__(self, other): return Quantity(-self.value, self.dimension) + other 136 | def __rmul__(self, other): return self.__mul__(other) 137 | def __rtruediv__(self, other): 138 | if not isinstance(other, (int, float, np.ndarray)): 139 | raise TypeError("Division is only supported between scalars, arrays, and Quantity values.") 140 | return Quantity(other / self.value, dimless / self.dimension) 141 | 142 | # 一元负号 143 | def __neg__(self) -> 'Quantity': return Quantity(-self.value, self.dimension) 144 | 145 | # 幂运算符重载 146 | def __pow__(self, power) -> 'Quantity': 147 | if not isinstance(power, (int, float)): 148 | raise TypeError("Power must be an integer or float.") 149 | return Quantity(self.value ** power, self.dimension ** power) 150 | 151 | # 比较运算符 152 | def __eq__(self, other) -> bool: 153 | return isinstance(other, Quantity) and np.array_equal(self.value, other.value) and self.dimension == other.dimension 154 | 155 | # 原地操作符重载 156 | def __iadd__(self, other)-> 'Quantity': 157 | """ 158 | 重载原地加法运算符(+=)。 159 | 160 | 参数: 161 | other (Quantity 或 scalar): 另一个 Quantity 对象或标量。 162 | 163 | 返回: 164 | self: 修改后的 Quantity 对象。 165 | 166 | 异常: 167 | TypeError: 如果 other 不是 Quantity 实例或标量。 168 | ValueError: 如果量纲不匹配且启用了量纲检查。 169 | """ 170 | if isinstance(other, Quantity): 171 | self._check_dimension(other) 172 | self.__value += other.value # 直接修改内部数组 173 | elif isinstance(other, (int, float)) and self.dimension == dimless: 174 | self.__value += other # 直接修改内部数组 175 | else: 176 | raise TypeError("原地加法仅支持 Quantity 实例之间或无量纲 Quantity 与标量之间的相加。") 177 | return self 178 | 179 | def __isub__(self, other)-> 'Quantity': 180 | """ 181 | 重载原地减法运算符(-=)。 182 | 183 | 参数: 184 | other (Quantity 或 scalar): 另一个 Quantity 对象或标量。 185 | 186 | 返回: 187 | self: 修改后的 Quantity 对象。 188 | 189 | 异常: 190 | TypeError: 如果 other 不是 Quantity 实例或标量。 191 | ValueError: 如果量纲不匹配且启用了量纲检查。 192 | """ 193 | if isinstance(other, Quantity): 194 | self._check_dimension(other) 195 | self.value -= other.value 196 | elif isinstance(other, (int, float)) and self.dimension == dimless: 197 | self.value -= other 198 | else: 199 | raise TypeError("原地减法仅支持 Quantity 实例之间或无量纲 Quantity 与标量之间的相减。") 200 | return self 201 | 202 | def __imul__(self, other)-> 'Quantity': 203 | """ 204 | 重载原地乘法运算符(*=)。 205 | *=乘法运算量纲会改变,因此仅支持无量纲的 Quantity 对象或标量。 206 | """ 207 | if isinstance(other, Quantity) and other.dimension == dimless: 208 | self.value *= other.value 209 | elif isinstance(other, (int, float)): 210 | self.value *= other 211 | else: 212 | raise TypeError("原地乘法仅支持 Quantity 对象或标量。") 213 | return self 214 | 215 | def __itruediv__(self, other)-> 'Quantity': 216 | """ 217 | 重载原地除法运算符(/=)。 218 | /=除法运算量纲会改变,因此仅支持无量纲的 Quantity 对象或标量。 219 | """ 220 | if isinstance(other, Quantity) and other.dimension == dimless: 221 | self.value /= other.value 222 | elif isinstance(other, (int, float)): 223 | self.value /= other 224 | else: 225 | raise TypeError("原地除法仅支持 Quantity 对象或标量。") 226 | return self 227 | 228 | # 类的字符串表示 229 | def __repr__(self)-> str: 230 | """ 231 | 返回对象的官方字符串表示,优化以避免处理大型数组时过慢。 232 | 233 | 返回: 234 | str: 对象的字符串表示。 235 | """ 236 | max_elements = 10 # 最大显示的元素数量 237 | if self.value.size > max_elements: 238 | # 只显示前几个元素,并指示省略 239 | displayed_value = f"{self.value[:max_elements]}... (total {self.value.size} elements)" 240 | else: 241 | displayed_value = self.value 242 | # return f"Quantity(value={displayed_value}, dimension={self.dimension})" 243 | return f"Quantity(value={displayed_value}, shape={self.__value.shape}, dimension={self.dimension})" 244 | 245 | 246 | def __str__(self) -> str: 247 | return self.__repr__() 248 | 249 | def __getitem__(self, key)-> 'Quantity': 250 | """ 251 | 重载切片操作符,以支持返回带量纲的 Quantity 对象。 252 | 253 | 参数: 254 | key (int, slice): 切片索引或整数索引。 255 | 256 | 返回: 257 | Quantity: 切片后的 Quantity 对象。 258 | """ 259 | # 使用 self.value[key] 获取切片后的数值部分 260 | # sliced_value = self.__value[key] 261 | # print(f"切片键: {key}, 切片后的值: {sliced_value}") 262 | # 不在 __init__ 中重新创建数组,直接传入现有的 ndarray 263 | sliced_value = self.value[key] # 这是一个视图 264 | return Quantity(sliced_value, self.dimension, copy=False) 265 | 266 | def __setitem__(self, key, value): 267 | """ 268 | 重载赋值操作符,以支持直接修改数组部分的值。 269 | 270 | 参数: 271 | key (int, slice): 要修改的索引或切片。 272 | value (int, float, list, numpy.ndarray): 新的值,可以是标量或数组。 273 | """ 274 | # 这里检查传入值是否与当前存储的数据类型兼容 275 | if isinstance(value, Quantity): 276 | # 如果传入的是 Quantity 对象,确保量纲相同 277 | self._check_dimension(value) 278 | self.__value[key] = value.value.copy() 279 | elif isinstance(value, (int, float)) and self.dimension == dimless: 280 | self.__value[key] = float(value) 281 | # elif isinstance(value, (list, tuple, np.ndarray)): 282 | # self.__value[key] = np.array(value, dtype=float) 283 | else: 284 | raise TypeError("赋值值必须是 Quantity 实例、标量或数组。") 285 | 286 | 287 | # def magnitude(self): 288 | # """ 289 | # 获取数值部分的拷贝。 290 | 291 | # 返回: 292 | # numpy.ndarray: 数值数组的拷贝。 293 | # """ 294 | # return self.value.copy() 295 | 296 | def copy(self)-> 'Quantity': 297 | """ 298 | 创建 Quantity 对象的副本。 299 | 300 | 返回: 301 | Quantity: 副本对象。 302 | """ 303 | return Quantity(self.value.copy(), self.dimension) 304 | 305 | def apply(self, func: Callable[..., Any], *args, **kwargs)-> 'Quantity': 306 | """ 307 | 对 Quantity 的数值部分应用一个函数,并更新数值部分。 308 | 309 | 参数: 310 | func (Callable): 要应用的函数,例如 np.squeeze。 311 | *args: 传递给 func 的位置参数。 312 | **kwargs: 传递给 func 的关键字参数。 313 | """ 314 | if not callable(func): 315 | raise TypeError("func 必须是可调用的函数。") 316 | 317 | new_value = func(self.value, *args, **kwargs) 318 | self.value = new_value 319 | return self 320 | 321 | def __array_ufunc__(self, ufunc, method, *inputs, **kwargs)-> 'Quantity': 322 | if method != '__call__': 323 | return NotImplemented 324 | 325 | # 分离 Quantity 对象和其他输入 326 | values = [] 327 | dimensions = [] 328 | for input_ in inputs: 329 | if isinstance(input_, Quantity): 330 | values.append(input_.value) 331 | dimensions.append(input_.dimension) 332 | else: 333 | values.append(input_) 334 | dimensions.append(dimless) # 假设标量为无量纲 335 | 336 | # 处理不同的 ufunc 337 | if ufunc in (np.add, np.subtract,np.maximum,np.minimum): 338 | # 检查所有 Quantity 对象的量纲是否相同 339 | base_dim = dimensions[0] 340 | for dim in dimensions[1:]: 341 | if dim != base_dim: 342 | raise ValueError(f"无法对不同量纲的 Quantity 对象进行运算:{dimensions}") 343 | result_value = getattr(ufunc, method)(*values, **kwargs) 344 | return Quantity(result_value, base_dim) 345 | 346 | elif ufunc in (np.multiply, np.divide): 347 | # 计算新的量纲 348 | if ufunc == np.multiply: 349 | new_dimension = dimensions[0] 350 | for dim in dimensions[1:]: 351 | new_dimension = new_dimension * dim 352 | else: # np.divide 353 | new_dimension = dimensions[0] 354 | for dim in dimensions[1:]: 355 | new_dimension = new_dimension / dim 356 | result_value = getattr(ufunc, method)(*values, **kwargs) 357 | return Quantity(result_value, new_dimension) 358 | 359 | elif ufunc == np.power: 360 | # 处理幂运算,假设幂次为标量 361 | if not isinstance(inputs[1], (int, float)): 362 | raise TypeError("幂次必须是标量") 363 | power = inputs[1] 364 | new_dimension = dimensions[0] ** power 365 | result_value = getattr(ufunc, method)(inputs[0].value, power, **kwargs) 366 | return Quantity(result_value, new_dimension) 367 | 368 | else: 369 | # 对于其他未处理的 ufunc,返回 NotImplemented 370 | return NotImplemented 371 | 372 | def __array_function__(self, func, types, args, kwargs) -> 'Quantity': 373 | # Check if the function should be handled 374 | if not all(issubclass(t, Quantity) for t in types): 375 | return NotImplemented 376 | 377 | # 提取所有 Quantity 对象的 value 378 | new_args = [] 379 | new_kwargs = {} 380 | for i, arg in enumerate(args): 381 | if isinstance(arg, Quantity): 382 | new_args.append(arg.value) 383 | else: 384 | new_args.append(arg) 385 | 386 | for key, value in kwargs.items(): 387 | if isinstance(value, Quantity): 388 | new_kwargs[key] = value.value 389 | else: 390 | new_kwargs[key] = value 391 | 392 | # Categorize functions 393 | elementwise_funcs = { 394 | np.sin, np.cos, np.tan, np.arcsin, np.arccos, 395 | np.arctan, np.arctan2, np.arcsinh, np.arccosh, np.arctanh, 396 | np.exp, np.log, np.sqrt, np.abs, np.sign, 397 | # Add more element-wise functions as needed 398 | } 399 | 400 | aggregation_funcs = { 401 | np.sum, np.mean, np.min, np.max, np.prod, np.std, np.var, 402 | # Add more aggregation functions as needed 403 | } 404 | 405 | reshaping_funcs = { 406 | np.squeeze, np.expand_dims, np.reshape, 407 | # Add more reshaping functions as needed 408 | } 409 | 410 | if func in elementwise_funcs: 411 | # Apply the function to the value 412 | result_value = func(*new_args, **new_kwargs) 413 | # Handle dimension changes based on function semantics 414 | # Example: trigonometric functions require dimensionless inputs 415 | trigonometric_funcs = {np.sin, np.cos, np.tan, np.arcsin, np.arccos, np.arctan, np.arctan2, np.arcsinh, np.arccosh, np.arctanh} 416 | if func in trigonometric_funcs: 417 | quantity = args[0] 418 | if quantity.dimension.dimensions==dimless: 419 | # Result is dimensionless 420 | return Quantity(result_value, Quantity.dimless) 421 | else: 422 | raise ValueError(f"Cannot apply {func.__name__} to Quantity with dimension {quantity.dimension}") 423 | elif func==np.sqrt: 424 | # Absolute value and sign functions preserve dimensions 425 | return Quantity(result_value, args[0].dimension**0.5) 426 | else: 427 | # For other element-wise functions, assume dimensions are preserved 428 | # This may not be accurate for all functions and may need customization 429 | return Quantity(result_value, args[0].dimension) 430 | 431 | elif func in aggregation_funcs: 432 | # Apply the function to the value 433 | result_value = func(*new_args, **new_kwargs) 434 | # Aggregation typically reduces dimensions, but depends on context 435 | # Here, assume dimensions are preserved 436 | return Quantity(result_value, args[0].dimension) 437 | 438 | elif func in reshaping_funcs: 439 | # Apply the function to the value 440 | result_value = func(*new_args, **new_kwargs) 441 | # Reshaping doesn't change dimensions 442 | return Quantity(result_value, args[0].dimension) 443 | 444 | else: 445 | # For other functions, attempt a generic application 446 | try: 447 | result_value = func(*new_args, **new_kwargs) 448 | if isinstance(result_value, np.ndarray) or isinstance(result_value, (int, float)): 449 | # Assume dimensions are preserved 450 | return Quantity(result_value, args[0].dimension) 451 | else: 452 | return NotImplemented 453 | except: 454 | return NotImplemented -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | # config.py 2 | ENABLE_DIMENSION_CHECK = True # 在开发和调试阶段启用量纲检查 3 | cfdIsCompressible=False 4 | pp_nonlinear_corrected=False -------------------------------------------------------------------------------- /src/pyFVM/Coefficients.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class Coefficients(): 5 | 6 | def __init__(self,Region): 7 | ''' 8 | 这段Python代码定义了一个名为`Coefficients`的类,它用于设置计算流体动力学(CFD)模拟中求解方程组所需的系数。以下是对类的构造器和`setupCoefficients`方法的详细解释: 9 | 10 | 1. **类定义**: 11 | - `class Coefficients():` 定义了一个名为`Coefficients`的类。 12 | 13 | 2. **构造器**: 14 | - `def __init__(self, Region):` 构造器接收一个参数`Region`,它是一个包含模拟区域信息的对象。 15 | 16 | 3. **实例属性**: 17 | - `self.region`: 存储传入的`Region`实例,作为类的局部属性。 18 | 19 | 4. **初始化系数方法**: 20 | - `def setupCoefficients(self, **kwargs):` 定义了一个实例方法,用于设置求解方程所需的系数数组。接收任意数量的关键字参数。 21 | 22 | 5. **处理关键字参数**: 23 | - 如果没有提供关键字参数(`len(kwargs) == 0`),则使用`self.region.mesh.elementNeighbours`作为连接性信息。 24 | 25 | 6. **设置连接性数组**: 26 | - `self.theCConn`: 存储网格元素的邻居元素列表,与`polyMesh.elementNeighbours`结构相同。 27 | 28 | 7. **计算每个元素的邻居数量**: 29 | - `self.theCSize`: 一个数组,包含每个元素的邻居元素数量。 30 | 31 | 8. **初始化系数数组**: 32 | - `self.ac`: 一个数组,包含每个单元格中心对通量项的贡献,通常是常数和恒定的扩散系数。 33 | - `self.ac_old`: 与`self.ac`类似,但用于上一时间步。 34 | - `self.bc`: 一个数组,包含边界条件对通量项的贡献。 35 | 36 | 9. **设置邻接元素系数**: 37 | - `self.anb`: 一个列表的列表,为每个元素的每个邻居元素设置系数。 38 | 39 | 10. **初始化其他数组**: 40 | - `self.dc`: 一个数组,可能用于存储与对流或扩散相关的系数。 41 | - `self.rc`: 一个数组,可能用于存储源项或其他常数项。 42 | - `self.dphi`: 一个数组,可能用于存储场变量的增量。 43 | 44 | ### 注意事项: 45 | - 类的构造器中调用了`setupCoefficients`方法,但没有提供任何关键字参数,因此方法会使用`Region`实例的网格连接性信息。 46 | - `self.theCConn`和`self.theCSize`基于网格的拓扑结构进行初始化,这对于后续计算邻居元素的贡献是必要的。 47 | - 代码中的注释提供了一些额外信息,例如`self.ac`和`self.ac_old`的区别,以及可能需要进一步确认的点。 48 | - `self.anb`的初始化使用了列表推导式,为每个元素创建了一个长度等于其邻居数量的零列表。 49 | 50 | `Coefficients`类是CFD模拟中用于管理方程求解过程中的系数的一个辅助类。通过这种方式,可以方便地访问和操作与网格拓扑结构相关的系数。 51 | ''' 52 | 53 | ## local attribute of simulation's region instance 54 | # self.region=Region 55 | # self.setupCoefficients() 56 | 57 | # def setupCoefficients(self,**kwargs): 58 | # """Setups empty arrays containing the coefficients (ac and bc) required to solve the system of equations 59 | # """ 60 | # if len(kwargs)==0: 61 | 62 | ## (list of lists) identical to polyMesh.elementNeighbours. Provides a list where each index represents an element in the domain. Each index has an associated list which contains the elements for which is shares a face (i.e. the neighouring elements). 63 | self.theCConn = Region.mesh.elementNeighbours 64 | 65 | ## array containing the number of neighbouring elements for each element in the domain 66 | self.theCSize = np.array([int(len(neighbours)) for neighbours in self.theCConn],dtype=np.int32) # 强制转换为整数 67 | 68 | theNumberOfElements=int(len(self.theCConn)) 69 | 70 | self.NumberOfElements=theNumberOfElements 71 | 72 | ## array of cell-centered contribution to the flux term. These are constants and constant diffusion coefficients and therefore act as 'coefficients' in the algebraic equations. See p. 229 Moukalled. 73 | self.ac=np.zeros((theNumberOfElements),dtype=np.float64) 74 | 75 | ## see ac, however this is for the previous timestep? Check this later when you know more. 76 | self.ac_old=np.zeros((theNumberOfElements),dtype=np.float64) 77 | 78 | ## array of the boundary condition contributions to the flux term. 79 | self.bc=np.zeros((theNumberOfElements),dtype=np.float64) 80 | 81 | # 使用NumPy对象数组,允许每个元素的邻居数不一样 82 | self.anb = [np.zeros(len(neighbors), dtype=np.float64) for neighbors in self.theCConn] 83 | # self.anb = np.empty(theNumberOfElements, dtype=object) 84 | # for iElement in range(theNumberOfElements): 85 | # # easiest way to make a list of zeros of defined length ... 86 | # self.anb[iElement] = np.zeros(int(self.theCSize[iElement]),dtype=float) 87 | 88 | self.dphi=np.zeros((theNumberOfElements),dtype=np.float64) 89 | self._A_sparse_needs_update = True 90 | # self.dc=np.zeros((theNumberOfElements),dtype=float) 91 | # self.rc=np.zeros((theNumberOfElements),dtype=float) 92 | 93 | def cfdZeroCoefficients(self): 94 | # ========================================================================== 95 | # Routine Description: 96 | # This function zeros the coefficients 97 | # -------------------------------------------------------------------------- 98 | 99 | # array of cell-centered contribution to the flux term. These are constants and constant diffusion coefficients and therefore act as 'coefficients' in the algebraic equations. See p. 229 Moukalled. 100 | self.ac.fill(0) 101 | self.ac_old.fill(0) 102 | # array of the boundary condition contributions to the flux term. 103 | self.bc.fill(0) 104 | # reset the anb list of lists 105 | for iElement in range(self.NumberOfElements): 106 | self.anb[iElement].fill(0) 107 | self.dphi.fill(0) 108 | self._A_sparse_needs_update = True 109 | # self.dc.fill(0) 110 | # self.rc.fill(0) 111 | 112 | def assemble_sparse_matrix_coo(self): 113 | """ 114 | Assemble the sparse matrix A from ac, anb, and cconn. 115 | Args: 116 | ac (ndarray): Diagonal elements of the matrix A. 117 | anb (list of lists): Off-diagonal neighbor elements of A. 118 | cconn (list of lists): Connectivity (indices of neighbors) for each row. 119 | 120 | Returns: 121 | A_sparse (scipy.sparse.csr_matrix): The assembled sparse matrix in CSR format. 122 | """ 123 | numberOfElements = self.NumberOfElements 124 | if not hasattr(self, '_coo_structure'): 125 | # row_indices = [] 126 | # col_indices = [] 127 | # # Add diagonal elements 128 | # for i in range(numberOfElements): 129 | # row_indices.append(i) 130 | # col_indices.append(i) 131 | # # Add off-diagonal elements 132 | # for i in range(numberOfElements): 133 | # neighbors = self.theCConn[i] 134 | # anb_values = self.anb[i] 135 | # for j_, j in enumerate(neighbors): 136 | # row_indices.append(i) 137 | # col_indices.append(j) 138 | # self._coo_row_indices=np.array(row_indices, dtype=np.int32) 139 | # self._coo_col_indices=np.array(col_indices, dtype=np.int32) 140 | 141 | # Precompute the row and column indices 142 | # Diagonal indices 143 | diag_indices = np.arange(numberOfElements, dtype=np.int32) 144 | 145 | # Off-diagonal indices 146 | off_diag_row_indices = [] 147 | off_diag_col_indices = [] 148 | for i in range(numberOfElements): 149 | neighbors = self.theCConn[i] 150 | off_diag_row_indices.extend([i] * len(neighbors)) 151 | off_diag_col_indices.extend(neighbors) 152 | 153 | # Combine indices 154 | self._coo_row_indices = np.concatenate([diag_indices, np.array(off_diag_row_indices, dtype=np.int32)]) 155 | self._coo_col_indices = np.concatenate([diag_indices, np.array(off_diag_col_indices, dtype=np.int32)]) 156 | self._coo_structure = True 157 | 158 | # data = [] 159 | # # Add diagonal elements 160 | # for i in range(numberOfElements): 161 | # data.append(self.ac[i]) 162 | # # Add off-diagonal elements 163 | # for i in range(numberOfElements): 164 | # neighbors = self.theCConn[i] 165 | # anb_values = self.anb[i] 166 | # for j_, j in enumerate(neighbors): 167 | # data.append(anb_values[j_]) 168 | # data=np.array(data, dtype=np.float64) 169 | # Assemble data array 170 | # Diagonal data 171 | diag_data = self.ac.astype(np.float64) 172 | # Off-diagonal data 173 | off_diag_data = np.concatenate(self.anb).astype(np.float64) 174 | # Combine data 175 | data = np.concatenate([diag_data, off_diag_data]) 176 | 177 | if not hasattr(self, '_A_sparse'): 178 | from scipy.sparse import coo_matrix 179 | # Create the sparse matrix in COO format 180 | A_coo = coo_matrix((data, (self._coo_row_indices, self._coo_col_indices)), shape=(numberOfElements, numberOfElements)) 181 | # Convert to CSR format for efficient arithmetic and solving 182 | self._A_sparse = A_coo.tocsr() 183 | else: 184 | # Update existing data array 185 | self._A_sparse.data[:numberOfElements] = diag_data 186 | self._A_sparse.data[numberOfElements:] = off_diag_data 187 | self._A_sparse_needs_update = False 188 | 189 | 190 | def assemble_sparse_matrix_csr(self): 191 | """ 192 | 使用 Numpy 数组将 ac, anb 和 cconn 组装成 CSR 格式的稀疏矩阵。 193 | 194 | 参数: 195 | ac (ndarray): 矩阵 A 的对角元素。 196 | anb (list of lists): 矩阵 A 的非对角(邻接)元素。 197 | cconn (list of lists): 每一行的邻接(邻居的索引)。 198 | 199 | 返回: 200 | data (ndarray): 矩阵的非零值。 201 | indices (ndarray): 非零值对应的列索引。 202 | indptr (ndarray): 每一行在 data 和 indices 中的起始位置索引。 203 | """ 204 | NumberOfElements=self.NumberOfElements 205 | if not hasattr(self, '_csr_structure'): 206 | # 组装矩阵结构部分(indices 和 indptr) 207 | # indices = [] 208 | # indptr = [0] 209 | # for i in range(self.NumberOfElements): 210 | # # 添加对角线索引 211 | # indices.append(i) 212 | # # 添加非对角线索引 213 | # indices.extend(self.theCConn[i]) 214 | # indptr.append(len(indices)) 215 | # self._indices = np.array(indices, dtype=np.int32) 216 | # self._indptr = np.array(indptr, dtype=np.int32) 217 | indptr = np.zeros(NumberOfElements + 1, dtype=np.int32) 218 | indices = [] 219 | for i in range(NumberOfElements): 220 | neighbors = self.theCConn[i] 221 | # Diagonal and off-diagonal indices 222 | row_indices = [i] + neighbors 223 | indices.extend(row_indices) 224 | indptr[i + 1] = indptr[i] + len(row_indices) 225 | self._indices = np.array(indices, dtype=np.int32) 226 | self._indptr = indptr 227 | self._data = np.zeros(len(self._indices), dtype=np.float64) 228 | self._csr_structure = True 229 | 230 | # Assemble data array 231 | # 组装数据部分(按行顺序) 232 | for i in range(NumberOfElements): 233 | start = self._indptr[i] 234 | self._data[start] = self.ac[i] 235 | self._data[start + 1:start + 1 + len(self.anb[i])] = self.anb[i] 236 | 237 | 238 | if not hasattr(self, '_A_sparse'): 239 | from scipy.sparse import csr_matrix 240 | self._A_sparse = csr_matrix((self._data, self._indices, self._indptr), shape=(NumberOfElements, NumberOfElements)) 241 | else: 242 | # Update existing data array 243 | self._A_sparse.data = self._data 244 | self._A_sparse_needs_update = False 245 | 246 | 247 | 248 | def assemble_sparse_matrix_lil(self): 249 | NumberOfElements = self.NumberOfElements 250 | from scipy.sparse import lil_matrix 251 | 252 | A = lil_matrix((NumberOfElements, NumberOfElements), dtype=np.float64) 253 | for i in range(NumberOfElements): 254 | A[i, i] = self.ac[i] 255 | for j, neighbor in enumerate(self.theCConn[i]): 256 | A[i, neighbor] = self.anb[i][j] 257 | self._A_sparse = A.tocsr() 258 | self._A_sparse_needs_update = False 259 | return self._A_sparse 260 | 261 | 262 | def cfdComputeResidualsArray(self): 263 | Adphi=self.theCoefficients_Matrix_multiplication(self.dphi) 264 | rc= self.bc-Adphi 265 | return rc 266 | 267 | def theCoefficients_Matrix_multiplication(self,d): 268 | # if hasattr(self, '_A_sparse') and not self._A_sparse_needs_update: 269 | # Ad = self._A_sparse @ d 270 | # else: 271 | # Ad = np.zeros_like(d) 272 | # for iElement in range(self.NumberOfElements): 273 | # Ad[iElement] = self.ac[iElement] * d[iElement] 274 | # for iLocalNeighbour,neighbor in enumerate(self.theCConn[iElement]): 275 | # Ad[iElement] += self.anb[iElement][iLocalNeighbour] * d[neighbor] 276 | return self.theCoefficients_sparse_multiplication(d) 277 | 278 | def theCoefficients_sparse_multiplication(self,d): 279 | if not hasattr(self, '_A_sparse') or self._A_sparse_needs_update: 280 | self.assemble_sparse_matrix() 281 | return self._A_sparse @ d 282 | 283 | def assemble_sparse_matrix(self,method='csr'): 284 | if not hasattr(self, '_A_sparse') or self._A_sparse_needs_update: 285 | if method=='csr': 286 | self.assemble_sparse_matrix_csr() 287 | elif method=='coo': 288 | self.assemble_sparse_matrix_coo() 289 | else: 290 | raise ValueError(f"Unknown method {method}") 291 | return self._A_sparse 292 | 293 | 294 | 295 | 296 | 297 | def verify_matrix_properties(self): 298 | from scipy.sparse.linalg import norm 299 | # 检查对称性:计算 Frobenius 范数 300 | symmetry_error = norm(self._A_sparse - self._A_sparse.T, ord='fro') 301 | if symmetry_error > 1e-6: 302 | raise ValueError(f"矩阵 A 不是对称的,对称性误差为 {symmetry_error}") 303 | 304 | # 检查正定性 305 | if np.any(self._A_sparse.diagonal() <= 0): 306 | raise ValueError("矩阵 A 不是正定的") 307 | 308 | -------------------------------------------------------------------------------- /src/pyFVM/Equation.py: -------------------------------------------------------------------------------- 1 | # import os 2 | import numpy as np 3 | import cfdtool.Math as mth 4 | # import pyFVM.Coefficients as coefficients 5 | # import pyFVM.Solve as solve 6 | # import pyFVM.Scalar as scalar 7 | # import pyFVM.Assemble as assemble 8 | 9 | class Equation(): 10 | 11 | def __init__(self,fieldName): 12 | 13 | # self.Region = Region 14 | self.name=fieldName 15 | self.initializeResiduals() 16 | 17 | def initializeResiduals(self): 18 | if self.name == 'U': 19 | self.rmsResidual =[1,1,1] 20 | self.maxResidual =[1,1,1] 21 | self.initResidual =[1,1,1] 22 | self.finalResidual =[1,1,1] 23 | else: 24 | self.rmsResidual = 1 25 | self.maxResidual = 1 26 | self.initResidual = 1 27 | self.finalResidual = 1 28 | 29 | def setTerms(self, terms): 30 | self.terms = terms 31 | 32 | def cfdComputeScaledRMSResiduals(self,Region,*args): 33 | theEquationName=self.name 34 | scale = Region.fluid[theEquationName].scale 35 | 36 | # % Get coefficients 37 | ac = Region.coefficients.ac 38 | bc = Region.coefficients.bc 39 | 40 | if theEquationName=='p': 41 | # % Fore pressure correction equation, the divergence of the mass flow 42 | # % rate is the residual. 43 | # % Scale with scale value (max value) 44 | p_scale = Region.fluid['p'].scale 45 | maxResidual = max(abs(bc)/(ac*p_scale)) 46 | rmsResidual = mth.cfdResidual(abs(bc)/(ac*p_scale),'RMS') 47 | else: 48 | # % Other equations ... 49 | # % Get info 50 | theNumberOfElements = Region.mesh.numberOfElements 51 | volumes = Region.mesh.elementVolumes.value 52 | # % Another approach which takes the transient term into consideration 53 | if not Region.STEADY_STATE_RUN: 54 | rho = Region.fluid['rho'].phi.value.copy() 55 | if theEquationName== 'T': 56 | try: 57 | Cp = Region.fluid['kappa'].phi.value.copy() 58 | rho *= Cp 59 | except AttributeError: 60 | pass 61 | 62 | deltaT = Region.dictionaries.controlDict['deltaT'] 63 | theMaxResidualSquared = 0. 64 | theMaxScaledResidual = 0 65 | for iElement in range(theNumberOfElements): 66 | volume = volumes[iElement] 67 | local_ac = ac[iElement] 68 | if not Region.STEADY_STATE_RUN: 69 | at = volume*rho[iElement]/deltaT 70 | local_ac -= at 71 | if local_ac < 1e-6*at: 72 | local_ac = at 73 | 74 | local_residual = bc[iElement] 75 | local_residual /= local_ac*scale 76 | theMaxScaledResidual = max(theMaxScaledResidual,abs(local_residual)) 77 | theMaxResidualSquared += local_residual*local_residual 78 | 79 | maxResidual = theMaxScaledResidual 80 | rmsResidual = np.sqrt(theMaxResidualSquared/theNumberOfElements) 81 | else: 82 | theMaxResidualSquared = 0 83 | theMaxScaledResidual = 0 84 | for iElement in range(theNumberOfElements): 85 | local_residual = bc[iElement] 86 | local_residual = local_residual/(ac[iElement]*scale) 87 | 88 | theMaxScaledResidual = max(theMaxScaledResidual,abs(local_residual)) 89 | theMaxResidualSquared += local_residual*local_residual 90 | 91 | maxResidual = theMaxScaledResidual 92 | rmsResidual = np.sqrt(theMaxResidualSquared/theNumberOfElements) 93 | 94 | if len(args)==1: 95 | iComponent = args[0] 96 | self.maxResidual[iComponent]=maxResidual 97 | self.rmsResidual[iComponent]=rmsResidual 98 | else: 99 | self.maxResidual=maxResidual 100 | self.rmsResidual=rmsResidual 101 | -------------------------------------------------------------------------------- /src/pyFVM/Fluxes.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from cfdtool.quantities import Quantity as Q_ 3 | import cfdtool.dimensions as dm 4 | 5 | class Fluxes(): 6 | 7 | def __init__(self,Region): 8 | ''' 9 | 这段Python代码定义了一个名为 `Fluxes` 的类,它用于在计算流体动力学(CFD)模拟中设置和管理通量(fluxes)。以下是对类的构造器和初始化的通量属性的详细解释: 10 | 1. **类定义**: 11 | - `class Fluxes():` 定义了一个名为 `Fluxes` 的类。 12 | 13 | 2. **构造器**: 14 | - `def __init__(self, Region):` 构造器接收一个参数 `Region`,它是一个包含模拟区域信息的对象。 15 | 16 | 3. **初始化属性**: 17 | - 构造器中初始化了一系列与通量相关的属性。这些属性用于存储面通量和体积通量的系数和值。 18 | 19 | 4. **面通量属性**: 20 | - `self.FluxCf`: 面通量的线性化系数,用于单元格C(感兴趣单元格)。 21 | - `self.FluxFf`: 面通量的线性化系数,用于邻近单元格。 22 | - `self.FluxVf`: 面通量的非线性系数。 23 | - `self.FluxTf`: 总面通量,等于 `FluxCf * phiC + FluxFf * phiF + FluxVf`。 24 | 25 | 5. **体积通量属性**: 26 | - `self.FluxC`: 体积通量的线性化系数。 27 | - `self.FluxV`: 体积通量,等于源值乘以单元格体积(`Q_C^phi * Vc`)。 28 | - `self.FluxT`: 总体积通量。 29 | - `self.FluxC_old`: 上一时间步的体积通量。 30 | 31 | 6. **初始化数组大小**: 32 | - 所有通量数组的大小都是基于网格的面数量 `theNumberOfFaces` 和单元格数量 `theNumberOfElements`。 33 | 34 | 7. **注释的方法**: 35 | - 代码中注释掉了 `setupFluxes` 方法,这个方法可能被用于更复杂的初始化逻辑,但在当前构造器中并未使用。 36 | 37 | ### 注意事项: 38 | - 类的构造器在类单独的方法中实现。 39 | - 构造器中使用 `Region.mesh.numberOfFaces` 和 `Region.mesh.numberOfElements` 获取网格的面和单元格的数量。 40 | - 所有通量数组都初始化为全零数组,使用 `np.zeros()` 创建。 41 | - 代码中的注释提供了对每个通量数组用途的说明,有助于理解每个属性的预期用途。 42 | 43 | `Fluxes` 类是CFD模拟中用于管理通量计算的关键组件,它提供了一种机制来存储和操作面通量和体积通量的系数,这些是求解流体动力学方程时的重要数据。通过这种方式,可以方便地访问和更新通量信息,以实现模拟的数值求解。 44 | ''' 45 | # self.region=Region 46 | self.setupFluxes(Region) 47 | 48 | def setupFluxes(self,Region,**kwargs): 49 | theNumberOfFaces=Region.mesh.numberOfFaces 50 | theNumberOfElements=Region.mesh.numberOfElements 51 | self.FluxCf = {} # 面通量的线性化系数,用于单元格 C(感兴趣单元格) 52 | self.FluxFf = {} # 面通量的线性化系数,用于邻近单元格 53 | self.FluxVf = {} # 面通量的非线性系数 54 | self.FluxTf = {} # 总面通量,等于 FluxCf * phiC + FluxFf * phiF + FluxVf 55 | 56 | self.FluxC = {} # 体积通量的线性化系数 57 | self.FluxV = {} # 体积通量,等于源值乘以单元格体积(Q_C^phi * Vc) 58 | self.FluxT = {} # 总体积通量 59 | self.FluxC_old = {} # 上一时间步的体积通量 60 | # 为每个方程初始化通量 61 | for equation_name in Region.model.equations: 62 | CoffeDim = Region.model.equations[equation_name].CoffeDim 63 | Dim=Region.fluid[equation_name].phi.dimension*CoffeDim 64 | #值保存在面心上 65 | #face fluxes 66 | # face flux linearization coefficients for cell C (cell of interest) 67 | self.FluxCf[equation_name]=Q_(np.zeros((theNumberOfFaces),dtype=float),CoffeDim) 68 | # face flux linearization coefficients for neighbouring cell 69 | self.FluxFf[equation_name]=Q_(np.zeros((theNumberOfFaces),dtype=float),CoffeDim) 70 | # non-linear face coefficients 71 | self.FluxVf[equation_name]=Q_(np.zeros((theNumberOfFaces),dtype=float),Dim) 72 | # total face flux (equal to FluxCf*phiC+FluxFf*phiF+FluxVf) 73 | self.FluxTf[equation_name]=Q_(np.zeros((theNumberOfFaces),dtype=float),Dim) 74 | 75 | #值保存在体心上 76 | #Volume fluxes (treated as source terms) 77 | self.FluxC[equation_name]=Q_(np.zeros((theNumberOfElements),dtype=float),CoffeDim) 78 | # volume fluxes from previous time step 79 | self.FluxC_old[equation_name]=Q_(np.zeros((theNumberOfElements),dtype=float),CoffeDim) 80 | # volume flux equal to source value times cell volume (Q_{C}^{phi} * Vc) 81 | self.FluxV[equation_name]=Q_(np.zeros((theNumberOfElements),dtype=float),Dim) 82 | self.FluxT[equation_name]=Q_(np.zeros((theNumberOfElements),dtype=float),Dim) 83 | 84 | print('fluxes success!') 85 | 86 | def cfdZeroElementFLUXCoefficients(self,equation_name): 87 | # print('Inside cfdZeroElementFLUXCoefficients') 88 | self.FluxC[equation_name].value.fill(0) 89 | self.FluxV[equation_name].value.fill(0) 90 | self.FluxT[equation_name].value.fill(0) 91 | self.FluxC_old[equation_name].value.fill(0) 92 | 93 | def cfdZeroFaceFLUXCoefficients(self,equation_name): 94 | # print('Inside cfdZeroFaceFLUXCoefficients') 95 | self.FluxCf[equation_name].value.fill(0) 96 | self.FluxVf[equation_name].value.fill(0) 97 | self.FluxTf[equation_name].value.fill(0) 98 | self.FluxFf[equation_name].value.fill(0) 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/pyFVM/Gradient.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cfdtool.Math as mth 3 | import cfdtool.IO as io 4 | from cfdtool.quantities import Quantity as Q_ 5 | # import cfdtool.dimensions as dm 6 | 7 | from cfdtool.base import DimensionChecked 8 | 9 | class Gradient(DimensionChecked): 10 | 11 | def __init__(self, Region, phiName): 12 | """ 13 | Handles gradient calculations on the field specified by phiName. First, 14 | create an instance of the class, then call cfdComputeGradientGaussLinear0, 15 | which calculates the gradients at each cell. At the end of 16 | cfdComputeGradientGaussLinear0, cfdUpdateGradient() is called which updates 17 | the gradients on the boundary faces. 18 | 这段Python代码定义了一个名为 `Gradient` 的类,它用于计算指定场(由 `phiName` 指定)的梯度。以下是对类的构造器和相关属性的详细解释: 19 | 1. **类定义**: 20 | - `class Gradient():` 定义了一个名为 `Gradient` 的类。 21 | 22 | 2. **构造器**: 23 | - `def __init__(self, Region, phiName):` 构造器接收两个参数:`Region`(区域案例的实例)和 `phiName`(要计算梯度的场的名称)。 24 | 25 | 3. **初始化属性**: 26 | - 构造器中初始化了一系列属性,包括场名称、场数据、场类型、梯度计算所需的网格信息等。 27 | 28 | 4. **场属性**: 29 | - `self.phiName`: 存储场名称。 30 | - `self.phi`: 存储从 `Region.fluid` 字典中获取的场数据。 31 | - `self.type`: 存储场类型。 32 | 33 | 5. **梯度计算属性**: 34 | - `theSize`: 根据场数据的维度确定是标量场还是矢量场。 35 | - `self.theNumberOfComponents`: 根据场的维度存储组件数量。 36 | 37 | 6. **网格几何属性**: 38 | - `self.elementCentroids`: 存储单元格质心的数组。 39 | - `self.theNumberOfElements`: 存储网格单元格数量。 40 | 41 | 7. **内部面属性**: 42 | - `self.owners_f` 和 `self.neighbours_f`: 分别存储内部面的所有者和邻居单元格的索引。 43 | - `self.Sf` 和 `self.g_f`: 分别存储内部面的法向量和权重。 44 | - `self.iFaces`: 存储内部面的总数。 45 | - `self.ones`: 存储全1数组,用于内部面的数量。 46 | - `self.phi_f`: 初始化为零的数组,用于存储面场数据。 47 | 48 | 8. **边界面属性**: 49 | - `self.boundaryPatches`: 存储边界补丁信息。 50 | - `self.theBoundaryArraySize`: 存储边界元素的数量。 51 | - `self.iBElements`: 存储边界元素的索引。 52 | - `self.phi_b`: 存储边界上的场数据。 53 | - `self.owners_b` 和 `self.Sf_b`: 分别存储边界面的所有者和法向量。 54 | 55 | 9. **梯度数组**: 56 | - `self.phi`: 初始化为零的数组,用于存储内部和边界质心处的梯度值。数组的形状取决于场是标量还是矢量。 57 | 58 | ### 注意事项: 59 | - 类的构造器中注释掉了 `self.Region`,表明可能在初始设计中考虑过,但在最终实现中未使用。 60 | - 构造器中直接调用了 `cfdComputeGradientGaussLinear0` 方法来计算梯度,但这个方法的实现没有在代码中给出。 61 | - `cfdUpdateGradient()` 方法在梯度计算结束后被调用以更新边界面上的梯度,但这个方法的实现也没有在代码中给出。 62 | - 代码中的注释提供了对每个属性用途的说明,有助于理解每个属性的预期用途。 63 | 64 | `Gradient` 类是CFD模拟中用于梯度计算的关键组件,它提供了一种机制来存储和操作场的梯度数据。通过这种方式,可以方便地访问和更新梯度信息,以实现模拟中的数值求解和边界条件处理。 65 | """ 66 | # self.Region=Region 67 | self.phiName=phiName 68 | self.type=Region.fluid[self.phiName].type 69 | theSize=Region.fluid[self.phiName].iComponent 70 | if theSize == 3: 71 | self.theNumberOfComponents=3 72 | else: 73 | self.theNumberOfComponents=1 74 | self.iFaces=Region.mesh.numberOfInteriorFaces 75 | # 复制 dimensions 对象,确保可修改 76 | dim = Region.fluid[phiName].phi.dimension.grad() 77 | self.expected_dimensions = { 78 | 'phi': dim, 79 | 'phi_TR': dim, 80 | 'phi_Trace':dim, 81 | } 82 | # 创建 Quantity 对象 83 | self.phi=Q_(np.zeros((Region.mesh.numberOfElements+Region.mesh.numberOfBElements,3,self.theNumberOfComponents)),dim) 84 | if self.phiName=='U': 85 | self.phi_TR=self.phi.copy() 86 | if Region.cfdIsCompressible: 87 | self.phi_Trace=Q_(np.zeros((Region.mesh.numberOfElements+Region.mesh.numberOfBElements)),dim) 88 | 89 | # self.cfdComputeGradientGaussLinear0(Region) 90 | self.cfdUpdateGradient(Region) 91 | 92 | 93 | 94 | def cfdUpdateGradient(self,Region): 95 | # %========================================================================== 96 | # % Routine Description: 97 | # % This function updates gradient of the field 98 | # %-------------------------------------------------------------------------- 99 | gradSchemes = Region.dictionaries.fvSchemes['gradSchemes']['default'] 100 | if gradSchemes=='Gauss linear': 101 | self.cfdComputeGradientGaussLinear0(Region) 102 | else: 103 | io.cfdError('\n%s is incorrect\n', gradSchemes) 104 | 105 | 106 | def cfdComputeGradientGaussLinear0(self,Region): 107 | """ 108 | This function computes the gradient for a field at the centroids of 109 | the elements using a first order gauss interpolation. No correction for 110 | non-conjuntionality is applied. 'phi' is the name of a field 111 | used when the class is instantiated. 112 | To-do: Check this in-depth over a number of scenarios 113 | 这段Python代码定义了一个名为`cfdComputeGradientGaussLinear0`的方法,它是`Gradient`类的一部分,用于使用一阶高斯线性插值计算场在单元格质心处的梯度。以下是对这个方法的详细解释: 114 | 115 | 1. **方法定义**: 116 | - `def cfdComputeGradientGaussLinear0(self, Region):` 定义了一个实例方法,接收一个参数`Region`,表示区域案例的实例。 117 | 118 | 2. **内部面贡献**: 119 | - 通过遍历场的每个组件(标量或矢量场的每个分量),使用线性插值计算面场`phi_f`。 120 | - 对于每个内部面,更新面的所有者和邻居单元格质心处的梯度累积器。 121 | 122 | 3. **边界面贡献**: 123 | - 对于边界面上的每个组件,直接将边界面上的场值乘以面法向量,累加到边界面所有者单元格质心的梯度。 124 | 125 | 4. **计算体积加权梯度**: 126 | - 通过将累积的梯度除以单元格体积,计算体积加权平均梯度。 127 | 128 | 5. **边界梯度更新**: 129 | - 遍历边界补丁,根据边界的物理类型(如墙壁、入口、出口、对称性或空),调用相应的方法更新边界梯度。 130 | 131 | 6. **设置场梯度**: 132 | - 将计算得到的梯度赋值给`Region.fluid[self.phiName].phi`,这样在`Region`的流体字典中,指定场的梯度被更新。 133 | 134 | ### 注意事项: 135 | - 方法中有一些被注释掉的代码,例如`self.cfdUpdateGradient(Region)`和`def cfdUpdateGradient(self, Region):`,这表明这些部分可能还在开发中或已被弃用。 136 | - 方法中的`To-do`注释表明需要在多种情况下深入检查这个方法。 137 | - 边界梯度更新部分打印了边界的类型,这有助于调试和验证边界条件的处理。 138 | - 方法假设`Region`对象包含网格信息和流体场数据,这些信息用于梯度计算和边界条件处理。 139 | `cfdComputeGradientGaussLinear0`方法在CFD模拟中是计算梯度的关键步骤,它为求解流体动力学方程提供了必要的空间导数信息。通过这种方法,可以获取场在网格质心处的梯度,这对于理解和模拟流体现象的局部变化非常重要。 140 | """ 141 | # phi_f=Q_(np.zeros((self.iFaces,self.theNumberOfComponents)),Region.fluid[self.phiName].phi.dimension) 142 | phi_b=Region.fluid[self.phiName].phi[Region.mesh.iBElements,:] 143 | owners=Region.mesh.interiorFaceOwners[:self.iFaces] 144 | neighbours=Region.mesh.interiorFaceNeighbours[:self.iFaces] 145 | owners_b=Region.mesh.owners_b[:Region.mesh.numberOfBFaces] 146 | #face contribution, treats vectors as three scalars (u,v,w) 147 | for iComponent in range(self.theNumberOfComponents): 148 | #vectorized linear interpolation (same as Interpolate.interpolateFromElementsToFaces('linear')) 149 | phi_f=Region.mesh.interiorFaceWeights*Region.fluid[self.phiName].phi[Region.mesh.interiorFaceNeighbours,iComponent] +\ 150 | (1.0-Region.mesh.interiorFaceWeights)*Region.fluid[self.phiName].phi[Region.mesh.interiorFaceOwners,iComponent] 151 | # interior face contribution 152 | phi_f_Sf=phi_f[:,np.newaxis]*Region.mesh.interiorFaceSf[:self.iFaces,:]# phi_f*Sf 153 | #accumlator of phi_f*Sf for the owner centroid of the face 154 | self.phi[owners,:,iComponent] +=phi_f_Sf/Region.mesh.elementVolumes[owners,np.newaxis] 155 | #accumlator of phi_f*Sf for the neighbour centroid of the face 156 | self.phi[neighbours,:,iComponent] -=phi_f_Sf/Region.mesh.elementVolumes[neighbours,np.newaxis] 157 | # Boundary face contributions 158 | self.phi[owners_b,:,iComponent] += phi_b[:Region.mesh.numberOfBFaces,iComponent,np.newaxis]*Region.mesh.Sf_b[:Region.mesh.numberOfBFaces,:]/Region.mesh.elementVolumes[owners_b,np.newaxis]# phi_b*Sf_b 159 | 160 | self.phi.value[Region.mesh.iBElements,:,:] = self.phi.value[Region.mesh.owners_b,:,:] 161 | 162 | self.cfdUpdateBoundaryGradient(Region) 163 | if self.phiName=='U': 164 | self.cfdTransposeComputeGradient(Region) 165 | if Region.cfdIsCompressible: 166 | self.cfdTraceGradient(Region) 167 | 168 | def cfdTransposeComputeGradient(self,Region): 169 | for iElement in range(Region.mesh.numberOfElements+Region.mesh.numberOfBElements): 170 | #vectorized linear interpolation (same as Interpolate.interpolateFromElementsToFaces('linear')) 171 | self.phi_TR.value[iElement,:,:]=np.transpose(self.phi.value[iElement,:,:]) 172 | # pass 173 | def cfdTraceGradient(self,Region): 174 | for iElement in range(Region.mesh.numberOfElements+Region.mesh.numberOfBElements): 175 | #vectorized linear interpolation (same as Interpolate.interpolateFromElementsToFaces('linear')) 176 | self.phi_Trace.value[iElement]=np.trace(self.phi.value[iElement,:,:]) 177 | # pass 178 | 179 | def cfdUpdateBoundaryGradient(self,Region): 180 | """ Prints the boundary type and then assigns the calculated phiGrad field to self.Region.fluid[self.phiName].phi 181 | """ 182 | # % BOUNDARY Gradients 183 | for iBPatch, theBCInfo in Region.mesh.cfdBoundaryPatchesArray.items(): 184 | # % Get Physical and Equation Boundary Conditions 185 | thePhysicalType = theBCInfo['type'] 186 | # % BC info 187 | if thePhysicalType=='wall': 188 | self.updateWallGradients(Region,iBPatch) 189 | elif thePhysicalType=='inlet': 190 | self.updateInletGradients(Region,iBPatch) 191 | elif thePhysicalType=='outlet': 192 | self.updateOutletGradients(Region,iBPatch) 193 | elif thePhysicalType=='symmetry' or thePhysicalType=='empty': 194 | self.updateSymmetryGradients(Region,iBPatch) 195 | else: 196 | io.cfdError('Boundary condition not recognized') 197 | 198 | def updateWallGradients(self,Region,patch): 199 | owners_b=Region.mesh.cfdBoundaryPatchesArray[patch]['owners_b'] 200 | iBElements=Region.mesh.cfdBoundaryPatchesArray[patch]['iBElements'] 201 | numberOfBFaces = Region.mesh.cfdBoundaryPatchesArray[patch]['numberOfBFaces'] 202 | startBFace = Region.mesh.cfdBoundaryPatchesArray[patch]['startFaceIndex'] 203 | grad_b=np.zeros((numberOfBFaces, 3,self.theNumberOfComponents)) 204 | dCf = Region.mesh.faceCF[startBFace: startBFace + numberOfBFaces].value 205 | e = dCf / mth.cfdMag(dCf)[:, np.newaxis] 206 | for iComponent in range(self.theNumberOfComponents): 207 | #保留切向分量,再考虑边界面的相邻单元的梯度计算 书籍《The Finite Volume Method in Computational Fluid Dynamics》Page 289页 208 | grad_b[:numberOfBFaces,:,iComponent]=self.phi.value[owners_b,:,iComponent]\ 209 | + ((Region.fluid[self.phiName].phi.value[iBElements,iComponent] 210 | - Region.fluid[self.phiName].phi.value[owners_b,iComponent])/mth.cfdMag(dCf) 211 | -mth.cfdDot(self.phi.value[owners_b,:,iComponent],e))[:,np.newaxis]*e 212 | self.phi.value[iBElements,:,:]=grad_b 213 | 214 | def updateInletGradients(self,Region,patch): 215 | ''' 216 | 这段Python代码定义了一个名为`updateInletGradients`的方法,它用于更新边界条件为入口(inlet)的梯度。这个方法是`Gradient`类的一部分,用于计算和设置边界面上的梯度,以确保物理上合理的流动条件。以下是对这个方法的详细解释: 217 | 1. **方法定义**: 218 | - `def updateInletGradients(self, Region, patch):` 定义了一个实例方法,接收两个参数:`Region`(区域案例的实例)和`patch`(当前处理的边界补丁的名称或索引)。 219 | 220 | 2. **获取边界补丁信息**: 221 | - 从`Region.mesh.cfdBoundaryPatchesArray`中获取与`patch`相关的所有者单元格、面质心、边界元素索引、边界面数量和起始面索引。 222 | 223 | 3. **计算边界元素索引范围**: 224 | - `startBElement`:计算边界单元格的起始索引。 225 | - `endBElement`:计算边界单元格的结束索引。 226 | 227 | 4. **初始化边界梯度数组**: 228 | - `grad_b`:初始化一个用于存储边界面上梯度的三维数组。 229 | 230 | 5. **计算边界梯度**: 231 | - 通过双重循环遍历每个分量`iComponent`和每个边界面`iBFace`。 232 | - 对于每个边界面,计算面质心`Cf`与所有者单元格质心`C`之间的向量`dCf`。 233 | - 计算`dCf`的单位向量`e`。 234 | 235 | 6. **设置边界梯度**: 236 | - 根据所有者单元格的梯度、面质心与单元格质心之间的距离,以及边界面上的场值与所有者单元格场值之间的差,计算边界梯度`grad_b`。 237 | 238 | 7. **更新`phiGrad`**: 239 | - 将计算得到的边界梯度`grad_b`赋值给`self.phi.value[iBElements, :, :]`,这样边界面上的梯度就被更新了。 240 | 241 | ### 注意事项: 242 | - 方法中使用了`mth.cfdMag`函数来计算向量的模长,这个函数可能在类的其他部分或外部模块定义。 243 | - `self.phi`和`self.phi`分别存储了梯度和场值,这些属性在类的构造器中被初始化。 244 | - 代码中的计算考虑了边界面上的流动特性,特别是入口处的梯度条件,这对于模拟入口处的流动非常重要。 245 | - 该方法假设`Region`对象包含网格信息和流体场数据,这些信息用于梯度计算和边界条件处理。 246 | 247 | `updateInletGradients`方法在CFD模拟中是应用入口边界条件的关键步骤,它确保了在入口处梯度的物理合理性,从而影响了模拟的准确性和稳定性。 248 | ''' 249 | self.updateWallGradients(Region,patch) 250 | 251 | 252 | def updateOutletGradients(self,Region,patch): 253 | self.updateWallGradients(Region,patch) 254 | 255 | 256 | def updateSymmetryGradients(self,Region,patch): 257 | pass 258 | 259 | -------------------------------------------------------------------------------- /src/pyFVM/Model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cfdtool.IO as io 3 | import pyFVM.Field as field 4 | import pyFVM.Gradient as grad 5 | import pyFVM.Equation as equation 6 | import pyFVM.cfdfunction as cfun 7 | import cfdtool.dimensions as dm 8 | 9 | class Model(): 10 | def __init__(self,Region): 11 | 12 | # self.Region = Region 13 | # self.name=fieldName 14 | # self.initializeResiduals() 15 | #Define transient-convection equation 16 | # if not os.path.isfile(gravityFilePath): 17 | # print('\n\nNo g file found\n') 18 | # pass 19 | # else: 20 | self.equations = {} 21 | self.rhophi_exists=False 22 | # Create theEquation structure 23 | self.DefineMomentumEquation(Region) 24 | self.DefineContinuityEquation(Region) 25 | self.DefineEnergyEquation(Region) 26 | self.DefineScalarTransportEquation(Region) 27 | 28 | def DefineContinuityEquation(self,Region): 29 | initCasePath=Region.caseDirectoryPath + os.sep+'0' 30 | if not os.path.isfile(initCasePath+ os.sep+'p'): 31 | print('A file of the name p doesn''t exist in ', initCasePath) 32 | else: 33 | self.equations['p']=equation.Equation('p') 34 | self.rhophi_exists=True 35 | self.equations['p'].setTerms(['massDivergenceTerm']) 36 | if not Region.STEADY_STATE_RUN: 37 | self.equations['p'].terms.append('Transient') 38 | Region.fluid['pprime']=field.Field(Region,'pprime','volScalarField',dm.pressure_dim) 39 | Region.fluid['pprime'].boundaryPatchRef=Region.fluid['p'].boundaryPatchRef 40 | for iBPatch, values in Region.fluid['pprime'].boundaryPatchRef.items(): 41 | if values['type']=='fixedValue': 42 | Region.fluid['pprime'].boundaryPatchRef[iBPatch]['value']=[0.0] 43 | Region.fluid['p'].Grad=grad.Gradient(Region,'p') 44 | Region.fluid['pprime'].Grad=grad.Gradient(Region,'pprime') 45 | self.equations['p'].CoffeDim=dm.length_dim*dm.time_dim 46 | self.DefineDUfield(Region,'DU') 47 | # self.DefineDUfield(Region,'DUT') 48 | self.DefineDUSffield(Region,'DUSf') 49 | self.DefineDUSffield(Region,'DUEf') 50 | 51 | def DefineEnergyEquation(self,Region): 52 | initCasePath=Region.caseDirectoryPath + os.sep+'0' 53 | if not os.path.isfile(initCasePath+ os.sep+'T'): 54 | print('A file of the name T doesn''t exist in ', initCasePath) 55 | else: 56 | self.equations['T']=equation.Equation('T') 57 | self.equations['T'].setTerms([]) 58 | if not Region.STEADY_STATE_RUN: 59 | self.equations['T'].terms.append('Transient') 60 | self.rhophi_exists=True 61 | for iterm in Region.dictionaries.fvSchemes['laplacianSchemes']: 62 | if io.contains_term(iterm,'T'): 63 | self.equations['T'].terms.append('Diffusion') 64 | parts=io.term_split(iterm) 65 | terms_to_remove = ['laplacian', 'T'] 66 | str_gammas=io.remove_terms(parts,terms_to_remove)[0] 67 | try: 68 | self.equations['T'].gamma=Region.fluid[str_gammas].phi*Region.fluid['rho'].phi 69 | except AttributeError: 70 | self.equations['T'].gamma=Region.fluid['k'].phi/Region.fluid['Cp'].phi*Region.fluid['rho'].phi 71 | print("self.equations['T'].gamma information doesn't exist in the FoamDictionaries object") 72 | 73 | for iterm in Region.dictionaries.fvSchemes['divSchemes']: 74 | if io.contains_term(iterm,'T'): 75 | self.equations['T'].terms.append('Convection') 76 | self.rhophi_exists=True 77 | 78 | Region.fluid['T'].Grad=grad.Gradient(Region,'T') 79 | self.equations['T'].CoffeDim=dm.flux_dim 80 | 81 | def DefineMomentumEquation(self,Region): 82 | initCasePath=Region.caseDirectoryPath + os.sep+'0' 83 | if not os.path.isfile(initCasePath+ os.sep+'U'): 84 | print('A file of the name U doesn''t exist in ', initCasePath) 85 | else: 86 | self.equations['U']=equation.Equation('U') 87 | self.equations['U'].gamma=Region.fluid['mu'].phi 88 | self.equations['U'].setTerms([]) 89 | if not Region.STEADY_STATE_RUN: 90 | self.equations['U'].terms.append('Transient') 91 | self.rhophi_exists=True 92 | # self.equations['U'].setTerms(['Transient', 'Convection','Diffusion']) 93 | # else: 94 | # self.equations['U'].setTerms(['Convection','Diffusion']) 95 | for iterm in Region.dictionaries.fvSchemes['divSchemes']: 96 | if io.contains_term(iterm,'U'): 97 | self.equations['U'].terms.append('Convection') 98 | self.rhophi_exists=True 99 | 100 | for iterm in Region.dictionaries.fvSchemes['laplacianSchemes']: 101 | if io.contains_term(iterm,'default'): 102 | self.equations['U'].terms.append('Diffusion') 103 | 104 | 105 | if os.path.isfile(initCasePath+ os.sep+'p'): 106 | self.equations['U'].terms.append('PressureGradient') 107 | 108 | try: 109 | Region.dictionaries.g 110 | self.equations['U'].terms.append('Buoyancy') 111 | self.rhophi_exists=True 112 | except AttributeError: 113 | print("Gravity information doesn't exist in the FoamDictionaries object") 114 | #Define mdot_f field 115 | self.DefineMdot_f(Region) 116 | Region.fluid['U'].Grad=grad.Gradient(Region,'U') 117 | self.equations['U'].CoffeDim=dm.flux_dim 118 | 119 | def DefineScalarTransportEquation(self,Region): 120 | pass 121 | 122 | def DefineDUfield(self,Region,Name,*args): 123 | Dp_dim=Region.fluid['p'].Grad.phi.dimension.copy() 124 | DU_dim=dm.velocity_dim/Dp_dim 125 | Region.fluid[Name]=field.Field(Region,Name,'volVectorField',DU_dim) 126 | # Region.fluid[Name].dimensions=[-1,3,1,0,0,0,0] 127 | # Region.fluid[Name].boundaryPatchRef=Region.fluid['U'].boundaryPatchRef 128 | 129 | 130 | def DefineDUSffield(self,Region,Name,*args): 131 | DU_dim=Region.fluid['DU'].phi.dimension.copy() 132 | DUSf_dim=DU_dim*dm.area_dim 133 | Region.fluid[Name]=field.Field(Region,Name,'surfaceVectorField',DUSf_dim) 134 | Region.fluid[Name].dimensions=[-1,5,1,0,0,0,0] 135 | # Region.fluid[Name].boundaryPatchRef=Region.fluid['U'].boundaryPatchRef 136 | 137 | 138 | def DefineMdot_f(self,Region): 139 | # dimensions=[-2,1,-1,0,0,0,0] 140 | Region.fluid['mdot_f']=field.Field(Region,'mdot_f','surfaceScalarField',dm.flux_dim) 141 | Region.fluid['mdot_f'].boundaryPatchRef=Region.fluid['U'].boundaryPatchRef 142 | cfun.initializeMdotFromU(Region) 143 | 144 | -------------------------------------------------------------------------------- /src/pyFVM/Region.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cfdtool.IO as io 3 | import pyFVM.FoamDictionaries as fd 4 | import pyFVM.Polymesh as pm 5 | import pyFVM.Coefficients as coefficients 6 | import pyFVM.Fluxes as fluxes 7 | import cfdtool.Time as time 8 | import pyFVM.Assemble as assemble 9 | import pyFVM.Model as model 10 | import cfdtool.Solve as solve 11 | import cfdtool.cfdPlot as plt 12 | import config as cfg 13 | 14 | 15 | class Region(): 16 | """Sets up the simulation's 'Region'. 17 | An instance of the Region class is required for the case to run. 18 | The instance is created at the beginning of each case and is used to hold 19 | other class instances, such as 'polyMesh', any number of 'fluid' instances 20 | and a number of other attributes required for the simulation to run. 21 | All information related to the mesh's topology (i.e., distances of cell centers to wall, face surface areas, face normals and cell volumes) is available in the Region class. 22 | """ 23 | def __init__(self,casePath): 24 | self.cfdIsCompressible=cfg.cfdIsCompressible 25 | self.pp_nonlinear_corrected=cfg.pp_nonlinear_corrected 26 | self.caseDirectoryPath = casePath 27 | self.StartSession() 28 | self.ReadOpenFoamFiles() 29 | self.model=model.Model(self) 30 | 31 | def RunCase(self): 32 | """ 33 | Runs the case by performing the necessary computations and iterations for each equation in the model. 34 | """ 35 | io.cfdPrintHeader() 36 | ## Instance of Coefficients class which contains information related to the connectivity of the mesh.定义ac,anb 37 | self.coefficients=coefficients.Coefficients(self) 38 | ## Instance of Fluxes class which contains flux information 39 | self.fluxes=fluxes.Fluxes(self) 40 | self.time = time.Time(self) 41 | self.assembledPhi = {} 42 | for iTerm in self.model.equations: 43 | self.assembledPhi[iTerm]=assemble.Assemble(self,iTerm) 44 | self.fluid[iTerm].cfdfieldUpdate(self) 45 | for iTerm in self.fluid: 46 | self.fluid[iTerm].setPreviousTimeStep() 47 | 48 | while(self.time.cfdDoTransientLoop()): 49 | #manage time 50 | self.time.cfdPrintCurrentTime() 51 | self.time.cfdUpdateRunTime() 52 | for iTerm in self.model.equations: 53 | self.fluid[iTerm].setPreviousTimeStep() 54 | # self.fluid[iTerm].setPreviousIter() 55 | #sub-loop 56 | if iTerm=='U': 57 | self.MomentumIteration() 58 | elif iTerm=='p': 59 | self.ContinuityIteration() 60 | else: 61 | self.ScalarTransportIteration(iTerm) 62 | plt.cfdPlotRes(self.caseDirectoryPath,self.model.equations) 63 | if self.time.cfdDoWriteTime(): 64 | # io.cfdWriteOpenFoamParaViewData(self) 65 | pass 66 | 67 | def MomentumIteration(self): 68 | numVector=self.fluid['U'].iComponent 69 | for iComponent in range(numVector): 70 | io.MomentumPrintInteration(iComponent) 71 | self.assembledPhi['U'].cfdAssembleEquation(self,iComponent) 72 | self.cfdSolveUpdateEquation('U',iComponent) 73 | self.fluid['U'].cfdfieldUpdateGradient_Scale(self) 74 | 75 | def ContinuityIteration(self): 76 | io.ContinuityPrintInteration() 77 | self.assembledPhi['p'].cfdAssembleEquation(self) 78 | self.cfdSolveUpdateEquation('p') 79 | self.cfdCorrectNSSystemFields() 80 | 81 | def ScalarTransportIteration(self,iTerm): 82 | io.ScalarTransportPrintInteration() 83 | self.assembledPhi[iTerm].cfdAssembleEquation(self) 84 | self.cfdSolveUpdateEquation(iTerm) 85 | 86 | def cfdSolveUpdateEquation(self,theEquationName,iComponent=-1): 87 | solve.cfdSolveEquation(self,theEquationName,iComponent) 88 | self.fluid[theEquationName].cfdCorrectField(self,iComponent) 89 | if iComponent==-1: 90 | self.fluid[theEquationName].cfdfieldUpdateGradient_Scale(self) 91 | 92 | def cfdCorrectNSSystemFields(self): 93 | theNumberOfElements=self.mesh.numberOfElements 94 | self.fluid['pprime'].phi[:theNumberOfElements].value = self.coefficients.dphi[:,None] 95 | self.fluid['pprime'].cfdfieldUpdate(self)#更新pprime的梯度,来计算速度增量 96 | self.fluid['U'].cfdCorrectNSFields(self) 97 | self.cfdUpdateProperty() 98 | 99 | def StartSession(self): 100 | """Initiates the class instance with the caseDirectoryPath attribute 101 | and adds the 'dictionaries' and 'fluid' dictionaries. Reminder - 102 | __init__ functions are run automatically when a new class instance is 103 | created. 104 | """ 105 | io.cfdPrintMainHeader() 106 | io.cfdInitDirectories(self.caseDirectoryPath) 107 | print('Working case directory is %s' % self.caseDirectoryPath) 108 | 109 | def ReadOpenFoamFiles(self): 110 | """ 111 | Reads the OpenFOAM files and initializes the necessary dictionaries and variables. 112 | This method performs the following steps: 113 | 1. Initializes the 'fluid' dictionary to hold fluid properties. 114 | 2. Initializes the 'dictionaries' dictionary to hold information from various OpenFOAM dictionaries. 115 | 3. Checks if the simulation is steady-state or transient based on the 'ddtSchemes' entry in 'fvSchemes'. 116 | 4. Determines the time solver type based on the 'fvSolution' entries. 117 | 5. Initializes the 'mesh' dictionary to hold FVM mesh information. 118 | 6. Reads the transport properties and thermophysical properties from the dictionaries. 119 | 7. Calculates the geometric length scale of the mesh. 120 | 8. Reads the time directory from the dictionaries. 121 | Note: Some steps require the mesh information and are called after initializing the 'mesh' dictionary. 122 | """ 123 | ## Dictionary to hold 'fluid' properties. We are considering changing this to a more meaningful name such as 'field' because often this dictionary is used to store field and field variables which are not necessarily fluids. 124 | self.fluid={} 125 | ## Dictionary holding information contained within the various c++ dictionaries used in OpenFOAM. For example, the contents of the './system/controlDict' file can be retrieved by calling Region.dictionaries.controlDict which return the dictionary containing all the entries in controlDict. 126 | self.dictionaries=fd.FoamDictionaries(self) 127 | 128 | if self.dictionaries.fvSchemes['ddtSchemes']['default']=='steadyState': 129 | self.STEADY_STATE_RUN = True 130 | else: 131 | self.STEADY_STATE_RUN = False 132 | 133 | for item in self.dictionaries.fvSolution: 134 | if item=='PISO': 135 | self.Timesolver = 'PISO' 136 | if item=='SIMPLE': 137 | self.Timesolver = 'SIMPLE' 138 | if item=='PIMPLE': 139 | self.Timesolver = 'PIMPLE' 140 | io.cfdError('PIMPLE solver not yet implemented') 141 | 142 | ## Dictionary containing all the information related to the FVM mesh. 143 | self.mesh=pm.Polymesh(self) 144 | #cfdReadTransportProperties需要用到网格信息。因此滞后计算,另外由于更新p.cfdUpdateScale需要密度信息,因此需要提前计算!!!!! 145 | self.dictionaries.cfdReadTransportProperties(self) 146 | self.dictionaries.cfdReadThermophysicalProperties(self) 147 | 148 | """cfdGeometricLengthScale() and self.dictionaries.cfdReadTimeDirectory() require the mesh and therefore are not included in the __init__ function of FoamDictionaries and are instead called after the self.mesh=pm.Polymesh(self) line above.""" 149 | self.dictionaries.cfdReadTimeDirectory(self) 150 | 151 | def cfdUpdateProperty(self): 152 | if self.cfdIsCompressible: 153 | self.dictionaries.cfdUpdateCompressibleProperties(self) 154 | self.dictionaries.cfdUpdateTransportProperties(self) 155 | self.dictionaries.cfdUpdateThermophysicalProperties(self) 156 | self.dictionaries.cfdUpdateTurbulenceProperties(self) 157 | self.dictionaries.cfdUpdatePressureReference(self) 158 | -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Assemble.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Assemble.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Coefficients.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Coefficients.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Equation.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Equation.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Field.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Field.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Fluxes.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Fluxes.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/FoamDictionaries.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/FoamDictionaries.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Gradient.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Gradient.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Model.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Model.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Polymesh.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Polymesh.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/Region.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/Region.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/cfdGetTools.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/cfdGetTools.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/__pycache__/cfdfunction.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sherlockyzd/pyOpenFOAM/a9fa37bb93ad79fa2756e5c125f24ac56d91289b/src/pyFVM/__pycache__/cfdfunction.cpython-310.pyc -------------------------------------------------------------------------------- /src/pyFVM/cfdGetTools.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | # import cfdtool.IO as io 3 | import cfdtool.Math as mth 4 | import cfdtool.Interpolate as interp 5 | 6 | 7 | def cfdRhieChowValue(fieldName,Region): 8 | numberOfInteriorFaces = Region.mesh.numberOfInteriorFaces 9 | ## owners of elements of faces 10 | owners_f=Region.mesh.interiorFaceOwners 11 | ## neighbour elements of faces 12 | neighbours_f=Region.mesh.interiorFaceNeighbours 13 | ## face weights 14 | g_f=Region.mesh.interiorFaceWeights 15 | ## vector formed between owner (C) and neighbour (f) elements 16 | CF = Region.mesh.interiorFaceCF 17 | 18 | ## face gradient matrix 19 | gradPhi=np.squeeze(Region.fluid[fieldName].Grad.phi[:numberOfInteriorFaces,:]) 20 | grad_f=(1-g_f)[:,None]*gradPhi[neighbours_f,:]+g_f[:,None]*gradPhi[owners_f,:] 21 | # % ScfdUrface-normal gradient 22 | dcfdMag = mth.cfdMag(CF) 23 | e_CF = mth.cfdUnit(CF.value) 24 | # local_avg_grad=(grad_f*e_CF).sum(1)*e_CF 25 | # % Get pressure field and interpolate to faces 26 | phi = cfdGetSubArrayForInterior(fieldName,Region) 27 | local_grad_cfdMag_f = np.squeeze((phi[neighbours_f]-phi[owners_f]))/dcfdMag 28 | # 《the FVM in CFD》 P.289 29 | return (local_grad_cfdMag_f-mth.cfdDot(grad_f,e_CF))[:,None]*e_CF 30 | # return RhieChow_grad 31 | 32 | def cfdGetfield_grad_f(fieldName,Region): 33 | # % Get pressure field and interpolate to faces 34 | field_Interior = cfdGetSubArrayForInterior(fieldName,Region) 35 | Region.fluid[fieldName].Grad.cfdGetGradientSubArrayForInterior(Region) 36 | return interp.cfdInterpolateGradientsFromElementsToInteriorFaces(Region,Region.fluid[fieldName].Grad.phiInter, 'Gauss linear corrected', field_Interior) 37 | # field_grad_f = 38 | # return field_grad_f 39 | 40 | def cfdGetSubArrayForInterior(theFieldName,Region,*args): 41 | Fieldtype=Region.fluid[theFieldName].type 42 | if Fieldtype == 'surfaceScalarField': 43 | return Region.fluid[theFieldName].phi[:Region.mesh.numberOfInteriorFaces] 44 | elif Fieldtype == 'surfaceVectorField': 45 | if args: 46 | iComponent = args[0] 47 | return Region.fluid[theFieldName].phi[:Region.mesh.numberOfInteriorFaces,iComponent] 48 | else: 49 | phiInteriorSubArray = Region.fluid[theFieldName].phi[:Region.mesh.numberOfInteriorFaces, :] 50 | elif Fieldtype == 'volScalarField': 51 | return Region.fluid[theFieldName].phi[:Region.mesh.numberOfElements] 52 | elif Fieldtype == 'volVectorField': 53 | if args: 54 | iComponent = args[0] 55 | return Region.fluid[theFieldName].phi[:Region.mesh.numberOfElements,iComponent] 56 | else: 57 | return Region.fluid[theFieldName].phi[:Region.mesh.numberOfElements, :] 58 | else: 59 | raise ValueError('Field type not supported') 60 | 61 | 62 | 63 | def cfdGetSubArrayForBoundaryPatch(theFieldName, iBPatch, Region,*args): 64 | # %========================================================================== 65 | # % Routine Description: 66 | # % This function returns a subarray at a given cfdBoundary 67 | # %-------------------------------------------------------------------------- 68 | if Region.fluid[theFieldName].type=='surfaceScalarField': 69 | # iFaceStart = Region.mesh.cfdBoundaryPatchesArray[iBPatch]['startFaceIndex'] 70 | # iFaceEnd = iFaceStart+Region.mesh.cfdBoundaryPatchesArray[iBPatch]['numberOfBFaces'] 71 | iBFaces = Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBFaces'] 72 | phi_b = Region.fluid[theFieldName].phi[iBFaces] 73 | elif Region.fluid[theFieldName].type=='volScalarField': 74 | iBElements=Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBElements'] 75 | phi_b = Region.fluid[theFieldName].phi[iBElements] 76 | 77 | elif Region.fluid[theFieldName].type=='volVectorField': 78 | iBElements=Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBElements'] 79 | if args: 80 | iComponent=args[0] 81 | phi_b = Region.fluid[theFieldName].phi[iBElements,iComponent] 82 | else: 83 | phi_b = Region.fluid[theFieldName].phi[iBElements,:] 84 | return np.squeeze(phi_b) 85 | 86 | def cfdGetGradientSubArrayForBoundaryPatch(theFieldName, iBPatch, Region,*args): 87 | # %========================================================================== 88 | # % Routine Description: 89 | # % This function returns the gradient subarray at a given cfdBoundary 90 | # %-------------------------------------------------------------------------- 91 | if Region.fluid[theFieldName].type=='scfdUrfaceScalarField': 92 | # iFaceStart = Region.mesh.cfdBoundaryPatchesArray[iBPatch]['startFaceIndex'] 93 | # iFaceEnd = iFaceStart+Region.mesh.cfdBoundaryPatchesArray[iBPatch]['numberOfBFaces'] 94 | iBFaces = Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBFaces'] 95 | phiGrad_b = Region.fluid[theFieldName].Grad.phi[iBFaces, :] 96 | elif Region.fluid[theFieldName].type=='volScalarField': 97 | # iElementStart = Region.mesh.numberOfElements+Region.mesh.cfdBoundaryPatchesArray{iBPatch}.startFaceIndex-Region.mesh.numberOfInteriorFaces; 98 | # iElementEnd = iElementStart+Region.mesh.cfdBoundaryPatchesArray{iBPatch}.numberOfBFaces-1; 99 | # iBElements = iElementStart:iElementEnd; 100 | iBElements=Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBElements'] 101 | phiGrad_b = Region.fluid[theFieldName].Grad.phi[iBElements, :] 102 | 103 | elif Region.fluid[theFieldName].type=='volVectorField': 104 | iBElements=Region.mesh.cfdBoundaryPatchesArray[iBPatch]['iBElements'] 105 | if args: 106 | iComponent=args[0] 107 | phiGrad_b = Region.fluid[theFieldName].Grad.phi[iBElements, :, iComponent] 108 | else: 109 | phiGrad_b = Region.fluid[theFieldName].Grad.phi[iBElements, :, :] 110 | 111 | return np.squeeze(phiGrad_b) -------------------------------------------------------------------------------- /src/pyFVM/cfdfunction.py: -------------------------------------------------------------------------------- 1 | import cfdtool.IO as io 2 | import numpy as np 3 | import cfdtool.Interpolate as interp 4 | import cfdtool.Math as mth 5 | # from cfdtool.quantities import Quantity as Q_ 6 | # import cfdtool.dimensions as dm 7 | 8 | 9 | def initializeMdotFromU(Region): 10 | """ 11 | 初始化面上的质量流量 phi,通过将速度 U 和密度 rho 从单元格插值到面上,并计算质量流量。 12 | 13 | 参数: 14 | - Region: 包含流体区域信息的对象。 15 | 16 | 过程: 17 | 1. 使用线性插值方法将速度 U 和密度 rho 从单元格插值到面上。 18 | 2. 计算每个面的质量流量 phi = rho_f * (Sf ⋅ U_f)。 19 | 20 | 返回: 21 | - self.phi: 形状为 (numberOfFaces, numberOfComponents) 的质量流量数组。 22 | """ 23 | # U_f=interp.cfdInterpolateFromElementsToFaces(Region,'linear',Region.fluid['U'].phi) 24 | # rho_f=interp.cfdInterpolateFromElementsToFaces(Region,'linear',Region.fluid['rho'].phi) 25 | #calculate mass flux through faces, 必须写成二维数组的形式,便于后续与U的数组比较运算! 26 | # 计算通量 Sf ⋅ U_f,得到每个面的流量,形状为 (nFaces, 1) 27 | # 计算质量流量 phi = rho_f * flux,形状为 (nFaces, 1) 28 | Region.fluid['mdot_f'].phi = cal_flux(Region.fluid['U'].phi, Region.fluid['rho'].phi, Region) # 形状: (nFaces, 1) 29 | 30 | # 检查 phi 是否包含非有限值(如 NaN 或无穷大) 31 | if not np.all(np.isfinite(Region.fluid['mdot_f'].phi.value)): 32 | io.cfdError('计算得到的质量流量 phi 包含非有限值') 33 | 34 | def cal_flux(U_phi,rho_phi,Region): 35 | U_f=interp.cfdInterpolateFromElementsToFaces(Region,'linear',U_phi) 36 | rho_f=interp.cfdInterpolateFromElementsToFaces(Region,'linear',rho_phi) 37 | Sf=Region.mesh.faceSf 38 | return rho_f*mth.cfdDot(Sf, U_f)[:, np.newaxis] -------------------------------------------------------------------------------- /test/PCG_big_test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.sparse.linalg import cg, spilu, LinearOperator 3 | from scipy.sparse import csr_matrix 4 | from pyamg import smoothed_aggregation_solver 5 | import os 6 | import sys 7 | 8 | # 获取当前脚本文件所在的目录 9 | current_dir = os.path.dirname(os.path.abspath(__file__)) 10 | # Add the path to the pyFVM directory 11 | src_path = os.path.abspath(os.path.join(current_dir, '..', 'src')) 12 | print(f"src 路径: {src_path}") # 打印路径,确保正确 13 | sys.path.insert(0, src_path) 14 | 15 | from pyFVM import Coefficients as coefficients 16 | from cfdtool import Solve as slv 17 | 18 | # 定义一个简单的 Mesh 类 19 | class Mesh: 20 | def __init__(self, elementNeighbours): 21 | self.elementNeighbours = elementNeighbours 22 | 23 | # 定义一个简单的 Region 类 24 | class Region: 25 | def __init__(self, mesh): 26 | self.mesh = mesh 27 | 28 | # 测试函数 29 | def test_pcg_solver_large_system(N=1000): 30 | """ 31 | 测试PCG求解器在较大规模系统上的性能和准确性。 32 | 33 | Args: 34 | N (int, optional): 系统的规模(元素数量)。默认为1000。 35 | """ 36 | # 构造1D网格的邻接关系 37 | theCConn = [] 38 | for i in range(N): 39 | neighbors = [] 40 | if i > 0: 41 | neighbors.append(i - 1) 42 | if i < N - 1: 43 | neighbors.append(i + 1) 44 | theCConn.append(neighbors) 45 | 46 | # 设置系数 47 | ac = np.full(N, 2.0, dtype=np.float64) # 对角线元素 48 | anb = [] 49 | for i in range(N): 50 | neighbors = theCConn[i] 51 | coeffs = np.full(len(neighbors), -1.0, dtype=np.float64) # 非对角线元素 52 | anb.append(coeffs) 53 | bc = np.ones(N, dtype=np.float64) # 右端项,可以根据需要调整 54 | 55 | # 创建一个假的 Region 对象,包含 mesh.elementNeighbours 56 | mesh = Mesh(elementNeighbours=theCConn) 57 | region = Region(mesh=mesh) 58 | 59 | # 实例化 Coefficients 对象 60 | coeffs = coefficients.Coefficients(region) 61 | 62 | # 设置系数 63 | coeffs.ac = ac 64 | coeffs.anb = anb 65 | coeffs.bc = bc 66 | 67 | # 组装矩阵 68 | coeffs.assemble_sparse_matrix(method='csr') 69 | 70 | # 验证矩阵属性 71 | try: 72 | coeffs.verify_matrix_properties() 73 | print("矩阵通过对称性和正定性验证。") 74 | except ValueError as e: 75 | print(f"矩阵验证失败: {e}") 76 | return 77 | 78 | # # 定义PCG求解器 79 | # def cfdSolvePCG(theCoefficients, maxIter, tolerance, relTol, preconditioner='ILU'): 80 | # from scipy.sparse.linalg import spilu, LinearOperator # 确保导入 81 | 82 | # A_sparse = theCoefficients.assemble_sparse_matrix() 83 | # theCoefficients.verify_matrix_properties() 84 | 85 | # r = theCoefficients.cfdComputeResidualsArray() 86 | # initRes = np.linalg.norm(r) 87 | 88 | # if initRes < tolerance or maxIter == 0: 89 | # return initRes, initRes 90 | 91 | # dphi = np.copy(theCoefficients.dphi) # 初始猜测 92 | # bc = theCoefficients.bc 93 | 94 | # # 设置预处理器 95 | # if preconditioner == 'ILU': 96 | # try: 97 | # ilu = spilu(A_sparse) 98 | # M_x = lambda x: ilu.solve(x) 99 | # M = LinearOperator(shape=A_sparse.shape, matvec=M_x) 100 | # except Exception as e: 101 | # raise RuntimeError(f"ILU 分解失败: {e}") 102 | # elif preconditioner == 'DIC': 103 | # # diag_A = A_sparse.diagonal().copy() 104 | # # diag_A[diag_A <= 0] = 1e-10 105 | # # M_diag = 1.0 / np.sqrt(diag_A) 106 | # # M = LinearOperator(shape=A_sparse.shape, matvec=lambda x: M_diag * x) 107 | # # 使用pyamg的SMG作为预处理器示例 108 | # ml = smoothed_aggregation_solver(A_sparse) 109 | # M = ml.aspreconditioner() 110 | # elif preconditioner == 'None': 111 | # M = None # 不使用预处理器 112 | # else: 113 | # raise ValueError(f"Unknown preconditioner: {preconditioner}") 114 | 115 | # # 调用共轭梯度求解器 116 | # dphi, info = cg(A_sparse, bc, x0=dphi, tol=tolerance, maxiter=maxIter, M=M) 117 | 118 | # if info == 0: 119 | # print(f"求解成功收敛 (预处理器: {preconditioner})") 120 | # elif info > 0: 121 | # print(f"在 {info} 次迭代后达到设定的收敛容限 (预处理器: {preconditioner})") 122 | # else: 123 | # print(f"求解未能收敛 (预处理器: {preconditioner})") 124 | 125 | # finalRes = np.linalg.norm(bc - A_sparse @ dphi) 126 | # theCoefficients.dphi = dphi 127 | # return initRes, finalRes 128 | 129 | # 计算精确解以进行比较(仅在适当规模时) 130 | if N <= 1000: 131 | A_dense = np.zeros((N, N), dtype=np.float64) 132 | for i in range(N): 133 | A_dense[i, i] = 2.0 134 | if i > 0: 135 | A_dense[i, i - 1] = -1.0 136 | if i < N - 1: 137 | A_dense[i, i + 1] = -1.0 138 | b_dense = bc 139 | try: 140 | x_exact = np.linalg.solve(A_dense, b_dense) 141 | # print("精确解 x_exact:", x_exact) 142 | 143 | # 运行PCG求解器,使用ILU预处理器 144 | print("\n使用ILU预处理器进行求解:") 145 | maxIter = 1000 146 | tolerance = 1e-6 147 | relTol = 1e-6 148 | preconditioner = 'ILU' 149 | initRes_ilu, finalRes_ilu = slv.cfdSolvePCG(coeffs, maxIter, tolerance, relTol, preconditioner) 150 | print("初始残差:", initRes_ilu) 151 | print("最终残差:", finalRes_ilu) 152 | # print("求解结果 dphi (ILU):", coeffs.dphi) 153 | # 比较数值解和精确解 154 | error_ilu = np.linalg.norm(coeffs.dphi - x_exact) 155 | # print("\n精确解 x_exact:", x_exact) 156 | print("解的误差 (ILU):", error_ilu) 157 | if error_ilu < 1e-6: 158 | print("ILU预处理器测试通过") 159 | else: 160 | print("ILU预处理器测试失败") 161 | 162 | # 重置dphi和bc以进行下一次求解 163 | coeffs.dphi = np.zeros(N, dtype=np.float64) 164 | # coeffs.bc = bc # 保持bc不变 165 | 166 | # 运行PCG求解器,使用DIC预处理器 167 | print("\n使用DIC预处理器进行求解:") 168 | preconditioner = 'DIC' 169 | 170 | initRes_dic, finalRes_dic = slv.cfdSolvePCG(coeffs, maxIter, tolerance, relTol, preconditioner) 171 | 172 | print("初始残差:", initRes_dic) 173 | print("最终残差:", finalRes_dic) 174 | # print("求解结果 dphi (DIC):", coeffs.dphi) 175 | # 比较数值解和精确解 176 | error_dic = np.linalg.norm(coeffs.dphi - x_exact) 177 | # print("\n精确解 x_exact:", x_exact) 178 | print("解的误差 (DIC):", error_dic) 179 | if error_dic < 1e-6: 180 | print("DIC预处理器测试通过") 181 | else: 182 | print("DIC预处理器测试失败") 183 | 184 | 185 | # 重置dphi和bc以进行下一次求解 186 | coeffs.dphi = np.zeros(N, dtype=np.float64) 187 | # coeffs.bc = bc # 保持bc不变 188 | # 运行PCG求解器,不使用预处理器 189 | print("\n不使用预处理器进行求解:") 190 | preconditioner = 'None' 191 | initRes_none, finalRes_none = slv.cfdSolvePCG(coeffs, maxIter, tolerance, relTol, preconditioner) 192 | print("初始残差:", initRes_none) 193 | print("最终残差:", finalRes_none) 194 | # print("求解结果 dphi (None):", coeffs.dphi) 195 | # 比较数值解和精确解 196 | error_none = np.linalg.norm(coeffs.dphi - x_exact) 197 | # print("\n精确解 x_exact:", x_exact) 198 | print("解的误差 (None):", error_none) 199 | if error_none < 1e-6: 200 | print("无预处理器测试通过") 201 | else: 202 | print("无预处理器测试失败") 203 | 204 | except np.linalg.LinAlgError: 205 | print("精确解计算失败:矩阵可能太大或病态。") 206 | pass 207 | 208 | # 运行测试函数 209 | if __name__ == "__main__": 210 | # 设定系统规模为1000 211 | test_pcg_solver_large_system(N=1000) 212 | -------------------------------------------------------------------------------- /test/PCG_test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.sparse.linalg import cg 3 | from scipy.sparse import csr_matrix 4 | import os 5 | import sys 6 | 7 | # 获取当前脚本文件所在的目录 8 | current_dir = os.path.dirname(os.path.abspath(__file__)) 9 | # Add the path to the pyFVM directory 10 | src_path = os.path.abspath(os.path.join(current_dir,'..', 'src')) 11 | print(f"src 路径: {src_path}") # 打印路径,确保正确 12 | sys.path.insert(0, src_path) 13 | 14 | from pyFVM import Coefficients as coefficients 15 | 16 | # 定义一个简单的 Mesh 类 17 | class Mesh: 18 | def __init__(self, elementNeighbours): 19 | self.elementNeighbours = elementNeighbours 20 | 21 | # 定义一个简单的 Region 类 22 | class Region: 23 | def __init__(self, mesh): 24 | self.mesh = mesh 25 | 26 | # 定义 Coefficients 类(已在前面给出) 27 | 28 | # 测试函数 29 | def test_pcg_solver(): 30 | # 定义一个简单的 3 元素 1D 问题 31 | theCConn = [[1], [0, 2], [1]] # 邻接关系 32 | 33 | NumberOfElements = 3 34 | 35 | ac = np.array([2, 2, 2], dtype=np.float32) 36 | anb = [ 37 | np.array([-1], dtype=np.float32), 38 | np.array([-1, -1], dtype=np.float32), 39 | np.array([-1], dtype=np.float32) 40 | ] 41 | bc = np.array([1, 2, 3], dtype=np.float32) 42 | 43 | # 创建一个假的 Region 对象,包含 mesh.elementNeighbours 44 | mesh = Mesh(elementNeighbours=theCConn) 45 | region = Region(mesh=mesh) 46 | 47 | # 实例化 Coefficients 对象 48 | coeffs = coefficients.Coefficients(region) 49 | 50 | # 设置系数 51 | coeffs.ac = ac 52 | coeffs.anb = anb 53 | coeffs.bc = bc 54 | 55 | # 组装矩阵 56 | coeffs.assemble_sparse_matrix(method='csr') 57 | 58 | # 验证矩阵属性 59 | try: 60 | coeffs.verify_matrix_properties() 61 | print("矩阵通过对称性和正定性验证。") 62 | except ValueError as e: 63 | print(f"矩阵验证失败: {e}") 64 | return 65 | 66 | # 运行 PCG 求解器 67 | maxIter = 1000 68 | tolerance = 1e-6 69 | relTol = 1e-6 70 | preconditioner = 'None' # 对于小问题,不使用预处理器 71 | 72 | def cfdSolvePCG(theCoefficients, maxIter, tolerance, relTol, preconditioner='ILU'): 73 | A_sparse = theCoefficients.assemble_sparse_matrix() 74 | theCoefficients.verify_matrix_properties() 75 | 76 | r = theCoefficients.cfdComputeResidualsArray() 77 | initRes = np.linalg.norm(r) 78 | 79 | if initRes < tolerance or maxIter == 0: 80 | return initRes, initRes 81 | 82 | dphi = np.copy(theCoefficients.dphi) # 初始猜测 83 | bc = theCoefficients.bc 84 | 85 | # 设置预处理器 86 | if preconditioner == 'ILU': 87 | try: 88 | ilu = spilu(A_sparse) 89 | M_x = lambda x: ilu.solve(x) 90 | M = LinearOperator(shape=A_sparse.shape, matvec=M_x) 91 | except Exception as e: 92 | raise RuntimeError(f"ILU 分解失败: {e}") 93 | elif preconditioner == 'DIC': 94 | diag_A = A_sparse.diagonal().copy() 95 | diag_A[diag_A <= 0] = 1e-10 96 | M_diag = 1.0 / np.sqrt(diag_A) 97 | M = LinearOperator(shape=A_sparse.shape, matvec=lambda x: M_diag * x) 98 | elif preconditioner == 'None': 99 | M = None # 不使用预处理器 100 | else: 101 | raise ValueError(f"Unknown preconditioner: {preconditioner}") 102 | 103 | # 调用共轭梯度求解器 104 | dphi, info = cg(A_sparse, bc, x0=dphi, tol=tolerance, maxiter=maxIter, M=M) 105 | 106 | if info == 0: 107 | print("求解成功收敛") 108 | elif info > 0: 109 | print(f"在 {info} 次迭代后达到设定的收敛容限") 110 | else: 111 | print("求解未能收敛") 112 | 113 | finalRes = np.linalg.norm(bc - A_sparse @ dphi) 114 | theCoefficients.dphi = dphi 115 | return initRes, finalRes 116 | 117 | 118 | 119 | initRes, finalRes = cfdSolvePCG(coeffs, maxIter, tolerance, relTol, preconditioner) 120 | 121 | print("初始残差:", initRes) 122 | print("最终残差:", finalRes) 123 | print("求解结果 dphi:", coeffs.dphi) 124 | 125 | # 计算精确解以进行比较 126 | A_dense = np.array([ 127 | [2, -1, 0], 128 | [-1, 2, -1], 129 | [0, -1, 2] 130 | ], dtype=np.float32) 131 | b_dense = bc 132 | x_exact = np.linalg.solve(A_dense, b_dense) 133 | print("精确解 x_exact:", x_exact) 134 | 135 | # 比较数值解和精确解 136 | error = np.linalg.norm(coeffs.dphi - x_exact) 137 | print("解的误差:", error) 138 | if error < 1e-6: 139 | print("测试通过") 140 | else: 141 | print("测试失败") 142 | 143 | pass 144 | 145 | # 运行测试函数 146 | test_pcg_solver() 147 | -------------------------------------------------------------------------------- /test/Pint_test.py: -------------------------------------------------------------------------------- 1 | import pint 2 | import numpy as np 3 | 4 | # 创建 UnitRegistry 5 | ureg = pint.UnitRegistry() 6 | 7 | # 创建 Quantity 对象 8 | length = 5 * ureg.meter 9 | time = 10 * ureg.second 10 | velocity = length / time # 计算速度 11 | 12 | print(f"长度: {length}") # 输出: 长度: 5 meter 13 | print(f"时间: {time}") # 输出: 时间: 10 second 14 | print(f"速度: {velocity}") # 输出: 速度: 0.5 meter / second 15 | 16 | # 数组操作 17 | length_array = np.array([1, 2, 3]) * ureg.meter 18 | scaled_length = length_array * 2 19 | print(f"缩放后的长度数组: {scaled_length}") # 输出: 缩放后的长度数组: [2 4 6] meter 20 | 21 | # 单位转换 22 | length_cm = length.to(ureg.centimeter) 23 | print(f"长度(厘米): {length_cm}") # 输出: 长度(厘米): 500.0 centimeter 24 | 25 | # 自定义函数应用 26 | def multiply_length(q, factor): 27 | return q * factor 28 | 29 | new_length = multiply_length(length, 3) 30 | print(f"新的长度: {new_length}") # 输出: 新的长度: 15 meter 31 | 32 | # 无量纲操作 33 | dimensionless_quantity = 2 * ureg.dimensionless 34 | result = length * dimensionless_quantity 35 | print(f"结果: {result}") # 输出: 结果: 10 meter 36 | 37 | # 错误操作示例 38 | try: 39 | invalid = length + time 40 | except pint.DimensionalityError as e: 41 | print(f"错误: {e}") # 输出: 错误: Cannot add 'meter' and 'second' 42 | 43 | # 自定义单位 44 | ureg.define('lightyear = 9.461e15 * meter') 45 | distance = 2 * ureg.lightyear 46 | print(f"距离: {distance}") # 输出: 距离: 2 lightyear 47 | 48 | # 单位转换 49 | distance_km = distance.to(ureg.kilometer) 50 | print(f"距离(千米): {distance_km}") # 输出: 距离(千米): 1.8922e+19 kilometer 51 | 52 | pass 53 | -------------------------------------------------------------------------------- /test/Quantity_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | # 获取当前脚本文件所在的目录 5 | current_dir = os.path.dirname(os.path.abspath(__file__)) 6 | # Add the path to the pyFVM directory 7 | src_path = os.path.abspath(os.path.join(current_dir,'..', 'src')) 8 | print(f"src 路径: {src_path}") # 打印路径,确保正确 9 | sys.path.insert(0, src_path) 10 | 11 | from cfdtool.quantities import Quantity as Q_ 12 | import cfdtool.dimensions as dm 13 | import cfdtool.Math as mth 14 | 15 | local_centre = np.zeros(3) # 标量 16 | face_centroid = Q_([2, 3, 4], dm.Dimension([0, 1, 0, 0, 0, 0, 0])) # 带量纲 17 | # 触发 __add__,这应该抛出错误 18 | # result1 = face_centroid + local_centre # 调用 __add__ 19 | 20 | # 触发 __radd__,这也应该抛出错误 21 | # result2 = local_centre + face_centroid # 调用 __radd__ 22 | LL=dm.Dimension(L=1) 23 | MM=dm.Dimension(M=1) 24 | LL*MM 25 | dm.mass_dim 26 | dm.Dimension(dim_list=[0, 1, 0, 0, 0, 0, 0]) 27 | dm.length_dim*dm.pressure_dim 28 | q1=Q_([1, 2, 3, 4, 5],dm.length_dim) 29 | q2=Q_([30, 40, 50, 40, 44],dm.length_dim) 30 | q3=Q_([3, 4, 5, 4, 44],dm.pressure_dim) 31 | q4=q1+q2 32 | q5=q1*q2 33 | q6=q1/q2 34 | 35 | 36 | print(q2) 37 | q2+=q1 38 | print(q1) 39 | print(q2) 40 | q2[3:]+=q1[3:] 41 | print(q2) 42 | q2[3:].value+=q1[3:].value 43 | print(q2) 44 | 45 | 46 | 47 | print('--------------------------------------------------\n') 48 | q7=q3 49 | q8=Q_(q3.value,q3.dimension) 50 | print(q3) 51 | q7.value[2]=33.3 52 | print(q7) 53 | print(q3) 54 | print(q8) 55 | print('--------------------------------------------------\n') 56 | q8.value=q3.value 57 | q3.value[2]=111 58 | print(q7) 59 | print(q8) 60 | 61 | 62 | print(np.linalg.norm(q1)) 63 | # print(q1.apply(np.linalg.norm)) 64 | print(q1) 65 | print(q2) 66 | q2[2:5]=q1[2:5] 67 | print(q2) 68 | q2 += q1 69 | print(q2) 70 | q1.value[2]=300 71 | print(q2) 72 | q2[2:5]=q6[2:5] 73 | # q5=q1-q3 -------------------------------------------------------------------------------- /test/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from cfdtool import Solve 3 | 4 | def test_cfdSolveAlgebraicSystem(): 5 | # Test case 1: Smoother = 'DILU' 6 | theCoefficients = { 7 | 'ac': [1.0, 2.0, 3.0], 8 | 'anb': [np.array([0.1, 0.2]), np.array([0.3, 0.4]), np.array([0.5, 0.6])], 9 | 'bc': [0.5, 0.7, 0.9], 10 | 'theCConn': [[1], [0, 2], [1]], 11 | 'dphi': [0.0, 0.0, 0.0], 12 | 'NumberOfElements': 3 13 | } 14 | maxIter = 20 15 | tolerance = 1e-6 16 | relTol = 0.1 17 | iComponent = -1 18 | 19 | initialResidual, finalResidual = Solve.cfdSolveAlgebraicSystem(1, 'Equation 1', theCoefficients, smoother='DILU', maxIter=maxIter, tolerance=tolerance, relTol=relTol, iComponent=iComponent) 20 | 21 | assert initialResidual == 0.0 22 | assert finalResidual == 0.0 23 | 24 | # Test case 2: Smoother = 'SOR' 25 | theCoefficients = { 26 | 'ac': [1.0, 2.0, 3.0], 27 | 'anb': [np.array([0.1, 0.2]), np.array([0.3, 0.4]), np.array([0.5, 0.6])], 28 | 'bc': [0.5, 0.7, 0.9], 29 | 'theCConn': [[1], [0, 2], [1]], 30 | 'dphi': [0.0, 0.0, 0.0], 31 | 'NumberOfElements': 3 32 | } 33 | maxIter = 20 34 | tolerance = 1e-6 35 | relTol = 0.1 36 | iComponent = -1 37 | 38 | initialResidual, finalResidual = Solve.cfdSolveAlgebraicSystem(1, 'Equation 1', theCoefficients, smoother='SOR', maxIter=maxIter, tolerance=tolerance, relTol=relTol, iComponent=iComponent) 39 | 40 | assert initialResidual == 0.0 41 | assert finalResidual == 0.0 42 | 43 | # Test case 3: Smoother = 'GaussSeidel' 44 | theCoefficients = { 45 | 'ac': [1.0, 2.0, 3.0], 46 | 'anb': [np.array([0.1, 0.2]), np.array([0.3, 0.4]), np.array([0.5, 0.6])], 47 | 'bc': [0.5, 0.7, 0.9], 48 | 'theCConn': [[1], [0, 2], [1]], 49 | 'dphi': [0.0, 0.0, 0.0], 50 | 'NumberOfElements': 3 51 | } 52 | maxIter = 20 53 | tolerance = 1e-6 54 | relTol = 0.1 55 | iComponent = -1 56 | 57 | initialResidual, finalResidual = Solve.cfdSolveAlgebraicSystem(1, 'Equation 1', theCoefficients, smoother='GaussSeidel', maxIter=maxIter, tolerance=tolerance, relTol=relTol, iComponent=iComponent) 58 | 59 | assert initialResidual == 0.0 60 | assert finalResidual == 0.0 61 | 62 | test_cfdSolveAlgebraicSystem() --------------------------------------------------------------------------------