"""
mshreader.py is a script to process the msh file into the data required for
SolverInput object for a general 2D structures.
"""
import sys
import os
import ast
import numpy as np
[docs]class MshProcessor:
"""
MshProcessor reads in a msh file and convert it into txt files. It also
includes methods that is used interactively in the script.
"""
def __init__(self, filename, show_obj_msg=True):
self. show_obj_msg = show_obj_msg
self.entity_dict = {}
self.is_file_valid = False # used to do simple check of the file
self._read_msh(filename)
self.bc = []
self.bcnh = []
self.nf = []
self.has_property = False
self.bound_con = None
self.bound_con_nonhomo = None
self.properties = None
self.materials = None
def _read_msh(self, filename):
node_summary = {"numEntityBlocks" : 0,
"numNodes" : 0,
"minNodeTag" : 0,
"maxNodeTag" : 0}
in_nodes_section = False
in_nodes_summary = False
in_node_entity = False
in_node_tag = False
in_node_coord = False
in_node_tag = False
current_entityDim = -1
current_entity_ctr = 0
entity_stop_ctr = 0
# this info can later be used to assign the boundary condition
entity_dict = {}
nodal_tag_list = []
in_elements_section = False
in_elements_summary = False
in_elements_entity = False
in_element_tag = False
skip_elements = True
element_ctr = 0
element_ctr = 0
element_list = []
# check if the file contains $Node label and #Elements label
status_check = [0, 0]
self.is_file_valid = False
for line in open(filename, 'r'):
if line.strip() == "$Nodes":
in_nodes_section = True
in_nodes_summary = True
status_check[0] = 1
continue
if line.strip() == "$EndNodes":
in_nodes_section = False
continue
if in_nodes_section:
if in_nodes_summary:
str_list = line.strip().split()
num_list = list(map(int, str_list))
(node_summary["numEntityBlocks"], node_summary["numNodes"],
node_summary["minNodeTag"], node_summary["maxNodeTag"]) \
= num_list
nodal_coord = np.zeros((node_summary["numNodes"], 2))
nodal_data_ctr = 0
in_node_entity = True
in_nodes_summary = False
continue
if in_node_entity:
entity_info_list = list(map(int, line.strip().split()))
entity_dict[(entity_info_list[0], entity_info_list[1])] \
= []
entity_stop_ctr = entity_info_list[3] - 1
in_node_entity = False
in_node_tag = True
continue
if in_node_tag:
nodal_tag_list.append(int(line.strip()))
entity_dict[(entity_info_list[0], entity_info_list[1])] \
.append(int(line.strip()))
if current_entity_ctr < entity_stop_ctr:
current_entity_ctr += 1
elif current_entity_ctr == entity_stop_ctr:
current_entity_ctr = 0
in_node_tag = False
in_node_coord = True
continue
if in_node_coord:
nodal_coord[nodal_data_ctr, :] \
= list(map(float, line.strip().split()[0:2]))
nodal_data_ctr += 1
if current_entity_ctr < entity_stop_ctr:
current_entity_ctr += 1
elif current_entity_ctr == entity_stop_ctr:
current_entity_ctr = 0
in_node_coord = False
in_node_entity = True
continue
if line.strip() == "$Elements":
in_elements_section = True
in_elements_summary = True
status_check[1] = 1
continue
if line.strip() == "$EndElements":
in_elements_section = False
continue
if in_elements_section:
if in_elements_summary:
in_elements_entity = True
in_elements_summary = False
continue
if in_elements_entity:
entity_info_list = list(map(int, line.strip().split()))
# skipped all non 2D elements
if entity_info_list[2] != 2 \
and entity_info_list[2] != 3:
skip_elements = True
element_ctr = 0
elements_in_block = entity_info_list[3]
in_elements_entity = False
in_element_tag = True
continue
if in_element_tag:
if not skip_elements:
current_list = list(map(float, line.strip().split()))
current_list.append(1) # include property label
element_list \
.append(current_list)
element_ctr += 1
if element_ctr == elements_in_block:
in_element_tag = False
in_elements_entity = True
skip_elements = False
element_ctr = 0
continue
if 0 not in status_check:
self.is_file_valid = True
# 2D only - ignored z coordinate data
self.nodal_data = np.zeros((len(nodal_tag_list), 3))
self.nodal_data[:,0] = np.asarray(nodal_tag_list)
self.nodal_data[:,1:3] = np.asarray(nodal_coord)
self.element_data = np.asarray(element_list)
self.entity_dict = entity_dict
[docs] def add_bc(self, method_label, input_list_label,
node_entity_tags, x_tag, y_tag):
if input_list_label.lower() == 'bc':
if x_tag not in [0,1] or y_tag not in [0,1]:
print("Invalid x_tag or y_tag value - only 0 or 1 can be used.")
return
input_list = self.bc
elif input_list_label.lower() == 'bcnh':
input_list = self.bcnh
elif input_list_label.lower() == 'nf':
input_list = self.nf
else:
print("Invalid input list label. Use 'bc', 'bcnh', or 'nf'.")
return
if method_label == 'n': # add by list of node tags
self._node_assignments(node_entity_tags, input_list,
x_tag, y_tag, 'bound_con')
if method_label == 'e': # add by entity tuples
if type(node_entity_tags) == tuple and len(node_entity_tags) == 2:
if node_entity_tags in self.entity_dict:
node_tags = self.entity_dict[node_entity_tags]
self._node_assignments(node_tags, input_list, x_tag, y_tag,
'bound_con')
else:
print("Given entity tuple does not exists.")
return
elif type(node_entity_tags) == list:
for tuple_ in node_entity_tags:
node_tags = self.entity_dict[tuple_]
self._node_assignments(node_tags, input_list, x_tag, y_tag,
'bound_con')
else:
print(node_entity_tags, type(node_entity_tags))
print("invalid entity tuple(s) input.")
def _node_assignments(self, node_tags, data_list, x_val, y_val, data_str):
for node_tag in node_tags:
if node_tag not in [item[0] for item in data_list]:
data_list.append([node_tag, x_val, y_val])
else:
print("node ", node_tag, "is already added to the ", data_str)
return
[docs] def clear(self, data_str):
if data_str == 'bc':
self.bc = []
elif data_str == 'bcnh':
self.bcnh = []
elif data_str == 'nf':
self.nf = []
# currently for single property and single material
[docs] def add_prop(self, thickness, youngs_modulus, density):
self.has_property = True
self.properties = np.array([[1, 1, thickness]])
self.materials = np.array([[1, youngs_modulus, density]])
[docs] def clear_prop(self):
self.has_property = False
self.properties = None
self.materials = None
[docs] def show_entities(self):
print()
for key, value in self.entity_dict.items():
print('nodes in entity ',key,' ---')
print(value)
[docs] def show_prop(self):
print('bound_con assignment state---')
print(self.bc)
print('bound_con_nonhomo state---')
print(self.bcnh)
print('nodal_forces state---')
print(self.nf)
print('properties state--')
print(self.properties)
print('materials state--')
print(self.materials)
[docs] def save_data(self, folder_name):
self.bound_con = np.asarray(self.bc)
self.bound_con_nonhomo = np.asarray(self.bcnh)
self.nodal_forces = np.asarray(self.nf)
cwd = os.getcwd()
output_path = cwd + '/' + folder_name
if not os.path.exists(output_path):
os.mkdir(output_path)
np.savetxt(output_path + '/' +'nodal_data.txt', self.nodal_data)
np.savetxt(output_path + '/' +'element_data.txt',self.element_data)
if self.bound_con is not None:
np.savetxt(output_path + '/' +'bound_con.txt', self.bound_con)
if self.bound_con_nonhomo is not None:
np.savetxt(output_path + '/' +'bound_con_nonhomo.txt',
self.bound_con_nonhomo)
if self.nodal_forces is not None:
np.savetxt(output_path + '/' +'nodal_forces.txt', self.nodal_forces)
if self.has_property:
np.savetxt(output_path + '/' +'properties.txt', self.properties)
np.savetxt(output_path + '/' +'materials.txt', self.materials)
[docs]def main():
print('------------------------------------------------------------------')
print('mshreader -- read msh file from gmsh and assign boundary data.')
print('This script provides an interface to convert the msh file into')
print('the input data format required by the basicfem.py. The nodal_data')
print('and the element_data will be built once the msh file is read. The')
print('bound_con, bound_con_nonhomo, nodal_forces, properties,',
'and materials')
print('will need to be assigned either by entity tuples or nodal labels.')
print('------------------------------------------------------------------')
test = 1
has_exception = False
is_file_valid = False
filename = input('Enter the input msh file name: ')
try:
mesh = MshProcessor(filename)
print('--------------------------------------------------------')
print("The nodal_data and element_data matrices build complete.")
print("Solver input data to be assigned.")
print('--------------------------------------------------------')
has_exception = False
is_file_valid = mesh.is_file_valid
except:
has_exception = True
while not is_file_valid or has_exception:
print('Invalid msh ile content. please try again.')
filename = input('Enter the input msh file name: ')
try:
mesh = MshProcessor(filename)
print('--------------------------------------------------------')
print("The nodal_data and element_data matrices build complete.")
print("Solver input data to be assigned.")
print('--------------------------------------------------------')
has_exception = False
is_file_valid = mesh.is_file_valid
except:
has_exception = True
exit_flag = False
while not exit_flag:
print('\nOperations:')
print('add --> add boundary conditions, forces or material' +
'properties')
print('clear --> clear previously assigned data.')
print('show --> show the current state of mesh data.')
print('showm --> show entity tuple - nodal groups mapping')
print('save --> save data into basicfem input format')
print('exit --> exit mshreader')
option = input('Enter an operation' +
'(add, clear, show, showm, save, and exit): ')
option = option.strip()
if option == 'add':
option_add(mesh)
elif option == 'show':
mesh.show_prop()
elif option == 'showm':
mesh.show_entities()
elif option == 'clear':
option_clear(mesh)
elif option == 'save':
option_save(mesh)
elif option == 'exit':
exit_flag = True
else:
print("Invalid operation.")
[docs]def option_save(mesh):
print("Specify a folder name in local directory. Data will be saved into")
print("this folder. If no such folder name exists, a new folder will be")
print("created. If the folder exists, previous data will be overwritten.")
folder_name = input("Enter a folder name: ")
mesh.save_data(folder_name)
print("Data saved.")
[docs]def option_clear(mesh):
print("\nwhich data to clear? Enter one of the following label:\n" +
"`bc` -> clear bound_con\n" +
"`bcnh` -> clear bounr_con_nonhomo\n" +
"`nf` -> clear nodal_forces\n" +
"`prop` -> clear materials and properties")
option = input("Enter a label: ")
while option not in ['bc', 'bcnh', 'nf', 'prop']:
print("Invalid label.")
print("\nwhich data to clear? Enter one of the following label:\n" +
"`bc` -> clear bound_con\n" +
"`bcnh` -> clear bounr_con_nonhomo\n" +
"`nf` -> clear nodal_forces\n" +
"`prop` -> clear materials and properties")
if option == 'prop':
mesh.clear_prop()
else:
mesh.clear(option)
print(option + " cleared.")
[docs]def option_add(mesh):
print('\nWhat data you want to add? '+
'Specify the label and then the input values.')
add_list_input_descriptions()
val_string = input('\nEnter label and corresponding values: ')
add_args = val_string.split()
add_args[1:] = [float(str_) for str_ in add_args[1:]]
while not is_valid_add_args(add_args):
add_list_input_descriptions()
val_string = input('\nEnter label and corresponding values: ')
add_args = val_string.split()
add_args[1:] = [float(str_) for str_ in add_args[1:]]
print('\nSpecify the nodal entities (a tuple) or nodal labels (a list).')
print('The nodal labels are consistent to the label generates by the gmsh')
print('software, so it might be helpful to look at the gmsh GUI and check')
print('which nodal labels you want to specify.')
print('Nodal entity are a mapping of entity tuples to nodal label lists,')
print('and these nodal label groups are assigned by gmsh.')
print()
nodal_label_input_descriptions()
label_str = input('\nEnter label and corresponding list: ')
label_args = label_str.split(" ", 1)
while not is_valid_label_args(label_args) or label_args[0] == 'showm' \
or label_args[0] == 'show':
if label_args[0] == 'showm':
mesh.show_entities()
elif label_args[0] == 'show':
mesh.show_prop()
nodal_label_input_descriptions()
label_str = input('\nEnter label and corresponding list: ')
label_args = label_str.split(" ", 1)
label_args[1] = ast.literal_eval(label_args[1])
if add_args[0] == 'prop':
mesh.add_prop(add_args[1], add_args[2], add_args[3])
else:
mesh.add_bc(label_args[0], add_args[0], label_args[1],
add_args[1], add_args[2])
# add_bc(method_label, input_list_label, node_entity_tags, x_tag, y_tag)
print('Assigned successfully.')
[docs]def is_valid_label_args(label_args):
if label_args[0] not in ['e', 'n', 'show','showm'] :
print("Invalid label. Need to be `e`, `n`, 'show' or `showm`.")
return False
if label_args[0] != 'showm' and label_args[0] != 'show':
if not (label_args[1].startswith('(') or \
label_args[1].startswith('[')):
print("Invalid values. Must be a tuple or list of numbers/tuples.")
return False
try:
ast.literal_eval(label_args[1])
except:
print('Invalid expression of list.')
return False
return True
[docs]def is_valid_add_args(add_args):
if add_args[0] not in ['bc', 'bcnh', 'nf', 'prop']:
print('Invalid label.')
return False
if add_args[0] != 'prop' and len(add_args) != 3 :
print('Incorrect amount of input arguments.')
return False
if add_args[0] == 'prop' and len(add_args) != 4 :
print('Incorrect amount of input arguments.')
return False
if add_args[0] == 'bc' and \
(add_args[1] not in (0, 1) or add_args[2] not in (0, 1)):
print('Invalid values for bc : 0 means free node and ' +
'1 means fixed node.')
return False
return True
if __name__ == "__main__":
main()