【CFD小工坊】模型网格(三角形网格)

news2024/11/19 15:37:19

【CFD小工坊】模型网格(三角形网格)

  • 前言
  • 网格几何
  • 网格编号
  • 编程实现
    • 数据读入
    • 网格数据构建

本系列博文的是我学习二维浅水方程理论,直至编译一个实用的二维浅水流动模型的过程。(上一篇Blog回顾)

前言

本二维浅水模型将采用非结构化网格。为简单起见,我将从三角形网格开始构建模型、编写求解器。
许多代码库,软件都能实现三角形网格的生成。例如DHI MIKE软件、Triangle库等。对此,我个人推荐SMS软件;SMS软件能够通过导入边界格点,方便地生成三角形网格,且能检查所生成网格的质量。
当然,我们需要将这些网格数据导出,并编辑成我们所需的数据,最后输入本浅水模型。本博文将会明确我们导出的数据有哪些。

网格几何

首先,我对三角形网格的各个部分做了如下定义:

  1. 网格节点(point),指构成三角形的三个顶点,如下图空心圆所示;
  2. 网格面(cell),指这整个三角形;
  3. 网格中心(cell center),指三角形的几何中心,如下图红圆所示;
  4. 网格边(edges),一条边由两个网格节点(points)确定。
    在这里插入图片描述

一般来说,构成网格的两个必要元素是网格节点(points)和网格边(edges);当画出了三角形的顶点,并连出了网格边,则一套三角形网格得以确定。
但在一般情况下,我们常用的输入数据是网格节点,以及构成每个三角形的顶点序号。对于网格节点,输入数据即各个顶点的序号、x和y坐标值;之后,分别给出构成各个三角形的序号序列。以上图为例,point数据如下:

节点序号x 坐标y坐标
1x1y1
2x2y2
3x3y3
4x4y4
5x5y5

构成各个cell的数据如下(以下将这个类型的数据简称为“cell”,即默认的输入元素为point和cell数据):

网格序号构成网格的三个格点(格点序号)
11,2,5
22,3,5
33,4,5

网格编号

根据上面的例子,我们知道point和cell数据并非共用一套编号系统。例如,1号网格和1号节点并不存在必然关联,而网格三角形与节点间的关联通过cell数据描述。
这里,我们强调三角形网格中的点(point)、边(edge)和网格本身(cell)都各自用着一套编号系统;只有网格中心(cell center)和cell数据可共用同一套编号系统。因此,在本模型中,网格三角形cell的编号等同于其中心点cell center的编号。

编程实现

数据读入

首先,我们通过fopen、fgets等函数,从txt文件中分别读入格点坐标,记为xp和yp。xp和yp均为一个大小为Np的一维数值(xp[0 ~ (Np-1)]),Np表示总网格数。要注意的是,c语言中,数组索引的起点为0。
之后,再读入构成各个三角形的节点序号cell,记为node数组。node数据为一个二维数组,其大小 node[0 ~ 2][0 ~ (Nc-1)];其中,Nc表示三角形总数。

在此,我们给出一个示例代码:

/*
* Function: ReadGrid
* Usage: Read the grid data from the input files
* Called by: main
*/
void ReadGrid() {
	int num=0, i;

	// 1. to read grid points
	FILE* fp1 = fopen("points.txt", "r");
	if (fp1 == NULL) {
		fprintf(stderr, "Fail to read grid files (points.txt)! \n");
		exit(EXIT_FAILURE);
	}
	char* row1 = (char*)malloc(sizeof(char) * N_str);
	fgets(row1, N_str, fp1);
	sscanf(row1, "%d", &num);

	xp = (double *)malloc(sizeof(double) * num);
	yp = (double *)malloc(sizeof(double) * num);
	zbp = (double *)malloc(sizeof(double) * num);
	for (i = 0; i < num; i++) {
		fgets(row1, N_str, fp1);
		sscanf(row1, "%lf %lf \n", &xp[i], &yp[i]);
	}
	Np = num;
	free(row1);
	fclose(fp1);

	// 2. to read geometric data of all cells
	FILE* fp2 = fopen("cells.txt", "r");
	if (fp2 == NULL) {
		fprintf(stderr, "Fail to read grid files (elements.txt)! \n");
		exit(EXIT_FAILURE);
	}
	char* row2 = (char*)malloc(sizeof(char) * N_str);
	fgets(row2, N_str, fp2);
	sscanf(row2, "%d", &num);

	node = (int **)malloc(sizeof(int) * 3);
	for (int i = 0; i < 3; i++)
	{
		node[i] = (int *)malloc(sizeof(int) * num);
	}
	for (i = 0; i < num; i++) {
		fgets(row2, N_str, fp1);
		sscanf(row2, "%d %d %d \n", &node[0][i], &node[1][i], &node[2][i]);
		
		// redefine the index from zero instead of one
		node[0][i] = node[0][i] - 1;
		node[1][i] = node[1][i] - 1;
		node[2][i] = node[2][i] - 1;

	}
	Nc = num;
	free(row2);
	fclose(fp2);

	fprintf(stderr, "The grid data has been read... \n");
}

代码所需的读入文件points.txt的示例如下(第一行表示Np):

5
0.0	0.0
1.0	1.0
4.0 1.0
5.0	0.0
3.0	0.0

代码所需的读入文件cells.txt的示例如下(第一行表示Nc):

3
1 2 5
2 3 5
3 4 5

网格数据构建

在模型中,我们还需要知道的量有中心点坐标、网格面积、各条边的边长、网格的相邻关系等等(这个之后还会介绍)。在此,我们先以这几个量为例,介绍网格数据的处理。

/*
* Function: ConstructGrid
* Usage: Construct the grids for reading other data
* Called by: main
*/
void ConstructGrid() {
	int i, j, k, p1, p2, p3, count, count2;
	double x1, x2, x3, y1, y2, y3;

	xc = (double *)malloc(sizeof(double) * Nc);
	yc = (double *)malloc(sizeof(double) * Nc);
	area = (double *)malloc(sizeof(double) * Nc);
	neigh = (int **)malloc(sizeof(int) * 3);
	for (int i = 0; i < 3; i++)
	{
		neigh[i] = (int *)malloc(sizeof(int) * Nc);
	}
	edgeL = (double **)malloc(sizeof(int) * 3);
	for (int i = 0; i < 3; i++)
	{
		edgeL[i] = (double *)malloc(sizeof(int) * Nc);
	}

	for (i = 0; i < Nc; i++) {
		p1 = node[0][i];
		p2 = node[1][i];
		p3 = node[2][i];

		x1 = xp[p1];
		x2 = xp[p2];
		x3 = xp[p3];
		y1 = yp[p1];
		y2 = yp[p2];
		y3 = yp[p3];

		// 1. to define the cell centers and calculate the area
		xc[i] = (x1 + x2 + x3) / 3.0;
		yc[i] = (y1 + y2 + y3) / 3.0;
		area[i] = fabs(x1*y2 - x2*y1 + x2*y3 - x3*y2 + x3*y1 - x1*y3);

		// 2. to find the neiborning cells
		count = 0;
		for (j = 0; j < Nc; j++) {
			if (count >= 3) break;		// "count=3" means that all neighboring cells have been found
			if (j == i) continue;		// skip the itself
			count2 = 0;
			for (k = 0; k < 3; k++) {
				if (node[k][j] == p1 || node[k][j] == p2 || node[k][j] == p3)	count2 = count2 + 1;
			}
			if (count2 == 2) {
				neigh[count][i] = j;
				count = count + 1;
			}
		}
		
		if (count == 1) {
			neigh[1][i] = -1;
			neigh[2][i] = -1;
		}
		else if(count == 2){
			neigh[2][i] = -1;
		}

		// 3. to calculate the lengths of all edges
		edgeL[0][i] = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
		edgeL[1][i] = sqrt((x3 - x2)*(x3 - x2) + (y3 - y2)*(y3 - y2));
		edgeL[2][i] = sqrt((x1 - x3)*(x1 - x3) + (y1 - y3)*(y1 - y3));

	}
	fprintf(stderr, "The computational grid has been constructed... \n");
}

首先第一部分,我们定义了数组xc和yc用于记录网格中心点的坐标,这两个变量可以通过malloc函数开辟存储空间(上述代码第10 ~ 11行)。其余变量同理,均可用malloc函数定义空间。
之后,我们计算网格中心的xc和yc(上述代码第37 ~ 38行);随后再计算每个网格的面积area(上述代码第39行),面积公式如下:
A r e a = ∣ x 1 y 2 − x 2 y 1 + x 2 y 3 − x 3 y 2 + x 3 y 1 − x 1 y 3 ∣ Area = |x_1 y_2 - x_2 y_1 + x_2 y_3 - x_3 y_2 + x_3 y_1 - x_1 y_3| Area=x1y2x2y1+x2y3x3y2+x3y1x1y3
之后,我们再搜索每个网格的相邻网格,这对我们求解网格间的通量很有帮助。因为在每个时间步内,只有相邻的网格才有质量和动量通量传递。网格的接邻情况我们通过数组neigh[0 ~ 2][1 ~ (Nc-1)]来存储。在上述代码的42 ~ 62行,我们搜索了相邻网格。思路如下:

  1. 对于网格i与网格j,判断它们是否有两个相同的顶点;这可以通过它们的node[ ][i]与node[ ][j]
    来判断。当找到node[ ][i]与node[ ][j]有两个相同的元素(上述代码第46 ~ 53行),记录此时的i和j(上述代码第50 ~ 53行)。
  2. 一个网格的相邻网格可能有1,2或3个。对于相邻网格数为3的情况,neigh数组无需额外处理;当相邻网格数为1或2,neigh数组多余的元素将用-1填充

以上图的网格为例,当搜索结束后,得到的neigh数组结果如下(注意这里的编号,c语言的数组起点是0):

1	-1	-1
0	2	-1
1	-1	-1

此外,注意上述第二点的这个处理方法是暂时的,相邻网格数为1或2的网格的边会对应了计算区域的边界。所以后续还需对neigh数组进行处理。
最后,我们计算每一条边的长度,存于edgeL数组中(上述代码第65 ~ 67行)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker部署Nginx+FastDFS插件

文章目录 一、部署FastDFS二、部署Nginx(带FastDFS插件)三、FastDFS上传文件Nginx访问验证 一、部署FastDFS 1、准备工作 docker pull qinziteng/fastdfs:5.05 Pwd"/data/software/fastdfs" mkdir ${Pwd}/{storage,tracker} -p2、创建TEST容器&#xff0c;将fastdf…

uniapp中vue3使用uni.createSelectorQuery().in(this)报错

因为VUE3中使用setup没有this作用域&#xff0c;所以报错 解决办法&#xff1a;使用getCurrentInstance()方法获取组件实例 import { getCurrentInstance } from vue;const instance getCurrentInstance(); // 获取组件实例 const DOMArr uni.createSelectorQuery().in(ins…

【C#】.Net基础语法二

目录 一、字符串(String) 【1.1】字符串创建和使用 【1.2】字符串其他方法 【1.3】字符串格式化的扩展方法 【1.4】字符串空值和空对象比较 【1.5】字符串中的转移字符 【1.6】大写的String和小写的string 【1.7】StringBuilder类的重要性 二、数组(Array) 【2.1】声…

大数据的崭露头角:数据湖与数据仓库的融合之道

文章目录 数据湖与数据仓库的基本概念数据湖&#xff08;Data Lake&#xff09;数据仓库&#xff08;Data Warehouse&#xff09; 数据湖和数据仓库的优势和劣势数据湖的优势数据湖的劣势数据仓库的优势数据仓库的劣势 数据湖与数据仓库的融合之道1. 数据分类和标记2. 元数据管…

RabbitMQ快速入门——消费者

public class Consumer_HelloWorld {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133"); ip 默认值 localhostfac…

Vue3-Vue3生命周期、自定义hook函数、toRef与toRefs、其他组合式API、组合式API的优势、Vue3新的组件和功能

Vue3&#xff08;2&#xff09; 更多Vue.js知识请点击——Vue.js &#x1f954;&#xff1a;有的山长满荆棘&#xff0c;有的山全是野兽&#xff0c;所以你应该是自己的那座山 文章目录 Vue3&#xff08;2&#xff09;一、Vue3生命周期二、自定义hook函数三、toRef与toRefs四、…

Ctfshow web入门 phpCVE篇 web311-web315 详细题解 全

CTFshow phpCVE web311 CVE-2019-11043 PHP远程代码执行漏洞复现&#xff08;CVE-2019-11043&#xff09;【反弹shell成功】-腾讯云开发者社区-腾讯云 (tencent.com) 漏洞描述 CVE-2019-11043 是一个远程代码执行漏洞&#xff0c;使用某些特定配置的 Nginx PHP-FPM 的服务…

RabbitMQ的工作模式——WorkQueues

1.工作队列模式 生产者代码 public class Producer_WorkQueues1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133&qu…

基于Android+OpenCV+CNN+Keras的智能手语数字实时翻译——深度学习算法应用(含Python、ipynb工程源码)+数据集(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow环境Keras环境Android环境1. 安装AndroidStudio2. 导入TensorFlow的jar包和so库3. 导入OpenCV库 相关其它博客工程源代码下载其它资料下载 前言 本项目依赖于Keras深度学习模型&#xff0c;旨在对手语…

idea更改java项目名

做了一个普通的java项目&#xff08;使用socket进行网络通信的练手项目&#xff09;&#xff0c;需要更改项目名&#xff0c;更改过程记录在这里。 修改项目名可能会出现很多错误&#xff0c;建议先备份当前项目 1.在idea里&#xff0c;右键项目名——》选择Refactor——》选择…

服务注册发现_搭建单机Eureka注册中心

创建cloud-eureka-server7001模块 pom添加依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&quo…

Xshell工具连接本地虚拟机Linux系统

你知道的越多&#xff0c;你不知道的越多&#xff1b;本文仅做记录&#xff0c;方便以后备阅。希望也能帮到正在看这篇文章的你。 使用Xshell工具连接Linux系统具有方便&#xff0c;易于操作等诸多特点。对于Xshell的介绍&#xff0c;我就不详细说了。我相信百度百科上的介绍更…

RabbitMQ工作模式——PubSub生产者及消费者

PubSub模式生产者代码 public class Producer_PubSub {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133"); ip 默…

SAP PO运维(四):适配器消息监控

登录SAP PO系统,点击“Configuration and Monitoring Home”,使用PISUPER账号登录: 2、选择“适配器引擎->消息监控器”: 3、查看是否有报错消息: 双击报错的数字,筛选出报错的条目(可以根据状态、接口命名空间等来筛选):常见的报错消息有: 接口配置问题:字段为空值…

java框架-Springboot3-场景整合

文章目录 java框架-Springboot3-场景整合批量安装中间件NoSQL整合步骤RedisTemplate定制化 接口文档远程调用WebClientHttp Interface 消息服务 java框架-Springboot3-场景整合 批量安装中间件 linux安装中间件视频 NoSQL 整合redis视频 整合步骤 RedisTemplate定制化 Re…

Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例

Qt5开发及实例V2.0-第十八章-Qt-MyselfQQ实例 第18章-Qt MyselfQQ18.1 概述18.2 、发送文件18.3 、接收文件18.4 、保证传输的安全和稳定18.5 、总结 本章相关例程源码下载1.Qt5开发及实例_CH1801.rar 下载 第18章-Qt MyselfQQ 18.1 概述 MyselfQQ是一个基于Qt5框架开发的轻量…

技术学习群-第三周内容共享

本周为技术群聊的第三周&#xff0c;看看本周&#xff0c;都聊了些啥。 探讨u2以及appium 什么是u2&#xff1f;也就是uiautomator2&#xff0c;主要探讨的是uiautomator2与appium哪个更好用。 为什么探讨&#xff1f;他们拿来干什么的&#xff1f; 都是可以拿来做app自动化的&…

基于springboot+vue的毕业生实习与就业管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

C语言之字符函数字符串函数篇(1)

目录 前言 求字符串长度 strlen strlen统计的是字符串\0之前的字符串长度 字符指针 strlen的返回值是无符号整型 strlen的三种模拟实现 计数器 函数递归 指针_指针 长度不受限制的字符串函数 strcpy strcpy会将源字符串中的 \0 拷贝到目标空间 strcpy参数目标空…