LibSVM-2.8程序代码注释

LibSVM-2.8程序代码注释
LibSVM-2.8程序代码注释

LibSVM-2.8程序代码注释

刘国平(ahphone@https://www.360docs.net/doc/3216637352.html,)

2005-08-06

我不经心地,服下你调好的毒

我知道今后我将万劫不复

但是你的红唇仍让我屈服

四月的樱花火红满天

我和你的梦,却要一以何处去缱绻?虽然人间的情爱万万千千

世上已有太多崩毁的誓言

七个黑夜,七个白天

我为你写下的歌,彩绘的纸笺

却只能随着晚风

飘在大海的岸边

我仍愿服下你精心为我调好的毒

从你那深情的吻

吞下我与你在人间

最后的流光万千辗转朱颜……

第一章LibSVM结构

一、文件结构

整个LibSVM由两个文件组成,svm.h, svm.cpp。其中svm.h中定义了使用LibSVM所需要的数据结构和函数。

数据结构:

struct svm_node:数据节点,每个节点只是单个样本矢量中的一个特征。

struct svm_problem :数据集,存放所有样本矢量的数据结构。

struct svm_parameter:SVM参数。

其实应该还有一个数据结构存放在头文件中:

struct svm_model :训练好的训练模型。

二、类结构图

其中有两组关键的类:

1、QMatrix类:包括QMatrix, Kernel, SVC_Q, SVR_Q, ONE_CLASS_Q;

2、Solver类:包括Solver, Solver_NU;

(矢量图,可以调整放大倍数)

第二章: 头文件SVM.h

本文件只是定义了若干个结构体,和若干接口函数。严格的来说,它们并不是接口函数,因为实际上整个代码里面,可以直接访问的函数还有其他。但事实上,如果仅仅是应用一下这份代码的话,只要知道这几个函数就可以了。

2.1 struct svm_node

struct svm_node { int index ; double value ;

};

struct svm_node 用来存储单一向量中的单个特征,例如: 向量 x1={ 0.002, 0.345, 4, 5.677};

那么用struct svm_node 来存储时就使用一个包含5个svm_node 的数组来存储此4维向量,内存映象如下:

其中如果value 为0.00,该特征将不会被存储,其中(特征3)被跳过:

0.00不保留的好处在于,做点乘)(y x 的时候,可以加快计算速度,对于稀疏矩阵,更能充分体现这种数据结构的优势。但做归一化时,操作就比较麻烦了。(类型转换不再说明)

2.2 struct svm_problem

struct svm_problem { int l ; double *y ;

struct svm_node **x ;

};

struct svm_problem 存储本次参加运算的所有样本(数据集),及其所属类别。在某些数据挖掘实现中,常用DataSet 来实现。 int l ;记录样本总数

double *y ;指向样本所属类别或者回归值的数组。在多类问题中,因为使用了one-agianst-one 方法,可能原始样本中y[i]的内容是1.0,2.0,3.0,…,也就是属于类别1,2,3,但但参与多类计算时,参加分类的两类所对应的y[i]内容是+1,和-1。 Struct svm_node **x ;指向一个存储内容为指针的数组;

如下图,最右边的四个长条格同上表,存储三维数据。(黑边框的是最主要的部分)

这样的数据结构有一个直接的好处,可以用x[i][j]来访问其中的某一元素(如果value 为0.00的也全部保留的话)。

私下认为其中有一个败笔,就是把svm_node* x_space 放到结构外面去了。

2.3 enums

enum { C_SVC , NU_SVC , ONE_CLASS , EPSILON_SVR , NU_SVR }; /* svm_type */ enum { LINEAR , POLY , RBF , SIGMOID }; /* kernel_type */

支持向量机类型以及若干文献: C-SVC :

C.J.C. Burges. A tutorial on support vector machines for pattern recognition. Data Mining and Knowledge Discovery , 2(2):955-974, 1998.

NU_SVC : (待补充)

2.4 struct svm_parameter

struct svm_parameter { int svm_type ;//SVM 类型,见前enum int kernel_type ;//核函数 double degree ; /* for poly */

double gamma ;

/* for poly/rbf/sigmoid */

double coef0; /* for poly/sigmoid */

/* these are for training only */ double cache_size ; /* in MB */ double eps ; /* stopping criteria */ double C ;

/* for C_SVC, EPSILON_SVR and NU_SVR */

int nr_weight

;

/* for C_SVC */

int *weight_label ; /* for C_SVC */ double * weight ; /* for C_SVC */

double nu ;

/* for NU_SVC, ONE_CLASS, and NU_SVR */

double p; /* for EPSILON_SVR */

int shrinking; /* use the shrinking heuristics */

int probability; /* do probability estimates */

};

部分参数解释,(附核函数)

double degree;//就是2式中的d

double gamma; //就是2,3,4式中的gamma

2.5 struct struct svm_model

struct svm_model

{

svm_parameter param; // parameter

int nr_class; // number of classes, = 2 in regression/one class svm

int l; // total #SV

svm_node **SV; // SVs (SV[l])

double **sv_coef; // coefficients for SVs in decision functions (sv_coef[n-1][l]) double *rho; // constants in decision functions (rho[n*(n-1)/2])

double *probA; // pariwise probability information

double *probB;

// for classification only

int *label; // label of each class (label[n])

int *nSV; // number of SVs for each class (nSV[n])

// nSV[0] + nSV[1] + ... + nSV[n-1] = l

// XXX

int free_sv; // 1 if svm_model is created by svm_load_model

// 0 if svm_model is created by svm_train

};

结构体svm_model用于保存训练后的训练模型,当然原来的训练参数也必须保留。本来这个结构体应该是在svm.cpp中,为统一起见,一起描述。

2.6 接口函数

//以下接口函数设计得非常合理,最后一节详细说明

//最主要的驱动函数,训练数据

struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param);

//用SVM做交叉验证

void svm_cross_validation(const struct svm_problem *prob, const struct svm_parameter

*param, int nr_fold, double *target);

//保存训练好的模型到文件

int svm_save_model(const char *model_file_name, const struct svm_model *model);

//从文件中把训练好的模型读到内存中

struct svm_model *svm_load_model(const char *model_file_name);

//得到数据集的SVM类型(必须经过训练得到模型后才可以用)

int svm_get_svm_type(const struct svm_model *model);

//得到数据集的类别数(必须经过训练得到模型后才可以用)

int svm_get_nr_class(const struct svm_model *model);

//得到数据集的类别标号(必须经过训练得到模型后才可以用)

void svm_get_labels(const struct svm_model *model, int *label);

//LibSvm2.6新增函数

double svm_get_svr_probability(const struct svm_model *model);

//用训练好的模型预报样本的值,输出结果保留到数组中。(并非接口函数)

void svm_predict_values(const struct svm_model *model, const struct svm_node *x, double* dec_values);

//预报某一样本的值

double svm_predict(const struct svm_model *model, const struct svm_node *x);

// LibSvm2.6新增函数

double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates);

//消除训练的模型,释放资源

void svm_destroy_model(struct svm_model *model);

// LibSvm2.6新增函数

void svm_destroy_param(struct svm_parameter *param);

//检查输入的参数,保证后面的训练能正常进行。

const char *svm_check_parameter(const struct svm_problem *prob, const struct

svm_parameter *param);

// LibSvm2.6新增函数

int svm_check_probability_model(const struct svm_model *model);

第三章:SVM.cpp文件

3.1 宏

.头文件:

从整个.cpp文件来看,感觉有些头文件是多余的,不知何故,反正多包含头文件不会犯错。

后面的typedef,特别是typedef float Qfloat,是为了方便控制内存存储的精度。

#include

#include

#include

#include

#include

#include

#include

#include "svm.h"

typedef float Qfloat;

typedef signed char schar;

//.以下是定义的几个主要的模板,主要是为了比较大小,交换数据和完全复制数据。

Min()和Max()在中提供了相应的函数,这里的处理是为了兼容Microsoft VC++编译器。因为VC对标准模板库(STL)支持得不是很好。下面对min, max的处理方法非常经典。

#ifndef min

template inline T min(T x,T y) { return (x

#endif

#ifndef max

template inline T max(T x,T y) { return (x>y)?x:y; }

#endif

//这里的克隆函数是完全克隆,不同于一般的复制。操作结束后,内部的所有数据和指针完全一样。

template inline void swap(T& x, T& y) { T t=x; x=y; y=t; }

template inline void clone(T*& dst, S* src, int n)

{

dst = new T[n];

memcpy((void *)dst,(void *)src,sizeof(T)*n);

}

//这里使用了define,非内联函数

#define Malloc(type,n) (type *)malloc((n)*sizeof(type))

一般来说,在cpp中使用delete比较正规,在c中使用malloc比较正规。LibSVM比较好地贯彻了这点,malloc用在接口函数中。

//以下的函数用作调试。跳过~ #if 1

void info (char *fmt ,...) { va_list ap ; va_start (ap ,fmt ); vprintf (fmt ,ap ); va_end (ap );

}

void info_flush () { fflush (stdout ); }

#elsevoid info (char *fmt ,...) {} void info_flush () {} #endif

//以下部分为svm.cpp 中的类继承和组合图: (实线表示继承关系,虚线表示组合关系),更正规的图例见第一章。

3.2 类Cache

本类主要负责运算所涉及的内存的管理,包括申请、释放等。本类对理解核心代码关系不

大,如果弄不清楚影响也不大。可以跳过。 类定义: class Cache { public : Cache (int l ,int size ); ~Cache ();

int get_data (const int

index , Qfloat **data , int len ); void swap_index (int i , int j ); // future_option private :

int l ;

int size ; struct head_t { head_t *prev , *next ; // a cicular list Qfloat *data ; int len ;

// data[0,len) is cached in this entry

};

head_t * head ; head_t lru_head ;

void lru_delete (head_t *h ); void lru_insert (head_t *h );

};

成员变量:

head_t * head ; //变量指针,该指针用来记录程序所申请的内存,单块申请到的内存用struct head_t 来记录所申请内存的指针,并记录长度。而且通过双向的指针,形成链表,增加寻址的速度。记录所有申请到的内存,一方面便于释放内存,另外方便在内存不够时适当释放一部分已经申请到的内存。

head_t lru_head ; //双向链表的头。 int l ; //样本总数。

int size ; //所指定的全部内存,据说用Mb 做单位。

成员函数:

void lru_delete (head_t *h ); //从双向环形链表中删除某个元素的链接,不删除、不释放该元素所涉及的内存。一般是删除当前所指向的元素。

void lru_insert (head_t *h ); //在链表后面插入一个新的链接;其实就是在lru_head 后添加。(环形的嘛)

Cache (int l ,int size );

构造函数。该函数根据样本数L ,申请

L 个head_t 的空间。根据说明,该区域会初始化为0。Lru_head 因为尚没有head_t 中申请到内存,故双向链表指向自己。至于size 的处理,先将原来的byte 数目转化为float 的数目,然后扣除L 个head_t 的内存数目。size 为程序指定的内存大小4M/40M 。size 不要设得太小。

int get_data (const int index , Qfloat **data , int len );

该函数保证head_t[index]中至少有len 个float 的内存,并且将可以使用的内存块的指针放

在data 指针中。返回值为申请到的内存。

函数首先将head_t[index]从链表中断开,如果head_t[index]原来没有分配内存,则跳过断开这步。计算当前head_t[index]已经申请到的内存,如果不够,释放部分内存(怀疑这样做的动机:老数据为什么就可以释放,而不真的另外申请一块?老数据没用了?),等内存足够后,重新分配内存。重新使head_t[index]进入双向链表。并返回申请到的内存的长度。

//返回值不为申请到的内存的长度,为head_t[index]原来的数据长度h->len 。 调用该函数后,程序会计算),(j

i

j

i

x x K y y Q ∑=

的值,并将其填入data 所指向的内存区

域,如果下次index 不变,正常情况下,不用重新计算该区域的值。若index 不变,则get_data()返回值len 与本次传入的len 一致,从Kernel::get_Q( )中可以看到,程序不会重新计算。从而提高运算速度。

While 循环内的部分基本上难得用到一次。

void swap_index (int i , int j );

交换head_t[i] 和head_t[j]的内容,先从双向链表中断开,交换后重新进入双向链表中。对后面的处理不理解,可能是防止中head_t[i] 和head_t[j]可能有一方并未申请内存。但h ->len > i 和 h ->len > j 无法解释。

for (head_t *h = lru_head .next ; h !=&lru_head ; h =h ->next ) { if (h ->len > i ) { if (h ->len > j ) swap (h ->data [i ],h ->data [j ]);

else { // give up lru_delete (h ); free (h ->data ); size += h ->len ; h ->data = 0; h ->len = 0;

}

}

}

后注:这部分内容我几次读过,都不太清楚,如果有谁弄懂了,请告知。

3.3 类QMatrix

class QMatrix { public:

virtual Qfloat *get_Q(int column, int len) const = 0; virtual Qfloat *get_QD() const = 0;

virtual void swap_index(int i, int j) const = 0;

};

纯虚类。理论上用户只要能在需要的时候,提供必要的数据,就可以实现其它的变形支持向量机。用户可以自己实现数据矩阵(很大的哦),然后直接提供给Solver。

3.4 类Kernel

class Kernel {

public:

Kernel(int l, svm_node * const * x, const svm_parameter& param);

virtual ~Kernel();

static double k_function(const svm_node *x, const svm_node *y, const svm_parameter& param);

virtual Qfloat *get_Q(int column, int len) const = 0;

virtual void swap_index(int i, int j) const// no so const...

{

swap(x[i],x[j]);

if(x_square) swap(x_square[i],x_square[j]);

}

protected:

double (Kernel::*kernel_function)(int i, int j) const;

private:

const svm_node **x;

double *x_square;

// svm_parameter

const int kernel_type;

const double degree;

const double gamma;

const double coef0;

static double dot(const svm_node *px, const svm_node *py);

double kernel_linear(int i, int j) const(skipped)

double kernel_poly(int i, int j) const(skipped)

double kernel_rbf(int i, int j) const(skipped)

double kernel_sigmoid(int i, int j) const(skipped)

};

成员变量:

const svm_node **x; //用来指向样本数据,每次数据传入时通过克隆函数来实现,完全重新分配内存,主要是为处理多类着想。

double *x_square; //使用RBF核才使用。

const int kernel_type; //核函数类型.

const double degree; // kernel_function

const double gamma ; // kernel_function const double coef0; // kernel_function 成员函数:

Kernel (int l , svm_node * const * x , const svm_parameter & param );

构造函数。初始化类中的部分常量、指定核函数、克隆样本数据。如果使用RBF 核函数,则计算x-sqare[i].

static double dot (const svm_node *px , const svm_node *py );

点乘两个样本数据,按svm_node 中index (一般为特征)进行运算,一般来说,index 中1,2,…直到-1。返回点乘总和。

例如:x1 = { 1,2,3} , x2 = {4, 5, 6} 总和为sum = 1*4 + 2*5 + 3*6 ;在svm_node[3]中存储index = -1时,停止计算。

static double k_function (const svm_node *x , const svm_node *y , const svm_parameter & param );

核函数。但只有在预报时才用到。

其中RBF 部分很有讲究。因为存储时,0值不保留。如果所有0值都保留,第一个while 就可以都做完了;如果第一个while 做不完,在x ,y 中任意一个出现index = -1,第一个while 就停止,剩下的代码中两个while 只会有一个工作,该循环直接把剩下的计算做完。

virtual Qfloat *get_Q (int column , int len ) const = 0; virtual Qfloat *get_QD () const = 0;

纯虚函数,将来在子类中实现。相当重要的函数。

virtual void swap_index (int i , int j )

虚函数,x[i]和x[j]中所存储指针的内容。如果x_square 不为空,则交换相应的内容。

double (Kernel ::*kernel_function )(int i , int j ) const ;

函数指针,根据相应的核函数类型,来决定所使用的函数。在计算矩阵Q 时使用。

),(j i j i x x K y y Q ∑=

1、j T i j i x x x x K =

),(

2、0,)(),(>+=γγd j T

i j i r x x x x K

3、0),exp(),(2

>--=γγj i j i x x x x K

4、)tanh(),(r x x x x K j T

i j i +=γ

3.4 类Solver

class Solver {

public:

Solver() {};

virtual ~Solver() {};

struct SolutionInfo {

double obj;

double rho;

double upper_bound_p;

double upper_bound_n;

double r; // for Solver_NU

};

void Solve(int l, const Kernel& Q, const double *b_, const schar *y_, double *alpha_, double Cp, double Cn, double eps,

SolutionInfo* si, int shrinking);

protected:

int active_size;

schar *y;

double *G; // gradient of objective function

enum { LOWER_BOUND, UPPER_BOUND, FREE };

char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE

double *alpha;

const Kernel *Q;

double eps;

double Cp,Cn;

double *b;

int *active_set;

double *G_bar; // gradient, if we treat free variables as 0

int l;

bool unshrinked; // XXX

double get_C(int i) { }

void update_alpha_status(int i) { }

bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } bool is_free(int i) { return alpha_status[i] == FREE; }

void swap_index(int i, int j);

void reconstruct_gradient();

virtual int select_working_set(int &i, int &j);

virtual double calculate_rho();

virtual void do_shrinking ();

};

成员变量:

int active_size ; // 计算时实际参加运算的样本数目,经过shrink 处理后,该数目会小于全部样本总数。

schar *y ; //样本所属类别,该值只取+1/-1 。虽然可以处理多类,最终是用两类SVM 完成的。

double *G; //梯度,计算公式如下(公式3.5)[1]:

t t t G f p Q =?=+)()(αα

在代码实现中,用b[i]来代替公式中的p 。

char *alpha_status ; //][i α的状态,根据情况分为 0][≤i α, c i ≥][α和0][0<

double *alpha ; //i α

const Kernel *Q ; //指定核。核函数和Solver 相互结合,可以产生多种SVC,SVR double eps ; //误差限

double *b ; //见double *G 的说明。 int *active_set ; // double *G_bar ;

//

G -

,(这名字取的)。计算公式如下:

l i Q C G C

ij j ,...,1,==∑=-

α

该值可以在对样本集做shrink 时,减小重建梯度的计算量。

∑∑<<==+

=C

l

j j ij ij Q j Q G G α

αα01

_

int l ; //样本总数 bool unshrinked ; // 成员函数:

double get_C (int i )

返回对应于样本的C 。 设置不同的Cp 和Cn 是为了处理数据的不平衡。见 《 6 Unbalanced data 》[1],有时Cp=Cn 。

void swap_index (int i , int j );

完全交换样本i 和样本j 的内容,包括所申请的内存的地址。

void reconstruct_gradient ();

重新计算梯度。G_bar[i]在初始化时并未加入b[i],所以程序首先增加b[i]。Shrink 后依然参加运算的样本位于active_size 和L-1位置上。在0~active_size 之间的alpha[i]如果在区间(0,c)

上,才有必要更新相应的active_size 和L-1位置上的样本的梯度。

virtual int select_working_set (int &i , int &j ) 选择工作集。公式如下:

}

0,1|)({},,1|)(min({arg }0,1|)({},,1|)(max({arg >=-?<-=?≡>-=?<=-?≡t t t t t t t t t t t t y f C y f j y f C y f i αααααααα

virtual void do_shrinking ();

对样本集做缩减。大致是当C <<α0时,(还有两种情况)程序认为该样本可以不参加下次迭代。(C <<α0时,为内部点)程序会减小active_size ,为(内部点)增加位置。active_size 表明了不可以参加下次迭代的样本的最小标号,在active_size 与L 之间的元素都对分类没有贡献。

程序中k--是为了消除交换后的影响,使重新换来的样本也被检查一次。

如果程序在缩减一次后没有达到结束条件,就重新构造梯度矢量,并再缩减一次(总觉得这里不太严密)。

virtual double calculate_rho ();

计算ρ值。见3.7[1]节,The calculation of b or

ρ

∑=<<=<

1

,01

,01

1

)(yi C yi C i

f r ααα

2

2

1r r +=

ρ

void Solve (int l , const Kernel & Q , const double *b_, const schar *y_,

double *alpha_, double Cp , double Cn , double eps , SolutionInfo * si , int shrinking );

//程序较大,逐步分解 part 1

// initialize alpha_status { alpha_status = new char [l ]; for (int i =0;i

update_alpha_status (i );

}

更新一下alpha 的状态 part 2

// initialize active set (for shrinking) {

active_set = new int [l ];

for (int i =0;i

active_set [i ] = i ; active_size = l ;

}

为缩减做准备,将来要做交换 part 3 // initialize gradient { G = new double [l ]; G_bar = new double [l ]; int i ;

for (i =0;i

}

for (i =0;i

for (j =0;j

G_bar [j ] += get_C (i ) * Q_i [j ];

}

}

G_bar [j ]的生成公式如下:(注意,其中不包含b[i]的值)

l i Q C G C

ij j ,...,1,==∑=-

α

因为第一次建立G(i),所以没有判断alpha 的状态。而是按公式,全部计算了一遍。 get_Q (i ,l )返回的值是ij Q 矩阵中的第i 列,而不是第i 行,这是需要注意的地方。

再往下是大循环:

如果有必要,先进行筛选,使部分数据不再参加运算;选择工作集;更新alpha_i, alpha_j,其更新的思路是保证:j old j i old i j new j i new

i y y y y αααα+=+;对于边界情况,有特殊处理,主要是考

虑i i

C ≤≤α0的要求。当某一alpha 小于0时,做适当调整,调整的结果是alpha_i, alpha_j 仍然在

i i C ≤≤α0范围内,同时其和同原来一样。对于推导过程,可以参考Sequential Minimal

Optimization for SVM

part 4

更新G(i),根据j i αα,的变化更新; // update G double delta_alpha_i = alpha [i ] - old_alpha_i ; double delta_alpha_j = alpha [j ] - old_alpha_j ;

for (int k =0;k

} part 5

以下是更新alpha_status 和_

G ,ahpha 状态更新较简单,根据alpha 状态前后是否有变化,适当更新,更新的内容参考公式l i Q C G C

ij j ,...,1,==∑=-α

// update alpha_status and G_bar { bool ui = is_upper_bound (i ); bool uj = is_upper_bound (j ); update_alpha_status (i ); update_alpha_status (j ); int k ;

if (ui != is_upper_bound (i ))//更新alpha_i 的影响 { Q_i = Q .get_Q (i ,l ); if (ui ) for (k =0;k

G_bar [k ] -= C_i * Q_i [k ];

else for (k =0;k

G_bar [k ] += C_i * Q_i [k ]; }

if (uj != is_upper_bound (j )) //更新alpha_j 的影响 { Q_j = Q .get_Q (j ,l ); if (uj )

for (k =0;k

G_bar [k ] -= C_j * Q_j [k ];

else for (k =0;k

G_bar [k ] += C_j * Q_j [k ];

}

}

part 6

以下计算目标函数值,因为t t p Q G )(+=α,而目标值为αααT T p Q +2

1

,故:

// calculate objective value { double v = 0; int i ;

for (i =0;i

v += alpha [i ] * (G [i ] + b [i ]);

si ->obj = v /2;

} part 7 回送结果。 // put back the solution { for (int i =0;i

alpha_[active_set [i ]] = alpha [i ];

}

2.3 类Solver_NU

class Solver_NU : public Solver { public : Solver_NU () {}

void Solve (int l , const Kernel & Q , const double *b , const schar *y , double *alpha , double Cp , double Cn , double eps , SolutionInfo * si , int shrinking ) { this ->si = si ;

Solver ::Solve (l ,Q ,b ,y ,alpha ,Cp ,Cn ,eps ,si ,shrinking ); }

private :

SolutionInfo *si ;

int select_working_set (int &i , int &j );

double calculate_rho ();

程序代码注释编写规范

程序代码注释编写规范 为提高控制程序的阅读性与可理解性,现制定相关代码程序代码注释编写的编写规范。 一般情况下,源程序有效注释量必须在20%以上,注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。 常规注释有以下两种方式。 单行:以"//"符号开始,任何位于该符号之后的本行文字都视为注释。 多行:以"/*"符号开始,以"*/"结束。任何介于这对符号之间的文字都视为注释。 一、说明性文件 说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。 示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************* COPYRIGHT (C), MicTiVo International. Co., Ltd. File NAME: // 文件 Author: Version: Date: // 作者、版本及完成日期 DESCRIPTION: // 用于详细说明此程序文件完成的主要功能,与其他模块 // 或函数的接口,输出值、取值范围、含义及参数间的控 // 制、顺序、独立或依赖等关系 Others: // 其它内容的说明 Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明 1.... History: // 修改历史记录列表,每条修改记录应包括修改日期、修改 // 者及修改内容简述 1. Date: Author: Modification: 2. .. *************************************************/ 二、源文件头 源文件头部应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。 示例:下面这段源文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************************ COPYRIGHT (C), MicTiVo International. Co., Ltd. FileName: Author:

UMAT全过程

UMAT全过程——技术篇 1.ABAQUS中非线性问题的处理2.用户子程序接口3.用户子程序和主程序的结合4.用户材料子程序UMAT接口的原理5.UMAT子程序流程ABAQUS是怎么计算的 I.ABAQUS一共有42个用户子程序接口,15个应用程序接口,可以定义包括边界条 件,荷载条件,接触条件,材料特性以及利用用户子程序和其它应用软件进行数值交换。 1.根据ABAQUS提供的相应接口,按照FORTRAN语法自己编写的代码,是一个独立的程序单元,可以独立地被储存和编译,也能被其他程序单元引用。 I.一般结构形式 II. 一个算例中,可以用到多个用户子程序,但必须把它们放在一个以.for为扩展名的文件中。 III.运行带有用户子程序的算例的两种方法 1.在CAE中运行,在EDIT JOB菜单中的GENRAL子菜单的USERSUBROUTINE GILE对话框中选择用户子程序所在的文件 2.在https://www.360docs.net/doc/3216637352.html,MAND中运行语法如下 IV.编制用户子程序时应注意: 1.用户子程序相互之间不能调用,可以调用用户自己编写的Fortran子程序和 ABAQUS应用程序,ABAQUS应用程序必须由用户子程序调用。编写Fortran子程序时,建议子程序以K开头,以免和ABAQUS内部程序冲突。2.用户在用户子程序中利用OPEN打开外部文件时,要注意以下两点: (1)设备号的选择有限制,只能取15~18和大于100的设备号 (2)用户需提供外部文件的绝对路径而不是相对路径。3.对于不同的用户子程序,ABAQUS调用的时间不相同,有的在每个STEP的开始,有的在结尾,有的在每个 INCREMENT的开始。(当ABAQUS在调用用户子程序时,都会把当前的STEP 和INCREMENT 利用用户子程序的两个实参KSTEP 和KINC 传给用户子程序,用户可把他们输出到外部文件中,这样可清楚知道何时调用) V.ABAQUS提供给用户定义自己的材料属性的Fortran程序接口,用户材料子程序 UMAT 通过与ABAQUS主求解程序的接口实现与ABAQUS的资料交流,输入文件中,使用“UESER MATERIAL”表示定义用户材料属性。 I.UMAT子程序采用Fortran语言编制,包括以下几个部分:子程序定义语句、 ABAQUS 定义的参数说明、用户定义的局部变量说明、用户编制的程序主体、子程序返回和结束语句。I.

基于内点法的最优潮流计算

基于内点法的最优潮流计 算 Prepared on 24 November 2020

摘要 内点法是一种能在可行域内部寻优的方法,即从初始内点出发,沿着中心路径方向在可行域内部直接走向最优解的方法。其中路径跟踪法是目前最具有发展潜力的一类内点算法,该方法鲁棒性强,对初值的选择不敏感,在目前电力系统优化问题中得到了广泛的应用。本文采用路径跟踪法进行最优求解,首先介绍了路径跟踪法的基本模型,并且结合具体算例,用编写的Matlab程序进行仿真分析,验证了该方法在最优潮流计算中的优越性能。 关键词:最优潮流、内点法、路径跟踪法、仿真

目次

0、引言 电力系统最优潮流,简称OPF(Optimal Power Flow)。OPF问题是一个复杂的非线性规划问题,要求满足待定的电力系统运行和安全约束条件下,通过调整系统中可利用控制手段实现预定目标最优的系统稳定运行状态。针对不同的应用,OPF模型课以选择不同的控制变量、状态变量集合,不同的目标函数,以及不同的约束条件,其数学模型可描述为确定一组最优控制变量u,以使目标函数取极小值,并且满足如下等式和不等式。 {min u f(x,u) S.t.?(x,u)=0 g(x,u)≤0 (0-1)其中min u f(x,u)为优化的目标函数,可以表示系统运行成本最小、或者系统运行网损最小。S.t.?(x,u)=0为等式约束,表示满足系统稳定运行的功率平衡。g(x,u)≤0为不等式约束,表示电源有功出力的上下界约束、节点电压上下线约束、线路传输功率上下线约束等等。 电力系统最优潮流算法大致可以分为两类:经典算法和智能算法。其中经典算法主要是指以简化梯度法、牛顿法、内点法和解耦法为代表的基于线性规划和非线性规划以及解耦原则的算法,是研究最多的最优潮流算法, 这类算法的特点是以一阶或二阶梯度作为寻找最优解的主要信息。智能算法主要是指遗传算法和模拟退火发等,这类算法的特点是不以梯度作为寻优信息,属于非导数的优化方法。 因此经典算法的优点是能按目标函数的导数信息确定搜索方向,计算速度快,算法比较成熟,结果可信度高。缺点是对目标函数及约束条件有一定的限

华为JAVA编程规范

1 Java 编程规范 1.1 排版 1.1.1 规则 规则1程序块要采用缩进风格编写,缩进的空格数为4个,不允许使用TAB缩进。(1.42+) 说明:缩进使程序更易阅读,使用空格缩进可以适应不同操作系统与不同开发工具。 规则2分界符(如大括号…{?和…}?)应各独占一行,同时与引用它们的语句左对齐。在函数体的开始、类和接口的定义、以及if、for、do、while、switch、case语句中的程序 或者static、,synchronized等语句块中都要采用如上的缩进方式。(1.42+) 示例: if (a>b) { doStart(); } 规则3较长的语句、表达式或参数(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐, 语句可读。(1.42+) 示例: if (logger.isDebugEnabled()) { logger.debug("Session destroyed,call-id" + event.getSession().getCallId()); } 规则4不允许把多个短语句写在一行中,即一行只写一条语句(1.42+) 说明:阅读代码更加清晰 示例:如下例子不符合规范。 Object o = new Object(); Object b = null; 规则5if, for, do, while, case, switch, default 等语句自占一行,且if, for, do, while,switch等语句的执行语句无论多少都要加括号{},case 的执行语句中如果定义变量必须加括号{}。 (1.42+) 说明:阅读代码更加清晰,减少错误产生 示例: if (a>b) { doStart(); }

ABAQUS子程序UMAT里弹塑本构的实现

前言 有限元法是工程中广泛使用的一种数值计算方法。它是力学、计算方法和计算机技术相结合的产物。在工程应用中,有限元法比其它数值分析方法更流行的一个重要原因在于:相对与其它数值分析方法,有限元法对边界的模拟更灵活,近似程度更高。所以,伴随着有限元理论以及计算机技术的发展,大有限元软件的应用证变得越来越普及。 ABAQUS软件一直以非线性有限元分析软件而闻名,这也是它和ANSYS,Nastran等软件的区别所在。非线性有限元分析的用处越来越大,因为在所用材料非常复杂很多情况下,用线性分析来近似已不再有效。比方说,一个复合材料就不能用传统的线性分析软件包进行分析。任何与时间有关联,有较大位移量的情况都不能用线性分析法来处理。多年前,虽然非线性分析能更适合、更准确的处理问题,但是由于当时计算设备的能力不够强大、非线性分析软件包线性分析功能不够健全,所以通常采用线性处理的方法。 这种情况已经得到了极大的改善,计算设备的能力变得更加强大、类似ABAQUS这样的产品功能日臻完善,应用日益广泛。 非线性有限元分析在各个制造行业得到了广泛应用,有不少大型用户。航空航天业一直是非线性有限元分析的大客户,一个重要原因是大量使用复合材料。新一代波音 787客机将全部采用复合材料。只有像 ABAQUS这样的软件,才能分析包括多个子系统的产品耐久性能。在汽车业,用线性有限元分析来做四轮耐久性分析不可能得到足够准确的结果。分析汽车的整体和各个子系统的性能要求(如悬挂系统等)需要进行非线性分析。在土木工程业, ABAQUS能处理包括混凝土静动力开裂分析以及沥青混凝土方面的静动力分析,还能处理高度复杂非线性材料的损伤和断裂问题,这对于大型桥梁结构,高层建筑的结构分析非常有效。 瞬态、大变形、高级材料的碰撞问题必须用非线性有限元分析来计算。线性分析在这种情况下是不适用的。以往有一些专门的软件来分析碰撞问题,但现在ABAQUS在通用有限元软件包就能解决这些问题。所以,ABAQUS可以在一个软件完成线性和非线性分析。 ABAQUS给用户提供了强大二次开发接口,尤其是在材料本构方面,给用户开发符合实际工程的材料本构模型提供了强大帮助,本文将针对其用户材料子程序展开研究,总结常用材料模型的开发方法。

数据结构_实验1_线性表的基本操作

实验1 线性表的基本操作 一、需求分析 目的: 掌握线性表运算与存储概念,并对线性表进行基本操作。 1.初始化线性表; 2.向链表中特定位置插入数据; 3.删除链表中特定的数据; 4.查找链表中的容; 5.销毁单链表释放空间; 二、概要设计 ●基础题 主要函数: 初始化线性表InitList(List* L,int ms) 向顺序表指定位置插入元素InsertList(List* L,int item,int rc)删除指定元素值的顺序表记录DeleteList1(List* L,int item) 删除指定位置的顺序表记录 DeleteList2(List* L,int rc) 查找顺序表中的元素 FindList(List L,int item) 输出顺序表元素OutputList(List L) 实验步骤: 1,初始化顺序表 2,调用插入函数 3,在顺序表中查找指定的元素 4,在顺序表中删除指定的元素 5,在顺序表中删除指定位置的元素 6,遍历并输出顺序表 ●提高题

要求以较高的效率实现删除线性表中元素值在x到y(x和y自定义)之间的所有元素 方法: 按顺序取出元素并与x、y比较,若小于x且大于y,则存进新表中。 编程实现将两个有序的线性表进行合并,要求同样的数据元素只出现一次。 方法: 分别按顺序取出L1,L2的元素并进行比较,若相等则将L1元素放进L中,否则将L 1,L2元素按顺序放进L。 本程序主要包含7个函数 主函数main() 初始化线性表InitList(List* L,int ms) 向顺序表指定位置插入元素InsertList(List* L,int item,int rc)删除指定元素值的顺序表记录DeleteList1(List* L,int item) 删除指定位置的顺序表记录 DeleteList2(List* L,int rc) 查找顺序表中的元素 FindList(List L,int item) 输出顺序表元素OutputList(List L) 提高题的程序 void Combine(List* L1,List* L2,List* L) void DeleteList3(List* L,int x,int y) 二、详细设计 初始化线性表InitList(List* L,int ms) void InitList(List* L,int ms) { L->list=(int*)malloc(LIST_INIT_SIZE*sizeof(int)); L->size=0; L->MAXSIZE=LIST_INIT_SIZE;

程序代码编写规范

程序编写规范及约定 (仅供内部使用) 文档作者:_______________ 日期:___/___/___ 开发/测试经理:_______________ 日期:___/___/___ 项目经理:_______________ 日期:___/___/___ 请在这里输入公司名称 版权所有不得复制

目录 程序编写规范及约定 (3) 1编写目的 (3) 2代码编写风格 (3) 2.1单元风格 (3) 2.2语句风格 (3) 3命名规则 (3) 3.1命名约定 (3) 3.1.1标志符 (3) 3.1.2类class (3) 3.1.3枚举类型enum (4) 3.1.4委托delegate (4) 3.1.5常量const (4) 3.1.6接口interface (4) 3.1.7方法function (4) 3.1.8命名空间namespace (4) 3.1.9参数 (4) 3.1.10局部变量 (5) 3.1.11数据成员 (5) 3.1.12自定义异常类 (5) 3.1.13命名缩写 (5) 3.1.14数据库命名 (5) 3.2代码编写命名规范 (6) 3.3界面常用控件命名约定 (6) 3.4文件命名规范 (7) 3.4.1文档文件命名 (7) 3.4.2配置文件命名 (7) 3.4.3程序文件命名 (7)

程序编写规范及约定 1编写目的 为了使编写代码具有可读性、可理解性、可维护性,对程序编写人员代码实行统一风格,使得程序代码能够以名称反映含义、以形式反映结构。此文档可供程序代码编写人员及代码维护人员使用。 2代码编写风格 2.1单元风格 2.2语句风格 3命名规则 3.1命名约定 Pascal和Camel命名约定: 编程的命名方式主要有Pascal和Camel两种(Pascal:每个单词的首字母大写,例如ProductType;Camel:首个单词的首字母小写,其余单词的首字母大写,例如productType) 3.1.1标志符 规则:Pascal、Camel 实例与描述:例子说明 3.1.2类class 规则:Pascal 实例与描述:Application

abaqus简单umat子程序

SUBROUTINE UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD,RPL,DDSDDT, 1 DRPLDE,DRPLDT,STRAN,DSTRAN,TIME,DTIME,TEMP,DTEMP,PREDEF,DPRED, 2 CMNAME,NDI,NSHR,NTENS,NSTATV,PROPS,NPROPS,COORDS,DROT, 3 PNEWDT,CELENT,DFGRD0,DFGRD1,NOEL,NPT,LAYER,KSPT,KSTEP,KINC) include 'aba_param.inc' CHARACTER*8 CMNAME DIMENSION STRESS(NTENS),STATEV(NSTATV),DDSDDE(NTENS,NTENS), 1 DDSDDT(NTENS),DRPLDE(NTENS),STRAN(NTENS),DSTRAN(NTENS), 2 TIME(2),PREDEF(1),DPRED(1),PROPS(NPROPS),COORDS(3),DROT(3,3), 3 DFGRD0(3,3),DFGRD1(3,3) C UMAT FOR ISOTROPIC ELASTICITY C CANNOT BE USE D FOR PLAN E STRESS C ---------------------------------------------------------------- C PROPS(1) - E C PROPS(2) - NU C ---------------------------------------------------------------- C IF (NDI.NE.3) THEN WRITE (*,*) 'THIS UMAT MAY ONLY BE USED FOR ELEMENTS 1 WITH THREE DIRECT STRESS COMPONENTS' CALL XIT ENDIF open(400,file='D:\test.txt') C ELASTIC PROPERTIES EMOD=PROPS(1) ENU=PROPS(2) EBULK3=EMOD/(1-2*ENU) EG2=EMOD/(1+ENU) EG=EG2/2 EG3=3*EG ELAM=(EBULK3-EG2)/3 write(400,*) 'temp=',temp C ELASTIC STIFFNESS C DO K1=1, NDI DO K2=1, NDI DDSDDE(K2, K1)=ELAM END DO DDSDDE(K1, K1)=EG2+ELAM

潮流计算问答题

1.什么是潮流计算?潮流计算的主要作用有哪些? 潮流计算是根据给定的电网结构、参数和发电机、负荷等元件的运行条件,确定电力系统各部分稳态运行状态参数的计算。 对于正在运行的电力系统,通过潮流计算可以判断电网母线电压、支路电流和功率是否越限,如果有越限,就应采取措施,调整运行方式。对于正在规划的电力系统,通过潮流计算,可以为选择电网供电方案和电气设备提供依据。潮流计算还可以为继电保护和自动装置整定计算、电力系统故障计算和稳定计算等提供原始数据。 2.潮流计算有哪些待求量、已知量? (已知量: 电力系统网络结构、参数; 决定系统运行状态的边界条件 待求量:系统稳态运行状态 例如各母线上的电压(幅值及相角)、网络中的功率分布以及功率损耗等)通常给定的运行条件有系统中各电源和负荷点的功率、枢纽点电压、平衡点的电压和相位角。 待求的运行状态参量包括电网各母线节点的电压幅值和相角,以及各支路的功率分布、网络的功率损耗等。 3.潮流计算节点分成哪几类?分类根据是什么? (分成三类:PQ节点、PV节点和平衡节点,分类依据是给定变量的不同) PV节点(电压控制母线):有功功率Pi和电压幅值Ui为给定。这种类型节点相当于发电机母线节点,或者相当于一个装有调相机或静止补偿器的变电所母线。 PQ节点:注入有功功率Pi和无功功率Qi是给定的。相当于实际电力系统中的一个负荷节点,或有功和无功功率给定的发电机母线。 平衡节点:用来平衡全电网的功率。平衡节点的电压幅值Ui和相角δi是给定的,通常以它的相角为参考点,即取其电压相角为零。 一个独立的电力网中只设一个平衡节点。 4.教材牛顿-拉夫逊法及有功-无功分解法是基于何种电路方程?可否采用其它类型方程? 基于节点电压方程,还可以采用回路电流方程和割集电压方程等。但是后两者不常用。

数据结构实现顺序表的各种基本运算(20210215233821)

实现顺序表的各种基本运算 一、实验目的 了解顺序表的结构特点及有关概念,掌握顺序表的各种基本操作算法思想及其实现。 二、实验内容 编写一个程序,实现顺序表的各种基本运算: 1、初始化顺序表; 2 、顺序表的插入; 3、顺序表的输出; 4 、求顺序表的长度 5 、判断顺序表是否为空; 6 、输出顺序表的第i位置的个元素; 7 、在顺序表中查找一个给定元素在表中的位置; 8、顺序表的删除; 9 、释放顺序表 三、算法思想与算法描述简图

主函数main

四、实验步骤与算法实现 #in clude #in clude #defi ne MaxSize 50 typedef char ElemType; typedef struct {ElemType data[MaxSize]; in t le ngth; void In itList(SqList*&L)〃 初始化顺序表 L {L=(SqList*)malloc(sizeof(SqList)); L->le ngth=0; for(i=0;ile ngth;i++) prin tf("%c ",L->data[i]); } void DestroyList(SqList*&L)〃 {free(L); } int ListEmpty(SqList*L)〃 {retur n( L->le ngth==O); } int Listle ngth(SqList*L)〃 {return(L->le ngth); } void DispList(SqList*L)〃 {int i; 释放顺序表 L

程序代码注释编写规范

百度文库- 让每个人平等地提升自我 1 程序代码注释编写规范 为提高控制程序的阅读性与可理解性,现制定相关代码程序代码注释编写的编写规范。 一般情况下,源程序有效注释量必须在20%以上,注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。 常规注释有以下两种方式。 单行:以"文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。 示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************* (C), MicTiVo International. Co., Ltd. 1.File : . History: Date: Author: Modification: 2. .. *************************************************/ 一、源文件头 源文件头部应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。 示例:下面这段源文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************************ (C), MicTiVo International. Co., Ltd. FileName: Author: Version : Date: : / /*receive _process() */ 意:与溢出中断写初值不同}

ABAQUS-二次开发资料-UMAT

各个楼层及内容索引 2-------------------------------------什么是UMAT 3-------------------------------------UMAT功能简介 4-------------------------------------UMAT开始的变量声明 5-------------------------------------UMAT中各个变量的详细解释 6-------------------------------------关于沙漏和横向剪切刚度 7-------------------------------------UMAT流程和参数表格实例展示 8-------------------------------------FORTRAN语言中的接口程序Interface 9-------------------------------------关于UMAT是否可以用Fortran90编写的问题 10-17--------------------------------Fortran77的一些有用的知识简介 20-25\30-32-----------------------弹塑性力学相关知识简介 34-37--------------------------------用户材料子程序实例JOhn-cook模型压缩包下载 38-------------------------------------JOhn-cook模型本构简介图 40-------------------------------------用户材料子程序实例JOhn-cook模型完整程序+david详细注解[欢迎大家来看看,并提供意见,完全是自己的diy的,不保证完全正确,希望共同探讨,以便更正,带"?"部分,还望各位大师\同仁指教] 1什么是UMAT??? 1.1 UMAT功能简介!!![-摘自庄茁老师的书 UMAT子程序具有强大的功能,使用UMAT子程序: (1)可以定义材料的本构关系,使用ABAQUS材料库中没有包含的材料进行计算,扩充程序 功能。ABAQUS软件2003年度用户年会论文集 (2)几乎可以用于力学行为分析的任何分析过程,几乎可以把用户材料属性赋予ABAQUS中 的任何单元; (3)必须在UMAT中提供材料本构模型的雅可比(Jacobian)矩阵,即应力增量对应变增量 的变化率。 (4)可以和用户子程序“USDFLD”联合使用,通过“USDFLD”重新定义单元每一物质点上传 递到UMAT中场变量的数值。 1.2 UMAT开始的变量声明 由于主程序与UMAT之间存在数据传递,甚至共用一些变量,因此必须遵守有关书写格式,UMAT中常用的变量在文件开头予以定义,通常格式为: SUBROUTINE UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD, 1 RPL,DDSDDT,DRPLDE,DRPLDT, 2STRAN,DSTRAN,TIME,DTIME,TEMP,DTEMP,PREDEF,DPRED,CMNAME 3 NDI,NSHR,NTENS,NSTATV,PROPS,NPROPS,COORDS,DROT,PNEWDT,

潮流计算的基本算法及使用方法Word版

潮流计算的基本算法及使用方法 一、 潮流计算的基本算法 1. 牛顿-拉夫逊法 1.1 概述 牛顿-拉夫逊法是目前求解非线性方程最好的一种方法。这种方法的特点就是把对非线 性方程的求解过程变成反复对相应的线性方程求解的过程,通常称为逐次线性化过程,就是牛顿-拉夫逊法的核心。 牛顿-拉夫逊法的基本原理是在解的某一邻域内的某一初始点出发,沿着该点的一阶偏 导数——雅可比矩阵,朝减小方程的残差的方向前进一步,在新的点上再计算残差和雅可矩阵继续前进,重复这一过程直到残差达到收敛标准,即得到了非线性方程组的解。因为越靠近解,偏导数的方向越准,收敛速度也越快,所以牛顿法具有二阶收敛特性。而所谓“某一邻域”是指雅可比方向均指向解的范围,否则可能走向非线性函数的其它极值点,一般来说潮流由平电压即各母线电压(相角为0,幅值为1)启动即在此邻域内。 1.2 一般概念 对于非线性代数方程组 ()0=x f 即 ()0,,,21=n i x x x f ()n i ,2,1= (1-1) 在待求量x 的某一个初始计算值() 0x 附件,将上式展开泰勒级数并略去二阶及以上的高 阶项,得到如下的线性化的方程组 ()()()() ()0000=?'+x x f x f (1-2) 上式称之为牛顿法的修正方程式。由此可以求得第一次迭代的修正量 ()() ()[]()()0 1 00x f x f x -'-=? (1-3) 将() 0x ?和() 0x 相加,得到变量的第一次改进值()1x 。接着再从() 1x 出发,重复上述计算 过程。因此从一定的初值() 0x 出发,应用牛顿法求解的迭代格式为 ()()()()() k k k x f x x f -=?' (1-4) ()()()k k k x x x ?+=+1 (1-5) 上两式中:()x f '是函数()x f 对于变量x 的一阶偏导数矩阵,即雅可比矩阵J ;k 为迭代

程序代码注释编写规范

程序代码注释编写规范 XXX份公司

为提高控制程序的阅读性与可理解性,现制定相关代码程序代码注释编写的编写规范。 一般情况下,源程序有效注释量必须在20%以上,注释的原则是有助于对程序的阅读理解,在该加的地方都加了,注释不宜太多也不能太少,注释语言必须准确、易懂、简洁。 常规注释有以下两种方式。 单行:以"//"符号开始,任何位于该符号之后的本行文字都视为注释。 多行:以"/*"符号开始,以"*/"结束。任何介于这对符号之间的文字都视为注释。 一、说明性文件 说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明。 示例:下面这段头文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************* COPYRIGHT (C), MicTiVo International. Co., Ltd. File NAME: // 文件 Author: Version: Date: // 作者、版本及完成日期 DESCRIPTION: // 用于详细说明此程序文件完成的主要功能,与其他模块 // 或函数的接口,输出值、取值范围、含义及参数间的控 // 制、顺序、独立或依赖等关系 Others: // 其它内容的说明 Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明 1.... History: // 修改历史记录列表,每条修改记录应包括修改日期、修改 // 者及修改内容简述 1. Date: Author: Modification: 2. .. *************************************************/ 二、源文件头 源文件头部应进行注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。 示例:下面这段源文件的头注释比较标准,当然,并不局限于此格式,但上述信息建议要包含在内。 /************************************************************

顺序表的基本操作

《数据结构》实验报告一 顺序表的基本操作 班级:网络工程学号:12015242183 实验日期:2016.9.25 姓名:邓宗永 程序文件名及说明:sequenlist 顺序表 一、实验目的 1、掌握使用Turbo C3.0上机调试线性表的基本方法; 2、掌握顺序表的基本操作:插入、删除、查找以及线性表合并等运算。 二、实验要求 1、认真阅读和掌握实验的程序。 2、上机运行程序。 3、保存和打印出程序的运行结果,并结合程序进行分析。 4、按照你对线性表的操作需要,编写写主程序并运行,打印出文件清单和运行结果 三、注意事项: 在磁盘上创建一个目录,专门用于存储数据结构实验的程序。 四、实验内容 1.顺序表的查找、插入与删除。设计算法,实现线性结构上的顺序表的产生以及元素的查找、插入与删除。具体实现要求: (1)从键盘输入10个整数,产生顺序表,并输入结点值。 (2)从键盘输入1个整数,在顺序表中查找该结点的位置。若找到,输出结点的位置;若找不到,则显示“找不到”。 (3)从键盘输入2个整数,一个表示欲插入的位置i,另一个表示欲插入的数值x,将x 插入在对应位置上,输出顺序表所有结点值,观察输出结果。 (4)从键盘输入1个整数,表示欲删除结点的位置,输出顺序表所有结点值,观察输出结果。 五、实验报告必须写明内容 1.程序设计的基本思想,原理和算法描述:(包括程序的结构,数据结构,输入/输出设 计,符号名说明等) 程序的结构:通过子函数实现输出,删除,插入,查找等功能,高耦合低内聚 数据结构:线性结构,顺序储存 输入/输出设计:根据屏幕提示,从键盘读取数据 2.源程序及注释: #include #include typedef int datatype; #define maxsize 10 typedef struct //创建一个顺序表包含10个整数

C语言编写规范之注释

1、头文件包含Includes 2、私有类型定义 Private typedef 3、私有定义Private define 4、私有宏定义 Private macro 5、私有变量 Private variables 6、私有函数原型Private function prototypes 7、私有函数Private functions 8、私有函数前注释 /****************************************************************************** * * Function Name : FSMC_NOR_Init * Description : Configures the FSMC and GPIOs to interface with the NOR memory. * This function must be called before any write/read operation * on the NOR. * Input : None * Output : None * Return : None ******************************************************************************* / 9、程序块采用缩进风格编写,缩进空格为4。 10、相对独立的程序块之间、变量说明之后必须加空行; 11、较长的字符(>80字符)要分成多行书写,长表达式要在低优先级操作符划分新行,操作符放在新行之首,新行要恰当缩进,保持排版整齐; 12、循环、判断等语句中若有较长的表达式或语句,则要进行适应的划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首; 13、若函数或过程中的参数较长,则要进行适当的划分。 14、不允许把多个短语句写在一行中,即一行只写一条语句。 15、if、for、do、while、case、switch、default等语句自占一行,且if、for、 do、while等语句的执行语句部分无论多少都要加括号{}。 16、对齐只使用空格键,不使用TAB键; 17、 函数或过程的开始、结构的定义及循环、判断等语句中的代码都要采用缩进风格,case 语句下的情况处理语句也要遵从语句缩进要求 18、 程序块的分界符(如C/C++语言的大括号‘{’和‘}’)应各独占一行并且位于同一 列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构的定义、枚举的定义以 及if、for、do、while、switch、case语句中的程序都要采用如上的缩进方式。 19、 在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或

Abaqus材料用户子程序UMAT基础知识与手册例子完整解释

1、为何需要使用用户材料子程序(User-Defined Material, UMAT )? 很简单,当ABAQUS 没有提供我们需要的材料模型时。所以,在决定自己定义一种新的材料模型之前,最好对ABAQUS 已经提供的模型心中有数,并且尽量使用现有的模型,因为这些模型已经经过详细的验证,并被广泛接受。 UMAT 子程序具有强大的功能,使用UMAT 子程序: (1)可以定义材料的本构关系,使用ABAQUS 材料库中没有包含的材料进行计算,扩充程序功能。 (2) 几乎可以用于力学行为分析的任何分析过程,几乎可以把用户材料属性赋予ABAQU S 中的任何单元。 (3) 必须在UMAT 中提供材料本构模型的雅可比(Jacobian )矩阵,即应力增量对应变增量的变化率。 (4) 可以和用户子程序“USDFLD ”联合使用,通过“USDFLD ”重新定义单元每一物质点上传递到UMAT 中场变量的数值。 2、需要哪些基础知识? 先看一下ABAQUS 手册(ABAQUS Analysis User's Manual )里的一段话: Warning: The use of this option generally requires considerable expertise(一定的专业知识). The user is cautioned that the implementation (实现) of any realistic constitutive (基本) model requires extensive (广泛的) development and testing. Initial testing on a single eleme nt model with prescribed traction loading (指定拉伸载荷) is strongly recommended. 但这并不意味着非力学专业,或者力学基础知识不很丰富者就只能望洋兴叹,因为我们的任务不是开发一套完整的有限元软件,而只是提供一个描述材料力学性能的本构方程(Constitutive equation )而已。当然,最基本的一些概念和知识还是要具备的,比如: 应力(stress),应变(strain )及其分量; volumetric part 和deviatoric part ;模量(modul us )、泊松比(Poisson’s ratio)、拉梅常数(Lame constant);矩阵的加减乘除甚至求逆;还有一些高等数学知识如积分、微分等。 3、UMAT 的基本任务? 我们知道,有限元计算(增量方法)的基本问题是: 已知第n 步的结果(应力,应变等)n σ,n ε,然后给出一个应变增量1+n d ε,计算新的应力1+n σ。UMAT 要完成这一计算,并要计算Jacobian 矩阵DDSDDE(I,J) =εσΔ?Δ?/。σΔ是应力增量矩阵(张量或许更合适),εΔ是应变增量矩阵。DDSDDE(I,J) 定义了第J 个应变分量的微小变化对

顺序表的基本操作 (2)

顺序表的基本操作 /*sqList.h 文件*/ #define LIST_INIT_SIZE 50 /*初始分配的顺序表长度*/ #define INCREM 10 /*溢出时,顺序表长度的增量*/ #define OVERFLOW 1 #define OK 0 #define ERROR -1 typedef int ElemType; /*定义表元素的类型*/ typedef struct SqList{ ElemType *elem; /*存储空间的基地址*/ int length; /*顺序表的当前长度*/ int listsize; /*当前分配的存储空间*/ }SqList; /*sqListOp.h 文件*/ #include "Sqlist.h" int InitList_sq(SqList &L); //顺序表创建函数定义 void FreeList_sq(SqList &L); //顺序表销毁函数定义 int ListInsert_sq(SqList &L, int i, ElemType e); //在顺序表的位置i插入元素e void PrintList_sq(SqList &L); //遍历并输出顺序表所有元素 int ListDelete_sq(SqList &L, int i,ElemType &e); //删除顺序表第i个元素的 bool ListEmpty(SqList &L); //判断顺序表是否为空 int LocateElem_sq(SqList L,ElemType e); //在顺序表里查找出第1个与e相等的数据元素位置//已知线性表La和Lb的元素按值非递减排列 //归并后的La和Lb得到新的顺序线性表Lc,Lc的元素也是按值非递减排列 void MergeList_sq(SqList La,SqList Lb, SqList &Lc); /*sqListOp.cpp文件*/ #include #include #include #include "sqlistOp.h" //创建顺序表 int InitList_sq(SqList &L) { L.elem = (ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType)); if (!L.elem) exit(OVERFLOW); /*初始化失败,返回0*/ L.length = 0; /*置空表长度为0*/ L.listsize = LIST_INIT_SIZE; /*置初始空间容量*/ return OK; /*初始化成功,返回1*/

程序源代码注释规范

程序注释规范说明 程序注释规范应包括以下三方面: 一、文件头部注释 在代码文件的头部进行注释,这样做的好处在于,我们能对代码文件做变更跟踪。在代码头部分标注出创始人、创始时间、修改人、修改时间、代码的功能,这在团队开发中必不可少,它们可以使后来维护/修改的同伴在遇到问题时,在第一时间知道他应该向谁去寻求帮助,并且知道这个文件经历了多少次迭代、经历了多少个程序员的开发和修改。 样本: /***************************************************** ** 作者:Liuchao ** 创始时间:2007-11-12 ** 修改人:Liuchao ** 修改时间:2007-11-12 ** 修改人:Liaochao ** 修改时间:2007-11-12 ** 描述: ** 主要用于产品信息的资料录入,… *****************************************************/ 二、函数、属性、类等注释 请使用///三斜线注释,这种注释是基于XML的,不仅能导出XML制作帮助文档,而且在各个函数、属性、类等的使用中,编辑环境会自动带出注释,方便你的开发。以protected,protected Internal,public声明的定义注释都建议以这样命名方法。 例如: ///

/// 用于从ERP系统中捞出产品信息的类 ///

class ProductTypeCollector { … } 三、逻辑点注释 在我们认为逻辑性较强的地方加入注释,说明这段程序的逻辑是怎样的,以方便我们自己后来的理解以及其他人的理解,并且这样还可以在一定程度上排除BUG。在注释中写明我们的逻辑思想,对照程序,判断程序是否符合我们的初衷,如果不是,则我们应该仔细思考耀修改的是注释还是程序了… 四、变量注释 我们在认为重要的变量后加以注释,说明变量的含义,以方便我们自己后来的理解以及其他人的理解,并且这样还可以在一定程度上排除BUG.我们常用///三斜线注释。 /// 用于从ERP系统中捞出产品信息的类 class ProductTypeCollector { int STData;/// … }