首页/文章/ 详情

基于abaqus的sbfem及其在混凝土坝非线性地震响应分析中的应用

2小时前浏览35
   

概述

       
   
   

基于abaqus的sbfem

     

采用abaqus提供的uel和uexternaldb子程序二次开发平台将sbfem嵌入到abaqus中,其中子程序uel用于实现sbfem中关键矩阵的计算,包括amartix、rhs等等,下面是基于sbfem的uel子程序流程图

   

子程序uexternaldb用于将任意多面体单元以外部文件的形式导入进abaqus中,下面展示的是2个任意多面体的单元-节点信息

   

上述的单元-节点信息无法仅仅从节点顺序重现单元的几何拓扑关系,因此需要准备外部txt文件将上述单元-节点信息导入到abaqus中,下面的数据是上面2个单元的信息

   

采用Fortran程序编写完成uel之后,还需要在inp文件中定义用户自定义单元,目前的abaqus只支持手动修改,对于上述单元,在inp文件中对应的关键字为

   

耦合sbfem-fem

       

abaqus有许许多多的非线性模型,比如材料模型和界面模型,因此发展一种耦合方法,将用户自定义单元与abaqus自带的非线性模型相结合,以便同时发挥abaqus和用户自定义单元的优势,下面简要介绍两种耦合算法。

首先是将自定义单元与abaqus的接触非线性模型结合,需要在用于自定义单元的界面进行处理,如

   

其对应的公式为

   

如果要对用户自定义单元进行可视化,需要在原有的自定义单元基础上再嵌套一层abaqus自带的单元,并将自带单元的材料属性设置为无限小,如

   

悬臂梁受动荷载

       

设计悬臂梁受动荷载算例,验证子程序求解刚度矩阵、阻尼矩阵和质量矩阵的正确性,模型的边界条件和受荷载为

   

荷载表达式为

   

采用三种单元对该几何模型进行离散,三种单元分别为

六面体单元

   

四面体单元

   

棱柱体单元

   

统计悬臂端关键点的位移等数据,最终计算的结果基本一致。

   

koyna混凝土坝地震响应分析

       

以koyna混凝土重力坝为研究对象,将文章提出的方法应用到实际工程的地震响应分析,koyna混凝土坝的截面为

   

采用两种单元对该模型进行离散,分别为

六面体单元

   

棱柱体单元

   

对上述网格设置三个计算工况分别为




case1:六面体单元+fem计算case2:六面体单元+sbfem计算case3:棱柱体单元+sbfem计算
 

首先对上述计算模型进行自振频率分析,统计前20阶自振频率数值对比为

   

可以发现自振频率分析计算结果保持一致。然后对koyna混凝土坝进行地震动力响应分析,提取了坝顶-坝锺的相对数据,分别为

竖直相对位移

   

水平相对位移

   

竖直相对速度

   

水平相对速度

   

计算结果均保持一致。计算时长比较为

   

可以发现sbfem的计算时长均比fem有缩短,其中case3的计算时长缩短最明显,说明棱柱体单元有较好的计算性能。

NG5拱坝地震响应分析

       

选取NG5拱坝为研究对象,考虑坝体横缝接触非线性与土-结构动力相互作用模型,探究文章提出的方法在拱坝地震响应分析中的作用。NG5拱坝的平面布置图及其横缝为

   

坝体-地基-库水的几何模型为

   

采用六面体单元离散几何模型,最终的计算模型为

   

对上述计算模型设置三种计算工况

   

土-结构动力相互作用采用黏弹性边界模拟,黏弹性边界是计算模型边界节点上一系列的弹簧-阻尼器元件

   

坝体横缝接触非线性采用abaqus自带的指数接触模型

   

输入地震动的位移和速度为

   

坝体横缝附近的关键点为

   

首先统计了坝体横的开度时程曲线

   

统计了关键点的动水压力时程曲线,c点数据为

   

d点数据为

   

e点数据为

   

统计了15s时刻坝面的动水压力云图

   

结合以上数据,可以发现,考虑了土-结构动力相互作用以后,混凝土坝整体的响应,包括相对位移、横缝开度和坝面动水压力等数据都有不同程度的降幅,说明了土-结构动力相互作用在混凝土坝抗震计算中应予以重点考虑。

结论

       

The scaled boundary finite element method (SBFEM) is implemented in the ABAQUS through the user subroutine interface. With the coupled SBFEM and standard finite element method (FEM), the dynamic interaction model of concrete dam – reservoir – foundation considering the geometrical nonlinearity of transverse joints is established using the viscous – spring absorbing boundary conditions. The overlaying element technique is applied to define the contact interface of transverse joints. The dynamic characteristics and linear seismic response of the Koyna gravity dam have been analyzed using polyhedral elements. Finally, nonlinear seismic analysis of the NG5 arch dam has been conducted. The following conclusions can be drawn:

(1) According to several benchmark examples, the proposed method has good accuracy and efficiency.

 (2) The overlaying element technique, which does not require the introduction of extra DOFs, completes the combination of the SBFEM with the built-in elements, models, and other functions of ABAQUS.

 (3) Based on the coupled SBFEM-FEM, the dynamic interaction model of dam, reservoir and foundation, the nonlinearity of joint contact, and the seismic input can be fully considered. The calculated results are consistent with those of ABAQUS built-in elements with a high computational efficiency. It will provide a novel method for the dynamic analysis of complex engineering structures.

     
      


       


来源:有限元先生
ACTAbaqus非线性二次开发UG材料
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2024-09-28
最近编辑:2小时前
外太空土豆儿
硕士 我们穷极一生,究竟在追寻什么?
获赞 2粉丝 0文章 5课程 0
点赞
收藏
作者推荐

C++指针与数组基础概念

零概述 指针是C/C++学习的核心,可以说是C/C++语言的明珠,所有的计算机编程语言中,只有C/C++有指针,如果能把指针运用得当,就算是把C/C++两种编程语言都精通了,但是精通指针是一件相当困难的事情。这个帖子只是简单的介绍一下指针和数组的基本概念,供大家参考,只能算是把读者引入门。指针更复杂的应用需要查看专业的书籍。壹指针 内存是一系列地址连续的储存区域,就像是一个个小房间 ,房间号是连续的,为了方便读取和写入数据,设计者通过给储存区域设置地址来进行数据的操作就像是连续的房间号,但是直接操作地址并不是一件简单的事情,因为内存里的地址是十六进制的数字,因此当我们声明一个变量时候,编译器会开辟一段内存区域,如 这个内存区域与我们声明的变量类型是对应的,这就是C++声明变量要非常严格的原因,我们给变量赋值的时候,编译器会自动的将数据放到变量所对应的内存区域,这样就完成了数据的读写。这就意味着,普通变量变量是为了方便索引地址,而普通变量变量对应的地址中,储存着数值。即普通变量储存的是数值。指针同样是一种数据类型,相对于变量储存的是数值本身,而的指针储存的则是变量的地址。指针为变量提供了一种极其高效的处理方法是,通过地址索引能够大大提高程序的运行速度。指针的声明方式为\\指针对应d额地址储存d额数值类型 *指针名称;int *p=NULL; \\声明了一个整型指针,该指针储存d的地址中保存的是整型数据类型\\初始声明将该指针设置为没有任何指向 下面程序简要介绍了一下指针的基本运算# include<iostream>#include<string>using namespace std;int main(){ int data = 10; \\声明一个整型变量 int *p = NULL; \\声明一个整型指针,初始值为空 p = &data; \\将data的地址赋值给整型指针 cout << "data的地址为:\t" << &data << endl; \\*P把指针指向d的地址储存的数值提取出来 \\这种操作方法被成为指针的解引用 \\指针的解引用相当于直接提取变量的数值 cout << "指针指向的地址储存的数值为:\t" << *p << endl; cout << "data变量的数值为:\t" << data << endl; system("pause"); return 0;} 上面程序用到了C++中的取地址符号“&”,这个符号能获取C++语言中任何变量的地址,甚至是函数的地址,上面程序输出的内容为data的地址为: 00EFFBAC指针指向的地址储存的数值为: 10data变量的数值为: 10 可以看到,指针的解引用确实相当于直接输出了变量的数值。上面的程序只是简单的介绍了指针的基本声明和取地址内容,是指针内容的九牛一毛,当指针和函数、结构体等功能结合在一起的时候,指针的威力才能完全的发挥出来。下面介绍下C++中数组的使用方法,以及指针与数组的关系。贰数组 数组就是储存同一类型数据的容器,可以储存int、float、double和char等等数据,数组的声明方式为//数据类型 数组名称[数组元素个数];int data[10]; //10个整整型数据的数组float data[90]; //90个单精度浮点数d额数组】double data[34]; //34个双精度浮点型数据的数组 数组和指针有着很紧密的关系,在C++中,数组的名称就是一个地址,可以直接输出出来,也可以将数组的名称(地址)赋值给其他的指针,就可以通过指针对数组进行访问,每移动一个指针,指针指向的地址就会移动一个整型单位,因此可以通过指针输出数组的内容,下面的程序采用两种方式对数组进行输出# include<iostream># include<string>using namespace std;int main(){ int data[10] ; int *p = data; //将数组的开始位置赋值给指针 //数组的名称即为数组开始地址 cout << "数组起始位置的地址(data)为:"<<data<< endl; cout << "数组起始位置的地址(p)为:" << p << endl; for (int i = 0; i < 10; i++){ data[i]=i*2; } //通过数组索引数组数值 cout << "通过数组索引数组数值:"<< endl; for (int i = 0; i < 10; i++){ cout << " data["<<i<<"]=\t" << data[i] << endl; } cout << "通过指针索引数组数值:" << endl; //通过指针索引数组数值 for (int i = 0; i < 10; i++){ cout << " *(P+" << i << "]=\t" << *(p+i) << endl; } system("pause"); return 0;} 程序运行的结果为数组起始位置的地址(data)为:012FFD98数组起始位置的地址(p)为:012FFD98通过数组索引数组数值: data[0]= 0 data[1]= 2 data[2]= 4 data[3]= 6 data[4]= 8 data[5]= 10 data[6]= 12 data[7]= 14 data[8]= 16 data[9]= 18通过指针索引数组数值: *(P+0]= 0 *(P+1]= 2 *(P+2]= 4 *(P+3]= 6 *(P+4]= 8 *(P+5]= 10 *(P+6]= 12 *(P+7]= 14 *(P+8]= 16 *(P+9]= 18 可以发现,数组的名称确实是一个地址,但是需要注意,程序每次运行数组的位置不是固定的,因为编译器每运行一次程序就会重新给数组分配一次内存地址。后面分别采用指针和数组索引的两种方式输出数组的数值,两种方式输出的结果是相同的。下面再给出一个采用指针操作字符串数组的例子#include<iostream>#include<string>using namespace std;int main(){ //初始化字符串 string data="introduction"; //声明一个指向字符额指针 char *p = NULL; //初始化指针为空 //采用数组索引的方式输出字符串 cout << "采用数组索引的方式输出字符串"<< endl; for (int i = 0; i < sizeof("introduction") / sizeof(char); i++){ cout << data[i] << "\t"; } cout << endl; //换行 //将字符串的第一个字符地址赋值给指针 p = &(data[0]); //采用指针的形式输出字符串 cout << "采用指针索引的方式输出字符串" << endl; for (int i = 0; i < sizeof("introduction") / sizeof(char); i++){ cout << *(p+i)<< "\t"; } cout <<endl; //换行 system("pause"); return 0;} 上面的程序先声明了一个字符串数组“introduction”,后声明了一个指向字符的指针,注意这里的指针没有指向字符串,因为我们想要操控字符,二而不是字符串。然后采用数组的方操控字符串。下面将字符串中第一个字符的地址赋值给之前声明的指针,注意这里并没有直接将字符串数组的名称赋值给指针,这与整型数组不同,这里的字符串名称并不是地址,因此需要将字符串数组第一个字符的地址赋值给指针。后续采用指针操控字符串数组,输出的结果对比如下采用数组索引的方式输出字符串i n t r o d u c t i o n采用指针索引的方式输出字符串i n t r o d u c t i o n 二者的输出是完全相同的。叁指针、数组和函数联用 设计数组求和的例子,使用了指针、数组和函数三种工具,目的是学习函数之间传递指针的用法。这里我们采用两种计算方法,一种是直接在函数之间传递数组,另一种是在函数之间传递指针,首先展示的例子是在函数之间传递数组,如#include<iostream>using namespace std;const int people = 8;//直接传递数组int sum(int [],int );int main(){ int data; int arr[8] = {1,2,3,4,5,6,7,8}; data = sum(arr, people); cout << "班级里所有人签到的总次数为:\t"<<data<< endl; system("pause"); return 0;}int sum(int arr[], int n){ int m = 0; for (int i = 0; i < n; i++) { m += arr[i]; } return m;} 上面第七行的函数原型,里面的参数是数组本身,后面具体的函数定义中,也是直接将数组所有的数值传递进函数中进行求和计算,最终计算的结果是36 下面采用传递指针的方式给数组求和,如# include <iostream>#include <string>using namespace std;//函数原型中的参数是指针int sum(int *, int);int main(){ int ar[8] = {1,2,3,4,5,6,7,8}; int *p; p = ar; cout << sum(ar, 8) << endl; system("pause"); return 0;}int sum(int *p, int n){ int i; int op=0; for (i = 0; i < n; i++) { op += *(p + i); } return op;} 上面程序中第七行函数原型int sum(int *,int) 表明了传入函数的第一个参数是一个指向整型对的指针,第二个参数是数组的数量。在具体的函数定义中int sum(int *p, int n){ int i; int op=0; for (i = 0; i < n; i++) { op += *(p + i); } return op;} 按照指针的方式进行索引数组,指针没移动一次,移动的距离就是指针指向的数据类型的大小,数组一共有n个元素,则指针一共移动n次,最终的计算结果为36 上面两种数组求和分别向行数传递了数组和指针,这二者是有很大区别的,因为C++是传值的,这意味着当向函数传递一整个数组的时候,编译器需要更大的缓存空间,而传递指针则不同,一个整型纸指针占用的储存空间位置为4个字节,这就对大大缩小了编译器申请的缓存空间,这在大型计算中的优势极为明显。下面同样给了一个函数接收字符型指针数据访问数组的例子#include<iostream>#include<string>using namespace std;void fun_pointer(char *, int);void fun_array(char [], int);int main(){ char str[] = "introduction"; int len; char *p; len = strlen(str); // 计算字符串长度 p = str; //将字符型指针赋值给指针 cout << "采用指针访问字符串数组。"<< endl; fun_pointer(p, len); cout << "直接访问数组。" << endl; fun_array(str, len); system("pause"); return 0;}//void fun_array(char str[], int n){ for (int i = 0; i < n; i++) { if (i == n-1) { cout << str[i] << "\t" << endl; } else { cout << str[i] << "\t"; } }}//void fun_pointer(char *p, int n){ for (int i = 0; i < n; i++) { if (i ==n-1) { cout <<*(p+i)<<"\t"<< endl; } else { cout << *(p + i) << "\t"; } }} 分别直接和采用指针访问数组,输出的结果为采用指针访问字符串数组。i n t r o d u c t i o n直接访问数组。i n t r o d u c t i o n 肆总结 C/C++中的指针是一个非常强大的工具,它允许我们直接在低智商对数据进行字节程度的操作,这是其他所有的编程语言都做不到的。可以看到,指针编程的思考方式与变量计算的方式完全不同,指针的操作与完全基于计算机硬件的内存操控思路来的,指针记录了内存中的地址,一切的操作都是基于地址的,这极大地方便了数据的读写。但是,强大的工具必然是双刃剑,指针对使用者的计算机硬件基础与编程功底有着极高的要求,上面所有的例子仅仅是科普水平,指针就像是一把蝴蝶刀,使用不好,会对内存导致意想不到的后果轻则程序崩溃,重则系统出问题,指针的使用要慎之又慎,指针的学习要从一而终,永无止境。深入学习指针的使用是十分有必要的。 来源:有限元先生

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习 福利任务 兑换礼品
下载APP
联系我们
帮助与反馈