Plaxis远程脚本教程二——实体对象及其参数(板)

0引言

0.1上一章回顾

上第一章节的名称为:Plaxis远程脚本自动分析技术教程一——总纲。链接地址为:https://www.eatrice.cn/post/Plaxis2DPythonAPIOne/

在上一章对Plaixs的Python远程脚本服务器的使用做了简单介绍,针对Python的自定义IDE的Plaxis脚本模块的安装方法进行了详细介绍。最后给出了一个简单的有限元模型分析实例和相应的代码,并逐行分析了该示例代码的作用,以及部分重要参数的可选取值,基于Plaxis文档对这些取值进行了举例。在给出代码示例的同时,针对程序设计中的部分方法和对象,稍微指出如何使用脚本参考文档基本方法,将文档上传至我的个人博客上,给出了公网可达的在线文档地址:分别为:

0.2本章内容

实体单元是岩土工程结构中不可或缺的一部分,也是有限元分析的主要分析对象。在Plaxis中主要使用以下几类实体对象,包括:土体和界面、板、土工格栅、embedded桩、锚杆、梁(3D)。其中,梁实体单元是Plaixs3D中特有的实体单元,一般用作考虑重力的水平横向支撑或其他在力学上受弯的杆式构件。

不同的实体单元起的作用也各不相同,但主要可以分为以下三类几何实体:

Plaxis版本 线实体 面实体 体实体
Plaxis2D 界面、板、土工格栅、embedded桩、锚杆 土体
Plaxis3D embedded桩、梁、锚杆 界面、板、土工格栅 土体

由上表可见,Plaxis不同维度的版本中,实体单元区别最大的有板、土工格栅这两类实体单元。其中板(Plate)在Plaxis2D中向垂直于屏幕方向延伸,一般用于描述基坑支护结构中的地下了连续墙或支护桩、隧道管片或衬砌等岩平面问题的长度变化方向的连续结构。

对于土体来说,在不同版本的Plaxis中,土体的主要区别在于土体实体单元的构成为平面和空间体构造方式的区别,其他诸如单元划分、土体的本构模型以及必须的力学参数等在2D或3D软件中计算时的区别不大。

下面针对不同的实体单元进行详细的介绍,分别从实体单元特性、用途、计算方法、Python接口及调用方法、计算结果输出类型和命令文档地址等方面进行浅析。

1板(Plate)

板是一种在长度方向上截面相同,且截面为直线或曲线的一种单元实体,在2D模型的表现形式一般如下图所示:

板的模型示例

一般通过选中线对象(2D)或选中面对象后进行创建,在3D中通过面对象创建板的方法如下图所示:

Plaxis 3D中在平面上创建板

1.1实体特性及其用途

官方文档中对板实体单元的描述如下所示:

Plates which are actually shell elements are structural objects used to model thin two-dimensiional structure in the ground with a significant flexural rigidity (bending stiffness). The creation of a plate is similar to the creation of a geometry surface.

根据官方文档的描述,Plate实体单元是一种在几何上不虑厚度(但厚度又是必填参数,且计算时又考虑厚度的搭接)的一种十分有趣的二维实体,同时考虑其轴向刚度和抗弯刚度,一般用作模拟墙、板和隧道衬砌等的弹性或弹塑性力学响应。

二维中得到的Plate单元为3或5节点线单元,每个节点分别可以在x方向位移、y方向位移和在xoy平面内旋转三个方向分别有三个自由度。

三维中得到的Plate单元为6节点矩形单元,每个节点分别可以在xyz坐标轴方向位移和xoy、xoz和yoz三个平面内旋转共计有六个自由度。

Plaxis3D中使用的Plate计算理论为Mindlin板理论[1],Mindlin板理论基于等几何分析,考虑了一截剪切变形,使得挠度和同时反映弯矩和剪应力协同作用的变形特性。

板的单元刚度矩阵由所定义的材料参数和来自3或5对高斯积分点的数值积分得到。每对高斯积分点设置在距离板的中线上下分别为$\frac{1}{6}\sqrt {3d}$的位置,其中$d$为板的厚度。

1.2计算方法

板的主要参数包括:弹性模量E、剪切模量G、厚度d、重度(3D可选)、各向同性、防止冲孔和瑞利波阻尼等。不同Plaxis版本的参数属性名、单位和描述均列些在官方文档的表格中,板在2D与3D的材料属性列表文档地址如下所示:

1.2.1必填参数与换算方法

其中对于2D来说,其必填参数和换算关系如下:

属性名 描述 计算公式或取值方法
Elasticity 定义材料的弹性或弹塑性 0:弹性, 1: 弹塑性, 2: MK弹塑性(3D)
IsIsotropic 定义材料的各向同性 False: 不是各向同性, True: 是各向同性
PreventPunching 是否加入人造桩端阻力 False: 不加入, True: 加入
Gref 定义弹性模量(2D)的一半??? 弹性模量$E=2G_{ref}$
d 定义板的厚度m 根据实际输入厚度,会影响和EA和EI
EA 轴向刚度kN/m $EA=2G_{ref} \times d$
EI 定义抗弯刚度$kNm^2/m$ $EI = {G_{ref}}\frac{{{h^3}}}{6}$
Mp 最大允许弯矩kNm/m(塑性材料) 材料的最大允许弯矩
Np 轴向最大允许拉力KN/m(塑性材料) 材料得到最大许用拉力

Gref参数在官方文档没有出现过,但是通过软件的命令行执行效果的追踪得到,该参数决定弹性模量E,且是弹性模量的一半。而直接通过_sps Plate1 "E" 122E5命令则无法设置材料的弹性模量,因此定义板的弹性模量只能通过设置。不知道是不是因为软件是盗版的才有这个问题,目前还没有正版软件进行测试。

基于上述提示,后面所有变量值将会根据所给的计算公式进行计算,命令IsIsotropic在本地测试的时候也不起作用,无论设置与否材料的各向同性参数总是无法勾上,因此需要手动设置另一个方向的轴向刚度值,即EA2。奇怪的是这个值又能够通过Python脚本命令直接赋值,真是奇了个大怪!

当前经过测试,Plaxis 2D对板材料的赋值存在一些问题,比如出现某些属性无法被直接赋值,某些参数在文档对象中并不存在,但是在实际的命令编写中却要进行赋值而且影响重要参数的情况,由于暂时没有正版软件进行测试,因此不清楚是否是软件本身的问题。

在Plaxis 3D中,由于板单元由线单元变为了面单元,因此材料的相关维度也提高了一维,在非各向同性的情况下,不仅需要设置深度方向的各项弹塑性参数,同时还需要设置宽度方向上的各项弹塑性参数。各项弹塑性参数的设置方法同Plaxis 2D,但是弹性模量E的赋值问题却是正常的,可以直接使用E1和E2来定义板结构在不同方向上的弹性模量。

在Plaxis 3D中,新增了屈服应力的概念,其包括两个方向的屈服应力,如果设置材料为各向同性时,则两个方向的屈服应力相同。Plaxis 3D中指定了一项新的参数$W_{ii}$,表示当前方向上的塑性截面模量,其等于截面绕其中性轴计算得到的惯性矩除以中性轴距最外边缘的距离。矩形截面的塑性截面模量计算公式如下所示:

$$ w = \frac{{b{h^2}}}{6} $$

塑性截面模量在Plaxis 3D中不会再自动计算,因此使用Python脚本设置材料参数时,必须手动设置该材料参数的值。

1.2.2板的搭接补偿

在几何模型中,Plaxis认为板对象单元是没有厚度的,因此无法直接在板上选取曲线点。但是在计算工程中板却被认为是有厚度的,板的厚度搭接部分是以板的中线为轴,各向两侧拓展$\frac{d}{2}$的部分。

对于桩来说桩的两侧都是土,因此搭接整个厚度如下图所示:

桩的搭接示意图

考虑搭接的主要原因是桩的密度与土的密度不同,需要考虑桩本身的附加应力的影响,因此Plaxis对土的搭接部分的重度进行修正,以保证计算的精确度。搭接部分的附加应力补偿计算公式如下所示:

$$ w = (\gamma _{plate} - \gamma _{soil})\times d_{real} $$

其中,$\gamma _{plate}$为板的材料重度;$\gamma _{soil}$如果在水位线以上等于$\gamma _{unsat}$,如果在地下水位以下则等于$\gamma _{sat}$。

对于隧道来说,由于衬砌是安装在围岩上的,Plaxis认为板与隧道围岩只搭接一半,即板与围岩接触的一侧与围岩搭接,计算模型示意图如下图所示:

隧道搭接示意图

搭接部分的结构自重计算公式如下所示:

$$ W = ({\gamma _{plate}} \times {d_{real}}) - ({\gamma _{soil}} \times \frac{{{d_{real}}}}{2})r = \frac{{{r_{inside}} + {r_{outside}}}}{2} $$

若板作为基坑的地下连续墙,当墙一侧的土体未挖开时,计算为厚度全部搭接;若基坑一侧土体挖开后,则计算为厚度一半搭接。

1.3Python接口及调用方法

在Plaxis 2D中,需要通过线来创建板,而在Plaxis 3D中,则是通过面来创建板,如上图所示。因此在使用Python脚本创建实体单元前应该先创建实体所依赖的几何结构。下面着重示例在Plaxis 2D中如何循序渐进创建板对象,先创建几何结构然后基于几何结构创建板实体,最后对板实体进行材料的赋值。

1.3.1Plaxis 2D中创建板对象的命令

假如我们需要在某个基坑侧壁创建地下为连续墙,其中一道地下连续墙的位置在(5, 0)和(5, -8)所连成的直线上,如下图所示:

创建的简单2D桩示意图

图中打勾的位置即已经创建好了地下连续墙,同时左侧的对象浏览器中已经为该对象完成了材料赋值,赋值了已经定义好的桩材料。

下面分析软件自带的命令行流程,如下所示:

1
2
3
4
5
6
7
8
0022> _line (5 0) (5 -8)
添加 Point_1
添加 Point_2
添加 Line_1
0023> _plate (Line_1)
添加 Plate_1
0024> _set Line_1.Plate.Material 桩
确定

1.3.2使用Python创建线对象

第一行命令为_line (5 0) (5 -8),表示在坐标(5, 0)到(5, -8)上创建一条线段,根据输出可以得到,该命令先创建了线段的两个端点,然后再创建了一条直线。与远程脚本服务器相对应的是,使用Python创建线的命令与软件自带命令类似,如下所示:

1
2
3
4
5
6
line = g_i.line((5, 0), (5, -8))
print(line)

[<Point {7649F3A0-8724-4BB2-B54D-799CD4544DF4}>,
<Point {574039DF-AD6E-457D-8448-576CDF521E06}>,
<Line {EA5B45AB-11AD-40D3-A877-8F29B115539F}>]

分析以上Python代码,首先使用了g_i.line方法创建了一条线,线的端点分别为(5,0)和(5,-8),创建线的方法执行完之后会返回一个对象,我们将该对象打印,根据输出结果可以得到,到对象是由两个点和一条线组成的列表。因此,实际上变量line被赋值为了一个列表,而我们要获得这条线的话,则需要提取这个列表的最后一个元素,如下代码所示:

1
2
3
4
line = g_i.line((5, 0), (5, -8))[-1]
print(line)

<Line {E75C9042-F8A1-45F9-A659-F76662343CC1}>

得到的变量即为我们需要的线对象。g_i.line方法不仅接受两个直接坐标的有序数对的传入,也可以同时传入多个有序数对,那么脚本服务器会根据命令,先后创建多个点对象,然后将创建顺序先后相邻的两个点连接得到多条直线,如下所示:

1
2
3
4
5
6
7
8
9
10
line = g_i.line((5, 0), (5, -8), (8, -8), (8, 0))
print(line)

[<Point {1737ADF0-64AA-4B34-9C84-6ACB978D77FD}>,
<Point {D5F3CDD5-CF69-479D-8E22-153A9D936F05}>,
<Point {66F097D8-F85E-4819-A7A6-F7E0ABF5BE09}>,
<Point {F5555973-D5FD-46A3-B211-458C0412BA92}>,
<Line {4A60B983-9597-4A47-BCA4-A1B9824EFD8E}>,
<Line {D5E47D2A-32EF-4002-BAE1-9893809F2017}>,
<Line {2BE1535D-1DD7-4C7D-A3CC-B5A529AC6507}>]

g_i.line方法的使用方式还有多种参数传入方法,比如直接传入点对象;或确定起点的位置,然后通过线段长度和转角来确定另一个端点的位置等方法。具体的参数说明在文档中有详细介绍,该方法的文档地址为:https://www.eatrice.cn/Plaxis2D/input_commands/commands_line.html?searchstring=line

1.3.3使用Python创建板对象

第二行命令为_plate (Line_1),表示在第一步得到的线段line_1上创建板对象,根据输出可以得到,该命令创建成功了一个板对象的并且命名为Plate_1。与远程脚本服务器相对应的是,使用Python创建板的命令与软件的自带命令类似,如下所示:

1
2
3
4
5
line = g_i.line((5, 0), (5, -8))[-1]
plate = g_i.plate(line)
print(plate)

<Plate {AD20DBEA-211A-4FA1-9BF2-42C9C206C2F7}>

以上Python代码调用了g_i.plate方法,将上一步创建的直线作为参数传入到该方法中,结果返回了一个plate对象。

1.3.4使用Python创建板的材料参数

现在我们需要对板进行材料的赋值,在上文中的演示图片中,我们将板的材料赋值为桩。而在初始化情况下,Plaxis脚本服务器中没有定义任何材料类型,因此在对板进行材料赋值前,我们需要首先定义一项材料。

1
2
3
4
5
6
pipeMat = g_i.platemat('MaterialName', '桩', 'MaterialNumber', 0, 'd', 0.8
'Elasticity', 0, 'IsIsotropic', True, 'PreventPunching', False,
'Density', 0, 'Gref', 7.625E7, "EA2", 1.22E8, 'nu', 0)
print(pipeMat)

<PlateMat2D {14D2350E-CF29-4006-AD3B-F537E13764BF}>

以上Python代码调用了g_i.platemat方法,该方法会接受指定的输入参数,并返回一个板材料对象,并将该板材料命名为桩。该对象除了上文中提到的必须参数之外,还另外定义了其他说明性参数,包括:

  1. MaterialName,指定该材料的名称,这里定义的材料名称为“桩”;
  2. MaterialNumber,指定该材料在材料列表中显示的顺序,当设置为0的时候,该材料置顶显示;

运行完这项命令后,打开Plaxis材料管理面板,点开我们新创建的桩材料对象,如下图所示:

生成的桩材料参数

由上图可以看到,板的各向同性在Python脚本中设置为True,但是在材料参数面板中显示的是没有勾选状态,经过多种尝试,发现无法通过Python脚本勾选各向同性属性,因此在设置参数时需要对两个方向上的参数一起设置。

如果我们想要后期修改桩的参数的话,可以使用setproperties方法实现,如果我需要将泊松比由0修改为0.3的话,命令如下所示:

1
2
3
print(pipeMat.setproperties('nu', 0.3))

Edited 桩

以上命令返回一个桩材料编辑完成的结果,说明桩的材料已经成功修改,我们进入Plaxis的材料界面查看,如下图所示:

修改后的桩材料参数

如上图所示,桩的材料参数已经修改成功!

注意:材料的泊松比必须小于0.5!!!

基于Python对板的材料赋值在Plaxis 3D中的表现十分有趣,同样的,各向同性选项在Python脚本里面不论怎么设置都无法勾选上。因此对于二维的板结构来说,需要在Python脚本中设置好两个方向上的弹性模量,属性名分别为E1和E2;分别设置好三个方向上的剪切模量,属性名分别为G12、G13和G23。二维板结构的泊松比的属性名相较于一维线结构板的属性名也稍有变化,变为Nu12。

Plaxis 3D板结构中包含有两类弹性模型:弹性和弹塑性。当Elasticity属性设置为1时,材料为弹塑性材料,需要增加相关的弹塑性参数,包括两个方向上的屈服应力sigy11和sigy22,文档中描述的二维板单元

基于以上,完整设置Plaxis 3D的板结构的材料类型的Python脚本命令如下所示:

1
2
3
4
pipeMat = g_i.platemat('MaterialName', '桩', 'MaterialNumber', 0, 'Elasticity', 0,
'PreventPunching', False, 'IsIsotropic', True, 'E1', 15.25E7,
'E2', 15.25E7, 'G12', 7.625E7, 'G13', 7.625E7, 'G23', 7.625E7,
'd', 0.8, 'Nu12', 0.3)

出现部分命令无法配置的情况,经过各种调试仍无法找到问题,但是给出了解决部分配置失效问题的解决方案。推测该问题主要是由于盗版软件产生的。

在线文档对两类材料的参数属性值和具体意义有详细的描述,建议在进行超出本教程之外的板材料配置情境下多参考文档中的配置内容,Plaxis 2D和3D的配置文档如下所示:

1.3.5使用Python对板的材料进行赋值

第三行命令为_set Line_1.Plate.Material桩,表示在第三步的时候将板的材料赋值为板材料。根据输出可以得到,板的材料已经成功赋值完成。该命令的调用方式与远程脚本服务器的Python使用方法略有不同,使用Python对板的材料赋值的方法如下所示:

1
plate.Material = pipeMat

以上代码实现了对先前创建的板进行代码赋值的过程,打开Plaxis对象管理器,选中板对象即可看到板的材料已经赋值为先前创建的桩材料了。

1.4计算结果输出类型

在完成结构计算之后,我们需要查看结果。由于在Plaxis2D和3D的几何模型中,板单元对象是没有厚度的,在划分网格之后没有办法直接对实体单元进行曲线点的选取,如下图所示:

选取板结构单元的点

由图可以看到,当我们在板单元上选点的时候,曲线点列表中会将选取的节点归类到土中的节点上,进行从该节点的得到的力和变形均为土节点在当前位置的力和变形。

1.4.1学会查阅输出对象类型文档

基于以上,在获取实体单元上节点的内力和变形时,不能直接通过选取曲线点方式实现。Plaxis提供了一个实体单元的结果输出对象Plate,Plate输出对象中各参数的属性名和描述如文档所示,文档列举如下:

输出对象包含在ResultTypes集合中,该集合包含了所有储存计算结果的对象类型。本教程按照不同的实体对象单元分别进行讲解,本节主要讲解板单元在Plaxis 2D和3D的输出结果类型。

文档中列举了所有板单元对象所有可以输出的类型结果,每一列的意义和作用解释如下:

  1. 第一列为属性名Property,指定用于访问输出结果类型。如果我需要访问实体单元的X轴方向的位移,那么构造命令ResultTypes.Plate.Ux;如果我需要访问实体单元的最大弯矩,那么构造命令ResultTypes.Plate.M_EnvelopeMax2D

  2. 第二列字段Unit为属性对应的Plaxis的表示方法,用于指示输出类型的一般表示符号。比如$M_{max}$表示实体单元上的最大弯矩,同Plaxis图形用户界面上的表示方法一致;

  3. 第三列字段Description为属性对应的单位,长度单位一般为m,力单位一般为kN,温度单位一般为℃;

  4. 第四列字段Description为属性的简要描述,指出该属性是什么。

1.4.2一个简单实例

下面演示一根插入桩的弯矩和剪力的提取。模型概况:

  • 基于Plaxis 2D,土体深度为10m,土体宽度为20m,桩长8m,两个端点分别为(10, 0)和(10, -8)
  • 土体为砂土,其物理力学特性如代码所示;桩采用板单元进行模拟,板材料参数如代码所示
  • 网格尺度划分密度为0.05
  • 创建一个阶段,该阶段插入当前桩,耗时一天

建立的模型如下图所示:

一个简单板的模型示例

完整代码如下所示:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from plxscripting.easy import *
passwd = 'zV=BUCU?aR7H%e<L'
s_i, g_i = new_server('localhost', 10000, password=passwd)
s_i.new()
g_i.gotosoil()
g_i.SoilContour.initializerectangular(0, -20, 20, 10)
g_i.borehole(0)
g_i.soillayer(10)
soilMat = g_i.soilmat('SoilModel', 4, 'gammaUnsat', 15, 'E50ref', 1E4, 'EurRef', 3E4,
'gammaSat', 15, 'DrainageType', 0, 'einit', 0.5, 'powerm', 0.7,
'cref', 0, 'phi', 31.6, 'psi', 1.6, 'EoedRef', 1E4, 'K0nc', 0.476,
'gamma07', 6e-05, 'G0ref', 5E4, 'MaterialName', '砂土')
g_i.Soils[0].Material = soilMat

g_i.gotostructures()
line = g_i.line((10, 0), (10, -8))[-1]
plate = g_i.plate(line)
pipeMat = g_i.platemat('MaterialName', '桩', 'MaterialNumber', 0,
'Elasticity', 0, 'IsIsotropic', True, 'PreventPunching', False,
'Density', 0, 'Gref', 7.625E7, "EA2", 1.22E8, 'd', 0.8, 'nu', 0)
plate.Material = pipeMat
g_i.gotomesh()
g_i.mesh(0.05, True)

g_i.gotostages()
phase1 = g_i.phase(g_i.Phases[0])
phase1.setproperties('Identification', '打下第一根桩', 'TimeInterval', 1)
plate = g_i.Plates[0]
plate.Active[phase1] = True

g_i.calculate()

output = g_i.view(phase1)
s_o, g_o = new_server('localhost', output, password=passwd)

Ydia = list(g_o.getresults(plate, phase1, g_o.ResultTypes.Plate.Y, 'node'))
print(Ydia)
Qdia = list(g_o.getresults(plate, phase1, g_o.ResultTypes.Plate.Q2D, 'node'))
print(Qdia)
Mdia = list(g_o.getresults(plate, phase1, g_o.ResultTypes.Plate.M2D, 'node'))
print(Mdia)

[0, -0.0689655172413793, -0.137931034482759, ...,
-7.93103448275862, -8]

[-1.19641446206042e-10, -7.60978572776848e-10, ...,
7.39874950166084e-10, 3.86559056658707e-10]

[1.13722016440528e-20, -3.19642322184935e-11, ...,
-3.97632752610123e-11, 1.07372922582527e-20]

g_o.getresults(plate, phase1, g_o.ResultTypes.Plate.Y, 'node')方法接受四个传入的参数:

  1. 板对象plate,该对象有一点需要注意,在结构建立阶段使用plate()方法对该变量做过一次赋值,但是该变量不能直接使用到施工阶段对象的激活与冻结中,需要通过调用所有板的集合对象g_i.Plates[0]来对该变量重新赋值。由于本例只创建了一个板对象,因此该板对应的集合下表为0。若创建了多个板需要精确对应,可以在创建完板对象之后调用g_i.Plates[-1]来更新相应的变量使得可以在施工阶段中进行调整;
  2. 施工阶段对象phase1,为前期创建的施工对象,表示从指定的施工阶段中获取实体单元的计算结果。当有多个施工阶段时,可以将不同施工阶段存储为数组,调用循环语句来快速获得大量结果;
  3. 第三个参数为上文中的计算结果对象类型参数,通过查阅文档,根据实际需要进行调用;
  4. 第四个对象为需要在实体单元上选取的曲线点类型,类型主要包括一下两类:
    1. node单元节点,该单元节点的计算结果值类型比较全面,支持所有ResultTypes.Plate中的对象调用
    2. Stress point应力节点,该单元节点只能输出力学特性的计算结果

不仅是对于板结构,所有的其他结构在获取计算结果时应该都使用node节点来获取,以减少程序可能发生的错误。

根据YdiaQdiaMdia的输出结果可以得到g_o.ResultTypes.Plate.Q2D得到的是一个列表,在划分网格时,实体单元上划分了多个节点node,Plaxis将这些节点的坐标和受力变形排列为等长度的列表,通过结合获取到的Y坐标值即可将剪力与弯矩在实体单元上的位置一一对应。如下图所示:

单桩剪力图

下一节

鉴于本节篇幅较长,因此将不同的构件分成不同的小节进行介绍。下一节将简单介绍土工格栅和Embeded桩的特性和用法。

由于该教程是以笔者的知识水平为视角撰写的,因此难免存在各种疏漏与表述不清楚的部分,同时对于教程中所给的示例代码均假设读者具有一定的Python程序设计水平,因此部分简单的内容没有进行呈现。

希望读者们可以不吝赐教,在文章底部或通过其他方式提供一些改进的建议。

参考文献

  1. METHODS C, MECH A, BENSON D J, et al. Isogeometric shell analysis : The Reissner – Mindlin shell [J]. 2010

Plaxis远程脚本教程二——实体对象及其参数(板)
https://www.eatrice.cn/post/Plaxis2DPythonAPITwo/
作者
吃白饭-EatRice
发布于
2022年3月31日
许可协议