https://www.cnblogs.com/gentle-min-601/p/9785812.htmlhtml
这几天刚刚开始学习MATLAB的面向对象编程。之前作的事情都是用MATLAB写一些简单的脚本或者函数,这方面MATLAB成熟的函数和直截了当的矩阵运算方法和语法都很容易上手,方便人专一于算法自己。前几天写代码的时候想到,在实际用MATLAB进行数值计算时,将数据和函数用一些方法组织起来也会带来很大的便利,不然零散的数据和函数总归看着不舒服。好比,我刚好最近想在MATLAB里面写一点代码让应力张量相关的计算变得简洁一些。python
1、应力张量示例的物理背景算法
在弹性力学里,应力张量是描述弹性体内部某一点的应力情况的;它和过该点且法向量为 n^n^ 的面上的应力(矢量)关系是:编程
这里粗写的 tt 是应力矢量;粗写的 TT 是应力张量,是一个二阶张量,能够表示为一个三维矩阵。应力张量显然不是一个能够直接观测/测量的量,对于通常理解来讲,应力矢量显然是一个更为直观、物理意义更加明确的物理量,所以已知应力张量求应力矢量应当是一个很基本的操做。数组
已知应力张量,求某一个面上的正应力和剪应力大小的方法是:编辑器
正应力就是应力矢量和面的法向量再作一次内积,也能够当作是应力张量和法向量的二次内积。剪应力和正应力方向正交,其矢量和为应力矢量,所以用勾股定理就能获得其大小。在弹性材料中,剪应力每每是很是重要的一个物理量,由于许多材料(好比岩石等)都有抗压不抗剪的特色,剪应力的大小将直接决定这些材料是否会发生破裂、如何(沿哪一个方向)发生破裂。因此已知应力张量求正应力和剪应力也是很基本的一个操做。函数
此外,弹性体中的应力张量老是能够改写成主轴坐标系的形式,其物理意义是老是存在三个正交的主应力方向,以这三个主应力方向为坐标轴表示,则应力张量剪切份量均为零;其数学意义是应力张量老是能够对角化。在主应力坐标轴下表示有特别的简洁性,从而有一些好处,所以求出应力张量的主应力方向和主应力大小都具备重要的意义。oop
如今,我有了三个想要实现的操做。固然,由于MATLAB直观人性化的矩阵运算语法,这三个操做在脚本里都很容易实现。好比,我如今有一个应力张量 T,三个操做分别能够用如下的代码实现:post
1
2
3
4
5
6
7
8
9
10
11
12
|
% 求应力矢量,要求为行向量
t = (T * n).';
% 若n为列向量
t = n * T;
% 若n为行向量
% 求正应力
sigma = n.' * T * n;
% 若n为列向量
sigma = n * T * n.';
% 若n为行向量
% 求剪应力
tau =
sqrt
(
norm
(t)^2 - sigma^2);
% 求主应力及其对应方向
[V, D] =
eig
(T);
|
看起来彷佛也不复杂,可是这个存在四个不足之处:(一) 求应力矢量和求正应力时必需要判断输入的n是行向量仍是列向量,若是每次写一个判断分支语句很是麻烦;(二)求剪应力必须以求正应力为基础,这意味着每次求剪应力都须要手写一遍求正应力的操做,这也包括判断语句;(三)主应力的求解结果为两个矩阵,若是想要直接获得“主应力-主应力方向”的结构怎么办呢?(四)表达不直观,可读性比较差。学习
这些不足之处能够用自定义函数来解决,写一个function把代码放进去就能够了。可是这样又有一个问题:这几个操做仍是没有发生关联!咱们不知道这几个操做都是对于同一个物理量——应力张量进行的操做。若是能像C/C++/Python那样放个类来存放这些函数,而后再用T.X()这样的方法调用岂不是美滋滋?这就是要学习——
2、定义应力张量的MATLAB类
如今,利用MATLAB中的“类”,将应力张量的几个操做集合到一块儿。那么首先,须要定义一个MATLAB类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
% stressTensor.m
% 关键词classdef,后跟一个自定义类名,存放属性和方法,以end结束
classdef
stressTensor
% 应力张量(stressTensor)类
% 关键词properties:用来存放类的属性(成员变量),以end结束
properties
tensorMat
end
% 关键词methods:用来存放类的方法(成员函数),以end结束
methods
% 关键词function:这个见过不少遍了,就是用来定义函数的,写法和直接定义函数同样,以end结束
% 下为构造函数,名称和类名称相同,返回值必须是stressTensor类,所以在书写时能够直接写"item.tensorMat",即访问其做为stressTensor类具备的成员变量
function
item = stressTensor( A )
if
nargin
== 0
item.tensorMat =
zeros
(3, 3);
else
item.tensorMat = A;
end
end
end
end
|
在定义一个类的时候,至少要定义一个构造函数(正如上文已经实现的),不然可能根本没法生成一个实例。这里有个小技巧,在函数中nargin是一个特殊的关键词,它的涵义能够理解为:n-arguments-input,即传入参数的个数。利用nargin,能够在函数结构内部判断输入了几个参数,而且分别作出响应,这等于写一个函数就达到了函数重载的做用。除此以外,MATLAB的函数在定义其实现方法时,能够采用可变参数列表varargin(即 variable-arguments-input),这可使得无需指明可能的参数个数(更别提类型),在函数结构内部再进行识别和操做。固然,能够想见此时的varargin确定不是普通的只能组织浮点数数据的向量;它必须可以组织多种多样的数据——元胞数组(cell)。
存放类的定义的文件通常和类同名(stressTensor.m);在该文件内部须要指明类的属性(成员变量),在“属性(properties)”关键词下完成(见上代码);指明类的方法(成员函数),在“方法(methods)”关键词下完成(同见上代码)。好比,求应力矢量、求正应力、求剪应力:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
function
stress = getStress( obj, direction )
% 求应力矢量
% 输入一个参数:待求面法向量direction;
% 返回:该面上应力矢量stress(行向量)
direction = direction /
norm
(direction);
if
size
(direction, 1) > 1
stress = (obj.tensorMat * direction).';
else
stress = direction * obj.tensorMat;
end
end
function
stressNorm = getNormal( obj, vec )
% 求正应力
% 输入一个参数:待求面法向量vec;
% 返回:该面上正应力stressNorm(行向量)
normVal = obj.getNormalVal(vec);
stressNorm = normVal*vec/
norm
(vec);
end
function
stressShear = getShear( obj, vec )
% 求剪应力
% 输入一个参数:待求面法向量vec;
% 返回:该面上剪应力矢量stressShear(行向量)
vunit = vec/
norm
(vec);
stress = obj.getStress(vec);
stressVal =
dot
(stress, vunit);
stressNorm = stressVal*vunit;
stressShear = stress - stressNorm;
end
|
以及求主应力的方法:
1
2
3
4
5
6
7
8
9
10
11
12
|
function
pAxis = principalAxis(obj)
% 求主应力及其对应方向
% 空输入;返回一个3*2元胞数组,第i行为:第i个主应力-第i个主应力对应方向
pAxis =
cell
(3, 2);
A = obj.tensorMat;
[eigvec, eigval] =
eig
(A);
pStress =
diag
(eigval);
for
i
= 1:3
pAxis{
i
, 1} = pStress(
i
);
pAxis{
i
, 2} = eigvec(:,
i
).';
end
end
|
咱们注意到在类的函数中有参数obj,这个词代表该函数中须要调用类自身的其余函数或成员变量,这和python中但凡是从属于类的方法(成员函数),在定义类的过程当中必需要包含self这个参数所起的做用是相同的。C++中用法相似的是类自身指针this,可是在参数中并不须要包含。包含了obj参数之后就能够在function内部调用自身的属性和方法了,所用的语法分别是obj.property和obj.method(arguments)。就我所知,MATLAB中类函数的定义中obj不是强求包含的,可是若是不包含,这个函数放在类中的意义就不明确了,除类内重载的函数外,每每能够放在类外(对比Python的静态(static)函数)。
3、类函数的重载和运算符重载
最后,谈到MATLAB就不能不谈到数值计算,即便是面向对象编程也不例外;谈到数值计算,就必须谈到运算符重载,由于只有这样才能真正地把本身写的类融入到MATLAB的一套运算语法中去。MATLAB里的运算符重载比其余许多语言都显得直截了当得多,由于它的每一个运算符都对应一个函数,这个函数和普通的函数没有什么区别。好比+运算符,对应的函数文件就是plus.m,函数名即plus。所以在自定义类中,运算符重载和函数重载别无二致,只要重写一个位于这个类下的同名方法便可,例如:
1
2
3
4
|
function
result = plus( obj1, obj2 )
A = obj1.tensorMat + obj2.tensorMat;
result = stressTensor(A);
end
|
只要肯定plus是从属于stressTensor的方法,就实现了+运算符重载。
具体有哪些运算符能够重载,对应的函数名是什么能够参见运算符重载参考页。或者下面这个很大的表也提供了相应的信息:
4、检验和实际调用
接下来,只须要把以上这一系列函数放在类定义体(classdef stressTensor)的方法(methods)关键词下就能够了。咱们能够写一个脚本调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import
stressTensor.*;
% 首先,import这个类及其内容,可以使用通配符.*导入类下的全部内容
T1 = stressTensor();
% 无参构造函数,参见以上构造函数定义时nargin==0的情形
A = 3*
eye
(3) +
diag
([1, 2], 1) +
diag
([1, 2], -1);
T2 = stressTensor(A);
% 含参构造函数
T = T1 + T2;
% 调用重载的加法
disp
(T.tensorMat);
% 显示此时的应力张量
% 输出:
% 3 1 0
% 1 3 2
% 0 2 3
v = [1, 1, 1];
stress = T.getStress(v);
% 调用求应力矢量的方法
disp
(stress);
% 输出:
% 2.3094 3.4641 2.8868
disp
(T.getShearVal(v));
% 调用求剪应力的方法
% 输出:
% -0.5774 0.5774 -0.0000
|
获得的结果符合预期。
5、类函数拆分为文件和组织方式
最后,关于文件的拆分。
一个类也许会很大,包含一些属性和数量庞大和规模庞大的方法,这确定会形成咱们刚刚写的stressTensor.m这样储存类的信息的文件愈来愈大、愈来愈乱,不便于管理。对此,C++的处理方法是,将类的定义和类函数的声明放在头文件里,函数的实现放在源文件里,并且能够经过多个源文件完成函数实现。MATLAB更简单,全部的函数均可以原封不动地拆出来,把全部的代码剪切到一个新文件里就好了,新文件的文件名设置为函数的名称。可是!为了让MATLAB编辑器明白它们都是从属于一个类的函数,全部关于一个类的方法和这个类的定义自己须要放在一个文件夹里,这个文件夹的名字为“@"+类名,@是为了让编译器明白这是一个类的文件夹;好比以前的应力张量,文件夹名为"@stressTensor"。下图就是个人这个stressTensor类的组织方式。