cloudpss 3.2.0a2__py3-none-any.whl → 4.0.0a5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. cloudpss/__init__.py +6 -4
  2. cloudpss/dslab/DSLabFinancialResult.py +96 -0
  3. cloudpss/dslab/__init__.py +2 -0
  4. cloudpss/dslab/dataManageModel.py +267 -0
  5. cloudpss/dslab/dslab.py +145 -0
  6. cloudpss/dslab/files/__init__.py +2 -0
  7. cloudpss/dslab/files/curveData.py +140205 -0
  8. cloudpss/dslab/files/files.py +19 -0
  9. cloudpss/dslab/financialAnalysisModel.py +137 -0
  10. cloudpss/function/functionExecution.py +0 -2
  11. cloudpss/ieslab/DataManageModel.py +415 -0
  12. cloudpss/ieslab/EvaluationModel.py +189 -0
  13. cloudpss/ieslab/IESLabPlan.py +132 -0
  14. cloudpss/ieslab/IESLabSimulation.py +54 -0
  15. cloudpss/ieslab/PlanModel.py +143 -0
  16. cloudpss/ieslab/__init__.py +4 -0
  17. cloudpss/model/jobDefinitions.py +34 -0
  18. cloudpss/model/model.py +202 -15
  19. cloudpss/model/revision.py +2 -2
  20. cloudpss/project/project.py +4 -3
  21. cloudpss/runner/DSLabResult.py +92 -0
  22. cloudpss/runner/IESLabEvaluationResult.py +143 -0
  23. cloudpss/runner/IESLabPlanResult.py +195 -0
  24. cloudpss/runner/IESLabTypicalDayResult.py +142 -0
  25. cloudpss/runner/MessageStreamReceiver.py +193 -0
  26. cloudpss/runner/receiver.py +3 -3
  27. cloudpss/runner/result.py +38 -4
  28. cloudpss/runner/runner.py +61 -18
  29. cloudpss/utils/IO.py +153 -0
  30. cloudpss/utils/httprequests.py +14 -11
  31. cloudpss/verify.py +27 -14
  32. cloudpss/version.py +1 -0
  33. {cloudpss-3.2.0a2.dist-info → cloudpss-4.0.0a5.dist-info}/METADATA +2 -4
  34. cloudpss-4.0.0a5.dist-info/RECORD +54 -0
  35. {cloudpss-3.2.0a2.dist-info → cloudpss-4.0.0a5.dist-info}/WHEEL +1 -1
  36. cloudpss-3.2.0a2.dist-info/RECORD +0 -33
  37. {cloudpss-3.2.0a2.dist-info → cloudpss-4.0.0a5.dist-info}/top_level.txt +0 -0
cloudpss/__init__.py CHANGED
@@ -1,22 +1,24 @@
1
1
  # coding=UTF-8
2
+ from cloudpss.ieslab import IESLabSimulation, IESLabPlan
2
3
  from .verify import setToken
3
4
  from .runner import Runner, Result, EMTResult, PowerFlowResult
4
5
  from .model import Model, ModelRevision, ModelTopology
5
6
  from .project import Project
6
7
  from .utils import MatlabDataEncoder, DateTimeEncode
7
8
  from . import function
8
- from .function import FunctionExecution
9
9
 
10
+ from .function import FunctionExecution
11
+ from .version import __version__
10
12
  __all__ = [
11
13
  'setToken', 'Model', 'ModelRevision', 'ModelTopology', 'Runner', 'Result',
12
14
  'PowerFlowResult', 'EMTResult', 'MatlabDataEncoder', 'DateTimeEncode',
13
- 'function', 'Project', 'currentJob'
15
+ 'function', 'Project', 'currentJob', 'IESLabSimulation', 'IESLabPlan','__version__'
14
16
  ]
15
- __version__ = '3.2.0.alpha.2'
17
+
16
18
 
17
19
 
18
20
  def currentJob():
19
21
  """
20
22
  获取当前的 currentExecution 实例
21
23
  """
22
- return FunctionExecution.current()
24
+ return FunctionExecution.current()
@@ -0,0 +1,96 @@
1
+ import json
2
+ from ..utils.httprequests import request
3
+
4
+ class DSLabResult(object):
5
+ _baseUri = 'api/ies/rest/'
6
+ _kindNameMap = {
7
+ "利润与利润分配": "getEconomyResult",
8
+ "财务计划现金": "getFinancialPlanCashFlowResult",
9
+ "资产负债": "getLiabilityAssetsResult",
10
+ "投资使用计划与资金筹措": "getInvestPlanDataResult",
11
+ "借款还本付息计划": "getLoanRepaymentPlanResult",
12
+ "流动资金估算": "getFlowCashEvaluteResult",
13
+ "资产折旧与摊销估算": "getFlowCashEvaluteResult",
14
+ "总成本费用估算表": "getSumCostResult",
15
+ "项目总投资现金流量": "getSumInvestFlowCashResult",
16
+ "项目资本金现金流量": "getProjectCashFlowResult",
17
+ "营业收入、税金、附加和增值税估算": "getIncomeTaxResult",
18
+ }
19
+
20
+ def __init__(self, simulationId, taskId=None, **keywords) -> None:
21
+ """
22
+ 初始化
23
+ """
24
+ self.simulationId = simulationId
25
+ self.timeId = keywords.get('timeId', 0)
26
+ self.planId = keywords.get('planId', 0)
27
+ self.cmdType = keywords.get('cmdType', None)
28
+
29
+ def _fetchItemData(self, url, planID):
30
+ '''
31
+ 获取planID对应的优化方案下resultType财务评估结果
32
+
33
+ :param planID int 类型,表示优化方案的ID,数值位于0~优化方案数量之间
34
+ :param resultType enum 类型,表示财务评价结果表格的类型
35
+
36
+ :return: dict 类型,为源数据的引用,代表方案对应的财务评价基础参数信息
37
+ '''
38
+ r = request('GET',
39
+ url,
40
+ params={
41
+ "simu": self.simulationId,
42
+ "planId": planID,
43
+ "time": 0
44
+ })
45
+ data = json.loads(r.text)
46
+ return data
47
+
48
+ # def status(self):
49
+ # '''
50
+ # 获取运行状态
51
+
52
+ # :return: boolean 类型
53
+ # '''
54
+ # result = None
55
+ # if self.cmdType is None:
56
+ # result = self.GetOverviewResult()
57
+ # if result is None:
58
+ # return False
59
+ # return True
60
+
61
+ def GetFinancialResult(self, resultType):
62
+ '''
63
+ 获取planID对应的优化方案下resultType财务评估结果
64
+ :param planID int 类型,表示优化方案的ID,数值位于0~优化方案数量之间
65
+ :param resultType enum 类型,表示财务评价结果表格的类型
66
+
67
+ :return: dict 类型,为源数据的引用,代表方案对应的财务评价基础参数信息
68
+
69
+ '''
70
+ assert (resultType in self._kindNameMap), "数据类型不存在"
71
+ kind = self._kindNameMap.get(resultType, resultType)
72
+ url = f'{self._baseUri}{kind}'
73
+ list = self._fetchItemData(url, self.planId)
74
+ dict_result = dict()
75
+ for val in list:
76
+ for k, v in val.items():
77
+ dict_result[k] = v
78
+ return dict_result['data']
79
+
80
+ def GetOverviewResult(self):
81
+ '''
82
+ 获取当前结果类对应的概览结果
83
+
84
+ :return: array类型,代表该方案对应的概览结果
85
+ '''
86
+ r = request('GET',
87
+ "api/ies/rest/getOverviewResult",
88
+ params={
89
+ "simu": self.simulationId,
90
+ "planId": self.planId,
91
+ "time": self.timeId
92
+ })
93
+ result = json.loads(r.text)
94
+ if len(result) > 0:
95
+ return result[0]['data']
96
+ return None
@@ -0,0 +1,2 @@
1
+ from .dslab import DSLab
2
+ __all__ = ['DSLab']
@@ -0,0 +1,267 @@
1
+ from ..utils import request, fileLoad, graphql_request
2
+ import json
3
+ import time, datetime
4
+ import copy
5
+ import os
6
+ from cloudpss.dslab.files import getCurveData
7
+
8
+ class DataManageModel(object):
9
+ _weatherUrl = ''
10
+ _baseUri = ''
11
+ _kindUrlMap = {}
12
+ _itemDataMap={}
13
+ _kindItemDataMap={}
14
+ _kindIdMap={}
15
+
16
+ def __init__(self, resourceId):
17
+ self.resourceId = resourceId
18
+
19
+ def _status(self):
20
+ '''
21
+ 获取运行状态
22
+
23
+ :return: boolean 类型
24
+ '''
25
+ pass
26
+
27
+ def _fetchItemData(self, url):
28
+ '''
29
+ 私有方法,获取simu对应所有数据项的列表
30
+ :params: url string类型,request请求对应的url链接
31
+
32
+ :return: list类型,返回该种类下所有数据项的的列表
33
+ '''
34
+ r = request('GET',
35
+ url,
36
+ params={
37
+ "simu": self.resourceId,
38
+ })
39
+ return json.loads(r.text)
40
+
41
+ def _saveItemData(self, url, data):
42
+ '''
43
+ 私有方法,保存url链接对应的data数据
44
+ :params: url string类型,request请求对应的url链接
45
+ :params: data dict类型,表示添加的数据内容,其数据结构应满足对应数据项的结构要求
46
+
47
+ :return: 无
48
+ '''
49
+ r = request('POST', url, data=json.dumps(data))
50
+
51
+
52
+ def _updateItemData(self, url, data):
53
+ '''
54
+ 私有方法,更新url链接对应的data数据
55
+ :params: url string类型,request请求对应的url链接
56
+ :params: data dict类型,表示添加的数据内容,其数据结构应满足对应数据项的结构要求
57
+
58
+ :return: 无
59
+ '''
60
+ r = request('PUT', url, data=json.dumps(data))
61
+
62
+ def _deleteItemData(self, url):
63
+ '''
64
+ 私有方法,删除url链接对应的数据
65
+ :params: url string类型,request请求对应的url链接
66
+
67
+ :return: 无
68
+ '''
69
+ r = request('DELETE',url)
70
+
71
+ def LocationGet(self):
72
+ '''
73
+ 获取气象定位点数据
74
+
75
+ :return: list<dict>类型,为源数据的引用,包含id,经度坐标,纬度坐标,定位点名称
76
+ '''
77
+ url = f"{self._baseUri}rest/location"
78
+ r = request('GET',
79
+ url,
80
+ params={"simu": self.resourceId})
81
+ return json.loads(r.text)
82
+
83
+ def LocationCreate(self, name=None, longitude=None, latitude=None):
84
+ '''
85
+ 创建气象定位点
86
+ :param: name 定位点名称,可选
87
+ :params: longitude float类型,可选,表示经度,范围为气象数据源的经度范围
88
+ :params: latitude float类型,可选,表示纬度,范围为气象数据源的纬度范围
89
+
90
+ :return: 无
91
+ '''
92
+ url = f"{self._baseUri}rest/location"
93
+ r = request('POST',
94
+ url,
95
+ data=json.dumps({
96
+ "lat": '34.734492',
97
+ "lng": '113.648906',
98
+ "simu": self.resourceId,
99
+ "name": '定位点'
100
+ }))
101
+ d = json.loads(r.text)
102
+ if name is not None and longitude is not None and latitude is not None:
103
+ if (float(longitude) > 180 or float(longitude) < -180
104
+ or float(latitude) > 90 or float(latitude) < -90):
105
+ raise Exception('经纬度坐标不存在')
106
+ else:
107
+ r = request('PUT',
108
+ url,
109
+ data=json.dumps({
110
+ "lat": latitude,
111
+ "lng": longitude,
112
+ "simu": self.resourceId,
113
+ "name": name,
114
+ "id": d['id']
115
+ }))
116
+ else:
117
+ raise Exception('参数缺失')
118
+
119
+ def LocationUpdate(self, id, name, longitude, latitude):
120
+ '''
121
+ 修改气象定位点
122
+ :param id: 定位点id
123
+ :param: name 定位点名称,可选
124
+ :params: longitude float类型,可选,表示经度,范围为气象数据源的经度范围
125
+ :params: latitude float类型,可选,表示纬度,范围为气象数据源的纬度范围
126
+
127
+ :return: 无
128
+ '''
129
+ if (float(longitude) > 180 or float(longitude) < -180
130
+ or float(latitude) > 90 or float(latitude) < -90):
131
+ raise Exception('经纬度坐标不存在')
132
+ else:
133
+ url = f"{self._baseUri}rest/location"
134
+ r = request('PUT',
135
+ url,
136
+ data=json.dumps({
137
+ "lat": latitude,
138
+ "lng": longitude,
139
+ "simu": self.resourceId,
140
+ "name": name,
141
+ "id": id
142
+ }))
143
+
144
+ def LocationDelete(self, id):
145
+ '''
146
+ 删除气象定位点
147
+ :param id: 定位点id
148
+
149
+ :return: 无
150
+ '''
151
+ url = f"{self._baseUri}rest/location/{str(id)}"
152
+ r = request('DELETE',
153
+ url)
154
+
155
+ def LoadWeather(self):
156
+ '''
157
+ 加载气象数据
158
+
159
+ :return: 无
160
+ '''
161
+ url = f"{self._baseUri}rest/load_weather"
162
+ r = request('GET',
163
+ url,
164
+ params = {
165
+ "simu": self.resourceId,
166
+ })
167
+
168
+ def GetAtmosData(self, locationId, date):
169
+ '''
170
+ 获取日期在date的气象数据
171
+ :params: locationId str类型,表示定位点id
172
+ :params: date dateTime类型,表示时间
173
+
174
+ :return: list<dict>类型,为源数据的引用,返回当前项目位置对应时间范围内的气象数据序列,每个元素用字典进行表示,字典的key即区分不同的气象数据项(如风速、太阳辐照等)以及标识当前时间点
175
+ '''
176
+ rDate = datetime.date(*map(int, date.split('-')))
177
+ r = request('GET',
178
+ self._weatherUrl,
179
+ params={
180
+ "locationId": str(locationId),
181
+ "date": rDate,
182
+ })
183
+ return json.loads(r.text)
184
+
185
+ def AddDataItem(self, kind, data):
186
+ '''
187
+ 向kind类型的数据库中添加内容为data的数据项
188
+ :params: kind str类型,数据的种类标识,包含:光伏、风机、燃气、水电、常规小火电、生物质发电、垃圾电厂、传输线、变压器、开关、负荷分类、负荷用户、储能配置、上网电价、输配电价、常数电价、阶梯电价、分时电价、分时阶梯电价
189
+ :params: data dict类型,表示添加的数据内容,其数据结构应满足对应数据项的结构要求
190
+
191
+ :return: list<dict>类型,返回该种类下所有数据项的列表
192
+ '''
193
+ assert (kind in self._kindUrlMap), "数据类型不存在"
194
+ extra = data.get('extra')
195
+ if extra is None or not extra:
196
+ extra = getCurveData(kind)
197
+ r = {
198
+ 'simu': self.resourceId,
199
+ 'name': data.get('name', ''),
200
+ 'extra': extra,
201
+ 'data': data.get('data', {}),
202
+ }
203
+ url = f"{self._baseUri}rest/{kind}"
204
+ self._saveItemData(url, r)
205
+ return self._fetchItemData(url)
206
+ else:
207
+ r = {
208
+ 'simu': self.resourceId,
209
+ 'name': data.get('name', ''),
210
+ 'extra': extra,
211
+ 'data': data.get('data', {}),
212
+ }
213
+ url = f"{self._baseUri}rest/{kind}"
214
+ self._saveItemData(url, r)
215
+ return self._fetchItemData(url)
216
+
217
+ def DeleteDataItem(self, id, kind):
218
+ '''
219
+ 获取kind类型对应所有数据项的列表
220
+ :params: id int类型,数据的id
221
+ :params: kind str类型,数据的类型
222
+
223
+ :return: list<dict>类型,返回该种类下所有数据项的列表
224
+ '''
225
+ url = f"{self._baseUri}rest/id/{str(id)}"
226
+ self._deleteItemData(url)
227
+ return self._fetchItemData(f"{self._baseUri}rest/{kind}")
228
+
229
+
230
+ def UpdateDataItem(self, kind, data):
231
+ '''
232
+ 更新kind类型对应数据项
233
+ :params: kind str类型,数据的类型
234
+ :params: data dict类型,表示添加的数据内容,其数据结构应满足对应数据项的结构要求
235
+
236
+ :return: list<dict>类型,返回该种类下所有数据项的列表
237
+ '''
238
+ url = f"{self._baseUri}rest/{kind}"
239
+ r = {
240
+ 'id': data.get('id', ''),
241
+ 'name': data.get('name', ''),
242
+ 'data': data.get('data', {}),
243
+ }
244
+ self._updateItemData(url, r)
245
+ return self._fetchItemData(url)
246
+
247
+
248
+ def GetItemList(self, kind):
249
+ '''
250
+ 获取kind类型对应所有数据项的列表
251
+ :params: kind str类型,数据的种类标识,包含:光伏、风机、燃气、水电、常规小火电、生物质发电、垃圾电厂、传输线、变压器、开关、负荷分类、负荷用户、储能配置、上网电价、输配电价、常数电价、阶梯电价、分时电价、分时阶梯电价
252
+
253
+ :return: list<dict>类型,返回该种类下所有数据项的列表
254
+ '''
255
+ assert (kind in self._kindUrlMap), "数据类型不存在"
256
+ url = f"{self._baseUri}rest/{kind}"
257
+ return self._fetchItemData(url)
258
+
259
+ class DSLabDataManageModel(DataManageModel):
260
+ _baseUri = 'api/ies/'
261
+ _weatherUrl = 'api/ies/rest/weather'
262
+ _kindUrlMap = {kind: f"api/ies/rest/{kind}" for kind in [
263
+ "光伏", "风机", "燃气", "水电", "常规小火电", "生物质发电", "垃圾电厂",
264
+ "传输线", "变压器", "开关", "负荷分类", "负荷用户", "储能配置",
265
+ "上网电价", "输配电价", "常数电价", "阶梯电价", "分时电价", "分时阶梯电价"
266
+ ]}
267
+
@@ -0,0 +1,145 @@
1
+ import json
2
+ from ..utils import request
3
+ from ..model.model import Model
4
+ from .dataManageModel import DSLabDataManageModel
5
+ from .financialAnalysisModel import DSLabFinancialAnalysisModel
6
+ from cloudpss.runner.DSLabResult import DSLabResult
7
+ from cloudpss.runner.runner import Runner
8
+ from cloudpss.runner.result import IESResult, EMTResult
9
+
10
+ class DSLab(object):
11
+ def __init__(self, project={}):
12
+ '''
13
+ 初始化
14
+ '''
15
+ self.id = project.get('id', None)
16
+ self.resource = project.get('resource', None)
17
+ self.name = project.get('name', None)
18
+ self.__modelRid = project.get('model', None)
19
+ if self.__modelRid is not None:
20
+ self.model = Model.fetch(self.__modelRid)
21
+ self.dataManageModel = DSLabDataManageModel(self.resource)
22
+ self.financialAnalysisModel = DSLabFinancialAnalysisModel(self.resource)
23
+ self.currentEvaluationResult = DSLabResult(self.resource)
24
+
25
+ @staticmethod
26
+ def fetch(simulationId):
27
+ '''
28
+ 获取算例信息
29
+
30
+ :params: simulationId string类型,代表数据项的算例id
31
+
32
+ :return: DSLab
33
+ '''
34
+ try:
35
+ r = request(
36
+ 'GET', 'api/ies/rest/simulation/{0}'.format(simulationId))
37
+ project = json.loads(r.text)
38
+ return DSLab(project)
39
+ except Exception as e:
40
+ if 'Unauthorized' in str(e):
41
+ raise Exception('token 无效')
42
+ else:
43
+ raise Exception('未查询到当前算例')
44
+
45
+ def run(self, job=None, name=None):
46
+ '''
47
+ 调用仿真
48
+
49
+ :params job: 调用仿真时使用的计算方案,不指定将使用算例保存时选中的计算方案
50
+ :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
51
+
52
+ :return: 返回一个运行实例
53
+ '''
54
+ if job is None:
55
+ currentJob = self.model.context['currentJob']
56
+ job = self.model.jobs[currentJob]
57
+
58
+ job['args']['simulationId'] = self.resource
59
+ return self.model.run(job, name=name)
60
+
61
+ def dsLabRun(self):
62
+ '''
63
+ 生成方案优选算例
64
+
65
+ :return: 方案优选运行实例
66
+ '''
67
+ pass
68
+
69
+ def dsLabFinancialRun(self, planID):
70
+ '''
71
+ 运行技术经济分析
72
+ :param planID int 类型,表示优化方案的ID,数值位于0~优化方案数量之间
73
+
74
+ :return: 技术经济分析实例
75
+
76
+ '''
77
+ return self.financialAnalysisModel.run(planID)
78
+
79
+
80
+ def runIESLoadPrediction(self,job=None,name=None, **kwargs)->Runner[IESResult]:
81
+ '''
82
+ 运行 负荷预测方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
83
+
84
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
85
+ :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
86
+
87
+ :return: runner Runner[IESResult]
88
+ '''
89
+ if job is None:
90
+ currentJob = self.model.context['currentJob']
91
+ job = self.model.jobs[currentJob]
92
+ if job['rid'] != 'job-definition/ies/ies-load-prediction':
93
+ for j in self.model.jobs:
94
+ if j['rid'] == 'job-definition/ies/ies-load-prediction':
95
+ job = j
96
+ if job is None:
97
+ raise Exception("找不到负荷预测方案内核运行的计算方案")
98
+ if job['rid'] != 'job-definition/ies/ies-load-prediction':
99
+ raise Exception("不是负荷预测方案内核运行生成算法的计算方案")
100
+ return self.run(job=job, name=name)
101
+
102
+ def runIESPowerFlow(self,job=None,name=None, **kwargs)->Runner[IESResult]:
103
+ '''
104
+ 运行 时序潮流方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
105
+
106
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
107
+ :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
108
+
109
+ :return: runner Runner[IESResult]
110
+ '''
111
+ if job is None:
112
+ currentJob = self.model.context['currentJob']
113
+ job = self.model.jobs[currentJob]
114
+ if job['rid'] != 'job-definition/ies/ies-power-flow':
115
+ for j in self.model.jobs:
116
+ if j['rid'] == 'job-definition/ies/ies-power-flow':
117
+ job = j
118
+ if job is None:
119
+ raise Exception("找不到时序潮流方案内核运行的计算方案")
120
+ if job['rid'] != 'job-definition/ies/ies-power-flow':
121
+ raise Exception("不是时序潮流方案内核运行生成算法的计算方案")
122
+ return self.run(job=job, name=name)
123
+
124
+ def runIESEnergyStoragePlan(self,job=None,name=None, **kwargs)->Runner[IESResult]:
125
+ '''
126
+ 运行 储能规划方案 内核,如果当前 model 没有创建 Job 时报错,默认使用第一个计算方案,进行仿真。
127
+
128
+ :param: job 计算方案名称,可选,字符串类型或者字典类型,默认使用第一个计算方案,如果同名使用最靠前一个
129
+ :params name: 任务名称,为空时使用项目的参数方案名称和计算方案名称
130
+
131
+ :return: runner Runner[IESResult]
132
+ '''
133
+ if job is None:
134
+ currentJob = self.model.context['currentJob']
135
+ job = self.model.jobs[currentJob]
136
+ if job['rid'] != 'job-definition/ies/ies-energy-storage-plan':
137
+ for j in self.model.jobs:
138
+ if j['rid'] == 'job-definition/ies/ies-energy-storage-plan':
139
+ job = j
140
+ if job is None:
141
+ raise Exception("找不到储能规划方案内核运行的计算方案")
142
+ if job['rid'] != 'job-definition/ies/ies-energy-storage-plan':
143
+ raise Exception("不是储能规划方案内核运行生成算法的计算方案")
144
+ return self.run(job=job, name=name)
145
+
@@ -0,0 +1,2 @@
1
+ from .files import getCurveData
2
+ __all__ = ['getCurveData']