Skip to content

Commit b77dfcb

Browse files
committed
Open FRD only once, fixed modal results conversion
1 parent e360cfa commit b77dfcb

File tree

4 files changed

+1360
-929
lines changed

4 files changed

+1360
-929
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,18 @@ Read about VTK [file formats](https://vtk.org/wp-content/uploads/2015/04/file-fo
185185

186186
# TODO
187187

188+
Log memory consumption.
189+
190+
Which file is written faster: VTK or VTU? VTK is twice faster?
191+
192+
Use threads for convertion and writing. Read FRD in the main thread.
193+
188194
Read DAT files: it would be a killer feature if Paraview could visualize results in Gauss points. Use [CCXStressReader](https://github.com/Mote3D/CCXStressReader).
189195

190196
Multiprocessing for tests.
191197

192198
Read binary .frd files.
193199

194-
Open FRD only once.
195-
196200
Contribute to meshio. FRD writer. Use meshio XDMF writer: https://github.com/calculix/ccx2paraview/issues/6
197201

198202
Include an integer scalar containing each element’s GROUP or MATERIAL.

ccx2paraview.py

Lines changed: 105 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,11 @@ def __init__(self, node_block, elem_block, result_blocks=[]):
343343
self.elem_block = elem_block
344344
self.result_blocks = result_blocks
345345

346-
def fill_point_data(self, inc):
346+
def fill_point_data(self, step, inc):
347347
# POINT DATA - from here start all the results
348348
pd = self.ugrid.GetPointData()
349349
for b in self.result_blocks: # iterate over NodalResultsBlock (increments)
350-
if b.inc == inc: # write results for one time increment only
350+
if b.inc == inc and b.step == step: # write results for one time increment only
351351
if len(b.results) and len(b.components):
352352
da = convert_frd_data_to_vtk(b, self.node_block.numnod)
353353
pd.AddArray(da)
@@ -512,9 +512,9 @@ def read_vars_info(self):
512512
'PE':'PEEQ',
513513
}
514514
self.name = match.group(1) # dataset name
515-
# txt = 'Vars info: name {}, ncomps {}' \
516-
# .format(self.name, self.ncomps)
517-
# logging.debug(txt)
515+
txt = 'Vars info: name {}, ncomps {}' \
516+
.format(self.name, self.ncomps)
517+
logging.debug(txt)
518518
if self.name in inpname:
519519
self.name = inpname[self.name]
520520

@@ -643,33 +643,37 @@ class FRD:
643643
To implement large file parsing we need a step-by-step reader and writer.
644644
"""
645645

646-
def __init__(self, file_name):
646+
def __init__(self, in_file):
647647
"""Read contents of the .frd file."""
648-
self.file_name = file_name # path to the .frd-file to be read
648+
self.in_file = in_file # path to the .frd-file to be read
649649
self.node_block = None # node block
650650
self.elem_block = None # elements block
651651
self.result_blocks = [] # all result blocks in order of appearance
652-
self.increments = set()
652+
self.steps_increments = [] # [(step, inc), ]
653653

654654
def parse_mesh(self):
655-
with open(self.file_name, 'r') as in_file:
656-
while True:
657-
line = in_file.readline()
658-
if not line:
659-
break
660-
key = line[:5].strip()
655+
while True:
656+
line = self.in_file.readline()
657+
if not line:
658+
break
659+
key = line[:5].strip()
661660

662-
# Nodes
663-
if key == '2':
664-
self.node_block = NodalPointCoordinateBlock(in_file)
661+
# Nodes
662+
if key == '2':
663+
self.node_block = NodalPointCoordinateBlock(self.in_file)
665664

666-
# Elements
667-
elif key == '3':
668-
self.elem_block = ElementDefinitionBlock(in_file)
665+
# Elements
666+
elif key == '3':
667+
self.elem_block = ElementDefinitionBlock(self.in_file)
669668

670-
# End
671-
elif key == '9999':
672-
break
669+
# Results
670+
if key == '100':
671+
self.in_file.seek(self.in_file.tell() - len(line)) # go up one line
672+
break
673+
674+
# End
675+
elif key == '9999':
676+
break
673677

674678
return self.node_block, self.elem_block
675679

@@ -678,64 +682,73 @@ def count_increments(self):
678682
It is assumed, that there is constant set of variables calculated at
679683
each time increment.
680684
"""
681-
with open(self.file_name, 'r') as in_file:
682-
while True:
683-
line = in_file.readline()
684-
if not line:
685-
break
686-
key = line[:5].strip()
687-
# Results
688-
if key == '100':
689-
inc = get_inc_step(line)[0]
690-
self.increments.add(inc)
691-
# End
692-
elif key == '9999':
693-
break
685+
init_pos = self.in_file.tell()
686+
while True:
687+
line = self.in_file.readline()
688+
if not line:
689+
break
690+
key = line[:5].strip()
691+
# Results
692+
if key == '100':
693+
inc, step = get_inc_step(line)
694+
if (step, inc) not in self.steps_increments:
695+
self.steps_increments.append((step, inc))
696+
# End
697+
elif key == '9999':
698+
break
699+
self.in_file.seek(init_pos)
694700

695-
i = len(self.increments)
701+
i = len(self.steps_increments)
696702
if i:
697703
msg = '{} time increment{}'.format(i, 's'*min(1, i-1))
698704
logging.info(msg)
705+
logging.debug('Steps-increments: {}'.format(self.steps_increments))
699706
else:
700707
logging.warning('No time increments!')
701708
return i
702709

703-
def parse_results(self, inc):
710+
def parse_results(self, step, inc):
704711
"""Header: key == '1' or key == '1P'."""
705712
self.result_blocks.clear()
706-
with open(self.file_name, 'r') as in_file:
707-
while True:
708-
line = in_file.readline()
709-
if not line:
710-
break
711-
key = line[:5].strip()
712-
713-
# Read results for certain time increment
714-
if key == '100':
715-
if inc != get_inc_step(line)[0]:
716-
continue
717-
718-
b = NodalResultsBlock(line)
719-
b.run(in_file, self.node_block)
720-
self.result_blocks.append(b)
721-
b.print_some_log()
722-
if b.name == 'S':
723-
b1 = self.calculate_mises_stress(b)
724-
b2 = self.calculate_principal(b)
725-
self.result_blocks.extend([b1, b2])
726-
b1.print_some_log()
727-
b2.print_some_log()
728-
if b.name == 'E':
729-
b1 = self.calculate_mises_strain(b)
730-
b2 = self.calculate_principal(b)
731-
self.result_blocks.extend([b1, b2])
732-
b1.print_some_log()
733-
b2.print_some_log()
734-
735-
# End
736-
elif key == '9999':
713+
while True:
714+
line = self.in_file.readline()
715+
if not line:
716+
break
717+
key = line[:5].strip()
718+
719+
# Read results for certain time increment
720+
if key == '100':
721+
logging.debug('line: ' + line)
722+
got_inc, got_step = get_inc_step(line)
723+
if inc != got_inc:
724+
logging.debug('{} != {}'.format(inc, got_inc))
725+
if step != got_step:
726+
logging.debug('{} != {}'.format(step, got_step))
727+
if inc != got_inc or step != got_step:
728+
self.in_file.seek(self.in_file.tell() - len(line)) # go up one line
737729
break
738730

731+
b = NodalResultsBlock(line)
732+
b.run(self.in_file, self.node_block)
733+
self.result_blocks.append(b)
734+
b.print_some_log()
735+
if b.name == 'S':
736+
b1 = self.calculate_mises_stress(b)
737+
b2 = self.calculate_principal(b)
738+
self.result_blocks.extend([b1, b2])
739+
b1.print_some_log()
740+
b2.print_some_log()
741+
if b.name == 'E':
742+
b1 = self.calculate_mises_strain(b)
743+
b2 = self.calculate_principal(b)
744+
self.result_blocks.extend([b1, b2])
745+
b1.print_some_log()
746+
b2.print_some_log()
747+
748+
# End
749+
elif key == '9999':
750+
break
751+
739752
return self.result_blocks
740753

741754
def calculate_mises_stress(self, b):
@@ -835,9 +848,8 @@ def get_inc_step(line):
835848
match = match_line(regex, line)
836849
inc = float(match.group(1)) # could be frequency, time or any numerical value
837850
step = int(match.group(2)) # step number
838-
# txt = 'Step info: value {}, step {}' \
839-
# .format(inc, step)
840-
# logging.debug(txt)
851+
txt = 'Step info: value {}, step {}'.format(inc, step)
852+
logging.debug(txt)
841853
return inc, step
842854

843855

@@ -859,12 +871,12 @@ def match_line(regex, line):
859871
"""Utility functions."""
860872

861873

862-
def screen():
874+
def clean_screen():
863875
"""Clean screen."""
864876
os.system('cls' if os.name=='nt' else 'clear')
865877

866878

867-
def cache(folder=None):
879+
def clean_cache(folder=None):
868880
"""Recursively delete cached files in all subfolders."""
869881
if folder is None:
870882
folder = os.getcwd()
@@ -876,12 +888,12 @@ def cache(folder=None):
876888
try:
877889
for f in os.scandir(folder):
878890
if f.is_dir():
879-
cache(f.path)
891+
clean_cache(f.path)
880892
except PermissionError:
881893
logging.error('Insufficient permissions for ' + folder)
882894

883895

884-
def results(folder=None):
896+
def clean_results(folder=None):
885897
"""Cleaup old result files."""
886898
if folder is None:
887899
folder = os.getcwd()
@@ -909,10 +921,11 @@ class Converter:
909921
def __init__(self, frd_file_name, fmt_list):
910922
self.frd_file_name = frd_file_name
911923
self.fmt_list = [fmt.lower() for fmt in fmt_list]
912-
self.frd = FRD(self.frd_file_name)
913924

914925
def run(self):
915926
logging.info('Reading ' + os.path.basename(self.frd_file_name))
927+
in_file = open(self.frd_file_name, 'r')
928+
self.frd = FRD(in_file)
916929

917930
# Check if file contains mesh data
918931
node_block, elem_block = self.frd.parse_mesh()
@@ -931,33 +944,36 @@ def run(self):
931944
"""For each time increment generate separate .vt* file
932945
Output file name will be the same as input but with increment time.
933946
"""
934-
for inc, file_name in self.inc_filenames():
935-
result_blocks = self.frd.parse_results(inc)
947+
for step, inc, file_name in self.steps_inc_filenames():
948+
logging.debug('step {}, inc {}'.format(step, inc))
949+
result_blocks = self.frd.parse_results(step, inc)
936950
w = Writer(node_block, elem_block, result_blocks)
937-
w.fill_point_data(inc)
951+
w.fill_point_data(step, inc)
938952
for fmt in self.fmt_list: # ['vtk', 'vtu']
939953
w.write_file(file_name + fmt)
940954

941955
# Write ParaView Data (PVD) for series of VTU files
942956
if i > 1 and 'vtu' in self.fmt_list:
943957
self.write_pvd()
944958

945-
def inc_filenames(self):
959+
in_file.close()
960+
961+
def steps_inc_filenames(self):
946962
"""If model has many time increments - many output files
947963
will be created. Each output file's name should contain
948964
increment number padded with zero. In this method file_name
949965
has no extension.
950966
"""
951-
i = len(self.frd.increments)
952-
d = {} # {increment time: file name}
953-
for counter,t in enumerate(sorted(self.frd.increments)):
967+
i = len(self.frd.steps_increments)
968+
d = [] # [(step, inc, file name), ]
969+
for counter, (step, inc) in enumerate(self.frd.steps_increments):
954970
if i > 1:
955971
num = '.{:0{width}}.'.format(counter+1, width=len(str(i)))
956972
else:
957973
num = '.'
958974
file_name = self.frd_file_name[:-4] + num
959-
d[t] = file_name # without extension
960-
return sorted(d.items())
975+
d.append((step, inc, file_name)) # without extension
976+
return d
961977

962978
def write_pvd(self):
963979
"""Writes ParaView Data (PVD) file for series of VTU files."""
@@ -966,7 +982,7 @@ def write_pvd(self):
966982
f.write('<VTKFile type="Collection" version="0.1" byte_order="LittleEndian">\n')
967983
f.write('\t<Collection>\n')
968984

969-
for inc, file_name in self.inc_filenames():
985+
for step, inc, file_name in self.steps_inc_filenames():
970986
f.write('\t\t<DataSet file="{}vtu" timestep="{}"/>\n'\
971987
.format(os.path.basename(file_name), inc))
972988

@@ -996,6 +1012,6 @@ def main():
9961012

9971013

9981014
if __name__ == '__main__':
999-
screen()
1015+
clean_screen()
10001016
main()
1001-
cache()
1017+
clean_cache()

0 commit comments

Comments
 (0)