55from pandas import DataFrame , read_csv
66
77from os import remove
8- from re import search , match
8+ from re import search , match , findall
9+ from json import dumps
910
1011from typing import List , Union
1112
1213from .exceptions import ParseException
13- from .inputs import Object , CV , FL
14+ from .inputs import Object , CV , FL , CF
1415from .constants import CV_KEYS
1516
1617
@@ -24,6 +25,7 @@ def __init__(self, filename: str):
2425
2526 self ._cv_list = self ._read_cvs ()
2627 self ._fl_list = self ._read_fls ()
28+ self ._cf_list = self ._read_cfs ()
2729
2830#------------------------ OBJECT MANIPULATION TOOLS -----------------------#
2931
@@ -40,6 +42,8 @@ def _read_object(self, id_regex: str) -> List[Object]:
4042 objs .append (self .get_cv (id .group ()[:- 2 ]))
4143 elif 'FL' in id_regex :
4244 objs .append (self .get_fl (id .group ()[:- 2 ]))
45+ elif 'CF' in id_regex :
46+ objs .append (self .get_cf (id .group ()[:- 2 ]))
4347 return objs
4448
4549 def _read_cvs (self ) -> List [CV ]:
@@ -53,6 +57,12 @@ def _read_fls(self) -> List[FL]:
5357 Looks for FLs in the input file and returns them as a list of FL objects.
5458 '''
5559 return self ._read_object (r'\bFL\d{3}00\b' )
60+
61+ def _read_cfs (self ) -> List [CF ]:
62+ '''
63+ Looks for CFs in the input file and returns them as a list of CF objects.
64+ '''
65+ return self ._read_object (r'\bCF\d{3,8}00\b' )
5666
5767 def get_cv (self , cv_id : str ) -> CV :
5868 '''
@@ -183,6 +193,64 @@ def get_fl(self, fl_id: str) -> FL:
183193
184194 return FL (fl_data )
185195
196+ def get_cf (self , cf_id : str ) -> CF :
197+ '''
198+ Searches for a CF in the input file and returns it as a CF object.
199+ '''
200+ cf_data = {}
201+
202+ arg_c = 0 # arg counter
203+
204+ with open (self ._filename , 'r' ) as file :
205+ for line in file :
206+ if line .startswith (cf_id ):
207+ record = line .split ()
208+ record_id = record [0 ]
209+ record_data = {}
210+
211+ termination = record_id [- 2 :]
212+
213+ try :
214+ if termination == '00' :
215+ record_data ['CFNAME' ] = record [1 ]
216+ record_data ['CFTYPE' ] = record [2 ]
217+ record_data ['NCFARG' ] = record [3 ]
218+ record_data ['CFSCAL' ] = record [4 ]
219+ record_data ['CFADCN' ] = record [5 ] if len (record ) > 5 else 0.0
220+ elif termination == '01' :
221+ if record [1 ] in ['.TRUE.' , '.FALSE.' ]:
222+ record_data ['LCFVAL' ] = record [1 ]
223+ else :
224+ record_data ['CFVALR' ] = record [1 ]
225+ elif termination == '02' :
226+ record_data ['ICFLIM' ] = record [1 ]
227+ if int (record [1 ]) in [1 , 2 , 3 ]:
228+ record_data ['CFLIML' ] = record [2 ]
229+ if int (record [1 ]) in [2 , 3 ]:
230+ record_data ['CFLIMU' ] = record [3 ]
231+ elif match (r'0[3-4]' , termination ):
232+ record_data ['FIELDS' ] = record [1 ] # Fixable
233+ elif termination == '05' :
234+ record_data ['CLASS' ] = record [1 ]
235+ elif termination == '06' :
236+ record_data ['MSGFIL' ] = record [1 ]
237+ if int (record [1 ]) in [1 , 2 ]:
238+ record_data ['SWTMSG' ] = record [2 ]
239+ elif match (r'[10-99]' , termination ):
240+ record_data ['ARSCAL_' + str (arg_c )] = record [1 ]
241+ record_data ['ARADCN_' + str (arg_c )] = record [2 ]
242+ record_data ['CHARG_' + str (arg_c )] = record [3 ]
243+ arg_c += 1
244+ else :
245+ raise ParseException (
246+ cf_id , f'Unknown record: { record_id } ' )
247+ except :
248+ raise ParseException (
249+ cf_id , f'Invalid number of attributes for record { record_id } ' )
250+ cf_data [record_id ] = record_data
251+
252+ return CF (cf_data )
253+
186254 def get_cv_list (self ) -> List [CV ]:
187255 '''
188256 Return the list of CVs in parsed file.
@@ -194,6 +262,12 @@ def get_fl_list(self) -> List[FL]:
194262 Return the list of CVs in parsed file.
195263 '''
196264 return self ._fl_list
265+
266+ def get_cf_list (self ) -> List [CF ]:
267+ '''
268+ Return the list of CFs in parsed file.
269+ '''
270+ return self ._cf_list
197271
198272 def remove_object (self , obj_id : str , src_file : str = None , new_file : str = None ) -> None :
199273 '''
@@ -376,6 +450,37 @@ def get_connected_cvs(self, cv_id: str) -> List[CV]:
376450 cv_connected .append (self .id_search (
377451 self ._cv_list , 'CV' + fl .get_field ('KCVFM' )))
378452 return cv_connected
453+
454+
455+ def get_connected_cfs (self , obj_id : str ) -> List [CF ]:
456+ '''
457+ Get those CFs directly or indirectly connected to a given Object.
458+ - If the Object is an FL, the CF associated with the CFnnnTk record is returned and, recursively, all CFs on which this CF depends are added.
459+ - If the object is a CF, that CF and its dependencies are returned.
460+ '''
461+ cf_connected = []
462+
463+ # Get a CF specified in the FL Tk record
464+ if obj_id .startswith ('FL' ):
465+ fl = self .get_fl (obj_id )
466+ for key in fl .records .keys ():
467+ if match (obj_id + r'T[0-9]$' , key ) and fl .records [key ]['NTFLAG' ] == '2' :
468+ cf_id = 'CF' + fl .records [key ]['NFUN' ]
469+ cf_connected .append (self .get_cf (cf_id ))
470+ # Recursion for interdependent CFs
471+ cf_connected += self .get_connected_cfs (cf_id )
472+ # Get those CFs related to a given CF
473+ elif obj_id .startswith ('CF' ):
474+ cf = self .get_cf (obj_id )
475+ cf_values = findall (r'\bCFVALU\.\d+\b' , dumps (cf .records ))
476+ for value in cf_values :
477+ dot_pos = value .find ('.' )
478+ cf_id = 'CF' + value [dot_pos + 1 :]
479+ cf_connected .append (self .get_cf (cf_id ))
480+ cf_connected += self .get_connected_cfs (cf_id )
481+
482+ return cf_connected
483+
379484
380485 def create_submodel (self , cv_id : str , new_file : str = None ) -> Union [List [CV ], List [FL ]]:
381486 '''
0 commit comments