-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvaluewithdimension.py
More file actions
443 lines (370 loc) · 13.8 KB
/
valuewithdimension.py
File metadata and controls
443 lines (370 loc) · 13.8 KB
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
from copy import deepcopy
from unittest import TestCase as tc
import re
from myfile import is_number
class SmallDimension:
"""描述某一类量纲的单位和阶数"""
def __init__(self,dim,unit):
self.dim=dim
self.order=unit
def __eq__(self, other):
"""判断阶数是否相同"""
if self.order==other.order:
return True
return False
# if self.order==0 and other.order==0:
# return True
# if self.dim==other.dim and self.order == other.order:
# return True
# return False
def __ne__(self, other):
return not self.__eq__(other)
def save_dimension_wrapper(func):
"""在对ValueWithDimension进行运算前 保存单位系统 在结束后复原单位系统"""
def wrapper(*args):
assert len(args)==2
one=args[0]
save_dimension1=one.dimension_text_exclude_order
second=args[1]
if isinstance(second,ValueWithDimension):
save_dimension2=second.dimension_text_exclude_order # 存储单位
v=func(*args)
if isinstance(second,ValueWithDimension):
second.switch_dimension(save_dimension2)
one.switch_dimension(save_dimension1)
return v
return wrapper
class ValueWithDimension:
length_dimension={'mm':1,
'cm':10,
'm':1000}
mass_dimension={'kg':1,
't':1000}
time_dimesion={'ms':1e-3,
's':1,
'min':60,
'h':3600}
force_dimesion={'N':1,
'kN':1000}
valid_dimension={'length':length_dimension,
'mass':mass_dimension,
'time':time_dimesion,
'force':force_dimesion}
dimensionless={'length':1,
'mass':1,
'time':-2,
'force':-1} # 这个字典代表1=m*kg*s^-2*N^-1
def __init__(self,value=0,*args):
"""构造用法案例"""
"""g=9.8 m*s^-2可用"""
"""g=ValueWithDimension(9.8,"m*s^-2")"""
"""g=ValueWithDimension(9.8,'m',1,'s',-2)"""
self.value=value
self.dimension={'length':SmallDimension('m',0),
'mass':SmallDimension('kg',0),
'time':SmallDimension('s',0),
'force':SmallDimension('N',0)}
if 0!=len(args) % 2:
if 1==len(args) and isinstance(args[0],str):
dtext=args[0]
dimension_setup=self.dimension_text_interpreter(dtext)
elif 1==len(args) and isinstance(args[0],ValueWithDimension):
self.value=args[0].value
self.dimension=deepcopy(args[0].dimension)
return
else:
raise Exception("必须两个一组")
else:
dimension_setup=list(args)
for i in range(0,len(dimension_setup)-1,2):
dim=dimension_setup[i]
od=dimension_setup[i+1]
self.dimension[self.dimension_interpreter(dim)].dim=dim
if self.dimension[self.dimension_interpreter(dim)].order != 0:
raise Exception("重复指定同一个量纲")
self.dimension[self.dimension_interpreter(dim)].order = od
def switch_dimension1(self,x):
"""处理给定具体单位时的字符串如 x='kg/s*m^2'"""
lst=re.split('[*/^]',x)
tmp=[x for x in lst if not x.isdigit()]
self.switch_dimension(",".join(tmp))
def switch_dimension(self,line,flag_auto=True):
"""改变单位体系"""
"""
line是字符串可以是:
kg,s,t
kg/s*t^2
flag_auto代表是否进行智能判断 开启智能判断可能会导致死循环
"""
assert isinstance(line,str)
if '*' in line or '/' in line or '^' in line:
lst = re.split('[*/^]', line)
args = [x for x in lst if not is_number(x)[0]]
else:
args = line.split(",")
# 如果有力的单位将质量设为0 如果有质量的的单位将力设为0
if True==flag_auto:
for ut in args:
if self.dimension_interpreter(ut) == 'force':
self.format(erase_dim='mass')
break
if self.dimension_interpreter(ut) == 'mass':
self.format(erase_dim='force')
break
for ut in args:
tp=self.dimension_interpreter(ut)
old_un=self.dimension[tp].dim
if old_un != ut:
self.value=self.value*(self.valid_dimension[tp][old_un]/self.valid_dimension[tp][ut])**self.dimension[tp].order
self.dimension[tp].dim=ut
def is_same_dimension(self,x):
"""判断两个数是否有相同的量纲"""
self.format()
x.format()
for k in self.dimension.keys():
if self.dimension[k]!=x.dimension[k]:
return False
return True
def format(self,erase_dim='force'):
"""由于时间 质量 长度 和力 是重复的,在一些情况下会造成麻烦 这里默认去掉力"""
assert erase_dim in self.dimension.keys()
self.switch_dimension("kg,m,s,N",False) # 先转化为默认单位
if self.dimension[erase_dim].order==0:
return
tmp=deepcopy(self.dimensionless)
scale=-1.0/tmp[erase_dim]
for k in tmp.keys():
tmp[k]=tmp[k]*scale
x=self.dimension[erase_dim].order
for k,v in self.dimension.items():
if k==erase_dim:
v.order=0
continue
v.order+=tmp[k]*x
#
#
#
#
#
#
# if self.dimension['force'].order!=0:
# if 'N'==self.dimension['force'].dim:
# self.switch_dimension("kg,m,s")
# self.dimension['length'].order+=self.dimension['force'].order
# self.dimension['mass'].order += self.dimension['force'].order
# self.dimension['time'].order -= self.dimension['force'].order*2
# self.dimension['force'].order=0
# elif 'kN'==self.dimension['force'].dim:
# self.switch_dimension("t,m,s")
# self.dimension['length'].order+=self.dimension['force'].order
# self.dimension['mass'].order += self.dimension['force'].order
# self.dimension['time'].order -= self.dimension['force'].order*2
# self.dimension['force'].order=0
# else:
# raise Exception("不应该执行到这里")
@save_dimension_wrapper
def __add__(self, other):
assert self.is_same_dimension(other)
# 单位会向第一个看齐
save_dimension=other.dimension_text_exclude_order
other.switch_dimension(self.dimension_text_exclude_order)
c=deepcopy(self)
c.value+=other.value
other.switch_dimension(save_dimension)
return c
@save_dimension_wrapper
def __sub__(self, other):
assert self.is_same_dimension(other)
# 单位会向第一个看齐
other.switch_dimension(self.dimension_text_exclude_order)
c = deepcopy(self)
c.value -= other.value
return c
@save_dimension_wrapper
def __truediv__(self, other):
c = deepcopy(self)
if isinstance(other, (float, int)):
c.value = c.value / other
return c
if isinstance(other, ValueWithDimension):
# save_dimension = other.dimension_text_exclude_order
other.switch_dimension(self.dimension_text_exclude_order) # 统一单位
c.value = self.value / other.value
for k in c.dimension.keys():
c.dimension[k].order -= other.dimension[k].order
# other.switch_dimension(save_dimension)
return c
raise Exception("类型错误")
@save_dimension_wrapper
def __mul__(self, other):
c=deepcopy(self)
if isinstance(other,(float,int)):
c.value=c.value*other
return c
if isinstance(other,ValueWithDimension):
other.switch_dimension(self.dimension_text_exclude_order) # 统一单位
c.value=self.value*other.value
for k in c.dimension.keys():
c.dimension[k].order+=other.dimension[k].order
return c
raise Exception("类型错误")
def __pow__(self, power, modulo=None):
c=deepcopy(self)
c.value=c.value**power
for v in c.dimension.values():
v.order=v.order*power
return c
@save_dimension_wrapper
def __eq__(self, other):
if isinstance(other,(float,int)):# 与数比较
if other==0 and self.value==0:
return True
tmp=[x.order for x in self.dimension.values()]
tmp=[x for x in tmp if x!=0]
if len(tmp)==0:
if other==self.value:
return True
else:
return False
else:
return False
self.format()
other.format()
other.switch_dimension(self.dimension_text_exclude_order) # 统一单位
if self.value!=other.value:
return False
for k in self.dimension.keys():
if self.dimension[k].order != other.dimension[k].order:
return False
return True
@property
def dimension_text(self):
"""返回单位字符串"""
line = [v.dim + "^%f" % v.order for v in self.dimension.values()]
line = "*".join(line)
return line
@property
def dimension_text_exclude_order(self):
"""返回字符串 不含阶数 用,连接"""
line=[v.dim for v in self.dimension.values()]
return ",".join(line)
def __str__(self):
return "%f %s"%(self.value,self.dimension_text)
@staticmethod
def dimension_interpreter(dim):
"""根据具体的单位返回单位的类别"""
# if dim in ValueWithDimension.length_dimension.keys():
# return "length"
# if dim in ValueWithDimension.mass_dimension.keys():
# return "mass"
for k,v in ValueWithDimension.valid_dimension.items():
if dim in v.keys():
return k
raise Exception("无效单位")
@staticmethod
def dimension_text_interpreter(text):
"""将单位字符串转化为可以识别的列表
返回偶数位的列表 【单位类型,阶数,。。。】
a="mm*t^2"
b="m^3*kg^1"
print(ValueWithDimension.dimension_text_interpreter(a))
print(ValueWithDimension.dimension_text_interpreter(b))"""
def script(text):
if "/" in text:#有/ 把它换成 *?^-1
p=re.compile(r"/[^/*]+")
r=p.search(text)
if r is not None:
s1,s2=r.span(0)
be=text[0:s1]
af=text[s2:]
tg=r.group(0)
if "^" not in tg:#如果没有 ^
tg=tg+"^1" #添加^
# tg[0]="*"#把 / 换成 *
tg=tg.replace("/","*")
p=re.compile(r"-?\d+\.?\d*e?-?\d*?")#识别数字(含科学计数 小数点)的pattern 任意数字
r=p.search(tg)#找到阶数
od=r.group(0)
od=-1*float(od)
tg=tg[0:r.span(0)[0]]+str(od)#组合
t=be+tg+af
return script(t)
else:
raise Exception("应该找得到")
else:
return text
text=script(text)
id1=text.split("*")
lst=[]
for v in id1:
if '^' in v:
idline = v.split('^')
assert len(idline) == 2
idline[1] = float(idline[1])
lst = lst + idline
else:
idline = [v]
idline.append(1.0)
lst = lst + idline
return lst
if __name__=="__main__":
# 以下为测试所用 不能抛出异常
# 测试开始
a=ValueWithDimension(1,"mm*kg^-1")
b=ValueWithDimension(0.5,"m*t^-1")
c=ValueWithDimension(1.5,"m*t^-1")
d=ValueWithDimension(0.5,"m^2*t^-2")
e=2
e1=ValueWithDimension(2)
f=ValueWithDimension(0.25,"mm^2*kg^-2")
assert a+b==c
assert a*b==d
assert a/b==e
assert a/b==e1
assert a-b ==b
tc.assertRaises(None,Exception,a.__add__,d)
assert b**2==f
a=ValueWithDimension(60,'s')
b=ValueWithDimension(1,"min")
assert a==b
a=ValueWithDimension(1.1,"kN")
print(a)
b=ValueWithDimension(1100,"kg*m*s^-2")
assert a==b
print(a)
a=ValueWithDimension(1.1,'N')
b=ValueWithDimension(2.2,"kg*m")
c=ValueWithDimension(0.5,"s^-2")
assert b*c==a
d=ValueWithDimension(2,"N")
e=a+d
a = ValueWithDimension(1.1, 'N')
a.switch_dimension('kg')
b= ValueWithDimension(1.1, 'kg*m*s^-2')
b.switch_dimension('N')
assert a.dimension['mass'].order==1
assert b.dimension['force'].order==1
print(a)
print(b)
a = ValueWithDimension(1001, 'N*m^-1')
a.switch_dimension('kN')
assert abs(a.value-1.001)<1e-5
print(a.value)
a = ValueWithDimension(1001, 'N*m^-1')
a.switch_dimension('kN*m^-1')
assert abs(a.value - 1.001) < 1e-5
print(a.value)
a = ValueWithDimension(1001, 'N/m*s')
a.switch_dimension('kN*m^-1')
assert abs(a.value - 1.001) < 1e-5
a = ValueWithDimension(1001, 'N/m^2*s')
a.switch_dimension('kN*m^-1')
assert abs(a.value - 1.001) < 1e-5
print(a)
a = ValueWithDimension(1001, 'N/m^2')
a.switch_dimension('kN*m^-1')
assert abs(a.value - 1.001) < 1e-5
print(a)
# print(a.value)
# 测试结束
pass