个人收集整理仅供参考学习1第9章有限元法程序设计9.1引言在用有限元法进行结构分析时,将会遇到大量的数值计算,因而在实用上是一定要借助于计算机和有限元程序,才能完成这些复杂而繁重的数值计算工作。事实上,有限元程序的设计是有限元研究的一个很重要的部分。它是理论和方法的载体,是理论用于实际必不可少的桥梁,是有限元学术研究与实际应用水平的代表。有好的、高深的理论和算法并不等于有好的程序,还必须有实际的程序开发经验的多年积累、丰富的计算机知识、大量的资金和人力的投入,多年的开发修正与改进才能编制出好的程序来。一些著名的有限元程序开发的发展历史也体现出了这一规律。设计一个用于结构分析的有限元法程序,要求设计者至少应该掌握下列知识:(1)掌握一种程序开发工具,如VC(VisualC++),CB(C++Buildel),Delphi,VB(VisualBasic)或VF(VisualFortran)等。在本书中所有程序均用VC写出。(2)数值方法,如线性和非线性代数方程的求解,矩阵特征值的求解以及数值积分等。(3)结构分析的基本理论,特别是用有限元法对结构进行分析的原理、方法和步骤。由于一般的软件工程师不懂结构分析原理,因此,结构分析程序的开发任务主要应由结构工程师来承担。掌握结构分析程序设计方法,是以计算机辅助设计为主要标志的现代工程设计方法对结构工程师的要求。作为结构工程师,应该具有对结构分析程序的使用、阅读、修改和编制的基础知识和技术素质。有限元程序的总体组成可分为三个部分:前处理部分,有限元分析本体部分以及后处理部分。有限元分析本体部分是有限元分析程序的核心。它根据离散模型的数据文件进行有限元分析,有限元分析的原理和采用的数值方法集中于此。因此,这一部分程序是有限元分析是否准确可靠的关键部分。有限元分析所使用的离散模型的数据文件主要包括:模型的节点数、节点坐标与节点编码,单元数据与单元编码;材料和载荷信息等。实际工程问题的离散模型数据文件个人收集整理仅供参考学习2十分庞大。一般情况下,用人工方法来生成工作量太大,并且容易出错,有时甚至是不可能的。为解决这一问题,有限元程序必须有前处理程序。前处理程序根据使用者提供的对计算模型外形及网格要求的简单数据描述,能自动地或半自动地生成离散模型的数据文件,并能绘制结构计算简图和网格图,供用户检查修改。前处理程序的功能在很大程度上决定了有限元程序使用的方便性。有限元分析程序的计算结果是由离散模型而得到的,输出的数据量往往很大,不易整理,也不易获得分析对象的全貌。所以,一个使用方便的有限元分析程序还应具有较强的后处理功能。能够按照用户的需要提供应力分级图、等值线图,结构变形图或振型图等图形显示功能,以及按照一定的要求对计算成果进行列表显示或打印。因此,这部分程序设计的好坏,对整个有限元程序使用起来是否方便,具有举足轻重的作用。程序设计工作经历了纯技巧阶段,已经形成了一门被称为软件工程的学科。对于程序的质量评价也逐渐形成了一套客观标准。一个质量较高的程序应该具有较好的可管理性和较高的运行可靠性。可管理性要求程序的可读性好,易于调试、修改和发展,使用方便且效率高等。可靠性要求程序能正确无误地完成规定的功能,当出现不正常情况时,能中止无价值的运行并输出有关的信息。程序开发的过程大致可分为下述三个阶段:(1)程序功能的规定;(2)程序结构的设计,源程序及其说明的编写;(3)调试和纠错。目前在实际的程序开发中,流行着两种截然不同的方法,即面向过程的方法和面向对象的方法。大量的资料说明,在开发大型应用软件时,面向对象的方法与传统的过程化程序设计方法相比,显示出很大的优越性。然而在开发一些规模不大的中小型程序时,面向过程的方法仍然有一定的优势。本章将以平面杆系结构的静力分析为例,介绍用面向过程的方法进行有限元主体程序设计的方法。9.1.1结构化程序设计概述结构化程序设计方法是一种传统的软件设计方法。其基本要点是,自顶向下,逐步个人收集整理仅供参考学习3求精,以及模块化设计。其基本思想是,把一个复杂问题的求解过程划分成若干阶段来进行。每一个阶段所要解决的问题都控制在人们容易理解和处理的范围内,直到把原来的问题变换成若干个易于编写成程序的子问题(即模块)为止。这种程序的逐步分解和精化是从抽象的做什么到具体的怎么做的发展过程。程序展开的基本结构有下列三种:(1)顺序结构。把一个功能块展开成若干个顺序连接的语句块。用元语言(即程序设计语言和自然语言的一种混合文体,也称伪语言)表示如下:语句块1;//入口语句块2;…………语句块n;//出口(2)选择结构。把一个功能块展开成两个可供选择的语句块。用元语言表示如下:if(exp)//入口语句块1;else语句块2;//出口在C/C++中,Switch语句组成的结构也属于这种结构。(3)循环结构。把一个功能块展开成需要重复执行的语句块。用元语言表示如下:while(exp)//入口语句块;//出口在C/C++中类似的结构还可由dowhile语句以及for语句构成。上述程序展开的三种基本结构表达了程序内部语句块的执行次序。一个语句块可以是空的,可以是只含有一条简单语句,也可以是由一系列的语句组成的子问题块。一个个人收集整理仅供参考学习4语句块只有一个入口点和一个出口点。在一般情况下尽可能少使用goto之类的语句,最好不使用它。这是因为这类跳转语句在程序中的频繁出现,会极大地破坏程序的逻辑清晰度,使程序难以阅读和理解。结构化程序设计的另一个重要技术就是模块化编程。逐步求精的结果是以子功能块为单位的算法描述。以子功能块为单位进行程序设计,实现其求解算法的方式称为模块化设计。模块化方法可以有效地简化问题的复杂性和提高程序的正确性。模块化的准则是高独立性、高内聚性、低耦合性。自顶向下法和自底向上法是两种最基本的模块化编程方法。前者是逐步分解和求精的过程,而后者则是逐步组合的过程。自顶向下法是从整个程序的功能描述开始的。即首先把总功能分解为若干个较小的,然而相对来说较为简单的功能块。连续不断地这种分解,将会发现有些功能块已经足够简单了。于是就可以把它改写成程序。如果用元语言的程序文本来描述这一设计过程的话,则此时程序文本中的一部分已可以用程序语言表示了,而另一部分则仍然用元语言表示。随着不断地精化,最后将会把用元语言表示的整个设计文本全部由程序设计语言来表示,从而完成程序的编写工作。上述用元语言表示程序流程的方法便于计算机存贮与操作,而且对表示功能的自然语言(做什么),在编写完相应的程序段(怎么做)之后,加上注解标记就可以成为程序中的注解行。元语言表示正日益广泛地被程序设计人员采用。最终的程序结构可以用一树状图来表示。如图9.1所示,功能A被分解成B、C、D三个模块,然后再依次分解为E、F、G、H、I、J。此时模块E、F、G、H可以用程序语言编写,H、J分解为K、L。接着K、L可以用程序语言来编写。于是逐层修改完善程序文本,使说明程序功能的自然语言成为程序的注解行。个人收集整理仅供参考学习5图9.1自顶向下的程序设计自底而上的编程方法同自顶而下法正好相反。自顶而下法中的最后一个功能分解正是自底而上法中的第一个功能组合。不断进行这种由多个简单的功能块逐步组合成一个较为复杂的功能块的过程,直至完成全部编程工作。自顶而下法同自底而上法相比,具有效率高、适应性强,容易控制模块的复杂程序,以及能增加编程工作中的能见度等特点。因而通常被认为是更好的设计方法。但是当遇到比较复杂的实际问题时,为使设计更有把握,应该优先考虑关键部分。另外,当使用自顶而下法逐步分解功能块的同时,应该考虑在较低层次的功能块中充分利用自己已有的程序经验和知识,来组合所需要的功能块的可能性。因此,在实践中灵活交替地使用这两种方法可以提高工作效率。9.1.2程序的可读性和程序风格几乎所有的程序在使用过程中都要经受各种各样的维护性修改。据统计,程序在研制完成后的这种维护性的工作量大大超过了它在研制阶段的工作量。因而程序除了能正确地完成一系列的计算之外,还应该让维护人员容易看清其算法的逻辑联系,即应该具有良好的可读性。程序的可读性好了,维护和修改起来就会很方便。提高程序的可读性是以良好的程序设计风格来体现的。所谓程序风格是指不同的程序员,在其编写的程序中,在代码文件,语句构造以及变量命名等方面所表现出的特点。具体来讲程序风格体现在以下几个方面:1.程序的模块化、结构化。如前所述,程序的模块化是按照功能划分模块的设计方法,目的是使程序的层次和作用更明确,也易于修改和调试。而程序的结构化是指按照结构化方法进行程序设计。个人收集整理仅供参考学习6结构化程序应具有以下特点:(1)全部程序均由上节所讲述的三种基本结构组成,不包含其它类型的结构;(2)只有一个入口和一个出口;(3)程序的执行是有限的,无死循环;(4)无死语句,即每条语句均有被执行的机会。由上可见,结构化程序实际上是由许多相对完整独立的程序段构成,每一段均只有一个入口和一个出口。条理清楚,层次分明,即使程序长一些,也仍然易读可靠,容易验证,而且修改某一段一般不会影响其它段。2.程序内部的文档。程序内部的文档通常包括以下几方面:(1)必要的注解。在程序的关键部分,程序、过程、函数和段落的开头,一些公式之前等处,应加上简明扼要的注解和说明语句。这些说明性语句应能提供某些对理解程序有用的东西,而不仅仅是代码的语言解释。请看下面的一个注解:for(i=0;inElem;i++)//i从o到nElem-1循环{…}该注解只是把语句解释了一下,丝毫没有增加有利于读者理解的新信息,因此,它是多余的。换成下面的一种写法才是有意义的:for(i=0;inElem;i++)//按单元循环{…}(2)正确地命名和使用标记符。这体现在正确地使用变量的类型和变量名两点上,个人收集整理仅供参考学习7要充分利用程序设计语言本身提供的特点,根据不同需要,分别选用整、实、字符、枚举、结构(或记录)和数组等各种变量类型。例如,为了方便地表示一周中的七天,可以这样定义一个枚举类型变量:enumDay{Sunday,Monday,Tuesday,Wednesday,Tursday,Friday,Saturday};这使人看起来很习惯,也很自然。又如平面刚架的节点信息,可以选用结构体这种数据类型,用如下方式定义:structNode{doubledX;//横坐标doubledY;//纵坐标intiaDOFIndex[3];//节点的三个自由度编号};以上两类问题如果仅用整、实型变量表示就不直观,不容易记忆。为了使程序易读,要尽量采用容易记忆且能反映自身特征的变量名,如力可用F表示,面积用A表示,惯性矩用I表示,角度用Alpha,Beta表示等等。诸如此类的问题,要尽量采用人们熟悉的,接近自然语言的,并为有关学科广泛采用的符号来表示。在Windows程序设计中,匈牙利命名法是一个广泛采用的命名法。这种命名法是以下面两条规则为基础的:规则1:标识符的名字是以一个或者多个小写字母开头(前缀),用这些字母来指定数据类型。例如以小写字母c作前缀表示字符型,而以p作前缀表示指针类型,n或i开头表示整型等等。经常使用的前缀如下表所示。表9.1匈牙利命名法的前缀前缀数据类型a数组个人收集整理仅供参考学习8b布尔型c字符型d双精度型f单精度型i整型n整型p指针s字符串规则2:在标识符内,前缀以后就是一个或多个首字母大写的单词,这些单词清楚地指出了源代码内对象的意义和用途。例如,用nLoad表示载荷数,用sFileName表示文件名等。用匈牙利命名法命名的变量往往可以提供比较丰富的变量信息,但有时显得较罗嗦,因此本书对于某些局部变量和循环变量等并未采用以上规则。(3)充分利用分隔符。充分利用括号、空格、空白行和连接符等的作用,以便明显隔开不同的语句成分。(4)正确采用缩进规则。在程序中各种嵌套之处采用适当的缩进表示。如内层语句较外层语句缩进若干空格;不同的函数段与函数段之间用空行或注解行隔开等,都可增强程序的层次感和逻辑