2019-11-12 14:36:23 +01:00
|
|
|
from collections import OrderedDict
|
2019-12-12 15:20:56 +01:00
|
|
|
from TexSoup import TexSoup
|
2020-08-02 16:24:48 +02:00
|
|
|
from .LaTeX import newCommand,extractMath
|
2020-02-01 16:40:11 +01:00
|
|
|
from .utils import getValFromCell,checkFloat
|
2020-04-22 17:08:45 +02:00
|
|
|
from TexSoup import TexNode,TexEnv
|
2019-11-25 11:47:24 +01:00
|
|
|
from enum import IntEnum,auto,unique,IntFlag
|
2020-07-03 14:51:12 +02:00
|
|
|
from .formats import getFormatHandlers
|
2019-11-12 14:36:23 +01:00
|
|
|
import re
|
2020-09-15 12:51:07 +02:00
|
|
|
import os
|
2019-12-12 15:20:25 +01:00
|
|
|
import numpy as np
|
2020-04-30 16:06:46 +02:00
|
|
|
import json
|
2019-11-25 11:55:25 +01:00
|
|
|
|
2019-11-12 14:36:23 +01:00
|
|
|
class state:
|
|
|
|
def __init__(self,number, multiplicity, symetry):
|
|
|
|
self.number = number
|
|
|
|
self.multiplicity = multiplicity
|
|
|
|
self.symetry = symetry
|
2020-06-20 18:30:18 +02:00
|
|
|
@staticmethod
|
|
|
|
def fromString(string):
|
|
|
|
m=re.match(r"^(?P<number>\d)\s*\^(?P<multiplicity>\d)(?P<sym>\S*)",string)
|
|
|
|
num=m.group('number')
|
|
|
|
mul=m.group('multiplicity')
|
|
|
|
sym=m.group('sym')
|
|
|
|
return state(num,mul,sym)
|
2019-11-12 14:36:23 +01:00
|
|
|
@unique
|
2020-06-18 12:27:24 +02:00
|
|
|
class DataType(IntEnum):
|
2019-11-12 14:36:23 +01:00
|
|
|
ABS=auto()
|
|
|
|
FLUO=auto()
|
2020-07-03 14:51:12 +02:00
|
|
|
|
|
|
|
def datafileSelector(dataType):
|
|
|
|
switcher={
|
|
|
|
DataType.ABS:AbsDataFile,
|
|
|
|
DataType.FLUO:FluoDataFile,
|
|
|
|
}
|
|
|
|
return switcher[dataType]
|
|
|
|
|
2020-07-08 13:31:00 +02:00
|
|
|
def getSubtablesRange(table,firstindex=2,column=0,count=1):
|
2020-07-08 13:21:40 +02:00
|
|
|
subtablesRange=list()
|
2020-07-03 14:51:12 +02:00
|
|
|
i=firstindex+count
|
|
|
|
while i<np.size(table,0):
|
|
|
|
if str(table[i,column])!="":
|
2020-07-08 13:21:40 +02:00
|
|
|
subtablesRange.append(range(firstindex,i))
|
2020-07-03 14:51:12 +02:00
|
|
|
firstindex=i
|
|
|
|
i+=count
|
|
|
|
else:
|
|
|
|
i+=1
|
2020-07-08 13:21:40 +02:00
|
|
|
subtablesRange.append(range(firstindex,np.size(table,0)))
|
|
|
|
return subtablesRange
|
2020-07-03 14:51:12 +02:00
|
|
|
|
2019-11-12 14:36:23 +01:00
|
|
|
class dataFileBase(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.molecule = ''
|
|
|
|
self.comment = ''
|
|
|
|
self.code = None
|
|
|
|
self.method = None
|
|
|
|
self.excitations = []
|
2020-09-10 09:36:07 +02:00
|
|
|
self.article = ''
|
2019-11-12 14:36:23 +01:00
|
|
|
|
2019-11-24 19:40:57 +01:00
|
|
|
@property
|
2019-11-25 14:00:17 +01:00
|
|
|
def IsTBE(self):
|
|
|
|
return self.method.name=="TBE"
|
2019-11-24 19:40:57 +01:00
|
|
|
|
2019-11-12 14:36:23 +01:00
|
|
|
@staticmethod
|
|
|
|
def GetFileType():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@staticmethod
|
2020-06-25 16:46:33 +02:00
|
|
|
def convertState(StateTablelist,initialState,default=DataType.ABS,commands=[]):
|
2019-11-12 14:36:23 +01:00
|
|
|
tmplst=[]
|
|
|
|
for TexState in StateTablelist:
|
2020-08-02 16:24:48 +02:00
|
|
|
st=str(extractMath(TexState,Soup=True,commands=commands))
|
2020-02-04 13:05:49 +01:00
|
|
|
m=re.match(r"^\^(?P<multiplicity>\d)(?P<symm>[^\s\[(]*)\s*(?:\[(?:\\mathrm{)?(?P<special>\w)(?:})\])?\s*(:?\((?P<type>[^\)]*)\))?",st)
|
2020-02-17 15:18:08 +01:00
|
|
|
mul=int(m.group("multiplicity"))
|
|
|
|
symm=m.group("symm")
|
2019-11-23 18:08:27 +01:00
|
|
|
spgrp=m.group("special")
|
|
|
|
if spgrp is not None and spgrp=="F":
|
2020-06-18 12:27:24 +02:00
|
|
|
trsp=DataType.FLUO
|
2019-11-26 14:36:23 +01:00
|
|
|
else:
|
|
|
|
trsp=default
|
|
|
|
tygrp=m.group("type")
|
2020-02-17 15:18:08 +01:00
|
|
|
tmplst.append((mul,symm,trsp,tygrp))
|
2019-11-12 14:36:23 +01:00
|
|
|
lst=[]
|
|
|
|
for index,item in enumerate(tmplst):
|
2020-06-25 16:46:33 +02:00
|
|
|
unforminitialstate=(initialState.multiplicity,initialState.symetry)
|
|
|
|
countlst=[unforminitialstate]+[(it[0],it[1]) for it in tmplst[:index+1]]
|
2020-02-17 15:18:08 +01:00
|
|
|
countitem=(item[0],item[1])
|
|
|
|
count=countlst.count(countitem)
|
|
|
|
lst.append((state(count,item[0],item[1]),item[2],item[3]))
|
2019-11-12 14:36:23 +01:00
|
|
|
return lst
|
2020-09-11 17:26:49 +02:00
|
|
|
def _OnReadMetaPair(self, key, value):
|
|
|
|
if key == "molecule":
|
|
|
|
self.molecule = value
|
|
|
|
elif key == "comment":
|
|
|
|
self.comment = value
|
|
|
|
elif key == "code":
|
|
|
|
self.code = code.fromString(value)
|
|
|
|
elif key == "method":
|
|
|
|
self.method = method.fromString(value)
|
|
|
|
elif key == "article":
|
|
|
|
self.article = value
|
|
|
|
|
2019-11-23 18:08:27 +01:00
|
|
|
@staticmethod
|
2020-06-26 14:36:35 +02:00
|
|
|
def readFromTable(table,TexOps, commands=[]):
|
2020-07-06 10:28:59 +02:00
|
|
|
for formatName,Cls in getFormatHandlers():
|
2020-07-03 14:51:12 +02:00
|
|
|
if formatName.lower()==TexOps.format.lower():
|
|
|
|
handler=Cls(TexOps,commands)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise ValueError()
|
|
|
|
return handler.readFromTable(table)
|
2019-11-12 14:36:23 +01:00
|
|
|
def getMetadata(self):
|
|
|
|
dic=OrderedDict()
|
|
|
|
dic["Molecule"]=self.molecule
|
|
|
|
dic["Comment"]=self.comment
|
2019-11-12 15:20:54 +01:00
|
|
|
dic["code"]="" if self.code is None else self.code.toDataString()
|
|
|
|
dic["method"]="" if self.method is None else self.method.toDataString()
|
2020-09-10 09:36:07 +02:00
|
|
|
dic["article"]="" if self.article is None else self.article
|
2019-11-12 14:36:23 +01:00
|
|
|
return dic
|
|
|
|
|
2020-09-11 17:26:49 +02:00
|
|
|
def _OnReadMeta(self,line,dataType):
|
|
|
|
#get key value
|
|
|
|
match = dataFileBase._GetMetaRexEx().match(line)
|
|
|
|
# normalize key to lower
|
|
|
|
key = match.group(1).lower()
|
|
|
|
# if data has value
|
|
|
|
if match.group(2):
|
|
|
|
val = match.group(2)
|
|
|
|
self._OnReadMetaPair(key, val)
|
|
|
|
@staticmethod
|
|
|
|
def _OnReadRow(line):
|
|
|
|
vals = re.findall(r"\([^\)]+\)|\S+",line)
|
|
|
|
start = state(int(vals[0]), int(vals[1]), vals[2])
|
|
|
|
end = state(int(vals[3]), int(vals[4]), vals[5])
|
|
|
|
Type = vals[6] if (len(vals) >= 7) else None
|
|
|
|
if Type == "_":
|
|
|
|
Type = None
|
|
|
|
if Type:
|
|
|
|
m = re.match(r"^\(([^\)]*)\)$",Type)
|
|
|
|
if m:
|
|
|
|
Type = m[1]
|
|
|
|
val = vals[7] if len(vals) >= 8 else str(np.NaN)
|
|
|
|
T1 = vals[8] if len(vals) >= 9 else str(np.NaN)
|
|
|
|
oscilatorForces = vals[9] if len(vals) >= 10 else str(np.NaN)
|
|
|
|
isUnsafe = vals[10] == json.dumps(True) if len(vals) >= 11 else False
|
|
|
|
ex = excitationValue(start, end, val, Type,T1,isUnsafe,oscilatorForces)
|
|
|
|
return ex
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _GetMetaRexEx():
|
|
|
|
#metadata RegExp (start with #; maybe somme spaces; : ; maybe somme space; datas)
|
|
|
|
return re.compile(r"^#\s*([A-Za-z_]+)\s*:\s*(.*)$")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def readFile(stream,dataType):
|
|
|
|
lines = stream.readlines()
|
|
|
|
# for each line with metadata
|
|
|
|
ismetaArea = True
|
|
|
|
dat = datafileSelector(dataType)()
|
|
|
|
for line in lines:
|
|
|
|
#if it's not empty line
|
|
|
|
line = line.strip()
|
|
|
|
if line:
|
|
|
|
# if # may be metadata or comment
|
|
|
|
if line[0] == "#":
|
|
|
|
# if it's metadata
|
|
|
|
if ismetaArea and dataFileBase._GetMetaRexEx().match(line):
|
|
|
|
dat._OnReadMeta(line,dataType)
|
|
|
|
else: # else its row
|
|
|
|
ismetaArea = False
|
|
|
|
dat.excitations.append(dat._OnReadRow(line))
|
|
|
|
return dat
|
|
|
|
|
2020-06-17 13:19:44 +02:00
|
|
|
def toFile(self,datadir,suffix=None):
|
2019-11-12 14:36:23 +01:00
|
|
|
subpath=datadir/self.GetFileType().name.lower()
|
|
|
|
if not subpath.exists():
|
2020-09-15 12:51:07 +02:00
|
|
|
os.makedirs(str(subpath))
|
2020-04-22 17:08:45 +02:00
|
|
|
molsoup=TexSoup(self.molecule)
|
|
|
|
molcomp=list(molsoup.contents)[0]
|
|
|
|
molfilename=self.molecule if isinstance(molcomp,str) else molcomp.args[0].value
|
2020-08-12 18:13:12 +02:00
|
|
|
molfilename=molfilename.lower()
|
2020-04-22 17:08:45 +02:00
|
|
|
fileNameComp=[molfilename,self.method.name]
|
2020-03-28 13:00:17 +01:00
|
|
|
if self.method.basis:
|
|
|
|
fileNameComp.append(self.method.basis)
|
2020-06-17 13:19:44 +02:00
|
|
|
if suffix:
|
|
|
|
fileNameComp.append(suffix)
|
2020-08-12 18:13:12 +02:00
|
|
|
fileName="_".join(fileNameComp).replace(" ","_")+".dat"
|
2019-12-09 13:34:30 +01:00
|
|
|
file=subpath/fileName
|
2019-11-12 14:36:23 +01:00
|
|
|
if not file.exists():
|
|
|
|
with file.open("w") as f:
|
|
|
|
for key,value in self.getMetadata().items():
|
|
|
|
if value is not None:
|
2019-11-12 15:20:54 +01:00
|
|
|
f.write("# {:9s}: {}\n".format(key,value))
|
2019-11-12 14:36:23 +01:00
|
|
|
f.write("""
|
2019-12-14 12:25:56 +01:00
|
|
|
# Initial state Final state Transition Energies (eV) %T1 Oscilator forces unsafe
|
|
|
|
####################### ####################### ######################################## ############# ####### ################### ##############
|
|
|
|
# Number Spin Symm Number Spin Symm type E_{:5s} %T1 f is unsafe\n""".format(self.GetFileType().name.lower()))
|
2019-11-12 14:36:23 +01:00
|
|
|
for ex in self.excitations:
|
2020-03-27 14:35:01 +01:00
|
|
|
mystr=" {:7s} {:5s} {:10s} {:7s} {:5s} {:12s} {:39s} {:13s} {:14s} {:13s}{}\n".format(
|
2019-12-14 17:16:54 +01:00
|
|
|
str(ex.initial.number),
|
|
|
|
str(ex.initial.multiplicity),
|
|
|
|
ex.initial.symetry,
|
|
|
|
str(ex.final.number),
|
|
|
|
str(ex.final.multiplicity),
|
2019-12-16 15:50:39 +01:00
|
|
|
ex.final.symetry,"("+str(ex.type)+")" if ex.type is not None else "_",
|
2019-12-14 17:16:54 +01:00
|
|
|
str(ex.value) if ex.value is not None else "_",
|
|
|
|
str(ex.T1) if ex.T1 is not None else "_",
|
|
|
|
str(ex.oscilatorForces) if ex.oscilatorForces is not None else "_",
|
2020-04-30 16:06:46 +02:00
|
|
|
json.dumps(ex.isUnsafe))
|
2019-11-12 14:36:23 +01:00
|
|
|
f.write(mystr)
|
|
|
|
class method:
|
2019-11-20 20:15:53 +01:00
|
|
|
def __init__(self,name, *args):
|
2019-11-12 14:36:23 +01:00
|
|
|
self.name = name
|
2019-11-20 20:15:53 +01:00
|
|
|
self.basis=args[0] if len(args)>0 else None
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fromString(string):
|
|
|
|
vals = string.split(",")
|
2019-11-20 20:15:53 +01:00
|
|
|
return method(*vals)
|
|
|
|
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
string = self.name
|
|
|
|
if (self.basis):
|
|
|
|
string+= '/' + self.basis
|
|
|
|
return string
|
|
|
|
|
|
|
|
def toDataString(self):
|
|
|
|
string=self.name
|
|
|
|
if (self.basis):
|
|
|
|
string+=","+self.basis
|
2019-12-09 13:34:30 +01:00
|
|
|
return string
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
class code:
|
|
|
|
def __init__(self,name, version):
|
|
|
|
self.name = name
|
|
|
|
self.version = version
|
|
|
|
|
2020-09-11 17:26:49 +02:00
|
|
|
@staticmethod
|
|
|
|
def fromString(string):
|
|
|
|
vals = string.split(",")
|
2020-09-15 15:31:17 +02:00
|
|
|
return code(*vals)
|
2020-09-11 17:26:49 +02:00
|
|
|
|
2019-11-12 14:36:23 +01:00
|
|
|
def toDataString(self):
|
|
|
|
string=self.name
|
|
|
|
if (self.version):
|
|
|
|
string+=","+self.version
|
|
|
|
return string
|
|
|
|
|
|
|
|
class oneStateDataFileBase(dataFileBase):
|
|
|
|
def __init__(self):
|
|
|
|
super(oneStateDataFileBase,self).__init__()
|
|
|
|
self.geometry = None
|
2020-09-11 17:26:49 +02:00
|
|
|
|
|
|
|
def _OnReadMetaPair(self, key, value):
|
|
|
|
if key == "geom":
|
|
|
|
self.geometry = method.fromString(value)
|
|
|
|
else:
|
|
|
|
super(oneStateDataFileBase,self)._OnReadMetaPair(key, value)
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
def getMetadata(self):
|
|
|
|
dic=super(oneStateDataFileBase,self).getMetadata()
|
2019-11-12 15:20:54 +01:00
|
|
|
dic["geom"]= "" if self.geometry is None else self.geometry.toDataString()
|
2020-09-10 15:08:41 +02:00
|
|
|
dic.move_to_end("article")
|
2019-11-12 14:36:23 +01:00
|
|
|
return dic
|
|
|
|
|
|
|
|
class AbsDataFile(oneStateDataFileBase):
|
|
|
|
def __init__(self):
|
|
|
|
super(AbsDataFile,self).__init__()
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def GetFileType():
|
2020-06-18 12:27:24 +02:00
|
|
|
return DataType.ABS
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
class FluoDataFile(oneStateDataFileBase):
|
|
|
|
def __init__(self):
|
|
|
|
super(FluoDataFile,self).__init__()
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def GetFileType():
|
2020-06-18 12:27:24 +02:00
|
|
|
return DataType.FLUO
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
|
|
|
|
class excitationBase:
|
2020-04-21 17:43:25 +02:00
|
|
|
def __init__(self,initial, final,type=None, T1=None,isUnsafe=False):
|
2019-11-12 14:36:23 +01:00
|
|
|
self.initial = initial
|
|
|
|
self.final = final
|
2020-04-21 17:43:25 +02:00
|
|
|
self.type = type
|
|
|
|
self.T1 = T1
|
|
|
|
self.isUnsafe = isUnsafe
|
2019-11-12 14:36:23 +01:00
|
|
|
|
|
|
|
class excitationValue(excitationBase):
|
2020-04-21 17:43:25 +02:00
|
|
|
def __init__(self,initial, final, value, type=None, T1=None,isUnsafe=False,oscilatorForces=None):
|
2020-08-06 18:24:42 +02:00
|
|
|
super(excitationValue,self).__init__(initial, final,type=type,T1=T1,isUnsafe=isUnsafe)
|
2019-11-24 19:40:57 +01:00
|
|
|
self.value = value
|
2020-04-21 17:43:25 +02:00
|
|
|
self.oscilatorForces = oscilatorForces
|