# 设计 Design

本章讨论 SpDB 的概要设计，阐述基本设计原则，基础架构，数据模型和功能模块。

.. note:: 本文中以 Python 作为默认开发语言，但设计思想不以此为限，主要功能都应可采用其他语言实现。

## Principles of Design 设计原则

明确“设计原则”是为了保证程序实现的整体一致性，消除歧义。

Data is defined by the data model and data representation

- Data model: the semantics and structure of data :  ITER Physics Data Model /IMAS Data Dictionary (DD)
  
- Data representation: the concrete data format :  File Format: HDC, HDF5, MDSplus, JSON, XML，sdf

Language-specified data type: Python(dict,list,numpy)，Fortran(array,structure)....

The data model has a unique definition, but the content changes frequently. There are many types of data representation,
and their APIs are always fixed. In SpDB, different data sources are mapped to a common data representation through configurable
data mapping files. Data is read into Python as a hierarchical tree through plug-ins, and then mapped to IMAS DD according to mapping files (XML file).

Database is a collection of documents. Document is a hierarchical tree of data with fixed schema (IMAS DD) .
Data can be divided into three catalogs:

- Configuration data (readonly, identified by 'device'):  the parameters of the device configuration, such as the shape of the limiter and the position of the PF coil. For certain devices, these data always remain unchanged and have read-only attributes.
  
- Experimental data (readonly, identified by 'shot): the control and diagnostic signals collected during the experiment usually come from the experimental database or the scenario design.
  
- Analysis data (readonly, identified by 'run'):the data generated by simulation, modeling and analysis.

### Data Model 数据模型

数据模型是 SpDB 的骨干
用来描述 Tokamak 各个物理系统时间、空间和物理量的状态信息，表现物理量之间的逻辑和隶属关系。

数据模型不描述约束关系和动态演化
数据量之间存在由物理规律（演化方程、边界条件）决定的内在约束，数据模型检查内在约束关系是否成立，也不负责由这些约束
关系导出新的数据。

数据模型不限定于某一种物理载体
数据模型是实例的存储、传递依赖于后端实现。数据模型只对访问路径与数据的对应关系给出标准化的约定。

数据组织应采用层次化树状结构  
 Tokamak 状态描述涉及大量参数，层次化树状结构能够更好表达数据组织关系和实际语义，遵循 Document Object Model (DOM)
设计模式。这一原则延续了 ITER “物理数据模型”的设计思想。

数据应具有明确的语义
明确定义的语义对于模块间数据交互至关重要。这里“语义”，包含但不限于，数据类型（如，int，float，string，array 等），
物理含义，物理量单位，对于时间序列还应明确时间轴。数据的“语义”定义依赖于 IMAS 的 Data Dictionary（DD）的定义，
可根据数据在 DOM 中的路径（path）在 DD 中找到对应语义。

数据应保持全局的一致性
数据全局一致性是指，在全局任何一个地方经由同一 path 访问数据树所获得的结果应是确定一致的。由于 SpDB 模块与主程序之间
并不一定共享地址空间，数据在不同进程和存储介质间传递、转换过程中保持一致性是必须要始终贯彻原则。采用统一数据访问接口
Unified Data Access, UDA，是解决这一问题的可行方法。

消除数据歧义：多个路径对应同一数据
为了保证对于单一 IDS 表述的完整性，有些全局性数据会在不同的 IDS 中重复出现，例如，在 equilibrium 和 core_profiles 等多个
IDS 出现的 vacuum_magnetic_field。DD 描述的是数据访问的语义，并未限制数据存储方式。在访问路径 path 与实际数据的映射之间，
需要保证数据的唯一性。

数据对象应具有“不可变（immutable）”语义
数据对象带有“immutable”语义可以大幅度降低复杂度。数据节点之间存在复杂的网状依赖关系，单一数据也会出现分布式存储情况。
保持“immutable”属性可以避免破坏数据语义的全局一致性。

维护物理量间的自洽关系
IDS 中定义的物理量对于描述系统状态来说是冗余的，一些物理量之间存在明确的导出关系。例如，环向磁通 :math:`\Phi_{tor}`，
角向磁通 :math:`\psi` 与安全因子 :math:`q` 三者间由明确定义的数学关系。同时存储三个物理量，不能明确保证三者间的依赖关系。
应考虑存储其中两个量，在实际访问第三个量时实时计算出第三个量。配合 immutable 属性和数据缓冲，可以保证数据内在逻辑的自洽。

### Modules 模块

模块描述数据节点之间的约束关系
不同的数据状态在数据树中对应不同的数据节点，模块负责节点之间的数据的转化关系。以数据为节点，模块为边构成数据流的
有向无环图（DAG）

物理模块定义数据量之间的物理约束关系
物理量之间存在由物理规律决定的内在约束，也就是各类方程、函数。“模块”负责这些约束关系的具体实现。也就是数值计算对应
的求解边值问题和初值问题。

模块划分应与 IMAS 数据模型保持一致
IMAS 物理数据模型对于 Tokamak 各个子系统的描述十分详尽。采用与其一致的模块划分可以保证数据交互接口的兼容性。

模块应是无状态的，不可变的 immutable，可复制的
目的是为了降低模块部署、调度过程中的复杂度，有利于将 DAG 在分布式并行环境的调度部署。

模块功能保持明确单一，保证模块的可替代性
模块的功能应保持明确且单一，仅实现一个操作，求解一组方程。不应进行超出约定之外的数据操作。

## Infrastructure 基础架构

.. figure:: figures/SpDB2.svg
:align: center

### SpDB 基础环境架构

SpDB 的基础架构是指运行 SpDB 的计算环境，主要由四部分构成：

* **UI/UX 交互界面** : 用户与 SpDB 交互的环境。 需要在其中实现“执行图”编写、调试、数据可视化等功能。计划采用科学交互,计算环境Jupyter作为主要环境。
  
* **数据源** : 是数据的实际存储载体。可以是本地数据文件、远程数据库或其他具有CRUD(Create, Read, Update, Delete)功能数据源。这些数据源通常不具有标准的IMAS数据标准语义，需要通过UDA进行转换。
  
* **外部模块** ：SpDB 中的物理模块可通过插件的方式替换。每个模块对应一个外部模拟程序，同一个功能会有多个不同外部程序实现。这些模块需要通过独立的版本系统管理，从而保障整体建模结果的可追溯性和可重复性。
  
* **高性能计算环境** ：物理建模需要庞大的计算资源支持，离不开分布式的高性能计算环境。当需要并行计算时 SpDB 生成描述工作流的DAG，然后通过调度引擎完成计算。

关于 SpDB 运行环境架构的细节，在《 SpDB 设计文档》中详细阐述。

## Data 数据

### Data Representation 数据表述

在 SpDB 中数据类型方式划分为四种基本形式：

* **Scalar 标量** : 对应于整型（int），浮点（float）和 字符串（string）等元数据类型；
  
* **nd-array 多维数组**: 由单一元数据类型（scalar）构成的紧密规则排列的阵列，在不同场景下也被称为tensor、matrix
      或者array。实现上采用numpy.ndarray表示。
  
* **Sequence 序列** : 一组数据的有序排列，数据类型可以不一致，在不同场合下也被称为 list，tuple，array（不同于前面
      的单一类型构成的数组）
  
* **Mapping 映射** : 一组 key-value 组成的集合，在不同场合下也被称为 dict，object，structure等

目前，结构化数据表述方式主要有 JSON 和 XML 两种风格。SpDB 的表述方式在 JSON 格式的基础上，针对数值计算的需求增加了
ndarray 类型。

SpDB 采用层次化树状数据结构，其中根节点为 Mapping 类型，中间节点为 Sequence 或者 Mapping 类型，叶节点为 Scalar 或者
nd-array 类型。

与 XML 文档树结构转换时，XML 文档树中具有相同标签 Tag 的兄弟节点汇总为一个 Sequence 节点，默认按照 XML 文档中的先后顺序排列。

在 Mapping 类型中，key-value 对的顺序，依照构建时插入的顺序排列（这里遵循 Python OrderedDict 定义）。

### Path 数据访问路径

通过 Path 在文档中定位节点位置，支持 XPath 和 JSONPath 两种语法。

例如：`/wall/description_2d[0]/limiter/unit[1]/outline/r` 或者 `equilibrium.time_slice[1:10:2].profiles_2d.psi`

| XPath | JSONPath           | 描述                                 |
| ----- | ------------------ | ------------------------------------ |
| `/`   | `$`                | 根节点                               |
| `.`   | `@`                | 当前值                               |
| `/`   | `.` or `[]`        | 子节点算符                           |
| `//`  | `..`               | 上一层                               |
| `*`   | `*`                | 通配符                               |
| `[]`  | `[]`               | 下标算符                             |
| ``    | `[,]`              | 联合算符 (对于两个以上下标算符）     |
| n/a   | `[start:end:step]` | slice 算符（仅对 Sequence 节点有效） |
| `[]`  | `?()`              | filter expression                    |

语法：属性访问 

	>>> equilibrium.time_slice[1:10:2].profiles_2d.psi[:]=0.0

语法：遍历 
    >>> for eq in equilibrium.time_slice:
			print(eq.profiles_2d.psi)

### Unified Data Access 统一数据访问接口

Tokamak 实验数据组织的特点是围绕着放电（shot）展开，每个 shot 单独可以成为一个树。在 SpDB 中每一个 shot 抽象表述为一个“文档（Document）”，具有唯一的编号 ID。
数据库则被视为文档的集合（collection）。这种数据组织形式可以归类为 No-SQL 数据库形式，所谓的 Document Database。例如，传统 MDSPlus 的数据库，商业数据库 MongnoDB，
Amazon Cosmos DB，Microsoft Document DB 等满足这一抽象。

数据源的通常指实验数据库，其访问协议是多样的，为了简化应用 SpDB 采用了 Unified Data Access 统一数据访问接口形式。

数据源采用 Uniform Resource Identifier (URI) 表述，遵循国际标准 RFC 3986， 其正则表达式为

    ``r"^((?P<schema>[^:/?#]+):)?(//(?P<authority>[^/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?")``

例如，

	>>>entry=open_entry("east+mdsplus://mdsplus.ipp.ac.cn/?tree_name=efit_east#shot=55555, time_slice=100")
	>>>entry=open_entry("east+mdsplus:///home/salmon/public_data/~t/?tree_name=efit_east#shot=55555, time_slice=100")
	>>>entry=open_entry("/home/salmon/public_data/efit_east_00055555.h5#time_slice=100")

其中
_ schema 表示数据源的访问协议和采取数据映射（见 `Mapping 数据映射`\_），例如 "east+mdsplus"，意为采用 EAST 数据映射，mdsplus 访问协议。
_ authority 表示数据源访问权限，例如，mdsplus.ipp.ac.cn 。
_ path 指 Collection 在数据库中的路径，当 schema 和 authority 为空的时候，path 可对应为本地文件，数据格式由文件的扩展名确定。
_ query 和 fragment，解析为一系列 key-value 值，用于在 Document 树中定位节点，例如，`time_slice=100`，意为选择编号为 100 的时间片

### Mapping 数据映射

**物理数据模型** 根据数据语义内在的依赖关系，将其组织为层次化的树状结构，并给予统一的访问标识 URI，形成统一的命名空间。 原始数据是分散
存储在不同的数据源中的，需要经过 **Mapping 数据映射** 的过程将其组织在统一命名空间下。

根据数据的存储特性，可以分为三类

* static data 静态数据，通常指装置构型的参数，如 limiter 的形状，PF 线圈的位置等。对于确定的装置，这些数据始终保持不变，具有只读属性。
  
* dynamic data 动态数据, 实验过程中采集的控制和诊断信号，一般源自实验数据库，依赖于具体的实验炮号（shot number），具有只读属性。
  
* generated data 生成数据， 通过模拟、建模、分析的过程生成的数据，可以进行读写操作。



###    数据映射

数据映射过程如上图所示，其中映射文件以 XML 文件的格式定义。文件结构与 IMAS 物理数据一致，节点的值对应映射的结果。对于静态数据直接将数据存储在映射文件中，
动态数据返回对应的数据库访问命令。
