#
# Copyright © 2020 MSC.Software Corporation
#  
# Permission to use Scripts
#  
# Permission is granted to MSC.Software customers with valid licenses of the MSC Apex™ software 
# to use script files posted on this page (collectively “Scripts”) solely for customers’ own authorized 
# internal use in conjunction with licensed use of the MSC Apex software.  
# No other use is permitted.  No license to MSC Apex or other software is granted under this Permission.
#  
# SCRIPTS ARE PROVIDED ON AN "AS-IS" BASIS, WITH NO WARRANTIES OF ANY KIND.  USE OF THE SCRIPTS IS AT CUSTOMER'S SOLE RISK.
#  
# IN NO EVENT SHALL MSC.SOFTWARE CORPORATION, ITS AFFILIATES, SUBSIDIARIES, SUPPLIERS, AGENTS, REPRESENTATIVES OR EMPLOYEES BE LIABLE FOR ANY 
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, 
# STRICT LIABILITY, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION USE OF THE SCRIPTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
#  
# Customer shall indemnify, defend and hold MSC.Software Corporation, its affiliates, subsidiaries, suppliers, agents, 
# employees or representatives (collectively, “Indemnified Parties”) harmless from and against any third-party claims 
# brought against Indemnified Parties rising out of or in connection with Customer’s use of the Scripts.
#
import os
import subprocess
import time
import datetime
import math
import shelve
import shutil
import traceback
#  if run from apex environment
try:
	import apex
	from apex.construct import Point3D, Point2D
except:
	pass
	
#from CLexApexClass.py import CLexApex
#from MaterialClass.py import material

def getBConectD(inl1,DefaultContactType):
	BConectD={}
	n=0
	N=len(inl1)
	
	while n< N:
		if inl1[n].find('BCONECT') == 0:
			# 
			BConectId = int(inl1[n][8:16])
			BConprgId = int(inl1[n][16:24])
			# Stefan Added 200505
			try:
				BConprpId = int(inl1[n][24:32])
			except:
				BConprpId = ""
			SurfL = []
			tmpL = []

			tmp_str = inl1[n - 1]
			#print tmp_str
			indx = tmp_str.find('#')
			if indx > 0:
				ContactType = tmp_str[indx + 1:indx + 2]
				# print "@@@@ " + tmp_str[indx+1:indx+2]
				if ContactType != "G" and ContactType != "T":
					ContactType = DefaultContactType[0]
			else:
				ContactType = DefaultContactType[0]

			i = 5  # first lines
			while i <= 10:
				try:
					if inl1[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						SurfL.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					n += 1
					break

			# Create a dict with BCONPRG and the assigned contact type
			BConectD[BConectId] = [SurfL, ContactType, BConprgId,BConprpId]
		n+=1
	return BConectD

def getPreloadFromPartName(partName,PreLoadD,PreloadLookupFile):
	if partName.find("Bolts_m") == 0:
		#print(" Found bolt partName for 1st body " + partName)
		# print tmp_bsurfs[0]
		if partName.find("#") > -1:
			part_preload_str = partName.split('#')[1].replace('N', '').replace('mm', '').replace('_', '.')
			#print part_preload_str
			part_preload = float(part_preload_str)
			if partName.split('#')[1].find('mm') > -1:
				part_preload_type = "Disp"
			else:
				part_preload_type = "Force"
			#print "preload: " + str(part_preload)
		# check if preload is defined in prelaod file
		
		elif PreloadLookupFile != "":
			# split partName name as to get the right thread dimension
			#$ partName Bolts_m14_hex
			preload_key=partName.split('_')[1]
			part_preload_str=PreLoadD[preload_key].replace('N', '').replace('mm', '')
			part_preload = float(part_preload_str)
			#
			if PreLoadD[preload_key].find('mm') > -1:
				part_preload_type = "Disp"
			else:
				part_preload_type = "Force"
			print(("Preload Lookup value returned: " + part_preload_str + " " + part_preload_type))
		else:
			# No preload specified on Bolt, use default Preload
			part_preload_str=DefaultBoltPreload
			if part_preload_str.find('mm') > -1:
				part_preload_type = "Disp"
			else:
				part_preload_type = "Force"
			part_preload=float(part_preload_str.replace('N', '').replace('mm', '').replace('_', '.'))
			#print "preload: " + str(part_preload)

		return part_preload, part_preload_type, True
	
	else:
		print("Part is not a bolt part...or part does not follow name convention Bolt_mi where i is an integer ")
		return "", "", False
							

def createPart2PidDict(inl1):
	PartD = {}
	n = 0
	N = len(inl1)
	while n < N:
		if inl1[n].find('$ Part ') == 0:
			# read elems and get PID until Grid section starts
			PartName = inl1[n][7:].replace('.bdf', '').strip()
			#print PartName
			PartD[PartName] = []
			# Check if part is empty
			if inl1[n+2].find("$       2       3") == 0:
				n += 5
				# check so that part is not empty
				if inl1[n][0] == 'C':
					while inl1[n].find('GRID') != 0:
						if inl1[n].find("C") == 0 and inl1[n].find("CORD") != 0:
							try:
								_pid = int(inl1[n][16:24])
							except:
								print(inl1[n])
							if _pid not in PartD[PartName]:
								PartD[PartName].append(_pid)
						n += 1
				else:
					n += 1
			else:
				#print "Empty part... continue"
				n += 1			
		else:
			n += 1
	
	return PartD

def findInterFaceNodesFromMatName(bdf_full_name,offset,mat_name="_preload_dummy_material"):
	
	#offset=10000
	#bdf_full_name=r"D:\Software\Apex\Script\Bolts\UpdateBoltLogic\rectangle.bdf"
	bdf_h=open(bdf_full_name,'r')
	bdf_lines=bdf_h.readlines()
	bdf_h.close()

	#ElemD,PsolidD = ReadChexas(bdf_lines)
	ElemD,PsolidD = ReadSolids(bdf_lines)
	
	# print('ElemD')
	# n=0
	# for item in ElemD.keys():
		# print(item,ElemD[item])
		# n+=1
		# if n==10:
			# break
		
	
	# print('PsolidD')
	# n=0
	# for item in PsolidD.keys():
		# print(item,PsolidD[item][:10])
		# n+=1
		# if n==10:
			# break
	
	
	
	Rbe2D=ReadRbe2s(bdf_lines)
	
	#print('Rbe2D',Rbe2D)

	# Build Elem2PidD where Elem2PidD[eid] returns pid
	Elem2PidD={}
	for pid in PsolidD.keys():
		for eid in PsolidD[pid]:
			Elem2PidD[eid]=pid

	# Build Node2ElemD where Node2ElemD[node_id] returns eid of referenced elements	
	Node2ElemD={}
	for eid in ElemD.keys():
		for node in ElemD[eid]:
			if node in Node2ElemD:
				Node2ElemD[node].append(eid)
			else:
				Node2ElemD[node]=[eid]
	
	# Build Node2PidD where Node2PidD[nodeid] returns list of pids
	Node2PidD={}		
	for node in Node2ElemD.keys():
		for eid in  Node2ElemD[node]:
			pid= Elem2PidD[eid]
			if node in Node2PidD:
				if pid not in Node2PidD[node]:
					Node2PidD[node].append(pid)
			else:
				Node2PidD[node]=[pid]
				
	MxPxD=read_mat_and_props(bdf_full_name)
	#print("MAT1")
	#print(MxPxD['MAT1'])
	#print("PSOLID")
	#print(MxPxD['PSOLID'])
	
	#Build Mid2PidD where Mid2PidD[mid] returns pid
	#Build Pid2MidD where Pid2MidD[pid] returns mid
	Mid2PidD={}
	Pid2MidD={}

	
	for pid in MxPxD['PSOLID'].keys():
		mid=int(MxPxD['PSOLID'][pid][0][16:24])
		if mid in Mid2PidD:
			Mid2PidD[mid].append(pid)
		else:
			Mid2PidD[mid]=[pid]
			
		Pid2MidD[pid]=mid
			
	
	#print("ElemD",ElemD)
	#print("PsolidD",PsolidD)
	#print("Node2ElemD",Node2ElemD)
	#print("Mid2PidD",Mid2PidD)
	#print("Pid2MidD",Pid2MidD)
	
	# 0. Find TipPsolids referencing BolTip Material
	# 1. Find TipElems referencing TipPsolids
	# 2. Find TipNodes referencing TipElems
	# 3. Find Interface Nodes referencing both TipElems and NonTipElems
	
	# 0. Find TipPsolids referencing BolTip Material
	for mat_id in MxPxD['MAT1'].keys():
		#print( MxPxD['MAT1'][mat_id][1] )
		#if MxPxD['MAT1'][mat_id][1].find("$ Material Record: BolTip") == 0:
		if MxPxD['MAT1'][mat_id][1].find("$ Material Record: " + mat_name) == 0:
			TipPsolidL=Mid2PidD[mat_id]
			break
	#print("TipPsolidL",TipPsolidL)
	
	# 1. Find TipElems referencing TipPsolids
	
	# Build InterfaceElem2TipNodesD where InterfaceElem2TipNodesD[node] returns Tip Elem
	
	# Create a lookup for replacing tip material ID with bolt Material Id
	TipPsolid2BoltMatD={}
	# Need to connect blobs to pids in order to obtain preload later
	TipPsolid2BoltPsolidD={}
	
	InterfaceElem2TipNodesD={}
	TipNodeL=[]
	# for now use the pid instead of BSURF Id... might connect later
	# MergeGridsD and CtrlNodeD is output from partition_nodr_list_per_contact_body (to be reoplaced)
	# 
	
	# Break list of TipElems up in contiguous mesh pieces
	MeshBlobD={}
	#print("TipPsolidL",TipPsolidL)

	AllTipElems=[]
	for pid in TipPsolidL:
		print("processing pid",pid)
		AllTipElems += PsolidD[pid]
			
	
	#print(AllTipElems)
	#print("Number of tip Elems", len(AllTipElems) )
	n=0
	blob_counter=0
	ProcessedL=[]
	while len(AllTipElems) > 0:
		
		tmp_blob_list=[]
		add_blob_list=[]
		check_elem=AllTipElems[n]			
		#print("check_elem",check_elem)
		tmp_nodes=ElemD[check_elem]
		#print("==========Blob ",blob_counter,"=================")
		while len(add_blob_list) > 0 or len(tmp_blob_list)==0:
			test_add_blob_list=[]
			for _node in tmp_nodes:
				test_add_blob_list+=Node2ElemD[_node]
			test_add_blob_list=list(set(test_add_blob_list))	
			add_blob_list=[]
			for item in test_add_blob_list:
				if item in AllTipElems:
					if item not in tmp_blob_list:
						if item not in add_blob_list:
							add_blob_list.append(item)
							
			#print("Found: ", len(add_blob_list), " elems")
			tmp_blob_list += add_blob_list

			#update tmp_nodes
			tmp_nodes=[]
			for _elem in tmp_blob_list:
				tmp_nodes += ElemD[_elem]
			tmp_nodes=list(set(	tmp_nodes))
			
			n+=1

			
		#print("Blob ",blob_counter," contains ",len(tmp_blob_list), " elements")
		#print("tmp_blob_list",tmp_blob_list)
		MeshBlobD[blob_counter]=tmp_blob_list
		
		# remove found elements from AllTipElements
		for eid in tmp_blob_list:
			AllTipElems.remove(eid)
		blob_counter+=1
		n=0
		#break
		
	
	
	MergeGridsD={}
	CtrlNodeD={}
	InterfaceNodeD={}
	CtrlElemD={}
	Blob2TipPidD={}
	InterfaceNodeL=[]
	Blob2PreloadTypeD={}
	Blob2BoltRefNodeD={}
	deleteRBE2L=[]
	
	#for pid in TipPsolidL:
	for blob in MeshBlobD.keys():
		#print("Processing_blob",blob)
		TipNodeL=[]
		TipElemL=MeshBlobD[blob]
		if blob not in CtrlElemD:
			CtrlElemD[blob]=TipElemL[0]
			Blob2TipPidD[blob]=Elem2PidD[TipElemL[0]]
			
			
		# 2. find TipNodes
		
		for eid in TipElemL:
			TipNodeL+=ElemD[eid]
		TipNodeL=list(set(TipNodeL))
		
		#3. Find Interface Nodes referencing both TipElems and NonTipElems	
		for node in TipNodeL:
			if len(Node2PidD[node]) > 1:
				InterfaceNodeL.append(node)
				#print("Inteference Node with Id: ", node, " found")
				# Buld MergeGridsD
				if blob in MergeGridsD:
					MergeGridsD[blob].append((node,node+offset))
				else:
					MergeGridsD[blob]=[(node,node+offset)]
				
				for eid in Node2ElemD[node]:
					if eid in TipElemL:
						if eid in InterfaceElem2TipNodesD:
							InterfaceElem2TipNodesD[eid].append(node)
						else:
							InterfaceElem2TipNodesD[eid]=[node]
						
				if blob in InterfaceNodeD:
					InterfaceNodeD[blob].append(node)
				else:
					InterfaceNodeD[blob]=[node]
				# Create a lookup for replacing tip material ID with bolt Id
				tmp_pids=Node2PidD[node]
				
				if tmp_pids[0] in TipPsolidL:
					tip_pid=tmp_pids[0]
					bolt_pid=tmp_pids[1]
				else:
					tip_pid=tmp_pids[1]
					bolt_pid=tmp_pids[0]
				# get Material id corresponding to bolt pid
				if tip_pid not in TipPsolid2BoltMatD:
					TipPsolid2BoltMatD[tip_pid]=Pid2MidD[bolt_pid]
				# get bolt pid that corresponds to a given tip pid
				if tip_pid not in TipPsolid2BoltPsolidD:
					TipPsolid2BoltPsolidD[tip_pid]=bolt_pid

		# get first interface node and see if it occusrs in SlaveL
		# if so, it is semi-automatic preload if not it is automated
		
		
		# form list with all dep nodes
		Blob2PreloadTypeD[blob]='Auto'
		for key in Rbe2D.keys():
			if InterfaceNodeD[blob][0] in Rbe2D[key][1]:
				Blob2PreloadTypeD[blob]='Semi'
				Blob2BoltRefNodeD[blob]=key
				# remove placeholder RBE3
				deleteRBE2L.append(Rbe2D[key][0])
				break

	# Built CtrlNodeD a node on bolt tip that is not on the interface
	# get first element referenced by intrface node
	# Build Blob2BoltPidD that output a pid referencing the bolt pid
	Blob2BoltPidD={}
	for blob in MeshBlobD.keys():
		tmp_elem=CtrlElemD[blob]
		tip_pid=Elem2PidD[tmp_elem]
		Blob2BoltPidD[blob]=TipPsolid2BoltPsolidD[tip_pid]
		
		for node in ElemD[tmp_elem]:
			if node not in InterfaceNodeD[blob]:
				CtrlNodeD[blob]=node
				break
	#print('CtrlNodeD',CtrlNodeD)
	
	return TipPsolidL,InterfaceElem2TipNodesD,ElemD,InterfaceNodeL,TipPsolid2BoltMatD, MergeGridsD, CtrlNodeD, Blob2BoltPidD, Blob2PreloadTypeD, Blob2BoltRefNodeD, deleteRBE2L



def writeBackAfterSolidSplit(bdf_full_name,TipPsolidL,InterfaceElem2TipNodesD,ElemD,InterfaceNodeL,TipPsolid2BoltMatD,offset,deleteRBE2L,deleteNodeL):

	bdf_h=open(bdf_full_name,'r')
	bdf_lines=bdf_h.readlines()
	bdf_h.close()
	
	
	new_file_h=open(bdf_full_name,'w')
	
	n=0
	N=len(bdf_lines)
	
	#print("InterfaceNodeL", InterfaceNodeL)
	print("Split Nodes offset: ",offset)
	#print("bdf_full_name",bdf_full_name)
	while n < N:
		if bdf_lines[n].find("GRID")==0:
			node_id= int(bdf_lines[n][8:16])
			# write orig
			if node_id not in deleteNodeL:
				new_file_h.write(bdf_lines[n])
			if node_id in InterfaceNodeL:
				# write copy with node id offset
				new_file_h.write("%-8s%-8d%-s" % (bdf_lines[n][0:8],int(bdf_lines[n][8:16]) + offset, bdf_lines[n][16:] ) )
		elif bdf_lines[n].find("RBE2")==0:
			rbe2id=int(bdf_lines[n][8:16])
			if rbe2id not in deleteRBE2L:
				new_file_h.write(bdf_lines[n])
			else:
				# skip this line and all cont. lines
				while bdf_lines[n+1][0] == '+' or bdf_lines[n+1][0]==' ':
					n+=1
			
		elif bdf_lines[n].find("CHEXA")==0:
			eid=int(bdf_lines[n][8:16])
			if eid in InterfaceElem2TipNodesD:
				# write back CHEXA with offset nodes and correct PID
				new_file_h.write("%-24s" % (bdf_lines[n][0:24] ) ) 
				# pid=int(bdf_lines[n][16:24])
				# if pid in TipPsolidL:
					# pid=TipPsolid2BoltMatD[pid]
				# new_file_h.write("%-16s%-8d" % (bdf_lines[n][0:16],pid ) ) 
				# loop over all nodes of CHEXA, offset ids in InterfaceElem2TipNodesD[eid]
				i=0
				for tmp_node in ElemD[eid]:
					if  i==6:
						new_file_h.write("%-8s\n%-8s" % ("+","+") )
					if tmp_node in InterfaceElem2TipNodesD[eid]:
						new_file_h.write("%-8d" % (tmp_node + offset ) )
					else:
						new_file_h.write("%-8d" % (tmp_node ) )
					i+=1
				new_file_h.write("\n")
			else:
				new_file_h.write(bdf_lines[n])
				new_file_h.write(bdf_lines[n+1])
			n+=1
		elif bdf_lines[n].find("PSOLID")==0:
			pid=int(bdf_lines[n][8:16])
			mid=int(bdf_lines[n][16:24])
			# if Tip Psolid, replace dummy Mat to same mat as for bolt head
			if pid in TipPsolidL:
				new_file_h.write("%-16s%8d%-s\n" % (bdf_lines[n][0:16], TipPsolid2BoltMatD[pid] , bdf_lines[n][24:] ) )
			else:
				new_file_h.write(bdf_lines[n])
		
		else:
			new_file_h.write(bdf_lines[n])

	
		n+=1
		
		
	new_file_h.close()

	return



#import material_methods
def read_mat_and_props(bdf_file):
	print("reading: " + bdf_file)
	#print("***************I am in here*********")
	bdf_h=open(bdf_file,'r')
	bdf_lines=bdf_h.readlines()
	bdf_h.close()
	
	# Add all sub dicts to a master MatAndPropD
	
	MxPxD={}
	
	#$!  SX Names for Materials
	#$SXNAME MAT     1       ISOTROPI"Alu"
	#$SXNAME MAT     2       ISOTROPI"TA6V900"
	#$SXNAME MAT     3       ISOTROPI"TA6V1100"
	#$SXNAME MAT     4       ISOTROPI"inox"
	
	
	SXNames={}
	
	# suported cards
	MxPxD['MAT1']={}
	MxPxD['MAT2']={}
	MxPxD['MAT8']={}
	MxPxD['MATEP']={}
	MxPxD['MATS1']={}
	MxPxD['MATTEP']={}
	MxPxD['MATT1']={}
	MxPxD['MATHE']={}
	MxPxD['PCOMP']={}
	MxPxD['PSHELL']={}
	MxPxD['PSOLID']={}
	MxPxD['PSLDN1']={}
	
	MxPxD['TABLES1']={}
	MxPxD['TABLEST']={}
	MxPxD['TABLEM1']={}
	
	# add cards, and coments to dict
	
	n=0
	N=len(bdf_lines)
	while n < N:
		# check if card is in "look for" list
		card_type=bdf_lines[n][0:8].strip().replace('*','')		
		if card_type in MxPxD.keys():
			if bdf_lines[n][0:8].find('*') > 0:
				#long format
				id=int(bdf_lines[n][8:24])
			else:
				# short format
				id=int(bdf_lines[n][8:16])
				
			_lines=bdf_lines[n]
			_comments=""
			
			# set MxPxD in case there is just one line 
			MxPxD[card_type][id]=[_lines,_comments]

			
			i=1
			while bdf_lines[n-i].find('$') == 0:
				_comments+=bdf_lines[n-i]
				i-=1
				
			if n == N-1:
				break
				
			while  bdf_lines[n+1].find("*") ==0 or bdf_lines[n+1].find("+") == 0 :
				_lines+=bdf_lines[n+1]
				n+=1
				if n == N-1:
					break
				
			MxPxD[card_type][id]=[_lines,_comments]
				
		elif card_type=="$SXNAME" and bdf_lines[n][8:16].strip()=="MAT":
			#print("I am in SX stuff")
			mat_id=int(bdf_lines[n][16:24].strip())
			mat_name=bdf_lines[n][24:].replace("ISOTROPI","").replace('"','').strip()
			SXNames[mat_id]=mat_name
			
		
		else:
			pass
			
		n+=1
		
		if 1==0:
			# if bdf_lines[n].find("PCOMP*") ==0:
				# pcomp_sect=bdf_lines[n-2].split(':')[1].strip()
				# print(pcomp_sect)
				# PcompId=bdf_lines[n][8:24].strip()
				# HyperSizerGroupName="C" + PcompId
				# PcompD[pcomp_sect]=HyperSizerGroupName
				
			# elif bdf_lines[n].find("MAT8*") ==0:
				# mat8Id=int(bdf_lines[n][8:24])
				# mat8_lines=bdf_lines[n]
				# while bdf_lines[n+1].find("*") ==0:
					# mat8_lines+=bdf_lines[n+1]
					# n+=1
				# Mat8D[mat8Id]=mat8_lines
			
			# elif bdf_lines[n].find("PSHELL") ==0:
				# # read all PSHELLs
				# pshell_sect=bdf_lines[n-2].split(':')[1].strip()
				# pshellId=int(bdf_lines[n][8:16])
				# pshell_lines=bdf_lines[n]
				# while bdf_lines[n+1].find("+") ==0:
					# pshell_lines+=bdf_lines[n+1]
					# n+=1
				# PShellD[pshellId]=[pshell_sect,pshell_lines]

			# n+=1	
				
			pass
	
	
	#print(SXNames)
	# ADD SX Name to comment for MAT1 cards
	for mat_id in MxPxD["MAT1"].keys():
		if mat_id in SXNames:
			MxPxD["MAT1"][mat_id].append(SXNames[mat_id])
		else:
			#Try to get name from comment line:
			_comments=MxPxD["MAT1"][mat_id][1]
			tmp=_comments.split('\n')
			#print(len(tmp))
			#print(tmp)
			for item in tmp:
				if item.find("$ Material Record: ") == 0:
					mat_name=item.split(':')[1].strip()
					MxPxD["MAT1"][mat_id].append(mat_name)
					break
	
	return MxPxD


class CLexApex(object):
	@staticmethod
	def getSubPanelFromHyperSizerName(hsName):
		for subpanel in CLexApex.subPanels:
			print("comparing apex: ", subpanel.name , " to Hypesizer ",  hsName)
			if PcompD[subpanel.name] == hsName:
				print("Found subpanel:", hsName, " corresponding to HyperSizer panel: " ,subpanel.name )
				return subpanel
		print("No subpanel with name ", hsName, " found")
		
	@staticmethod
	def getSubPanelFromApexName(apexName):
		for subpanel in CLexApex.subPanels:
			print("..checking... ",subpanel.name )
			if subpanel.name == apexName:
				print("Found subpanel:", apexName, " corresponding to Face with id: " , subpanel.id )
				return subpanel
		print("No subpanel with name ", apexName, " found")	
		
	@staticmethod
	def addSubPanel(sub_panel):
		try:
			CLexApex.subPanels.append(sub_panel)
			print("added subpanel")
		except:
			CLexApex.subPanels=[]
			CLexApex.subPanels.append(sub_panel)
			print("added subpanel")
			
	@staticmethod
	def addPanel(panel):
		try:
			CLexApex.panels.append(panel)
			print("added panel")	
		except:
			CLexApex.panels=[]
			CLexApex.panels.append(panel)
			print("added panel")

	@staticmethod
	def addMaterial(mat):
		try:
			CLexApex.materials.append(mat)
		except:
			CLexApex.materials=[]
			CLexApex.materials.append(mat)
				
	@staticmethod
	def getmaterialFromHyperSizerName(hsMatName):
		hsMatShortName=hsMatName.split('"')[3]
		print(hsMatName)
		print(hsMatShortName)
		for material in CLexApex.materials:
			if material.name == hsMatShortName:
				print("Found material:", hsMatName, " corresponding to Apex material with name: " , material.name , " with id: ", material.id ) 
				return material
		print("No material with name ", hsMatName, " found")	

	@staticmethod
	def getmaterialFromId(matId):
		for material in CLexApex.materials:
			if material.id == matId:
				print("Found material:", material.name , " corresponding to id : " , material.id ) 
				return material
		print("No material with id ", material.id, " found")
		
	@staticmethod
	def trackAndReturnId(mat_name,cardType,id):
		""" takes a card type  i.e TABLES1  and id that is referenced from a certain material (self)
		Checks if id already used, if so, returns next free id, if not returns same id
		Also check if card is being referenced already, if so also returns False (not to print a duplicate out)
		else, if first time the entitiy (i.e TABLES1) is being referenced, return True to prompt entity to be output
		
		"""
		cardType=cardType.upper()
		#Use to track Ids of entities
		# Make sure Dict exists
		try:
			CLexApex.IdTrackerD
		except AttributeError:
			CLexApex.IdTrackerD={}
		
		# this dict will control whether a referenced Table is going to be reused
		try:
			CLexApex.ReUseD
		except AttributeError:
			CLexApex.ReUseD={}
			
		# first, check if id is already referenced from this material	
		if (mat_name,cardType,id) not in CLexApex.ReUseD.keys():
			
			if cardType in CLexApex.IdTrackerD.keys():
				if id in CLexApex.IdTrackerD[cardType]:
					# get a free id:
					next_id=max(CLexApex.IdTrackerD[cardType]) +1
					CLexApex.IdTrackerD[cardType].append(next_id)
					print("Returning new id: ", next_id, " for card of type: " , cardType, " Orig Id: ", id )
					CLexApex.ReUseD[(mat_name,cardType,id)]=next_id
					return next_id, True
				else:
					CLexApex.IdTrackerD[cardType].append(id)
					print("Returning original id: ", id, " for card of type: " , cardType, " (first usage)")
					CLexApex.ReUseD[(mat_name,cardType,id)]=id
					return id, True
			
			else:
				CLexApex.IdTrackerD[cardType]=[id]
				print("Returning original id: ", id, " for card of type: " , cardType )
				CLexApex.ReUseD[(mat_name,cardType,id)]=id
				return id, True
		
		else:
			print(cardType," with id: ", id, " is already being referenced, for ", mat_name, "  reuse id, do not write duplicate" )
			return CLexApex.ReUseD[(mat_name,cardType,id)], False
	
	@staticmethod
	def setMxPxD(MxPxD):
		CLexApex.mxpxD=MxPxD

	
		
class material:
	def __init__(self,bdf_str,prop_ids=[]):
		""" Read a string optionally multi line, extract Material data and optionally creates an Apex Material
		Unsupported cards are managed via a MAT1 placeholder and mapped to the proper card at export"""
		self.ALPHA=0.0
		self.bdf_str=bdf_str
		# this is to connect properties back to the material
		self.pids=prop_ids
		# get id and properties from bdf
		self.readMatFromBdfLines()
		# get the hypersizer name from the id
		CLexApex.addMaterial(self)

	
	
	def readMatFromBdfLines(self):
	
		if self.bdf_str.find("MAT1") == 0:
			self.readMAT1(createApexMat=False)
			
		elif self.bdf_str.find("MAT2") == 0:
			self.readMAT2(createApexMat=False)
			
		elif self.bdf_str.find("MAT8") == 0:
			self.readMAT8(createApexMat=False)	
			#self.getMat8NameFromId()
		
		elif self.bdf_str.find("MATEP") == 0:
			self.readMATEP(createApexMat=False)

		elif self.bdf_str.find("MATS1") == 0:
			self.readMATS1(createApexMat=False)	
			
		elif self.bdf_str.find("MATTEP") == 0:
			self.readMATTEP(createApexMat=False)

		elif self.bdf_str.find("MATT1") == 0:
			self.readMATT1(createApexMat=False)	
			
		elif self.bdf_str.find("MATHE") == 0:
			self.readMATHE(createApexMat=False)	
			
		elif self.bdf_str.find("TABLES1") == 0:
			self.readTABLES1(createApexMat=False)	
			
		elif self.bdf_str.find("TABLEST") == 0:
			self.readTABLEST(createApexMat=False)	

		elif self.bdf_str.find("TABLEM1") == 0:
			self.readTABLEM1(createApexMat=False)

		elif self.bdf_str.find("PSLDN1") == 0:
			self.readPSLDN1(createApexMat=False)
			
	
	def readMAT1(self,createApexMat=True):
		"""create dummy MAT1 that will be converted to NonLinearMat1 on export"""
		MxPxD=CLexApex.mxpxD
		
		tmp_str=self.bdf_str.split('\n')
		print("***")
		print(tmp_str)
		print("***")
		if tmp_str[0][0:8].find("MAT1 ") == 0:
			self.id=int(tmp_str[0][8:16])	
			self.E=myfloat(tmp_str[0][16:24])
			self.POI=float(tmp_str[0][32:40])
			try:
				self.RHO=myfloat(tmp_str[0][40:48])
			except:
				self.RHO=0.0
			
			try:
				self.ALPHA=myfloat(tmp_str[0][48:56])
			except:
				self.ALPHA=0.0
			print("E",self.E)
			print("POI",self.POI)
			print("RHO",self.RHO)
			# hardcoded poissons ratio			
		elif tmp_str[0][0:8].find("MAT1*") == 0:
			print("MAT1 long format not yet supported")
		else:
			print("Not a MAT1 card")
			
		self.name="MAT1_"+ str(self.id)
		
		# get PSHELL		
		try:
			for psh_id in MxPxD['PSHELL'].keys():
				_bdfstr=MxPxD['PSHELL'][psh_id][0]
				if int(_bdfstr[16:24].strip() ) == self.id :
					self.pshell=material(_bdfstr)
					break
			print("Associated PSHELL with id ",self.pshell.id)
		except:
			print("***Info*** No PSHELL with id ",self.id," can be found")
		
		
		# get PSOLID		
		try:
			for psh_id in MxPxD['PSOLID'].keys():
				_bdfstr=MxPxD['PSOLID'][psh_id][0]
				if int(_bdfstr[16:24].strip() ) == self.id :
					self.psolid=material(_bdfstr)
					break
			print("Associated PSOLID with id ",self.pshell.id)
		except:
			print("***Info*** No PSOLID with id ",self.id," can be found")
		
		
		
		# get PSLDN1
		try:
			# check in PSDLN1 cards for matching mid (=self.id)
			#_bdfstr=MxPxD['PSLDN1'][self.id][0]
			
			for psldn1_id in MxPxD['PSLDN1'].keys():
				_bdfstr=MxPxD['PSLDN1'][psldn1_id][0]
				if int(_bdfstr[24:32].strip() ) == self.id :
					self.psldn1=material(_bdfstr)
					break
			print("Associated PSLDN1 with id ",self.psldn1.id)
		except:
			print("***Info*** No PSLDN1 with id ",self.id," can be found")
		
		# get MATEP 
		try:
			_bdfstr=MxPxD['MATEP'][self.id][0]
			self.matep=material(_bdfstr)
			print("Associated MATEP with id ",self.matep.id)
		except:
			print("***Info*** No MATEP with id ",self.id," can be found")
			#traceback.print_exc()	
		
		# get MATS1 
		try:
			#print("===")
			#print(MxPxD['MATS1'])
			_bdfstr=MxPxD['MATS1'][self.id][0]
			self.mats1=material(_bdfstr)
			print("Associated MATS1 with id ",self.mats1.id)
		except:
			print("***Info*** No MATS1 with id ",self.id," can be found")
			
		# get MATHE
		try:
			#print("===")
			#print(MxPxD['MATS1'])
			_bdfstr=MxPxD['MATHE'][self.id][0]
			self.mathe=material(_bdfstr)
			print("Associated MATHE with id ",self.mathe.id)
		except:
			print("***Info*** No MATHE with id ",self.id," can be found")	
			
			
		# get MATTEP 
		try:
			_bdfstr=MxPxD['MATTEP'][self.id][0]
			self.mattep=material(_bdfstr)
			print("Associated MATTEP with id ",self.mattep.id)
		except:
			print("***Info*** No MATTEP with id ",self.id," can be found")	
		
		# get MATT1
		try:
			_bdfstr=MxPxD['MATT1'][self.id][0]
			self.matt1=material(_bdfstr)
			print("Associated MATT1 with id ",self.matt1.id)
		except:
			print("***Info*** No MATT1 with id ",self.id," can be found")

		if createApexMat:
			self.createApexIsotropic()	
			
	def readMATEP(self,createApexMat=False):
		"""read MATEP the card info is linked to the MAT1 card
		TABLEST and TABL3d not supported on FORM=TABLE
		Reffect OPTION TABLE or RTID Tables1 not yet supported  """
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MATEP ") == 0:
			self.id=int(tmp_str[0][8:16])
			if tmp_str[0][16:24].upper().find('TABLE') >= 0:
				self.tables1Id=int(tmp_str[0][32:40])
			else:
				self.tables1Id=-1
			self.name="MATEP_"+ str(self.id)
		elif tmp_str[0][0:8].find("MATEP*") == 0:
			print("MATEP long format not yet supported")
		else:
			print("Not a MATEP card")
		# try to obtain linked TABLES1
		#
		try:
			_bdfstr=MxPxD['TABLES1'][self.tables1Id][0]
			self.tables1=material(_bdfstr)
			print("Associated TABLES1 with id ",self.tables1.id)
		except:
			print("***Error*** No TABLES1 with id ",self.tables1Id," can not be found")
					
	def readMATHE(self,createApexMat=False):
		"""read MATHE the card info is linked to the MAT1 card"""
		
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MATHE ") == 0:
			self.id=int(tmp_str[0][8:16])
		elif tmp_str[0][0:8].find("MATHE*") == 0:
			print("MATHE long format not yet supported")
		else:
			print("Not a MATHE card")

	def readMATS1(self,createApexMat=False):
		"""read MATS1 the card info is linked to the MAT1 card"""
		print("*******************  MATS1 ****************")
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MATS1 ") == 0:
			self.id=int(tmp_str[0][8:16])
			self.tables1Id=int(tmp_str[0][16:24])
			self.name="MATS1_"+ str(self.id)
		elif tmp_str[0][0:8].find("MATS1*") == 0:
			print("MATS1 long format not yet supported")
		else:
			print("Not a MATS1 card")
		# try to obtain linked TABLES1
		#
		try:
			_bdfstr=MxPxD['TABLES1'][self.tables1Id][0]
			self.tables1=material(_bdfstr)
			print("Associated TABLES1 with id ",self.tables1.id)
		except:
			print("***Error*** No TABLES1 with id ",self.tables1Id," can not be found")
	
	
	
	def readMATTEP(self,createApexMat=False):
		"""read MATTEP the card info is linked to the MAT1 card"""
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MATTEP ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.tablestId=int(tmp_str[0][32:40])
			self.name="MATTEP_"+ str(self.id)
		elif tmp_str[0][0:8].find("MATTEP*") == 0:
			print("MATTEP long format not yet supported")
		else:
			print("Not a MATTEP card")
			
			
		# try to obtain linked TABLEST
		try:
			_bdfstr=MxPxD['TABLEST'][self.tablestId][0]
			self.tablest=material(_bdfstr)
			print("Associated TABLEST with id ",self.tablest.id)
		except:
			print("***Error*** No TABLEST with id ",self.tablestId," can not be found")
			traceback.print_exc()	
		
	def readMATT1(self,createApexMat=False):
		"""read MATT1 the card info is linked to the MAT1 card"""
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MATT1 ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.tablem1Id1=int(tmp_str[0][16:24])
			self.tablem1Id2=int(tmp_str[0][48:56])
			self.name="MATT1_"+ str(self.id)
		elif tmp_str[0][0:8].find("MATT1*") == 0:
			print("MATT1 long format not yet supported")
		else:
			print("Not a MATT1 card")
			
			
		# try to obtain linked TABLEM1
		try:
			_bdfstr1=MxPxD['TABLEM1'][self.tablem1Id1][0]
			self.tablem1_1=material(_bdfstr1)
			_bdfstr2=MxPxD['TABLEM1'][self.tablem1Id2][0]
			self.tablem1_2=material(_bdfstr2)
			print("Associated TABLEM1's with ids ",self.tablem1_1.id," ",self.tablem1_2.id )
		except:
			print("***Error*** One or both of TABLEM1's with ids ",self.tablem1Id1," and ",self.tablem1Id2 ," can not be found")	
					
	def readTABLES1(self,createApexMat=False):
		"""read TABLES1  card info that is linked to the MATx card"""
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("TABLES1 ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.name="TABLES1_"+ str(self.id)
		elif tmp_str[0][0:8].find("TABLES1*") == 0:
			print("TABLES1 long format not yet supported")
		else:
			print("Not a TABLES1 card") 	

	def readTABLEST(self,createApexMat=False):
		"""read TABLES1  card info that is linked to the MATx card"""
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("TABLEST ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.name="TABLEST_"+ str(self.id)			
			
			# try to obtain linked TABLES1
			# TABLES1's are paired with temperature values starting on line2
			Tables1IdL=[]
			n=1
			while n< len(tmp_str):
				tmp_line=tmp_str[n]
				# check all temp/tables1 pairs on each line
				i=0
				while i <4:
					try:
						#print(8 + 8*(2*i+1), 8 + 8*(2*i+1) +8 )
						tmp_id=int(tmp_line[ 8 + 8*(2*i+1)  :  8 + 8*(2*i+1) +8 ].strip()  )     #i=0  16:24   # i=1  32:40   #i=2  48:
						Tables1IdL.append(tmp_id)
					except:
						break
					i+=1
				n+=1
			print("Found TABLES1 ids:",Tables1IdL)
			self.Tables1IdL=Tables1IdL
			self.Tables1StrL=[]
			for tabid in Tables1IdL:
				try:
					self.Tables1StrL.append(MxPxD['TABLES1'][tabid][0])
					print("Associated TABLES1 with id ",tabid)
				except:
					print("***Error*** No TABLES1 with id ",self.tables1Id," can not be found")			
					
			
		elif tmp_str[0][0:8].find("TABLEST*") == 0:
			print("TABLEST long format not yet supported")
		else:
			print("Not a TABLEST card") 		
			
	def readTABLEM1(self,createApexMat=False):
		"""read TABLEM1  card info that is linked to the MATx card"""
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("TABLEM1 ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.name="TABLEM1"+ str(self.id)
		elif tmp_str[0][0:8].find("TABLEM1*") == 0:
			print("TABLEM1 long format not yet supported")
		else:
			print("Not a TABLEM1 card") 

	def readMAT8(self,createApexMat=True):
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MAT8*") == 0:
			#long format
			self.id=int(tmp_str[0][8:24])
			self.E1=float(tmp_str[0][24:40])
			self.E2=float(tmp_str[0][40:56])
			self.nu12=float(tmp_str[0][56:72])
			
			self.G12=float(tmp_str[1][8:24])
			self.G1Z=float(tmp_str[1][24:40])
			self.G2Z=float(tmp_str[1][40:56])
			self.RHO=float(tmp_str[1][56:72])
			
			self.A1=float(tmp_str[2][8:24])
			self.A2=float(tmp_str[2][24:40])
			self.TREF=float(tmp_str[2][40:56])
			self.Xt=float(tmp_str[2][56:72])
			
			self.Xc=float(tmp_str[3][8:24])
			self.Yt=float(tmp_str[3][24:40])
			self.Yc=float(tmp_str[3][40:56])
			self.S=float(tmp_str[3][56:72])
			try:
				self.GE=float(tmp_str[4][8:24])
				self.F12=float(tmp_str[4][24:40])
				self.STRN=tmp_str[4][40:56].strip()
			except:
				pass
			self.name="MAT8_" +str(self.id)
			
			# Dummy props for MAT1
			self.E=self.E1
			self.POI=self.nu12
			
		elif tmp_str[0][0:8].find("MAT8 ") == 0:
			print("short format, not yet supported")
		else:
			print("Not a MAT8 card")
			
			
		if createApexMat:	
			self.createApexOrtotropic()
	
	def readMAT2(self,createApexMat=True):
		"""create dummy MAT1 that will be converted to MAT2 via script triggers or export Utils
		write lines to bdf with MAT2 props and PSHELL props to read via script triggers
		create an isotropic placeholder material"""
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("MAT2*") == 0:
			self.id=int(tmp_str[0][8:24])
			# SET G11 to be used as E for placeholder MAT1
			self.E=float(tmp_str[0][24:40])
			try:
				self.RHO=myfloat(tmp_str[1][56:72])
			except:
				self.RHO=0.0
			# hardcoded poissons ratio
			self.POI=0.29
			
			self.name="MAT2_"+ str(self.id)
			if len(self.pids) > 0:
				# just one psh id supported, take the first
				psh_id=self.pids[0]
				
				
				psh=MxPxD['PSHELL'][psh_id]
				
				
				try:
					self.mid1Id=int(MxPxD['PSHELL'][psh_id][0][16:24])
				except:
					self.mid1Id=""
				
				try:
					self.mid2Id=int(MxPxD['PSHELL'][psh_id][0][32:40])
				except:
					self.mid2Id=""	

				try:
					self.mid3Id=int(MxPxD['PSHELL'][psh_id][0][48:56])
				except:
					self.mid3Id=""
					
				try:
					self.mid4Id=int(MxPxD['PSHELL'][psh_id][0][98:106].strip())
				except:
					self.mid4Id=""
					
				print(self.mid1Id)
				print(self.mid2Id)
				print(self.mid3Id)
				print(self.mid4Id)
				
				print("Found PSHELL with id ", psh_id, " referencing MAT2 with Mid1: ", self.id)
				self.pshell=material(MxPxD['PSHELL'][psh_id][0])	
					
					
				if self.mid1Id !="": # and self.mid1Id != self.id:
					self.mid1=material(MxPxD['MAT2'][self.mid1Id][0])
				if self.mid2Id !="": # and self.mid2Id != self.id:
					self.mid2=material(MxPxD['MAT2'][self.mid2Id][0])	
				if self.mid3Id !="": # and self.mid3Id != self.id:
					self.mid3=material(MxPxD['MAT2'][self.mid3Id][0])		
				if self.mid4Id !="": # and self.mid4Id != self.id:
					self.mid4=material(MxPxD['MAT2'][self.mid4Id][0])	
			
			#else:
			#	print("MAT2",self.id," is not referenced by any property")
				


		if createApexMat:
			self.createApexIsotropic()
		elif tmp_str[0][0:8].find("MAT2 ") == 0:
			print("MAT2 short format not yet supported")
			


	def readPSLDN1(self,createApexMat=False):
		"""read PSLDN1- this card is associated to MAT1"""

		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("PSLDN1 ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.mid1=int(tmp_str[0][24:32])

			self.name="PSLDN1_"+ str(self.id)
			
		elif tmp_str[0][0:8].find("PSLDN1*") == 0:
			print("PSLDN1 long format not yet supported")
			return
		else:
			print("Not a PSLDN1 card")
			return
			
		# Check for MAT1 
		# try to obtain linked MID1
		try:
			_bdfstr1=MxPxD['MAT1'][self.self.mid1][0]
			self.mid1=material(_bdfstr1)
		except:
			pass
			
	def readPSHELL(self,createApexMat=False):
		"""read PSHELL- this card is associated to MID1
		MID2,MID3 and MID4 is associated to PSHELL"""
		MxPxD=CLexApex.mxpxD
		tmp_str=self.bdf_str.split('\n')
		if tmp_str[0][0:8].find("PSHELL ") == 0:
			self.id=int(tmp_str[0][8:16])			
			self.mid1=int(tmp_str[0][16:24])
			self.mid2=int(tmp_str[0][32:40])
			self.mid3=int(tmp_str[0][48:56])
			try:
				self.mid4=int(tmp_str[1][24:32])
			except:
				pass
			self.name="PSHELL_"+ str(self.id)
			
		elif tmp_str[0][0:8].find("PSHELL*") == 0:
			print("PSHELL long format not yet supported")
			return
		else:
			print("Not a PSHELL card")
			return
			
		# Check for MAT1 or MAT2 cards that links to PSHELL	
		# try to obtain linked MID1
		try:
			_bdfstr1=MxPxD['MAT1'][self.self.mid1][0]
			self.mid1=material(_bdfstr1)
		except:
			pass
			
		# try to obtain linked MID2
		try:
			_bdfstr1=MxPxD['MAT1'][self.self.mid2][0]
			self.mid2=material(_bdfstr1)
		except:
			pass

		# try to obtain linked MID3
		try:
			_bdfstr1=MxPxD['MAT1'][self.self.mid3][0]
			self.mid3=material(_bdfstr1)
		except:
			pass	
			
		# try to obtain linked MID4
		try:
			_bdfstr1=MxPxD['MAT1'][self.self.mid4][0]
			self.mid4=material(_bdfstr1)
		except:
			pass
			
		# try to obtain linked MID1
		try:
			_bdfstr1=MxPxD['MAT2'][self.self.mid1][0]
			self.mid1=material(_bdfstr1)
		except:
			pass
			
		# try to obtain linked MID2
		try:
			_bdfstr1=MxPxD['MAT2'][self.self.mid2][0]
			self.mid2=material(_bdfstr1)
		except:
			pass

		# try to obtain linked MID3
		try:
			_bdfstr1=MxPxD['MAT2'][self.self.mid3][0]
			self.mid3=material(_bdfstr1)
		except:
			pass	
			
		# try to obtain linked MID4
		try:
			_bdfstr1=MxPxD['MAT2'][self.self.mid4][0]
			self.mid4=material(_bdfstr1)
		except:
			pass	
		
	def createApexOrtotropic(self):
		try:
			myMatl = apex.catalog.getMaterial( name = self.name )
			#print("Material with name", self.name  , " already exists")
			
		except:
			# Create material if not already existing
			myMatl = apex.catalog.createMaterial( name = self.name, description = "", color = [0, 0, 255] )
			
			myMatl.update(materialType = apex.attribute.MaterialType.Orthotropic)
			myMatl.update(elasticityOrthotropic2D = {'elasticModulusX' : self.E1, } )
			myMatl.update(elasticityOrthotropic2D = {'elasticModulusY' : self.E2, } )
			myMatl.update(elasticityOrthotropic2D = {'shearModulusXY' : self.G12, } )
			myMatl.update(elasticityOrthotropic2D = {'shearModulusYZ' : self.G2Z, } )
			myMatl.update(elasticityOrthotropic2D = {'shearModulusXZ' : self.G1Z, } )
			myMatl.update(poissonRatio = self.nu12 )
			myMatl.update(failureLimitsOrthotropic2D = {'stressLimitTensionX' : self.Xt, } )
			myMatl.update(failureLimitsOrthotropic2D = {'stressLimitTensionY' : self.Yt, } )
			myMatl.update(failureLimitsOrthotropic2D = {'stressLimitCompressionX' : self.Xc, } )
			myMatl.update(failureLimitsOrthotropic2D = {'stressLimitCompressionY' : self.Yc, } )
			myMatl.update(failureLimitsOrthotropic2D = {'stressLimitInplaneShear' : self.S, } )
			myMatl.update(density = self.RHO)
			
		self.apexMat=myMatl
			
	def createApexIsotropic(self):
		try:
			myMatl = apex.catalog.getMaterial( name = self.name )
			#print("Material with name", self.name  , " already exists")
			
		except:
			# Create material if not already existing
			myMatl = apex.catalog.createMaterial( name = self.name, description = "", color = [255, 0, 0] )
	
			myMatl.update( elasticModulus = self.E, )
			myMatl.update( poissonRatio = self.POI, )
			myMatl.update( density = self.RHO, )
			myMatl.update( thermalExpansionCoeff = self.ALPHA )
			try:
				myMatl.update(	description = self.descriptor,	)
			except:
				myMatl.update(	description = "Placeholder for unsupported material type",	)
			
	
		self.apexMat=myMatl		
		
	def printInfo(self):
		print(self.__dict__)

	def writeMatToTempFile(self,mat_dir):
		# add material to MaterialTempFolder:
		#mat_dir=r"C:\Users\e7390\Documents\MSC_Apex Workspace\JaguarUtils\TmpMaterials"
		if not os.path.isdir(mat_dir):
			os.mkdir(mat_dir)
		tmp_mat_file=mat_dir + '\\' + self.name + '.bdf'
		
		file_h=open(tmp_mat_file,'w' )
		if not hasattr(self, 'E'):
			self.E=1.0		
		if not hasattr(self, 'POI'):
			self.POI=0.29
		if not hasattr(self, 'RHO'):
			self.RHO=0.0
		#getattr(a, 'property', 'default value')
		
		# first write dummy MAT1- except for MAT8 cards
		
		if self.bdf_str.find("MAT8") < 0 and self.bdf_str.find("MAT2") < 0:
			file_h.write("%-8s%8d%8s%8s%8s%s\n" % ("MAT1",self.id,num2str(self.E),"",num2str(self.POI),num2str(self.RHO)) )
			if not hasattr(self, 'E'):
				self.E=1.0		
				if not hasattr(self, 'POI'):
					self.POI=0.29
				if not hasattr(self, 'RHO'):
					self.RHO=0.0
		
		#  MAT1
		if self.bdf_str.find("MAT1") == 0:
			#file_h.write("%-8s%8d%8.1f%8s%8.3f%8.3e\n" % ("MAT1",self.id,self.E,"",self.POI,self.RHO) )
			# write  MAT2 Mid1 Mid2 and Mid4...Mid3 appears to be blank
			# write any associated data 
			
			if hasattr(self,'matep') : 
				file_h.write(self.matep.bdf_str)
				if  hasattr(self.matep,'tables1'): 
					file_h.write(self.matep.tables1.bdf_str)
					
			if hasattr(self,'mattep') : 
				file_h.write(self.mattep.bdf_str)
				if  hasattr(self.mattep,'tablest'): 
					file_h.write(self.mattep.tablest.bdf_str)
					if hasattr(self.mattep.tablest, 'Tables1StrL'):
						for item in self.mattep.tablest.Tables1StrL:
							file_h.write(item)

			if hasattr(self,'matt1') : 
				file_h.write(self.matt1.bdf_str)
				if  hasattr(self.matt1,'tablem1_1'): 
					file_h.write(self.matt1.tablem1_1.bdf_str)
				else:
					print("warning: tablem1_1 does not exist")

				if  hasattr(self.matt1,'tablem1_2'): 
					file_h.write(self.matt1.tablem1_2.bdf_str)
				else:
					print("warning: tablem1_2 does not exist")
				
		
		#file_h.write(self.material.bdf_str)
		#file_h.write(self.mat2.bdf_str)
		#file_h.write(self.mat4.bdf_str)
		## write PSHELL
		#file_h.write(PShellD[self.stack[0]][1])
		
		elif self.bdf_str.find("MAT2") == 0:
			print("I am in writing MAT2")
			#file_h.write(self.bdf_str)
			if hasattr(self,'pshell') : 
				file_h.write(self.pshell.bdf_str)
			if  hasattr(self,'mid1'): 
				file_h.write(self.mid1.bdf_str)
			if  hasattr(self,'mid2'): 
				file_h.write(self.mid2.bdf_str)
			if  hasattr(self,'mid3'): 
				file_h.write(self.mid3.bdf_str)	
			if  hasattr(self,'mid4'): 
				file_h.write(self.mid4.bdf_str)	
					
		elif self.bdf_str.find("MAT8") == 0:
			print("I am in writing MAT8")
			file_h.write(self.bdf_str)
			
		file_h.close()
		
		print("written material info to " + tmp_mat_file )

	def writeBackMatToBdf_h(self,new_id,bdf_h):
		
		bdf_h.write("$ Adding unsupported cards\n")

		if self.E is None: self.E=1.0		
		if self.POI is None: self.POI=0.29
		if self.RHO is None: self.RHO=0.0
		
		#getattr(a, 'property', 'default value')
		# first write dummy MAT1-
		#bdf_h.write("%-8s%8d%8.1f%8s%8.3f%8.3e\n" % ("MAT1",self.id,self.E,"",self.POI,self.RHO) )
		# make sure formatting is ok
		bdf_h.write("%-8s%8d%8s%8s%8s%s\n" % ("MAT1",new_id,num2str(self.E),"",num2str(self.POI),num2str(self.RHO)) )
		# write  MAT2 Mid1 Mid2 and Mid4...Mid3 appears to be blank
		# write any associated data 
		
		if hasattr(self,'matep') : 			
			#bdf_h.write(self.matep.bdf_str)
			if hasattr(self.matep,'tables1'):
				_tables1Id, writeFlag=CLexApex.trackAndReturnId(self.name,'tables1',self.matep.tables1Id)
				bdf_h.write("%-8s%8d%-16s%8d%-s" % ("MATEP",new_id,self.matep.bdf_str[16:32],_tables1Id,self.matep.bdf_str[40:]))
				if writeFlag:
					bdf_h.write("%-8s%8d%-s" % ("TABLES1",_tables1Id,self.matep.tables1.bdf_str[16:]))
			else:
				bdf_h.write("%-8s%8d%-s" % ("MATEP",new_id,self.matep.bdf_str[16:]))
		

		if hasattr(self,'mats1') : 			
			if hasattr(self.mats1,'tables1'):
				_tables1Id, writeFlag=CLexApex.trackAndReturnId(self.name,'tables1',self.mats1.tables1Id)
				#df_h.write("%-8s%8d%-16s%8d%-s" % ("MATS1",new_id,self.mats1.bdf_str[16:32],_tables1Id,self.matep.bdf_str[40:]))
				bdf_h.write("%-8s%8d%8d%-s" % ("MATS1",new_id,_tables1Id,self.mats1.bdf_str[24:]))
				if writeFlag:
					bdf_h.write("%-8s%8d%-s" % ("TABLES1",_tables1Id,self.mats1.tables1.bdf_str[16:]))
			else:
				bdf_h.write("%-8s%8d%-s" % ("MATS1",new_id,self.mats1.bdf_str[16:]))

		if hasattr(self,'mathe') : 			
			bdf_h.write("%-8s%8d%-s" % ("MATHE",new_id,self.mathe.bdf_str[16:]))
		
		
		if hasattr(self,'psldn1') : 
			# now, I need the PSOLID id for elements referencing this mat
			for pid in self.pids:
				bdf_h.write("%-8s%8d%-8s%8d%-s" % ("PSLDN1",pid,self.psldn1.bdf_str[16:24],new_id,self.psldn1.bdf_str[32:]))
		
		
				
		if hasattr(self,'mattep') : 
			#bdf_h.write(self.mattep.bdf_str)
			if  hasattr(self.mattep,'tablest'):
				_tablestId, writeFlag=CLexApex.trackAndReturnId(self.name,'tablest',self.mattep.tablestId)
				bdf_h.write("%-8s%8d%-16s%8d%-s" %   ("MATTEP",new_id,self.mattep.bdf_str[16:32],_tablestId,self.mattep.bdf_str[40:]))
				if hasattr(self.mattep.tablest, 'Tables1StrL') :
					# before writing back TABLESE1, check and change the MATS1 ids store in dict 
					_tables1IdD={}
					for item in self.mattep.tablest.Tables1IdL:
						# a tuple with id and Bool is stored in list
						_tables1IdD[item]=CLexApex.trackAndReturnId(self.name,'tables1',item)
					# write the TABLEST card (first, split by '\n')
					tmp_str=self.mattep.tablest.bdf_str.split('\n')
					bdf_h.write(tmp_str[0] + '\n')
					n=1
					N=len(tmp_str)
					# there is a blank line following the spliton '\n'...ignore this
					while n< N-1:
						tmp_line=tmp_str[n]
						# check all temp/tables1 pairs on each line
						i=0
						# print first continuation
						bdf_h.write("%-8s" % ('+') )
						while i <4:
							try:
								tmp_t=tmp_line[ 8*(2*i+1)  : 8*(2*i+1) +8 ]
								tmp_id=int(tmp_line[ 8 + 8*(2*i+1)  :  8 + 8*(2*i+1) +8 ].strip()  )
								bdf_h.write("%8s%8d" % (tmp_t,_tables1IdD[tmp_id][0]) )
								if i==3:
									if n==N-2:
										#This is last line, add  ENDT on new line
										bdf_h.write("%-8s\n%8s%-8s" % ("+","+","ENDT") )
									else:
										bdf_h.write("%-8s\n" % ('+') )
							except:
								bdf_h.write("%8s\n" % ("ENDT" ) )
								traceback.print_exc()
								break
							i+=1
						n+=1
						
					# Now print write the TABLES1 cards

					for _tmp_tables1bdf in self.mattep.tablest.Tables1StrL:
						tmp_id=int( _tmp_tables1bdf[8:16])
						if _tables1IdD[tmp_id][1]:
							bdf_h.write("%-8s%8d%-s" % ("TABLES1",_tables1IdD[tmp_id][0], _tmp_tables1bdf[16:] ) )
						
					
				else:
					bdf_h.write("%-8s%8d%-s" %("TABLEST",_tablestId,self.mattep.tablest.bdf_str[16:]))
					#bdf_h.write(self.mattep.tablest.bdf_str)

				
			else:
				bdf_h.write("%-8s%8d%-s" %   ("MATTEP",new_id,self.mattep.bdf_str[16:]))

		if hasattr(self,'matt1') : 
			#bdf_h.write(self.matt1.bdf_str)
			_tablem1Id1_str=""
			_tablem1Id2_str=""
			tablem1_str=""
			if  hasattr(self.matt1,'tablem1_1'):
				_tablem1Id1, writeFlag=CLexApex.trackAndReturnId(self.name,'TABLEM1',self.matt1.tablem1Id1)
				_tablem1Id1_str="%8d" % (_tablem1Id1)
				tablem1_str+="%-8s%8d%-s" % ("TABLEM1",_tablem1Id1,self.matt1.tablem1_1.bdf_str[16:])
			else:
				print("warning: tablem1_1 does not exist")	
			if hasattr(self.matt1,'tablem1_2'):
				_tablem1Id2, writeFlag=CLexApex.trackAndReturnId(self.name,'TABLEM1',self.matt1.tablem1Id2)
				_tablem1Id2_str="%8d" % (_tablem1Id2)
				tablem1_str+="%-8s%8d%-s" % ("TABLEM1",_tablem1Id2,self.matt1.tablem1_2.bdf_str[16:])
			else:
				print("warning: tablem1_2 does not exist")
				
			bdf_h.write("%-8s%8d%8s%-24s%8s%-s" %  ("MATT1",new_id,_tablem1Id1_str,self.matt1.bdf_str[24:48],_tablem1Id2_str,self.matt1.bdf_str[56:]))
			bdf_h.write(tablem1_str)
				
		
		
		#bdf_h.write(self.material.bdf_str)
		#bdf_h.write(self.mat2.bdf_str)
		#bdf_h.write(self.mat4.bdf_str)
		## write PSHELL
		#bdf_h.write(PShellD[self.stack[0]][1])
		
		print("written material info to bdf_h" )



def read_settings(settings_file, tmp_h=""):
	silent_flag = True
	#
	# settings_file=r'C:\Users\e7390\Documents\Predator\Topics\Scripting\Sample_files\UtilityUserSettings.xml'
	inf = open(settings_file, 'r')
	lines = inf.readlines()
	inf.close()
	keys = []
	SettingsD = {}
	n = 0
	while n < len(lines):
		if lines[n].find('<') > -1 and  lines[n].find('\\') < 0 and lines[n].find('/') < 0:
			# found start tag
			# print lines[n]
			tmp = lines[n].strip().replace('<', '').replace('>', '')
			# print tmp
			endtag = "<\\" + tmp + ">"
			endtag2 = "</" + tmp + ">"
			# print endtag
			n += 1
			SettingsD[tmp] = {}
			while lines[n].find(endtag) < 0 and lines[n].find(endtag2) <0 :
				#while lines[n].find(endtag) < 0 :
				if lines[n].find('=') > 1:
					newkey = lines[n].strip().split('=')[0]
					SettingsD[tmp][newkey] = lines[n].strip().split('=')[1]
					n += 1
				elif lines[n].find('#') == 0:
					continue
				else:
					# not a valid settnigs command
					#print "Not a valid settngs command: " + lines[n]
					n += 1
				#print(lines[n])
		n += 1

	# supress write out of all settings

	if silent_flag != True:
		if tmp_h != -1:
			print("====")
			print("Following setting groups detected:")

		for item in reversed(sorted(SettingsD.keys())):
			if tmp_h != -1:
				print("==== " + item + "   " + " ====")

			for key in list(SettingsD[item].keys()):
				if tmp_h != "" and tmp_h != -1:
					print("%-16s : %-12s" % (key, SettingsD[item][key].strip()))
				elif tmp_h == -1:
					continue
				else:
					print("%-16s : %-12s" % (key, SettingsD[item][key].strip()))
	#
	return SettingsD


if 1==0:
	# These are functions needed to specify current Nastran and Apex instrallations
	from winreg import *

	def parse_key(key):
		roots_hives = {
			"HKEY_CLASSES_ROOT": HKEY_CLASSES_ROOT,
			"HKEY_CURRENT_USER": HKEY_CURRENT_USER,
			"HKEY_LOCAL_MACHINE": HKEY_LOCAL_MACHINE,
			"HKEY_USERS": HKEY_USERS,
			"HKEY_PERFORMANCE_DATA": HKEY_PERFORMANCE_DATA,
			"HKEY_CURRENT_CONFIG": HKEY_CURRENT_CONFIG,
			"HKEY_DYN_DATA": HKEY_DYN_DATA
		}


		key = key.upper()
		parts = key.split('\\')
		root_hive_name = parts[0]
		root_hive = roots_hives.get(root_hive_name)
		partial_key = '\\'.join(parts[1:])

		if not root_hive:
			raise Exception('root hive "{}" was not found'.format(root_hive_name))

		return partial_key, root_hive

	def get_values(key, fields):
		partial_key, root_hive = parse_key(key)

		with ConnectRegistry(None, root_hive) as reg:
			with OpenKey(reg, partial_key) as key_object:
				data = {}
				for field in fields:
					try:
						value, type = QueryValueEx(key_object, field)
						data[field] = value
					except WindowsError:
						pass

				return data
				
	def getPath(reg_path):
		out_path=""
		#reg_path = r'HKEY_CLASSES_ROOT\MSC Apex\shell\open\command'
		#reg_path = r'HKEY_CLASSES_ROOT\Applications\nastranw.exe\shell\open\command'
		value = get_values(reg_path, [''])
		if value:
			out_path=value[''].split('"')[1]
		return out_path

def myfloat(item):
	"""converts string to float, if empty string is input, 0.0 is returned"""
	
	if type(item) is str:
		if item=="":
			return 0.0
		#print(type(item))
		tmp_str=item.strip()
		#tmp_str2=tmp_str[0] +  tmp_str[1:].upper().replace('E','').replace('-','e-').replace('+','e+')
		tmp_str2=tmp_str[0] +  tmp_str[1:].upper().replace('-','e-').replace('+','e+').replace('Ee','e')
		return float(tmp_str2)
	else:
		return float(item)
		
def split_BCONECT_cards(bdf_file):
	BConprg_initial_D={}
	BConect_relations_D={}
	
	bdf_h=open(bdf_file,'r')
	lines=bdf_h.readlines()
	bdf_h.close()

	new_bdf_file_h = open(bdf_file, 'w')

	bconect_offset=1000000
	new_bconectIdL=[]

	# first collect all BCONPRG cards - at this point there are no PCONPRP cards
	n=0
	N=len(lines)
	while n<N:
		if lines[n].find('BCONPRG') == 0 :
			bconprg_id = int(lines[n][8:16])
			#Default contacts, coming from bdf exported from Apex before any  contact table editing
			
			tmp_str = lines[n][24:]
			
			while lines[n + 1].find("+") == 0:
				tmp_str = tmp_str + lines[n + 1]
				n += 1
			BConprg_initial_D[bconprg_id]=tmp_str

		n+=1	
	
	#print(BConprg_initial_D)
	
	# max 9999 contact pairs
	# max 99 slaves per master
	bctab_id=-1
	n=0
	while n< len(lines):
		#print(n,"##", lines[n])
		if lines[n].find('Permanent Glue Interaction: ') > 0:	
			BC_header=lines[n]
		elif lines[n].find('BCONECT')==0:
			# there is a n+=1 problem causing bthe header to be missed.... temprarily fix
			if lines[n-1].find('Permanent Glue Interaction: ') > 0:	
				BC_header=lines[n-1]
			# end fix
			bconect_id=int(lines[n][8:16].strip())
			bconprg_id=lines[n][16:24]
			bconprp_id=lines[n][24:32]
			slave_id=lines[n][32:40]
			master_id=lines[n][40:48]
			#print("#",slave_id,"#",master_id,"#",bconprg_id,"#",bconprp_id,"#",)
			# Original glue output-> one line per  BCONECT
			if lines[n+1][0] != '+' and lines[n+1][0] != ' ':
				#print("Found type 1: " + lines[n] )
				new_bdf_file_h.write(BC_header)
				new_bdf_file_h.write(lines[n])
				#new_bdf_file_h.write("%-8s%-8d%-8s%8s%-8s%-8s\n" % ("BCONECT",bconect_id,bconprg_id,bconprp_id,slave_id,master_id) )
				new_bconectIdL.append(bconect_id )
				tmp_str=BConprg_initial_D[bconect_id]
				# Do not write BCONPRG...the original will be used
				#new_bdf_file_h.write("%-8s%8d%8s" % ('BCONPRG',bconect_id,"") + tmp_str )
			# New glue output-> multiple lines per BCONECT
			else:
				slaveL=[]
				masterL=[]
				#print("Found type 2: " + lines[n] )
				while lines[n+1][0] == '+' or lines[n+1][0] == ' ':
					# read the bconect cards
					n+=1
					if lines[n][8:16].strip()=="SLAVES":
						#print("Found Slave")
						#print(lines[n][0])
						while lines[n][0] == '+' or lines[n][0].strip() == '':
							i = 3  # first lines
							while i <= 10:
								try:
									#print("# " + lines[n][(i - 1) * 8])
									if lines[n][(i - 1) * 8] != "+":
										# print inl1[n][(i-1)*8:(i-1)*8+8]
										slaveL.append(int(lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
										i += 1
									else:
										n += 1  # encountered a continuation "+"
										i = 3
								except:
									#traceback.print_exc()
									n += 1
									break
					elif lines[n][8:16].strip()=="MASTERS":
						#print("Found Master")
						while lines[n][0] == '+' or lines[n][0] == ' ':
							i = 3  # first lines
							while i <= 10:
								try:
									if lines[n][(i - 1) * 8] != "+":
										# print inl1[n][(i-1)*8:(i-1)*8+8]
										masterL.append(int(lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
										i += 1
									else:
										n += 1  # encountered a continuation "+"
										i = 3
								except:
									#traceback.print_exc()
									n += 1
									break				
					#n+=1
				#print("Slaves: ",slaveL)		
				#print("Masters: ",masterL)	
				# write new "split" bconect cards
				slv=0
				mast=0
				if master_id.strip() != "":
					for tmp_slave_id in slaveL:
						slv+=1
						#slv=3 bconect_id=1 =>  1000000 + 10000*3 +1 = 1 030 001
						#slv=99  bconect_id=8888 => 1000000 + 10000*99 + 9998 = 1 998 888
						new_id=bconect_offset + bconect_offset/100*slv + bconect_id
						new_bdf_file_h.write(BC_header)
						new_bdf_file_h.write("%-8s%-8d%-8d%8s%-8s%-8s\n" % ("BCONECT",new_id,new_id,bconprp_id,tmp_slave_id,master_id) )
						# Write correpsonding BCONPRG
						tmp_str=BConprg_initial_D[bconect_id]
						new_bdf_file_h.write("%-8s%8d%8s" % ('BCONPRG',new_id,"") + tmp_str )

						new_bconectIdL.append(new_id)
				if  slave_id.strip() != "":
					for tmp_master_id in masterL:
						mast+=1
						#mast=3 bconect_id=1 => 2*1000000 + 10000*3 +1 = 2 030 001
						#mast=99  bconect_id=8888 => 2*1000000 + 100000 + 10000*99 + 9998 = 2 998 888
						new_id=2*bconect_offset  + bconect_offset/100*mast + bconect_id
						new_bdf_file_h.write(BC_header)
						new_bdf_file_h.write("%-8s%-8d%-8d%8s%-8s%-8s\n" % ("BCONECT",new_id,new_id,bconprp_id,slave_id,tmp_master_id) )
						new_bconectIdL.append(new_id)
						# Write correpsonding BCONPRG
						tmp_str=BConprg_initial_D[bconect_id]
						new_bdf_file_h.write("%-8s%8d%8s" % ('BCONPRG',new_id,"") + tmp_str )
						
		elif lines[n].find('BCTABL1')==0:
			i = 3  # first line
			bctabL = []
			bctab_id = int(lines[n][8:16])
			while i <= 10:
				try:
					if lines[n][(i - 1) * 8] != "+":
						bctabL.append(int(lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					# n+=1
					break
		 
		elif lines[n].find('ENDDATA')==0:
			if bctab_id > -1:
				write_BCTABLE(new_bdf_file_h,bctab_id,new_bconectIdL)
			new_bdf_file_h.write('ENDDATA\n')
			
		else:
			new_bdf_file_h.write(lines[n])

		n+=1
	
	new_bdf_file_h.close()
	return

def write_BCTABLE(new_bdf_file_h,bctable_id,BConectL):
	# BCTABLE 1
	i=0
	j=0
	new_bdf_file_h.write("%-8s%-8d" % ("BCTABL1",bctable_id) )
	for bcon_id in  BConectL:
		if i== len(BConectL) -1:
			new_bdf_file_h.write("%8d\n" % (bcon_id) )
		elif (j == 6 and i==6) or (j == 7 and i>6):
			new_bdf_file_h.write("%8d%-8s\n%-8s" % (bcon_id,"+","+") )
			j=0
			i+=1
		else:
			new_bdf_file_h.write("%8d" % (bcon_id) )
			j+=1
			i+=1
	return

def write_sol106_bdf_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag):
	print("I am in write sol106_bdf_stuff")
	#
	#try to obtain any non linear material definitions in material folder
	# specified in UtilityUserSettings: <System><MaterialDatabaseDir>
	MaterialD=readMaterialsFromFile(SettingsD)
	#print MaterialD
	
	
	
	n = 0
	while n < len(bdf_lines):
		line = bdf_lines[n]
		#if n > 2500 and n < 2550:
		#	print((n,bdf_lines[n]))

		# for line in bdf_lines:
		# remove any sol101, cend and BCONTACT already written out
		
		if (line.find('SOL 101') > -1 or line.find('CEND') > -1  ):
			pass
			
		elif line.find('BEGIN BULK') > -1:
			
			for template_bulk_line in template_bulk_lines:
				bdf_h.write(template_bulk_line)
		
		elif line.find('HDF5') > -1:
			pass
		
		
		elif line.find('PARAM,PRTMAXIM,YES') > -1:
			#This is set in the sol101 template file
			pass
			
		elif line.find('MAT1') == 0:
			# Check if any of comment lines above contains Material Name->
			m=1
			while bdf_lines[n-m].find("$") == 0:
				if bdf_lines[n-m].find("$ Material Record:") == 0:
					material_name=bdf_lines[n-m].split(':')[1].strip()
				m+=1
				
			mat_id=int(line[8:16])
			E_orig=myfloat(line[16:24])
			poi_orig=float(line[32:40])
			# adjust for using compact float 7.8-12
			tmp_str=line[40:48].strip()
			try:
				rho_orig=myfloat(tmp_str[0] + tmp_str[1:].replace('-','e-') )
			except:
				rho_orig=0.0
			#rho_orig=float(line[40:48])
			print("Found material: " + material_name + " in bdf file")

			#If Non linear material exists in material folder, replace this	
			print("===" + material_name + "===")
			for mat in MaterialD:
				print(mat)
			print("===")
			
			write_orig_mat_Flag=False
			if material_name in MaterialD:
				print("Found material Name " + material_name)
				tmp_matL=MaterialD[material_name]
				E_file=myfloat(tmp_matL[0][16:24])
				poi_file=float(tmp_matL[0][32:40])
				rho_file=myfloat(tmp_matL[0][40:48])
				# replace material id from MatD with actual MatId
				# 1 Check so that youngs modulus rho and poi has not changed
				if abs(E_orig -E_file)/E_orig > 0.01:
					print(("Warning Youngs modulus for material " + material_name + " in database and material dB file differs more than 1% "))
				if abs(rho_orig -rho_file)/rho_orig > 0.01:
					print(("Warning density for material " + material_name + " in database and material dB file differs more than 1% "))
				if abs(poi_orig - poi_file)/poi_orig > 0.01:
					print(("Warning poissons ratio for material " + material_name + " in database and material dB file differs more than 1% "))
				print("===")
				for s in tmp_matL[1:]:
					print(s)
				print("===")
				
				#
				for mat_line in tmp_matL[1:]:
					#if mat_line.find('MATEP') == 0  or mat_line.find('MATS1') == 0 or mat_line.find('MATHE') == 0:
					if mat_line.find('MATS1') == 0 :							
						bdf_h.write(line)
						bdf_h.write(mat_line[:8] + "%-8d" % (mat_id) + mat_line[16:])
						write_orig_mat_Flag=True
						
					else:
						bdf_h.write(mat_line)								
				if write_orig_mat_Flag==False:
					#write orig mat, this section is entered when there is just a standard linear material
					bdf_h.write(line)
			else:
				print()
				bdf_h.write(line)
				print("Material Name " + material_name + " could not be found")		
			
			
			
			
			
					
		elif line.find('ENDDATA') == 0:
			# here iks a good place to add stuff at the end of the bdf before exporting out
					
			# write the ENDDATA
			bdf_h.write(line)




			
					
		else:
			bdf_h.write(line)
		n += 1

def process_sol106_template(bdf_file, SettingsD, OptionsD={}):

	print(list(SettingsD.keys()))
	
	if 'NASTRAN_JOB_SETUP' in SettingsD:
		if 'SOL106Template' in SettingsD['NASTRAN_JOB_SETUP']:

			template_file = resolve_pathname(SettingsD['NASTRAN_JOB_SETUP']['SOL106Template'])
			print("template_file:",template_file)
			
			try:
				LoadId = int(SettingsD['NASTRAN_JOB_SETUP']['LoadId'])
			except:
				LoadId = 999999

			try:
				SpcId = int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId = 888888

			# read existing bdf file
			#print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			ForceStr = ReadForces(bdf_lines, LoadId)
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			ConctactStrL, BConprgD = ReadContacts(bdf_lines)
			
			StudyFlag=False
			# check if a study is being exported, or a default subcase
			for line in bdf_lines:
				if line.find('Linear Static Scenario') > -1:
					StudyFlag=True
					print("Linear Stati study detected")
					break

			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()
			else:
				template_lines["$ template file," + template_file + " is empty or non existing\n"]

			try:
				maxiter = int(SettingsD['NASTRAN_JOB_SETUP']['MaxNoOfIterations'])
			except:
				maxiter = 25
			# NLPARM MAXDIV	
			try:
				maxdiv = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumDiversions'])
			except:
				maxdiv = 3		
			# NLPARM MAXLS	
			try:
				maxls = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumLineSearces'])
			except:
				maxls = 4

			# NLPARM MAXBIS	
			try:
				maxbis = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumBisections'])
				maxbis_str="%-8d" % (maxbis)
			except:
				maxbis_str = ""
				
			# Convergence criteria	
			try:
				CONV = SettingsD['NASTRAN_JOB_SETUP']['SOL106CONV']
			except:
				CONV = ""
				
			# Number of increments 		
			try:
				NoOfIncrements=int(SettingsD['NASTRAN_JOB_SETUP']['SOL106NoOfIncrements'])
			except:
				NoOfIncrements=10	
					
			try:
				EPSU = float(SettingsD['NASTRAN_JOB_SETUP']['SOL106EPSU'])
				EPSU="%8.3f" % (EPSU)
			except:
				EPSU = ""
				
			try:
				EPSP = float(SettingsD['NASTRAN_JOB_SETUP']['SOL106EPSP'])
				EPSP="%8.3f" % (EPSP)
			except:
				EPSP = ""
					

			# write back
			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			
			template_bulk_lines=[]
			BeginBulkFlag=False
			
			for line in template_lines:
				if line.find('SUBCASE') > -1:
					# if Debug==True:
						# bdf_h.write("NLOPRM NLDBG=ADVDBG\n")
					#
					bdf_h.write("NLPARM=" + str(1) + "\n")
#
					if StudyFlag== False:
						bdf_h.write(line)	
						bdf_h.write("   LOAD=" + str(LoadId) + "\n")
						bdf_h.write("   SPC=" + str(SpcId) + "\n")
						if len(ConctactStrL) == 1:
							bdf_h.write(ConctactStrL[0])
						elif len(ConctactStrL) > 1:
							bdf_h.write(ConctactStrL[0])
							bdf_h.write("$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
				elif line.find('STRESS') > -1 or line.find('DISP') > -1 or 	line.find('ESE') > -1:
					if StudyFlag== False:
						bdf_h.write(line)
					else:
						pass
						
				elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
					print("skip placeholder comment")

				elif line.find('BEGIN BULK') > -1:					
					BeginBulkFlag=True
					#pass
					if 	StudyFlag == False:
						bdf_h.write(line)
						# Write NLPARM for controlling Non linear stepping for SOL106
						bdf_h.write("%-8s%-8d%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s\n%-8s%-8s%-8s%-8s%-8d%-8s%-8d%-16s" % ("NLPARM",1,NoOfIncrements,"","","",maxiter,CONV,"","+","+",EPSU,EPSP,"",maxdiv,"",maxls,""))
						if maxbis_str != "" :
							bdf_h.write("%-8s\n%-8s%-8s" % ("+","+",maxbis_str) )
						bdf_h.write("\n")
						bdf_h.write(ForceStr + "\n")
						bdf_h.write(SpcStr + "\n")
					
					else:
						template_bulk_lines.append(line)
						template_bulk_lines.append("%-8s%-8d%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s\n%-8s%-8s%-8s%-8s%-8d%-8s%-8d%-16s" % ("NLPARM",1,NoOfIncrements,"","","",maxiter,CONV,"","+","+",EPSU,EPSP,"",maxdiv,"",maxls,""))
						if maxbis_str != "" :
							template_bulk_lines.append("%-8s\n%-8s%-8s" % ("+","+",maxbis_str) )
						template_bulk_lines.append("\n")

					
				else:
					if BeginBulkFlag==True:
						template_bulk_lines.append(line)
					else:
						bdf_h.write(line)
			
			#Bcon_strD=write_sol106_contact_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag,BConprgD)
			
			
			# process exported bdf file from Apex
			
			write_sol106_bdf_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag)
			
			bdf_h.close()
			
		else:
			print("No SOL106Template found")
	else:
		print("No Nastran Job Setup info is settings file")
		
def write_BCONPRG_cards(bdf_h,bconD,bconprg_id):

	# write back  the modified BCONPRG card
	# bconD is a dict containing params and values for BCONPRG for each contact pair
	IntParams=['COPTS','COPTM','FGCFLG','FGCNSTR','FGCTSTR','FGCNSTI','FGCTSTI',
	           'FGCRCEN','ICOORD','IGLUE','ISEARCH','JGLUE','OPINTRF','TBINTRF',
			   'CBINTRF','CDINTRF','VXINTRF','OPINGP','CDINGP']
	RealParams=['AUGDIST','BIAS','CINTERF','ERROR','HARDS','FGCNST','FGCTST',
	            'PENALT','SLIDE','STKSLP','TPENALT','VYINTRF','VZINTRF','PTINTRF',
				'XCINTRF','YCINTRF','ZCINTRF','TOLINGP','SFNPNLT','SFTPNLT']
		
	bdf_h.write("%-8s%-8d%8s" % ("BCONPRG", bconprg_id, "") )
	q=0
	m=0
	p=0
	
	# remove duplicate cards created from double sided contact table entries
	
	
	for item in sorted(bconD.keys()):
		param=item
		#deal with blanks
		DefaultFlag=False
		try:
			if bconD[item].strip() == "":
				value="%8s" % ("")
				DefaultFlag=True
			else:
				DefaultFlag=False
		except:
			pass
			
		if not DefaultFlag:	
		
			if param in IntParams:
				print((param,bconD[item]))
				value="%8d" % (int(bconD[item]))
			elif param in RealParams:
				print(bconD[item])
				print(type(bconD[item]))
				tmp_num=myfloat(bconD[item])
				value=num2str(tmp_num)
				#value=num2str(bconD[item])
			else:
				print("Warning, unknown parameter, no format check")
				value=bconD[item]
		
			if ( q == 0 and m < 2 )  or  ( q > 0 and m < 3 ):
				bdf_h.write("%-8s%-8s" % (param,value))
				#print "#1 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
				m+=1
				
			elif ((q == 0 and m == 2) or (q > 0 and m == 3 )) and p != len(list(bconD.keys())) -1 :
				bdf_h.write("%-8s%-8s" % (param,value))
				bdf_h.write("%-8s\n%-8s" %	('+','+'))
				#print "#2 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
				m=0
				q+=1
			#else:
			#	print("what")
			#	#print "#4 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
			
		p+=1
	bdf_h.write("\n")

	return

def write_sol101_contact_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag,BConprgD={},ct_tableD={}):
	print("I am in write sol101_contact_stuff")
	#initiate dict to hold BconD entries (use in csv file)
	Bcon_strD={}
	print("BConprgD")
	print(BConprgD)
	print("ct_tableD")
	print(ct_tableD)
	
	# find out if contact flag is set SOL101DefaultContactType=Touch
	try:
		DefaultContactType = SettingsD['NASTRAN_JOB_SETUP']['SOL101DefaultContactType']
		if DefaultContactType == "Touch" or DefaultContactType == "Touching" or DefaultContactType == "T":
			DefaultContactType = "Touch"
		else:
			DefaultContactType = "Glue"
	except:
		print(" except")
		DefaultContactType = "Glue"
		
	
	# BCPARA ERROR	
	try:
		global_contact_tol = float(SettingsD['NASTRAN_JOB_SETUP']['ContactToleranceError'])
		global_contact_tol_str="%-8.4f" % (global_contact_tol)
	except:
		global_contact_tol_str = ""	
	# BCPARA IBSEP
	try:
		ibsep_crit = int(SettingsD['NASTRAN_JOB_SETUP']['SeparationCriteria'])
	except:
		ibsep_crit = 0		
	# BCPARA FNTOL	
	try:
		fntol = float(SettingsD['NASTRAN_JOB_SETUP']['SeparationForce'])
		fntol_str="%-8.4f" % (fntol)
	except:
		fntol_str = ""

	try:
		TouchErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101TouchingContactError'])
	except:
		TouchErrorTol = ""
		
	try:
		GlueErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101GlueContactError'])
	except:
		GlueErrorTol = "Default"
		
	
	try:
		StressFreeInitialContact=SettingsD['NASTRAN_JOB_SETUP']['StressFreeInitialContact']
		if StressFreeInitialContact.upper()=="TRUE":
			StressFreeInitialContact=True
		else:
			StressFreeInitialContact=False
	except:
		StressFreeInitialContact=False
	
	try:
		ContactMethod=SettingsD['NASTRAN_JOB_SETUP']['ContactMethod']
		print("##0")
		if ContactMethod.upper() == "NODE2SEG":
			print("##1")
			ContactMethod="NODESURF"
		else:
			print("##2")
			ContactMethod="SEGTOSEG"
		print("##3")
	except:
		ContactMethod="SEGTOSEG"

	
	try:
		AnalyticContact=SettingsD['NASTRAN_JOB_SETUP']['AnalyticContact']
		if AnalyticContact.upper()=="TRUE":
			AnalyticContact=True
			Sangle=60
			# Need NODESURF contact for this to work
			ContactMethod="NODESURF"
		else:
			AnalyticContact=False
	except:
		AnalyticContact=False
		
		
	try:
		AutoMsetFlag = SettingsD['NASTRAN_JOB_SETUP']['AUTOMSET']
		if AutoMsetFlag.upper().replace("TRUE","YES") == "YES":
			AutoMsetFlag="YES"
		elif AutoMsetFlag.upper() == "LUSOL":
			pass
		else:
			AutoMsetFlag="NO"
	except:
		AutoMsetFlag="YES"	
		
		
		

	new_bctable1_entriesL=[]
	bctab_remove_idL=[]

	if ct_tableD!={}:
		for item in list(ct_tableD.keys()):
			print((item,ct_tableD[item]))
	
	
	
	n = 0
	while n < len(bdf_lines):
		line = bdf_lines[n]
		#if n > 2500 and n < 2550:
		#	print((n,bdf_lines[n]))

		# for line in bdf_lines:
		# remove any sol101, cend and BCONTACT already written out
		
		if (line.find('SOL 101') > -1 or line.find('CEND') > -1  ) and ct_tableD=={}:
			pass
		
		#A GEOMCHECK is sometimes written out... skip this
		elif line.find('GEOMCHECK') > -1 :
			pass	
			
		elif line.find('BEGIN BULK') > -1 and ct_tableD=={}:
			#bdf_h.write(line)
			
			for template_bulk_line in template_bulk_lines:
				bdf_h.write(template_bulk_line)
		
		elif line.find('HDF5') > -1 and ct_tableD=={}:
			pass
		
		elif line.find('BCONTACT')  > -1 and ct_tableD=={}   :
			if StudyFlag==True:
				bdf_h.write(line)
			else:
				pass
				
		elif line.find('AUTOMSET')  > -1 and ct_tableD=={}   :
			bdf_h.write("%s,%s,%s\n" % ("PARAM","AUTOMSET",AutoMsetFlag))
		
				
		
		elif line.find('PARAM,PRTMAXIM,YES') > -1 and ct_tableD=={}:
			#This is set in the sol101 template file
			pass
			
				
		elif line.find('BCPARA') == 0 and ct_tableD=={}:
			print("I am in BCPARA ContactMethod New " +  ContactMethod)
			bcparaL=line.split(",")
			bcpara_str="%-8s%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s%-8s\n" % ("BCPARA",int(bcparaL[1]),"METHOD",ContactMethod,"FTYPE",6,"ERROR",global_contact_tol_str,"","+")
			bdf_h.write(bcpara_str)
			bcpara_str="%-8s%-8s%-8d%-8s%-8s\n" % ("+","IBSEP",ibsep_crit,"FNTOL",fntol_str)
			bdf_h.write(bcpara_str)
		

		elif line.find('BCONECT') == 0 and ct_tableD != {} :
			print(("I am in BCONECT: ",line))
			for jtem in ct_tableD:
				print((jtem," ",ct_tableD[jtem]," ",len(ct_tableD[jtem]) ) )
			#assume only one line per BCONECT card
			#write only if the slave/master appears in the ct_tableD
			bconect_pair=( int(line[40:48]),int(line[32:40]) )
			reverse_pair=( int(line[32:40]),int(line[40:48]) )
			#bconect_pair=( int(line[32:40]),int(line[40:48]) )
			if bconect_pair in list(ct_tableD.keys()):
				print( ('pair to test: ',bconect_pair) )
				if len(ct_tableD[bconect_pair]) == 3 :
					bdf_h.write(line)
					print( ("bconect_pair ",bconect_pair," added") )
				elif reverse_pair in list(ct_tableD.keys()) and ct_tableD[reverse_pair][0] != '':
						print(("reverse bconect line:",line ) )
						bdf_h.write("%-32s%-8s%-8s\n" % (line[:32],line[40:48].strip(), line[32:40].strip() ) )
				elif ct_tableD[bconect_pair][0] == '' and ct_tableD[reverse_pair][0] == '' :				
					print( ("bconect_pair ",bconect_pair,"Seems to be deactivated") )
					# add id to contact table items to be removed
					bctab_remove_idL.append(int(line[8:16]))
				#else:
				#	print("what now...bconect")
				
		elif line.find('BCBODY1') == 0 and ct_tableD=={}:
			if AnalyticContact==True:
				bcbody_id=int(line[8:16])
				bdf_h.write("%-8s%-8d%-8d%s" % ("BCBODY1",bcbody_id,1000+bcbody_id,line[24:]))
				#BCBDPRP  5001            IDSPL   -1      SANGLE 60.
				bdf_h.write("%-8s%-8d%8s%-8s%8d%8s%8.1f\n" % ("BCBDPRP",1000+bcbody_id,"","IDSPL",-1,"SANGLE",Sangle))
			else:
				bdf_h.write(line)		

		elif line.find('BCONPRG') == 0 :
			#print("I am in BCONPRG")
			bconprg_id = int(line[8:16])
			print(("BCONPRG id:" + str(bconprg_id) ))
			#Default contacts, coming from bdf exported from Apex before any  contact table editing
			
			# fixed in Jaguar
			tmp_str = bdf_lines[n][24:72].replace('+','')
			
			
			while bdf_lines[n + 1].find("+") == 0:
				tmp_str = tmp_str + bdf_lines[n + 1].replace('+       ','').replace('+','')
				n += 1

			if BConprgD != {}:
				print("I am in 1")
				print(BConprgD)
				
				
				bconD={}
				# Organize tmp stri in a list with chunks of 16, then into a dict
				bconL=[tmp_str[i:i + 16] for i in range(0, len(tmp_str), 16)]
				#print bconL	
				for item in bconL:	
					bconD[item[0:8].strip()]=item[8:16].strip()
				#print bconD	
				if bconprg_id in list(BConprgD.keys()):
					ContactType = BConprgD[bconprg_id]
				else:
					ContactType = DefaultContactType
				
				# Adjust contact Type
				if ContactType == "Touch" or ContactType == "T" or ( ContactType == "D" and DefaultContactType == "Touch" ):
					bconD['IGLUE']="%-8s" % (0)
					if TouchErrorTol != "Default":
						bconD['ERROR']=TouchErrorTol
				else:
					if GlueErrorTol != "Default": 
						bconD['ERROR']=GlueErrorTol
				if StressFreeInitialContact==True:
					bconD['ICOORD']=1					
				if ContactMethod=="NODESURF":
					bconD['ISEARCH']=2
				#else:
				#	# Assign Error Tol from GlueContact tolerance
				#	TouchErrorTol=GlueErrorTol
					
				# Adjust  error tol for all contact pairs
				#if TouchErrorTol != "":
				#	bconD['ERROR']="%8.3f" % (TouchErrorTol)
					# No need to add contact for SHELLs B=10 C=10 -> C																	 
				# print the modified BCONPRG card
				print("Contact Type: " + ContactType + " Error: " +  str(bconD['ERROR']))
				# Add contact pair info  for csv file
				Bcon_strD[bconprg_id]=bconD
				
				write_BCONPRG_cards(bdf_h,bconD,bconprg_id)	

			#Default contacts, coming from bdf exported from Apex before any  contact table editing
			elif BConprgD == {} and ct_tableD != {}:
				#print("I am in 2001")
				# form bconD for the bconprg_id using the contact tabel info from the csv, remove "used" pairs
				for pair in list(ct_tableD.keys()):
					reverse_pair=(pair[1],pair[0])
					if len(ct_tableD[pair]) == 3:
						#print("I am in 3")
						if ct_tableD[pair][2]==bconprg_id:
							#print(pair)
							#print(ct_tableD[pair])
							bconD={}
							for item in ct_tableD[pair][1]:
								bconD[item.split(',')[0] ] = item.split(',')[1].strip()
							del ct_tableD[pair]
							
							try:
								del ct_tableD[reverse_pair]
							except:
								pass
							
							write_BCONPRG_cards(bdf_h,bconD,bconprg_id)												
							#bdf_h.write("\n")
							
							break
							
			
			else:
				print("=====What=====")
								
			# Not for SOL101 add friction for Touching contact pairs
			#if ContactType == "Touch" or ContactType == "T":
			#	bdf_h.write("%-8s%-8d%8s%8s%8.4f\n" % ("BCONPRP", bconprg_id, "", "FRIC", DefaultFriction))														

		elif line.find('BCTABL1') == 0 :
			# for first run through (ct_tableD == {}  write bctable as usual)
			# for modifications through bctable edits,  write bctable str  and write back at the end , just before ENDDATA			
			
			i = 3  # first line
			bctabL = []
			print("I am in bctab_1str")
			bctab_id = int(bdf_lines[n][8:16])
			print(("bctab_id",bctab_id))
			while i <= 10:
				try:
					if bdf_lines[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						bctabL.append(int(bdf_lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					# n+=1
					break
			# write back
			# also create str list for BCTABLE =0
			if ct_tableD =={}:
				#bdf_h.write("%-8s%8d" % ("BCTABL1", bctab_id))
				bctab_1str=  ("%-8s%8d" % ("BCTABL1", bctab_id))
				bctab_0str = ("%-8s%8d" % ("BCTABL1", 0))
				
				bctab_str=""
				
				# no need for SOL101
				#new_bctabL = [id for id in bctabL if id not in BoltBConectL]
				new_bctabL=bctabL
				m = 2
				for id in new_bctabL:
					#if id not in BoltBConectL:
					if id not in bctab_remove_idL:
						#bdf_h.write("%8d" % (id))
						bctab_str +=  "%8d" % (id) 
					#bctab_str += "%8d" % (id)
					if m == 8:
						if id != new_bctabL[-1]:
							#bdf_h.write("%-8s\n%-8s" % ("+", "+"))
							bctab_str += "%-8s\n%-8s" % ("+", "+")
							m += 1
						else:
							#bdf_h.write("\n")
							bctab_str += "\n"
						m = 1
					elif m != 8 and id == new_bctabL[-1]:
						#bdf_h.write("\n")
						bctab_str += "\n"
					else:
						m += 1
			
				# if this is first run through, write BCTABLE
				# if not, write to str and add just before enddate

				# Only write BCTABLE1 =0   when BConprgD != {}

				bdf_h.write(bctab_1str)
				bdf_h.write(bctab_str)
				if BConprgD != {}:
					if StressFreeInitialContact==True:
						bdf_h.write(bctab_0str)
						bdf_h.write(bctab_str)
					else:
						bdf_h.write("$Stress free initial contact is not requested\n")

					
		elif line.find('ENDDATA') == 0 and ct_tableD !={}:

			#new_bctable1_entriesL=[]
			#write any remaning (new, coming from contact table edit) BCONECT and BCONPRG cards"
			for pair in list(ct_tableD.keys()):
				print("I am in writing remaining BCONPRGD cards")
				if len(ct_tableD[pair]) ==3:
					print((ct_tableD[pair]))
					new_bconprg_id=ct_tableD[pair][2]
					new_contact_type=ct_tableD[pair][0]
					bdf_h.write("$ New contact pair created from contact table edit #" +  new_contact_type + "\n" )
					#BCONECT 3       3               4       2
					bdf_h.write("%-8s%-8d%-8d%8s%8d%8d\n" % ("BCONECT",new_bconprg_id,new_bconprg_id,"",pair[1],pair[0]) )
					bconD={}
					for item in ct_tableD[pair][1]:
						print(item.split(',')[1].strip())
						bconD[item.split(',')[0] ] = item.split(',')[1].strip()
					del ct_tableD[pair]
					write_BCONPRG_cards(bdf_h,bconD,new_bconprg_id)	
					new_bctable1_entriesL.append(new_bconprg_id)
			


			#bdf_h.write("%-8s%8d" % ("BCTABL1", bctab_id))
			bctab_1str=  ("%-8s%8d" % ("BCTABL1", bctab_id))
			bctab_0str = ("%-8s%8d" % ("BCTABL1", 0))
			
			bctab_str=""
			
			# no need for SOL101
			#new_bctabL = [id for id in bctabL if id not in BoltBConectL]
			# remove entries from bctab_remove_idL
			j=0
			for bctab_id in bctabL:
				if bctab_id in bctab_remove_idL:
					del bctabL[j]
					print(("removed ", str(bctab_id), " from list"))
				j+=1
			new_bctabL=bctabL+new_bctable1_entriesL
			m = 2
			for id in new_bctabL:
				# if id not in BoltBConectL:
				#bdf_h.write("%8d" % (id))
				bctab_str += "%8d" % (id)
				if m == 8:
					if id != new_bctabL[-1]:
						#bdf_h.write("%-8s\n%-8s" % ("+", "+"))
						bctab_str += "%-8s\n%-8s" % ("+", "+")
						m += 1
					else:
						#bdf_h.write("\n")
						bctab_str += "\n"
					m = 1
				elif m != 8 and id == new_bctabL[-1]:
					#bdf_h.write("\n")
					bctab_str += "\n"
				else:
					m += 1
				
			# write the updated BCTABLE1 cards
			bdf_h.write("$Updated BCTABL1 reflecting changes in contact table\n")
			bdf_h.write(bctab_1str)
			bdf_h.write(bctab_str)
			# Only write BCTABLE1 =0   when BConprgD != {}
			if BConprgD != {}:
				if StressFreeInitialContact==True:
					bdf_h.write(bctab_0str)
					bdf_h.write(bctab_str)
				else:
					bdf_h.write("$Stress free initial contact is not requested\n")

			
					
					
			# write the ENDDATA
			bdf_h.write(line)		
					
		else:
			bdf_h.write(line)
		n += 1
	
	
	###
	#for key in ct_tableD.keys():
	#	print((key,ct_tableD[key],len(ct_tableD[key])))
	
	#print("writing Bcon_strD")
	#print(Bcon_strD)
	return Bcon_strD
	
def read_contacttable_csv(csv_file,SettingsD={}):
	csv_file_h=open(csv_file,'r')
	csv_lines=csv_file_h.readlines()
	csv_file_h.close()
	# print("==================")
	# for line in csv_lines:
		# print(line)
	# print("==================")
	
	n=0
	#bodies=[]
	body2indexD={}
	#bodyL=[0]*len(csv_lines)
	# key is contact pair tuple (3,1), value is list with contact status and nested list of params ["T", [params] ]
	ct_tableD={}
	bodies=[]
	max_bconprg_id=0
	while n < len(csv_lines):
		if n!=0:
			tmpL=csv_lines[n].split(',')
		#header
		if n==0:		
			# index 3 points at body3 et.c.
			tmpL=[]
			_slaskL=csv_lines[n].split(',')
			tmpL.append(_slaskL[0])
			for tmp_item in _slaskL:
				if tmp_item.strip() != "":
					tmpL.append(tmp_item)
			
			p=1
			for body in tmpL[1:]:
			# first body is index 1 et.c.
				body2indexD[body.strip()]=p
				bodies.append(body.strip())
				p+=1
			
		# contact table	
		elif tmpL[0] in bodies and len(tmpL[0].strip()) > 0 and  ( tmpL[1].strip() =="" or tmpL[1]=="G" or tmpL[1]=="T"):
			m=1
			while m < len(tmpL):
				ct_tableD[(n,m)]=[tmpL[m].strip()]
				m+=1
		# header for contact parameters for pair
		elif tmpL[0] == "Contact pair id":
			paramL=tmpL[3:]
			#remaining lines are contact parameters (first columns is an integer representing BCONPRG id)			
			while n < len(csv_lines)-1:
				n+=1
				tmpL=csv_lines[n].split(',')

				tmp_pair=(body2indexD[tmpL[1]],body2indexD[tmpL[2]])
				reversed_tmp_pair=(body2indexD[tmpL[2]],body2indexD[tmpL[1]])
				tmp_paramL=[]
				p=0
				for param in tmpL[3:]:
					# check consistency so that IGLUE =0 if T in contact table
					if paramL[p].strip()=='IGLUE':
						if ct_tableD[tmp_pair][0] == 'T' and int(param.strip()) != 0 :
							print((("Warning, IGLUE  has to be 0 for Touching contact... override specified IGLUE value from csv for pair"),tmp_pair))
							param='0'
						elif ct_tableD[tmp_pair][0] == 'G' and int(param.strip()) == 0  :
							print((("Warning, IGLUE  has to be > 0 for Glued contact... override specified IGLUE value from csv"),tmp_pair))
							param='2'
						elif ct_tableD[tmp_pair][0] == '' :
							# Do Not add contact pair to table even though an entry exist among params
							print("Warning, there is no corresponding contact table entry, to this pair. Skip  pair ")
							# check for the other diagonal
							if ct_tableD[reversed_tmp_pair][0] == 'T'  and int(param.strip())  !=0 :
								print((("Warning, IGLUE  has to be 0 for Touching contact... override specified IGLUE value from csv for  pair [Using Diagonal]"),tmp_pair))
								param='0'
							elif ct_tableD[reversed_tmp_pair][0] == 'G'  and int(param.strip()) == 0 :
								print((("Warning, IGLUE  has to be > 0 for Glued contact... override specified IGLUE value from csv [Using Diagonal] "),tmp_pair))
								param='2'
							#============================
							# Add contact pair to table even though an entry exist among params
							# print("Warning, there is no corresponding contact table entry, to this pair. Contact pair added")
							# if param.strip() == 0:
								# # param iglue indicates Touching
								# ct_tableD[tmp_pair][0]='T'
								# ct_tableD[(tmp_pair(1),tmp_pair(0))][0]='T'
							# else:
								# # param iglue indicates Glue
								# ct_tableD[tmp_pair][0]='G'
								# ct_tableD[(tmp_pair(1),tmp_pair(0))][0]='G'
						else:
							pass
							#print("What the ...")
							#print(ct_tableD[tmp_pair])
							#print("==========")
					tmp_paramL.append( paramL[p].strip()  + ","  +  param.strip()[:8] )
					p+=1
				
				if ct_tableD[tmp_pair][0].strip() == '' and ct_tableD[reversed_tmp_pair][0].strip() != '':
					print("Info, Params will be applied to opposite diagonal")
				
				if ct_tableD[tmp_pair][0].strip() != '' or ct_tableD[reversed_tmp_pair][0].strip() != '':
					print("================")
					print(( ("Adding contact params to  " , tmp_pair), ct_tableD[tmp_pair]))
				
					ct_tableD[tmp_pair].append(tmp_paramL)
					# add bconprg id
					bconprg_id=int(tmpL[0])
					if bconprg_id > max_bconprg_id:
						max_bconprg_id=bconprg_id
					ct_tableD[tmp_pair].append(int(tmpL[0]))

				else:
					ct_tableD[tmp_pair]=['']
					print("================")
					print( ("No matching pair for " , tmp_pair) )

		else:
			print(tmpL)
			pass
		n+=1
	
	#print("=======A========")
	#for key in list(ct_tableD.keys()):
	#	print((key,ct_tableD[key]))
	#
	#print("=======A========")
	

	print(("max_bconprg_id",max_bconprg_id))
	print(("Number of  bodies are  :" + str(len(bodies)) ))
	
	#print(bodies)
	
	# Clean up table for excess spaces
	for key in 	list(ct_tableD.keys()):
		reverse_key=(key[1],key[0])
		# remove cases where there are more columns than rows"
		if key[1] > len(bodies):
			# invalid 
			del(ct_tableD[key])
	print("####")
	print(ct_tableD)
	print("####")
	# Check if there are changes  from reading csv info that is not existing in params (bconprg)
	for key in 	sorted(ct_tableD.keys()):
		# read table row by row
		reverse_key=(key[1],key[0])
		#print(key)

		if ct_tableD[key][0].strip() == '' and ct_tableD[reverse_key][0].strip() =='':
			# remove entries if both pairs are blank
			if len(ct_tableD[key])==3:
				print("Params exist but contact pair is cleared")
				ct_tableD[key]==['']
		elif ct_tableD[key][0].strip() == '' and ct_tableD[reverse_key][0].strip() !='':		
			# the first entry  is blank, but the second is non-blank
			print(("1-One sided contact detected for ",key,ct_tableD[key]))
			if len(ct_tableD[key]) == 3:
				#shift params to non-zero contact pair, reset blanked param
				print("1- Reuse contact params data for one sided contact")
				ct_tableD[reverse_key]=ct_tableD[key]
				ct_tableD[key]=['']
		elif ct_tableD[key][0].strip() != '' and ct_tableD[reverse_key][0].strip() =='':
			# the first entry  is not blank, but the second is blank
			print(("2-One sided contact detected for ",key,ct_tableD[key]))
			if len(ct_tableD[key]) == 3:
				pass
			elif len(ct_tableD[reverse_key]) == 3:
				#shift params to non-zero contact pair, reset blanked param
				print("2-Reuse contact params data for one sided contact")
				ct_tableD[key]=ct_tableD[reverse_key]
				ct_tableD[reverse_key]=['']
		else:
			print("Double sided contact")
			# Check if pair has non-blank inconsistent data 
			if ct_tableD[key][0].strip().upper() != ct_tableD[reverse_key][0].strip().upper():
				print("###Warning### Non consitent pairs - Upper diagonal will take precedence unless there are params for lower diagonal")
				if len(ct_tableD[key]) == 3 and len(ct_tableD[reverse_key]) != 3:
					ct_tableD[reverse_key]=ct_tableD[key]
				elif len(ct_tableD[key]) != 3 and len(ct_tableD[reverse_key]) == 3:
					ct_tableD[key]=ct_tableD[reverse_key]
				else:
					#no params for any of the pairs, use the pair in upper right diaginal
					ct_tableD[reverse_key]=ct_tableD[key]
					
				
		
			if len(ct_tableD[key]) == 3 and len(ct_tableD[reverse_key]) == 3:
				pass
			elif len(ct_tableD[key]) == 3 and len(ct_tableD[reverse_key]) != 3:
				ct_tableD[reverse_key]=ct_tableD[key]
			elif len(ct_tableD[key]) != 3 and len(ct_tableD[reverse_key]) == 3:
				ct_tableD[key]=ct_tableD[reverse_key]
			


		
			# #if at least one of them is blank, then it is still ok and one-sided contact is detected
			# if ct_tableD[key][0].strip() == '' or ct_tableD[reverse_key][0].strip() =='':
				# print(("One sided contact detected for ",key,ct_tableD[key]))
				# if ct_tableD[key][0].strip() == "" and ct_tableD[reverse_key][0].strip() !='':
					# if len(ct_tableD[key]) == 3:
						# print((key,"params correspond to the active side in one sided contact"))
					# else:
						# print((key,"Swap ct_table keys as params does not correlate to the right side"))
						# ct_tableD[key]=ct_tableD[reverse_key]
						# ct_tableD[reverse_key]=['']
						
		
		# Old version check so that diagonals are consistent
		# if 	ct_tableD[key][0].strip() != ct_tableD[reverse_key][0].strip():
			# #if at least one of them is blank, then it is still ok and one-sided contact is detected
			# if ct_tableD[key][0].strip() == '' or ct_tableD[reverse_key][0].strip() =='':
				# print(("One sided contact detected for ",key,ct_tableD[key]))
				# if ct_tableD[key][0].strip() == "" and ct_tableD[reverse_key][0].strip() !='':
					# if len(ct_tableD[key]) == 3:
						# print((key,"params correspond to the active side in one sided contact"))
					# else:
						# print((key,"Swap ct_table keys as params does not correlate to the right side"))
						# ct_tableD[key]=ct_tableD[reverse_key]
						# ct_tableD[reverse_key]=['']




		
			# else:
				# print(("Error: Non consistent diagonal value for pair: ",key))
				# #ct_tableD[(key[1],key[0])][0]=ct_tableD[key]
					
			#print((key,ct_tableD[key]))
		if 1==1:
			if ct_tableD[key][0].strip() != "":
				# found a contact pair
				if len(ct_tableD[key]) ==1:
				
					print(" Found pair without   params")
					#print(ct_tableD[key])
					print((key,ct_tableD[key]))
					reversed_key=(key[1],key[0])
					if len(ct_tableD[reversed_key] ) ==3:
						print("Found mate - do not use these for contact pair definition")
					#	ct_tableD[key].append(ct_tableD[reversed_key][1])
					#	print((reversed_key,ct_tableD[reversed_key]))
					else:

						print("New contact table entry, with key. "" with no params, create one using defaults")
						
						# Glued contact
						if ct_tableD[key][0]=="G":
							# assume only 3D contacts for now.
							ct_tableD[key].append(['IGLUE,2'])
							try:
								if SettingsD['NASTRAN_JOB_SETUP']['SOL101GlueContactError'].upper() == "DEFAULT":
									pass
								else:
									ct_tableD[key][1].append('ERROR,' + SettingsD['NASTRAN_JOB_SETUP']['SOL101GlueContactError'])
							except:
								# use default error tol, leave blank
								pass
						#Touching contact	
						else:
							ct_tableD[key].append(['IGLUE,0'])		
							try:
								ct_tableD[key][1].append('ERROR,' + SettingsD['NASTRAN_JOB_SETUP']['SOL101TouchingContactError'])
							except:
								# use default error tol, leave blank
								ct_tableD[key][1].append('ERROR,' + '')
						ct_tableD[key].append(max_bconprg_id + 1)
						max_bconprg_id+=1	
		
	for key in sorted(ct_tableD.keys()):
		print((key,ct_tableD[key]))
		
	return ct_tableD, bodies

def old_write_sol101_contact_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag,BConprgD):
	
	#initiate dict to hold BconD entries (use in csv file)
	Bcon_strD={}
	
	
	# find out if contact flag is set SOL101DefaultContactType=Touch
	try:
		DefaultContactType = SettingsD['NASTRAN_JOB_SETUP']['SOL101DefaultContactType']
		if DefaultContactType == "Touch" or DefaultContactType == "Touching" or DefaultContactType == "T":
			DefaultContactType = "Touch"
		else:
			DefaultContactType = "Glue"
	except:
		print(" except")
		DefaultContactType = "Glue"
		
	
	# BCPARA ERROR	
	try:
		global_contact_tol = float(SettingsD['NASTRAN_JOB_SETUP']['ContactToleranceError'])
		global_contact_tol_str="%-8.4f" % (global_contact_tol)
	except:
		global_contact_tol_str = ""	
	# BCPARA IBSEP
	try:
		ibsep_crit = int(SettingsD['NASTRAN_JOB_SETUP']['SeparationCriteria'])
	except:
		ibsep_crit = 0		
	# BCPARA FNTOL	
	try:
		fntol = float(SettingsD['NASTRAN_JOB_SETUP']['SeparationForce'])
		fntol_str="%-8.4f" % (fntol)
	except:
		fntol_str = ""

	try:
		TouchErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101TouchingContactError'])
	except:
		TouchErrorTol = ""
		
	try:
		GlueErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101GlueContactError'])
	except:
		GlueErrorTol = "Default"
		
	
	try:
		StressFreeInitialContact=SettingsD['NASTRAN_JOB_SETUP']['StressFreeInitialContact']
		if StressFreeInitialContact.upper()=="TRUE":
			StressFreeInitialContact=True
		else:
			StressFreeInitialContact=False
	except:
		StressFreeInitialContact=False
	
	try:
		ContactMethod=SettingsD['NASTRAN_JOB_SETUP']['ContactMethod']
		print("##0")
		if ContactMethod.upper() == "NODE2SEG":
			print("##1")
			ContactMethod="NODESURF"
		else:
			print("##2")
			ContactMethod="SEGTOSEG"
		print("##3")
	except:
		ContactMethod="SEGTOSEG"

	
	try:
		AnalyticContact=SettingsD['NASTRAN_JOB_SETUP']['AnalyticContact']
		if AnalyticContact.upper()=="TRUE":
			AnalyticContact=True
			Sangle=60
			# Need NODESURF contact for this to work
			ContactMethod="NODESURF"
		else:
			AnalyticContact=False
	except:
		AnalyticContact=False

		
	n = 0
	while n < len(bdf_lines):
		line = bdf_lines[n]
		# for line in bdf_lines:
		# remove any sol101, cend and BCONTACT already written out
		
		if line.find('SOL 101') > -1 or line.find('CEND') > -1:
			pass
			
		elif line.find('BEGIN BULK') > -1:
			#bdf_h.write(line)
			
			for template_bulk_line in template_bulk_lines:
				bdf_h.write(template_bulk_line)
		
		elif line.find('HDF5') > -1:
			pass
		
		elif line.find('BCONTACT') > -1:
			if StudyFlag==True:
				bdf_h.write(line)
			else:
				pass
		
		elif line.find('PARAM,PRTMAXIM,YES') > -1:
			#This is set in the sol101 template file
			pass
			
				
		elif line.find('BCPARA') == 0:
			print("I am in BCPARA ContactMethod New" +  ContactMethod)
			bcparaL=line.split(",")
			bcpara_str="%-8s%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s%-8s\n" % ("BCPARA",int(bcparaL[1]),"METHOD",ContactMethod,"FTYPE",6,"ERROR",global_contact_tol_str,"","+")
			bdf_h.write(bcpara_str)
			bcpara_str="%-8s%-8s%-8d%-8s%-8s\n" % ("+","IBSEP",ibsep_crit,"FNTOL",fntol_str)
			bdf_h.write(bcpara_str)
				
				
				
		#*******New BCONPRG**********
		elif line.find('BCONPRG') == 0 :
			# concatenate all bconprg_strings, remove cont
			bconprg_id = int(line[8:16])
			# Hawk Beta is writing one extra set of 8 chars in bdf file 
			# start reading from position 32... should be position 24 once this is fixed
			#tmp_str = bdf_lines[n][32:72].replace('+','')
			#fixed in Jaguar
			tmp_str = bdf_lines[n][24:72].replace('+','')
			while bdf_lines[n + 1].find("+") == 0:
				tmp_str = tmp_str + bdf_lines[n + 1].replace('+       ','').replace('+','')
				n += 1			
			bconD={}
			# Organize tmp stri in a list with chunks of 16, then into a dict
			bconL=[tmp_str[i:i + 16] for i in range(0, len(tmp_str), 16)]
			#print bconL	
			for item in bconL:	
				bconD[item[0:8].strip()]=item[8:16].strip()
			#print bconD	
			if bconprg_id in list(BConprgD.keys()):
				ContactType = BConprgD[bconprg_id]
			else:
				ContactType = DefaultContactType
			
			# Adjust contact Type
			if ContactType == "Touch" or ContactType == "T" or ( ContactType == "D" and DefaultContactType == "Touch" ):
				bconD['IGLUE']="%-8s" % (0)
				if TouchErrorTol != "Default":
					bconD['ERROR']=TouchErrorTol
			else:
				if GlueErrorTol != "Default": 
					bconD['ERROR']=GlueErrorTol
			if StressFreeInitialContact==True:
				bconD['ICOORD']=1					
			if ContactMethod=="NODESURF":
				bconD['ISEARCH']=2
			#else:
			#	# Assign Error Tol from GlueContact tolerance
			#	TouchErrorTol=GlueErrorTol
				
			# Adjust  error tol for all contact pairs
			#if TouchErrorTol != "":
			#	bconD['ERROR']="%8.3f" % (TouchErrorTol)
				# No need to add contact for SHELLs B=10 C=10 -> C																	 
			# print the modified BCONPRG card
			print("Contact Type: " + ContactType + " Error: " +  str(bconD['ERROR']))

			write_BCONPRG_cards(bdf_h,bconD,bconprg_id)

			# bdf_h.write("%-8s%-8d%8s" % ("BCONPRG", bconprg_id, "") )
			# q=0
			# m=0
			# p=0
			# for item in sorted(bconD.keys()):
				# if ( q == 0 and m < 2 )  or  ( q > 0 and m < 3 ):
					# bdf_h.write("%-8s%-8s" % (item,bconD[item]))
					# #print "#1 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
					# m+=1
					
				# elif ((q == 0 and m == 2) or (q > 0 and m == 3 )) and p != len(bconD.keys()) -1 :
					# bdf_h.write("%-8s%-8s" % (item,bconD[item]))
					# bdf_h.write("%-8s\n%-8s" %	('+','+'))
					# #print "#2 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
					# m=0
					# q+=1
				# else:
					# print "what"
					# #print "#4 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
					
				# p+=1
				
				
				
				
			bdf_h.write("\n")
			# Add contact pair info  for csv file
			Bcon_strD[bconprg_id]=bconD
			# Not for SOL101 add friction for Touching contact pairs
			#if ContactType == "Touch" or ContactType == "T":
			#	bdf_h.write("%-8s%-8d%8s%8s%8.4f\n" % ("BCONPRP", bconprg_id, "", "FRIC", DefaultFriction))														
		elif line.find('BCBODY1') == 0:
			if AnalyticContact==True:
				bcbody_id=int(line[8:16])
				bdf_h.write("%-8s%-8d%-8d%s" % ("BCBODY1",bcbody_id,1000+bcbody_id,line[24:]))
				#BCBDPRP  5001            IDSPL   -1      SANGLE 60.
				bdf_h.write("%-8s%-8d%8s%-8s%8d%8s%8.1f\n" % ("BCBDPRP",1000+bcbody_id,"","IDSPL",-1,"SANGLE",Sangle))
			else:
				bdf_h.write(line)
		elif line.find('BCTABL1') == 0:
			i = 3  # first line
			bctabL = []
			bctab_id = int(bdf_lines[n][8:16])
			while i <= 10:
				try:
					if bdf_lines[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						bctabL.append(int(bdf_lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					# n+=1
					break
			# write back
			# also create str list for BCTABLE =0

			bdf_h.write("%-8s%8d" % ("BCTABL1", bctab_id))
			bctab_0str = ("%-8s%8d" % ("BCTABL1", 0))
			# no need for SOL101
			#new_bctabL = [id for id in bctabL if id not in BoltBConectL]
			new_bctabL=bctabL
			m = 2
			for id in new_bctabL:
				# if id not in BoltBConectL:
				bdf_h.write("%8d" % (id))
				bctab_0str += "%8d" % (id)
				if m == 8:
					if id != new_bctabL[-1]:
						bdf_h.write("%-8s\n%-8s" % ("+", "+"))
						bctab_0str += "%-8s\n%-8s" % ("+", "+")
						m += 1
					else:
						bdf_h.write("\n")
						bctab_0str += "\n"
					m = 1
				elif m != 8 and id == new_bctabL[-1]:
					bdf_h.write("\n")
					bctab_0str += "\n"
				else:
					m += 1
			if StressFreeInitialContact==True:
				bdf_h.write(bctab_0str)
			else:
				bdf_h.write("$Stress free initial contact is not requested\n")

		else:
			bdf_h.write(line)
		n += 1
	
	return Bcon_strD

def read_RSPLINE(bdf_file):
	# reads RSPLINE cards and stores in dict with EID as key, and another dict as value.
	# this dict, in turns, hold diamter ratio   for first GridId in spline, and dofs (or blanks)  for the other Grids
	# dict also shows the ordered GridIds  under key 'GridList'
	file_h=open(bdf_file,'r')
	bdf_lines=file_h.readlines()
	file_h.close()
	#
	n=0
	N=len(bdf_lines)
	RSplineD={}
	while n < N:
		line=bdf_lines[n]
		if line.find('RSPLINE') > -1:
			# concatenate all rspline lines, remove cont
			rspline_eid = int(line[8:16])
			rspline_1st=int(line[24:32])
			rspline_wt=float(line[16:24])
			# First 4 fields are card name id and and ref point and wt factor 
			
			# start reading from position 32
			tmp_str = bdf_lines[n][32:72].replace('+','')
			while bdf_lines[n + 1].find("+") == 0:
				tmp_str = tmp_str + bdf_lines[n + 1].replace('+       ','').replace('+','').replace('\n','')
				n += 1			
			tmp_rsplineD={}
			
			# Organize tmp str in chunks of 16 to have grid and weight factors 			
			rsplineL=[tmp_str[i:i + 16] for i in range(0, len(tmp_str), 16)]
			
			#print rsplineL	
			for item in rsplineL:	
				tmp_rsplineD[int(item[0:8])]=item[8:16].strip()
				
			GridL=[rspline_1st]
			GridL += [int(i[0:8].strip()) for i in rsplineL ]	
			tmp_rsplineD[rspline_1st]=rspline_wt
			tmp_rsplineD['GridList']=GridL
			RSplineD[rspline_eid]=tmp_rsplineD
					
		n+=1
	#print(GridL)
	#print(rsplineD)
	return RSplineD

def convert_RSPLINE_to_RBE3(RSplineD):
	# converts RSPLINE_to_RBE3
	#wt_dummy=0.700811
	Rbe3StrL=["$RBE3s converted from RSPLINEs, distinguished by REF dofs 12345 \n"]
	for eid in list(RSplineD.keys()):
		tmpD=RSplineD[eid]
		GridList=tmpD['GridList']
		RefGrid=GridList[0]
		Wtfact=tmpD[RefGrid]
		Rbe3StrL.append("%-8s%8d%8s%8d%8d" % ("RBE3",eid,"",RefGrid,12345))
		K=len(GridList)
		i=0 #row counter
		j=0	#field counter
		k=1 #grid counter
		tmp_str=""
		# to ensure the order is maintained going from spline to RBE3, the RBE3 wtfact will be used for sequencing
		# starting from 0.90101 to 0.90100 (maximum of 100 points in spline)
		cnt=1
		for gi in GridList[1:]:
			wt_fact=0.90100 + cnt*1e-5
			tmp_str+="%8.5f%8s%8d" % (wt_fact,tmpD[gi],gi)
			cnt+=1
		# split tmp _str in chinks of  8 
		chunks=[ tmp_str[l:l+8] for l in range(0, len(tmp_str), 8) ]
		n=0
		# the  last 3 chunks represent last item on splinse, use dof "456" and Wtfact
		chunks[-2]="   456"
		chunks[-3]="%8.5f" % (Wtfact)
		while n< len(chunks):
			# first line... just type first chunk more than one gi or  not first but 8th field and not  not end of data
			#Rbe3StrL.append("#" + str(i) + " " + str(j) + " " +  str(n) + " " + str(len(chunks)))
			if ( i==0 and j==3 and n!=len(chunks) ) or  ( i!=0 and j == 7 and n!=len(chunks) ):
				Rbe3StrL.append(chunks[n] + "%-8s\n%-8s" % ("+","+"))
				i+=1
				j=0

			# first line... only one gi or not first line but end of data
			elif ( i==0 and j==0 and n==len(chunks) ) or ( i!=0 and n==len(chunks) ):
				Rbe3StrL.append(chunks[n] + "\n" )
			# 
			else:
				Rbe3StrL.append(chunks[n])
				j+=1
			
			n+=1
	return Rbe3StrL	

def writeback_RBE3_from_RSPLINE(bdf_file,Rbe3StrL):

	# first - find the RSPLINEs
	# reads RSPLINE cards and stores in dict with EID as key, and another dict as value.
	# this dict, in turns, hold diamter ratio   for first GridId in spline, and dofs (or blanks)  for the other Grids
	# dict also shows the ordered GridIds  under key 'GridList'
	# to ensure the order is maintained going from spline to RBE3, the RBE3 wtfact will be used for sequencing
	# starting from 0.90101 to 0.90100 (maximum of 100 points in spline)
	file_h=open(bdf_file,'r')
	bdf_lines=file_h.readlines()
	file_h.close()
	
	file_h=open(bdf_file,'w')
	
	#
	n=0
	N=len(bdf_lines)
	RSplineD={}
	while n < N:
		line=bdf_lines[n]
		if line.find('RSPLINE') > -1:
			#rspline_eid = int(line[8:16])
			file_h.write("$" + line[1:])
			# First 4 fields are card name id and and ref point and wt factor 			
			while bdf_lines[n + 1].find("+") == 0:
				file_h.write("$" + bdf_lines[n + 1][1:]) 
				n += 1
		elif line.find('BEGIN BULK') ==0:
			#write back all RBE3s
			file_h.write(line)
			for item in Rbe3StrL:
				file_h.write(item)
			file_h.write("\n")	
		else:
			file_h.write(line)
		n+=1
	file_h.close()

def writeback_RSPLPINE_from_RBE3(bdf_file):
	#Read RBAE3s (created from orig splines) - the order of the grids are preserved by the weifht factors
	# starating with 0.90101, 0.90102, up to the max 0.90200
	file_h=open(bdf_file,'r')
	bdf_lines=file_h.readlines()
	file_h.close()

	n = 0
	while n < len(bdf_lines):
		if bdf_lines[n].find("RBE3 ") == 0:
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				# print bdf_lines[n+p]
				p += 1
			#
			i=n
			tmp_str=""
			while i < (n+p):
				tmp_str=tmp_str + bdf_lines[i].replace('\n','')
				i+=1
			
			#tmp_str = "".join(bdf_lines[n:n + p])
			
			chunks=[ tmp_str[l:l+8] for l in range(0, len(tmp_str), 8) if tmp_str[l:l+8].find('+') <0 ]
			#bdf_h.write(a)
			n = n + p
		else:
			n+=1
	print(tmp_str)
	print("####")
	print(chunks)
	print("####")
	
	#first 5 items is eid, ref_grid and dofs
	#tmp_spline[2] is temporarily left blank. It will hold the diameter ratio (read from indep RBE3 dofs)
	
	tmp_spline=["%-8s" % ("RSPLINE"),"%8s" % (chunks[1].strip()),"%8s" % (""),"%8s" % (chunks[3].strip())]
	print(tmp_spline)

	giL=[]
	wtiL=[]
	n=5
	print(("len(chunks)" + str(len(chunks) ) ))
	while n < len(chunks):
		#check if float
		if chunks[n].find('.') >-1:
			# Check if internal gi
			# to ensure the order is maintained going from spline to RBE3, the RBE3 wtfact will be used for sequencing
			# starting from 0.90101 to 0.90200 (maximum of 100 points in spline)
			if ( float(chunks[n]) > 0.90 ) and (float(chunks[n]) < 0.902):
				pass
				print(("##" +str(n) ))
				print((chunks[n:n+3]))
				wtiL.append(chunks[n])
				giL.append([chunks[n+1],chunks[n+2]])
				print()
				n+=3
			else:
				print(" I am in last gi")
				#print("##" + str(float(chunks[n]) ) )
				# found last gi of spline (that holds diameter ratio for RSPLINE)
				tmp_spline[2]="%8s" % (chunks[n].strip())
				last_gi=chunks[n+2]
				print((chunks[n] + " " + last_gi))
				n+=1

			
			#Original
			
			# while chunks[n].find('.') < 0  :
				# print(str(n) + " "  + chunks[n])
				# giL.append([tmp_dof,chunks[n]])
				# n+=1
				# if n == len(chunks):
					# break
		else:
			n+=1
			#print("what")
	giL.append(["%8s" % (""),last_gi])
	
	# sort wtiL to make sure RSPLINE order is maintained
	indx=sorted(list(range(len(wtiL))), key=lambda k: wtiL[k])
	
	print(indx)
	
	#reshape giL list to tmpL
	tmpL=[]
	for ii in indx:
		tmpL.append(giL[ii][1])
		tmpL.append(giL[ii][0])
	print(tmpL)
	
	file_h=open(bdf_file,'w')
	
	file_h.write(("").join(tmp_spline) )
	#i=0 #row 
	j=4	#field
	n=0	#entry
	while n < len(tmpL):
		if j < 8 and n!=len(tmpL):
			file_h.write( "%8s" % (tmpL[n].rstrip()) )			#print("what standard?" + str(j) + " " + str(n) )
			j+=1
		elif j==8 and n!=len(tmpL):
			file_h.write(tmpL[n] + "%-8s\n%-8s" % ("+","+") )			#print("what here?" + str(j) + " " + str(n) )
			j=0
		elif n==len(tmpL):
			file_h.write(("%8s" % (tmpL[n].rstrip()) + "\n" ) )
			j+=1
		#else:
		#	print(("what now?" + str(j) + " " + str(n) ))	
		n+=1		
	
	
	file_h.close()

def read_MPCFORCES(bdf_full_name):
	#reads an f06 file with MPCFORCE results
	# generates csv file with normal and shear force and, bending and torsional results
	f06_file=bdf_full_name.replace('bdf','f06')
	f06_file_h=open(f06_file,'r')
	f06_lines=f06_file_h.readlines()
	f06_file_h.close()

	csv_file=f06_file.replace('f06','csv')

	# BoltForceD[ [GridId, SubcaseId, StepId] ]= [  Subcase[ [Step 1 [ [Inc 1] [Inc 2] [Inc 3] ]  [Step 2] ... [Step N]  ]  ]
	# Each Step is a list [kx, ky, kz, mx, my, mz]
	BoltForceD={}
	n=0
	N=len(f06_lines)
	SubcaseL=[]
	StepL=[]
	GridL=[]

	while n < N:
		if f06_lines[n].find("F O R C E S   O F   M U L T I P O I N T   C O N S T R A I N T") > -1:
			subcase_id=int(f06_lines[n-2][105:112])
			step_id=int(f06_lines[n-2][122:128])
			inc=float(f06_lines[n-1][18:29])
			#print subcase_id
			#print step_id
			#print inc
			# jmp to actual results
			if subcase_id not in SubcaseL:
				SubcaseL.append(subcase_id)
			if step_id not in StepL:
				StepL.append(step_id)
		
			
			n+=3
			while f06_lines[n].find(' G ') > -1:
				grid_id=int(f06_lines[n][:14])
				if grid_id not in GridL:
					GridL.append(grid_id)
				Fx=float(f06_lines[n][25:39])
				Fy=float(f06_lines[n][40:54])
				Fz=float(f06_lines[n][55:69])
				Mx=float(f06_lines[n][70:84])
				My=float(f06_lines[n][85:99])
				Mz=float(f06_lines[n][100:114])
				
				#print str(grid_id) + " " + "%8.2f%16.3f" % (inc,Fz)
				result_key=(subcase_id,step_id,grid_id)
				if result_key not in list(BoltForceD.keys()):
					BoltForceD[result_key]=[]
				BoltForceD[result_key].append([inc,Fx,Fy,Fz,Mx,My,Mz])
				n+=1
		n+=1
	#
	Fnorm={}
	Fshear={}
	Mbend={}
	Mtors={}
	incL=[]
	#
	for subcase_id in SubcaseL:
		for step_id in StepL:
			for result_type in ['Fnorm','Fshear','Mbend','Mtors']:
				for grid_id in GridL:
					result_key=(subcase_id,step_id,grid_id)
					tmpL=BoltForceD[result_key]
					for line in tmpL:
						inc=line[0]
						if inc not in incL:
							incL.append(inc)
						if result_type == 'Fnorm':
							if inc not in list(Fnorm.keys()):
								Fnorm[inc]=[]
							Fnorm[inc].append(line[3])
						elif result_type == 'Fshear':
							if inc not in list(Fshear.keys()):
								Fshear[inc]=[]
							Fshear[inc].append((line[1]**2 + line[2]**2)**0.5)
						elif result_type == 'Mbend':
							if inc not in list(Mbend.keys()):
								Mbend[inc]=[]
							Mbend[inc].append((line[4]**2 + line[5]**2)**0.5)
						elif result_type == 'Mtors':
							if inc not in list(Mtors.keys()):
								Mtors[inc]=[]
							Mtors[inc].append(line[6])					
						else:
							pass
					#print(str(grid_id) + "," + result_type + "," + 'Step' + str(step_id) + "," + "%8.3f" % (inc) + "," + "%16.3f" % (data) )

	print("===============")				

	# genarate csv files:
	if len(list(Fnorm.keys())) != 0:
		csv_file_h=open(csv_file,'w')
		# 
		for result_type in ['Fnorm','Fshear','Mbend','Mtors']:
			if result_type=='Fnorm':
				data=Fnorm
			elif result_type=='Fshear':
				data=Fshear
			elif result_type=='Mbend':
				data=Mbend
			elif result_type=='Mtors':
				data=Mtors
			else:
				pass
			csv_file_h.write("Increment" + "," + ','.join(result_type + " " + str(id) for id in GridL) + "\n")
			print("Increment",','.join(result_type + " " + str(id) for id in GridL) + "\n")
			for inc in incL:
				print(str(inc) + "," + ','.join(str(data_point) for data_point in data[inc])  + "\n")
				csv_file_h.write(str(inc) + "," + ','.join(str(data_point) for data_point in data[inc])  + "\n")
			print("   ")
			csv_file_h.write("\n")
		csv_file_h.close()

def remove_tmp_file():
	try:
		UtilsGoFile=os.environ.get('TEMP') + '\\UtilsGoFlagFile.txt'
		file_h=open(UtilsGoFile,'r')
		lines=file_h.readlines()
		for line in lines:
			print(line)
		file_h.close()
		os.remove(UtilsGoFile)
		print("Hawk Utils script will be stopped")
	except:
		print("Hawk utils does not seem to be running")

def WriteBoltBdfCards(bdf_file, SettingsD):


	try:
		preload_flag = SettingsD['NASTRAN_JOB_SETUP']['CreateBoltPreLoad']
	except:
		preload_flag = False
	

	if preload_flag:
		try:
			node_id = int(SettingsD['BoltPreLoad']['BoltControlNodeStartId'])
			cord_id = int(SettingsD['BoltPreLoad']['BoltCord2rStartId'])
			bolt_id = int(SettingsD['BoltPreLoad']['BoltStartId'])
			pre_load_spc_id = int(SettingsD['BoltPreLoad']['PreLoadStepSpcId'])
			lock_bolt_spc_id = int(SettingsD['BoltPreLoad']['LockBoltStepSpcId'])
			pre_load_force_id = int(SettingsD['BoltPreLoad']['PreLoadForceId'])
			load_step1_id=int(SettingsD['NASTRAN_JOB_SETUP']['LoadId'])
			pre_load_default_force = float(SettingsD['BoltPreLoad']['DefaultPreLoadForce'])
		except:
			node_id = 50000001
			cord_id = 5000001
			bolt_id = 500001
			load_step1_id=1001
			#pre_load_default_force = 1.
		try:
			default_contact_type = SettingsD['NASTRAN_JOB_SETUP']['SOL400DefaultContactType'][0].upper()
			if default_contact_type != "T" and default_contact_type != "G":
				default_contact_type = "G"
		except:
			default_contact_type = "G"

		try:	
			default_bolt_preload = SettingsD['BoltPreLoad']['DefaultPreLoadForce']
			if default_bolt_preload.find('N') > 0 or default_bolt_preload.find('mm') > 0:
				#try: 
				if 1==1:
					float(default_bolt_preload.replace('N','').replace('mm',''))
					print("default preload: " + default_bolt_preload + " is detected")
				#except:
				#	print "Default preload not properly defined, must contain number and N or mm "
				#	default_bolt_preload='0N'
			else:
				print("Default preload is not defined in settings file")
				default_bolt_preload='0N'

		except:
			print("Default preload is not defined in settings file")
			default_bolt_preload='0N'
			#traceback.print_exc()
		
		PreLoadLookupFile=resolve_pathname(	SettingsD['BoltPreLoad']['PreLoadLookupFile'], SettingsD['System']['SettingsFile'])
		
		#print(("Bolt preload Lookup file: " + PreLoadLookupFile))
		
		
		offset=10000
		
		if PreLoadLookupFile != "":
			preload_file_h=open(PreLoadLookupFile,'r')
			preload_lines=preload_file_h.readlines()
			preload_file_h.close()
			PreLoadD={}
			try:
				for line in preload_lines[1:]:
					tmpL=line.split()
					PreLoadD['m'+tmpL[0]] = tmpL[1] + " " + tmpL[2]
			except:
				print("Preload file seems to have the wrong format...omitting ")
				PreLoadLookupFile = ""
				
				
		#Build PartD where PartD[partname] returns pid list of pids referenced by part
		# PartD {'Bracket': [1], 'CADBolts': [], 'Sleeve': [2], 'Suppport': [3], 'Bolts_m10_hex': [4, 5], 'Nuts': [6], 'Washers': [7]}
		
		bdf_h=open(bdf_file,'r')
		bdf_lines=bdf_h.readlines()
		bdf_h.close()
		PartD=createPart2PidDict(bdf_lines)
		GridD = ReadGrids(bdf_lines)
		BConectD=getBConectD(bdf_lines,default_contact_type)
		
		#print("PartD",PartD)
		
		#Update Blob2BoltPidD with new param that will set either of two states  "BoltPreLoadType" to  "Auto" or "Semi"
		#Detect "semi" through RBE attached to interface nodes- store info in Blob2PreloadTypeD
		TipPsolidL,InterfaceElem2TipNodesD,ElemD,InterfaceNodeL,TipPsolid2BoltMatD, MergeGridsD, CtrlNodeD, Blob2BoltPidD, Blob2PreloadTypeD, Blob2BoltRefNodeD, deleteRBE2L = findInterFaceNodesFromMatName(bdf_file,offset,mat_name="_preload_dummy_material")

		deleteNodeL=[]
		for blob in Blob2BoltRefNodeD:
			deleteNodeL.append(Blob2BoltRefNodeD[blob])
			
		#print("Blob2BoltRefNodeD",Blob2BoltRefNodeD)

		BoltPreloadD={}
		for blob in MergeGridsD:
			# need partname wich holds blob
			blob_pid=Blob2BoltPidD[blob]
			for part_name in PartD:
				pids=PartD[part_name]
				if blob_pid in pids:
					#print("Blob with id", blob, "refers to part ", part_name )
					if Blob2PreloadTypeD[blob] =='Auto':
						part_preload, part_preload_type, bolt_flag= getPreloadFromPartName(part_name,PreLoadD,PreLoadLookupFile)				
						BoltPreloadD[blob] = [part_preload, part_preload_type]


		# replace tip interface nodes with offset 

		#write_back updated bdf after splitting the solid based on
		
		""" !!!!Temporarily deactivate"""
		bdf_new_name=writeBackAfterSolidSplit(bdf_file,TipPsolidL,InterfaceElem2TipNodesD,ElemD,InterfaceNodeL,TipPsolid2BoltMatD,offset,deleteRBE2L,deleteNodeL)
		
		
		# set Outputs that are now obsolete but still required in calling function (might fix later)
		BoltBConectL=[]
		BoltBConprgL=[] 
		ContactTableStr=""
			
			
		# for key in MergeGridsD:
		#	print key
		#	print MergeGridsD[key]

		# The bolt BSurfList coontains the BSurfs involved in bolt pretensioning

		cord_str = ""
		bolt_str = ""
		ctrl_node_str = ""
		pre_load_spc_str = ""
		lock_bolt_spc_str = ""
		pre_load_str = ""
		
		#print("BoltBsurfL",BoltBSurfL)
		#print("MergeGridsD")
		#for blob in MergeGridsD.keys():
		#	print(blob,MergeGridsD[blob])
		#print("====")
		
		
		#
		AutoBoltsExistFlag=False

		for blob in MergeGridsD.keys():
			#remove inner loop if test becomes obsolete
			if MergeGridsD[blob] != []:
				# get bolt preload
				if Blob2PreloadTypeD[blob]=='Auto':
					try:
						PreLoad = BoltPreloadD[blob][0]
						PreLoadType = BoltPreloadD[blob][1]
						if PreLoadType == "Disp":
							# provide the load id from the Case Control (hard coded 101)
							pre_load_id = load_step1_id
						else:
							pre_load_id = pre_load_force_id
					except:
						PreLoad=pre_load_default_force
						PreLoadType="Force"
				else:
					PreLoad=""
					PreLoadType=""
					pre_load_id=pre_load_force_id
					#pre_load_spc_id=""
					#lock_bolt_spc_id=""
					
				ctrl_node = CtrlNodeD[blob]
				#print "ctrl node"
				#print ctrl_node

				tmp_cord_str, tmp_bolt_str, tmp_ctrl_node_str, tmp_pre_load_spc_str, tmp_lock_bolt_spc_str, tmp_pre_load_str = \
					CreateBoltCards(MergeGridsD[blob], GridD, bolt_id, node_id, cord_id, PreLoad, pre_load_spc_id,
									lock_bolt_spc_id, pre_load_id, ctrl_node, Blob2PreloadTypeD[blob], PreLoadType)
				

				
				# change id on Ref Node to match id from semi-aut bolt RBE2
				if Blob2PreloadTypeD[blob]=='Semi':
					tmp_ctrl_node_str=tmp_ctrl_node_str[0:8] + "%8d" % (Blob2BoltRefNodeD[blob]) + tmp_ctrl_node_str[16:]
					tmp_bolt_str=tmp_bolt_str[0:16] + "%8d" % (Blob2BoltRefNodeD[blob]) + tmp_bolt_str[24:]
				else:
					AutoBoltsExistFlag=True
				
									
				# print stuff
				cord_str = cord_str + tmp_cord_str
				bolt_str = bolt_str + tmp_bolt_str
				ctrl_node_str = ctrl_node_str + tmp_ctrl_node_str
				pre_load_spc_str = pre_load_spc_str + tmp_pre_load_spc_str
				lock_bolt_spc_str = lock_bolt_spc_str + tmp_lock_bolt_spc_str
				pre_load_str = pre_load_str + tmp_pre_load_str
				#
				bolt_id += 1
				node_id += 1
				cord_id += 1
			else:
				print("===Warning... MergeGridsD[blob] == [] for blob: " + str(blob) ) 
		#print cord_str
		#print ctrl_node_str
		#print bolt_str
		#print pre_load_spc_str
		#print lock_bolt_spc_str
		#print pre_load_str
		# write bulkdata for bolt pretension Loadsteps
		out_str = cord_str + ctrl_node_str + bolt_str + pre_load_spc_str + lock_bolt_spc_str + pre_load_str
	else:
		out_str=""
	
	return MergeGridsD.keys(), BoltBConectL, BoltBConprgL, out_str, ContactTableStr, pre_load_spc_id, lock_bolt_spc_id, pre_load_force_id, BConectD, AutoBoltsExistFlag
	#return BoltBSurfL, BoltBConectL, BoltBConprgL, out_str, ContactTableStr, pre_load_spc_id, lock_bolt_spc_id, pre_load_force_id, BConectD

def partition_nodelist_per_contact_body(MergeL, bsurfD, CelemD):
	# bdf_file=r"C:\tmp\TestTouch\Bolt_5000N.bdf"
	# bdf_h=open(bdf_file,'r')
	# lines=bdf_h.readlines()
	# bdf_h.close()
	# bsurfD=ReadBsurfs(SettingsD, lines)
	# CelemD=ReadCelems(lines)
	#print("MergeList")
	#print(MergeL)

	bsurfGridsD = {}
	MergeGridsD = {}
	bsurf_to_gridsD={}

	# split the Nodes2 merge pairs so that we can create separate BOLTs even if there are multiple contacts
	# build dict with Nodes per Surfcae body

	for key in list(bsurfD.keys()):
		# print  bsurfD[key]
		# prepare list of MergeGrids
		MergeGridsD[key] = []
		bsurf_to_gridsD[key] = []
		#
		tmp_nodes = []
		for elem in bsurfD[key]:
			tmp_nodes = tmp_nodes + CelemD[elem][0]
		for node in tmp_nodes:
			bsurfGridsD[node] = key
		bsurf_to_gridsD[key]=tmp_nodes
		
	tmpL = []
	# need to build a list of nodes on bolt tip
	# for each MergeL (pair) find tip nodeA
	TipNodesD={}
	
	for item in MergeL:
		# get the bsurf id from the first node in the pair from MergeL
		try:
			tmp_surf_id = bsurfGridsD[item[0]]
			tmp_surf_id2 = bsurfGridsD[item[1]]
			elem1=bsurfD[tmp_surf_id][0]
			pid1=CelemD[elem1][1]
			elem2=bsurfD[tmp_surf_id2][0]
			pid2=CelemD[elem2][1]
			if pid1 > pid2:
				reverse_flag=True
			else:	
				reverse_flag=False
			if tmp_surf_id not in list(TipNodesD.keys()) or tmp_surf_id2 not in list(TipNodesD.keys()):
				#elem1=bsurfD[tmp_surf_id][0]
				#pid1=CelemD[elem1][1]
				#elem2=bsurfD[tmp_surf_id2][0]
				#pid2=CelemD[elem2][1]
				
				print(("pid 1: " +  str(pid1) + " pid 2:" + str(pid2) ))
				if reverse_flag==True:
					print(("pid: " + str(pid1) + " is the bolt tip" ))
					#get grids from tmp_surf_id
					TipNodesD[tmp_surf_id]=tmp_surf_id
					TipNodesD[tmp_surf_id2]=tmp_surf_id
					reverse_flag=True
				else:
					print(("pid: " + str(pid2) + " is the bolt  tip" ))
					TipNodesD[tmp_surf_id]=tmp_surf_id2
					TipNodesD[tmp_surf_id2]=tmp_surf_id2
	
			# check so that also the other node in the pair is a Bolt Node
			if bsurfGridsD[item[1]] in list(bsurfD.keys()):
				
				if reverse_flag == False:
					#print("Surf: " + str(tmp_surf_id) + " No reverse: " + str(item[0]) + " " + str(item[1]))
					
					MergeGridsD[tmp_surf_id].append(item)
				else:
					#print("Surf: " + str(tmp_surf_id) + " Reverse: " + str(item[1]) + " " + str(item[0]))
					new_tuple=(item[1],item[0])
					MergeGridsD[tmp_surf_id].append(new_tuple)
		
		except KeyError:
			print("Something went wrong in checking largest pid")
			traceback.print_exc()
			continue

	# need to build a list of nodes on bolt tip	
	# get 

	# TipNodesD[bsurf]
	
	# find BSURF with lowest pid 
	#elem1=bsurfD[tmp_surf_id][0]
	#elem2=bsurfD[tmp_surf_id_2][0]
	
	#pid1=CelemD[elem1][1]f
	#pid2=CelemD[elem2][1]
			
	CtrlNodeD = {}
	
	#print("TipNodesD")
	#print(TipNodesD)
	
	for bsurf in list(TipNodesD.keys()):
		tip_surf=TipNodesD[bsurf]
		for node in bsurf_to_gridsD[tip_surf]:
		
			if node not in MergeGridsD[bsurf]:
				#found a node that is not on the face
				 CtrlNodeD[bsurf] = node
				 print(("found control node " + str(node) + " for bsurf "  + str(bsurf) ))
				 break
				
				
	# for node in bsurfGridsD.keys():
		#node is the key in bsurfGridsD
		# bsurf = bsurfGridsD[node]
		# if node not in MergeGridsD[bsurf]:
			#found a node that is not on the face
			# CtrlNodeD[bsurf] = node

	return MergeGridsD, CtrlNodeD

def get_model_file(log_file):
	# read log file to get current model
	log_file_h = open(log_file, 'r')
	lines = log_file_h.readlines()
	log_file_h.close()
	for line in reversed(lines):
		if line.find("** Information *** PFW - Create model:") > 0:
			model_file = line[40:].strip()
			print("*** Information *** Current Model file: " + model_file.replace('Model Files',''))
			# tmp_h1.append("*** Information *** Current Model file: " + model_file +  '\n')
			break
	return model_file

def scan_log_file(log_file, StartTime, UtilsGoFile, last_read_n):
	print("### Scan Log started ####")
	log_file_h = open(log_file, 'r')
	lines = log_file_h.readlines()
	log_file_h.close()
	KeepScriptRunning = True
	Go = True
	# bdf_full_name=""

	while Go == True:
		# continue unless script is stopped
		if os.path.isfile(UtilsGoFile):
			Go = True
		else:
			#print "UtilsGoFlagFile removed...script will be stopped"
			Go = False

		log_file_h = open(log_file, 'r')
		lines = log_file_h.readlines()
		log_file_h.close()
		# for line in reversed(lines)
		N = len(lines)
		n = N - 1
		while n >= 0:
			#
			# print n
			line = lines[n]
			# print "scan_log_file...reading log file"
			if line.find("*** Information *** Exported ") >= 0 and line.find("MSC.ModelingPlugin") > 0:
				print("scan_log_file found bdf : " + line)
				bdf_name = line.split('"')[1]
				bdf_path = line.split('"')[3]

				bdf_full_name = bdf_path + '\\' + bdf_name

				# ####New#### ... Check so that file is created after utils script was started... no old files will be processed
				# get time from bdf

				# try:
				# get modified time (not create time)

				# print "bdf_full_name: " + bdf_full_name
				if os.path.isfile(bdf_full_name):

					filetime = datetime.datetime.fromtimestamp(os.path.getmtime(bdf_full_name))
					aaa_str = filetime.strftime("%I:%M:%S%p on %B %d, %Y")
					# print "*** File **** " + bdf_full_name + " created: " + aaa_str
					# except:
					#	print "file no longer exist, set old time stamp"
					#	filetime = StartTime - datetime.timedelta(days=1)
					#	aaa_str="N/A"

					if filetime > StartTime:
						print(" Scanning log file ...checking: " + bdf_full_name)

						# print "Compare time   : " + StartTime.strftime("%I:%M:%S%p on %B %d, %Y")
						# print "File crete time: " + aaa_str
						# only execute on files exported after script was started
						if n > last_read_n:
							print("*** GO! **** " + bdf_full_name + " modified: " + aaa_str + " meaning it is new")
							print("*** Information *** Processing  bdf file: " + bdf_full_name)
							last_read_n = n
							Go = False
							# stop reading more lines in log file
							n = -1
						else:
							print("exported bdf already processed")
							# stop reading more lines in log file
							n = -1

					else:
						# file is older than comparison time, continue unless script is stopped
						# print "*** No match **** " + bdf_full_name + " modified: " + aaa_str + " is old... modified before  previous check"
						# stop reading more lines in log file
						n = -1

						if os.path.isfile(UtilsGoFile):
							# print "UtilsGoFlagFile exists"
							Go = True

						else:
							#print "UtilsGoFlagFile removed...script will be stopped"
							Go = False
							KeepScriptRunning = False

				# bdf from log_file seems to have been removed, wait for new output
				else:
					n = -1

			elif line.find("*** Information *** Normal Termination.") == 0:
				Go = False
				KeepScriptRunning = False
				print("Normal termination of Apex session, stop python subprocess")
				# tmp_h1.append("*** Information *** Normal termination of Apex session, stop python subprocess" + '\n')
				n = -1

			else:
				# reads next line from log file
				# print "pass"
				pass
			n -= 1

		# print "...sleeping...."
		time.sleep(2)

	# end of while loop
	return bdf_full_name, KeepScriptRunning, last_read_n

def getSampleElementLength(inl1, NodeD, NoOfElems=10):
	N = len(inl1)
	tmp_quad = []
	tmp_tria = []
	tmp_hexa = []
	elems = []
	n = 0
	while n < N:
		lin = inl1[n]
		if lin[0] == "C":
			if lin[0:5] == "CTRIA":
				tmp_tria.append(lin)
			elif lin[0:5] == "CQUAD":
				tmp_quad.append(lin)
			elif lin[0:5] == "CHEXA":
				tmp_hexa.append(lin)
		n += 1

		div = max(int(len(tmp_tria) / NoOfElems), 1)
		tria = tmp_tria[0::div]

		div = max(int(len(tmp_quad) / NoOfElems), 1)
		quad = tmp_quad[0::div]

		div = max(int(len(tmp_hexa) / NoOfElems), 1)
		hex = tmp_hexa[0::div]

	print(tria + quad + hex)

	distL = []
	for tmp_line in tria + quad + hex:
		# print tmp_line
		# print tmp_line[24:32]
		# print tmp_line[32:40]
		Node1 = int(tmp_line[24:32])
		Node2 = int(tmp_line[32:40])
		cord1 = NodeD[Node1][0:3]
		cord2 = NodeD[Node2][0:3]
		distL.append(GetDist(cord1, cord2))
	avg_lgth = sum(distL) / len(distL)
	# print "average length: " +  str(avg_lgth)
	return avg_lgth

def check_and_delete_file(new_file, old_file):
	# old_file=r'C:\Users\e7390\Documents\Predator\Topics\Scripting\Sample_files\orig_mnf_eagle_Model_4.bdf'
	print("old bdf last modified: %s" % time.ctime(os.path.getmtime(old_file)))
	old_time = time.ctime(os.path.getmtime(old_file))
	# new_file=r'C:\Users\e7390\Documents\Predator\Topics\Scripting\Sample_files\mnf_eagle_Model_4.bdf'
	print("new bdf last modified: %s" % time.ctime(os.path.getmtime(new_file)))
	new_time = time.ctime(os.path.getmtime(new_file))
	if os.path.getmtime(old_file) < os.path.getmtime(new_file):
		print("old_file " + old_file + " is older than new_file")
		print(os.path.getmtime(old_file))
		os.remove(old_file)
	else:
		print("new_file " + new_file + " is older than old_file... resetting name of orig_file")
		print(os.path.getmtime(new_file))
		# use this only for debugging... rename files
		os.remove(new_file)
		os.rename(old_bdf, new_bdf)

def to_precision(x, p):
	"""
    returns a string representation of x formatted with a precision of p

    Based on the webkit javascript implementation taken from here:
    https://code.google.com/p/webkit-mirror/source/browse/JavaScriptCore/kjs/number_object.cpp
    """
	x = float(x)

	if x == 0.:
		return "0." + "0" * (p - 1)

	out = []

	if x < 0:
		out.append("-")
		x = -x

	e = int(math.log10(x))
	tens = math.pow(10, e - p + 1)
	n = math.floor(x / tens)

	if n < math.pow(10, p - 1):
		e = e - 1
		tens = math.pow(10, e - p + 1)
		n = math.floor(x / tens)

	if abs((n + 1.) * tens - x) <= abs(n * tens - x):
		n = n + 1

	if n >= math.pow(10, p):
		n = n / 10.
		e = e + 1

	m = "%.*g" % (p, n)

	if e < -2 or e >= p:
		out.append(m[0])
		if p > 1:
			out.append(".")
			out.extend(m[1:p])
		out.append('e')
		if e > 0:
			out.append("+")
		out.append(str(e))
	elif e == (p - 1):
		out.append(m)
	elif e >= 0:
		out.append(m[:e + 1])
		if e + 1 < len(m):
			out.append(".")
			out.extend(m[e + 1:])
	else:
		out.append("0.")
		out.extend(["0"] * -(e + 1))
		out.append(m)

	return "".join(out)

def num2str(cord):
	if cord > 0:
		if cord >= 1e10:
			b = to_precision(cord,4)
		elif cord > 100000:
			b = to_precision(cord,5)
		elif cord > 1:
			b = to_precision(cord, 7)
		elif cord >= 0.1:
			b = to_precision(cord, 6)
		elif cord >= 9e-9:
			b = to_precision(cord, 5)
		else:
			b = to_precision(cord, 4)

		# b = "%9.5g" % cord
		c = b.replace('e+0', '+').replace('e-0', '-').replace('e+', '+').replace('e-', '-')
		return c
	else:
		if cord <= -10000:
			b = to_precision(cord, 4)
		elif cord <= -10:
			b = to_precision(cord, 6)
		elif cord <= -0.1:
			b = to_precision(cord, 5)
		elif cord <= -9e-9:
			b = to_precision(cord, 4)			
		else:
			b = to_precision(cord, 3)
			
		# b = "%9.4g" % cord
		c = b.replace('e+0', '+').replace('e-0', '-').replace('e-', '-').replace('e+', '+')
		
		if len(c) > 8:
			print((" num2str returns a number longer than 8 chars: "  +  str(cord) ))
			print(c)
			return ""
		elif len(c) == 8 and c.find('+') < 0 and c.find('.') < 0:
			print((" num2str returns a number that is  8 chars long but no decimal point"  +  str(cord) ))
			print(c) 
			return ""
		elif len(c) < 8 and c.find('+') < 0 and c.find('.') < 0:
			return c + "."
		else:
			return c

def midpoint(cord1, cord2):
	outvect = []
	outvect.append((cord2[0] + cord1[0]) / 2)
	outvect.append((cord2[1] + cord1[1]) / 2)
	outvect.append((cord2[2] + cord1[2]) / 2)
	return outvect

def v_lgth(cord2):
	lgth = ((cord2[0]) ** 2 + (cord2[1]) ** 2 + (cord2[2]) ** 2) ** 0.5
	return lgth

def cross(u, v):
	u1 = u[0]
	u2 = u[1]
	u3 = u[2]
	v1 = v[0]
	v2 = v[1]
	v3 = v[2]
	vect = [u2 * v3 - u3 * v2, u3 * v1 - u1 * v3, u1 * v2 - u2 * v1]
	return vect

def projection(u, v):
	u1 = u[0]
	u2 = u[1]
	u3 = u[2]
	v1 = v[0]
	v2 = v[1]
	v3 = v[2]
	scalar = (u1 * v1 + u2 * v2 + u3 * v3) / v_lgth(v)
	return scalar

def GetDist(cord1, cord2, vA=[0, 0, 0]):
	if vA == [0, 0, 0]:
		# This function will not give the desired result when the RBE3 indep grids are  not in the same plane as the center grid
		dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
	# print "A: Min dist is: " + str(dist)
	else:
		# need direction of the hole
		# v1 is the projection of the distance to the hole axis
		# v2 is the vector perpendicular to hole axis
		# v0 is the vector between hole center and rbe center
		v0 = [cord2[0] - cord1[0], cord2[1] - cord1[1], cord2[2] - cord1[2]]
		s1 = projection(v0, vA)
		# print "==== vA, v1==="
		# print "vA (bar)   :" + str(vA)
		# print "v1 (dot): " + str(s1)
		dist = (v_lgth(v0) ** 2 - s1 ** 2) ** 0.5
	# print "B: Min dist is: " + str(dist)
	return dist

def GetCoincidentMPCNodes2(NodeA, NodeL, NodeD, Tol=0.01):
	OutBdfLog = []
	maxTolL = []
	# print NodeA
	# print NodeB
	# print NodeL
	KeepIndepL = []
	FailedMinL = ""

	#
	# remove indent and take Nodea and NodeL from function argumenet inputs
	# for NodeA in MPCD.keys():
	# NodeL=MPCD[NodeA]
	cord1 = NodeD[NodeA][0:3]  # x,y and z for NodeA
	# print " =================="
	# print NodeA
	# print cord1
	# print NodeL
	# print " =================="
	zero_length_flag = 0
	temp_min = 1000
	min_id = 0
	# print "==============\n"
	# print "NodeA: " + str(NodeA)
	# print NodeL
	for Node2 in NodeL:
		cord2 = NodeD[Node2][0:3]  # x,y and z
		# print cord2
		dist = GetDist(cord1, cord2)
		if dist < temp_min:
			temp_min = dist
			min_id = Node2
		# print dist
		# print NodeA
		# print Node2
		# print NodeB

		if dist <= Tol:
			# print "Node: " + str(NodeA)  + " and  Node: " + str(Node2) + " are within distance"
			KeepIndepL.append(Node2)
			zero_length_flag = 1
	if zero_length_flag == 0:
		# No mergeable nodes found for MPC
		# str= "Failed to find mergeable node for Node:  " + "%8d" % (NodeA) +  " within tolerance tol: " + "%8.3f" % (Tol) +  " Smallest dist is : " + "%8.3f" % (temp_min) + " for node: " +  "%-8d" % (min_id)
		# print str
		FailedMinL = min_id
		OutBdfLog.append(str)
		maxTolL.append(temp_min)

	return KeepIndepL, FailedMinL, OutBdfLog, maxTolL

def WriteGrid(id, coord, cp=0, cd=0):
	grid_line = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
		"GRID", id, cp, num2str(coord[0]), num2str(coord[1]), num2str(coord[2]), cd)
	return grid_line

def WriteCtria3(id, grids, psh, matid):
	ctria3_line = "%-8s%8d%8d%-8d%-8d%-8d\n" % ("CTRIA3", id, psh, grids[0], grids[1], grids[2])
	return ctria3_line

def WriteCTriaForFailedGrids(file_h, FailL, NodeD, start_coord=[0, 0, 0]):
	# create a bdf with CTRIAs for each failed MPC grid
	grid0_id = 1000001
	elem_id = 1
	pshell_line = "PSHELL      9999     999     10.     999      1.     999 0.83333\n"
	mat1_line = "MAT1    999     205000.         .29     1.00E-12\n"
	grid0 = WriteGrid(grid0_id, start_coord, 0, 0)
	file_h.write(grid0)
	n = 0
	for nodes in FailL:
		line1 = WriteGrid(nodes[0], NodeD[nodes[0]], 0, 0)
		line2 = WriteGrid(nodes[1], NodeD[nodes[1]], 0, 0)
		line3 = WriteCtria3(elem_id, [grid0_id, nodes[0], nodes[1]], 9999, 999)
		#
		file_h.write(line1)
		file_h.write(line2)
		file_h.write(line3)
		elem_id += 1
		n += 1
	file_h.write(pshell_line)
	file_h.write(mat1_line)
	print("There are: " + "%-8d" % (
		n) + "  MPCs with non coincident nodes- import the file find_noncoincident.bdf to locate the failed grids")

def NodeMinDist(NodeA, NodeL, NodeD, NodeB, DiaTol=10000):
	# print NodeA
	# print NodeB
	# print NodeL
	distL = []
	cord1 = NodeD[NodeA][0:3]  # x,y and z for NodeA
	cord0 = NodeD[NodeB][0:3]
	for Node2 in NodeL:
		cord2 = NodeD[Node2][0:3]  # x,y and z
		vect = [cord1[0] - cord0[0], cord1[1] - cord0[1], cord1[2] - cord0[2]]
		# print vect
		# print "NodeA: " + str(NodeA)
		# print "Node2: " + str(Node2)
		# print "Vect: " + str(vect)
		dist = GetDist(cord1, cord2, [cord1[0] - cord0[0], cord1[1] - cord0[1], cord1[2] - cord0[2]])
		# print NodeA
		# print Node2
		# print NodeB
		# print "==============\n"
		distL.append(dist)

	# make sure the 3 nodes with shortest distance to hole center is within diaTol, otherwise return negative radius
	distL.sort()
	if len(distL) < 3:
		print("Not enough nodes on RBE3 to define a hole [Error -1] ")
		return -1
	else:
		distL.sort()
		if abs(distL[0] - distL[2]) <= DiaTol:
			return min(distL)
		else:
			print("There are not enough nodes with same distance from BAR center to establish a diamter [Error -2]")
			return -2

def NodeMinDist_error(NodeA, NodeL, NodeD, NodeB, DiaTol=10000):
	# print NodeA
	# print NodeB
	# print NodeL
	distL = []
	cord0 = NodeD[NodeA][0:3]  # x,y and z for NodeA

	for Node2 in NodeL:
		cord1 = NodeD[Node2][0:3]  # x,y and z
		vect = [cord1[0] - cord0[0], cord1[1] - cord0[1], cord1[2] - cord0[2]]
		# print vect
		# print "NodeA: " + str(NodeA)
		# print "Node2: " + str(Node2)
		# print "Vect: " + str(vect)
		dist = GetDist(cord1, cord2, [cord1[0] - cord0[0], cord1[1] - cord0[1], cord1[2] - cord0[2]])
		# print NodeA
		# print Node2
		# print NodeB
		# print "==============\n"
		distL.append(dist)

	# make sure the 3 nodes with shortest distance to hole center is within diaTol, otherwise return negative radius
	distL.sort()
	if len(distL) < 3:
		print("Not enough nodes on RBE3 to define a hole [Error -1] ")
		return -1
	else:
		distL.sort()
		if abs(distL[0] - distL[2]) <= DiaTol:
			return min(distL)
		else:
			print("There are not enough nodes with same distance from BAR center to establish a diamter [Error -2]")
			return -2

def RBE3s_to_RBE2s(Rbe3D):
	# only convert RBE3s associated with the RBAR
	# the dep node is used as key and element id is first item in list, indep node list is second item in disct list
	temp_str = ""
	Rbe2CardL = []
	# print Rbe3D.keys()
	for key in list(Rbe3D.keys()):
		temp_str = "%-8s%8d%8d%8s" % ("RBE2", Rbe3D[key][0], key, "123456")
		i = 1
		j = 1
		k = 1
		for dep_grid in Rbe3D[key][1]:
			# print "==================="
			# print dep_grid
			if (i == 1 and j > 5) or (i > 1 and j > 7):
				# print len(Rbe3D[key][1])
				if k == len(Rbe3D[key][1]):
					# print "Processing A   i=" +  str(i) + "  j=" + str(j)+ "  k=" + str(k)
					temp_str = temp_str + "\n"
				else:
					# print "Processing B   i=" +  str(i) + "  j=" + str(j)+ "  k=" + str(k)
					temp_str = temp_str + "+        \n+        " + ("%7s") % (dep_grid)
					i += 1
					j = 1
					k += 1
			else:
				if k == len(Rbe3D[key][1]):
					# print "Processing D   i=" +  str(i) + "  j=" + str(j)+ "  k=" + str(k)
					temp_str = temp_str + "%8d" % (dep_grid) + "\n"
				# print temp_str
				else:
					# print "Processing C   i=" +  str(i) + "  j=" + str(j)+ "  k=" + str(k)
					temp_str = temp_str + "%8d" % (dep_grid)
					j += 1
					k += 1

		Rbe2CardL.append(temp_str)
	return Rbe2CardL

def GetVector(Node1, Node2, NodeD):
	outvect = []
	cord1 = NodeD[Node1][0:3]
	cord2 = NodeD[Node2][0:3]
	dist = GetDist(cord1, cord2)
	outvect.append((cord2[0] - cord1[0]) / dist)
	outvect.append((cord2[1] - cord1[1]) / dist)
	outvect.append((cord2[2] - cord1[2]) / dist)
	return outvect

def Rbars_2_Cbars(RbarD, NodeD, Rbe3D, Mid):
	# Mid=10001
	# This function will convert an RBAR to a CBAR with a

	# Get user defined minimum diameter DiaMin and maximum diameter DiaMax
	# for distance to hole centres to create a CBAR
	#
	# A minimum of 3 nodes are required within DiaTol
	try:
		DiaMax = float(SettingsD['RBEX_CBAR']['DiaMax'])
		DiaMin = float(SettingsD['RBEX_CBAR']['DiaMin'])
		DiaTol = float(SettingsD['RBEX_CBAR']['DiaTol'])

	except:
		print("Can not find DiaMax and DiaMin from settings file, use default 1 and 1000 [assuming mm]")
		DiaMax = 1000.0
		DiaMin = 1.0
		DiaTol = 0.1
	print("DiaMax :" + str(DiaMax))
	print("DiaMin :" + str(DiaMin))
	print("DiaTol :" + str(DiaTol))
	# PBARL corresponding to hole diamater
	temp_str = ""
	CbarCardL = []
	radiiD = {}
	# All RBARs failed to be converted is added to OmitList
	OmitList = []
	# remove all RBARs that are not referenced by an RBE3
	rbe3nodes = list(Rbe3D.keys())
	for key in list(RbarD.keys()):
		check_nodes = RbarD[key]
		if (check_nodes[0] in rbe3nodes) or (check_nodes[1] in rbe3nodes):
			continue
		else:
			RbarD.pop(key)
		# print "Rbar with id_ " + str(key) + " is removed as it is not connected to an RBE3"

	for key in list(RbarD.keys()):
		# find diameter of hole
		# find RBAR end node(s) and corresponding RBE3 indep nodes-calculate distance to center
		# use smallest distance found in case hole is not circular
		nodeA = RbarD[key][0]
		nodeB = RbarD[key][1]
		# first rbe3:
		# check both ends of the RBE
		if nodeA in Rbe3D:
			rbe3A_nodes = Rbe3D[nodeA][1]
			radius_rawA = NodeMinDist(nodeA, rbe3A_nodes, NodeD, nodeB, DiaTol)
		else:
			print("The RBAR node: " + "%d" % (nodeA) + " has no RBE3 connected")
			radius_rawA = -3

		if nodeB in Rbe3D:
			rbe3B_nodes = Rbe3D[nodeB][1]
			radius_rawB = NodeMinDist(nodeA, rbe3B_nodes, NodeD, nodeB, DiaTol)
		else:
			print("The RBAR node: " + "%d" % (nodeA) + " has no RBE3 connected")
			radius_rawB = -3

		# calculate Min distance between center node and each of the RBE3 indep nodes
		# This is the radius of a PBARL card


		if (radius_rawA < DiaMax / 2 and radius_rawA > DiaMin / 2) and (
						radius_rawB < DiaMax / 2 and radius_rawB > DiaMin / 2):
			radius = round(min([radius_rawA, radius_rawB]), 1)
			vect = GetVector(nodeA, rbe3A_nodes[0], NodeD)

			if not radius in list(radiiD.keys()):
				Pid = len(list(radiiD.keys())) + 1000
				radiiD[radius] = Pid
			else:
				Pid = radiiD[radius]
			# Add CBAR card, use vector between hole center and
			temp_str = "%-8s%8d%8d%8d%8d%8.3f%8.3f%8.3f\n" % ("CBAR", key, Pid, nodeA, nodeB, vect[0], vect[1], vect[2])
			CbarCardL.append(temp_str)
		else:
			print("The rigidlink with RBAR Id " + "%d" % (key) + " failed convertion to  CBAR")
			CbarCardL.append("$ RBAR with id : " + "%8d" % (key) + " failed convertion to  CBAR\n")
			OmitList.append(key)

		# print PBARL cards
		for key in list(radiiD.keys()):
			temp_str = "%-8s%8d%8d%8s%8s%32s+\n%-8s%8.3f\n" % (
				"PBARL", radiiD[key], Mid, "MSCBML0", "ROD", "", "+", key)
			CbarCardL.append(temp_str)

	return CbarCardL, OmitList

def Rbars_2_Cbush(RbarD, NodeD, Rbe3D, SettingsD):
	# parameters for defining cbush behaviour
	# try to obtain from settings file
	try:
		Knorm = float(SettingsD['RBEX_CBUSH']['K_normal'])
		Kshear = float(SettingsD['RBEX_CBUSH']['K_shear'])
		Kbend = float(SettingsD['RBEX_CBUSH']['K_bendmoment'])
		Ktors = float(SettingsD['RBEX_CBUSH']['K_torsmoment'])
		Pid = float(SettingsD['RBEX_CBUSH']['PID'])
	except:
		print(" except material")
		Knorm = 1e5
		Kshear = 1e6
		Kbend = 1e6
		Ktors = 1e5
		Pid = 10001
	try:
		cid = int(SettingsD['RBEX_CBUSH']['Cord2r_start_id'])
	except:
		print(" except cid")
		cid = 1001
	try:
		zlf = SettingsD['RBEX_CBUSH']['ZeroLength']
		if zlf.upper() == "TRUE" or zlf.upper() == "YES" or zlf == "1":
			ZeroLengthFlag = True
		else:
			ZeroLengthFlag = False
	except:
		print("except ZeroLengthFlag")
		ZeroLengthFlag = True
	#
	GridStrL = []
	CbushStrL = []
	CordStrL = []
	PbushStrL = []
	#
	tmp_str = "%-8s%8d%8s%8s%8s%8s%8s%8s%8s\n" % (
		"PBUSH", Pid, "K", num2str(Kshear), num2str(Kshear), num2str(Knorm), num2str(Kbend), num2str(Kbend),
		num2str(Ktors))

	PbushStrL.append(tmp_str)
	# Grids with replaced coordiantes
	GridReplaceL = []

	# remove all RBARs that are not referenced by an RBE3
	rbe3nodes = list(Rbe3D.keys())
	for key in list(RbarD.keys()):
		check_nodes = RbarD[key]
		if (check_nodes[0] in rbe3nodes) or (check_nodes[1] in rbe3nodes):
			continue
		else:
			RbarD.pop(key)
		# print "Rbar with id_ " + str(key) + " is removed as it is not connected to an RBE3"

	for key in list(RbarD.keys()):
		# find RBAR end node(s) and corresponding RBE3 indep nodes-calculate distance to center
		# use smallest distance found in case hole is not circular
		nodeA = RbarD[key][0]
		nodeB = RbarD[key][1]
		# Need coordinates to define system for CBUSH with coincident grids if ZeroLengthFlag==True
		# Consider Following scenarios:
		# 1a)  2D or 3D and non-zero length RBAR => get Normal direction from RBAR, second direction from first RBE3 grid indep grid
		# 1b)  2D and zero length RBAR => get direction from normal direction of hole (assume flat)  (not yet covered)
		# 1c)  3D and zero length RBAR (not covered)
		#
		# vect is [1,0,0] as local coordinate system z-axis is always in hole axis direction
		# get cid:
		# get first RBE3 grid indep grid
		nodeC = Rbe3D[nodeA][1][0]
		# print " NodeA: " + str(nodeA) + " NodeB: " + str(nodeB) + " NodeC: " + str(nodeC)
		#
		# define new mid point
		if NodeD[nodeA][3] == 0 and NodeD[nodeB][3] == 0:
			cord = midpoint([NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]],
							[NodeD[nodeB][0], NodeD[nodeB][1], NodeD[nodeB][2]])
			if ZeroLengthFlag == True:
				# write Grids with new midpoint
				# print "Cord:  " + str(cord)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeA, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeB, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				GridReplaceL.append(nodeA)
				GridReplaceL.append(nodeB)
		else:
			# script do not handle grids defined in local coordinates at this point
			# no coordinate replacemenr possible use NodeA as center for coord system
			cord = [NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]]

		# define cord system  put center in new bush center
		A1 = num2str(cord[0]);
		B1 = num2str(NodeD[nodeB][0]);
		C1 = num2str(NodeD[nodeC][0])
		A2 = num2str(cord[1]);
		B2 = num2str(NodeD[nodeB][1]);
		C2 = num2str(NodeD[nodeC][1])
		A3 = num2str(cord[2]);
		B3 = num2str(NodeD[nodeB][2]);
		C3 = num2str(NodeD[nodeC][2])

		temp_str1 = "%-8s%8d%8d%8s%8s%8s%8s%8s%8s+\n+%7s%8s%8s%8s\n" % (
			"CORD2R", cid, 0, A1, A2, A3, B1, B2, B3, " ", C1, C2, C3)
		CordStrL.append(temp_str1)
		vect = [1.0, 0.0, 0.0]
		temp_str2 = "%-8s%8d%8d%8d%8d%8.3f%8.3f%8.3f%8d\n" % (
			"CBUSH", key, Pid, nodeA, nodeB, vect[0], vect[1], vect[2], cid)
		CbushStrL.append(temp_str2)
		cid += 1

	return (GridStrL, CbushStrL, CordStrL, PbushStrL, GridReplaceL)

def Cbars_2_Cbush(CbarD, NodeD, Rbe3D, SettingsD):
	# parameters for defining cbush behaviour
	# try to obtain from settings file
	# This is used to convert 1D bolts as Cbars to very Softs Springs in SOL400
	# for stability
	
	try:
		Knorm = float(SettingsD['RBEX_CBUSH']['K_normal'])
		Kshear = float(SettingsD['RBEX_CBUSH']['K_shear'])
		Kbend = float(SettingsD['RBEX_CBUSH']['K_bendmoment'])
		Ktors = float(SettingsD['RBEX_CBUSH']['K_torsmoment'])
		Pid = float(SettingsD['RBEX_CBUSH']['PID'])
	except:
		print(" except material")
		Knorm = 1e5
		Kshear = 1e6
		Kbend = 1e6
		Ktors = 1e5
		Pid = 10001
	try:
		cid = int(SettingsD['RBEX_CBUSH']['Cord2r_start_id'])
	except:
		print(" except cid")
		cid = 1001
	try:
		zlf = SettingsD['RBEX_CBUSH']['ZeroLength']
		if zlf.upper() == "TRUE" or zlf.upper() == "YES" or zlf == "1":
			ZeroLengthFlag = True
		else:
			ZeroLengthFlag = False
	except:
		print("except ZeroLengthFlag")
		ZeroLengthFlag = True
	#
	GridStrL = []
	CbushStrL = []
	CordStrL = []
	PbushStrL = []
	#
	tmp_str = "%-8s%8d%8s%8s%8s%8s%8s%8s%8s\n" % (
		"PBUSH", Pid, "K", num2str(Kshear), num2str(Kshear), num2str(Knorm), num2str(Kbend), num2str(Kbend),
		num2str(Ktors))

	PbushStrL.append(tmp_str)
	# Grids with replaced coordiantes
	GridReplaceL = []

	# remove all RBARs that are not referenced by an RBE3
	rbe3nodes = list(Rbe3D.keys())
	for key in list(CbarD.keys()):
		check_nodes = CbarD[key]
		if (check_nodes[0] in rbe3nodes) or (check_nodes[1] in rbe3nodes):
			continue
		else:
			CbarD.pop(key)
		# print "Rbar with id_ " + str(key) + " is removed as it is not connected to an RBE3"

	for key in list(CbarD.keys()):
		# find RBAR end node(s) and corresponding RBE3 indep nodes-calculate distance to center
		# use smallest distance found in case hole is not circular
		nodeA = CbarD[key][0]
		nodeB = CbarD[key][1]
		# Need coordinates to define system for CBUSH with coincident grids if ZeroLengthFlag==True
		# Consider Following scenarios:
		# 1a)  2D or 3D and non-zero length RBAR => get Normal direction from RBAR, second direction from first RBE3 grid indep grid
		# 1b)  2D and zero length RBAR => get direction from normal direction of hole (assume flat)  (not yet covered)
		# 1c)  3D and zero length RBAR (not covered)
		#
		# vect is [1,0,0] as local coordinate system z-axis is always in hole axis direction
		# get cid:
		# get first RBE3 grid indep grid
		nodeC = Rbe3D[nodeA][1][0]
		# print " NodeA: " + str(nodeA) + " NodeB: " + str(nodeB) + " NodeC: " + str(nodeC)
		#
		# define new mid point
		if NodeD[nodeA][3] == 0 and NodeD[nodeB][3] == 0:
			cord = midpoint([NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]],
							[NodeD[nodeB][0], NodeD[nodeB][1], NodeD[nodeB][2]])
			if ZeroLengthFlag == True:
				# write Grids with new midpoint
				# print "Cord:  " + str(cord)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeA, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeB, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				GridReplaceL.append(nodeA)
				GridReplaceL.append(nodeB)
		else:
			# script do not handle grids defined in local coordinates at this point
			# no coordinate replacemenr possible use NodeA as center for coord system
			cord = [NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]]

		# define cord system  put center in new bush center
		A1 = num2str(cord[0]);
		B1 = num2str(NodeD[nodeB][0]);
		C1 = num2str(NodeD[nodeC][0])
		A2 = num2str(cord[1]);
		B2 = num2str(NodeD[nodeB][1]);
		C2 = num2str(NodeD[nodeC][1])
		A3 = num2str(cord[2]);
		B3 = num2str(NodeD[nodeB][2]);
		C3 = num2str(NodeD[nodeC][2])

		temp_str1 = "%-8s%8d%8d%8s%8s%8s%8s%8s%8s+\n+%7s%8s%8s%8s\n" % (
			"CORD2R", cid, 0, A1, A2, A3, B1, B2, B3, " ", C1, C2, C3)
		CordStrL.append(temp_str1)
		vect = [1.0, 0.0, 0.0]
		temp_str2 = "%-8s%8d%8d%8d%8d%8.3f%8.3f%8.3f%8d\n" % (
			"CBUSH", key, Pid, nodeA, nodeB, vect[0], vect[1], vect[2], cid)
		CbushStrL.append(temp_str2)
		cid += 1

	return (GridStrL, CbushStrL, CordStrL, PbushStrL, GridReplaceL)	
		
def Cbush1d_2_Cbush(Cbush1D, NodeD, Rbe3D, SettingsD):
	# parameters for defining cbush behaviour
	# try to obtain from settings file
	try:
		# Knorm is not used as the orig cbush1d stiffness is used
		# Knorm=float(SettingsD['RBEX_CBUSH']['K_normal'])
		Kshear = float(SettingsD['RBEX_CBUSH']['K_shear'])
		Kbend = float(SettingsD['RBEX_CBUSH']['K_bendmoment'])
		Ktors = float(SettingsD['RBEX_CBUSH']['K_torsmoment'])
	# Pid is taken from PBUSH1D
	# Pid=float(SettingsD['RBEX_CBUSH']['PID'])
	except:
		print(" except material")
		# Knorm=1e5
		Kshear = 1e6
		Kbend = 1e6
		Ktors = 1e5
	# Pid=10001
	try:
		cid = int(SettingsD['RBEX_CBUSH']['Cord2r_start_id'])
	except:
		print(" except cid")
		cid = 1001
	try:
		zlf = SettingsD['RBEX_CBUSH']['ZeroLength']
		if zlf.upper() == "TRUE" or zlf.upper() == "YES" or zlf == "1":
			ZeroLengthFlag = True
		else:
			ZeroLengthFlag = False
	except:
		print("except ZeroLengthFlag")
		ZeroLengthFlag = True
	#
	GridStrL = []
	CbushStrL = []
	CordStrL = []
	PbushStrL = []
	#
	# for CBush1D genreate a PBUSH card where the "normal "  (z dir) stiffness is read from the PBUSH1D
	pidL = []
	# create one Pbush per cbush, unless duplicate
	for key in list(Cbush1D.keys()):
		prop = Cbush1D[key][2]
		if prop not in pidL:
			pidL.append(prop)
			tmp_str = "%-8s%8d%8s%8s%8s%8s%8s%8s%8s\n" % (
				"PBUSH", prop, "K", num2str(Kshear), num2str(Kshear), num2str(Cbush1D[key][3]), num2str(Kbend),
				num2str(Kbend), num2str(Ktors))
	PbushStrL.append(tmp_str)
	# Grids with replaced coordiantes
	GridReplaceL = []

	# remove all RBARs that are not referenced by an RBE3
	rbe3nodes = list(Rbe3D.keys())
	for key in list(Cbush1D.keys()):
		check_nodes = Cbush1D[key]
		if (check_nodes[0] in rbe3nodes) or (check_nodes[1] in rbe3nodes):
			continue
		else:
			Cbush1D.pop(key)
		# print "Rbar with id_ " + str(key) + " is removed as it is not connected to an RBE3"

	for key in list(Cbush1D.keys()):
		# find RBAR end node(s) and corresponding RBE3 indep nodes-calculate distance to center
		# use smallest distance found in case hole is not circular
		nodeA = Cbush1D[key][0]
		nodeB = Cbush1D[key][1]
		Pid = Cbush1D[key][2]
		# Need coordinates to define system for CBUSH with coincident grids if ZeroLengthFlag==True
		# Consider Following scenarios:
		# 1a)  2D or 3D and non-zero length RBAR => get Normal direction from RBAR, second direction from first RBE3 grid indep grid
		# 1b)  2D and zero length RBAR => get direction from normal direction of hole (assume flat)  (not yet covered)
		# 1c)  3D and zero length RBAR (not covered)
		#
		# vect is [1,0,0] as local coordinate system z-axis is always in hole axis direction
		# get cid:
		# get first RBE3 grid indep grid
		nodeC = Rbe3D[nodeA][1][0]
		# print " NodeA: " + str(nodeA) + " NodeB: " + str(nodeB) + " NodeC: " + str(nodeC)
		#
		# define new mid point
		if NodeD[nodeA][3] == 0 and NodeD[nodeB][3] == 0:
			cord = midpoint([NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]],
							[NodeD[nodeB][0], NodeD[nodeB][1], NodeD[nodeB][2]])
			if ZeroLengthFlag == True:
				# write Grids with new midpoint
				# print "Cord:  " + str(cord)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeA, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
					"GRID", nodeB, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
				GridStrL.append(tmp_str0)
				GridReplaceL.append(nodeA)
				GridReplaceL.append(nodeB)
		else:
			# script do not handle grids defined in local coordinates at this point
			# no coordinate replacemenr possible use NodeA as center for coord system
			cord = [NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]]

		# define cord system  put center in new bush center
		A1 = num2str(cord[0]);
		B1 = num2str(NodeD[nodeB][0]);
		C1 = num2str(NodeD[nodeC][0])
		A2 = num2str(cord[1]);
		B2 = num2str(NodeD[nodeB][1]);
		C2 = num2str(NodeD[nodeC][1])
		A3 = num2str(cord[2]);
		B3 = num2str(NodeD[nodeB][2]);
		C3 = num2str(NodeD[nodeC][2])

		temp_str1 = "%-8s%8d%8d%8s%8s%8s%8s%8s%8s+\n+%7s%8s%8s%8s\n" % (
			"CORD2R", cid, 0, A1, A2, A3, B1, B2, B3, " ", C1, C2, C3)
		CordStrL.append(temp_str1)
		vect = [1.0, 0.0, 0.0]
		temp_str2 = "%-8s%8d%8d%8d%8d%8.3f%8.3f%8.3f%8d\n" % (
			"CBUSH", key, Pid, nodeA, nodeB, vect[0], vect[1], vect[2], cid)
		CbushStrL.append(temp_str2)
		cid += 1

	return (GridStrL, CbushStrL, CordStrL, PbushStrL, GridReplaceL)

def CreateBoltCards(MergeL, GridD, bolt_id, node_id, cord_id, pre_load, pre_load_spc_id, lock_bolt_spc_id,
					pre_load_force_id, ctrl_node, bolt_type='Auto', pre_load_type=""):
	BoltCardL = []
	
	
	if len(MergeL) != 0 :
		top_str = ""
		bot_str = ""

		tmpL = []  # first item from MergeL
		tmpL2 = []  # 2nd item from MergeL
		
		# check if node is a head or tip part
		# tip has the higher pid
		
		for item in MergeL:
			tmpL.append(item[0])
			tmpL2.append(item[1])
			
		# Check so that the bolt halves meshes are coincident, that is, there are no duplicates in the MergeL pairs
		# Get pid from the bsurfs representing top and bottom

		#print("Check duplicates")	
		dupes1 = [x for n, x in enumerate(tmpL) if x in tmpL[:n]]
		#print(dupes1)	
		
		dupes2 = [x for n, x in enumerate(tmpL2) if x in tmpL2[:n]]
		#print(dupes2)
		
		cog = GetMidPointFromGrids(tmpL, GridD)	
		ctrl_node_str = WriteGrid(node_id, cog, 0, cord_id)
		
		if len(dupes1) == 0 and len(dupes2) == 0:
			start_str = "%-8s%8d%8d\n" % ("BOLT", bolt_id, node_id)
			n = 0
			m = 1
			N = len(MergeL)
			while n < N:
				# print MergeL[n]
				if n < N:
					if n == 0 and m == 1:
						top_str = "%-8s%-8s" % ("+", "TOP")
						bot_str = "%-8s%-8s" % ("+", "BOTTOM")
					elif m == 1 and n != 0:
						top_str = top_str + "%-16s" % ("+")
						bot_str = bot_str + "%-16s" % ("+")
					if m < 7 and n != (N - 1):
						top_str = top_str + ("%8d") % (MergeL[n][0])
						bot_str = bot_str + ("%8d") % (MergeL[n][1])
						n += 1
						m += 1
					elif m < 7 and n == (N - 1):
						top_str = top_str + ("%8d%8s\n") % (MergeL[n][0], " " * 8 * (7 - m) + "+")
						bot_str = bot_str + ("%8d\n") % (MergeL[n][1])
						n += 1
						m = 1
					elif m == 7 and n == (N - 1):
						top_str = top_str + ("%8d%-8s\n") % (MergeL[n][0], "+")
						bot_str = bot_str + ("%8d\n") % (MergeL[n][1])
						n += 1
						m = 1
					else:
						top_str = top_str + ("%8d%-8s\n") % (MergeL[n][0], "+")
						bot_str = bot_str + ("%8d%-8s\n") % (MergeL[n][1], "+")
						n += 1
						m = 1
						
			bolt_str = start_str + top_str + bot_str	
			
			cord_str = CreateCord2R(MergeL, GridD, cord_id, cog, ctrl_node)
			# print cord_str
			
		else:
			dup_str1=""
			dup_str2=""
			for dup in dupes1:
				dup_str1=dup_str1 + ("%s " % (dup) )
			for dup in dupes2:
				dup_str2= dup_str2 + ("%s " % (dup) )	
			# The bolt halves meshes are not coincident... 
			bolt_str="### Apex utils Error ### The bolt halves have non-matching meshes, BOLT card could not be created, fix mesh or change EQ_TOL in Settings.xml file\n### Failed grids:\n### " + dup_str1 + "\n### " + dup_str2 + "\n###"


		# print bolt_str
		# print ctrl_node_str
	
	else:
		print("MergeL is empty- this is the idication of  a mentat bolt without split\n")
		print("Proceed without writing BOLT cards and cord system. load and constraints will be generated\n")
		cord_str=""
		bolt_str=""
	
	if  bolt_type=='Auto':
		pre_load_spc_str = ("%-8s%8d%8s%8d\n") % ("SPC1", pre_load_spc_id, "12", node_id)
		lock_bolt_spc_str = ("%-8s%8d%8s%8d\n") % ("SPC1", lock_bolt_spc_id, "123", node_id)
	
		# enure there is a "." if load str is an integer
		tmp_load_str=num2str(pre_load)
		if tmp_load_str.find('.') <0 :
			tmp_load_str=tmp_load_str + "."
			
		if pre_load_type != "Disp":
			pre_load_str = ("%-8s%8d%8d%8d%8s%8.1f%8.1f%8.1f\n") % (
				"FORCE", pre_load_force_id, node_id, cord_id, tmp_load_str , 0.0, 0.0, 1.0)
		else:
			# create SPCD card and SPC1 for the spcp dofs
			# preload force id has to be same as LOAD id in Case control
			pre_load_str = ("%-8s%8d%8d%8d%8s\n") % ("SPCD", pre_load_force_id, node_id, 3, tmp_load_str ) + ( "%-8s%8d%8s%8d\n") % ( "SPC1", pre_load_spc_id, "3", node_id)
	else:
		pre_load_spc_str=""
		lock_bolt_spc_str=""
		pre_load_str=""
	
	
	return cord_str, bolt_str, ctrl_node_str, pre_load_spc_str, lock_bolt_spc_str, pre_load_str

def CreateCord2R(MergeL, NodeD, cid, orig, ctrl_node=0):
	# ctrl node is use to make sure z vector is pointing away from body
	# get directions from the nodes in the "top surf"
	# use top 3 nodes to get normal
	#print("======================")
	#print(("cid: " + str(cid) ))
	#print((str(orig[0]) + " " + str(orig[1]) + " " + str(orig[2])))
	#print(("ctrl node: " + str(ctrl_node) ))
	#print((str(NodeD[ctrl_node][0]) + " " + str(NodeD[ctrl_node][1]) + " " + str(NodeD[ctrl_node][2])))
	
	nodeA = MergeL[0][0]
	nodeB = MergeL[1][0]
	n = 2
	# loop through nodes until vector A-C is not colinear with vector A-B
	while n < len(MergeL):
		nodeC = MergeL[n][0]

		# assume they are planar
		# check so they  are not linear
		vect1 = GetVector(nodeA, nodeB, NodeD)
		vect2 = GetVector(nodeA, nodeC, NodeD)

		if GetDist(vect2, vect1) < 5.0:
			# print "vectors are colinear" + str(GetDist(vect2,vect1))
			# print "n=" + str(n)
			n += 1
		else:
			# print "vectors are ok: " + str(GetDist(vect2,vect1))
			# print "n=" + str(n)
			break
	# normal_vect=cross(vect1,vect2)
	normal_vect = [x / v_lgth(cross(vect1, vect2)) for x in cross(vect1, vect2)]
	# print normal_vect
	# try to obtain from settings file
	if ctrl_node != 0:
		# Z=NodeD[ctrl_node]
		# check if the projection of OZ to normal vect (local z axis) positive or negative
		OZ = [NodeD[ctrl_node][0] - orig[0], NodeD[ctrl_node][1] - orig[1], NodeD[ctrl_node][2] - orig[2]]
		if projection(OZ, normal_vect) > 0:
			#print("Coord system : " + str(cid) + " has Positive projection, switch direction of normal_vect")
			normal_vect = [-x for x in normal_vect]
		#else:
			#print("Coord system : " + str(cid) + " has Negative projection")
			

	# PointA  is the origin of coord syst, same as node A
	# Node B is z axis, that is Node A + the normal vector
	# Node C is spanning the x-z plane, use Node C

	# Move cord to origin O, that is subtract all coordinates with the vect A_O
	OA = [NodeD[nodeA][0] - orig[0], NodeD[nodeA][1] - orig[1], NodeD[nodeA][2] - orig[2]]

	# define cord system  put center in new bush center
	A1 = num2str(NodeD[nodeA][0] - OA[0]);
	B1 = num2str(NodeD[nodeA][0] + normal_vect[0] - OA[0]);
	C1 = num2str(NodeD[nodeC][0] - OA[0])
	A2 = num2str(NodeD[nodeA][1] - OA[1]);
	B2 = num2str(NodeD[nodeA][1] + normal_vect[1] - OA[1]);
	C2 = num2str(NodeD[nodeC][1] - OA[1])
	A3 = num2str(NodeD[nodeA][2] - OA[2]);
	B3 = num2str(NodeD[nodeA][2] + normal_vect[2] - OA[2]);
	C3 = num2str(NodeD[nodeC][2] - OA[2])

	if ctrl_node != 0:
		# Z=NodeD[ctrl_node]
		# check if the projection of OZ to orig on AB axis is positive or negative
		OZ = [NodeD[ctrl_node][0] - orig[0], NodeD[ctrl_node][1] - orig[1], NodeD[ctrl_node][2] - orig[2]]
		# AB=[float(B1)-float(A1),float(B2)-float(A2),float(B3)-float(A3)]
		AB = normal_vect
		#if projection(OZ, AB) > 0:
		#	print("Coord system : " + str(cid) + " has Positive projection")
		#else:
		#	print("Coord system : " + str(cid) + " has Negative projection")

	cord_str = "%-8s%8d%8d%8s%8s%8s%8s%8s%8s+\n+%7s%8s%8s%8s\n" % (
		"CORD2R", cid, 0, A1, A2, A3, B1, B2, B3, " ", C1, C2, C3)
	
	# if cid == 100004:
		# print("Something is fishy with this CID:" + str(cid) )
		# print("Node A: " +  str(nodeA))
		# print(NodeD[nodeA])
		# print("Node B: " +  str(nodeB))
		# print(NodeD[nodeB])
		# print("Node C: " +  str(nodeC))
		# print(NodeD[nodeC])
		# print("######################")
		
	return cord_str

def ZeroLengthCbush(Cbush1D, NodeD, Rbe3D, SettingsD):
	# try to obtain from settings file

	try:
		cid = int(SettingsD['RBEX_CBUSH']['Cord2r_start_id'])
	except:
		print(" except cid")
		cid = 1001

	#
	GridStrL = []
	CbushStrL = []
	CordStrL = []
	PbushStrL = []
	#
	# Grids with replaced coordiantes
	GridReplaceL = []

	# remove all RBARs that are not referenced by an RBE3
	rbe3nodes = list(Rbe3D.keys())
	for key in list(Cbush1D.keys()):
		check_nodes = Cbush1D[key]
		if (check_nodes[0] in rbe3nodes) or (check_nodes[1] in rbe3nodes):
			continue
		else:
			Cbush1D.pop(key)
		# print "Rbar with id_ " + str(key) + " is removed as it is not connected to an RBE3"

	for key in list(Cbush1D.keys()):
		# find RBAR end node(s) and corresponding RBE3 indep nodes-calculate distance to center
		# use smallest distance found in case hole is not circular
		nodeA = Cbush1D[key][0]
		nodeB = Cbush1D[key][1]
		Pid = Cbush1D[key][2]
		# Need coordinates to define system for CBUSH with coincident grids if ZeroLengthFlag==True
		# Consider Following scenarios:
		# 1a)  2D or 3D and non-zero length RBAR => get Normal direction from RBAR, second direction from first RBE3 grid indep grid
		# 1b)  2D and zero length RBAR => get direction from normal direction of hole (assume flat)  (not yet covered)
		# 1c)  3D and zero length RBAR (not covered)
		#
		# vect is [1,0,0] as local coordinate system z-axis is always in hole axis direction
		# get cid:
		# get first RBE3 grid indep grid
		nodeC = Rbe3D[nodeA][1][0]
		# print " NodeA: " + str(nodeA) + " NodeB: " + str(nodeB) + " NodeC: " + str(nodeC)
		#
		# define new mid point
		if NodeD[nodeA][3] == 0 and NodeD[nodeB][3] == 0:
			cord = midpoint([NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]],
							[NodeD[nodeB][0], NodeD[nodeB][1], NodeD[nodeB][2]])
			# if ZeroLengthFlag==True:
			# write Grids with new midpoint
			# print "Cord:  " + str(cord)
			tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
				"GRID", nodeA, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
			GridStrL.append(tmp_str0)
			tmp_str0 = "%-8s%8d%8d%8s%8s%8s%8d\n" % (
				"GRID", nodeB, 0, num2str(cord[0]), num2str(cord[1]), num2str(cord[2]), NodeD[nodeB][4])
			GridStrL.append(tmp_str0)
			GridReplaceL.append(nodeA)
			GridReplaceL.append(nodeB)
		else:
			# script do not handle grids defined in local coordinates at this point
			# no coordinate replacemenr possible use NodeA as center for coord system
			cord = [NodeD[nodeA][0], NodeD[nodeA][1], NodeD[nodeA][2]]

		if Cbush1D[key][3] == "":
			print("Defining cord system")
			# define cord system  put center in new bush center
			A1 = num2str(cord[0]);
			B1 = num2str(NodeD[nodeB][0]);
			C1 = num2str(NodeD[nodeC][0])
			A2 = num2str(cord[1]);
			B2 = num2str(NodeD[nodeB][1]);
			C2 = num2str(NodeD[nodeC][1])
			A3 = num2str(cord[2]);
			B3 = num2str(NodeD[nodeB][2]);
			C3 = num2str(NodeD[nodeC][2])

			temp_str1 = "%-8s%8d%8d%8s%8s%8s%8s%8s%8s+\n+%7s%8s%8s%8s\n" % (
				"CORD2R", cid, 0, A1, A2, A3, B1, B2, B3, " ", C1, C2, C3)
			CordStrL.append(temp_str1)
			vect = [1.0, 0.0, 0.0]
			# temp_str2="%-8s%8d%8d%8d%8d%8.3f%8.3f%8.3f%8d\n" % ("CBUSH",key,Pid,nodeA,nodeB,vect[0],vect[1],vect[2],cid)
			temp_str2 = "%-8s%8d%8d%8d%8d%8s%8s%8s%8d\n" % ("CBUSH", key, Pid, nodeA, nodeB, "", "", "", cid)
			CbushStrL.append(temp_str2)
			cid += 1
		else:
			temp_str2 = "%-8s%8d%8d%8d%8d%8s%8s%8s%8d\n" % (
				"CBUSH", key, Pid, nodeA, nodeB, "", "", "", Cbush1D[key][3])
			CbushStrL.append(temp_str2)
	#print("CordStrL")
	#print(CordStrL)
	return (GridStrL, CbushStrL, CordStrL, GridReplaceL)

def ReadGrids(inl1, FindNodeL=[]):
	# read also cp and cd put in list index 3 and 4
	# read grids store in dict with id as key followed by 	x	y	z	cp	cd
	#														0	1	2	3	4
	nodD = {}
	for lin in inl1:
		if lin[0:5] == 'GRID ':
			NodeId = int(lin[8:16])
			if FindNodeL != []:  # Only add Nodes from FindNode-List
				if NodeId not in FindNodeL:
					continue
			#
			try:
				cp = int(lin[16:24])
			except:
				cp = 0
			try:
				cd = int(lin[48:56])
			except:
				cd = 0

			# Apex exports floats on the format aaa-b (that is, without exponential aaaE-b
			a = lin[24:32].strip()
			b = lin[32:40].strip()
			c = lin[40:48].strip()
			#x = a[0:1] + a[1:].replace('-', 'E-').replace('EE', 'E')
			#y = b[0:1] + b[1:].replace('-', 'E-').replace('EE', 'E')
			#z = c[0:1] + c[1:].replace('-', 'E-').replace('EE', 'E')
			#x = a[0:1] + a[1:].replace('+', 'E+').replace('EE', 'E')
			#y = b[0:1] + b[1:].replace('+', 'E+').replace('EE', 'E')
			#z = c[0:1] + c[1:].replace('+', 'E+').replace('EE', 'E')
			
			x = a[0:1] + a[1:].replace('-', 'E-').replace('+', 'E+').replace('EE', 'E')
			y = b[0:1] + b[1:].replace('-', 'E-').replace('+', 'E+').replace('EE', 'E')
			z = c[0:1] + c[1:].replace('-', 'E-').replace('+', 'E+').replace('EE', 'E')
			

			nodD[NodeId] = [float(x), float(y), float(z), cp, cd]

	return nodD

def ReadForces(inl1, LoadId=999999):
	# need to add SPCD (?)
	ForceL = []
	LoadStr = ""
	for lin in inl1:
		if lin[0:6] == 'FORCE ' and int(lin[8:16]) not in ForceL:
			ForceL.append(int(lin[8:16]))
		elif lin[0:5] == 'PLOAD' and int(lin[8:16]) not in ForceL:
			ForceL.append(int(lin[8:16]))
		elif lin[0:5] == 'GRAV ' and int(lin[8:16]) not in ForceL:
			ForceL.append(int(lin[8:16]))
	LoadStr = LoadStr + "%-8s%8d%8.2f" % ("LOAD", LoadId, 1.0)
	i = 1
	j = i
	for item in ForceL:
		if (i == 1 and j < 3) or (i > 1 and j < 4):
			LoadStr = LoadStr + "%8.2f%8d" % (1.0, item)
			j += 1
		elif (i == 1 and j == 3) or (i > 1 and j == 4) or (i > len(ForceL)):
			LoadStr = LoadStr + "%8.2f%8d\n%8s" % (1.0, item, "")
			j = 1
			i += 1
		else:
			print("what i: " + str(i) + " j: " + str(j))
	if len(ForceL) != 0:
		return LoadStr
	else:
		return ""

def ReadSPCs(inl1, SpcId=888888):
	SpcL = []
	SpcStr = ""
	i = 0
	for lin in inl1:
		if lin[0:4] == 'SPC ' and int(lin[8:16]) not in SpcL:
			SpcL.append(int(lin[8:16]))
		i = 1
	j = i
	if len(SpcL) != 0:
		SpcStr = SpcStr + "%-8s%8d" % ("SPCADD", SpcId)

		for item in SpcL:
			if (i == 1 and j < 7) or (i > 1 and j < 8):
				SpcStr = SpcStr + "%8d" % (item)

				j += 1
			elif (i == 1 and j == 7) or (i > 1 and j == 8) or (i > len(SpcL)):
				SpcStr = SpcStr + "%8d\n%8s" % (item, "")
				j = 1
				i += 1
			else:
				print("what")
	return SpcStr, SpcL

def ReadContacts(inl1):
	BCTabL = []
	BCTabStrL = []
	BConectD = {}
	BConprgD = {}
	n = 0
	N = len(inl1)
	while n < N:
		# for lin in inl1:
		lin = inl1[n]
		if lin.find('BCTABL1') == 0 and int(lin[8:16]) not in BCTabL:
			BCTabL.append(int(lin[8:16]))
			BCTabStrL.append("%-8s = %d\n" % ("BCONTACT", int(lin[8:16])))
		elif lin.find('BCONECT') == 0:
			tmp_str = inl1[n - 1]
			indx = tmp_str.find('#')
			if indx > 0:
				ContactType = tmp_str[indx + 1:indx + 2]
				#print "@@@@ " + tmp_str[indx + 1:indx + 2]
				if ContactType != "G" and ContactType != "T":
					ContactType = "D"
			else:
				ContactType = "D"
			# Create a dict with BCONPRG and the assigned contact type
			BConprgD[int(lin[16:24])] = ContactType
		n += 1
	#print BConprgD
	return BCTabStrL, BConprgD

def ReadContacts400(inl1,OptionsD={}):
	BCTabL = []
	BCTabStrL = []
	BConectD = {}
	BConprgD = {}
	n = 0
	N = len(inl1)
	
	# remove all empty entries from OptionsD['contactPairSettings']
	# these are artefacts from splitting of contact settings
	_tmpItemL=[]
	if 'contactPairSettings' in OptionsD.keys():
		for item in OptionsD['contactPairSettings']:
			if OptionsD['contactPairSettings'][item]=={}:
				_tmpItemL.append(item)
		
		for item in _tmpItemL:
			del OptionsD['contactPairSettings'][item]
	
	
	while n < N:
		# for lin in inl1:
		lin = inl1[n]
		
		if lin.find('BCTABL1') == 0 and int(lin[8:16]) not in BCTabL:
			BCTabL.append(int(lin[8:16]))
			BCTabStrL.append("%-8s = %d\n" % ("BCONTACT", int(lin[8:16])))
		elif lin.find('BCONECT') == 0:
			# Check if contact in question has an entry in OptionsD
			# if not then get contact type OptionsD (coming from customAttributes) returned dict values are dicts
			# if not, get contact type from String  returned dict values are strings of length 1
			#print("*******************")
			tmp_str = inl1[n - 1].split('/')[1].strip()
			indx = tmp_str.find('#')
			_default_settingsFlag=True
			
			_tmp_id=int(lin[16:24])
			if 'contactPairSettings' in OptionsD.keys():
				if tmp_str in OptionsD['contactPairSettings'].keys():
					#print("I am in 1a")
					_optD= OptionsD['contactPairSettings'][tmp_str]
					_default_settingsFlag=False
					BConprgD[_tmp_id]=_optD

			if _default_settingsFlag:
				#print("I am in 2")
				if indx > 0:
					#print("# is found, id is",int(lin[16:24]) )
					ContactType = tmp_str[indx + 1:indx + 2]
					#print "@@@@ " + tmp_str[indx + 1:indx + 2]
					if ContactType != "G" and ContactType != "T":
						ContactType = "D"
				else:
					#print("Use default contact Type, id is",int(lin[16:24]) )
					ContactType = "D"
				# Create a dict with BCONPRG and the assigned contact type
				BConprgD[_tmp_id] = ContactType
				
				
			#print("BCONECT: ",_tmp_id," Contact: ",BConprgD[_tmp_id])
			
		n += 1
	print(BConprgD)
	return BCTabStrL, BConprgD


def ReadDisplayElems(inl1, PartName):
	DispL = []
	N = len(inl1)
	n = 0
	while n < N:
		find_str = " Part " + PartName
		# print find_str
		if inl1[n].find(find_str) > -1:
			# print "Processing 2"
			n += 5
			while inl1[n][0] == "C":
				DispL.append(int(inl1[n][8:16]))
				n += 1
		else:
			n += 1
	return DispL

def ReadRbars(inl1, FindRbarL=[]):
	RbarD = {}
	for lin in inl1:
		if lin[0:5] == 'RBAR ':
			ElemId = int(lin[8:16])
			if FindRbarL != []:  # Only add Nodes from FindNode-List
				if ElemId not in FindRbarL:
					continue
			RbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
	return RbarD
	
def ReadCbars(inl1, NameStr="Flexible_1D", FindCbarL=[]):
	CbarD = {}
	n=0
	N=len(inl1)
	first_n=""
	while n<N:
		if NameStr != "":
			#Check so that descriptor match NameStr (line above actual Card)
			if inl1[n].find(NameStr) > -1:
				# get the index of first descriptor
				if first_n== "":
					first_n=n
				lin=inl1[n+1]
				GoFlag=True
			else:
				GoFlag=False
			n+1
				
		else:
			lin=inl1[n]
			GoFlag=True
		
		if GoFlag:
			if lin[0:5] == 'CBAR ':
				ElemId = int(lin[8:16])
				if FindCbarL != []:  # Only add Nodes from FindNode-List
					if ElemId in FindCbarL:
						CbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
				else:
					CbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
		n+=1
		
	# workaround for HawkSp1 bug, first descriptor is not exported
	# get first occurrence of descriptor, step back 1
	if inl1[first_n - 1][0:5] == 'CBAR ':
		lin=inl1[first_n - 1]
		ElemId = int(lin[8:16])
		if FindCbarL != []:  # Only add Nodes from FindNode-List
			if ElemId in FindCbarL:
				CbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
		else:
			CbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
	return CbarD

def ReadCbush1D(inl1, FindCbush1DL=[]):
	Cbush1D = {}
	Pbush1D = ReadPbush1D(inl1)
	for lin in inl1:
		if lin[0:8] == 'CBUSH1D ':
			ElemId = int(lin[8:16])
			PropId = int(lin[16:24])
			if FindCbush1DL != []:  # Only add Nodes from FindNode-List
				if ElemId not in FindCbush1DL:
					continue
				#        eid			nod1			nod2	 pbush id	      stiff			  damp
			try:
				stiff = Pbush1D[PropId][0]
			except:
				print("There is no stiffness specified for the Cbush1d with id " + str(ElemId))
				stiff = 0
			Cbush1D[ElemId] = [int(lin[24:32]), int(lin[32:40]), PropId, Pbush1D[PropId][0], Pbush1D[PropId][1]]
	return Cbush1D

def ReadCbushD(inl1, FindCbushL=[]):
	CbushD = {}
	#PbushD=ReadPbushD(inl1)
	for lin in inl1:
		if lin.find('CBUSH ') == 0:
			ElemId = int(lin[8:16])
			PropId = int(lin[16:24])
			if FindCbushL != []:  # Only add Nodes from FindNode-List
				if ElemId not in FindCbushL:
					continue
				#        eid			nod1			nod2	 pbush id	      stiff			  damp
			#try:
			#	stiff = PbushD[PropId][0]
			#except:
			#	print "There is no stiffness specified for the Cbush1d with id " + str(ElemId)
			#	stiff = 0
			try:
				cid = int(lin[64:72])
			except:
				cid = ""
			CbushD[ElemId] = [int(lin[24:32]), int(lin[32:40]), PropId, cid]
	return CbushD

def ReadPbush1D(inl1):
	Pbush1D = {}
	for lin in inl1:
		if lin[0:8] == 'PBUSH1D ':
			PropId = int(lin[8:16])
			Stiff = float(lin[16:24])
			Damp = float(lin[24:32])
			Pbush1D[PropId] = [Stiff, Damp]
			print("Stiff=" + str(Stiff))
			print("Stiff=" + str(Stiff))
	return Pbush1D
	
def ReadPbushD(inl1):
	PbushD = {}
	for lin in inl1:
		if lin[0:6] == 'PBUSH ':
			PropId = int(lin[8:16])
			#Stiff = float(lin[16:24])
			#tmp=
			Stiff=[float(i) for i in lin[24:72].split()]
			#Damp = float(lin[24:32])
			PbushD[PropId] = [Stiff]
			#print "Stiff=" + str(Stiff)
			#print "Stiff=" + str(Stiff)
	return PbushD	
	
def ReadRbe3s(inl1, nodeL):
	# This RBE3 parser will  read the RBE3 format as generated from Apex and not the more general format
	# the dependent (mid) node will be used as key
	FindRbe3L = []
	Rbe3D = {}
	if nodeL != []:
		n = 0
		while n < len(inl1):
			lin = inl1[n]
			if inl1[n][0:5] == 'RBE3 ':
				ElemId = int(inl1[n][8:16])
				DepN = int(inl1[n][24:32])
				if nodeL != []:  # Only RBE3s that have dep nodes in NodeL
					if DepN not in nodeL:
						# print "skip RBE3: " + str(ElemId)
						n += 1
						continue
				IndepN = []
				tmpL = []
				# print "found rbe3"
				i = 8  # first lines
				while i <= 10:
					try:
						# print inl1[n]
						# print "### i: " + str(i) + "   n:" + str(n)
						# print str((i-1)*8)
						if inl1[n][(i - 1) * 8] != "+":
							# print inl1[n][(i-1)*8:(i-1)*8+8]
							IndepN.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
							i += 1
						else:
							# print " found a \+"
							n += 1  # encountered a continuation "+"
							i = 2
						# print str(i) + " " + str(n)
					except:
						n += 1
						break
				Rbe3D[DepN] = [ElemId, IndepN]
			# print Rbe3D[ElemId]
			else:
				n += 1
	return Rbe3D

def ReadBsurfs(SettingsD, bdf_file, DefaultContactType="Glue", DefaultBoltPreload="0N", PreloadLookupFile="", EditContactTable=False, BconstrD={}, CsvSeparator="," ):
	
	
	if SettingsD['NASTRAN_JOB_SETUP']['CreateBoltPreLoad'].upper().replace("YES","TRUE")=="TRUE":
		BoltPreLoadFlag=True
	else:
		BoltPreLoadFlag=False
	
	# Update to deal with new "THRU" format introduced in 2020
	#print("I am in ReadBsurfs")
	#print(BconstrD)
	
	# bdf_file=r"C:\tmp\TestTouch\Bolt_5000N.bdf"
	bdf_h = open(bdf_file, 'r')
	inl1 = bdf_h.readlines()
	bdf_h.close()

	if PreloadLookupFile != "":
		preload_file_h=open(PreloadLookupFile,'r')
		preload_lines=preload_file_h.readlines()
		preload_file_h.close()
		PreLoadD={}
		try:
			for line in preload_lines[1:]:
				tmpL=line.split()
				PreLoadD['m'+tmpL[0]] = tmpL[1] + " " + tmpL[2]
		except:
			print("Preload file seems to have the wrong format...omitting ")
			PreloadLookupFile = ""
			
	
	# This  parser will  read the  format as generated from Apex and not the more general format
	# the dependent (mid) node will be used as key
	FindBurfL = []
	BSurfD = {}
	BConectD = {}
	#PartD = {}
	CElemD = {}
	BCbodyL = []
	BCbodyD = {}
	bc = 0
	
	# moved to function
	PartD=createPart2PidDict(inl1)


	n = 0
	N = len(inl1)
	while n < N:
		lin = inl1[n]
		if inl1[n].find('BSURF') == 0:
			BSurfId = int(inl1[n][8:16])
			ElemL = []
			tmpL = []
			i = 3  # first lines
			while i <= 10:
				try:
					if inl1[n][(i - 1) * 8] != "+" :
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						
						tmp_str= inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]
						if tmp_str.find("THRU") < 0:
							ElemL.append(int(tmp_str))
							i += 1
							
						else:
							#print("I am in THRU")
							start_id=ElemL[-1]
							i+=1
							end_id=int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8])
							if 1==1:
								i+=1
								tmp_str= inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]
								if tmp_str.find("BY") > -1:
									i+=1
									step=int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8])

								else:
									step=1
								i+=1
							#print("###",step)	
							for thru_id in range(start_id + 1,end_id + 1,step):
								ElemL.append(thru_id)
							i+=1
	
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					#traceback.print_exc()
					n += 1
					break
			BSurfD[BSurfId] = ElemL

		elif inl1[n].find('BCBODY') == 0:
			#print " I am in bcbody"
			# if BCbodyL==[]:
			if bc == 0:
				# first pass, get the assembly name
				assy_prefix = inl1[n - 6][1:].strip()
				assy_name = assy_prefix
			else:
				assy_name = assy_prefix + '_' + str(bc)
			#print assy_name
			BCbodyL.append(int(inl1[n][8:16]))
			BCbodyL.append(int(inl1[n][8:16]))
			# [3d, deform, bsurf_id, mentat_name]
			BCbodyD[int(inl1[n][8:16])] = [inl1[n][24:32].strip(), inl1[n][32:40].strip(), int(inl1[n][40:48]), assy_name]
			n += 1
			bc += 1

		elif inl1[n].find('BCONECT') == 0:

			BConectId = int(inl1[n][8:16])
			BConprgId = int(inl1[n][16:24])
			# Stefan Added 200505
			try:
				BConprpId = int(inl1[n][24:32])
			except:
				BConprpId = ""
			SurfL = []
			tmpL = []
			# title str: 
			tmp_str = inl1[n - 1]
			BConectName=tmp_str.split('/')[1].strip()
			#print tmp_str
			indx = tmp_str.find('#')
			if indx > 0:
				ContactType = tmp_str[indx + 1:indx + 2]
				# print "@@@@ " + tmp_str[indx+1:indx+2]
				if ContactType != "G" and ContactType != "T":
					ContactType = DefaultContactType[0]
			else:
				ContactType = DefaultContactType[0]

			i = 5  # first lines
			while i <= 10:
				try:
					if inl1[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						SurfL.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					n += 1
					break

			# Create a dict with BCONPRG and the assigned contact type
			BConectD[BConectId] = [SurfL, ContactType, BConprgId,BConprpId,BConectName]
		elif lin.find('CQUAD') == 0 or lin.find('CTRIA') == 0 or lin.find('CTETRA') == 0 or lin.find(
				'CPENTA') == 0 or lin.find('CHEXA') == 0:
			lin = inl1[n]
			lin2 = inl1[n + 1]
			elem_id = int(lin[8:16])
			tmp_nodeL = []
			pid = int(lin[16:24])
			i = 4  # first line
			while i <= 10:
				try:
					if inl1[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						tmp_nodeL.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					n += 1
					break
			CElemD[elem_id] = [tmp_nodeL, pid]

		else:
			n += 1

	# Now find the Bolt BSurfs
	BoltBsurfL = []
	BoltBConectL = []
	BoltPreloadD = {}  # store bolt prloads in dict with Bsurfs  as keys
	#print(list(BConectD.keys()))
	
	if BoltPreLoadFlag:
		for key in list(BConectD.keys()):
			tmp_bsurfs = BConectD[key][0]
			#print tmp_bsurfs

			eid1_flag = False
			eid2_flag = False
			# BConectD[BConectId]=[SurfL,ContactType,BConprgId]
			# check so that both bsurfs in the contact pair has "#Bolt" in their name
			# first check so that there are exactly two contact bodies
			# Warning!!! Assuming bcbodies and bsurfs has the  same id, easy to fix... will do later if needed
			if len(tmp_bsurfs) == 2:
				# get 1st eid to get the part
				eid1 = BSurfD[tmp_bsurfs[0]][0]
				# print eid1
				eid2 = BSurfD[tmp_bsurfs[1]][0]
				# print eid2
				pid1 = CElemD[eid1][1]
				# print pid1
				pid2 = CElemD[eid2][1]
				# print pid2
				#print PartD
				#print("PartD.keys()",PartD.keys())
				for part in list(PartD.keys()):
					if pid1 in PartD[part]:
						#replace with function
						part_preload, part_preload_type, eid1_flag=getPreloadFromPartName(part,PreLoadD,PreloadLookupFile)

						#Check pid 2 only if pid 1 is ok
						if eid1_flag:
							eid2_flag = False
							if pid2 in PartD[part]:
								if part.find("Bolt") > -1:
									#print(" Found bolt part for 2nd body " + part)
									eid2_flag = True


			if eid1_flag and eid2_flag:
				BoltBsurfL = BoltBsurfL + tmp_bsurfs
				BoltBConectL.append(key)
				# assign pre_load to dict with BSURF ids as key
				BoltPreloadD[tmp_bsurfs[0]] = [part_preload, part_preload_type]
				BoltPreloadD[tmp_bsurfs[1]] = [part_preload, part_preload_type]
			else:
				print( part + " is not a bolt part")	

	part_cntD = {}

	for part in list(PartD.keys()):
		part_cntD[part] = 0
	#1
	for item in BCbodyL:
		#print item
		# eid1=BSurfD[tmp_bsurfs[0]][0]
		eid1 = BSurfD[item][0]
		pid1 = CElemD[eid1][1]
		for part in list(PartD.keys()):
			if pid1 in PartD[part]:
				# BcBodyPartNameD[item]=part
				if part_cntD[part] > 0:
					BCbodyD[item][3] = part.split('(')[0] + '_' + str(part_cntD[part])
				else:
					BCbodyD[item][3] = part.split('(')[0]
				part_cntD[part] = part_cntD[part] + 1

	#2#
	
	# generate Contact pair info 
	# first, get the params used for all contact pairs:
	paramL=[]
	for key in list(BconstrD.keys()):
		tmpD=BconstrD[key]
		for key2 in tmpD:
			if key2 not in paramL:
				paramL.append(key2)
	
	#
	BCBodyTableD = {}

	tmp_str = "%8s" % ""
	#ct_str  = " ,"
	ct_str = ""
	
	# Header bc_str
	if BconstrD != {}:
		bc_str="\n"
		bc_str+="Contact pair id"+ "," + "Body1" + "," + "Body2" 
		for param in sorted(paramL,reverse=True):
			bc_str+= "," + param
		bc_str+="\n"
		
		#BConectD[BConectId] = [SurfL, ContactType, BConprgId]
		for bcon_id in sorted(BConectD.keys()):
			#bc_str+= str(bcon_id) + "," + str(BConectD[BConectId][0][0])  + "," + str(BConectD[BConectId][0][1] ) 
			id1=BConectD[bcon_id][0][0]
			id2=BConectD[bcon_id][0][1]
			bc_str+= str(bcon_id) + "," + BCbodyD[id1][3]  + "," + BCbodyD[id2][3]
			
			for param in sorted(paramL,reverse=True):
				tmpD=BconstrD[bcon_id]
				if param in list(tmpD.keys()):
					bc_str += "," + str(tmpD[param])
				else:
					bc_str += ","
					
			bc_str += "\n"
		
	
	# Header ct_str
	for bsurf in sorted(BSurfD.keys()):
		tmp_bcbody_name=BCbodyD[bsurf][3]
		#print(tmp_bcbody_name)
		tmp_str = tmp_str + "%8d" % bsurf
		ct_str = ct_str + ","  + tmp_bcbody_name
	tmp_str = tmp_str + "\n"
	ct_str = ct_str + "\n"
	tmp_str = tmp_str + "-" * len(tmp_str) + "\n"
	# Table
	for bsurf in sorted(BSurfD.keys()):
		BCBodyTableD[bsurf] = []
		# find pairs where this bsurf appear, add all pairs where this key exist
		[BCBodyTableD[bsurf].extend(BConectD[key][0]) for key in list(BConectD.keys()) if bsurf in BConectD[key][0]]

		tmp_str = tmp_str + "%-7d" % bsurf + "|"
		#ct_str = ct_str + "," + BCbodyD[bsurf][3] 
		ct_str = ct_str +  BCbodyD[bsurf][3] 
		for col in sorted(BSurfD.keys()):
			# check self contact
			if col == bsurf:
				self_contact = False
				tmp0 = "%8s" % ""
				# check all pairs in BConectD if there is self contact for any of the pairs
				for bcon_id in list(BConectD.keys()):
					if BConectD[bcon_id][0] == [col, col]:
						self_contact = True
						# dig up contact status for face pairs
						tmp_status = BConectD[bcon_id][1]
						tmp0 = "%8s" % tmp_status					
					
				tmp_str = tmp_str + tmp0
				ct_str= ct_str + "," + tmp0.strip()
			elif col in BCBodyTableD[bsurf]:
				# find status
				for bcon_id in list(BConectD.keys()):
					if sorted([col, bsurf]) == sorted(BConectD[bcon_id][0]):
						tmp_status = BConectD[bcon_id][1]
				tmp_str = tmp_str + "%8s" % tmp_status
				ct_str = ct_str + "," + tmp_status
				
			else:
				tmp_str = tmp_str + "%8s" % " "
				ct_str= ct_str + "," + " "
		tmp_str = tmp_str + "\n"
		ct_str = ct_str + "\n"
	
	
	# Check if there is already exactly one csv file in the working directory with a matching number of contact bodies
	# If so, reuse this contact table
	attempt_reuse_csv=False
	try:
		if SettingsD['NASTRAN_JOB_SETUP']['ReuseExistingContactCsv'].upper().replace("YES","TRUE") == "TRUE":
			attempt_reuse_csv=True
	except:
		pass
		
	ReuseFlag=False		
	if attempt_reuse_csv:
	
		csv_fileL=[]
		for file in os.listdir(os.path.dirname(bdf_file)):
			if file.endswith("_contact_table.csv"):
				csv_fileL.append((os.path.join(os.path.dirname(bdf_file), file)))
		if len(csv_fileL) ==1:
			print("Exactly one csv file exists in working directory...check number of contact bodies")
			ct_tableD, csv_contact_bodies=read_contacttable_csv(csv_fileL[0])
		
			print("=== Bodies from csv files===")
			# bodies from file
			for tmp_body in csv_contact_bodies:
				print(tmp_body)
			print("=== Bodies from model===")	
			# Compare to contact table names in model
			for bsurf in sorted(BSurfD.keys()):
				tmp_bcbody_name=BCbodyD[bsurf][3]
				#print(tmp_bcbody_name)
				
			if len(csv_contact_bodies) == len(list(BSurfD.keys())):
				print("Same number of contact bodies... reuse")
				contact_table_file=csv_fileL[0]
				ReuseFlag=True
				n=0
				for bsurf in sorted(BSurfD.keys()):
					tmp_bcbody_name=BCbodyD[bsurf][3]
					if tmp_bcbody_name == csv_contact_bodies[n]:
						pass
					else:
						ReuseFlag=False
						print("Contact body names from existing csv  do not match, application can not reuse ")
						print((tmp_bcbody_name, csv_contact_bodies[n]))
						break
					n+=1
				
			
			
			
	# write contact table csv file
	#try:
	#if 1==1:
	
	try:
		if len(bc_str.strip())==0 or len(ct_str.strip())==0:
			EditContactTable=False
	except:
		EditContactTable=False
	
	if EditContactTable and not ReuseFlag:
		#contact_table_file=r"C:\temp\contact_table.csv"
		contact_table_file=os.path.dirname(bdf_file) + '\\' + os.path.basename(bdf_file)[:-4] + '_contact_table.csv'
		
		# Check if there is already a csv file in the working directory with a matching number of contact bodies
		# If so, reuse the contact table
	
		ct_file_h=open(contact_table_file,'w')
		ct_file_h.write(ct_str)
		ct_file_h.write(bc_str)
		ct_file_h.close()
	#except:
	#	print(" Can not write contact table, file " + contact_table_file + " is likely open in excel")
	
	print(("Edit Contact Table",EditContactTable))	
	if EditContactTable:
		# change separator as per Utils Setting
		
		print(("CsvSeparator__" + CsvSeparator + "__"))
		
		if CsvSeparator != ",":
			tmp_csv_h=open(contact_table_file,'r')
			tmp_csv_lines=tmp_csv_h.readlines()
			tmp_csv_h.close()
			tmp_csv_h=open(contact_table_file,'w')
			for line in tmp_csv_lines:
				tmp_csv_h.write(line.replace(",",CsvSeparator))
			tmp_csv_h.close()
			
		# Allow user to make manual edits - space in filename is now allowed

		print(("\"" + contact_table_file + "\""))
		os.system("\"" + contact_table_file + "\"")
		
		
		
		# here... we need to re-open the bdf file and write back stuff
		ContinueFlag=False
		while ContinueFlag==False:
			try:
				file_h=open(contact_table_file,'a')
				#file_h.write("read file\n")
				file_h.close()
				ContinueFlag=True
				
			except:
				print("file is open...wait")
				time.sleep(1)
		
		
		# change separator back to comma
		if CsvSeparator != ",":
			tmp_csv_h=open(contact_table_file,'r')
			tmp_csv_lines=tmp_csv_h.readlines()
			tmp_csv_h.close()
			tmp_csv_h=open(contact_table_file,'w')
			for line in tmp_csv_lines:
				tmp_csv_h.write(line.replace(CsvSeparator,","))
			tmp_csv_h.close() 
		
	else:
		print("No Option to Edit contact table selected, and not yet implemented for sol400")
		contact_table_file=""
	
	
	return BSurfD, CElemD, BoltBsurfL, BoltBConectL, BCBodyTableD, BConectD, tmp_str, BoltPreloadD, BCbodyL, BCbodyD, contact_table_file

def GetMidPointFromGrids(GridL, GridD):
	x = [GridD[i][0] for i in GridL]
	y = [GridD[i][1] for i in GridL]
	z = [GridD[i][2] for i in GridL]
	return [sum(x) / len(x), sum(y) / len(y), sum(z) / len(z)]

def ReadRbe2s(inl1, nodeL=[]):
	# This RBE2 parser will  read the RBE2 format as generated from Apex and not the more general format
	# the independent (mid) node will be used as key
	FindRbe2L = []
	Rbe2D = {}
	# print "Processing ReadRbe2s"
	N=len(inl1)
	n = 0
	while n < N:
		lin = inl1[n]
		if inl1[n][0:5] == 'RBE2 ':
			ElemId = int(inl1[n][8:16])
			DepN = int(inl1[n][16:24])
			if nodeL != []:  # Only RBE2s that have indep nodes in NodeL
				if DepN not in nodeL:
					# print "skip RBE2: " + str(ElemId)
					n += 1
					continue
			#else:
			#	pass
			IndepN = []
			tmpL = []
			#print("found rbe2")
			i = 5  # first lines
			while i <= 10:
				try:
					# print inl1[n]
					# print "### i: " + str(i) + "   n:" + str(n)
					# print str((i-1)*8)
					if inl1[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						IndepN.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						# print " found a \+"
						n += 1  # encountered a continuation "+"
						i = 2
					# print str(i) + " " + str(n)
				except:
					n += 1
					break
			Rbe2D[DepN] = [ElemId, IndepN]
		else:
			n += 1
	
	return Rbe2D

def old_ReadRbe2s(inl1, nodeL):
	# This RBE2 parser will  read the RBE2 format as generated from Apex and not the more general format
	# the independent (mid) node will be used as key
	FindRbe2L = []
	Rbe2D = {}
	# print "Processing ReadRbe2s"
	if nodeL != []:
		n = 0
		while n < len(inl1):
			lin = inl1[n]
			if inl1[n][0:5] == 'RBE2 ':
				ElemId = int(inl1[n][8:16])
				DepN = int(inl1[n][16:24])
				if nodeL != []:  # Only RBE2s that have indep nodes in NodeL
					if DepN not in nodeL:
						# print "skip RBE2: " + str(ElemId)
						n += 1
						continue
				IndepN = []
				tmpL = []
				# print "found rbe2"
				i = 5  # first lines
				while i <= 10:
					try:
						# print inl1[n]
						# print "### i: " + str(i) + "   n:" + str(n)
						# print str((i-1)*8)
						if inl1[n][(i - 1) * 8] != "+":
							# print inl1[n][(i-1)*8:(i-1)*8+8]
							IndepN.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
							i += 1
						else:
							# print " found a \+"
							n += 1  # encountered a continuation "+"
							i = 2
						# print str(i) + " " + str(n)
					except:
						n += 1
						break
				Rbe2D[DepN] = [ElemId, IndepN]
			else:
				n += 1
	
	
	
	
	
	
	
	
	
	return Rbe2D


	
def ReadMPCs(inl1, NodeD, Tol=0.01):
	# This MPC parser will  read the MPC fofrmat as generated from Apex and not the more general format
	# the dependent (mid) node will be used as key
	FindMPCL = []
	MPCD = {}
	FailedL = []
	MergeL = []
	ProcessedL = []
	AllIndepsD = {}
	bdf_log_L = []
	min_tol_L = []
	if 1 == 1:
		n = 0
		Len = len(inl1)
		while n < Len:
			lin = inl1[n]
			if inl1[n][0:5] == 'MPC* ':
				# print "found MPC"
				DepN = int(inl1[n][24:40])
				IndepN = []
				tmpL = []
				n += 1
				m = 1
				# First pass is simply sorting dep and indep nodes
				# only keep Indep Nodes that are within tol
				# for even n, read integer from position 8:24
				# for odd  n, read integer from position 24:40

				while inl1[n][0:2] == "* ":
					if m % 2 != 0:  #
						# print inl1[n]
						# print "###" + inl1[n][8:24] + "###"
						nde = int(inl1[n][8:24])
					else:
						# print inl1[n]
						# print "@@@" + inl1[n][24:40] + "@@@"
						nde = int(inl1[n][24:40])

					if nde not in IndepN and nde != DepN:
						IndepN.append(nde)

					m += 1
					if n < Len - 1:
						n += 1
					else:
						print("End of File")
						break

				# Append to ProcessedL  in order to avoid rework, (typically 6 MPCs per node with same ids)
				tmplist = [DepN, sorted(IndepN)]
				if not tmplist in ProcessedL:
					ProcessedL.append(tmplist)

					# Only keep Nodes within Tol... save other nodes to Fail
					KeepIndepL, FailedMinL, bdf_log_tmp, min_tol_tmp = GetCoincidentMPCNodes2(DepN, IndepN, NodeD, Tol)
					bdf_log_L = bdf_log_L + bdf_log_tmp
					min_tol_L = min_tol_L + (min_tol_tmp)

					if [DepN, FailedMinL] not in FailedL and FailedMinL != "":
						FailedL.append([DepN, FailedMinL])
					# else:
					#	print "tji"
				# else:
				#	print " duplicate"
				#	print tmplist

				##print "adding nodes to MPCD"
				## Check if Dep Node already exists as dict key, then  append Indep nodes after checks
				# if DepN  already an Indep:
				if DepN in AllIndepsD:
					# treat DepN as an Indep when perfoming checks
					if DepN not in KeepIndepL:
						KeepIndepL.append(DepN)
					# assign the old DepN to New DepN
					DepN = AllIndepsD[DepN]
				elif DepN in list(MPCD.keys()):
					# DepN is already a key in MPCD
					continue
				else:
					# This is a new entry
					MPCD[DepN] = []

				# Check the Indep Nodes
				for IndepNode in KeepIndepL:
					# print IndepNode
					# Check if  Indep is  dep in another MPCD and so that it is not same as DepN...
					# then... add these Indeps (and DepN) to existing MPCD
					if IndepNode in list(MPCD.keys()) and IndepNode != DepN:
						#
						print("The Indep " + str(IndepNode) + "  is dep in another MPC. Merge those  indeps with new")
						#
						for ReassignNode in MPCD[IndepNode]:
							if ReassignNode not in MPCD[DepN]:
								MPCD[DepN].append(ReassignNode)

						# delete the key that has been reasssigned
						del MPCD[IndepNode]
					#
					# move already assigned Indep Nodes (if there are any) to new Indep
					# finally delete key from MPCD"
					# try:
					#	for ReassignNode in MPCD[DepN]:
					#		if ReassignNode not in MPCD[IndepNode] and ReassignNode != IndepNode:
					#			MPCD[IndepNode].append(ReassignNode)
					#			#del MPCD[DepN]
					# except:
					#	continue
					# DepN=IndepNode # make node new DepN and assign remaining nodes

					# Indep is not dep in another MPC...simply add it unless it already exists

					# print "DepNode " + str(DepN)
					# print "IndepNode " + str(IndepNode)

					# if DepN in MPCD.keys():
					try:
						# if DepN is already existing
						if not IndepNode in MPCD[DepN]:
							# ... and the Indep is not already added...
							# print "Node " + str(IndepNode) + " is not duplicate"
							MPCD[DepN].append(IndepNode)
							if not IndepNode in AllIndepsD:
								# print " adding " + str(IndepNode) + " to MPCD[ " + str(DepN) + "]"
								AllIndepsD[IndepNode] = DepN
					except:
						continue
					# print " #######diagnostoics####################################################################"
					# if DepN in AllIndepsD.keys():
					#	print "DepN " +str(DepN) + "  is a key in AllIndepsD"
					# print "IndepNode: " + str(IndepNode)
					# print "KeepIndepL"
					# for item in KeepIndepL:
					#	print item
					# print " #####################################################################################"

			# next line in reading MPC cards
			else:
				if n < Len - 1:
					n += 1
				else:
					break
	# print "MPCD[40]"
	# print MPCD[40]
	# print "MPCD[66]"
	# print MPCD[66]
	print("Processed MPCs finding" + str(len(list(MPCD.keys()))) + " Indeps")
	# print "=====MPCD====="
	# print MPCD
	for key in list(MPCD.keys()):
		# print "#" + str(key)
		# print MPCD[key]
		for item in MPCD[key]:
			# take care of duplicates
			if not (item, key) in MergeL:
				MergeL.append((item, key))

	# print "=====FailedL====="
	# print FailedL
	# min_tol_tmp=[]
	# for item in min_tol_L:
	#	print item


	return MergeL, FailedL, bdf_log_L, sorted(min_tol_L)

def save_orig_file_if_not_new(old_bdf, bdf_file):
	# check if orig_file exists
	# print "Processing save_orig_file_if_not_new"
	if not os.path.isfile(old_bdf):
		# print "Processing 1"
		# orig_ file does not exist... create it
		old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
		os.rename(bdf_file, old_bdf)
	else:
		# print " Processing 2"
		# orig_file exists... check if it was modified (delta) seconds ago
		delta = 5
		print("orig file was last modified: %s" % time.ctime(os.path.getmtime(old_bdf)))
		print("modified file was last modified: %s" % time.ctime(os.path.getmtime(bdf_file)))

		if os.path.getmtime(bdf_file) - os.path.getmtime(old_bdf) < delta:
			# file was just created - do not create a new
			print("A recent " + old_bdf + " already exists... no new copy is created")
		else:
			os.remove(old_bdf)
			os.rename(bdf_file, old_bdf)

def run_nastran(bdf_file, SettingsD="", xtra_args=[]):
	

	go_flag = False
	try:
		print("Look for nastran executable in UtilityUserSettings.xml")
		nas_exe = SettingsD['System']['NastranPath']
		if not os.path.isfile(nas_exe):
			print("Path in UtilityUserSettings.xml is invalid")
		else:
			go_flag = True
	except:
		print("Could not locate nastran path setting in UtilityUserSettings.xml")
		


	if not go_flag:
		print("")
		# get Nastran path through settings file
		try:
			nas_exe = SettingsD['System']['NastranPath']
			if os.path.isfile(nas_exe):
				print(("Located nastran executable through settings file: " + nas_exe ))
				go_flag = True
			
		except:
			print("Failed to find a Nastran executable through Settings file")
			go_flag = False
		


		if not go_flag:
			print("Locating nastran exceutable  using registry path")
			#nas_exe=C:\MSC.Software\MSC_Nastran\20180\bin\nastranw.exe
			try:
				nas_exe=getPath(r'HKEY_CLASSES_ROOT\Applications\nastranw.exe\shell\open\command').replace('nastranw.exe','nastran.exe')
				if os.path.isfile(nas_exe):
					print(("Located nastran executable through registry: " + nas_exe ))
					go_flag = True
		
		
			except:
				print("Failed to obtain nastran through Registry")
				go_flag = False	
			

	if go_flag:		
		args = []
		args.append(nas_exe)
		args.append(bdf_file)
		# args.append("dbs=%s" % (os.path.dirname(bdf_file)))
		args.append("out=%s" % (os.path.dirname(bdf_file)))
		args.append("scr=yes")
		# add xtra_args
		for arg in xtra_args:
			args.append(arg)
		
		print("Nastran executable args:")
		print(args)
		# specify current working directory for the execution of nastran
		nastran_cwd = os.path.dirname(bdf_file)

		# modify the SCA resource dir env variable, that conflicts the nastran run
		my_env = os.environ
		my_env["SCA_RESOURCE_DIR"] = ""
		# SCA_RESOURCE_DIR=c:\msc.software\msc_nastran\20141\msc20141\res

		# p = subprocess.Popen(args,shell=True, stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,cwd=nastran_cwd)
		p = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
							 cwd=nastran_cwd, env=my_env)
		out, err = p.communicate()
		p.stderr.close()
		p.stdin.close()
	# print out
	# print "out: " + out
	# print "err: " + err

	else:
		out = "*** nastran execution failed"
	return out

def RunSesamConverter(SettingsD,in_bdf,temp_dir):

	out_fem_file=""
	try:
		sesamconverter_exe = SettingsD['SESAM_EXPORT']['SesamConverter']
	except:
		# Try locate nastran from standar location
		print("Path to sesam converter can not be found")
		
	if os.isfile(sesamconverter_exe):
		try:
			os.system("cd C:\\tmp\\OutTmp & " + sesamconverter_exe + "-n -i" + in_bdf + " -o " + os.path.dirname(in_bdf) + " --ngap 1 -egap ")
			out_fem_file=in_bdf[:-3] + "fem"
		except:
			print("There was a problem exceuting the sesam converter script")
	else:
		print("A valid sesam converter could not be found")
		
	return out_fem_file

def write_mnf_case_control_header(file_h, bdf_full_name):
	mnf_CASECONTROL = [
		'NASTRAN',
		'$ BEGIN FMS ',
		'$',
		'$ASSIGN MNF=' + os.path.basename(bdf_full_name[4:-4]) + '.mnf STATUS=NEW DELETE',
		'$ END OF FMS',
		'$',
		'SOL 103',
		'$ Direct Text Input for Executive Control',
		'CEND',
		'TITLE = MSC.Nastran job to generate mnf',
		'ECHO = NONE',
		'$ Direct Text Input for Global Case Control Data',
		'ADAMSMNF FLEXBODY=YES',
		'SUBCASE 1',
		'   SUBTITLE=Default',
		'   $SPC=1',
		'   METHOD = 1',
		'   DISPLACEMENT(PLOT) = ALL']
	for item in mnf_CASECONTROL:
		file_h.write(item + '\n')

def find_CONM2_w_zeromass(lines):
	#
	zero_mass_node_list = []
	non_zero_mass_node_list = []
	#
	print("processing find_CONM2_w_zeromass")
	for line in lines:
		if line[0:8].strip() == 'CONM2':
			if float(line[32:40]) == 0:
				zero_mass_node_list.append(int(line[16:24]))
			else:
				non_zero_mass_node_list.append(int(line[16:24]))
	return zero_mass_node_list, non_zero_mass_node_list

def write_ASET(aset_list, file_h):
	file_h.write('$ASET nodes identified by zero masses in exported bdf\n')
	for id in aset_list:
		file_h.write('%-8s%8d%8s\n' % ('ASET', id, "123456"))

def write_mnf_bulk_data(file_h):
	mnf_BULKDATA = [
		'$---------------',
		'EIGRL,1,,,20',
		'$',
		'SPOINT, 100001,thru,100020',
		'QSET1,0,100001,thru,100020',
		'$PARAM,AUTOQSET,YES',
		'$\n',
		'$PARAM,COUPMASS,1',
		'PARAM,GRDPNT,0',
		'DTI,UNITS,1,MGG,N,MM,S',
		'$',
		'$---------------------']
	for item in mnf_BULKDATA:
		file_h.write(item + '\n')

def write_bulk_data(file_h, lines):
	# exclude CONM2s with zero mass
	for line in lines:
		if line[0:8].strip() == 'CONM2' and float(line[32:40]) == 0:
			print("skip zero masses")
			print(line)
			print(float(line[32:40]))
		elif line[0:10] == "BEGIN BULK":
			print("skipping BEGIN BULK")
		else:
			file_h.write(line)

def find_aset_nodes(lines):
	#
	aset_node_list = []
	asetD = {}
	#
	print("Processing find aset nodes")
	n = 0
	while n < len(lines):
		line = lines[n]
		if line.find('ASET') == 0:
			aset_node = int(line[16:24])
			aset_node_list.append(aset_node)
			if lines[n - 1].find("$ Description:") == 0:
				descr = lines[n - 1][15:]
			else:
				descr = ""
			if lines[n - 2].find("$") == 0 and lines[n - 2].find("#") > -1:
				tmp_id = lines[n - 2].split("#")[1].strip().replace('"', '')
				try:
					aset_id = int(tmp_id)
					print(aset_id)
					print("found a new id for Aset Node: " + str(aset_node) + " -> " + str(tmp_id))
				except:
					aset_id = aset_node
			else:
				aset_id = aset_node
			# create dict with Aset node id as key and list with new id and description
			asetD[aset_node] = [aset_id, descr]
		n += 1
	return aset_node_list, asetD

def process_bdf_mnf(bdf_file, SettingsD):
	
	print(list(SettingsD.keys()))
	if 'ADAMS_MNF' in SettingsD:
		if 'MNFTemplate' in SettingsD['ADAMS_MNF']:
			if  os.path.isfile( os.path.dirname(__file__) + '\\' + SettingsD['ADAMS_MNF']['MNFTemplate']) :
				print("...relative path to the mnf template")
				# template_file=os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + "\\Documents\\MSC_Apex Workspace\\FossaUtils\\" + SettingsD['ADAMS_MNF']['MNFTemplate']
				template_file = os.path.dirname(__file__) + '\\' + SettingsD['ADAMS_MNF']['MNFTemplate']
			else:
				template_file = SettingsD['ADAMS_MNF']['MNFTemplate']
			try:
				execute_nastran = (SettingsD['ADAMS_MNF']['RunNastran'])
			except:
				execute_nastran = False

			try:
				rbe2_flag = (SettingsD['ADAMS_MNF']['Aset_RBE3_to_RBE2'])
			except:
				rbe2_flag = False

			try:
				reduce_rbe3_flag = (SettingsD['ADAMS_MNF']['ReduceRBE3Size'])
				if reduce_rbe3_flag == "True":
					reduce_rbe3_flag = True
				else:
					reduce_rbe3_flag = False
			except:
				reduce_rbe3_flag = False

			try:
				rbe3_reduce_ratio = float((SettingsD['ADAMS_MNF']['RBE3ReductionRatio']))
				if rbe3_reduce_ratio > 0 and rbe3_reduce_ratio <= 1:
					print("RBE3 reduction ratio is: " + ("%8.3f") % (rbe3_reduce_ratio))
				else:
					# invalid number, no reduction'
					print("***Warning*** The RBE3ReductionRatio nned to be a number between 0 and 1")
					print("No reduction of number of RBE3 legs (indep nodes)")
					reduce_rbe3_flag = False
					rbe3_reduce_ratio = 1
			except:
				rbe3_reduce_ratio = 1

			try:
				min_no_of_legs = int(SettingsD['ADAMS_MNF']['MinNoOfRBE3Legs'])
				if min_no_of_legs > 3:
					print("Minimum no of legs retained after RBE3 reduction is " + ("%8d") % (min_no_of_legs))
			except:
				print("***Warning*** Minimum number of legs reatained on reduced RBE3 could not be read or is missing")
				print("Keep at least 10 legs of RBE3")
				min_no_of_legs = 10

			try:
				rbe2_flag = (SettingsD['ADAMS_MNF']['Aset_RBE3_to_RBE2'])
			except:
				rbe2_flag = False

			try:
				no_of_modes = int(SettingsD['ADAMS_MNF']['NoOfModes'])
			except:
				no_of_modes = 10

			try:
				SpcId = int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId = 888888

			try:
				StripFlag = (SettingsD['ADAMS_MNF']['MNFStripInternal'])
			except:
				StripFlag = False

			try:
				CoarsenStr = 'coarsen(' + SettingsD['ADAMS_MNF']['MNFCoarsen'] + ')'
			except:
				CoarsenStr = ""

			# read existing bdf file
			print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()


			# ForceStr=ReadForces(bdf_lines,LoadId)
			
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			print("###SpcStr")
			print(SpcStr)
			ConctactStrL, BConprgD = ReadContacts(bdf_lines)

			DisplaySet = ReadDisplayElems(bdf_lines, "Display.bdf")

			aset_nodes, asetD = find_aset_nodes(bdf_lines)
			# Find all nodes to find max node id
			NodeD = ReadGrids(bdf_lines)
			# print "NodeD"
			# print NodeD
			max_id = max(NodeD.keys())
			print("max id: " + str(max_id))

			# get all RBE3s  attached to an aset node (with or without zero mass)
			AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)
			AsetRbe2D = ReadRbe2s(bdf_lines, aset_nodes)
			#
			# Enter only if reduce RBE3 is active
			if reduce_rbe3_flag == True:
				print("reducing Rbe3s")
				# reopen and read new
				# bdf_h=open(bdf_file,'r')
				# bdf_lines=bdf_h.readlines()
				# bdf_h.close()

				# now write back
				bdf_h = open(bdf_file, 'w')

				# dep id's are renumbered, use elem ids to find the RBEs to change AsetRbe3D[key][0]
				# print "###"
				# for key in AsetRbe3D.keys():
				#	print AsetRbe3D[key]
				# print "###"
				# if 1==1:
				#  nned a new dict form
				# AsetRbe3D=ReadRbe3s(bdf_lines,aset_nodes)
				# print "#New Aset RBE3D#"
				# for key in AsetRbe3D.keys():
				#	print AsetRbe3D[key]
				# print "###"

				n = 0
				while n < len(bdf_lines):
					if bdf_lines[n].find("RBE3 ") == 0:
						print("RBE3 type 1")
						# check so that RBE3 is one of the Aset RBE3s
						cont_card = True
						dep_id = int(bdf_lines[n][24:32])
						if dep_id in list(AsetRbe3D.keys()):
							print("Processing 1")
							lgth = len(AsetRbe3D[dep_id][1])
							if lgth > min_no_of_legs:
								if round(lgth * rbe3_reduce_ratio) > min_no_of_legs:
									skip_step = int(round(1. / rbe3_reduce_ratio))
									print("Rbe3 with id:" + str(
										AsetRbe3D[dep_id][0]) + " is larger than min length: " + str(
										lgth) + " vs " + str(min_no_of_legs))
								else:
									skip_step = int(math.floor(1. * lgth / min_no_of_legs))
									print("calculated new skip_step: " + str(skip_step))
									print("Rbe3 with id:" + str(AsetRbe3D[dep_id][
																	0]) + " needs new skip step as ratio time length is smaller than min_length: " + str(
										lgth) + " vs " + str(min_no_of_legs))

								indep_reduced = AsetRbe3D[dep_id][1][0::skip_step]

								# print out new RBE3
								# first line:
								i = 1
								j = 1
								bdf_h.write("%-56s" % (bdf_lines[n][0:56]))
								for indep in indep_reduced[:-2]:
									bdf_h.write("%-8s" % (indep))
									if (i == 1 and j == 2) or (i > 1 and j == 9):
										bdf_h.write("%-8s\n%-8s" % ("+", "+"))
										i += 1
										j = 1
									j += 1
								# write last item
								bdf_h.write("%-8s\n" % (indep_reduced[-1]))
								# check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
								cont_card = True
								while cont_card == True:
									if bdf_lines[n + 1][0:1] == "+" or bdf_lines[n + 1][0:1] == " " or bdf_lines[n + 1][
																									   0:1] == "*":
										bdf_h.write("$" + bdf_lines[n + 1])
										n += 1
									else:
										cont_card = False  # read next card
										n += 1

							else:
								print("Dep id: " + str(dep_id) + " Length: " + str(lgth) + " min no of legs " + str(
									min_no_of_legs))
								for key in list(AsetRbe3D.keys()):
									print(AsetRbe3D[key])

								print("too few legs in RBE3... no reduction")
								bdf_h.write(bdf_lines[n])
								n += 1
						else:
							print("This is an ordinary RBE3")
							bdf_h.write(bdf_lines[n])
							n += 1
					else:
						bdf_h.write(bdf_lines[n])
						n += 1

				bdf_h.close()

			# regenerate bdf_lines with updated RBE3s
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()
			
			
			# take out the Case Control Cards Added my Apex
			# if line.find('SOL 101') > -1 or line.find('CEND') > -1 or line.find('BCONTACT') > -1:
			# tmp_bdf_lines=[]
			# for line in bdf_lines:
				# if line.find('SOL 101') > -1 or line.find('CEND') > -1 or line.find('BCONTACT') > -1:
					# pass
				# else:
					# tmp_bdf_lines.append(line)
			
			# bdf_lines=tmp_bdf_lines
			
			
			try:
				bdf_lines.remove('SOL 101\n')
			except:
				pass
			try:
				bdf_lines.remove('SOL 103\n')
			except:
				pass

			try:
				bdf_lines.remove('CEND\n')
			except:
				pass
			try:
				bdf_lines.remove('BCONTACT = 1\n')
			except:
				pass
			try:
				bdf_lines.remove('BEGIN BULK\n')
			except:
				pass
			
			
			

			# # alos need new AsetRbe3D for PLOTELs
			# AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)

			# # read the mnf template lines
			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()

			# write back

			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			for line in template_lines:

				#  make sure mnf line and cont line is contained within 80 chars
				if line.find('ADAMSMNF') > -1 and len(DisplaySet) > 0:
					bdf_h.write(line.strip() + "PSETID=9099,\n")
				elif line.find('METHOD') > -1:
					# check if there is a contact table, then, write BCONTACT card just above subcase level
					if len(ConctactStrL) != 0:
						print("Generating ContactStrL")
						for lin in ConctactStrL:
							if lin[0:7] == 'BCTABL1':
								print(lin)
								BCTabLId = (int(lin[8:16]))
								bdf_h.write("%-8s = %d\n" % ("BCONTACT", BCTabLId))
					# end check BCTABLE
					bdf_h.write(line)
					# bdf_h.write("   LOAD=" + str(LoadId) + "\n")
					if len(SpcStr) != 0:
						bdf_h.write("   SPC=" + str(SpcId) + "\n")
					if len(ConctactStrL) == 1:
						bdf_h.write("   " + ConctactStrL[0])
					elif len(ConctactStrL) > 1:
						bdf_h.write("   " + ConctactStrL[0])
						bdf_h.write(
							"$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
				elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
					print("skip placeholder comment")

	

				elif line.find('BEGIN BULK') > -1:
					#removed in MergeGrids... put back
					#bdf_h.write("BCPARA,0,METHOD,SEGTOSEG\n")
				
					# check if there are plot-sets
					PlotelL = []
					if len(DisplaySet) != 0:
						# create PLOTELs for MPC legs
						elem_start = 8000001  # hardcoded
						n = 0
						for key in list(AsetRbe3D.keys()):
							for indep in AsetRbe3D[key][1]:
								# if asetD[indep][0] !=0 :
								#	indep=asetD[indep][0]
								PlotelL.append("%-8s%8d%8d%8d\n" % ("PLOTEL", elem_start + n, key, indep))
								n += 1
						for key in list(AsetRbe2D.keys()):
							for indep in AsetRbe2D[key][1]:
								# if asetD[indep][0] !=0 :
								#	indep=asetD[indep][0]
								PlotelL.append("%-8s%8d%8d%8d\n" % ("PLOTEL", elem_start + n, key, indep))
								n += 1

						# assume for now, that the output set is in a sequence
						bdf_h.write("OUTPUT(PLOT)\n")
						if len(DisplaySet) > 1:
							bdf_h.write("SET 9099= " + "%d%s%d" % (min(DisplaySet), " THRU ", max(DisplaySet)))
						else:
							bdf_h.write("SET 9099= " + "%d%s%d" % (min(DisplaySet)))
						# Add set for PLOTELs
						if len(PlotelL) != 0:
							bdf_h.write(",%d%s%d\n" % (elem_start, " THRU ", elem_start + (n - 1)))
						else:
							bdf_h.write("\n")

					bdf_h.write(line)
					# Obsolete - write Aset cards
					# write_ASET(aset_nodes,bdf_h)
					# write PLOTELs


					# write PARAMs
					bdf_h.write("%-8s%8s%8s\n" % ("PARAM", "AUTOQSET", "YES"))
					bdf_h.write("%-8s%8s%8d\n" % ("PARAM", "GRDPNT", 0))
					# added to mnf header file
					# bdf_h.write("DTI,UNITS,1,MGG,N,MM,S\n")
					# EIGRL to extract requested no of modes
					bdf_h.write("%-8s%8d%8s%8s%8d\n" % ("EIGRL", 1, "", "", no_of_modes))

					# bdf_h.write(ForceStr + "\n")
					bdf_h.write(SpcStr + "\n")

				# Obsolete- write all the replaced cards
				# bdf_h.write("$ Modified bdf file: RBE3's with zero masses  are replaced with RBE2s\n")
				# bdf_h.write("$===New RBE2 cards=======================================================\n")
				# for item in Aset_rbe2_cards:
				#	bdf_h.write(item)

				# New - write back reduced RBE3's


				else:
					bdf_h.write(line)

			replace_nodes = []

			MergeD = {}
			for aset_node in aset_nodes:
				MergeD[aset_node] = asetD[aset_node][0]
			# also need to replace any grids that are already taken

			for aset_node in aset_nodes:
				if asetD[aset_node][0] == aset_node:
					# no renumber for this
					print("Aset node with id: " + str(aset_node) + " doesn't need renumbering")
				# delete from MergeD change Aug 18th
				# del MergeD[aset_node]

				else:
					# check if new node is "taken"
					if asetD[aset_node][0] in list(NodeD.keys()):
						max_id += 1
						MergeD[asetD[aset_node][0]] = max_id
						replace_nodes.append(asetD[aset_node][0])

					# for aset_node in aset_nodes:
				replace_nodes.append(aset_node)

			# append PloteL to bdf_lines, handle renumbering in MergeGrids
			# check if last line is an ENDDATA
			if bdf_lines[-1].find('ENDDATA') == 0:
				del bdf_lines[-1]
				bdf_lines.extend(PlotelL)
				bdf_lines.append('ENDDATA\n')
			else:
				bdf_lines.extend(PlotelL)

			# print replace_nodes
			# print MergeD

			# merge grids
			# print "aaa"
			# print MergeD
			print("#####replace nodes####")
			print(replace_nodes)
			#
			print("####MergeD####")
			print(MergeD)
			
			###test####
			new_file = os.environ.get('TEMP') + '\\new_aaa.bdf'
			
			#new_file = r"C:\Temp\new_aaa.bdf"
			new_file_h = open(new_file, 'w')
			#
			#print "Woooahh"
			#print bdf_lines[:10]
			#
			for line in bdf_lines:
				#if line.find('SOL 101') > 0 or line.find('BCONTACT =') > 0 or line.find('CEND') > 0 or line.find('SOL 103') or line.find('BEGIN BULK'):
				#	pass
				#else:
				new_file_h.write(line)
					
			new_file_h.close()
			#
			MergeGrids(bdf_h, bdf_lines, replace_nodes, MergeD)

			bdf_h.close()

			# Enter only if reduce RBE3 is active
			# if reduce_rbe3_flag==True:
			# print "reducing Rbe3s"
			# # reopen and read new
			# bdf_h=open(bdf_file,'r')
			# bdf_lines=bdf_h.readlines()
			# bdf_h.close()

			# # now write back
			# bdf_h=open(bdf_file,'w')

			# # dep id's are renumbered, use elem ids to find the RBEs to change AsetRbe3D[key][0]
			# #print "###"
			# #for key in AsetRbe3D.keys():
			# #	print AsetRbe3D[key]
			# #print "###"
			# if 1==1:
			# #  nned a new dict form
			# new_aset_nodes=[]
			# for key in MergeD.keys():
			# new_aset_nodes.append(MergeD[key])

			# print new_aset_nodes

			# AsetRbe3D=ReadRbe3s(bdf_lines,new_aset_nodes)
			# #print "#New Aset RBE3D#"
			# #for key in AsetRbe3D.keys():
			# #	print AsetRbe3D[key]
			# #print "###"

			# n=0
			# while n < len(bdf_lines):
			# if bdf_lines[n].find("RBE3 ")==0 :
			# print "RBE3 type 1"
			# # check so that RBE3 is one of the Aset RBE3s
			# cont_card=True
			# dep_id=int(bdf_lines[n][24:32])
			# if dep_id in AsetRbe3D.keys():
			# print "Processing 1"
			# lgth=len(AsetRbe3D[dep_id][1])
			# if lgth > min_no_of_legs:
			# if round(lgth*rbe3_reduce_ratio) > min_no_of_legs:
			# skip_step=int(round(1./rbe3_reduce_ratio))
			# print "Rbe3 with id:" + str(AsetRbe3D[dep_id][0]) + " is larger than min length: "  + str(lgth) + " vs " + str(min_no_of_legs)
			# else:
			# skip_step=int(math.floor(1.*lgth/min_no_of_legs))
			# print "calculated new skip_step: " +str(skip_step)
			# print "Rbe3 with id:" + str(AsetRbe3D[dep_id][0]) + " needs new skip step as ratio time length is smaller than min_length: "  + str(lgth) + " vs " + str(min_no_of_legs)

			# indep_reduced=AsetRbe3D[dep_id][1][0::skip_step]

			# #print out new RBE3
			# #first line:
			# i=1
			# j=1
			# bdf_h.write("%-56s" % (bdf_lines[n][0:56]) )
			# for indep in indep_reduced[:-2]:
			# bdf_h.write("%-8s" % (indep) )
			# if (i==1 and j==2) or ( i>1 and j==9):
			# bdf_h.write("%-8s\n%-8s" % ("+","+") )
			# i+=1
			# j=1
			# j+=1
			# #write last item
			# bdf_h.write("%-8s\n" % (indep_reduced[-1]) )
			# # check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
			# cont_card=True
			# while cont_card==True:
			# if bdf_lines[n+1][0:1]=="+" or bdf_lines[n+1][0:1]==" " or bdf_lines[n+1][0:1]=="*":
			# bdf_h.write("$" + bdf_lines[n+1])
			# n+=1
			# else:
			# cont_card=False # read next card
			# n+=1

			# else:
			# print "Dep id: " +  str(dep_id) + " Length: " + str(lgth) + " min no of legs " + str(min_no_of_legs)
			# for key in AsetRbe3D.keys():
			# print AsetRbe3D[key]

			# print "too few legs in RBE3... no reduction"
			# bdf_h.write(bdf_lines[n])
			# n+=1
			# else:
			# print "This is an ordinary RBE3"
			# bdf_h.write(bdf_lines[n])
			# n+=1
			# else:
			# bdf_h.write(bdf_lines[n])
			# n+=1

			# bdf_h.close()

			# add PLOTELS


			print("sol103 nastran cards added")
		else:
			print("No MNFTemplate found")
	else:
		print("No Nastran Job Setup for MNF info in settings file")
	
	if execute_nastran == 'True':
		# apply the nastran options for mnf gerneatio
		xtra_args = []

		if StripFlag == "True":
			StripStr = "strip_face"
		else:
			StripStr = ""

		if CoarsenStr != "" and StripStr != "":
			mnf_str = 'mnfwriteoptions="' + CoarsenStr + ',' + StripStr + '"'
		elif CoarsenStr != "" and StripStr == "":
			mnf_str = 'mnfwriteoptions="' + CoarsenStr + '"'
		elif CoarsenStr == "" and StripStr != "":
			mnf_str = 'mnfwriteoptions="' + StripStr + '"'
		else:
			mnf_str = ""
		if mnf_str != "":
			xtra_args.append(mnf_str)

		print("...running nastran")
		# xtra_args=[]
		out_msg = run_nastran(bdf_file, SettingsD, xtra_args)
	# out_msg=run_nastran(bdf_file)
	else:
		out_msg = "**** mnf_file is created"
	#
	return out_msg

def MergeGrids(bdf_h, bdf_lines, replace_nodes, MergeD,comment_duplicates=False):
	print("Processing MergeGrids")
	n = 0
	while n < len(bdf_lines):
		if bdf_lines[n].find("RBE3 ") == 0:
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				# print bdf_lines[n+p]
				p += 1
			combined_line = "".join(bdf_lines[n:n + p])
			
			a = CheckAndReplaceGrids(combined_line, [3] + list(range(7, 10 * p)), replace_nodes, MergeD)
			bdf_h.write(a)
			n = n + p


		elif bdf_lines[n].find("ASET1") == 0:
			# print "Aset1"
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		# Do not replace the merged grids
		# elif bdf_lines[n].find("GRID") == 0:
		#	a=CheckAndReplaceGrids(bdf_lines[n],[1],replace_nodes,MergeD)
		#	bdf_h.write(a)
		#	n+=1

		elif bdf_lines[n].find("CTRIA3") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4, 5], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CQUAD4") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4, 5, 6], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CTRIA6") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4, 5, 6, 7, 8], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CQUAD8") == 0:
			combined_line = "".join(bdf_lines[n] + bdf_lines[n + 1])
			a = CheckAndReplaceGrids(combined_line, [3, 4, 5, 6, 7, 8, 10, 11], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 2

		elif bdf_lines[n].find("RBE2") == 0:
			# print "RBE2"
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				p += 1
			combined_line = "".join(bdf_lines[n:n + p])
			a = CheckAndReplaceGrids(combined_line, [2] + list(range(4, 10 * p)), replace_nodes, MergeD)
			bdf_h.write(a)
			n = n + p

		elif bdf_lines[n].find("CHEXA") == 0:
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				p += 1
			combined_line = "".join(bdf_lines[n:n + p])
			#print('_',combined_line,'_')
			a = CheckAndReplaceGrids(combined_line, list(range(3, 10 * p)), replace_nodes, MergeD)
			bdf_h.write(a)
			n = n + p

		elif bdf_lines[n].find("CPENTA") == 0:
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				p += 1
			combined_line = "".join(bdf_lines[n:n + p])
			a = CheckAndReplaceGrids(combined_line, list(range(3, 10 * p)), replace_nodes, MergeD)
			bdf_h.write(a)
			n = n + p

		elif bdf_lines[n].find("CTETRA") == 0:
			p = 1
			while bdf_lines[n + p].find(' ') == 0 or bdf_lines[n + p].find('*') == 0 or bdf_lines[n + p].find('+') == 0:
				p += 1
			combined_line = "".join(bdf_lines[n:n + p])
			a = CheckAndReplaceGrids(combined_line, list(range(3, 10 * p)), replace_nodes, MergeD)
			bdf_h.write(a)
			n = n + p

		elif bdf_lines[n].find("CBAR") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CBEAM") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CBUSH") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CBUSH1D") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [3, 4], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("RBAR") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2, 3], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("PLOTEL") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2, 3], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1


		elif bdf_lines[n].find("SPC") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("CONM2") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("FORCE") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("MOMENT") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("SPCD") == 0:
			a = CheckAndReplaceGrids(bdf_lines[n], [2], replace_nodes, MergeD)
			bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("GRID") == 0:
			if comment_duplicates == True and int(bdf_lines[n][8:16]) in replace_nodes:
				print(("comment node " + bdf_lines[n][8:16]))
				bdf_h.write("$" + bdf_lines[n][1:] )
			else:
				a = CheckAndReplaceGrids(bdf_lines[n], [1], replace_nodes, MergeD)
				bdf_h.write(a)
			n += 1

		elif bdf_lines[n].find("DTI     UNITS") == 0:
			# remove excessive DTI cards as this is added in mnf template
			bdf_h.write("$" + bdf_lines[n][1:])
			n += 1			
			
		#  Modified in Iberianl Lynx Utils
		#  Stefan Thynelius Feb 19th 2019
		# elif bdf_lines[n].find("BCPARA") > -1:
			# pass
			# n += 1
			
		# elif bdf_lines[n].find("MDLPRM,HDF5") > -1:
			# pass
			# n += 1	
	
		# elif bdf_lines[n].find("SOL 101") == 0:
			# n+=1

		# elif bdf_lines[n].find("BEGIN BULK") == 0:
			# n+=1
			
		# elif bdf_lines[n].find("BCONTACT =") == 0:
			# n+=1

		# elif bdf_lines[n].find("CEND") == 0:
			# n+=1	
		
		else:
			bdf_h.write(bdf_lines[n])
			n += 1
	return n

def process_bdf_inp(bdf_full_name, SettingsD):
	print("Processing inp v9a")
	out_inp = apex2aba_v9a.process_bdf_inp(bdf_full_name,SettingsD)

def process_rigidlink_2_CbarRbe2(bdf_file, SettingsD):
	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()

	NodeD = ReadGrids(inl1)
	RbarD = ReadRbars(inl1)

	# only convert RBE3's associated with RBARs in RBARD
	RbarNodeL = []
	for item in list(RbarD.keys()):
		RbarNodeL.append(RbarD[item][0])
		RbarNodeL.append(RbarD[item][1])

	# print RbarNodeL
	Rbe3D = ReadRbe3s(inl1, RbarNodeL)

	# only proceed if there are rigid links of type RBE3-RBAR-RBE3
	if Rbe3D:
		print("start converting rigid connections of type  RBE3-RBAR  to   RBE2-CBAR")
		# rename orig file unless it was modified seconds ago
		# meaning another process already stored the orig file

		old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
		save_orig_file_if_not_new(old_bdf, bdf_file)

		# write the file, except the cards to replace
		outf = open(bdf_file, 'w')
		# first write all the replaced cards
		outf.write("$ Modified bdf file: RBE3-RBAR-RBE3's are replaced with RBE2-CBAR-RBE2s\n")
		outf.write("$ MID 1001 (steel) is created assuming tonnes-mm-s is used\n")
		try:
			E = float(SettingsD['RBEX_CBAR']['E'])
			rho = float(SettingsD['RBEX_CBAR']['rho'])
			poi = float(SettingsD['RBEX_CBAR']['poisson'])
			MID = int(SettingsD['RBEX_CBAR']['MID'])
			CbarRbe2Flag = SettingsD['RBEX_CBAR']['Cbar_RBE3_to_RBE2']
		except:
			E = 205000
			rho = 7.85e-9
			poi = 0.29
			MID = 10001
			CbarRbe2Flag = "True"
		#print("#########################")
		# generate new cards
		outf.write("%-8s%8d%8.1f%8s%8.3f%8.2e\n" % ("MAT1", MID, E, "", poi, rho))
		# outf.write("MAT1        1001 210000.            0.33 7.85E-9\n")

		(cbar_cards, omit_list) = Rbars_2_Cbars(RbarD, NodeD, Rbe3D, MID)

		if CbarRbe2Flag == "True":
			print("Converting RBE3s to RBE2s")
			rbe2_cards = RBE3s_to_RBE2s(Rbe3D)
			#
			outf.write("$===New RBE2 cards=======================================================\n")
			for item in rbe2_cards:
				outf.write(item)
		else:
			outf.write("$===No RBE3 to RBE2 conversion chosen====================================\n")

		outf.write("$===New CBAR cards=======================================================\n")
		for item in cbar_cards:
			outf.write(item)
		outf.write("$===End added cards======================================================\n")
		n = 0
		while n < len(inl1):
			if inl1[n][0:5] == "RBE3 " and CbarRbe2Flag == "True":
				# comment all RBE3's flagged in Rbe3D (checking for dependent nodes)
				# preceed the RBE3s to replace  with comments if CbarRbe2Flag=="True"
				cont_card = True
				dep_id = int(inl1[n][24:32])
				if dep_id in list(Rbe3D.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				# check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
				while cont_card == True:
					if inl1[n + 1][0:1] == "+" or inl1[n + 1][0:1] == " " or inl1[n + 1][0:1] == "*":
						outf.write(pre_str + inl1[n + 1])
						n += 1
					else:
						cont_card = False  # read next card
						n += 1


			elif inl1[n][0:5] == "RBAR ":
				# comment all RBAR's flagged in RbarD (checking for element id)
				# preceed the RBE3s to replace  with comments
				id = int(inl1[n][8:16])
				if id in list(RbarD.keys()) and id not in omit_list:
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1
			else:
				outf.write(inl1[n])
				n += 1
			#
		outf.close()
	else:
		print("no rigid links of type  RBE3-RBAR  in file")

def process_rigidlink_2_CbushRbe2(bdf_file, SettingsD):
	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()


	NodeD = ReadGrids(inl1)
	RbarD = ReadRbars(inl1)

	try:
		CbushRbe2Flag = SettingsD['RBEX_CBUSH']['Cbush_RBE3_to_RBE2']
	except:
		CbushRbe2Flag = "True"

	# only convert RBE3's associated with RBARs in RBARD
	RbarNodeL = []
	for item in list(RbarD.keys()):
		RbarNodeL.append(RbarD[item][0])
		RbarNodeL.append(RbarD[item][1])
	print("whats in RbarNodeL")
	for item in RbarNodeL:
		print(RbarNodeL)

	Rbe3D = ReadRbe3s(inl1, RbarNodeL)
	# the function RBE3s_to_RBE2s replaces RBE3s with RBE2s

	# proceed only if there are rigid links of type RBE3-RBAR-RBE3
	if Rbe3D:
		print("What is in RBE3D")
		for item in list(Rbe3D.keys()):
			print(Rbe3D[item])

		if CbushRbe2Flag == "True":
			print("start converting rigid connections of type  RBE3-RBAR to RBE2-CBUSH")
			rbe2_cards = RBE3s_to_RBE2s(Rbe3D)
		# the function Rbars_2_Cbush gernerates CBUSH, PBUSH, CORD1R and Grid cards
		(grid_cards, cbush_cards, cord2r_cards, pbush_cards, GridReplaceL) = Rbars_2_Cbush(RbarD, NodeD, Rbe3D,
																						   SettingsD)

		#print("#########################")
		# rename orig file, delete if already exists
		old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
		save_orig_file_if_not_new(old_bdf, bdf_file)

		# write the file, except the cards to replace
		outf = open(bdf_file, 'w')
		# first write all the replaced cards
		outf.write("$ Modified bdf file: RBE3-CBUSH1D-RBE3's are replaced with RBE2-CBUSH-RBE2s\n")
		if CbushRbe2Flag == "True":
			outf.write("$===New RBE2 cards=======================================================\n")
			for item in rbe2_cards:
				outf.write(item)
		else:
			outf.write("$===No RBE3 to RBE2 conversion===========================================\n")
		#
		outf.write("$===Modified Grids cards (if zero length bushes are requested) ============\n")
		for item in grid_cards:
			outf.write(item)

		outf.write("$===New CORD2R cards=======================================================\n")
		for item in cord2r_cards:
			outf.write(item)

		outf.write("$===New CBUSH cards=======================================================\n")
		for item in cbush_cards:
			outf.write(item)

		outf.write("$===New PBUSH cards=======================================================\n")
		for item in pbush_cards:
			outf.write(item)

		outf.write("$===End added cards======================================================\n")
		n = 0
		while n < len(inl1):
			if inl1[n][0:5] == "RBE3 " and CbushRbe2Flag == "True":
				# comment all RBE3's flagged in Rbe3D (checking for dependent nodes)
				# preceed the RBE3s to replace  with comments
				cont_card = True
				dep_id = int(inl1[n][24:32])
				if dep_id in list(Rbe3D.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				# check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
				while cont_card == True:
					if inl1[n + 1][0:1] == "+" or inl1[n + 1][0:1] == " " or inl1[n + 1][0:1] == "*":
						outf.write(pre_str + inl1[n + 1])
						n += 1
					else:
						cont_card = False  # read next card
						n += 1


			elif inl1[n][0:5] == "RBAR ":
				# comment all RBAR's flagged in RbarD (checking for element id)
				# preceed the RBE3s to replace  with comments
				id = int(inl1[n][8:16])
				if id in list(RbarD.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			elif inl1[n][0:5] == "GRID ":
				id = int(inl1[n][8:16])
				if id in GridReplaceL:
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			else:
				outf.write(inl1[n])
				n += 1
			#
		outf.close()
	# os.utime(old_bdf,None)
	else:
		print("no rigid links of type  RBE3-RBAR  in file")

def process_spring_2_CbushRbe2(bdf_file, SettingsD):
	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()

	NodeD = ReadGrids(inl1)
	Cbush1D = ReadCbush1D(inl1)

	try:
		CbushRbe2Flag = SettingsD['RBEX_CBUSH']['Cbush_RBE3_to_RBE2']
	except:
		CbushRbe2Flag = "True"

	# only convert RBE3's associated with RBARs in Cbush1D
	Cbush1NodeL = []
	for item in list(Cbush1D.keys()):
		Cbush1NodeL.append(Cbush1D[item][0])
		Cbush1NodeL.append(Cbush1D[item][1])
	#print("whats in Cbush1NodeL")
	#for item in Cbush1NodeL:
	#	print(Cbush1NodeL)

	Rbe3D = ReadRbe3s(inl1, Cbush1NodeL)
	# the function RBE3s_to_RBE2s replaces RBE3s with RBE2s

	# proceed only if there are rigid links of type RBE3-RBAR-RBE3
	if Rbe3D:
		#print("What is in RBE3D")
		#for item in list(Rbe3D.keys()):
		#	print(Rbe3D[item])

		if CbushRbe2Flag == "True":
			print("start converting rigid connections of type  RBE3-RBAR  to   RBE2-CBUSH")
			rbe2_cards = RBE3s_to_RBE2s(Rbe3D)

		# the function Rbars_2_Cbush gernerates CBUSH, PBUSH, CORD1R and Grid cards
		(grid_cards, cbush_cards, cord2r_cards, pbush_cards, GridReplaceL) = Cbush1d_2_Cbush(Cbush1D, NodeD, Rbe3D,
																							 SettingsD)

		#print("#########################")

		# rename orig file, delete if already exists
		old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
		save_orig_file_if_not_new(old_bdf, bdf_file)

		# write the file, except the cards to replace
		outf = open(bdf_file, 'w')
		# first write all the replaced cards
		outf.write("$ Modified bdf file: RBE3-RBAR-RBE3's are replaced with RBE2-CBUSH-RBE2s\n")

		if CbushRbe2Flag == "True":
			outf.write("$===New RBE2 cards=======================================================\n")
			for item in rbe2_cards:
				outf.write(item)
		#
		outf.write("$===Modified Grids cards (if zero length bushes are requested) ============\n")
		for item in grid_cards:
			outf.write(item)

		outf.write("$===New CORD2R cards=======================================================\n")
		for item in cord2r_cards:
			outf.write(item)

		outf.write("$===New CBUSH cards=======================================================\n")
		for item in cbush_cards:
			outf.write(item)

		outf.write("$===New PBUSH cards=======================================================\n")
		for item in pbush_cards:
			outf.write(item)

		outf.write("$===End added cards======================================================\n")
		n = 0
		while n < len(inl1):
			if inl1[n][0:5] == "RBE3 " and CbushRbe2Flag == "True":
				# comment all RBE3's flagged in Rbe3D (checking for dependent nodes)
				# preceed the RBE3s to replace  with comments
				cont_card = True
				dep_id = int(inl1[n][24:32])
				if dep_id in list(Rbe3D.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				# check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
				while cont_card == True:
					if inl1[n + 1][0:1] == "+" or inl1[n + 1][0:1] == " " or inl1[n + 1][0:1] == "*":
						outf.write(pre_str + inl1[n + 1])
						n += 1
					else:
						cont_card = False  # read next card
						n += 1


			elif inl1[n][0:8] == "CBUSH1D ":
				# comment all CBUSH's flagged in Cbush1D (checking for element id)
				# preceed the RBE3s to replace  with comments
				id = int(inl1[n][8:16])
				if id in list(Cbush1D.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			elif inl1[n][0:8] == "PBUSH1D ":
				# comment all PBUSH1D's occurring in Cbush1D
				# preceed the PBUSH1D to replace  with comments
				id = int(inl1[n][8:16])
				for key in list(Cbush1D.keys()):
					if id == Cbush1D[key][2]:
						pre_str = "$"
					else:
						pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1
			elif inl1[n][0:5] == "GRID ":
				id = int(inl1[n][8:16])
				if id in GridReplaceL:
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			else:
				outf.write(inl1[n])
				n += 1
			#
		outf.close()
	# os.utime(old_bdf,None)
	else:
		print("no rigid links of type  RBE3-RBAR  in file")

def process_Cbush_2_ZeroLength(bdf_file, SettingsD):
	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()

	NodeD = ReadGrids(inl1)
	# Cbush1D=ReadCbush1D(inl1)
	CbushD = ReadCbushD(inl1)
	# try:
	#	CbushRbe2Flag=SettingsD['RBEX_CBUSH']['Cbush_RBE3_to_RBE2']
	# except:
	#	CbushRbe2Flag="True"

	# only convert RBE3's associated with RBARs in Cbush1D
	CbushNodeL = []
	for item in list(CbushD.keys()):
		CbushNodeL.append(CbushD[item][0])
		CbushNodeL.append(CbushD[item][1])
	# print "Whats in Cbush1NodeL..."
	# for item in CbushNodeL:
	#	print item
	# print "==="
	RbeXD = ReadRbe3s(inl1, CbushNodeL)
	# print RbeXD
	_Rbe2D = ReadRbe2s(inl1, CbushNodeL)
	# print Rbe2D
	RbeXD.update(_Rbe2D)
	# the function RBE3s_to_RBE2s replaces RBE3s with RBE2s

	# proceed only if there are rigid links of type RBE3-RBAR-RBE3
	if RbeXD:
		#print("What is in RBEXD")
		#for item in RbeXD.keys():
		#	print(RbeXD[item])

		# if 	CbushRbe2Flag=="True":
		#	print "start converting rigid connections of type  RBE3-RBAR  to   RBE2-CBUSH"
		#	rbe2_cards=RBE3s_to_RBE2s(RbeXD)

		# the function Rbars_2_Cbush generates CBUSH, PBUSH, CORD1R and Grid cards
		(grid_cards, cbush_cards, cord2r_cards, GridReplaceL) = ZeroLengthCbush(CbushD, NodeD, RbeXD, SettingsD)

		#print("#########################")

		# rename orig file, delete if already exists
		old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
		save_orig_file_if_not_new(old_bdf, bdf_file)

		# write the file, except the cards to replace
		outf = open(bdf_file, 'w')
		# first write all the replaced cards
		# add these directly under BEGIN BULK
		# 
		n=0
		while n < len(inl1):
			if inl1[n].find('BEGIN BULK') == 0:
				outf.write(inl1[n])
				n+=1
				break
			else:
				outf.write(inl1[n])
				n+=1
					
		# if CbushRbe2Flag=="True":
		#	outf.write("$===New RBE2 cards=======================================================\n")
		#	for item in rbe2_cards:
		#		outf.write(item)
		#
		outf.write("$===Modified Grids cards (if zero length bushes are requested) ============\n")
		for item in grid_cards:
			outf.write(item)

		outf.write("$===New CORD2R cards=======================================================\n")
		for item in cord2r_cards:
			outf.write(item)

		outf.write("$===New CBUSH cards=======================================================\n")
		for item in cbush_cards:
			outf.write(item)

		# outf.write("$===New PBUSH cards=======================================================\n")
		# for item in pbush_cards:
		#	outf.write(item)

		outf.write("$===End added cards======================================================\n")
		# continue from adding lines after Begin Bulk
		#n = 0
		while n < len(inl1):
			# if inl1[n][0:5]=="RBE3 " and CbushRbe2Flag=="True" :
			# # comment all RBE3's flagged in RbeXD (checking for dependent nodes)
			# # preceed the RBE3s to replace  with comments
			# cont_card=True
			# dep_id=int(inl1[n][24:32])
			# if dep_id in RbeXD.keys():
			# pre_str="$"
			# else:
			# pre_str=""
			# outf.write(pre_str + inl1[n])
			# # check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
			# while cont_card==True:
			# if inl1[n+1][0:1]=="+" or inl1[n+1][0:1]==" " or inl1[n+1][0:1]=="*":
			# outf.write(pre_str + inl1[n+1])
			# n+=1
			# else:
			# cont_card=False # read next card
			# n+=1


			if inl1[n][0:6] == "CBUSH ":
				#	# comment all CBUSH's flagged in Cbush1D (checking for element id)
				#	# preceed the CBUSH1D's to replace  with comments
				id = int(inl1[n][8:16])
				if id in list(CbushD.keys()):
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			# elif inl1[n][0:8]=="PBUSH1D ":
			# comment all PBUSH1D's occurring in Cbush1D
			# preceed the PBUSH1D to replace  with comments
			# id=int(inl1[n][8:16])
			# for key in Cbush1D.keys():
			# if id == Cbush1D[key][2]:
			# pre_str="$"
			# else:
			# pre_str=""
			# outf.write(pre_str + inl1[n])
			# n+=1
			elif inl1[n][0:5] == "GRID ":
				id = int(inl1[n][8:16])
				if id in GridReplaceL:
					pre_str = "$"
				else:
					pre_str = ""
				outf.write(pre_str + inl1[n])
				n += 1

			else:
				outf.write(inl1[n])
				n += 1
			#
		outf.close()
	# os.utime(old_bdf,None)
	else:
		print("no conversion of bushes")

def process_sol101_template(bdf_file, SettingsD, OptionsD={}):

	# read any tags on the glue pairs and cascade to glue patches
	# workaround to limitation preventing user from seeing changes in patch name:
	# change on glue pair and cascade name to patch, patch names are exported to bdf
	# no_of_pairs=UpdateGluePatchesFromGluePairName()
	# print "There are " + int(no_of_pairs) + " node pairs with user modified tags"
	
	
	# Split BCONECT cards in case the new format is used
	BConprg_initial_D={}
	BConect_relations_D={}
	bdf_h = open(bdf_file, 'r')
	lines = bdf_h.readlines()
	bdf_h.close()
	
	# split BCONECT cards in case the "MASTER" or "SLAVE" optoin is used
	split_BCONECT_cards(bdf_file)
	
	print(list(SettingsD.keys()))
	
	
	if 'NASTRAN_JOB_SETUP' in SettingsD:
		if 'SOL101Template' in SettingsD['NASTRAN_JOB_SETUP']:
			#if os.path.dirname(SettingsD['NASTRAN_JOB_SETUP']['SOL101Template']) == "":
			if os.path.isfile( os.path.dirname(__file__) + '\\' + SettingsD['NASTRAN_JOB_SETUP']['SOL101Template'] ):
				print("...relative  path to the nastran template")
				# template_file=os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + "\\Documents\\MSC_Apex Workspace\\FossaUtils\\" + SettingsD['NASTRAN_JOB_SETUP']['SOL101Template']
				template_file = os.path.dirname(__file__) + '\\' + SettingsD['NASTRAN_JOB_SETUP']['SOL101Template']
			else:
				template_file = SettingsD['NASTRAN_JOB_SETUP']['SOL101Template']
			try:
				LoadId = int(SettingsD['NASTRAN_JOB_SETUP']['LoadId'])
			except:
				LoadId = 999999

			try:
				SpcId = int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId = 888888

			# read existing bdf file
			print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			ForceStr = ReadForces(bdf_lines, LoadId)
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			ConctactStrL, BConprgD = ReadContacts(bdf_lines)
			
			StudyFlag=False
			# check if a study is being exported, or a default subcase
			for line in bdf_lines:
				if line.find('Linear Static Scenario') > -1:
					StudyFlag=True
					print("Linear Static study detected")
					break

			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()
			else:
				template_lines=["$ template file " + template_file  + " is empty or non existing\n"]

			# find out if contact flag is set SOL101DefaultContactType=Touch
			try:
				DefaultContactType = SettingsD['NASTRAN_JOB_SETUP']['SOL101DefaultContactType']
				if DefaultContactType == "Touch" or DefaultContactType == "Touching" or DefaultContactType == "T":
					DefaultContactType = "Touch"
				else:
					DefaultContactType = "Glue"
			except:
				print(" except")
				DefaultContactType = "Glue"
				

			try:
				maxiter = int(SettingsD['NASTRAN_JOB_SETUP']['MaxNoOfIterations'])
			except:
				maxiter = 25
			# NLPARM MAXDIV	
			try:
				maxdiv = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumDiversions'])
			except:
				maxdiv = 3		
			# NLPARM MAXLS	
			try:
				maxls = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumLineSearces'])
			except:
				maxls = 4

			# NLPARM MAXBIS	
			try:
				maxbis = int(SettingsD['NASTRAN_JOB_SETUP']['MaximumBisections'])
				maxbis_str="%-8d" % (maxbis)
			except:
				maxbis_str = ""	
				

			# Convergence criteria	
			try:
				CONV = SettingsD['NASTRAN_JOB_SETUP']['SOL101CONV']
			except:
				CONV = ""	
				
			try:
				EPSU = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101EPSU'])
				EPSU="%8.3f" % (EPSU)
			except:
				EPSU = ""
				
			try:
				EPSP = float(SettingsD['NASTRAN_JOB_SETUP']['SOL101EPSP'])
				EPSP="%8.3f" % (EPSP)
			except:
				EPSP = ""
				
			try:
				NoOfIncrements=int(SettingsD['NASTRAN_JOB_SETUP']['SOL101NoOfIncrements'])
			except:
				NoOfIncrements=10
						
			try:
				Debug=SettingsD['NASTRAN_JOB_SETUP']['Debug']
				if Debug.upper()=="TRUE":
					Debug=True
				else:
					Debug=False
			except:
				Debug=False
				
			try:
				EditContactTable=SettingsD['NASTRAN_JOB_SETUP']['EditContactTable']
				if EditContactTable.upper()=="TRUE":
					EditContactTable=True
					#print("I am in here...yeah")
				else:
					EditContactTable=False
					print("I am in here...why")
			except:
				print("something is wrong")
				EditContactTable=False	
				
			
			try:
				CsvSeparator=SettingsD['System']['CsvDelimiter']
				if CsvSeparator=="," or CsvSeparator==";":
					pass
				else:
					CsvSeparator=","
					print("Using ',' as csv separator")
			except:
				print("something is wrong, using ',' as csv separator ")
				CsvSeparator=","
				
			try:
				AutoSpcEps=float(SettingsD['NASTRAN_JOB_SETUP']['AUTOSPCEPS'])
				if AutoSpcEps > 0 and AutoSpcEps < 0.1:
					pass
				else:
					AutoSpcEps=1e-6
					print("AutoSpc EPS is set t0 1e-6 as input proviede from user is unrealistic")
			except:
				print("No AutoSpc Eps given, use values in template file")
				AutoSpcEps=""
	
			
				

			# write back
			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			
			template_bulk_lines=[]
			BeginBulkFlag=False
			AutoSpcFlag=False
			SubCaseFlag=False
			for line in template_lines:
				if line.find('SUBCASE') > -1:
					SubCaseFlag=True
					if AutoSpcFlag==False:
						if AutoSpcEps=="":
							AutoSpcEps=1.0E-6						
						bdf_h.write("AUTOSPC (NOPRINT, EPS=" + "%8.3e" % (AutoSpcEps) + ")=YES\n")
					if Debug==True:
						bdf_h.write("NLOPRM NLDBG=ADVDBG\n")
					#
					bdf_h.write("NLPARM=" + str(1) + "\n")
#
					if StudyFlag== False:
						bdf_h.write(line)	
						bdf_h.write("   LOAD=" + str(LoadId) + "\n")
						bdf_h.write("   SPC=" + str(SpcId) + "\n")
						if len(ConctactStrL) == 1:
							bdf_h.write(ConctactStrL[0])
						elif len(ConctactStrL) > 1:
							bdf_h.write(ConctactStrL[0])
							bdf_h.write("$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
				elif line.find('STRESS') > -1 or line.find('DISP') > -1 or 	line.find('ESE') > -1:
					if StudyFlag== False:
						bdf_h.write(line)
					else:
						pass
						
				elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
					print("skip placeholder comment")
					
				elif line.find('AUTOSPC') > -1:
					AutoSpcFlag=True
					print("Found Auto SPC replace with data from Settings")
					if AutoSpcEps ==  "":
						bdf_h.write(line)
					else:
						bdf_h.write("AUTOSPC (NOPRINT, EPS=" + "%8.3e" % (AutoSpcEps) + ")=YES\n")
						
					

				elif line.find('BEGIN BULK') > -1:					
					BeginBulkFlag=True
					#pass
					if 	StudyFlag == False:
						bdf_h.write(line)
						# Write NLPARM for controlling Non linear steppinf for SOL101
						bdf_h.write("%-8s%-8d%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s\n%-8s%-8s%-8s%-8s%-8d%-8s%-8d%-16s" % ("NLPARM",1,NoOfIncrements,"","","",maxiter,CONV,"","+","+",EPSU,EPSP,"",maxdiv,"",maxls,""))
						if maxbis_str != "" :
							bdf_h.write("%-8s\n%-8s%-8s" % ("+","+",maxbis_str) )
						bdf_h.write("\n")
						bdf_h.write(ForceStr + "\n")
						bdf_h.write(SpcStr + "\n")
					
					else:
						template_bulk_lines.append(line)
						template_bulk_lines.append("%-8s%-8d%-8d%-8s%-8s%-8s%-8d%-8s%-8s%-8s\n%-8s%-8s%-8s%-8s%-8d%-8s%-8d%-16s" % ("NLPARM",1,NoOfIncrements,"","","",maxiter,CONV,"","+","+",EPSU,EPSP,"",maxdiv,"",maxls,""))
						if maxbis_str != "" :
							template_bulk_lines.append("%-8s\n%-8s%-8s" % ("+","+",maxbis_str) )
						template_bulk_lines.append("\n")
					if Debug==True:
						# enforce creation of marct t19 file with output for each iteration
						if StudyFlag == False:
							bdf_h.write("$\n")
							bdf_h.write("$Write Marc t19 results for each inc. CDBMSG05=-3  outputs each iteration\n")
							bdf_h.write("%-8s%-8s%8d\n" % ("PARAM","CDBMSG05",-3) )
						else:
							template_bulk_lines.append("$\n")
							template_bulk_lines.append("$Write Marc t19 results for each inc. CDBMSG05=-3  outputs each iteration\n")
							template_bulk_lines.append("%-8s%-8s%8d\n" % ("PARAM","CDBMSG05",-3) )
	
				
				else:
					if BeginBulkFlag==True:
						template_bulk_lines.append(line)
					else:
						bdf_h.write(line)
			
			Bcon_strD=write_sol101_contact_stuff(bdf_h, bdf_lines,template_bulk_lines,SettingsD,StudyFlag,BConprgD)

			bdf_h.close()
			

			print("sol101 nastran cards added")
		else:
			print("No SOL101Template found")
	else:
		print("No Nastran Job Setup info is settings file")
		
		
	# for update of properties PSLDN1, we need a map between material and solids on before handle
	# loop once, just looking for PSOLIDs, use MAT id as key for dict 
	PShellD={}
	for line in bdf_lines:
		if line.find('PSHELL') == 0:
			mat_id=int(line[16:24])
			PShellD[mat_id]=line


	
	# Run ReadBsurfs to get the csv file stuff
	print("Entering ReadBSurfs")
	#
	# No preload in sol101
	BoltPreLoadFlag=False	
	BSurfD,CElemD,BoltBSurfL,BoltBConectL,BCBodyTableD,BConectD,ContactTableStr,BoltPreloadD,BCbodyL,BCbodyD,csv_file = ReadBsurfs(SettingsD, bdf_file, DefaultContactType,"0N","",EditContactTable,Bcon_strD,CsvSeparator)
	
	# update bdf with changes as per contact table edits
	if csv_file !="":
		ct_tableD, contact_bodies = read_contacttable_csv(csv_file,SettingsD)
		
		bdf_file_h=open(bdf_file,'r')
		bdf_lines=bdf_file_h.readlines()
		bdf_file_h.close()
	
		bdf_file_h=open(bdf_file,'w')
		Bcon_strD=write_sol101_contact_stuff(bdf_file_h, bdf_lines, [], SettingsD, False, {}, ct_tableD)
		bdf_file_h.close()
	# add any temperature dep materials (added Feb19th 2020)
	
	# persistent materials
	MaterialD=readMaterialsFromFile(SettingsD,TmpMat=True)
	
	print("=A=")
	print(MaterialD)
	print("=A=")
	# tmp materials
	#TmpMaterialD=readMaterialsFromFile(SettingsD,TmpMat=True)
	
	bdf_file_h=open(bdf_file,'r')
	bdf_lines=bdf_file_h.readlines()
	bdf_file_h.close()
	
	bdf_h=open(bdf_file,'w')
	
	written_psolidL=[]
	written_pshellL=[]

	n=0
	while n < len(bdf_lines):
		line=bdf_lines[n]
		if line.find('MAT1') == 0:
			# Check if any of comment lines above contains Material Name->
			m=1
			while bdf_lines[n-m].find("$") == 0:
				if bdf_lines[n-m].find("$ Material Record:") == 0:
					material_name=bdf_lines[n-m].split(':')[1].strip()
				m+=1
				
			mat_id=int(line[8:16])
			E_orig=myfloat(line[16:24])
			poi_orig=float(line[32:40])
			# adjust for using compact float 7.8-12
			tmp_str=line[40:48].strip()
			if tmp_str=="":
				rho_orig=0.0
			elif tmp_str[1:].find('E') < 0 and tmp_str[1:].find('e') <0:
				rho_orig=myfloat(tmp_str[0] + tmp_str[1:].replace('-','e-') )
			else:
				rho_orig=myfloat(tmp_str)
			#rho_orig=float(line[40:48])
			print("Found material: " + material_name + " in bdf file")

			#If non supported material exists in material folder, replace these	
			print("===" + material_name + "===")
			for mat in MaterialD:
				print(mat)
			print("===")
			
			write_orig_mat_Flag=False
			if material_name in MaterialD:
				print("Found material Name " + material_name)
				
				
				tmp_matL=MaterialD[material_name]
				E_file=myfloat(tmp_matL[0][16:24])
				poi_file=float(tmp_matL[0][32:40])
				rho_file=myfloat(tmp_matL[0][40:48])
				# added Feb19th 2020 STATIC
				try:
					alpha=tmp_matL[0][48:56].strip()
				except:
					alpha=""
				try:
					tref=tmp_matL[0][56:64].strip()
				except:
					tref=""
				try:
					ge=tmp_matL[0][64:72].strip()
				except:
					ge=""	
				
				# replace material id from MatD with actual MatId
				# 1 Check so that youngs modulus rho and poi has not changed
				if abs(E_orig -E_file)/E_orig > 0.01:
					print(("Warning Youngs modulus for material " + material_name + " in database and material dB file differs more than 1% "))
				if rho_orig > 1e-12:
					if abs(rho_orig -rho_file)/rho_orig > 0.01:
						print(("Warning density for material " + material_name + " in database and material dB file differs more than 1% "))
				else:
					print("Density is close to zero, no relative check of density")
				if abs(poi_orig - poi_file)/poi_orig > 0.01:
					print(("Warning poissons ratio for material " + material_name + " in database and material dB file differs more than 1% "))
				print("===")
				for s in tmp_matL[1:]:
					print(s)
				print("===")
				print(tmp_matL)
				#new_line="%-48s" % (line.strip()) + "%8s%8s\n" % (alpha,tref)
				#bdf_h.write(new_line)
				
				for mat_line in tmp_matL[1:]:
					if mat_line.find('MATEP') == 0  or mat_line.find('MATS1') == 0 or mat_line.find('MATHE') == 0:
						pass
						# if mat_line.find('MATHE') != 0:
							# # write back original material with alpha and tref  and damping
							# new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
							# bdf_h.write(new_line)
							# #bdf_h.write(line)
						# bdf_h.write(mat_line[:8] + "%-8d" % (mat_id) + mat_line[16:])
						# write_orig_mat_Flag=True
					elif mat_line.find('PSLDN1') == 0:
						pass
						# # add PSLDN1 card referencing the mat id
						# #first write the PSOLID, then PSLDN1
						# try:
							# bdf_h.write(SolidD[mat_id])
							# bdf_h.write(mat_line[:8] + "%-8d" % (int(SolidD[mat_id][8:16])) + mat_line[16:])
							# written_psolidL.append(int(SolidD[mat_id][8:16]))
						# except:
							# print("There seem to be no solids referencing this material... write material as exported")
							# bdf_h.write(line)
					elif mat_line.find('MAT2') == 0:
						# write all the MAT2's 
						# no support for alpha at this point
						# assuming MID2 and MID4 on PSHELL card is not clashing with existing PIDs-> fix later
						bdf_h.write(mat_line)
						#bdf_h.write(mat_line[:8] + "%-8d" % (mat_id) + mat_line[16:])
						
					elif mat_line.find('PSHELL') == 0:
						
						# write the new PSHELL... assume MID1 matches mat_id, make sure the PSHELL is updated
						# to match the original PSHELL
						# furthermore we are assuming that each MAT2 mid1 corresponds to exactly on PSHELL card
						try:
							bdf_h.write(mat_line[:8] + "%-8d" % (int(PShellD[mat_id][8:16])) + mat_line[16:])
							written_pshellL.append(int(PShellD[mat_id][8:16]))
						except:
							print("There seem to be no pshells referencing this material... write PSHELL as exported")
							bdf_h.write(line)

					else:
						#new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
						#bdf_h.write(new_line)
						bdf_h.write(mat_line)								
				if write_orig_mat_Flag==True:
					#write orig mat, this section is entered when there is just a standard linear material possibly with alpha
					new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
					bdf_h.write(new_line)
					#bdf_h.write(line)
			else:
				print()
				bdf_h.write(line)
				print("Material Name " + material_name + " could not be found")
		elif line.find('PSHELL') == 0:
			if int(line[8:16]) in written_pshellL:
				#this pshell is already processed and written with MAT2
				bdf_h.write("$" + line)
			else:
				bdf_h.write(line)
	
		else:
			bdf_h.write(line)
		n+=1
		
	bdf_h.close()
	
	return	
			
def ReadPartNames(inl1):
	PartNamePidD = {}
	n=0
	N=len(inl1)
	while n < N:
		lin=inl1[n]
		if lin.find("$ Part ") == 0 and  n< N-1:
			part_name=lin[7:].rstrip()
			print(part_name)
			# Assume one solids per part
			# get pid on first CTET/CHEX/CPENT directly following Part
			n+=1
			while inl1[n].find("CHEXA") < 0 and inl1[n].find("CPENTA") < 0 and inl1[n].find("CTETRA")  < 0 and n < N-1 :
				n+=1
			# found one
			Pid=int(inl1[n][16:24])
			PartNamePidD[part_name]=Pid
		n+=1

	return PartNamePidD 
	
def process_sol200_template(bdf_file, SettingsD):
	# read any tags on the glue pairs and cascade to glue patches
	# workaround to limitation preventing user from seeing changes in patch name:
	# change on glue pair and cascade name to patch, patch names are exported to bdf
	# no_of_pairs=UpdateGluePatchesFromGluePairName()
	# print "There are " + int(no_of_pairs) + " node pairs with user modified tags"

	print(list(SettingsD.keys()))
	if 'NASTRAN_JOB_SETUP' in SettingsD:
		if 'SOL200Template' in SettingsD['NASTRAN_JOB_SETUP']:
			
			if 'SOL200Template' in SettingsD['NASTRAN_JOB_SETUP']:
				template_file = resolve_pathname(SettingsD['NASTRAN_JOB_SETUP'])
			else:
				template_file = SettingsD['NASTRAN_JOB_SETUP']['SOL200Template']
			try:
				LoadId = int(SettingsD['NASTRAN_JOB_SETUP']['LoadId'])
			except:
				LoadId = 999999

			try:
				SpcId = int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId = 888888

			# read existing bdf file
			print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			ForceStr = ReadForces(bdf_lines, LoadId)
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			ConctactStrL, BConprgD = ReadContacts(bdf_lines)
			
			PartPidD=ReadPartNames(bdf_lines)
			print(PartPidD)
			
			StudyFlag=False
			# check if a study is being exported, or a default subcase
			for line in bdf_lines:
				if line.find('Linear Static Scenario') > -1:
					StudyFlag=True
					break
			
	

			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()
			else:
				template_lines["$ template file " + template_file + " is empty or non existing\n"]
				

			# find out if TOPVAR Tdmin param is specified
			try:
				Tdmin = float(SettingsD['NASTRAN_JOB_SETUP']['SOL200TopvarTdmin'])
				print(Tdmin)
				if Tdmin < 0:
					Tdmin = -1.0
			except:
				print("SOL200TopvarTdmin except")
				Tdmin = -1.0
				
			# find out if TOPVAR Tdmin param is specified
			try:
				Stress = float(SettingsD['NASTRAN_JOB_SETUP']['SOL200TopvarStress'])
				print(Stress)
				if Stress < 0:
					Stress = -1.0
			except:
				print("SOL200TopvarStress except")
				Stress = -1.0	



			# write back
			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			
			template_bulk_lines=[]
			BeginBulkFlag=False

			for line in template_lines:
				if line.find('SUBCASE') > -1:
#
					if StudyFlag== False:
						bdf_h.write(line)
						bdf_h.write("   LOAD=" + str(LoadId) + "\n")
						bdf_h.write("   SPC=" + str(SpcId) + "\n")
						if len(ConctactStrL) == 1:
							bdf_h.write(ConctactStrL[0])
						elif len(ConctactStrL) > 1:
							bdf_h.write(ConctactStrL[0])
							bdf_h.write("$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
				elif line.find('STRESS(') > -1 or line.find('DISP(') > -1 or 	line.find('ESE(') > -1:
					if StudyFlag== False:
						bdf_h.write(line)
				elif line.find('TOPVAR') == 0:
					for key in list(PartPidD.keys()):
						if key.find('#DS') > -1:
							#TOPO PART FOUND
							pid=PartPidD[key]
							break
					part_name=key.replace('#DS','') + "        "
					template_bulk_lines.append(line.replace('yyy',part_name[:8].rstrip()).replace('zzz',str(pid)).replace('xxx',str(pid)))
							
				elif line.find('TDMIN') > 0:
					if Tdmin > 0:
						template_bulk_lines.append(",TDMIN," + "%8.2f\n" % (Tdmin) )
						print((",TDMIN," + "%8.2f" % Tdmin))
					else:
						#print("What")
						pass
				elif line.find('STRESS') > 0:
					if Stress > 0:
						template_bulk_lines.append(",STRESS," + "%8.2f\n" % (Stress) )
						print((",STRESS," + "%8.2f" % Stress))
					else:
						#print("What")
						pass		
				
				elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
					print("skip placeholder comment")

				elif line.find('BEGIN BULK') > -1:
					BeginBulkFlag=True
					pass
					if 	StudyFlag == False:
						# Write NLPARM for controlling Non linear steppinf for SOL101
						#bdf_h.write("%-8s%8d%8d%8s%8s%8s%8s%8s%8s%-8s\n%-8s%8s%8s\n" % ("NLPARM",1,NoOfIncrements,"","","","",CONV,"","+","+",EPSU,EPSP))
					
						bdf_h.write(ForceStr + "\n")
						bdf_h.write(SpcStr + "\n")
										
				else:
					if BeginBulkFlag==True:
						template_bulk_lines.append(line)
					else:
						bdf_h.write(line)
				
			n = 0
			while n < len(bdf_lines):
				line = bdf_lines[n]
				# for line in bdf_lines:
				# remove any sol101, cend and BCONTACT already written out
				
				if line.find('SOL 101') > -1 or line.find('CEND') > -1:
					pass
				elif line.upper().find('SUBCASE') > -1:
					bdf_h.write(line.upper())
					bdf_h.write(" ANALYSIS=STATIC\n")
				elif line.find('BEGIN BULK') > -1:
					bdf_h.write(line)
					
					for template_bulk_line in template_bulk_lines:
						bdf_h.write(template_bulk_line)
				
				elif line.find('HDF5') > -1:
					pass

				
				else:
					bdf_h.write(line)
				n += 1
			bdf_h.close()

			print("sol200 nastran cards added")
		else:
			print("No SOL200Template found")
	else:
		print("No Nastran Job Setup info is settings file")		
					
def generate_MPCFORCES_request(CtrlNodeGridIdL,MPC_Set_Id=20000):
	CtrlNodeGridIdL=sorted(CtrlNodeGridIdL)
	if len(CtrlNodeGridIdL) > 0:
		MPCFORCES_str=""
		if len(CtrlNodeGridIdL) > 1:
			MPCFORCES_str += '   SET ' + str(MPC_Set_Id) + " = " + str(CtrlNodeGridIdL[0]) + " THRU " + str(CtrlNodeGridIdL[-1]) + "\n"
		elif len(CtrlNodeGridIdL) == 0:
			MPCFORCES_str += '   SET ' + str(MPC_Set_Id) + " = " + str(CtrlNodeGridIdL[0]) + "\n"

		if len(CtrlNodeGridIdL) > 0:
			MPCFORCES_str += "   MPCFORCES(PRINT,SORT1) = " + str(MPC_Set_Id) + "\n"
	else:
		MPCFORCES_str=""
		
	return MPCFORCES_str

def resolve_pathname(path_name, base_file=__file__):

	# check for "../" in pathname
	if path_name.find('..') > -1:
		# split in chunks, 
		path_chunks=path_name.split('/')
		n=0
		while n < len(path_chunks):
			if path_chunks[n] =='..':
				base_file=os.path.dirname(base_file)
			else:
				break
			n+=1
		# concatenate path_name with stripped away ".." with reminder of base_file
		print("new_base_file",base_file)
		new_path_chunk=('/').join(path_chunks[n:])
		print("new path chunk",new_path_chunk)
		new_path_name=base_file + '/'+ new_path_chunk
		print("New path name:", new_path_name)
		return new_path_name

	else:
		print("...resolving path_name...","path_name: ",path_name,"base_file: ",base_file)
		if os.path.isdir(path_name) or os.path.isfile(path_name):
			print("Absoulte path used:", path_name )
		elif os.path.isdir( os.path.dirname(base_file) + '/../' +  path_name) or os.path.isfile( os.path.dirname(base_file) + '/../' +  path_name):
			path_name=os.path.dirname(base_file) + '/../' +  path_name
			print("Relative path used ", path_name)
		elif os.path.isdir( os.path.dirname(base_file) + '/' +  path_name) or os.path.isfile( os.path.dirname(base_file) + '/' +  path_name):
			path_name=os.path.dirname(base_file) + '/' +  path_name
			print("Relative path used ", path_name)		
		else:
			print("Error...",  path_name, " can not be located")
			return
			
		return path_name

	
def process_sol400_template(bdf_file, SettingsD, OptionsD={} ):
	# read #T or #G  tags on glue patch names
	# workaround to manage touching contact in Apex
	
	#print('OptionsD')
	#print(OptionsD.keys())
	#try to obtain any non linear material definitions in material folder
	# specified in UtilityUserSettings: <System><MaterialDatabaseDir>
	
	# updated- uses new routine
	
	
	MaterialD=readMaterialsFromFile(SettingsD,False)
	
	TmpMaterialD=readMaterialsFromFile(SettingsD,True)
	
	for tmp_key in TmpMaterialD.keys():
		if tmp_key in MaterialD:
			print("Warning: Temp-Material with name: " + key + " already exists in standard material folder")
		else:
			MaterialD[tmp_key]=TmpMaterialD[tmp_key]

	OmitBConprgL=[]
	
	#print MaterialD
	
	# split BCONECT Cards
	# Apex sometimes writes ut multiple contact bodies on one same BCONECT card
	# These are split up to form BCONECT cards with excatly two contact bodies
	split_BCONECT_cards(bdf_file)
	
	#print SettingsD.keys()
	if 'NASTRAN_JOB_SETUP' in SettingsD:
		if 'SOL400Template' in SettingsD['NASTRAN_JOB_SETUP']:
			# read bdf file to see if a study has been exported 
			#print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			SubCaseStrL, SubCaseIdL, SpcIdD, SpcAddD =ReadSubcases(bdf_lines)
			
			#SpcIdL is a Dict!
			#print('SpcIdD',SpcIdD)
			#print('SpcAddD',SpcAddD)
			if SpcIdD=={} and SpcAddD=={}:
				print("***WARNING There are no SPCs in the selected scenario ...Exiting")
				return
			
			#print('SpcAddD',SpcAddD)
			
			template_file=resolve_pathname(SettingsD['NASTRAN_JOB_SETUP']['SOL400Template'],SettingsD['System']['SettingsFile'])
			#print("template_file: ",template_file)

			# get Ids for Loads, SPCs and Temps
			try:
				LoadId=int(SettingsD['NASTRAN_JOB_SETUP']['LoadId'])
			except:
				LoadId=1001
			try:
				TempLoadId=int(SettingsD['NASTRAN_JOB_SETUP']['ThermalLoadId'])
			except:
				TempLoadId=7001
			try:
				SpcId=int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId=10001	
				
			try:
				Sol400Mode = (SettingsD['NASTRAN_JOB_SETUP']['SOL400Mode'])
				if Sol400Mode.upper() in ['FASTERTHANFAST','NORMAL','SAFE','DEBUG','DEMO']:
					Sol400Mode=Sol400Mode.upper()
				else:
					Sol400Mode='NORMAL'
			except:
				Sol400Mode = False
				
			# Add to NLSTEP for SOL400	 (BCPARA in SOL101)
			try:
				maxiter = int(SettingsD['NASTRAN_JOB_SETUP']['MaxNoOfIterations'])
			except:
				maxiter = 25

			try:
				CombineBoltSteps = (SettingsD['BoltPreLoad']['CombineLoadSteps'])
				if CombineBoltSteps.upper() == "TRUE" or CombineBoltSteps.upper() == "YES":
					CombineBoltSteps=True
				else:
					CombineBoltSteps=False
			except:
				CombineBoltSteps = False	
					
			if 'SOL400NoOfIncrements' in SettingsD['NASTRAN_JOB_SETUP']:
				try:
					NoOfIncrements=int(SettingsD['NASTRAN_JOB_SETUP']['SOL400NoOfIncrements'])
				except:
					NoOfIncrements="Default"
			else:
				NoOfIncrements="Default"

			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()
			else:
				template_lines["$ template file " + template_file + " is empty or non existing\n"]
				
			ForceStr = ReadForces(bdf_lines, LoadId)
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			ConctactStrL, BConprgD = ReadContacts400(bdf_lines,OptionsD)
			ChexD,PsolidD = ReadChexas(bdf_lines)
			
			try:
				DefaultContactType = SettingsD['NASTRAN_JOB_SETUP']['SOL400DefaultContactType']
				if DefaultContactType == "Touch" or DefaultContactType == "Touching" or DefaultContactType == "T":
					DefaultContactType = "Touch"
				else:
					DefaultContactType = "Glue"
			except:
				DefaultContactType = "Glue"
				
			try:
				SelfContact = SettingsD['NASTRAN_JOB_SETUP']['SOL400SelfContact']
				if SelfContact.upper() == "TRUE" or SelfContact.upper() == "YES":
					SelfContact = True
				else:
					SelfContact = False
			except:
				SelfContact = False
				
			try:
				NonLinearSolver = (SettingsD['NASTRAN_JOB_SETUP']['NonLinearSolver'])
				if NonLinearSolver.upper()=='CASI' or NonLinearSolver.upper()== 'PARDISO':
					print("Solver selcted: " + NonLinearSolver)
			except:
				print("Default solver selcted: ")
				NonLinearSolver = "Default"	
				
			try:
				ContactErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL400TouchingContactError'])
			except:
				ContactErrorTol = "Default"
			#	
			try:
				GlueErrorTol = float(SettingsD['NASTRAN_JOB_SETUP']['SOL400GlueContactError'])
			except:
				GlueErrorTol = "Default"	
				
			try:
				DefaultFriction = float(SettingsD['NASTRAN_JOB_SETUP']['SOL400DefaultFriction'])
			except:
				DefaultFriction = 0.02
				
			try:
				CONV = SettingsD['NASTRAN_JOB_SETUP']['SOL400CONV']
			except:
				CONV = ""	
				
			try:
				EPSU = float(SettingsD['NASTRAN_JOB_SETUP']['SOL400EPSU'])
				EPSU="%8.3f" % (EPSU)
			except:
				EPSU = ""
				
			try:
				EPSP = float(SettingsD['NASTRAN_JOB_SETUP']['SOL400EPSP'])
				EPSP="%8.3f" % (EPSP)
			except:
				EPSP = ""
			
			try:
				StressFreeInitialContact=SettingsD['NASTRAN_JOB_SETUP']['StressFreeInitialContact']
				if StressFreeInitialContact.upper()=="TRUE":
					StressFreeInitialContact=True
				else:
					StressFreeInitialContact=False
			except:
				StressFreeInitialContact=False
			try:
				ContactMethod=SettingsD['NASTRAN_JOB_SETUP']['ContactMethod']
				if ContactMethod.upper() == "NODE2SEG":
					ContactMethod="NODESURF"
				else:
					ContactMethod="SEGTOSEG"
			except:
				ContactMethod="SEGTOSEG"

			
			try:
				AnalyticContact=SettingsD['NASTRAN_JOB_SETUP']['AnalyticContact']
				if AnalyticContact.upper()=="TRUE":
					AnalyticContact=True
					Sangle=60
					# Need NODESURF contact for this to work...no!
					#ContactMethod="NODESURF"
				else:
					AnalyticContact=False
			except:
				AnalyticContact=False

				
			try:
				Debug=SettingsD['NASTRAN_JOB_SETUP']['Debug']
				if Debug.upper()=="TRUE":
					Debug=True
					Sangle=60
				else:
					Debug=False
			except:
				Debug=False
				
			try:
				debugLevel=int(SettingsD['NASTRAN_JOB_SETUP']['DebugLevel'])
				if debugLevel==-2 or debugLevel==-3:
					pass
				else:
					debugLevel=-2
			except:
				debugLevel=-2
					
			# Preload settings
			try:
				BoltPreLoadFlag = SettingsD['NASTRAN_JOB_SETUP']['CreateBoltPreLoad']
				if BoltPreLoadFlag.upper() == "TRUE" or BoltPreLoadFlag.upper() == "YES":
					BoltPreLoadFlag=True
					#if BoltPreLoad = True change Contact Method to Seg2Seg and the Analytical Contact to False
					ContactMethod="SEGTOSEG"
				else:
					BoltPreLoadFlag=False
			except:
				BoltPreLoadFlag=False		

			print(("Bolt preload flag: " + str(BoltPreLoadFlag)))
			
			try:
				Pre_Load_Step=int(SettingsD['BoltPreLoad']['PreloadNoOfInc'])
			except:
				Pre_Load_Step=2
				
			try:
				Lock_Step=int(SettingsD['BoltPreLoad']['LockNoOfInc'])
			except:
				Lock_Step=2	
			
			AutoMsetFlag="NO"
			
			# try:
				# AutoMsetFlag = SettingsD['NASTRAN_JOB_SETUP']['AUTOMSET']
				# if AutoMsetFlag.upper().replace("TRUE","YES") == "YES":
					# AutoMsetFlag="YES"
				# elif AutoMsetFlag.upper() == "LUSOL":
					# pass
				# else:
					# AutoMsetFlag="NO"
			# except:
				# AutoMsetFlag="YES"	
			
			BoltBConprgL=[]
			ContactTableStr=""
			bolt_bulk_cards=[]
			if BoltPreLoadFlag==True:
				# There can be both automatic (through 3dBolt script) and semiautomated bolts in model
			
				BoltBSurfL, BoltBConectL, BoltBConprgL, bolt_bulk_cards, ContactTableStr, preload_spc, lock_spc, preload_id, BConectD, AutoBoltsExistFlag= WriteBoltBdfCards(
					bdf_file, SettingsD)
				if BoltBSurfL != []:
					BoltPreLoadFlag=True
					#if there are bolts in model, seg2seg is set and analytic contact deactivated
					AnalyticContact=False
					#ContactMethod="SEGTOSEG"
				else:
					BoltPreLoadFlag=False
					
				# WriteBoltBdfCards outputs a string with all cards, break up to a list	
				bolt_bulk_cardsL=bolt_bulk_cards.split('\n')
				#print "No. of bulk cards: " + str(len(bolt_bulk_cardsL))
				
				# Get Control Node Grids
				CtrlNodeGridIdL=[]
				for tmp_lne in bolt_bulk_cardsL:
					if tmp_lne.find("GRID") == 0:
						CtrlNodeGridIdL.append(int(tmp_lne[8:16].strip()))
				print(CtrlNodeGridIdL)
				
				#Generate SET for MPCFORCES to get bolt loadsteps
				mpcforces_str=generate_MPCFORCES_request(CtrlNodeGridIdL,20000)
					
					
			else:
				# I only need BConectD, BoltBsurfL,BoltBConectL
				BSurfD, CElemD, BoltBsurfL, BoltBConectL, BCBodyTableD, BConectD, tmp_str, BoltPreloadD, BCbodyL, BCbodyD, csv_file= ReadBsurfs(SettingsD, bdf_file, DefaultContactType, "0N")
			
			#for key in BSurfD:
			#	print(key,BSurfD[key])
			
			#print("===")
			#for key in BConectD:
			#	print(key,BConectD[key])	
		
			# write back...need to check if the file contains SUBCSES from a study or if "Loading" subcase need to be created from scripts
			#reload bdf_lines 
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()
			
			# Check if there is an initial temperature (currently stored as a comment in  header
			# $ Scenario description Initial Temperature:20
			init_temp=False
			for line in bdf_lines[:100]:
				if line.find('$ Scenario description') == 0:
					try:
						init_temp=float(line.split('Initial Temperature:')[1].strip())
					except:
						pass
					break
	
			
			# dict to map TempLoad IDs with Temperatures
			tempLoadIdToTempD={}
			
			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			
			m=0
			M=len(template_lines)
			#for line in template_lines:
			while m < M:
				line=template_lines[m]			
				if line.find('SOL') == 0:
					bdf_h.write(line)
					if NonLinearSolver.upper() == 'PARDISO':
						bdf_h.write('SPARSESOLVER NLSOLV (FACTMETH=PRDLDL)\n')
				elif line.find('SUBCASE') > -1:
					# write above subacase line
					if Debug==True:
						bdf_h.write("NLOPRM NLDBG=ADVDBG\n")
					if NonLinearSolver.upper() == 'CASI':
						bdf_h.write('SMETHOD = MATRIX\n')	
					if init_temp:
						bdf_h.write("TEMPERATURE(INITIAL) = " + str(TempLoadId) + "\n" )
						tempLoadIdToTempD[TempLoadId]=init_temp
						TempLoadId+=1	
					# write actual subaces line
					bdf_h.write(line)
					if BoltPreLoadFlag==True:
						bdf_h.write(mpcforces_str)
					if len(ConctactStrL) == 1:
						bdf_h.write("   " + ConctactStrL[0])
					elif len(ConctactStrL) > 1:
						bdf_h.write("   " + ConctactStrL[0])
						bdf_h.write("$ Warning: Bdf contains several BCTABL1 cards, only one supported in this version. first occurrence is used\n")		
				elif line.find('BEGIN BULK') > -1:
					# Add load steps referring to bolt preload
					if BoltPreLoadFlag:
						if AutoBoltsExistFlag:
							Step1Spc=SpcId
							#only write if there are Automated bolt preloads
							bdf_h.write("STEP 1\n")
							bdf_h.write("   SUBTITLE = Bolt Preload\n")
							bdf_h.write("   ANALYSIS = NLSTAT\n")
							# Assume preload step will use same set of SPCs as in first Subcase (SpcIdD[SubCaseIdL[0]])
							try:
								if len(SubCaseIdL) != 0:
									SpcAddD[Step1Spc]=SpcAddD[SpcIdD[SubCaseIdL[0]]] + [pre_load_spc]
								else:
									SpcAddD[Step1Spc]=[preload_spc] + SpcL
							except:
								# No SPCADD -create one and add existing SPC and fix spc-id
								if len(SubCaseIdL) != 0:
									SpcAddD[Step1Spc]=[SpcIdD[SubCaseIdL[0]],preload_spc]
								else:
									SpcAddD[Step1Spc]=[preload_spc] + SpcL
							
							bdf_h.write("   SPC = "+ str(Step1Spc) + "\n")
							bdf_h.write("   LOAD = " + str(LoadId) +"\n")
							bdf_h.write("   NLSTEP = 1\n")
						if len(SubCaseIdL) == 0:
							if AutoBoltsExistFlag:
								if  CombineBoltSteps == False:
									Step2Spc=SpcId+1
									# second preload step...this is skipped if CombineBoltSteps = True
									bdf_h.write("STEP 2\n")
									bdf_h.write("   ANALYSIS = NLSTAT\n")
									bdf_h.write("   SUBTITLE = Lock Bolts in Preloaded State\n")
									# Assume preload step will use same set of SPCs as in first Subcase (SpcIdD[SubCaseIdL[0]])
									try:
										if len(SubCaseIdL) != 0:
											SpcAddD[Step2Spc]=SpcAddD[SpcIdD[SubCaseIdL[0]]] + [lock_spc]
										else:
											SpcAddD[Step2Spc]=[lock_spc] + SpcL
									except:
										# first subcase does not have an SPCADD create one and add existing SPC and fix spc-id
										if len(SubCaseIdL) != 0:
											SpcAddD[Step2Spc]=[SpcIdD[SubCaseIdL[0]],lock_spc]
										else:
											SpcAddD[Step2Spc]=[lock_spc] + SpcL
									#
									bdf_h.write("   SPC = " + str(Step2Spc) + "\n")
									bdf_h.write("   LOAD = " + str(LoadId+ 1) + "\n")
									bdf_h.write("   NLSTEP = 2\n")
								
							
							# No External subcases, create one and  combine with fixation  preload step if CombineBoltSteps == True
							if len(SubCaseIdL) == 0:
								Step3Spc=SpcId+2
								bdf_h.write("STEP 3\n")
								bdf_h.write("   ANALYSIS = NLSTAT\n")	
								SpcAddD[Step3Spc]=[lock_spc] + SpcL
								bdf_h.write("   SPC = " + str(Step3Spc) + "\n")
								bdf_h.write("   LOAD = " + str(LoadId + 2) + "\n")
								bdf_h.write("   NLSTEP = " + str(3) +"\n")
						else:
							# external Subcases exist
							
							# !!Combination of bolt preloads Not permitted!! CombineBoltSteps == False:
							# second preload step

							if AutoBoltsExistFlag:
								
								Step2Spc=SpcId+1
								SpcId+=1
								bdf_h.write("STEP 2\n")
								bdf_h.write("   SUBTITLE = Lock Bolts in Preloaded State\n")
								bdf_h.write("   ANALYSIS = NLSTAT\n")
								# Assume preload step will use same set of SPCs as in first Subcase (SpcIdD[SubCaseIdL[0]])
								try:
									SpcAddD[Step2Spc]=SpcAddD[SpcIdD[SubCaseIdL[0]]] + [lock_spc]
								except:
									# first subcase does not have an SPCADD create one and add existing SPC and fix spc-id
									SpcAddD[Step2Spc]=[SpcIdD[SubCaseIdL[0]],lock_spc]

								bdf_h.write("   SPC = " + str(Step2Spc) + "\n")
								bdf_h.write("   LOAD = " + str(LoadId + 1) + "\n")
								bdf_h.write("   NLSTEP = 2\n")
							
							# write external loadsteps
							cnt=0
							
							# only offset step_id if there are auto bolts
							if AutoBoltsExistFlag:
								step_offset=2
							else:
								step_offset=0
							
							#for _line in SubCaseStrL :
							while cnt < len(SubCaseStrL):
								_line=SubCaseStrL[cnt]
								if _line.find('SUBCASE') == 0:
									#increase counter
									SpcId+=1
									step_id=int(_line.split()[1])
									bdf_h.write("STEP %d\n" % (step_id + step_offset))
									#if cnt !=0:
										# if there are more subcases
										# add restart step from STEP2
										#bdf_h.write("   NLIC SUBCASE 1       STEP 2       LOADFAC 1.00000\n")
									bdf_h.write("   ANALYSIS = NLSTAT\n")
									bdf_h.write("   NLSTEP = %d\n" % (step_id + step_offset) )
								elif _line.find('SUBTITLE') > -1:
									bdf_h.write("  " + _line)
									try:
										temp=float(_line.split("Temp @ ")[1].strip())
										bdf_h.write("   " + "TEMPERATURE(LOAD) =" + str(TempLoadId) + "\n" )
										tempLoadIdToTempD[TempLoadId]=temp
										TempLoadId+=1
									except:
										pass
									
								elif _line.find('SPC') > -1:
									# need to combine external SPCs with bolt loading SPC
									# Currently assume same SPCs are used in ALL subcases
									# Hence, use same SPC id as for Loadstep 2:
									# will fix this later by checking if SPC id refers to an SPCADD or not
									if AutoBoltsExistFlag:
										_spc_id=int(_line.split("=")[1].strip())
										# find corresponding SPC (or SPCADD) add fix spc-id
										try:
											SpcAddD[_spc_id].append(lock_spc)
										except:
											# SPC does not have an SPCADD create one and add existing SPC and fix spc-id
											SpcAddD[SpcId]=[_spc_id,lock_spc]
											bdf_h.write("   SPC = " + str(SpcId) + "\n")		
									else:
										#no auto bolts, hence no added constraints needed
										bdf_h.write("  " +  _line)
								elif _line.find("$ Event") > -1:
									pass
								else:
									bdf_h.write("  " + _line)
								cnt+=1
					# no bolt pre-loading								
					else:
						# No external subcases
						if len(SubCaseIdL) == 0:
							#Step1_LoadId=int(ForceStr[8:16].strip())
							#Step1_LoadId=LoadId
							# study not expoted, use defaults
							bdf_h.write("STEP 1\n")
							bdf_h.write("   ANALYSIS = NLSTAT\n")
							#bdf_h.write("   SPC = 10003\n")
							#bdf_h.write("   LOAD = " + str(Step1_LoadId) + " \n")
							bdf_h.write("   SPC = "+ str(SpcId +2) + "\n")
							bdf_h.write("   LOAD = " + str(LoadId) +"\n")	
							bdf_h.write("   NLSTEP = 1\n")
						
						
						# With external loadsteps							
						else:
							cnt=0
							while cnt < len(SubCaseStrL):
								_line=SubCaseStrL[cnt]
							
								if _line.find('SUBCASE') == 0:
									step_id=int(_line.split()[1])
									bdf_h.write("STEP %d\n" % (step_id ))
									#if cnt !=0:
										# if there are more subcases
										# add restart step from STEP2
										#bdf_h.write("   NLIC SUBCASE 1       STEP 2       LOADFAC 1.00000\n")
									bdf_h.write("   ANALYSIS = NLSTAT\n")
									bdf_h.write("   NLSTEP = %d\n" % (step_id) )
								elif _line.find('SUBTITLE') > -1:
									bdf_h.write("  " + _line)
									try:
										temp=float(_line.split("Temp @")[1].strip())
										bdf_h.write("   " + "TEMPERATURE(LOAD) =" + str(TempLoadId) +"\n")
										tempLoadIdToTempD[TempLoadId]=temp
										TempLoadId+=1
									except:
										pass
								elif _line.find("$ Event") > -1:
									pass
								else:
									bdf_h.write("  " + _line)
								cnt+=1		
									
					# write the BEGIN BULK card
					bdf_h.write(line)
					# add following line to simplify Abaqus translator
					bdf_h.write('$ Part None\n$\n')
					# write the temperature loads
					bdf_h.write("$ Temperature Loads generated from Export Utils tool\n")
					for temp_id in sorted(tempLoadIdToTempD.keys()):
						#TEMPD         91     22.
						bdf_h.write("%-8s%-8d%8.2f\n" % ("TEMPD",temp_id,tempLoadIdToTempD[temp_id]))
					
					if Debug==True:
						# enforce creation of marct t19 file with output for each iteration
						bdf_h.write("%-8s%-8s%8d\n" % ("PARAM","CDBMSG05",debugLevel ))

					# continue to write bolt related stuff
					bdf_h.write("$ Following cards are auto generated from the Apex Utils macro\n")
					
					if NoOfIncrements != 'Default':
						print(("No of increments as specified by user: " +  str(NoOfIncrements) ))
						Ext_Load_Step=NoOfIncrements
					
					if BoltPreLoadFlag and AutoBoltsExistFlag:
						# Assume for now that ALL SPCs are applied during preload step even if study is exported
						# write preload step... Warning No checks if the SpcStr is too long
						#bdf_h.write('%-8s%8d%56s%-8s\n%-8s%8s%8d' % ("NLSTEP", 1, "", "+", "+", "FIXED", Pre_Load_Step))
						bdf_h.write('%-8s%8d%56s%-8s\n' %  ("NLSTEP", 1, "", "+" ))
						bdf_h.write('%-8s%8s%8d%48s%-8s\n' % ( "+", "GENERAL",maxiter,"","+" ))
						bdf_h.write('%-8s%8s%8d' % ( "+", "FIXED", Pre_Load_Step ))
						if CONV == "":
							bdf_h.write('\n')
						else:
							bdf_h.write('%49s\n%-8s%8s%8s%8s%8s\n' % ("+","+", "MECH",CONV,EPSU,EPSP))
						
						print(("pre load id:" +  str(preload_id) ))
						# check if preload force or displacement
						test_force_str="%-8s%8d" % ("FORCE",preload_id)
						ForceFlag=False

						for tmp_lne in bolt_bulk_cardsL:
							if tmp_lne.find(test_force_str) ==0:
								ForceFlag=True
								#print("#####Found Force####")
								break
						if ForceFlag:
							#print("I am in force flag")
							bdf_h.write("%-8s%8d%8.1f%8.1f%8d\n" % ("LOAD", LoadId, 1., 1., preload_id))
							
						# deferred
						#bdf_h.write("%-8s%-8d%s" % ("SPCADD", SpcId, SpcStr[16:]) + "%-8d\n" % (preload_spc))

						# write Unload and lock  step
						bdf_h.write('%-8s%8d%56s%-8s\n' %  ("NLSTEP", 2, "", "+" ))
						bdf_h.write('%-8s%8s%8d%48s%-8s\n' % ( "+", "GENERAL",maxiter,"","+" ))
						bdf_h.write('%-8s%8s%8d' % ( "+", "FIXED", Lock_Step ))
						if CONV == "":
							bdf_h.write('\n')
						else:
							bdf_h.write('%49s\n%-8s%8s%8s%8s%8s\n' % ("+","+", "MECH",CONV,EPSU,EPSP))
						
						# defer write all SPCADD cards

						#bdf_h.write("%-8s%-8d%s" % ("SPCADD", SpcId+1, SpcStr[16:]) + "%-8d\n" % (lock_spc))

					# write Actual Loading step
					
					# if no events defined
					if len(SubCaseIdL) == 0:
						if BoltPreLoadFlag:
							nlstep_id=3
						else:
							nlstep_id=1
							
						bdf_h.write('%-8s%8d%56s%-8s\n' %  ("NLSTEP", nlstep_id, "", "+" ))
						bdf_h.write('%-8s%8s%8d%48s%-8s\n' % ( "+", "GENERAL",maxiter,"","+" ))
						bdf_h.write('%-8s%8s%8d' % ( "+", "FIXED", Ext_Load_Step ))
						
						if CONV == "":
							bdf_h.write('\n')
						else:
							bdf_h.write('%49s\n%-8s%8s%8s%8s%8s%8s%8s%8s%8s\n' % ("+","+", "MECH",CONV,EPSU,EPSP,"0.1","PFNT","","0"))
							
						bdf_h.write(ForceStr + "\n")
						
						# defer
						# if BoltPreLoadFlag and AutoBoltsExistFlag:
							# bdf_h.write("%-8s%-8d%s" % ("SPCADD", SpcId+2, SpcStr[16:]) + "%-8d\n" % (lock_spc))
						# else:
							# bdf_h.write("%-8s%-8d%s\n" % ("SPCADD", SpcId+2, SpcStr[16:]) )
							# #print("Spc Str")
							# #print(SpcStr)
					
					# if events are defined
					else:
						if BoltPreLoadFlag and AutoBoltsExistFlag:
							id_offset=2
						else:
							id_offset=0
							
						# get the load step settings for each even (if existing)
										
						try:
							eventSteps=[int(_stp) for _stp in OptionsD['eventStepL'] ]   # int
							eventEpsp=OptionsD['eventEpspL']   # str
							eventEpsu=OptionsD['eventEpsuL']   # str
							eventConvCrit=OptionsD['eventConvCrit']
						except:
							# create list with defaults
							eventSteps=[Ext_Load_Step for number in range(len(SubCaseIdL))]
							eventEpsp=[EPSP for number in range(len(SubCaseIdL))]
							eventEpsu=[EPSU for number in range(len(SubCaseIdL))]
							eventConvCrit=[CONV for number in range(len(SubCaseIdL))]
							
						_subc_id=0
						for id in SubCaseIdL:
							bdf_h.write('%-8s%8d%56s%-8s\n%-8s%8s%8d' % ("NLSTEP", id+id_offset, "", "+", "+", "FIXED", eventSteps[_subc_id]))
							if CONV == "":
								bdf_h.write('\n')
							else:
								bdf_h.write('%49s\n%-8s%8s%8s%8s%8s%8s%8s%8s%8s\n' % ("+","+", "MECH",eventConvCrit[_subc_id],eventEpsu[_subc_id],eventEpsp[_subc_id],"0.1","PFNT","","0"))
							
							# LOAD card is being generated in bdf
							#bdf_h.write(ForceStr + "\n")
							# SPCADD cards are generated in bdf (need to test if this is always the case when a study is exported)
							# When exporting SPCADD cards, add preload lock cards
							#bdf_h.write("%-8s%-8d%s" % ("SPCADD", 10003, SpcStr[16:]) + "%-8d\n" % (lock_spc))
							_subc_id+=1
					
					# write SPCADDs
					if AutoBoltsExistFlag:
						for spcadd_id in SpcAddD:
							bdf_h.write("%-8s%-8d" % ("SPCADD",spcadd_id) )
							p=0
							q=0
							tmp_list=SpcAddD[spcadd_id]
							while p < len(tmp_list):
								bdf_h.write("%-8d" % ( tmp_list[p] ))
								if p==len(tmp_list) -1:
									bdf_h.write("\n")
								elif (p==6 and q==0) or  (p==7 and q>0 ):
									bdf_h.write("%-8s\n%-8s" % ('+','+'))
									q+=1				
								p+=1
					
					
					# Write the bolt cards:					
					if BoltPreLoadFlag:
						bdf_h.write(bolt_bulk_cards)
						print ("### writing bolt cards ###")
					
				else:
					bdf_h.write(line)
					
				m+=1
				#End line.find('SUBCASE') > -1:
			#End for line in template_lines


			# ==OBSOLETE==
			# 1a. Remove Glue BCONECT with Bolt id's from contact table
			# 1b. Remove Glue BCONECT
			# 1c  Remove BCONPRG for Bolt cards
			# 2a  Add a BCONPRP (with same id as BCONPRG to add friction form the touching contacts)
			# ============
			
			# for update of properties PSLDN1, we need a map between material and solids on before handle
			# loop once, just looking for PSOLIDs, use MAT id as key for dict 
			SolidD={}
			
			for line in bdf_lines:
				if line.find('PSOLID') == 0:
					mat_id=int(line[16:24])
					#psolid_id=int(line[8:16])
					SolidD[mat_id]=line
					
			written_psolidL=[]
			n = 0
			GoFlag=False
			new_bconect_ids=[]
			
			
			while n < len(bdf_lines):
				line = bdf_lines[n]
				# for line in bdf_lines:
				#omit everything above BEGIN BULK
				# may have to change this if output is specified through the studies
				if line.find('BEGIN BULK') > -1:
					GoFlag=True
					n+=1
					continue

					
				if GoFlag:
					# remove any sol101, cend and BCONTACT already written out
					if line.find('BCONPRG') > -1:
						# concatenate all bconprg_strings, remove continuation
						bconprg_id = int(line[8:16])
						tmp_str = bdf_lines[n][24:72].replace('+','')
						#
						while bdf_lines[n + 1].find("+") == 0:
							tmp_str = tmp_str + bdf_lines[n + 1].replace('+       ','').replace('+','')
							n += 1			
						bconD={}
						# Organize tmp str in a list with chunks of 16, 
						bconL=[tmp_str[i:i + 16] for i in range(0, len(tmp_str), 16)]
						#print bconL	
						for item in bconL:	
							bconD[item[0:8].strip()]=item[8:16].strip()
						#print bconD

						# check if contact body has individual settings or if defaults should be applied
						if bconprg_id in BConprgD:
							try:
								if len(BConprgD[bconprg_id].keys()) > 0:
									IndividualSettingsFlag=True
								else:
									IndividualSettingsFlag=False
							except:
								IndividualSettingsFlag=False
						
						else:
							IndividualSettingsFlag=False
						#except:
						#	print('bconprg_id: ',bconprg_id,' not found in BConprgD. Do not write to  bdf')
						#	OmitBConprgL.append(bconprg_id)
						#	IndividualSettingsFlag=False
						

						
						#print("IndividualSettingsFlag",IndividualSettingsFlag)
						#except(KeyError,AttributeError):
						#	IndividualSettingsFlag=False
						
						if bconprg_id in list(BConprgD.keys()):
							# BConprgD[bconprg_id] will output a dict if "new" method is used, otherwise a string
							if IndividualSettingsFlag:
								#print("New method...")
								print("BCONPRG Id: ",bconprg_id , BConprgD[bconprg_id] )
								ContactType=BConprgD[bconprg_id]['contactType']
								if ContactType=="Glue":
									try:
										retain_overlaps=BConprgD[bconprg_id]['RetainGapOverlaps']
									except:
										retain_overlaps='False'
									if retain_overlaps=='True':
										#adds +1 to IGLUE 1-> 2  3->4
										if int(bconD['IGLUE']) == 1:
											bconD['IGLUE']="2"
										elif int(bconD['IGLUE']) == 3:
											bconD['IGLUE']="4"
									else:
										#adds +1 to IGLUE 1-> 2  3->4
										if int(bconD['IGLUE']) == 2:
											bconD['IGLUE']="1"
										elif int(bconD['IGLUE']) == 4:
											bconD['IGLUE']="3"		
											
								else:
									bconD['IGLUE']="0"
									try:
										stress_free_initial=BConprgD[bconprg_id]['StressFreeInitial']
									except:
										stress_free_initial='False'
										bconD['ICOORD']="1"
										
									try:
										friction=float(BConprgD[bconprg_id]['Friction'])
									except:
										friction=DefaultFriction

										
								bconD['ERROR']=float(BConprgD[bconprg_id]['Tolerance'])
								bconD['BIAS']=float(BConprgD[bconprg_id]['Bias'])
								
							else:
								# Get contact type from name #G or #T
								ContactType = BConprgD[bconprg_id]
						else:
							# use default contact type
							ContactType = DefaultContactType
						
						if IndividualSettingsFlag==False: 
							# Adjust contact Type
							#print("ContactType",bconprg_id,ContactType)
							if ContactType[0] == "T" or ( ContactType == "D" and DefaultContactType == "Touch" ):
								bconD['IGLUE']="0"
								if ContactErrorTol != "Default":
									bconD['ERROR']=ContactErrorTol	
								
								friction=DefaultFriction
							else:
								if GlueErrorTol != "Default": 
									bconD['ERROR']=GlueErrorTol									
							if StressFreeInitialContact==True:
								bconD['ICOORD']="1"
							print("BCONPRG Id: " + str(bconprg_id) + " " + ContactType )
						
						if ContactMethod=="NODESURF":
							bconD['ISEARCH']="2"
							
										
						# No need to add contact for SHELLs B=10 C=10 -> C																	 
						# print the modified BCONPRG card
						#print("Contact Type: " + ContactType + " Error: " +  str(bconD['ERROR']))	
						if bconprg_id not in OmitBConprgL:
							bdf_h.write("%-8s%-8d%8s" % ("BCONPRG", bconprg_id, "") )
							q=0
							m=0
							p=0
							for item in sorted(bconD.keys()):
								if ( q == 0 and m < 2 )  or  ( q > 0 and m < 3 ):
									bdf_h.write("%-8s%-8s" % (item,bconD[item]))
									#print "#1 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
									m+=1
									
								elif ((q == 0 and m == 2) or (q > 0 and m == 3 )) and p != len(list(bconD.keys())) -1 :
									bdf_h.write("%-8s%-8s" % (item,bconD[item]))
									bdf_h.write("%-8s\n%-8s" %	('+','+'))
									#print "#2 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)
									m=0
									q+=1
								#else:
								#	print("what")
								#	#print "#4 q: "  +str(q) + "    m: "  +str(m) +  "    p: "  +str(p)

								p+=1
							bdf_h.write("\n")
						else:
							bdf_h.write('$ BCONPRG ' + str(bconprg_id) + ' is not used\n')
								
						# add friction for Touching contact pairs
						if bconprg_id not in OmitBConprgL:
							if ContactType[0] == "T" or (ContactType == "D" and DefaultContactType == "Touch" ):
								bdf_h.write("%-8s%-8d%8s%8s%8.4f\n" % ("BCONPRP", bconprg_id, "", "FRIC", friction))	
						else:
							bdf_h.write('$ BCONPRP ' + str(bconprg_id) + ' is not  used\n')
								
					elif line.find('BCONECT') > -1:
						bconect_id = int(line[8:16])
						if bconect_id not in BoltBConectL:
							'''Test'''
							if BConectD[bconect_id][1] == "T" or ( BConectD[bconect_id][1] == "D" and DefaultContactType == "Touch"):
								# add bconprp, which has  the same id as bconprg, if created by this script
								# Do not write PCONPRG id if the contact type is Glue
								try:
									if BConprgD[bconect_id]['contactType']=='Glue':
										bdf_h.write(line)
									else:
										bdf_h.write(line[:24] + line[16:24] + line[32:])
								except:
									bdf_h.write(line[:24] + line[16:24] + line[32:])						
							else:
								bdf_h.write(line)

					elif line.find('PARAM,AUTOMSET,YES') > -1:
						# remove AUTOMSET if there are SPCDs in
						# always remove automset for sol400 /see settings above)
						#AutoMsetFlag = "NO"
						#for tmp_line in bolt_bulk_cards:
						#	if tmp_line.find('SPCD') == 0:
						#		AutoMsetFlag = "NO"
						bdf_h.write("%s,%s,%s\n" % ("PARAM", "AUTOMSET", AutoMsetFlag))				
					elif line.find('BCBODY1') == 0:
						bcbody_id=int(line[8:16])
						if AnalyticContact==True:
							bdf_h.write("%-8s%-8d%-8d%s" % ("BCBODY1",bcbody_id,1000+bcbody_id,line[24:]))
							#BCBDPRP  5001            IDSPL   -1      SANGLE 60.
							bdf_h.write("%-8s%-8d%8s%-8s%8d%8s%8.1f\n" % ("BCBDPRP",1000+bcbody_id,"","IDSPL",-1,"SANGLE",Sangle))
						else:
							bdf_h.write(line)
							
						if SelfContact==True:
							new_bconect_id=bcbody_id +1000
							bcon_prg_id=new_bconect_id
							bcon_prp_id=new_bconect_id
							# add BCONECT, BCONPRP, BCONPRG and add new BCONECTs to BCTABLE
							#BCONECT 2       2       2       3       1
							bdf_h.write("%-8s%-8d%-8d%-8d%-8d%-8d\n" % ("BCONECT",new_bconect_id,bcon_prg_id,bcon_prp_id,bcbody_id,bcbody_id))
							#BCONPRG 3               ERROR   0.1     IGLUE          0 
							if ContactErrorTol=="Default":
								ContactErrorTol=0.1
							bdf_h.write(("%-8s%-8d%8s%8s%8.3f%16s\n") % ("BCONPRG",bcon_prg_id,"","ERROR",ContactErrorTol,"IGLUE          0"))
							#BCONPRP 1                   FRIC  0.0500
							bdf_h.write(("%-8s%-8d%8s%8s%8.3f\n") % ("BCONPRP",bcon_prp_id,"","FRIC",DefaultFriction))
							# these are added to existing BCTABLE cards.
							new_bconect_ids.append(new_bconect_id)

							
					elif line.find('SPCADD') > -1:
						# Ignore all SPCADD cards, and their ncontinuation cards as they are added in previous steps if automated preload is used
						if AutoBoltsExistFlag:
							while bdf_lines[n+1].find('+')==0 or  bdf_lines[n+1].find(' ')==0:
								n+=1
						else:
							bdf_h.write(line)


					elif line.find('BCPARA') == 0:
						#print("I am in BCPARA ContactMethod " +  ContactMethod)
						bcparaL=line.split(",")
						bcpara_str="%-8s%-8d%-8s%-8s%8s%8d\n" % ("BCPARA",int(bcparaL[1]),"METHOD",ContactMethod,"FTYPE",6)
						bdf_h.write(bcpara_str)
					elif line.find('MDLPRM,HDF5') > -1:
						pass
						# consider to add AUTOMSET,LUSOL
						# this is needed to exclude bolt top to tip contact from bctable...
						# also used  for creating a BCTABL1 =0 entry
						print("")					
					elif line.find('BCTABL1') > -1:
						i = 3  # first line
						bctabL = []
						bctab_id = int(bdf_lines[n][8:16])
						while i <= 10:
							try:
								if bdf_lines[n][(i - 1) * 8] != "+":
									# print inl1[n][(i-1)*8:(i-1)*8+8]
									bctabL.append(int(bdf_lines[n][(i - 1) * 8:(i - 1) * 8 + 8]))
									i += 1
								else:
									n += 1  # encountered a continuation "+"
									i = 2
							except:
								# n+=1
								break
						# write back
						# also create str list for BCTABLE =0

						bdf_h.write("%-8s%8d" % ("BCTABL1", bctab_id))
						bctab_0str = ("%-8s%8d" % ("BCTABL1", 0))
						# first filter down list
						new_bctabL = [id for id in bctabL if id not in BoltBConectL]
						
						# add new bconect cards coming from self contact
						if len(new_bconect_ids) > 0:
							new_bctabL+=new_bconect_ids
							
						
						m = 2
						for id in new_bctabL:
							# if id not in BoltBConectL:
							bdf_h.write("%8d" % (id))
							bctab_0str += "%8d" % (id)
							if m == 8:
								if id != new_bctabL[-1]:
									bdf_h.write("%-8s\n%-8s" % ("+", "+"))
									bctab_0str += "%-8s\n%-8s" % ("+", "+")
									m += 1
								else:
									bdf_h.write("\n")
									bctab_0str += "\n"
								m = 1
							elif m != 8 and id == new_bctabL[-1]:
								bdf_h.write("\n")
								bctab_0str += "\n"
							else:
								m += 1
						bdf_h.write(bctab_0str)
						
					elif line.find('MAT1') == 0:
						# Check if any of comment lines above contains Material Name->
						m=1
						while bdf_lines[n-m].find("$") == 0:
							if bdf_lines[n-m].find("$ Material Record:") == 0:
								material_name=bdf_lines[n-m].split(':')[1].strip()
							m+=1
							
						mat_id=int(line[8:16])
						E_orig=myfloat(line[16:24])
						poi_orig=myfloat(line[32:40])
						# adjust for using compact float 7.8-12
						tmp_str=line[40:48].strip()
						try:
							if tmp_str[1:].find('E') < 0 and tmp_str[1:].find('e') <0:
								rho_orig=myfloat(tmp_str[0] + tmp_str[1:].replace('-','e-') )
							else:
								rho_orig=myfloat(tmp_str)
						except:
							rho_orig=0.0
						#rho_orig=float(line[40:48])
						print("Identified material: " + material_name + " in bdf file")

						#If Non linear material exists in material folder, replace this	
						#print("===" + material_name + "===")
						#for mat in MaterialD:
						#	print(mat)
						#print("===")
						
						write_orig_mat_Flag=False
						if material_name in MaterialD:
							print("Found matching material in database " + material_name)
							# create Material Class Object	
							
							# This is returned from readmatFromFile MatFileD[file.replace('.bdf','')]=mxpxD
							#global MxPxD
							
							MxPxD=MaterialD[material_name]
							CLexApex.setMxPxD(MxPxD)
							#There can currently only be one MAT1 card
							#print("==check MxPxD===")
							#print(MxPxD['MAT1'])
							#print(MxPxD['MAT1'].keys() )
							
							tmp_id=list(MxPxD['MAT1'].keys())[0]
							
							#m1=material(MxPxD['MAT1'][mat_id][0])
							m1=material(MxPxD['MAT1'][tmp_id][0])
							
							# write back to bdf_h after adjusting ids for MATs, Tables and Props.
							# use the actulal material_id: mat_id
			
							m1.writeBackMatToBdf_h(mat_id,bdf_h)
							
							if 1==0:
								_tmp_matD=MaterialD[material_name]
								print("===A===")
								print(_tmp_matD)
								print("===B===")
								_tmp_mat1D=_tmp_matD['MAT1']
								# only one MAT1 is allowed
								_mat1_keys=_tmp_mat1D.keys()
								if len(_mat1_keys) != 1:
									print("ERROR only one MAT1 allowed")
								else:
									tmp_matL=_tmp_mat1D[_mat1_keys[0]]
									print("===C===")
									print(tmp_matL)
									print("===D===")
								
								
								
								
								
								E_file=myfloat(tmp_matL[0][16:24])
								poi_file=float(tmp_matL[0][32:40])
								rho_file=myfloat(tmp_matL[0][40:48])
								# added Feb19th 2020 STATIC
								try:
									alpha=tmp_matL[0][48:56].strip()
								except:
									alpha=""
								try:
									tref=tmp_matL[0][56:64].strip()
								except:
									tref=""
								try:
									ge=tmp_matL[0][64:72].strip()
								except:
									ge=""	
								
								# replace material id from MatD with actual MatId
								# 1 Check so that youngs modulus rho and poi has not changed
								if abs(E_orig -E_file)/E_orig > 0.01:
									print(("Warning Youngs modulus for material " + material_name + " in database and material dB file differs more than 1% "))
								if abs(rho_orig -rho_file)/rho_orig > 0.01:
									print(("Warning density for material " + material_name + " in database and material dB file differs more than 1% "))
								if abs(poi_orig - poi_file)/poi_orig > 0.01:
									print(("Warning poissons ratio for material " + material_name + " in database and material dB file differs more than 1% "))
								print("===")
								for s in tmp_matL[1:]:
									print(s)
								print("===")
								print(tmp_matL)
								for mat_line in tmp_matL[1:]:
									if mat_line.find('MATEP') == 0  or mat_line.find('MATS1') == 0 or mat_line.find('MATHE') == 0:
										if mat_line.find('MATHE') != 0:
											# write back original material with alpha and tref  and damping
											new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
											bdf_h.write(new_line)
											#bdf_h.write(line)
										bdf_h.write(mat_line[:8] + "%-8d" % (mat_id) + mat_line[16:])
										write_orig_mat_Flag=True
									elif mat_line.find('PSLDN1') == 0:
										# add PSLDN1 card referencing the mat id
										#first write the PSOLID, then PSLDN1
										try:
											bdf_h.write(SolidD[mat_id])
											bdf_h.write(mat_line[:8] + "%-8d" % (int(SolidD[mat_id][8:16])) + mat_line[16:])
											written_psolidL.append(int(SolidD[mat_id][8:16]))
										except:
											print("There seem to be no solids referencing this material... write material as exported")
											bdf_h.write(line)
										#new_line=mat_line[:8] + "%-8d" % (int(SolidD[mat_id][8:16])) + "%32s" % (mat_line[16:].strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
										#bdf_h.write(new_line)
										
										
										
									else:
										#new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
										#bdf_h.write(new_line)
										bdf_h.write(mat_line)								
								if write_orig_mat_Flag==False:
									#write orig mat, this section is entered when there is just a standard linear material possibly with alpha
									new_line="%-48s" % (line.strip()) + "%8s%8s%8s\n" % (alpha,tref,ge)
									bdf_h.write(new_line)
									#bdf_h.write(line)
						else:
							bdf_h.write(line)
							print("No extended material behaviour defined"  )
							print()
					elif line.find('PSOLID') == 0:
						if int(line[8:16]) in written_psolidL:
							#this psolid is already processed and written with non linear material
							pass
						else:
							bdf_h.write(line)
					elif line.find('SPCD') == 0:
						#print("I am in SPCD check")
						# if there are no studies exported, SPCD id nnedd to match the id of the Loadset
						if len(SubCaseIdL) == 0:
							# No studies exported
							if BoltPreLoadFlag == True:
								# SPCD will appear in last loadstep
								bdf_h.write("%-8s%-8d%s" % ("SPCD",Step3_LoadId,line[16:]))
							else:
								# No bolt preloading, SPCD is the only exterior loading loadstep3
								bdf_h.write("%-8s%-8d%s" % ("SPCD",Step1_LoadId,line[16:]))
						else:
							#No renumbering... this is managed by the study export
							bdf_h.write(line)
						
						
					else:
						bdf_h.write(line)
					n += 1
				else:
					n+=1
			bdf_h.close()

			print("sol400 nastran cards added")
		else:
			print("No SOL400Template found")
	else:
		print("No Nastran Job Setup info is settings file")
		
		
	# Consolidate Contact Bodies
	
	try:
		consolidate_str=SettingsD['System']['ConsolidateContacts']
		if consolidate_str.upper()== 'FALSE' or consolidate_str== '':
			ConsolidateStrL=[]
		else:
			ConsolidateStrL=consolidate_str.split(',')
	except:
		ConsolidateStrL=[]
	
	print(ConsolidateStrL)
	
	if len(ConsolidateStrL) != 0:
		# consolidate Contact Bodies
	
		print("...consolidating...")
		BSurfD,CElemD,BoltBSurfL,BoltBConectL,BCBodyTableD,BConectD,ContactTableStr,BoltPreloadD,BCbodyL,BCbodyD,csv_file = ReadBsurfs(SettingsD, bdf_file, DefaultContactType)
		#print("BCbodyL (v42b)")
		#print(BCbodyL)
		
		#print("###Before condsolidate###")
		#print(BConectD)

		# consolidate Contactbodies with strings:
		ConsolidatedL=[]
		for item in ConsolidateStrL:
			BSurfD, tmp_L, BConectD, BCbodyD  =ConsolidateContactBodies(item,BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD)
			ConsolidatedL += tmp_L

		# Need to update bdf file only
		
		#print("#####################")
		#for item in BConectD:
		#	print(item, BConectD[item])
		#print("#####################")
		new_bdf_file=update_bdf_after_contact_body_consolidation(bdf_file,BCBodyTableD,BSurfD,BConectD)	
		
		
		
	return BoltPreLoadFlag

def write_proc_file(bdf_file, rigid_faces,SettingsD,GenerateContactTable=True):

	#Extract defaut settings
	# create default loadcase
	try:
		create_default_loadcase=SettingsD['MENTAT_EXPORT']['CreateDefaultLoadCase']
		if create_default_loadcase.upper()== 'TRUE' or create_default_loadcase.upper()== 'YES':
			create_default_loadcase=True
		else:
			create_default_loadcase=False
	except:
		create_default_loadcase=False
	
	# Run Marc
	try:
		run_marc=SettingsD['MENTAT_EXPORT']['RunMarc']
		if run_marc.upper()== 'TRUE' or run_marc.upper()== 'YES':
			run_marc=True
		else:
			run_marc=False
	except:
		run_marc=False
		
	# Run Monitor
	try:
		monitor_marc_job=SettingsD['MENTAT_EXPORT']['Monitor']
		if monitor_marc_job.upper()== 'TRUE' or monitor_marc_job.upper()== 'YES':
			monitor_marc_job=True
		else:
			monitor_marc_job=False
	except:
		monitor_marc_job=False
	
	# Apex Mode
	try:
		apex_mode=SettingsD['MENTAT_EXPORT']['ApexMode']
		if apex_mode.upper()== 'TRUE' or apex_mode.upper()== 'YES':
			apex_mode=True
		else:
			apex_mode=False
		# only for versions later than 2019 FP1	
		mentat_exe_str = SettingsD['System']['MentatPath']
		if mentat_exe_str.find("2019.1") < 0 and  mentat_exe_str.find("202") < 0:
			apex_mode=False
	except:
		apex_mode=False	
	
	
	
	# Consolidate COntacts
	try:
		consolidate_str=SettingsD['System']['ConsolidateContacts']
		if consolidate_str.upper()== 'FALSE' or consolidate_str== '':
			ConsolidateStrL=[]
		else:
			ConsolidateStrL=consolidate_str.split(',')
	except:
		ConsolidateStrL=[]
	
	print(ConsolidateStrL)
	
	print("Apex Mode:" + str(apex_mode) )
	
	
	# get the default contact type
	try:
		DefaultContactType = SettingsD['MENTAT_EXPORT']['MarcDefaultContactType']
		if DefaultContactType == "Touch" or DefaultContactType == "Touching" or DefaultContactType == "T":
			DefaultContactType = "Touch"
		else:
			DefaultContactType = "Glue"
	except:
		DefaultContactType = "Glue"
	
	# first consolidate contact bodies
	BSurfD,CElemD,BoltBSurfL,BoltBConectL,BCBodyTableD,BConectD,ContactTableStr,BoltPreloadD,BCbodyL,BCbodyD,csv_file = ReadBsurfs(SettingsD, bdf_file, DefaultContactType)
	#print("BCbodyL (v42b)")
	#print(BCbodyL)
	#print(BConectD)
	
	# consolidate Contactbodies with strings:
	ConsolidatedL=[]
	for item in ConsolidateStrL:
		BSurfD, tmp_L, BConectD, BCbodyD  =ConsolidateContactBodies(item,BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD)
		ConsolidatedL += tmp_L
	#BSurfD, ConsolidatedL0, BConectD, BCbodyD  =ConsolidateContactBodies("Washer",BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD)	
	#BSurfD, ConsolidatedL1, BConectD,  BCbodyD =ConsolidateContactBodies("Bolt",BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD)
	#BSurfD, ConsolidatedL2, BConectD,  BCbodyD =ConsolidateContactBodies("Nut",BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD)
	#ConsolidatedL=ConsolidatedL0 + ConsolidatedL1 + ConsolidatedL2
	
	# Need to update bdf file only
	new_bdf_file=update_bdf_after_contact_body_consolidation(bdf_file,BCBodyTableD,BSurfD,BConectD)
	
	


	# genrate a bdf_file without comments
	bdf_tmp = os.path.dirname(bdf_file) + '/tmp_' + os.path.basename(bdf_file)
	print(bdf_tmp)
	# work on the updated bdf
	bdf_h = open(new_bdf_file, 'r')
	lines = bdf_h.readlines()
	bdf_h.close()

	#
	load_ramp_lines=[]
	bdf_tmp_h = open(bdf_tmp, 'w')
	ForceD={}
	SpcdD={}
	PloadD={}
	MomentD={}
	
	N=len(lines)
	n=0
	print(GenerateContactTable)
	while n < N:
		line=lines[n]
		#
		if line.find('$') == 0:
			pass

		elif (line.find('BCONECT') == 0 or line.find('BCONPRG') ==0 or line.find('BCTABL1') ==0 ) and  GenerateContactTable == True:
			# do not write contact lines
			while lines[n+1].find('+') == 0 or lines[n+1].find(' ') == 0:
				#do not write consecutive continuation lines belonging to BCONECT
				n+=1
		
		else:
			bdf_tmp_h.write(line)	
			
		if line.find('SPCD') == 0:
			id=int(line[8:16])
			if id in list(SpcdD.keys()):
				# id already exists
				SpcdD[id].append(1)
				load_ramp_lines.append('*edit_apply spcd-' + str(id) + '_' + str(len(SpcdD[id])-1)  + '\n') 
				load_ramp_lines.append('*apply_option ramp:current_to_target\n')
			else:
				SpcdD[id]=[1]
				load_ramp_lines.append('*edit_apply spcd-' + str(id) + '\n') 
				load_ramp_lines.append('*apply_option ramp:current_to_target\n')
				
		else:
			pass
		n+=1
		
	bdf_tmp_h.close()
	#

	#except:
	#	print " except"
	#	DefaultContactType = "Glue"
	#


	proc_file = bdf_file[:-4].replace(' ', '_') + ".proc"
	proc_file_h = open(proc_file, 'w')
	write_proc_header(proc_file_h,apex_mode)

	# add scripts to speed up
	try:
		if SettingsD['MENTAT_EXPORT']['FastMode']=="True":
			fast_mode=True
		else:
			fast_mode=False
	except:
		fast_mode=True
	
	if fast_mode:
		proc_file_h.write('*set_undo off\n')
		proc_file_h.write('*set_update off\n')
		proc_file_h.write('*model_navigator_update off\n')

	# Enter loadcase to edit : *set_undo off
	# Command > *set_update off
	# Command > *model_navigator_update off
	# Command > *set_undo on
	# Command > *set_update on
	# Command > *prog_option model:backup:full
	# Command > *model_navigator_update on

	proc_file_h.write('*import nastran "' + bdf_tmp + '"\n')
	set_proc_rendering(proc_file_h)

	# The contact bodies in Mentat are defined in the same order as the BCBODY cards appear in bdf,
	# hence, the read sequence is important
	BCbodyNameD = {}
	# rename all bodies
	# bc_num=1
	
	
	
	# =====Enter only if GenerateContactTable = True     that is if UseApexUtilsContactTable=True is set in Utils xml file
	#      otherwise mentat will read BCONPRG data and generate the contact information (from 2018 and onwards)
	if GenerateContactTable==True:
	
		#for bcbody_id in BCbodyL:
		for bcbody_id in sorted(BCbodyD.keys()):
			# get the part names
			# ...later
			proc_file_h.write('*edit_contact_body ' + 'bcbody1-' + str(bcbody_id) + ' *contact_body_name\n')
			# proc_file_h.write('*edit_contact_body ' +  BCbodyD[bcbody_id][3] + ' *contact_body_name\n')

			# use part name for contact body
			proc_file_h.write(BCbodyD[bcbody_id][3] + '\n')
			BCbodyNameD[bcbody_id] = BCbodyD[bcbody_id][3]

		# proc_file_h.write('BCBODY_' + str(bcbody_id) + '\n')
		# BCbodyNameD[bcbody_id]='BCBODY_' + str(bcbody_id)

		# bc_num+=1
	
		contact_tableD = {}
		contact_lookup = {'T': 'touch', 'G': 'glue'}
		for bconect_id in list(BConectD.keys()):
			pairs = (BConectD[bconect_id][0][0], BConectD[bconect_id][0][1])
			contact_type = BConectD[bconect_id][1]
			interact_name = contact_type + contact_lookup[contact_type][1:] + '_' + str(pairs[0]) + '_' + str(pairs[1])
			contact_tableD[pairs] = interact_name
			proc_file_h.write('*new_interact mesh:mesh *interact_option state_1:solid *interact_option state_2:solid\n')
			proc_file_h.write('*interact_name ' + interact_name + '\n')
			proc_file_h.write('*interact_option contact_type:' + contact_lookup[contact_type] + '\n')

		part_counterD = {}
		for item in rigid_faces:
			print(item)
			parasolid_file = item[0]
			(tmp, ext) = os.path.splitext(parasolid_file)
			geom_body_name = os.path.basename(tmp)

			part_name = item[1]
			if part_name not in list(part_counterD.keys()):
				part_counterD[part_name] = 1
			else:
				part_counterD[part_name] += 1
			print("------------")

			bc_body_name = 'Rigid_' + part_name.split('#')[0].replace(' ', '') + '_' + str(part_counterD[part_name])
			print(bc_body_name)
			proc_file_h.write('*read_parasolid "' + parasolid_file + '"\n')
			# *read_parasolid "Surface 11.X_T"
			proc_file_h.write('*new_cbody geometry *contact_option geometry_nodes:off\n')
			# *new_cbody geometry *contact_option geometry_nodes:off

			proc_file_h.write('*contact_body_name ' + bc_body_name + '\n')
			proc_file_h.write('*edit_contact_body ' + bc_body_name + '\n')
			proc_file_h.write('*add_contact_body_sheet_bodies\n')
			proc_file_h.write(' ' + geom_body_name.replace(' ', '') + '\n')
			proc_file_h.write('# | End of List\n')
		# *add_contact_body_sheet_bodies
		# Surface11
		## | End of List

		# create contact table
		proc_file_h.write('*new_contact_table\n')
		
		for key in list(contact_tableD.keys()):
			print(key)
			print(contact_tableD[key])

		for (a1, a2) in list(contact_tableD.keys()):
			proc_file_h.write('*ctable_entry ' + BCbodyNameD[a1] + ' ' + BCbodyNameD[a2] + '\n')
			# ctable_entry Assembly_1_1 Assembly_1_2
			proc_file_h.write('*contact_table_option ' + BCbodyNameD[a1] + ' ' + BCbodyNameD[a2] + ' contact:on\n')
			# contact_table_option Assembly_1_1 Assembly_1_2 contact:on
			#
			proc_file_h.write(
				'*prog_string ctable:old_interact ' + contact_tableD[(a1, a2)] + ' *ctable_entry_interact ' + BCbodyNameD[
					a1] + ' ' + BCbodyNameD[a2] + '\n')
	#End contact stuff generation
					
					
	if create_default_loadcase:
		proc_file_h.write('*new_loadcase *loadcase_type struc:static\n')
		proc_file_h.write('*loadcase_name LoadCase1\n')
		# adding info on ramoping up the load
		for line in load_ramp_lines:
			proc_file_h.write(line)
			
		
	if run_marc and create_default_loadcase:
		proc_file_h.write('*prog_use_current_job on *new_job *job_class structural\n')
		proc_file_h.write('*job_name Job1\n')
		proc_file_h.write('*add_job_loadcases LoadCase1\n')
		proc_file_h.write('*edit_job Job1\n')
		proc_file_h.write('*add_post_tensor cauchy\n')
		proc_file_h.write('*add_post_tensor strain\n')
		
		proc_file_h.write('*update_job\n')
		proc_file_h.write('*submit_job 1\n')
		#proc_file_h.write('*job_edit_status_file\n')
		#proc_file_h.write('*monitor_job\n')
	if monitor_marc_job and run_marc and create_default_loadcase:
		#Enter edit job : *update_job
		proc_file_h.write('*post_open_default\n')
		#proc_file_h.write('*post_off\n')
		proc_file_h.write('*post_contour_bands\n')
		proc_file_h.write('*post_value Contact Status\n')
		#proc_file_h.write('*monitor_job\n')
		
		proc_file_h.write('*post_monitor\n')
		
				
	if fast_mode==True:			
		proc_file_h.write('*set_undo on\n')
		proc_file_h.write('*set_update on\n')
		proc_file_h.write('*model_navigator_update on\n')


	proc_file_h.close()

	print(BCbodyD)

	return proc_file
	
	
#BSurfD,CElemD,BoltBSurfL,BoltBConectL,BCBodyTableD,BConectD,ContactTableStr,BoltPreloadD,BCbodyL,BCbodyD,csv_file
def ConsolidateContactBodies(ident_str,BSurfD,BCBodyTableD,BConectD,BCbodyL,BCbodyD):
	# first find all contact bodies matching the indent_str
	ConsolidateL=[]
	print("===processing " + ident_str + " ======")
	for key in list(BCbodyD.keys()):
		if BCbodyD[key][3].find(ident_str) > -1 :
			print((BCbodyD[key]))
			
			# for the first occurrence, create a new contact Body (by renaming the first occurrence)
			# add remaining BSURF data to BSURF with master_id
			
			if len(ConsolidateL)==0:
				BCbodyD[key][3]=ident_str
				master_id=key
				print("Found: " +  ident_str + " Master id: " + str(master_id ) )
			ConsolidateL.append(key)
							
	for cont_body in ConsolidateL[1:]:
		print(("Consolidating: " + str(cont_body) ))
		BSurfD[master_id] += BSurfD[cont_body]
		print(BSurfD[cont_body])
		del BSurfD[cont_body]
		# also remove from BCBodyD
		del BCbodyD[cont_body]
		
	# remove BCONECT entries from contact table (except) the master_id => this is managed by ConsolidateConectL
	ConsolidateConectL=[]
	# 1 remove all consilidated ids to master id
	# processed_pairs
	ProcessedPairL=[]
	ConsolidateConectL=[]
	for conect_id in BConectD:
		tmp_L=  BConectD[conect_id]
		#[body1, body2],'T',bconprg_id]
		tmp_id1=tmp_L[0][0]
		tmp_id2=tmp_L[0][1]
		#cont_str=tmp_L[1]
		#bconprg_str=tmp_L[2]
		
		old_id1=tmp_id1
		old_id2=tmp_id2
		if tmp_id1 in ConsolidateL:
			tmp_id1=master_id
		if tmp_id2 in ConsolidateL:
			tmp_id2=master_id
		
		# 2 Check if the id -combination  exists before updating (else delete the entry to avoid duplicates	
			
		if (tmp_id1,tmp_id2) not in ProcessedPairL:
			print("ConectId: ",conect_id, " Old id ",old_id1, old_id2, "New id ", tmp_id1,tmp_id2)
			ProcessedPairL.append( (tmp_id1,tmp_id2) )
			BConectD[conect_id]=[[tmp_id1,tmp_id2],tmp_L[1],tmp_L[2],tmp_L[3]]
			print([[tmp_id1,tmp_id2],tmp_L[1],tmp_L[2],tmp_L[3]])
			print("#######")
		# # added 20200415
		# elif (tmp_id2,tmp_id1) not in ProcessedPairL:
			# print("scenario 2 ", tmp_id2,tmp_id1)
			# ProcessedPairL.append( (tmp_id2,tmp_id1) )
			# BConectD[conect_id]=[[tmp_id2,tmp_id1],tmp_L[2],tmp_L[1]]
		
		
		else:

			print("ConectId: ",conect_id, "Removed duplicate. Old Id ",old_id1, old_id2," New id ", tmp_id1,tmp_id2 )
			ConsolidateConectL.append(conect_id)	
	
	for cnct_key in ConsolidateConectL:
		del BConectD[cnct_key]
		print("Deleted ",cnct_key )
	
	return BSurfD, ConsolidateConectL, BConectD, BCbodyD
	
def write_proc_header(proc_file_h, apex_mode=False):
	# create header file
	if 1 == 1:

		proc_file_h.write("| Created by Apex JaguarUtils \n")
		if apex_mode:
			proc_file_h.write("*mode preset2\n")
		proc_file_h.write("*prog_option compatibility:prog_version:ment2017\n")
		proc_file_h.write("*prog_analysis_class structural\n")
		proc_file_h.write("*prog_use_current_job on\n")
		proc_file_h.write("*set_default_length_unit millimeter\n")
		proc_file_h.write("*set_model_length_unit millimeter\n")
		proc_file_h.write("|\n")

def set_proc_rendering(proc_file_h):
	# set rendering in Mentat
	proc_file_h.write("*fill_view\n")
	proc_file_h.write("*set_nodes off *redraw\n")
	proc_file_h.write("*surfaces_solid *solids_filled *elements_solid *redraw\n")
	proc_file_h.write("*identify_contact *redraw\n")

def process_checkout_template(bdf_file, SettingsD):

	# print SettingsD.keys()
	if 'NASTRAN_JOB_SETUP' in SettingsD:
		if 'CheckoutTemplate' in SettingsD['NASTRAN_JOB_SETUP']:
			if os.path.isfile( os.path.dirname(__file__) + '\\' + SettingsD['NASTRAN_JOB_SETUP']['CheckoutTemplate']) :
				# print "auto create full path to the nastran checkout template"
				# template_file=os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] + "\\Documents\\MSC_Apex Workspace\\FossaUtils\\" + SettingsD['NASTRAN_JOB_SETUP']['CheckoutTemplate']
				template_file = os.path.dirname(__file__) + '\\' + SettingsD['NASTRAN_JOB_SETUP']['CheckoutTemplate']
			else:
				template_file = SettingsD['NASTRAN_JOB_SETUP']['CheckoutTemplate']

			# read existing bdf file
			# print " I am reading " + bdf_file
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			# ConctactStrL=ReadContacts(bdf_lines)

			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()

			# # find out if contact flag is set SOL101ContactType=Touch
			# try:
			# ContactType=SettingsD['NASTRAN_JOB_SETUP']['SOL101ContactType']
			# if ContactType=="Touch" or ContactType=="Touching" or ContactType=="T":
			# ContactType="Touch"
			# else:
			# ContactType="Glue"
			# except:
			# print " except"
			# ContactType="Glue"

			try:
				EqTol = float(SettingsD['EQ_SETTINGS']['EQ_TOL'])
			except:
				EqTol = "0.01"

			# write back to checkout file
			checkout_file = os.path.dirname(bdf_file) + "/checkout_" + os.path.basename(bdf_file)
			# print " I am writing " + checkout_file
			bdf_h = open(checkout_file, 'w')
			for line in template_lines:
				bdf_h.write(line)
				#if line.find('SUBCASE') > -1:
				#	bdf_h.write(line)
				# bdf_h.write("   LOAD=" + str(LoadId) + "\n")
				# bdf_h.write("   SPC=" + str(SpcId) + "\n")

				# if len(ConctactStrL) ==1:
				#	bdf_h.write(ConctactStrL[0])
				# elif len(ConctactStrL) > 1:
				#	bdf_h.write(ConctactStrL[0])
				#	bdf_h.write("$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n" )
				# elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
				#	print "skip placeholder comment"

				#elif line.find('BEGIN BULK') > -1:
				#	bdf_h.write(line)
				# bdf_h.write(ForceStr + "\n")
				# bdf_h.write(SpcStr + "\n")

				#else:
				#	
			# omit header from Apex Grizzly
			n = 0
			GoFlag=False
			while n < len(bdf_lines):
				line = bdf_lines[n]
				if line.find("BEGIN BULK") == 0:
					GoFlag=True
					n+=1
					continue
				if GoFlag==True:
					#Check for adjustments to Bulk data section
					if line.find('COPTS') > -1:
						#Hawk writes COPTs on first line
						a=line.find('COPTS')
						line=line[0:a] + 'COPTS   60      ' + line[a+16:]
						b=line.find('COPTM')
						line=line[0:b] + 'COPTM   60      ' + line[b+16:]
						bdf_h.write(line)
					# make sure node to seg is used
					elif line.find('BCPARA,0,METHOD,SEGTOSEG') > -1:
						bdf_h.write("$" + line)
					else:
						bdf_h.write(line)
				n += 1
			bdf_h.close()

		# print "checkout  cards added"
		else:
			print("No Checkoput Template found")
	else:
		print("No Nastran Job Setup info is settings file")

	return checkout_file

def read_descriptions(bdf_file):
	# read descriptions and form SXNAME entries to be added to bdf
	bdf_h = open(bdf_file, 'r')
	bdf_lines = bdf_h.readlines()
	bdf_h.close()
	sxnameL = []
	n = 0
	while n < len(bdf_lines):
		line = bdf_lines[n]
		# for line in bdf_lines:
		if line.find('$ Material:') > -1:
			n += 1
			print(line)
			tmp_name = line.split(':')[1].strip()
			while bdf_lines[n].find("MAT1") != 0:
				n += 1

			if bdf_lines[n].find("MAT1") == 0:
				print(bdf_lines[n])
				print('$SXNAME MAT     ' + bdf_lines[n][8:16] + 'ISOTROPI' + '"' + tmp_name + '"')
				sxnameL.append('$SXNAME MAT     ' + bdf_lines[n][8:16] + 'ISOTROPI' + '"' + tmp_name + '"\n')


		elif line.find('$ Property:') > -1:
			n += 1
			print(line)
			tmp_name = line.split(':')[1][9:].split(',')[0].strip()
			while bdf_lines[n].find("PSHELL") != 0:
				n += 1

			if bdf_lines[n].find("PSHELL") == 0:
				print(bdf_lines[n])
				print('$SXNAME PROP    ' + bdf_lines[n][8:16] + 'PSHELL  ' + '"' + tmp_name + '"')
				sxnameL.append('$SXNAME PROP    ' + bdf_lines[n][8:16] + 'PSHELL  ' + '"' + tmp_name + '"\n')

		# This section is deactivated as the PBUSH1D names seems all to be pointing to the first property
		# elif line.find('$ Spring damper:') > -1:
		#	n+=1
		#	print line
		#	tmp_name=line.split(':')[1].strip()
		#	while bdf_lines[n].find("PBUSH1D") !=0:
		#		n+=1
		#
		#	if bdf_lines[n].find("PBUSH1D") == 0:
		#		print bdf_lines[n]
		#		print '$SXNAME PROP    ' + bdf_lines[n][8:16] + 'PBUSH1D ' + '"' + tmp_name + '"'
		#		sxnameL.append('$SXNAME PROP    ' + bdf_lines[n][8:16] + 'PBUSH1D ' + '"' + tmp_name + '"\n')

		n += 1

	if sxnameL != []:
		bdf_h = open(bdf_file, 'w')
		# bdf_h.write('\n')
		for line in bdf_lines[:-1]:
			bdf_h.write(line)

		bdf_h.write('$ SX Names exported by FossaUtils\n')
		for line in sxnameL:
			bdf_h.write(line)
		bdf_h.write('ENDDATA\n')
		bdf_h.close()

def process_glue_to_eqnodes(bdf_file, SettingsD):

	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()

	NodeD = ReadGrids(inl1)
	# RbarD=ReadRbars(inl1)

	# only convert RBE3's associated with RBARs in RBARD
	# RbarNodeL=[]
	# for item in RbarD.keys():
	#	RbarNodeL.append(RbarD[item][0])
	#	RbarNodeL.append(RbarD[item][1])
	contact_flag = False
	for line in inl1:
		if line.find('BCONPRG') == 0:
			contact_flag = True
			print("contact found")
			break

	# print RbarNodeL
	# Rbe3D=ReadRbe3s(inl1,RbarNodeL)

	# only proceed if there are rigid links of type RBE3-RBAR-RBE3
	if contact_flag:
		print("set up Nastran job to generate MPCs")

		# setup bdf to run
		check_out_bdf = process_checkout_template(bdf_file, SettingsD)
		print("Run Nastran " + SettingsD['NASTRAN_JOB_SETUP']['RunNastran'])
		try:
			RunNastran = SettingsD['NASTRAN_JOB_SETUP']['RunNastran']
			print("Nastran executed")
		# tmp_h1.append("Nastran execution chosen\n")
		except:
			print("RunNastran failed")
			RunNastran = "False"
		if RunNastran == "True":
			run_nastran(check_out_bdf, SettingsD)
		else:
			print("Nastran execution is not choosen")

		# Now read pch file to obtain MPCs from converted contact
		check_out_pch = check_out_bdf[:-4] + '.pch'

		check_h = open(check_out_pch, 'r')
		lines = check_h.readlines()
		check_h.close()

		NoOfElems = 10

		try:
			eq_tol_str = SettingsD['EQ_SETTINGS']['EQ_TOL']
			if eq_tol_str.find('%') > 0:
				print("eq tolerance relative to elemnt length is used: " + eq_tol_str)
				eq_tol_rel = float(eq_tol_str.replace('%', '').strip())
				avg_lgth = getSampleElementLength(inl1, NodeD, NoOfElems)
				eq_tol = avg_lgth * eq_tol_rel / 100
				print("avg el length is: " + str(avg_lgth) + " based on " + str(NoOfElems) + " elements")
				print("eq tol is: " + str(eq_tol))
			else:
				print("Absolute Merge Tolerance is: " + SettingsD['EQ_SETTINGS']['EQ_TOL'])
				eq_tol = float(SettingsD['EQ_SETTINGS']['EQ_TOL'])
		except:
			print("Merge Tolerance can not be found in settings file, use default 0.005")
			eq_tol = 0.005

		MergeL, FailedL, bdf_log_L, min_tol_L = ReadMPCs(lines, NodeD, eq_tol)

		# print "====FailedL======"
		# print FailedL
		# print "====Merge list==="
		# print MergeL

		# print "====Bdf_log_list==="
		# print bdf_log_L


		try:
			CenterOfFailedString = SettingsD['EQ_SETTINGS']['CenterOfFailedTrias']
			if CenterOfFailedString.find("COG") >= 0:
				# get COG from checkout run
				check_out_f06 = (check_out_bdf[:-3] + 'f06').lower()
				print(check_out_f06)
				f06_h = open(check_out_f06, 'r')
				f06_lines = f06_h.readlines()
				f06_h.close()
				N = len(f06_lines)
				n = 0
				x = 0
				y = 0
				z = 0
				cog_flag = 0
				while n < N:
					if f06_lines[n].find("MASS AXIS SYSTEM ") > 0:
						for line in f06_lines[n + 1:n + 4]:
							tmp = line.split()
							# print tmp
							x = x + 0.5 * float(tmp[2])
							y = y + 0.5 * float(tmp[3])
							z = z + 0.5 * float(tmp[4])
						print("Using Center of Gravity as center of failed  : [" + str(x) + "," + str(y) + "," + str(
							z) + "]")
						CtrOfFailed = [x, y, z]
						cog_flag = 1
						break

					else:
						n += 1
				if cog_flag == 0:
					print("Failed to find Center of Gravity... using default 0,0,0")
					CtrOfFailed = [0, 0, 0]

			else:
				CtrOfFailed = [float(i) for i in CenterOfFailedString.split(',')]
				if len(CtrOfFailed) != 3:
					print("Center of Failed Trias was not on the right format x,y,z   ... using default 0,0,0 ")
					CtrOfFailed = [0, 0, 0]
				else:
					print("Center of Failed Trias specified: " + CenterOfFailedString)

		except:
			print("Could not extract Center of Failed Trias from settings file...using default 0,0,0")
			CtrOfFailed = [0, 0, 0]

		out_tria_file = os.path.dirname(bdf_file) + "/find_failed_merge_nodes.bdf"
		failed_file_h = open(out_tria_file, 'w')

		if len(min_tol_L) > 0:
			failed_file_h.write(
				"$ Minimum merge tolerance needed to merge all grid pairs: " + "%10.4f" % (min_tol_L[-1]) + "\n")
			failed_file_h.write("$ Failed number of pairs: " + "%10s" % (len(min_tol_L)) + "\n")
		else:
			failed_file_h.write("$ All node pairs merged\n")
		failed_file_h.write("$\n")

		# temporarily comment
		# bdf_log_L,min_tol_L
		# for line in bdf_log_L:
		#	failed_file_h.write("$" + line[33:] + "\n")


		WriteCTriaForFailedGrids(failed_file_h, FailedL, NodeD, start_coord=CtrOfFailed)
		failed_file_h.close()
	
	else:
		MergeL=[]
		FailedL=[]
		CtrOfFailed=[]

	return MergeL, NodeD, FailedL, inl1, CtrOfFailed

def CheckAndReplaceGrids(tmp_line, PosL, replace_nodes, MergeD):
	# detected something strange with extra blanks, that was fixed for Aug 18th
	# looks ok now, but check for other inconsistencies...
	# in particular check Tie example
	# Stefan Thynelius Aug 18th
	# break string in chunks of 8
	# b=[tmp_line[i:i+8] for i in xrange(0, len(tmp_line), 8)]
	# changed Aug 18th
	# tmp_line=tmp_line.replace('+  ','+')
	# changed Aug 29th 2019
	# managed the continuation line by padding spaces to situation "+\n+"
	
	# manage continuations
	
	tmp_line=tmp_line.replace('+\n+','+      \n+')
	
	b = [tmp_line[i:i + 8] for i in range(0, len(tmp_line), 8)]
	# print "b is: " + str(len(b))
	# print b
	check_flag=False
	# if tmp_line.find("CHEXA   301") == 0 or tmp_line.find("CHEXA   302") == 0:
		# print b
		# check_flag=True


	for index in PosL:
		# catch the case where the index list ist too long or where index may contain blanks or continuation cards
		if index < len(b):
			# test if b[index] is an int
			try:
				a = int(b[index])
				go_temp = 1
			except:
				go_temp = 0
			#
			if go_temp == 1:
				if int(b[index]) in replace_nodes:
					if index != (len(b) - 1):
						b[index] = "%-8d" % (MergeD[int(b[index])])
					# if b[0].find("RBE3") == 0:
					#	print b[index]

					else:
						# Add newline if last element in list
						b[index] = "%8d\n" % (MergeD[int(b[index])])


	a = ""
	i = 0
	j = 0
	while i < len(b):
		# changed Aug 18th
		#a=a+b[i].replace('+ ','+   ')
		a = a + b[i]
		i += 1
	return a

def process_tie_to_eqnodes(bdf_file):
	comment_duplicate_flag=True
	print("Processing tie_to_eqnodes")
	inf = open(bdf_file, 'r')
	inl1 = inf.readlines()
	inf.close()
	#
	TieNodeL, RbarElemL = getTieNodes(inl1)
	merge_list = createMergeList(TieNodeL)
	#
	old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
	save_orig_file_if_not_new(old_bdf, bdf_file)

	OmitD = {}
	OmitD['RBAR'] = RbarElemL
	UpdateBdf(bdf_file, inl1, merge_list, OmitD, comment_duplicate_flag)

def UpdateBdf(outfile, inl1, MergeL, OmitD={}, comment_duplicate_flag=False):
	#print("Processing UpdateBdf")
	outL = []
	replace_nodes = []
	keep_nodes = []
	MergeD = {}
	# create list of nodes to replace and nodes to Merge
	# also create a dict to lookup replacement ids for
	for node_pair in MergeL:
		keep_nodes.append(node_pair[1])
		replace_nodes.append(node_pair[0])
		MergeD[node_pair[0]] = node_pair[1]
	# print "=========================="
	# print len(inl1)
	# print inl1[0:5]
	print(" No of grid pairs to merge: " + str(len(replace_nodes)))
	# for line in inl1:
	out_file_h = open(outfile, 'w')
	#print("replace_nodes")
	#print(replace_nodes)
	#print("MergeD")
	#print(MergeD)

	MergeGrids(out_file_h, inl1, replace_nodes, MergeD,comment_duplicate_flag)
	#
	out_file_h.close()
	if OmitD != {}:
		# OmitD is a dict with elem typ and list of EIDs to omit
		# right now a little clunky to re-read the file for each dict entry
		# current use is  just expecting  one type at the time (RBAR)
		for key in list(OmitD.keys()):
			in_file_h = open(outfile, 'r')
			lines = in_file_h.readlines()
			in_file_h.close()
			out_file_h = open(outfile, 'w')
			
			#print("I am in UpdateBdf processing: " + outfile )
			#print(OmitD)
			
			
			for line in lines:
				tmp_keyword=line[:8].strip()
				if tmp_keyword in OmitD.keys():

					if tmp_keyword == "ASET1":
						if int(line[16:24]) in OmitD[tmp_keyword]:
							out_file_h.write("$" + line)
						else:
							out_file_h.write(line)
						
					else:  # this is for instance RBAR
						if int(line[8:16]) in OmitD[tmp_keyword]:
							out_file_h.write("$" + line)
						else:
							out_file_h.write(line)
				else:
					out_file_h.write(line)
			
			
			
			# for key in list(OmitD.keys()):
				# for line in lines:
					# if key == "ASET1":
						# if line.find(key) == 0 and int(line[16:24]) in OmitD[key]:
							# out_file_h.write("$" + line)
						# else:
							# out_file_h.write(line)
					# else:
						# print("I am in here: RBAR"
						# print(
						# if line.find(key) == 0 and int(line[8:16]) in OmitD[key]:
							# out_file_h.write("$" + line)
						# else:
							# out_file_h.write(line)
			out_file_h.close()

def process_nodethickness_to_pshell(bdf_file, SettingsD, thick_inc=0.01, thick_lo_limit=0, thick_up_limit=100):
	file_h = open(bdf_file, 'r')
	lines = file_h.readlines()
	file_h.close()

	N = len(lines)
	n = 0

	NewPshellD = {}
	ExistingPshellD = {}

	# first pass... just read pshells to get the MID

	while n < N:
		if lines[n].find("PSHELL") == 0:
			pid = int(lines[n][8:16].strip())
			mid1 = int(lines[n][16:24].strip())
			mid2 = int(lines[n][32:40].strip())
			mid3 = int(lines[n][48:56].strip())
			ExistingPshellD[pid] = [mid1, mid2, mid3]
		# print ExistingPshellD[pid]
		n += 1
	try:
		# get max pid
		pid_max = max(ExistingPshellD.keys())
		# print pid_max
	except:
		print("There appear to be no Shell elemnts in model, returning")
		return

	pid_cntr = int(math.ceil(pid_max / 100.0)) * 100 + 1

	# print pid_cntr

	# second pass
	n = 0
	BdfL = []
	
	# get thickness ackording to ranges
	if thick_inc > 0 and thick_lo_limit >=0:
		number_of_steps=int((thick_up_limit-thick_lo_limit)/thick_inc) + 1
		thickL= [round((thick_lo_limit + x * thick_inc),4) for x in range(0, number_of_steps)]
	else:
		thickL=[]
	print(thickL)
	

	while n < N:
		if lines[n].find("CQUAD4") == 0 or lines[n].find("CTRIA3") == 0:
			# print "cquad4"
			if lines[n + 1].find('+') == 0:
				#print("variable thickness found")
				# assume thickness is constant among all grids (true for Apex releases at least up to Fossa)
				
				t0 = float(lines[n + 1][24:32].strip())
				
				# find nearest t in thickL
				if thickL != []:
					t=min(thickL, key=lambda x:abs(x-t0))
					print("thickness is: " + str(t0) + " nearest is: " + str(t))
				else:
					t=t0
					
				pid = int(lines[n][16:24].strip())
				# print t
				# print pid
				if t in list(NewPshellD.keys()):
					print("exists " + str(t))
					BdfL.append(lines[n][0:16] + "%-8d" % (NewPshellD[t][0]) + lines[n][24:].replace('+', ''))
				# print lines[n][0:16]+ "%-8d" % (NewPshellD[t][0]) + lines[n][24:].replace('+','')
				else:
					NewPshellD[t] = [pid_cntr, ExistingPshellD[pid]]
					pid_cntr += 1
					BdfL.append(lines[n][0:16] + "%-8d" % (NewPshellD[t][0]) + lines[n][24:].replace('+', ''))
				n += 1

			else:
				BdfL.append(lines[n])
		else:
			BdfL.append(lines[n])
		n += 1

	old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
	save_orig_file_if_not_new(old_bdf, bdf_file)

	# write back

	# change name on orig file
	# os.rename(tmp_file,os.path.dirname(tmp_file) + "\\" + os.path.basename(tmp_file) )
	# print os.path.dirname(tmp_file) + "\\orig_" + os.path.basename(tmp_file)
	# new_file= os.path.dirname(tmp_file) + "\\new_" + os.path.basename(tmp_file)
	file_h = open(bdf_file, 'w')


	# print all bulk data
	for line in BdfL:
		# write cards after BEGIN BULK
		if line.find("BEGIN BULK") == 0:
			# print modified pshell cards
			file_h.write("$Auto generated PSHELL cards based on element based nodal thickness \n")
			if thickL != []:
				file_h.write("$Thickness grouping Lower limit: " + str(thick_lo_limit) + " Increment: "  + str(thick_inc) + " Upper limit: " + str(thick_up_limit) +"\n")
			else:
				file_h.write("No grouping of thicknesses\n")
			for key in list(NewPshellD.keys()):
				t = key
				pid = NewPshellD[key][0]
				mid1 = NewPshellD[key][1][0]
				mid2 = NewPshellD[key][1][1]
				mid3 = NewPshellD[key][1][2]
				#
				file_h.write("%-8s%8d%8d%8.4f%8d%8.2f%8d%8.5f\n" % ("PSHELL", pid, mid1, key, mid2, 1.0, mid3, 0.83333))
			file_h.write("$\n")
		
		
		file_h.write(line)
	

	file_h.close()

def CreatePrefix(startpath, top_bdf, SettingsD={}, AllFilesL=[]):
	# assume no more than 100 levels

	try:
		ses_group_no_of_blanks = int(SettingsD['PatranGroups']['SubIndentNoOfBlanks'])
	except:
		ses_group_no_of_blanks = 2
	# if 1==1:
	indent_char = " "

	try:
		tree_view_indent_char = SettingsD['PatranGroups']['IndentChar']
		if tree_view_indent_char.upper() == "UNDERSCORE":
			tree_view_indent="_"
		else:
			tree_view_indent=" "			
	except:
		tree_view_indent = " "	
	
	
	try:
		expanded_tree_view = (SettingsD['PatranGroups']['ExpandedViewSort'])
		if expanded_tree_view.upper() == "NO":
			expanded_tree_view = "No"
			indent_char = "_"

		else:
			expanded_tree_view = "Yes"
			#indent_char = " "
			indent_char = tree_view_indent
	except:
		expanded_tree_view = "Yes"

	try:
		ses_group_assytree_in_name_flag = SettingsD['PatranGroups']['AssemblyTreeInName']
	except:
		ses_group_assytree_in_name_flag = "False"

	LevL = [0] * 100
	prefixD = {}
	# create top level prefix
	# top_bdf=startpath + '.bdf'
	prefixD[top_bdf.replace('\\', '/')] = '0 ' + os.path.basename(startpath)
	checkRootD = {}
	# old_level=0
	f_strL = []
	# top_bdf_mtime=os.path.getmtime(top_bdf)
	for root, dirs, files in os.walk(startpath):
		# need to understand if root can have other mtime than latest added files "YES IT CAN, SHIT"

		root_ok_flag = False
		for item in AllFilesL:
			if item.find(root) > -1:
				root_ok_flag = True
				break

		if root_ok_flag == True:
			# print "root is ok"
			# print "root: " + root + " has the right mtime"
			# print "Original files"
			# print files
			new_files = []
			for file in files:
				# file_mtime=os.path.getmtime(root + '\\' + file)
				# print file + " modified:  %s" % time.ctime(file_mtime)
				# delta_time=file_mtime - top_bdf_mtime
				# print abs(delta_time)
				if (root + '\\' + file) in AllFilesL:
					# if abs(delta_time) < 100:
					# print "tjohoo"
					new_files.append(file)
				else:
					print("File: " + file + " is not found in  AllFilesL")

			# new_files contain only files with the right time stamp

			level = root.replace(startpath, '').count(os.sep)

			# if new level, add level to LevL
			# if level >= old_level:
			tmp_str = '.'.join([str(i) for i in LevL[:level + 1]])
			if root not in list(checkRootD.keys()):
				checkRootD[root] = tmp_str
				# reset level beneath
				LevL[level + 1] = 1
				# increase counter for existing level
				LevL[level] = LevL[level] + 1
			else:
				tmp_str = checkRootD[root]

			# the indentation character will decide how the lists are sorted in Python





			print("################################################")
			# print indent_char

			# print "level: " + str(level)
			# indent = '_' * ses_group_no_of_blanks * (level)
			indent = indent_char * ses_group_no_of_blanks * (level)
			# root_str= str(level) +  " " + os.path.basename(root)
			# print('{}{}/'.format(indent, root_str))

			# subindent = '_' * ses_group_no_of_blanks * (level + 1)
			subindent = indent_char * ses_group_no_of_blanks * (level + 1)
			q = 0
			# print "====="
			# organize files so that files with name Material or Property are last in the list

			tmp_files = []
			mat_prop_files = []
			for f in new_files:
				if f.find('Materials') >= 0 or f.find('Properties') >= 0 or f.find('Loads_Constraints') >= 0:
					mat_prop_files.append(f)
				else:
					tmp_files.append(f)

			new_files = tmp_files + mat_prop_files

			for f in new_files:
				q += 1
				tmp_file_str = tmp_str + "." + str(q)
				tmp_file = root + "\\" + f

				f_str = tmp_file_str + " " + f
				f_strL.append(f_str)
				print(('{}{}'.format(subindent, f_str) + "    " + root))
				prefixD[tmp_file.replace('\\', '/')] = f_str.replace(".bdf", "")
				# print "------"

				# finally add the path to checkRootD
				if tmp_file.replace(".bdf", "") not in list(checkRootD.keys()):
					# print "added: " + f_str[:-4]
					checkRootD[tmp_file.replace(".bdf", "")] = tmp_file_str

				# old_level=level
		else:
			print(root + " is older than exported file")
		# print "mtime              :  %s" % time.ctime(os.path.getmtime(root))
		# print "top bdf mtime :  %s" % time.ctime(top_bdf_mtime)
		# print os.path.getmtime(root) - top_bdf_mtime

	# print " =========prefixD========="
	# print 	prefixD

	aaa = []
	# for key in checkRootD.keys():
	#	aaa.append(checkRootD[key])
	#	print key + "       " + checkRootD[key]

	# check depth of structure
	n = 0
	for key in list(prefixD.keys()):
		n = max(n, prefixD[key].count('.'))
	N = n + 1
	print("Assembly Tree Depth: " + str(N))

	for f in sorted(f_strL):
		# print f
		aaa.append(f.split()[0])

	replaceD = {}
	n = 0

	while n < N:
		# check all levels if there are more than 9 itmes
		# then, rename with leading zero
		m = 0
		while m < len(aaa):
			# print "n: " + str(n) + " m: " + str(m)
			a = aaa[m]
			# key=prefixD.keys()[m]
			# a=prefixD[key].split()[0]
			tmpL = a.split('.')
			if len(tmpL) > n:
				tmp = tmpL[n]
				try:
					ddd = float(tmp)
				except:
					print("failed: " + tmp)

				if float(tmp) > 9:
					# print tmp + "  ###  " + a
					# delete all keys 1-9 and replace with 01-09
					for q in range(1, 10):
						tmp_str = '.'.join([str(i) for i in tmpL[:n]]) + '.' + str(q) + '.' + '.'.join(
							[str(i) for i in tmpL[n + 1:]])
						if tmp_str[-1] == '.':
							tmp_str = tmp_str[:-1]

						if tmp_str in aaa:
							# #if tmp_str in PrefixD.values():
							# # look for tmp_str in PrefixD, find corresponding key so it can be changed
							# for key_path, value_prefix in prefixD.items():
							# snor=tmp_str + ' ' + prefixD[key].split()[1]
							# #if value_prefix== tmp_str + ' ' + prefixD[key].split(' ')[1]
							# if value_prefix==snor:
							# tmp_key=key_path
							# print "#####   found #####"
							# else:
							# print snor
							# print value_prefix
							# print "------------"

							aaa.remove(tmp_str)
							new_str = '.'.join([str(i) for i in tmpL[:n]]) + '.0' + str(q) + '.' + '.'.join(
								[str(i) for i in tmpL[n + 1:]])
							if new_str[-1] == '.':
								new_str = new_str[:-1]
							# prefixD[tmp_key]=new_str
							aaa.append(new_str)
						# print new_str
				m += 1
			else:
				m += 1
		n += 1

	# map new prefix to existing Prefix D:
	newPrefixdD = {}
	for a in aaa:
		tmpL = a.split('.')
		new_a = '.'.join(['%d' % (int(i)) for i in tmpL])
		newPrefixdD[new_a] = a

	# for key in newPrefixdD.keys():
	#	print key + "        " + newPrefixdD[key]

	for key in list(prefixD.keys()):
		prefix = prefixD[key].split()[0]
		if prefix in list(newPrefixdD.keys()):
			# prefixD[key]=newPrefixdD[prefix] + ' ' + prefixD[key].split()[1]
			# add the subindent
			subindent = indent_char * ses_group_no_of_blanks * newPrefixdD[prefix].count('.')
			# print ('{}{}'.format(subindent, f_str) + "    " + root)
			if ses_group_assytree_in_name_flag.upper() == "YES":
				subindent = indent_char * ses_group_no_of_blanks * newPrefixdD[prefix].count('.')
				if expanded_tree_view == "No":
					f_str = newPrefixdD[prefix] + ' ' + prefixD[key].split()[1]
					prefixD[key] = '{}{}'.format(subindent, f_str)
				else:
					f_str = prefixD[key].split()[1]
					prefixD[key] = newPrefixdD[prefix] + subindent + f_str
					# byt ut blanksmot underscore om 

			else:
				subindent = '_' * ses_group_no_of_blanks * newPrefixdD[prefix].count('.')
				f_str = prefixD[key].split()[1]
				# prefixD[key]='{}{}'.format(subindent, f_str)
				prefixD[key] = subindent + f_str
				print(prefixD[key])


		else:
			# print "What"
			# print prefix
			print("Root: " + prefix + " " + key + " " + prefixD[key])

	# print key  + "     " +  prefixD[key]

	# for item in prefixD.keys():
	#	print item  + "     " +  prefixD[item]
	#print "###Haaaar ni prefix eller"
	#print prefixD
	return prefixD

def old_CreatePrefix(startpath, top_bdf):
	# assume no more than 100 levels
	LevL = [0] * 100
	prefixD = {}
	# create top level prefix
	# top_bdf=startpath + '.bdf'
	prefixD[top_bdf.replace('\\', '/')] = '1 ' + os.path.basename(startpath)

	for root, dirs, files in os.walk(startpath):
		level = root.replace(startpath, '').count(os.sep)
		# if new level, add level to LevL
		LevL[level] = LevL[level] + 1
		# print "level: " + str(level)
		indent = ' ' * 4 * (level)
		root_str = str(level) + os.path.basename(root)
		print(('{}{}/'.format(indent, root_str)))
		subindent = ' ' * 4 * (level + 1)
		q = 0
		# print "====="
		# organize files so that files with name Material or Property are last in the list

		tmp_files = []
		mat_prop_files = []
		for f in files:
			if f.find('Materials') >= 0 or f.find('Properties') >= 0:
				mat_prop_files.append(f)
			else:
				tmp_files.append(f)

		files = tmp_files + mat_prop_files

		for f in files:
			q += 1
			tmp_str = '.'.join([str(i) for i in LevL[:level + 1]]) + "." + str(q)
			tmp_file = root + "\\" + f
			# print tmp_file
			f_str = tmp_str + " " + f
			print(('{}{}'.format(subindent, f_str)))
			prefixD[tmp_file.replace('\\', '/')] = f_str.replace(".bdf", "")
		# print "------"
	return prefixD

def FindAssyFiles(startpath, root_dir):
	# print "startpath " + startpath
	# print "root_dir " + root_dir
	tmp_partL = []
	for root, dirs, files in os.walk(startpath):
		level = root.replace(startpath, '').count(os.sep)
		for f in files:
			tmp_file = root + "\\" + f
			if root.find(root_dir) >= 0:
				tmp_partL.append(tmp_file)
			# print "Added file: " + tmp_file
			# else:
			#	print "root dir: " + root_dir + " is not found in  root : " + root
			#	print "file not added: " + tmp_file
	return tmp_partL

def read_hierarchy(top_bdf_file, settingsD={}):
	"""reads a hierarcy of include files and outputs a Dictionary with all ingoing parts
	"""
	print("Top bdf file: " + top_bdf_file)
	print("=============")
	print("Reading hierarchy from top most bdf... " + top_bdf_file)
	dir_name = os.path.dirname(top_bdf_file)
	base_name = os.path.basename(top_bdf_file)
	# print base_name
	# print dir_name
	# Need to get the Top Level dir name, there should be just one:

	tmp_h = open(top_bdf_file, 'r')
	lines = tmp_h.readlines()
	tmp_h.close()
	
	# if there is a solxxx in first 20 lines... this is a study

	study_flag=False
	for line in lines[:20]:
		if line.upper().find('SOL') == 0:
			study_flag=True
			print("Study exported")
			break
	
	
	
	# Modify to account for scenario where line is too long (and broken)
	#FirstLevelDirName = ""
	#for line in lines:
	#	if line.find('INCLUDE') == 0 or line.find('include') == 0:
	#		FirstLevelDirName = os.path.dirname(line.split(' ')[1].replace("'", ""))
	#		break
	
	#Change May1 19th 2018
	FirstLevelDirName = ""
	N=len(lines)
	n=0
	while n<N:
		line=lines[n]
		if line.find('INCLUDE') == 0 or line.find('include') == 0:
			#find lines with just one "'", this shows an include line is spllit over two lines
			#assumes no more than one line is used... may fail for very deep paths
			if line.count("'") == 1:
				line=line.strip() + lines[n+1].strip()
				print(("Concatendated long line: " + line))
			FirstLevelDirName = os.path.dirname(line.split(' ')[1].replace("'", ""))
			break		
		n+=1	
			
	
	# print "First level dir: " + FirstLevelDirName
	FirstLevelDirPath = dir_name + '\\' + FirstLevelDirName
	print("First level dir path: " + FirstLevelDirPath)

	incL = []
	D = {}
	tmpL = extract_assys(top_bdf_file, dir_name)
	print("this is the first extract_assys: printing tmpL")
	print(top_bdf_file)
	print(tmpL)

	incL.append(tmpL)
	D[top_bdf_file] = tmpL
	prefixD = {}
	n = 1
	goFlag = 1

	AllFilesL = []
	AllFilesL.append(top_bdf_file)

	print("############# INC ############")
	for item in incL:
		print(item)
	print("############# INC ############")

	while goFlag == 1:
		# print "------start of loop " + str(n) + " no of items in list " +  str(len(incL))
		# for item in incL:
		#	print item
		# check if first file contains any nested files
		# print "####"
		# print incL[n-1]
		tmp_incL = incL.pop()
		assy_partL = tmp_incL[0]
		assy_fileL = tmp_incL[1]
		# assy_partL=incL[n-1][0]
		# assy_fileL=incL[n-1][1]
		# print "##Primary  pass##"
		# print "Printing assy_partL"
		# print assy_partL
		# print "Printing assy_fileL"
		# print assy_fileL
		# print "####"

		for part in assy_partL:
			if part not in AllFilesL:
				AllFilesL.append(part)

		if goFlag == 1:
			for bdf_file in assy_fileL:
				tmpL = extract_assys(bdf_file, dir_name)
				incL.append(tmpL)
				D[bdf_file] = tmpL
				if bdf_file not in AllFilesL:
					AllFilesL.append(bdf_file)

			n += 1

		if len(incL) == 0:
			goFlag = 0

	print("------end of loop reading include files---")

	# print "=============AllFilesL======================"
	# for item in AllFilesL:
	#	print item
	# print "============================================"

	# print "D items"

	check_sum = 0
	for item in list(D.keys()):
		# print item
		# print D[item]
		for sub_item in D[item]:
			if sub_item != []:
				check_sum += 1

	# print "Check sum: " + str(check_sum)
	# DD['top']=D
	# DD.close()


	D_parts = {}
	prefixD = []
	new_tmp = ''

	if check_sum != 0:
		# Add all files to be merged to a flat bdf file
		FlatFilesL = []
		# goFlag=1
		n = 0
		bdf_list = [top_bdf_file]
		# all_bdf_list=[]
		print(" ##################")
		print("FirstLevelDirPath: " + FirstLevelDirPath)
		print("top_bdf_file :" + top_bdf_file)
		print(" ##################")
		prefixD = CreatePrefix(FirstLevelDirPath, top_bdf_file, settingsD, AllFilesL)
		# print "prefixD"
		# create a dict with prefixes as key... this is used to merge bdfs in the right order
		prefixD_rev = {}
		for key in list(prefixD.keys()):
			# print prefixD[key] + " ; " +  key
			prefixD_rev[prefixD[key]] = key
		# print "FlatFilesL"
		FlatFilesL = list(prefixD.keys())

		# for aaa in FlatFilesL:
		#	print aaa


		# for key in prefixD.keys():
		#	print key
		#	print prefixD[key]

		print(" ==============================")

		# find all parts in each  Assy
		# this is needed for creating the groups

		for key in list(D.keys()):
			# print "### key ###"
			# print "key.replace('//','\\')"  + key.replace('//','\\')

			# print "top_bdf_file[:-4]" + top_bdf_file[:-4]
			# print "FirstLevelDirPath" + FirstLevelDirPath
			# need to test this!!
			new_key = os.path.dirname(key) + '//' + os.path.basename(key).replace(' ', '_')

			tmp_partL = FindAssyFiles(FirstLevelDirPath, new_key.replace('//', '\\').replace('.bdf', ''))
			# tmp_partL=FindAssyFiles(top_bdf_file[:-4],key.replace('//','\\').replace('.bdf',''))
			# print "## len ##"
			# print len(tmp_partL)

			# Change on June 28th

			# print "------Check June 28 change"
			# print tmp_partL
			# print D[key][0]
			# print "---------end"
			for item in D[key][0]:
				if item not in tmp_partL:
					tmp_partL.append(item)
			D_parts[key] = tmp_partL

		# print "#####after meriging of top parts#####"
		# print key
		# print D_parts[key]

		new_tmp = os.path.dirname(top_bdf_file) + '\\new_' + os.path.basename(top_bdf_file)

		# outf=open(top_bdf_file,'w')
		outf = open(new_tmp, 'w')
		
		# ensure BEGIN BULK comes first if no bulkdata is exported.
		if not study_flag:
			outf.write('BEGIN BULK\n')
		print("========Study flag==========",study_flag)
			

		for prefix in sorted(prefixD_rev.keys()):
			item = prefixD_rev[prefix]

			# for item in FlatFilesL:
			# print "Merging: " + item
			tmp_h = open(item, 'r')
			lines = tmp_h.readlines()
			tmp_h.close()
			outf.write("$ Merging file:  " + item)
			if study_flag:
				for line in lines:
					if line.find('include') < 0 and line.find('ENDDATA') < 0 and line.count("'") != 1:
						outf.write(line)
			else:
				for line in lines:
					if line.find('include') < 0 and line.find('ENDDATA') < 0 and line.count("'") != 1 and line.find('BEGIN BULK') < 0 :
						outf.write(line)

		outf.write('ENDDATA\n')
		outf.close()
	else:
		print("###Warning###  There are no include files found, no patran groups  can be created")
	return D_parts, prefixD, new_tmp, top_bdf_file

def extract_assys(bdf_file, dir_name):
	# read existing bdf file
	print(" (extract_assys: reading... " + bdf_file)
	bdf_h = open(bdf_file, 'r')
	bdf_lines = bdf_h.readlines()
	bdf_h.close()
	IncludeL = []
	AssyL = []
	PartL = []  # also mat and prop files will be contained in this list
	IncludeFileL = []
	n = 0
	while n < len(bdf_lines):
		# for line in bdf_lines:
		line = bdf_lines[n]
		if line.find("include") == 0:
			while line.strip()[-1] != "'":
				line = line.strip() + bdf_lines[n + 1].strip()
				n += 1
			# print n
			# print line
			# this is a filename spanning multiple lines"

			IncludeFileL.append(dir_name + "\\" + line.split("'")[1])
		n += 1
	# find out if the include files themselves contains nested assembliues
	for file in IncludeFileL:
		file_h = open(file, 'r')
		lines = file_h.readlines()
		file_h.close()
		AssyFlag = 0
		for line in lines:
			if line.find("include") == 0:
				AssyFlag = 1
		if AssyFlag == 0:
			PartL.append(file)
		else:
			AssyL.append(file)
	IncludeL = [PartL, AssyL]
	return IncludeL

def create_patran_group_ses(bdf_file, AssyD, prefix_D, SettingsD):
	# first, extract all parts from AssyD:
	ses_file = os.path.dirname(bdf_file) + "//create_groups_" + os.path.basename(bdf_file)[:-3] + "ses"
	print("session file name: " + ses_file)
	print('---')

	# for key in AssyD.keys():
	#	print key
	#	print AssyD[key]
	#	print "---#"

	ses_h = open(ses_file, 'w')

	PartL = []
	for key in list(AssyD.keys()):
		for file in AssyD[key]:
			if file not in PartL and file not in list(AssyD.keys()):
				PartL.append(file)

	# print "###PartL####"
	# print PartL
	# for file in PartL:
	#	print file

	# write session files from parts
	write_ses_file_from_bdf_list(ses_h, PartL, prefix_D, SettingsD, True)
	# write session files from all assemblies
	# AssyD[key] contatins all parts and subassemblies for the assmbly in question

	# print "AssuD"
	# print AssyD



	for key in list(AssyD.keys()):
		# add the assy file itself to the end of the list of subassys
		tmp_list = AssyD[key]
		tmp_list.append(key)
		write_ses_file_from_bdf_list(ses_h, tmp_list, prefix_D, SettingsD, False)
	ses_h.close()
	return ses_file

def write_ses_file_from_bdf_list(ses_h, PartL, prefix_D, SettingsD, PartFlag=True):	
	# print "Processing write_ses_from_bdf_list"
	# print len(PartL)
	CreateGroups = False
	#print SettingsD
	print(SettingsD['System']['CreatePatranGroups'])
	if 'PatranGroups' in SettingsD:
		if 'CreatePatranGroups' in SettingsD['System']:
			if SettingsD['System']['CreatePatranGroups'].upper() == "YES" or SettingsD['System']['CreatePatranGroups'].upper() == "TRUE":
				CreateGroups = True

	# print " ###########create groups #######################"
	if PartFlag != True:
		# PartL is content of files in a subassembly Add all Eids in one list prior to creating group
		# Assy name is stored as the last  element of the list
		EidL = []
		GidL = []

		for part_bdf in PartL:
			part_name = os.path.basename(part_bdf)[:-4]
			part_file_h = open(part_bdf, 'r')
			lines = part_file_h.readlines()
			part_file_h.close()
			for line in lines:
				if line.find("C") == 0 and len(line) >= 40:
					EidL.append(int(line[8:16].strip()))
				elif line.find("GRID") == 0:
					GidL.append(int(line[8:16].strip()))
		# retin only the TopAssy in the list	(last element)
		del PartL[:-1]

	# debug
	# print "#2###"
	# for key in prefix_D.keys():
	#	print key

	for part_bdf in PartL:
		if PartFlag == True:
			#print "### Processing Part " + part_bdf
			# make one EidL per part
			part_name = os.path.basename(part_bdf)[:-4]
			part_file_h = open(part_bdf, 'r')
			lines = part_file_h.readlines()
			part_file_h.close()
			EidL = []
			GidL = []
			for line in lines:
				if line.find("C") == 0 and len(line) >= 40:
					EidL.append(int(line[8:16].strip()))
				elif line.find("GRID") == 0:
					GidL.append(int(line[8:16].strip()))				

		if len(EidL) > 0:
			# sort element list prior to checking
			EidL = sorted(EidL)
			if CreateGroups == True:
				part_bdf = part_bdf.replace("\\", "/").replace('//', '/')
				part_name = prefix_D[part_bdf]

			# max group name length is 44 (?)
			if len(part_name) > 44:
				part_name = part_name[:44]

			ses_h.write('ga_group_create( "' + part_name + '" )\n')
			
			# check if all eids are sequential
			diff = (EidL[-1]) - (EidL[0])
			lgth = len(EidL)
			if (EidL[-1]) - (EidL[0]) == len(EidL) - 1:
				ses_h.write('ga_group_entity_add( "' + part_name + '", "Elm ' + str(EidL[0]) + ':' + str(EidL[-1]) + '" )\n')
			else:
				# print "not sequential"
				tmp_str = ""
				n = 0
				m = 1
				tmp_1 = EidL[0]
				tmp_0 = EidL[n]
				while n < len(EidL) - 1:
					if EidL[n + 1] == int(tmp_0) + m:
						tmp_1 = EidL[n + 1]
						m += 1
						# if in sequence and end of string... write
						if n == len(EidL) - 2:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
					else:
						# check if there is a sequence to write out"
						# print "no, not in series"
						if tmp_1 - tmp_0 >= 1:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
						else:
							tmp_str = tmp_str + " " + str(tmp_0)
						# reset m
						m = 1
						tmp_0 = EidL[n + 1]
					n += 1

				if tmp_str.split(' ')[-1].split(':')[-1] != str(EidL[-1]):
					tmp_str = tmp_str + " " + str(EidL[-1])
				# print "adding last element id (not in range) "
				# remove leading and trailing blanks:
				tmp_str = tmp_str.strip()
				ses_h.write('ga_group_entity_add( "' + part_name + '", "Elm ')

				cut_away_chunk = 44 - len(part_name)
				if len(tmp_str) <= cut_away_chunk:
					ses_h.write(tmp_str + '" )\n')
				else:
					# write first cut_away_chunk   chars... then  strip  the first cut_away_chunk characters of tmp_str
					ses_h.write(tmp_str[:cut_away_chunk] + '" // @\n')
					tmp_str = tmp_str[cut_away_chunk:]
					# now divide the rest of the string in chunks of 73 chars
					chunk_size = 73
					chunks = len(tmp_str)
					chunkL = [tmp_str[i:i + chunk_size] for i in range(0, chunks, chunk_size)]
					r = 0
					while r < len(chunkL):
						chunk = chunkL[r]
						if r < len(chunkL) - 1:
							ses_h.write('"' + chunk + '" // @\n')
						else:
							ses_h.write('"' + chunk + '" )\n')
						r += 1
#====  Add Grids to set
		if len(GidL) > 0:
			# sort element list prior to checking

			GidL = sorted(GidL)
			#if CreateGroups == True:
			#	part_bdf = part_bdf.replace("\\", "/").replace('//', '/')
			#	part_name = prefix_D[part_bdf]

			# max group name length is 44 (?)
			#if len(part_name) > 44:
			#	part_name = part_name[:44]
				
			# Set already exists
			#ses_h.write('ga_group_create( "' + part_name + '" )\n')
			
			# check if all eids are sequential
			diff = (GidL[-1]) - (GidL[0])
			lgth = len(GidL)
			if (GidL[-1]) - (GidL[0]) == len(GidL) - 1:
				ses_h.write('ga_group_entity_add( "' + part_name + '", "Node ' + str(GidL[0]) + ':' + str(GidL[-1]) + '" )\n')
			else:
				# print "not sequential"
				tmp_str = ""
				n = 0
				m = 1
				tmp_1 = GidL[0]
				tmp_0 = GidL[n]
				while n < len(GidL) - 1:
					if GidL[n + 1] == int(tmp_0) + m:
						tmp_1 = GidL[n + 1]
						m += 1
						# if in sequence and end of string... write
						if n == len(GidL) - 2:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
					else:
						# check if there is a sequence to write out"
						# print "no, not in series"
						if tmp_1 - tmp_0 >= 1:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
						else:
							tmp_str = tmp_str + " " + str(tmp_0)
						# reset m
						m = 1
						tmp_0 = GidL[n + 1]
					n += 1

				if tmp_str.split(' ')[-1].split(':')[-1] != str(GidL[-1]):
					tmp_str = tmp_str + " " + str(GidL[-1])
				# print "adding last element id (not in range) "
				# remove leading and trailing blanks:
				tmp_str = tmp_str.strip()
				ses_h.write('ga_group_entity_add( "' + part_name + '", "Node ')

				cut_away_chunk = 44 - len(part_name)
				if len(tmp_str) <= cut_away_chunk:
					ses_h.write(tmp_str + '" )\n')
				else:
					# write first cut_away_chunk   chars... then  strip  the first cut_away_chunk characters of tmp_str
					ses_h.write(tmp_str[:cut_away_chunk] + '" // @\n')
					tmp_str = tmp_str[cut_away_chunk:]
					# now divide the rest of the string in chunks of 73 chars
					chunk_size = 73
					chunks = len(tmp_str)
					chunkL = [tmp_str[i:i + chunk_size] for i in range(0, chunks, chunk_size)]
					r = 0
					while r < len(chunkL):
						chunk = chunkL[r]
						if r < len(chunkL) - 1:
							ses_h.write('"' + chunk + '" // @\n')
						else:
							ses_h.write('"' + chunk + '" )\n')
						r += 1

#====

	return
		
def old_write_ses_file_from_bdf_list(ses_h, PartL, prefix_D, SettingsD, PartFlag=True):
	# print "Processing write_ses_from_bdf_list"
	# print len(PartL)
	CreateGroups = False
	#print SettingsD
	print(SettingsD['System']['CreatePatranGroups'])
	if 'PatranGroups' in SettingsD:
		if 'CreatePatranGroups' in SettingsD['System']:
			if SettingsD['System']['CreatePatranGroups'].upper() == "YES" or SettingsD['System']['CreatePatranGroups'].upper() == "TRUE":
				CreateGroups = True
				#print "CreateGroups = True"

	# print " ###########create groups #######################"
	if PartFlag != True:
		# PartL is content of files in a subassembly Add all Eids in one list prior to creating group
		# Assy name is stored as the last  element of the list
		EidL = []

		# debug
		# if PartL[-1].find('Frames_SB.bdf') > 0:
		#	print "####### list PartL for Frames_SB.bdf"
		#	for ss in  PartL:
		#		print ss
		# print "Processing Assembly: " + PartL[0] + " or perhaps ... " + PartL[-1]
		# print PartL
		# print "No of parts in Assy:" + str(len(PartL) - 1)

		for part_bdf in PartL:
			part_name = os.path.basename(part_bdf)[:-4]
			part_file_h = open(part_bdf, 'r')
			lines = part_file_h.readlines()
			part_file_h.close()
			for line in lines:
				if line.find("C") == 0 and len(line) >= 40:
					EidL.append(int(line[8:16].strip()))

		# retin only the TopAssy in the list	(last element)
		del PartL[:-1]

	# debug
	# print "#2###"
	# for key in prefix_D.keys():
	#	print key

	for part_bdf in PartL:
		if PartFlag == True:
			#print "### Processing Part " + part_bdf
			# make one EidL per part
			part_name = os.path.basename(part_bdf)[:-4]
			part_file_h = open(part_bdf, 'r')
			lines = part_file_h.readlines()
			part_file_h.close()
			EidL = []

			for line in lines:
				if line.find("C") == 0 and len(line) >= 40:
					EidL.append(int(line[8:16].strip()))
		# else:
		#	# input is a subassembly, EidL is already created and PartL is just one file
		#	print "create group from assy"


		if len(EidL) > 0:
			# mess up Eid in order to form long lists as a check
			# EidL = EidL[0::10]

			# sort element list prior to checking
			EidL = sorted(EidL)
			# print EidL[0]
			# print EidL[-1]
			#print "### " + part_name

			if CreateGroups == True:
				# debug
				#print "#1a###"
				#print prefix_D.keys()

				if 1 == 1:
					#print " I am in write_ses_file 1a"
					part_bdf = part_bdf.replace("\\", "/").replace('//', '/')
					#print "part_bdf; " + part_bdf
					part_name = prefix_D[part_bdf]
					#print "part_name: " + part_name

				# except:
				#	part_name="miss" + part_name
			# max group name length is 44 (?)
			if len(part_name) > 44:
				part_name = part_name[:44]

			ses_h.write('ga_group_create( "' + part_name + '" )\n')

			# check if all eids are sequential
			diff = (EidL[-1]) - (EidL[0])
			lgth = len(EidL)
			# print "Max Eid - Min Eid: " + str(diff)
			# print "No of elements in file: "  + str(lgth)

			if (EidL[-1]) - (EidL[0]) == len(EidL) - 1:

				#
				# print "list is sequential"
				# print str(EidL[0]) + ':' + str(EidL[-1])
				# print "-----------------------"
				ses_h.write(
					'ga_group_entity_add( "' + part_name + '", "Elm ' + str(EidL[0]) + ':' + str(EidL[-1]) + '" )\n')

			else:
				# print "not sequential"
				tmp_str = ""
				n = 0
				m = 1
				tmp_1 = EidL[0]
				tmp_0 = EidL[n]
				while n < len(EidL) - 1:
					# tmp_0=EidL[n]
					# check if next id is in sequence
					# print EidL[n+1] + " " + tmp_0 + " " + str(m)
					# print str(int(tmp_0) + m)
					if EidL[n + 1] == int(tmp_0) + m:

						# if part_name=="Part_3" and tmp_0==2292:
						#	print "ok...in a serie ..." + str(EidL[n+1])
						tmp_1 = EidL[n + 1]
						m += 1
						# if in sequence and end of string... write
						if n == len(EidL) - 2:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
					else:
						# check if there is a sequence to write out"
						# print "no, not in series"
						if tmp_1 - tmp_0 >= 1:
							tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)

						else:
							# tmp_str=tmp_str + " " + str(tmp_0) + " " +  str(tmp_1)
							tmp_str = tmp_str + " " + str(tmp_0)
						# reset m
						m = 1
						tmp_0 = EidL[n + 1]
					# print "new tmp_0: " +  str(tmp_0)
					n += 1

				if tmp_str.split(' ')[-1].split(':')[-1] != str(EidL[-1]):
					tmp_str = tmp_str + " " + str(EidL[-1])
				# print "adding last element id (not in range) "

				# debug print element set string
				# print tmp_str

				# remove leading and trailing blanks:
				tmp_str = tmp_str.strip()
				ses_h.write('ga_group_entity_add( "' + part_name + '", "Elm ')

				cut_away_chunk = 44 - len(part_name)
				if len(tmp_str) <= cut_away_chunk:
					ses_h.write(tmp_str + '" )\n')
				else:
					# write first cut_away_chunk   chars... then  strip  the first cut_away_chunk characters of tmp_str
					ses_h.write(tmp_str[:cut_away_chunk] + '" // @\n')
					tmp_str = tmp_str[cut_away_chunk:]
					# now divide the rest of the string in chunks of 73 chars
					chunk_size = 73
					chunks = len(tmp_str)
					chunkL = [tmp_str[i:i + chunk_size] for i in range(0, chunks, chunk_size)]
					r = 0
					while r < len(chunkL):
						chunk = chunkL[r]
						if r < len(chunkL) - 1:
							ses_h.write('"' + chunk + '" // @\n')
						else:
							ses_h.write('"' + chunk + '" )\n')
						r += 1

					# print "-----------------------"
					# else:
					#	print "EidL is empty"
	return

def getTieNodes(lines):
	# file_h=open(file,'r')
	# lines=file_h.readlines()
	# file_h.close()
	print("processing module getTieNodes")
	#
	TieNodeL = []
	RbarElemL = []
	n = 0
	while n < len(lines):
		if lines[n].find("Rigid Connectors for Edge Tie") > -1:
			n += 1
			while lines[n].find("RBAR") == 0:
				TieNodeL.append([int(lines[n][16:24]), int(lines[n][24:32])])
				RbarElemL.append(int(lines[n][8:16]))
				n += 1
		else:
			n += 1

	return TieNodeL, RbarElemL

def createMergeList(TieNodeL):
	print("....generating createMergeList")
	MergeList = []
	for pair in TieNodeL:
		n = 0
		found_flag = 0
		while n < len(MergeList):
			# check if any of the merge nodes connects
			if pair[0] in MergeList[n] or pair[1] in MergeList[n]:
				if pair[0] not in MergeList[n]:
					MergeList[n].append(pair[0])
				if pair[1] not in MergeList[n]:
					MergeList[n].append(pair[1])
				found_flag = 1
				break

			n += 1
		if found_flag == 0:
			MergeList.append(pair)

	# The Update Bdf requires a list of pairs, [ [replace_node1,keep_node1],[replace_node2,keep_node2] ]

	MergePairL = []
	for list in MergeList:
		for item in list[1:]:
			MergePairL.append([item, list[0]])

	return MergePairL

def getMass(check_out_f06):
	f06_h = open(check_out_f06, 'r')
	f06_lines = f06_h.readlines()
	f06_h.close()

	n = 0
	while n < len(f06_lines):
		if f06_lines[n].find(" MASS AXIS SYSTEM (S) ") > -1:
			mass_str = f06_lines[n + 1][47:60]
			mass = float(mass_str)
			xcog = float(f06_lines[n + 2][63:78])
			ycog = float(f06_lines[n + 1][78:92])
			zcog = float(f06_lines[n + 1][92:])
			break
		else:
			n += 1
	return mass, [xcog, ycog, zcog]

def getNoOfElems(check_out_f04):
	f04_h = open(check_out_f04, 'r')
	f04_lines = f04_h.readlines()
	f04_h.close()

	n = 0
	cardD = {}
	while n < len(f04_lines):
		if f04_lines[n].find("COUNT:ENTRY") > -1:
			while f04_lines[n + 1].find(r' *') == 0:
				# print f04_lines[n+1]
				n += 1
				tmpL = f04_lines[n][2:].split()
				#
				if f04_lines[n].count(':') == len(tmpL):

					for item in tmpL:
						key = item.split(':')[1]
						count = int(item.split(':')[0])
						cardD[key] = count
					# else:
					#	print f04_lines[n].count(':')
					#	print len(tmpL)

			n += 1
		else:
			n += 1
	return cardD

def process_generate_se(bdf_file, SettingsD):

	print(list(SettingsD.keys()))
	
	if 'NASTRAN_SE' in SettingsD:

		# RunNastran=True
		# NoOfModes=10
		# SuperElemGenTemplate=se_generate_template.txt
		# OutputType=DMIGOP2
		# DefaultSuperElemId=10





		if 'SuperElemGenTemplate' in SettingsD['NASTRAN_SE']:
			if os.path.isfile( os.path.dirname(__file__) + '\\' +SettingsD['NASTRAN_SE']['SuperElemGenTemplate']) :
				print("...relative path")
				template_file = os.path.dirname(__file__) + '\\' + SettingsD['NASTRAN_SE']['SuperElemGenTemplate']
			else:
				template_file = SettingsD['NASTRAN_SE']['SuperElemGenTemplate']
			try:
				execute_nastran = (SettingsD['NASTRAN_SE']['RunNastran'])
			except:
				execute_nastran = False

			try:
				output_type = (SettingsD['NASTRAN_SE']['OutputType'])
			except:
				output_type = 'DMIGOP2'

			try:
				reduction_type = (SettingsD['NASTRAN_SE']['ReductionType'])
			except:
				reduction_type = 'DMIGOP2'

			try:
				super_elem_start_id = int(SettingsD['NASTRAN_SE']['DefaultSuperElemId'])
			except:
				super_elem_start_id = 20

			try:
				dmigop2unit = int(SettingsD['NASTRAN_SE']['DefaultOp2Unit'])
			except:
				dmigop2unit = 26

			try:
				no_of_modes = int(SettingsD['NASTRAN_SE']['NoOfModes'])
			except:
				no_of_modes = 10

			try:
				SpcId = int(SettingsD['NASTRAN_JOB_SETUP']['SpcId'])
			except:
				SpcId = 888888

			name = os.path.splitext(os.path.basename(bdf_file))[0]
			print(name)
			nameL = name.split('_')
			print(nameL)
			try:
				seid = int(nameL[0].replace('se', ''))
				super_elem_start_id = seid
			except:
				print("use default seid")
			#
			try:
				unit = int(nameL[-1].replace('unit', ''))
				dmigop2unit = unit
			except:
				print("use default unit")

			# read existing bdf file
			print(" I am reading " + bdf_file)
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			# take out the Case Control Cards Added my Apex
			# if line.find('SOL 101') > -1 or line.find('CEND') > -1 or line.find('BCONTACT') > -1:
			try:
				bdf_lines.remove('SOL 101\n')
			except:
				pass
			try:
				bdf_lines.remove('SOL 103\n')
			except:
				pass

			try:
				bdf_lines.remove('CEND\n')
			except:
				pass
			try:
				bdf_lines.remove('	BCONTACT = 1\n')
			except:
				pass
			try:
				bdf_lines.remove('BEGIN BULK\n')
			except:
				pass
			# ForceStr=ReadForces(bdf_lines,LoadId)
			SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
			ConctactStrL, BConprgD = ReadContacts(bdf_lines)

			DisplaySet = ReadDisplayElems(bdf_lines, "Display.bdf")

			aset_nodes, asetD = find_aset_nodes(bdf_lines)
			# print aset_nodes
			print(asetD)
			# Find all nodes to find max node id
			NodeD = ReadGrids(bdf_lines)
			# print "NodeD"
			# print NodeD
			max_id = max(NodeD.keys())
			print("max id: " + str(max_id))

			# get all RBE3s  attached to an aset node (with or without zero mass)
			AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)
			AsetRbe2D = ReadRbe2s(bdf_lines, aset_nodes)
			#
			# Enter only if reduce RBE3 is active
			reduce_rbe3_flag = False
			if reduce_rbe3_flag == True:
				print("reducing Rbe3s")
				# reopen and read new
				# bdf_h=open(bdf_file,'r')
				# bdf_lines=bdf_h.readlines()
				# bdf_h.close()

				# now write back
				bdf_h = open(bdf_file, 'w')

				# dep id's are renumbered, use elem ids to find the RBEs to change AsetRbe3D[key][0]
				# print "###"
				# for key in AsetRbe3D.keys():
				#	print AsetRbe3D[key]
				# print "###"
				# if 1==1:
				#  nned a new dict form
				# AsetRbe3D=ReadRbe3s(bdf_lines,aset_nodes)
				# print "#New Aset RBE3D#"
				# for key in AsetRbe3D.keys():
				#	print AsetRbe3D[key]
				# print "###"

				n = 0
				while n < len(bdf_lines):
					if bdf_lines[n].find("RBE3 ") == 0:
						print("RBE3 type 1")
						# check so that RBE3 is one of the Aset RBE3s
						cont_card = True
						dep_id = int(bdf_lines[n][24:32])
						if dep_id in list(AsetRbe3D.keys()):
							print("Processing 1")
							lgth = len(AsetRbe3D[dep_id][1])
							if lgth > min_no_of_legs:
								if round(lgth * rbe3_reduce_ratio) > min_no_of_legs:
									skip_step = int(round(1. / rbe3_reduce_ratio))
									print("Rbe3 with id:" + str(
										AsetRbe3D[dep_id][0]) + " is larger than min length: " + str(
										lgth) + " vs " + str(min_no_of_legs))
								else:
									skip_step = int(math.floor(1. * lgth / min_no_of_legs))
									print("calculated new skip_step: " + str(skip_step))
									print("Rbe3 with id:" + str(AsetRbe3D[dep_id][
																	0]) + " needs new skip step as ratio time length is smaller than min_length: " + str(
										lgth) + " vs " + str(min_no_of_legs))

								indep_reduced = AsetRbe3D[dep_id][1][0::skip_step]

								# print out new RBE3
								# first line:
								i = 1
								j = 1
								bdf_h.write("%-56s" % (bdf_lines[n][0:56]))
								for indep in indep_reduced[:-2]:
									bdf_h.write("%-8s" % (indep))
									if (i == 1 and j == 2) or (i > 1 and j == 9):
										bdf_h.write("%-8s\n%-8s" % ("+", "+"))
										i += 1
										j = 1
									j += 1
								# write last item
								bdf_h.write("%-8s\n" % (indep_reduced[-1]))
								# check for continuation cards, starts with + or a blank or * (although we do not support large field formats for now)
								cont_card = True
								while cont_card == True:
									if bdf_lines[n + 1][0:1] == "+" or bdf_lines[n + 1][0:1] == " " or bdf_lines[n + 1][
																									   0:1] == "*":
										bdf_h.write("$" + bdf_lines[n + 1])
										n += 1
									else:
										cont_card = False  # read next card
										n += 1

							else:
								print("Dep id: " + str(dep_id) + " Length: " + str(lgth) + " min no of legs " + str(
									min_no_of_legs))
								for key in list(AsetRbe3D.keys()):
									print(AsetRbe3D[key])

								print("too few legs in RBE3... no reduction")
								bdf_h.write(bdf_lines[n])
								n += 1
						else:
							print("This is an ordinary RBE3")
							bdf_h.write(bdf_lines[n])
							n += 1
					else:
						bdf_h.write(bdf_lines[n])
						n += 1

				bdf_h.close()

			# regenerate bdf_lines with updated RBE3s
			bdf_h = open(bdf_file, 'r')
			bdf_lines = bdf_h.readlines()
			bdf_h.close()

			# also need new AsetRbe3D for PLOTELs
			AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)

			# read the mnf template lines
			if os.path.isfile(template_file):
				temp_file_h = open(template_file, 'r')
				template_lines = temp_file_h.readlines()
				temp_file_h.close()

			# write back

			print(" I am writing " + bdf_file)
			bdf_h = open(bdf_file, 'w')
			for line in template_lines:

				#  make sure mnf line and cont line is contained within 80 chars
				if line.find('ASSIGN OUTPUT2') == 0:
					#print "I am in 1"
					tmp_line = line.replace('UNIT=YY', 'UNIT=' + '%d' % (dmigop2unit)).replace('sexx', 'se' + '%d' % (
						super_elem_start_id))
					bdf_h.write(tmp_line.strip() + "\n")
				elif line.find('EXTSEOUT') == 0:
					tmp_line = line.replace('EXTID=xx', 'EXTID=' + '%d' % (super_elem_start_id)).replace('DMIGOP2=YY',
																										 'DMIGOP2=' + '%d' % (
																											 dmigop2unit))
					bdf_h.write(tmp_line.strip() + "\n")

				elif line.find('METHOD') > -1:
					# check if there is a contact table, then, write BCONTACT card just above subcase level
					if len(ConctactStrL) != 0:
						print("Generating ContactStrL")
						for lin in ConctactStrL:
							if lin[0:7] == 'BCTABL1':
								print(lin)
								BCTabLId = (int(lin[8:16]))
								bdf_h.write("%-8s = %d\n" % ("BCONTACT", BCTabLId))
					# end check BCTABLE
					bdf_h.write(line)
					# bdf_h.write("   LOAD=" + str(LoadId) + "\n")
					if len(SpcStr) != 0:
						bdf_h.write("   SPC=" + str(SpcId) + "\n")
					if len(ConctactStrL) == 1:
						bdf_h.write("   " + ConctactStrL[0])
					elif len(ConctactStrL) > 1:
						bdf_h.write("   " + ConctactStrL[0])
						bdf_h.write(
							"$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
				elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
					print("skip placeholder comment")


				elif line.find('BEGIN BULK') > -1:
					# check if there are plot-sets
					PlotelL = []
					if len(DisplaySet) != 0:
						# create PLOTELs for MPC legs
						elem_start = 8000001  # hardcoded
						n = 0
						for key in list(AsetRbe3D.keys()):
							for indep in AsetRbe3D[key][1]:
								# if asetD[indep][0] !=0 :
								#	indep=asetD[indep][0]
								PlotelL.append("%-8s%8d%8d%8d\n" % ("PLOTEL", elem_start + n, key, indep))
								n += 1
						for key in list(AsetRbe2D.keys()):
							for indep in AsetRbe2D[key][1]:
								# if asetD[indep][0] !=0 :
								#	indep=asetD[indep][0]
								PlotelL.append("%-8s%8d%8d%8d\n" % ("PLOTEL", elem_start + n, key, indep))
								n += 1

						# assume for now, that the output set is in a sequence
						bdf_h.write("OUTPUT(PLOT)\n")
						bdf_h.write("SET 9099= " + "%d%s%d" % (min(DisplaySet), " THRU ", max(DisplaySet)))
						# Add set for PLOTELs
						if len(PlotelL) != 0:
							bdf_h.write(",%d%s%d\n" % (elem_start, " THRU ", elem_start + (n - 1)))
						else:
							bdf_h.write("\n")

					bdf_h.write(line)
					# Obsolete - write Aset cards
					#write_ASET(aset_nodes,bdf_h)
					# write PLOTELs


					# write PARAMs
					# bdf_h.write("%-8s%8s%8s\n" % ("PARAM","AUTOQSET","YES"))
					# bdf_h.write("%-8s%8s%8d\n" % ("PARAM","GRDPNT",0))
					# added to mnf header file
					# bdf_h.write("DTI,UNITS,1,MGG,N,MM,S\n")
					# EIGRL to extract requested no of modes
					bdf_h.write("%-8s%8d%8s%8s%8d\n" % ("EIGRL", 100, "", "", no_of_modes))

					# bdf_h.write(ForceStr + "\n")
					bdf_h.write(SpcStr + "\n")




				else:
					bdf_h.write(line)

			replace_nodes = []

			MergeD = {}
			for aset_node in aset_nodes:
				MergeD[aset_node] = asetD[aset_node][0]
			# also need to replace any grids that are already taken

			for aset_node in aset_nodes:
				if asetD[aset_node][0] == aset_node:
					# no renumber for this
					print("Aset node with id: " + str(aset_node) + " doesn't need renumbering")
				# delete from MergeD change Aug 18th
				# del MergeD[aset_node]

				else:
					#print "I am in  2"
					#print asetD
					# check if new node is "taken" then assign new id to the taken node
					print(asetD[aset_node][0])
					print("===")
					if asetD[aset_node][0] in list(NodeD.keys()):
						#print "I am in 2b"
						max_id += 1
						MergeD[asetD[aset_node][0]] = max_id
						replace_nodes.append(asetD[aset_node][0])

					# for aset_node in aset_nodes:
				replace_nodes.append(aset_node)

			# append PloteL to bdf_lines, handle renumbering in MergeGrids
			# check if last line is an ENDDATA
			if bdf_lines[-1].find('ENDDATA') == 0:
				del bdf_lines[-1]
				bdf_lines.extend(PlotelL)
				bdf_lines.append('ENDDATA\n')
			else:
				bdf_lines.extend(PlotelL)

			# print replace_nodes
			# print MergeD

			# merge grids
			# print "aaa"
			# print MergeD
			print("#####replace nodes####")
			print(replace_nodes)
			#
			print("####MergeD####")
			print(MergeD)
			
			#test
			new_file = os.environ.get('TEMP') + '\\new_aaa.bdf'
			
			#new_file = r"C:\Temp\new_aaa.bdf"
			new_file_h = open(new_file, 'w')
			#
			for line in bdf_lines:
				new_file_h.write(line)
			new_file_h.close()
			#
			MergeGrids(bdf_h, bdf_lines, replace_nodes, MergeD)

			bdf_h.close()

			print("sol103 nastran cards added")
		else:
			print("No SE reduction template found")
	else:
		print("No Nastran Job Setup for S info in settings file")

	if execute_nastran == 'True':
		# apply the nastran options for mnf gerneatio
		xtra_args = []

		print("...running nastran")
		# xtra_args=[]
		out_msg = run_nastran(bdf_file, SettingsD, xtra_args)
	# out_msg=run_nastran(bdf_file)
	else:
		out_msg = "**** mnf_file is created"
	#
	return out_msg

def ReadGridsStr(inl1, FindNodeL=[]):
	# read also cp and cd put in list index 3 and 4
	# read grids store in dict with id as key followed by 	x	y	z	cp	cd
	#														0	1	2	3	4
	nodD = {}
	nodeStrD = {}
	for lin in inl1:
		if lin[0:5] == 'GRID ':
			NodeId = int(lin[8:16])
			if FindNodeL != []:  # Only add Nodes from FindNode-List
				if NodeId not in FindNodeL:
					continue
			#
			try:
				cp = int(lin[16:24])
			except:
				cp = 0
			try:
				cd = int(lin[48:56])
			except:
				cd = 0

			# Apex exports floats on the format aaa-b (that is, without exponential aaaE-b
			a = lin[24:32].strip()
			b = lin[32:40].strip()
			c = lin[40:48].strip()
			# x=a[0:1]+a[1:].replace('-','E-').replace('EE','E')
			# y=b[0:1]+b[1:].replace('-','E-').replace('EE','E')
			# z=c[0:1]+c[1:].replace('-','E-').replace('EE','E')
			nodD[NodeId] = [a, b, c]
	return nodD

def ReadChexas(inl1):
	ChexD = {}
	ChexPsolidD={}
	n = 0
	N = len(inl1)
	while n < N - 1:
		lin = inl1[n]
		lin2 = inl1[n + 1]
		if lin.find('CHEXA') == 0:
			ElemId = int(lin[8:16])
			ChexD[ElemId] = [int(lin[24:32]), int(lin[32:40]), int(lin[40:48]), int(lin[48:56]), int(lin[56:64]),
							 int(lin[64:72]), int(lin2[8:16]), int(lin2[16:24])]
			pid=int(lin[16:24])
			if pid not in ChexPsolidD:
				ChexPsolidD[pid]=[ElemId]
			else:
				ChexPsolidD[pid].append(ElemId)
							 
			n += 2
		else:
			n += 1
	return ChexD, ChexPsolidD

def ReadSolids(inl1):
	
	CsolidD = {}
	ChexPsolidD={}
	

	n = 0
	N = len(inl1)
	while n < N - 1:
		if inl1[n].find('CHEXA') == 0 or inl1[n].find('CTETRA') == 0 or inl1[n].find('PENTA') ==0:
			ElemId = int(inl1[n][8:16])
			pid=int(inl1[n][16:24])
			# first concatenate all  lines to one string, omit cont.
			_tmp_str=inl1[n][24:min(72,len(inl1[n]))]
			while inl1[n+1][0] == '+'  or  inl1[n+1][0] == ' ':
				n+=1
				_tmp_str+=inl1[n][8:min(72,len(inl1[n]))]
			# read _tmp_str in chunks of 8
			tmp_list=[_tmp_str[x:x+8] for x in range(0,len(_tmp_str),8)]
			CsolidD[ElemId]=[int(i.strip()) for i in tmp_list]
		
			if pid not in ChexPsolidD:
				ChexPsolidD[pid]=[ElemId]
			else:
				ChexPsolidD[pid].append(ElemId)
		else:
			n += 1
	return CsolidD, ChexPsolidD


def ReadCelems(inl1):
	# read CQUAD, CTRIA, CHEXA, CPENTA or CTETRA (elems that can be involved in contact) , excluding 2 noded and more exotic 2d elem types
	CElemD = {}
	n = 0
	N = len(inl1)
	while n < N - 1:
		lin = inl1[n]
		lin2 = inl1[n + 1]
		if lin.find('CQUAD') == 0 or lin.find('CTRIA') == 0 or lin.find('CTETRA') == 0 or lin.find(
				'CPENTA') == 0 or lin.find('CHEXA') == 0:
			elem_id = int(lin[8:16])
			tmp_nodeL = []
			i = 4  # first line
			while i <= 10:
				try:
					if inl1[n][(i - 1) * 8] != "+":
						# print inl1[n][(i-1)*8:(i-1)*8+8]
						tmp_nodeL.append(int(inl1[n][(i - 1) * 8:(i - 1) * 8 + 8]))
						i += 1
					else:
						n += 1  # encountered a continuation "+"
						i = 2
				except:
					n += 1
					break
			CElemD[elem_id] = tmp_nodeL
		else:
			n += 1
	return CElemD

def write_vtu_header(file_h):
	file_h.write('<?xml version="1.0"?>\n')
	file_h.write('<!--Input Deck Generated by Apex Grizzly Macro Version  : 0.0.1 /-->\n')
	file_h.write(
		'<VTKFile type="UnstructuredGrid" version="0.1" byte_order="LittleEndian" compressor="vtkZLibDataCompressor">\n')

def write_integers(file_h, list, no_per_line, indent=0):
	tmp = indent * " "
	n = 0
	m = 1
	N = len(list)
	while n < N:
		if n < N:
			if m < no_per_line:
				tmp = tmp + ("%d ") % (list[n])
				n += 1
				m += 1
			else:
				tmp = tmp + ("%d\n") % (list[n]) + indent * " "
				n += 1
				m = 1
	file_h.write(tmp + '\n')

def write_coordinates(file_h, cord_list, no_per_line, indent=0):
	tmp = indent * " "
	n = 0
	m = 1
	N = len(cord_list)
	while n < N:
		if n < N:
			if m < no_per_line:
				# -2.22045e-016
				tmp = tmp + ("%s %s %s ") % (cord_list[n][0], cord_list[n][1], cord_list[n][2])
				n += 1
				m += 1
			else:
				tmp = tmp + ("%s %s %s\n") % (cord_list[n][0], cord_list[n][1], cord_list[n][2]) + indent * " "
				n += 1
				m = 1

	file_h.write(tmp + '\n')

def write_vtu_bulk(file_h, GridD, ElemD):
	min_grid_id = min(GridD.keys())
	max_grid_id = max(GridD.keys())
	min_elem_id = min(GridD.keys())
	max_elem_id = max(GridD.keys())
	no_of_grids = len(list(GridD.keys()))
	no_of_elems = len(list(ElemD.keys()))

	# Start of generation of the VTU

	file_h.write('  <UnstructuredGrid>\n')
	file_h.write('    <FieldData>\n')
	file_h.write('      <DataArray type="Int32" Name="auto1" NumberOfTuples="' + str(
		no_of_elems) + ' " format="ascii" RangeMin="' + str(min_elem_id) + '" RangeMax="' + str(max_elem_id) + '">\n')
	write_integers(file_h, list(ElemD.keys()), 6, 8)
	file_h.write('      </DataArray>\n')
	file_h.write('    </FieldData>\n')
	file_h.write('    <Piece NumberOfPoints="' + str(no_of_grids) + '" NumberOfCells="' + str(no_of_elems) + '">\n')
	file_h.write('      <PointData>\n')
	file_h.write('        <DataArray type="Int32" Name="seqPointId" format="ascii" RangeMin="' + str(
		min_grid_id) + '" RangeMax="' + str(max_grid_id) + '">\n')
	write_integers(file_h, list(GridD.keys()), 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('      </PointData>\n')
	file_h.write('      <CellData>\n')
	file_h.write(
		'        <DataArray type="Int32" Name="MaterialId" format="ascii" RangeMin="' + "1" + '" RangeMax="' + "0" + '">\n')
	write_integers(file_h, [0] * no_of_elems, 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('        <DataArray type="Int32" Name="FormulationId" format="ascii" RangeMin="1" RangeMax="0">\n')
	write_integers(file_h, [0] * no_of_elems, 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('        <DataArray type="Int32" Name="seqCellId" format="ascii" RangeMin="' + str(
		min_elem_id) + '" RangeMax="' + str(max_elem_id) + '">\n')
	write_integers(file_h, list(ElemD.keys()), 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('      </CellData>\n')
	file_h.write('      <Points>\n')
	file_h.write(
		'        <DataArray type="Float32" NumberOfComponents="3" format="ascii" RangeMin="0.0" RangeMax="1.0">\n')
	cord_list = []
	for key in list(GridD.keys()):
		cord_list.append(GridD[key])
	write_coordinates(file_h, cord_list, 2, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('      </Points>\n')
	file_h.write('      <Cells>\n')
	file_h.write('        <DataArray type="Int32" Name="connectivity" format="ascii" RangeMin="' + str(
		min_grid_id) + '" RangeMax="' + str(max_grid_id) + '">\n')
	node_list = []
	for key in list(ElemD.keys()):
		node_list = node_list + ElemD[key]
	write_integers(file_h, node_list, 6, 8)
	file_h.write('        </DataArray>\n')
	range_len = len(node_list)
	offset_list = list(range(8, range_len + 1, 8))
	file_h.write('        <DataArray type="Int32" Name="offsets" format="ascii" RangeMin="' + str(
		min(offset_list)) + '" RangeMax="' + str(max(offset_list)) + '">\n')
	write_integers(file_h, offset_list, 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('        <DataArray type="UInt8" Name="types" format="ascii" RangeMin="12" RangeMax="12">\n')
	write_integers(file_h, [12] * no_of_elems, 6, 8)
	file_h.write('        </DataArray>\n')
	file_h.write('      </Cells>\n')
	file_h.write('      </Piece>\n')
	file_h.write('  </UnstructuredGrid>\n')
	file_h.write('</VTKFile>\n')

def process_bdf_vtu(bdf_full_name):
	# bdf="D:\Software\MSC Apex\Customers\Hutchinson\Vtu\cube_new.bdf"
	bdf_h = open(bdf_full_name, 'r')
	bdf_lines = bdf_h.readlines()
	bdf_h.close()
	vtu = bdf_full_name[:-4] + '.vtu'
	vtu_h = open(vtu, 'w')
	GridD = ReadGridsStr(bdf_lines)
	ElemD,PsolidD = ReadChexas(bdf_lines)
	# print GridD
	# print len(GridD.keys())
	# print ElemD
	write_vtu_header(vtu_h)
	write_vtu_bulk(vtu_h, GridD, ElemD)
	vtu_h.close()

def specify_out_file(temp_file):
	
	#os.system(temp_file)
	file_h=open(temp_file,'r')
	lines=file_h.readlines()
	file_h.close()
	out_file=""
	for line in lines:
		if line.find('#') != 0:
			if os.path.isfile(line.strip()):
				out_file=line.strip()
				break
	print("Read : " + temp_file)
	if out_file != "":
		print("Identified .out to import: " + 	out_file)
	else:
		print("Warning: Could not find a readable .out file in " + temp_file)
	
	return out_file
	
def write_ses_for_patran(ses_template_file,tmp_dir,bdf_file,hf5_file,NoOfSteps,close_flag=True,xtra_ses_file=""):
	# $OUT is the out file (in the temp diretory)
	# $DIR is the directory
	
	added_xtra_lines= False
	print("Extra session lines in file: " + xtra_ses_file)
	if os.path.isfile(xtra_ses_file):
		xtra_ses_file_h=open(xtra_ses_file,'r')
		xtra_lines=xtra_ses_file_h.readlines()
		xtra_ses_file_h.close()
	else:
		print("No xtra session file lines detected")
		xtra_lines=[]
	
	if NoOfSteps > 9:
		NoOfSteps=9
		print("Warning, no more than 9 steps currently supported")
	
	name_str=os.path.basename(hf5_file).split('.')[0]
	loadcase_str= "Step " + str(NoOfSteps)             #Step 3
	percentage_str= str(NoOfSteps) + "00. %"   #300. %
	ses_file=hf5_file[:-4] + ".ses"
	ses_file_h=open(ses_file,'w')
	template_h=open(ses_template_file,'r')
	template_lines=template_h.readlines()
	
	print("name: " + name_str)
	template_h.close()
	#for line in template_lines:
	
	#find out if there are solids or just shells
	bdf_h=open(bdf_file,'r')
	lines=bdf_h.readlines()
	bdf_h.close()
	SolidFlag=False
	for line in lines:
		if line.find("CTET") == 0:
			SolidFlag=True
			break
		elif line.find("CHEX") == 0:
			SolidFlag=True
			break

		elif line.find("CPENTA") == 0:
			SolidFlag=True
			break
		
	shell_only_lines=[
		'"A1:Non-linear: $PERCENTAGE of Load", "Stress Tensor", "", "At Z1", "VONM",  @\n',
		'"Default", "DeriveAverage", "All", "ShapeFunc" )\n',
		'res_data_dbres_list( 0, "Nodal", "Scalar", 1, ["SC1:$LOADCASE:"], [ @\n',
		'"A1:Non-linear: $PERCENTAGE of Load"], ["Stress Tensor"], [""], ["At Z2"] )\n',
		'res_data_list_max( 0, "Nodal", "Scalar", "Algebraic", "VONM" )\n',
		'res_data_title( 0, "Nodal", "Scalar", 1, [ @\n',
		'"$POFF@@@$PT: @@@$LCN, @@@$SCN, @@@$PRN, @@@$SRN, @@@$DRVL, 2 of 2 layers " // @\n',
		'"(Maximum)"] )\n']
	
	n=0
	while n < len(template_lines):
		line=template_lines[n]
		if line.find("res_data_load_dbresult") == 0 and SolidFlag == False:
			# write first line
			ses_file_h.write(line.replace("$DIR",tmp_dir).replace("$HF5",hf5_file).replace("$BDF",bdf_file).replace("$NAME",name_str).replace("$LOADCASE",loadcase_str).replace("$PERCENTAGE",percentage_str))
			# just write shell layers:
			for shell_line in shell_only_lines:
				ses_file_h.write(shell_line.replace("$LOADCASE",loadcase_str).replace("$PERCENTAGE",percentage_str))
			# skip next 9 lines
			n+=9
			# set SolidFlag to True to avoid enter this check a second time
			SolidFlag = True
		#	
		else:		
			ses_file_h.write(line.replace("$DIR",tmp_dir).replace("$HF5",hf5_file).replace("$BDF",bdf_file).replace("$NAME",name_str).replace("$LOADCASE",loadcase_str).replace("$PERCENTAGE",percentage_str))
			# insert xtra session file just after attachment of results

			
		if line.find("analysis_import") ==0 and len(xtra_lines) > 0:
			ses_file_h.write(template_lines[n+1].replace("$DIR",tmp_dir).replace("$HF5",hf5_file).replace("$BDF",bdf_file).replace("$NAME",name_str).replace("$LOADCASE",loadcase_str).replace("$PERCENTAGE",percentage_str))
			for xtra_line in xtra_lines:
				ses_file_h.write(xtra_line)
			added_xtra_lines=True
			ses_file_h.write("uil_imaging_coordframes.unpost_all(  )\n")
			ses_file_h.write("gu_fit_view(  )\n")
			n+=1
		n+=1
	# check so that xtra lines were added,if not append at end of file
	if added_xtra_lines== False:
		#append at end of file
		for xtra_line in xtra_lines:
			ses_file_h.write(xtra_line)
	
	
	# if patran batch mode, add line to close the database
	if close_flag:
		ses_file_h.write("uil_file_close.goquit(  )")
	ses_file_h.close()
	return ses_file

def write_ses_for_patran_import(ses_template_file,tmp_dir,bdf_file,close_flag=True,xtra_ses_file=""):
	# $BDF is the nastran bdf file
	# $DIR is the directory
	if os.path.isfile(xtra_ses_file):
		xtra_ses_file_h=open(xtra_ses_file,'r')
		xtra_lines=xtra_ses_file_h.readlines()
		xtra_ses_file_h.close()
	else:
		print("No xtra session file lines detected")
		xtra_lines=[]

	ses_file=bdf_file[:-4] + ".ses"
	ses_file_h=open(ses_file,'w')
	template_h=open(ses_template_file,'r')
	template_lines=template_h.readlines()
	print(tmp_dir)
	template_h.close()
	added_xtra_lines=False
	n=0
	while n < len(template_lines):
		#for line in template_lines:
		line=template_lines[n]
		ses_file_h.write(line.replace("$DIR",tmp_dir).replace("$BDF",bdf_file))
		# insert xtra session file just after attachment of results)
		if line.find("analysis_import") ==0 and len(xtra_lines) > 0:
			# print continuation line
			line=template_lines[n+1]
			ses_file_h.write(line.replace("$DIR",tmp_dir).replace("$BDF",bdf_file))
			for xtra_line in xtra_lines:
				ses_file_h.write(xtra_line)
			added_xtra_lines=True
			n+=1
		n+=1
	# check so that xtra lines were added,if not append at end of file
	if added_xtra_lines== False:
		#append at end of file
		for xtra_line in xtra_lines:
			ses_file_h.write(xtra_line)
			
	# if patran batch mode, add line to close the database
	if close_flag:
		ses_file_h.write("uil_file_close.goquit(  )")
	ses_file_h.close()
	return ses_file	
	
def start_patran_post(patran_exe,template_file,bdf_file,hf5_file,no_of_steps,groups_ses_file=""):	
	#patran_exe=r"C:\MSC.Software\Patran_x64\20170\bin\patran.exe -skin"
	
	tmp_dir="C:\\Temp\\OutTmp"

	if not os.path.exists(tmp_dir):
		os.makedirs(tmp_dir)

	# delete content of temp_dir
	filelist = [ f for f in os.listdir(tmp_dir) ]
	for f in filelist:
		os.remove(tmp_dir + '\\' + f)
	
	#wait for Nastran job to complete, then open patran
	wait_flag=True
	exit_flag=False
	exit_id="NaN"
	sts_file=bdf_file.replace('.bdf','.sts')
	if os.path.isfile(sts_file):
		print("sts_file exists:")
	while wait_flag:
		sts_file_h=open(sts_file,'r')
		lines=sts_file_h.readlines()
		sts_file_h.close()
		for line in lines:
			if line.find("Job ends with exit number") >= 0:
				exit_id=line.split(':')[1].strip()
				exit_flag=True
		if exit_flag:
			wait_flag=False
		else:
			time.sleep(2)
	if exit_id=="0" and os.path.isfile(hf5_file):
		print("job completed successfully with exit id: " + exit_id +  " importing " + hf5_file + " ...")
		# job has completed ... start patran
		
		
		#copy out file to tmp_dir, we have to keep directory names short
		shutil.copy2(hf5_file, tmp_dir + "\\")
		shutil.copy2(bdf_file, tmp_dir + "\\")
				
		tmp_hf5=tmp_dir + "\\" +  os.path.basename(hf5_file)
		tmp_bdf=tmp_dir + "\\" +  os.path.basename(bdf_file)

		
		ses_file=write_ses_for_patran(template_file,tmp_dir,tmp_bdf,tmp_hf5,no_of_steps,False,groups_ses_file)
		#os.system("cd C:\\tmp\\OutTmp & C:\\MSC.Software\\Patran_x64\\20170\\bin\\patran.exe -skin -sfp " + ses_file)
				
		os.system("cd C:\\Temp\\OutTmp & " + patran_exe + " -skin -sfp " + ses_file )
	else:
		

		print("job failed with exit id: " + exit_id +  " or " + hf5_file + " file does not exist")
	return
	
def start_export_to_patran(patran_exe,template_file,bdf_file,SettingsD=""):	
	#patran_exe=r"C:\MSC.Software\Patran_x64\20170\bin\patran.exe -skin"
	tmp_dir=r"C:\Temp\OutTmp"	
	StartPatranFlag	= SettingsD['PATRAN_EXPORT']['StartPatran']
	if StartPatranFlag.upper() == "True" or StartPatranFlag.upper() == "Yes":
		StartPatranFlag=True
	else:
		StartPatranFlag=False
	print(StartPatranFlag)
	
	if not os.path.exists(tmp_dir):
		os.makedirs(tmp_dir)
	# delete content of temp_dir
	filelist = [ f for f in os.listdir(tmp_dir) ]
	for f in filelist:
		os.remove(tmp_dir + '\\' + f)

	#copy out file to tmp_dir, we have to keep directory names short
	shutil.copy2(bdf_file, tmp_dir + "\\")
			
	tmp_bdf=tmp_dir + "\\" +  os.path.basename(bdf_file)

	ses_file=write_ses_for_patran_import(template_file,tmp_dir,tmp_bdf,False)
	print(("created session file: " + ses_file))

	#os.system("cd C:\\tmp\\OutTmp & C:\\MSC.Software\\Patran_x64\\20170\\bin\\patran.exe -skin -sfp " + ses_file)
	if 1==0:
		os.system("cd C:\\Temp\\OutTmp & " + patran_exe + " -skin -sfp " + ses_file )
	
		# copy the created Layup file to bdf file directory
		shutil.copy2(tmp_dir + "\\lm_import.Layup", os.path.dirname(bdf_file) + "\\")
	else:
		print("Run session file in C:\Temp\OutTemp")
	
	#try:
	#	shutil.copy2(tmp_dir + "\\lm_import.Layup", os.path.dirname(bdf_file) + "\\")
	#except:
	#	print( "...copying of file failed")
	#
	return
	
def createPatranGroupsFromBoxDict(BoxD,ses_file):
	ses_h=open(ses_file,'w')
	# loop over boxes:

	for box_id in BoxD:
		# create Zoom Box Assy
		BoxName=str(box_id) + ".0_ ZoomBox " +  str(box_id)
		print(BoxName)
		ses_h.write('ga_group_create( "' + BoxName + '" )\n')
		for GroupName in BoxD[box_id]:
			print(GroupName)
			
			#print BoxD[box_id][GroupName]
			EidL=BoxD[box_id][GroupName][1]

			if len(EidL) > 0:

				# sort element list prior to checking
				EidL = sorted(EidL)

				# max group name length is 44 (?)
				if len(GroupName) > 44:
					GroupName = GroupName[:44]

				ses_h.write('ga_group_create( "' + GroupName + '" )\n')

				# check if all eids are sequential
				diff = (EidL[-1]) - (EidL[0])
				lgth = len(EidL)
				# print "Max Eid - Min Eid: " + str(diff)
				# print "No of elements in file: "  + str(lgth)

				if (EidL[-1]) - (EidL[0]) == len(EidL) - 1:

					#
					# print "list is sequential"
					# print str(EidL[0]) + ':' + str(EidL[-1])
					# print "-----------------------"
					ses_h.write('ga_group_entity_add( "' + GroupName + '", "Elm ' + str(EidL[0]) + ':' + str(EidL[-1]) + '" )\n')
					# also add to the box
					ses_h.write('ga_group_entity_add( "' + BoxName + '", "Elm ' + str(EidL[0]) + ':' + str(EidL[-1]) + '" )\n')
				else:
					# print "not sequential"
					tmp_str = ""
					n = 0
					m = 1
					tmp_1 = EidL[0]
					tmp_0 = EidL[n]
					while n < len(EidL) - 1:
						# tmp_0=EidL[n]
						# check if next id is in sequence
						# print EidL[n+1] + " " + tmp_0 + " " + str(m)
						# print str(int(tmp_0) + m)
						if EidL[n + 1] == int(tmp_0) + m:

							# if GroupName=="Part_3" and tmp_0==2292:
							#	print "ok...in a serie ..." + str(EidL[n+1])
							tmp_1 = EidL[n + 1]
							m += 1
							# if in sequence and end of string... write
							if n == len(EidL) - 2:
								tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)
						else:
							# check if there is a sequence to write out"
							# print "no, not in series"
							if tmp_1 - tmp_0 >= 1:
								tmp_str = tmp_str + " " + str(tmp_0) + ":" + str(tmp_1)

							else:
								# tmp_str=tmp_str + " " + str(tmp_0) + " " +  str(tmp_1)
								tmp_str = tmp_str + " " + str(tmp_0)
							# reset m
							m = 1
							tmp_0 = EidL[n + 1]
						# print "new tmp_0: " +  str(tmp_0)
						n += 1

					if tmp_str.split(' ')[-1].split(':')[-1] != str(EidL[-1]):
						tmp_str = tmp_str + " " + str(EidL[-1])
					# print "adding last element id (not in range) "

					# debug print element set string
					# print tmp_str
					tmpL=[]
					# remove leading and trailing blanks:
					tmp_str = tmp_str.strip()
					ses_h.write('ga_group_entity_add( "' + GroupName + '", "Elm ')
					tmpL.append('ga_group_entity_add( "' + BoxName + '", "Elm ')
					# do this for part... repeat for box...but box lines in tmp list
					cut_away_chunk = 44 - len(GroupName)
					if len(tmp_str) <= cut_away_chunk:
						ses_h.write(tmp_str + '" )\n')
						tmpL.append(tmp_str + '" )\n')
					else:
						# write first cut_away_chunk   chars... then  strip  the first cut_away_chunk characters of tmp_str
						ses_h.write(tmp_str[:cut_away_chunk] + '" // @\n')
						tmpL.append(tmp_str[:cut_away_chunk] + '" // @\n')
						tmp_str = tmp_str[cut_away_chunk:]
						# now divide the rest of the string in chunks of 73 chars
						chunk_size = 73
						chunks = len(tmp_str)
						chunkL = [tmp_str[i:i + chunk_size] for i in range(0, chunks, chunk_size)]
						r = 0
						while r < len(chunkL):
							chunk = chunkL[r]
							if r < len(chunkL) - 1:
								ses_h.write('"' + chunk + '" // @\n')
								tmpL.append('"' + chunk + '" // @\n')
							else:
								ses_h.write('"' + chunk + '" )\n')
								tmpL.append('"' + chunk + '" )\n')
							r += 1

						# print "-----------------------"
						# else:
						#	print "EidL is empty"
	
					for item in tmpL:
						ses_h.write(item)

		
	
	ses_h.close()
	print(("Created Session file: " + ses_file))
	return	
	
def getEidFromBodyId(group_file,bdf_file):
	#get  group-info bdf
	#group_file=r"C:\Temp\zoombox_sets.txt"
	group_file_h = open(group_file, 'r')
	lines = group_file_h.readlines()
	group_file_h.close()

	# read zoombox info
	n=0
	N=len(lines)

	BodyList=[]
	PartList=[]
	Part2BodyD={}
	BoxD={}
	while n < N:
		line=lines[n]
		if line.find('ZoomBox')==0:
			GroupD={}
			id=int(line.split(':')[1].strip())
			print("Id: " + str(id))
			n+=1	
		else:
			while lines[n].find('ZoomBox') < 0:
				line=lines[n]
				bdy_name=line.split(';')[1].strip().replace(' ','_')
				grp_name=line.split(';')[0].strip()
				prt_name=line.split(';')[2].strip().replace(' ','_')
				BodyList.append(bdy_name)
				PartList.append(prt_name)
				Part2BodyD[prt_name]=bdy_name
				GroupD[grp_name]=[bdy_name,[]]
				if n< N-1:
					n+=1
				else:
					n+=1
					break
			BoxD[id]=GroupD
	print(BodyList)
	#print BoxD.keys()
	#print BoxD[1].keys()

	# first read eids, store by Body Id
		
	# bdf file
	#bdf_file=r"C:\tmp\Assembly 1.bdf"
	bdf_file_h = open(bdf_file, 'r')
	lines = bdf_file_h.readlines()
	bdf_file_h.close()

	# first pass. find Body Names and associated PSOLID IDs

	n=0
	N=len(lines)
	PidD={}
	EidD={}
	PidList=[]
	while n < N:
		line=lines[n]
		if line.find('$ Elements and Element Properties for Body:') == 0:
			bdy_name=line.split(':')[1].strip()
			if bdy_name in BodyList:
				print(bdy_name)
				pid=int(lines[n+1][8:16].strip())
				print(pid)
				PidD[pid]=bdy_name
				PidList.append(pid)
				EidD[pid]=[]
		elif line.find('$ Elements and Element Properties for Part:') == 0:
			prt_name=line.split(':')[1].strip()
			if prt_name in PartList:
				print(prt_name)
				pid=int(lines[n+1][8:16].strip())
				print(pid)
				#PidD[pid]=bdy_name
				PidD[pid]=Part2BodyD[prt_name]
				PidList.append(pid)
				EidD[pid]=[]
		n+=1
	# second loop, associate elements based on pid			
	n=0
	while n < N:
		line=lines[n]
		if line[0] =="C" and len(line) >= 40 and line[0:4] != "CORD":
			pid= int(line[16:24].strip())

			if pid in PidList:
				EidD[pid].append(int(line[8:16].strip()))
						
		#if line.find("C") == 0 and len(line) >= 40:
		#		EidL.append(int(line[8:16].strip()))
		n+=1		

	for pid in list(EidD.keys()):
		bdy_name = PidD[pid]
		# find the box containing the body_name
		for box_id in BoxD:
			for item in BoxD[box_id]:
				#print BoxD[box_id][item]
				if bdy_name in BoxD[box_id][item]:
					print(BoxD[box_id][item])
					BoxD[box_id][item][1]=EidD[pid]
				
		#print EidD[key][0:10]
	return BoxD
	
def ReadSubcases(inl1):
	# Reads subcases exported as a Study
	SubCaseStrL = []
	SubCaseIdL=[]
	SpcIdD={}
	SubCaseD={}
	SpcAddD={}
	Rbe2D = {}
	# print "Processing ReadRbe2s"

	n = 0
	while n < len(inl1):
		line = inl1[n]
		if line[0:8] == 'SUBCASE ':
			subcase_id=int(line.split()[1])
			SubCaseIdL.append(subcase_id)
			SubCaseStrL.append(line)
		elif line.find('SUBTITLE') > -1:
			SubCaseStrL.append(line)
		elif line.find('$ Event name') > -1:
			SubCaseStrL.append(line)
		elif line.find('$ Event description:') > -1:
			SubCaseStrL.append(line)	
		elif line.find(' LOAD ') == 0:
			SubCaseStrL.append(line)
		elif line.find(' SPC ') == 0:
			SpcIdD[subcase_id]=int(line.split('=')[1].strip())
			SubCaseStrL.append(line)
		elif line.find('SPCADD') == 0:
			spcadd_id=int(line[8:16])
			SpcAddD[spcadd_id]=[]
			# first concatenate all spcadd lines to one string, omit cont.
			_tmp_str=line[16:min(72,len(line))]
			while inl1[n+1][0] == '+'  or  inl1[n+1][0] == ' ':
				n+=1
				_tmp_str+=inl1[n][8:min(72,len(line))]
			# read _tmp_str in chunks of 8
			tmp_list=[_tmp_str[x:x+8] for x in range(0,len(_tmp_str),8)]
			SpcAddD[spcadd_id]=[int(i.strip()) for i in tmp_list]
		else:
			pass
		n+=1
			
	return SubCaseStrL, SubCaseIdL, SpcIdD, SpcAddD

def readMaterialsFromFile(SettingsD,TmpMat=False):
	global utils_
	# get files from directory
	if TmpMat== False:
		print('Material Database  Diretory:')
		MatDbFlag='MaterialDatabaseDir'
		#matdb_dir =  SettingsD['System'][MatDbFlag]
	else:
		print('Material Database Temporary Diretory:')
		MatDbFlag='MaterialDatabaseTempDir'		
	matdb_dir=SettingsD['System'][MatDbFlag]
	#
	if os.path.dirname(__file__) == "":
		#print(" script executes as macro...Assume standard path ")
		base_file=os.env("%USERHOME%") + '/Documents/MSC_Apex Workspace/ExportTools/BDFExport/bdfExport.py'
	else:
		base_file=__file__
	
	# Find Material template , use xml file as reference for relative paths
	matdb_dir = resolve_pathname(matdb_dir,SettingsD['System']['SettingsFile'])
	print("matdb_dir",matdb_dir)

	
	#print("Base_file",	base_file )
	#
	if os.path.isdir(matdb_dir):
		#print("Absoulte path used")
		pass
	elif os.path.isdir( os.path.dirname(base_file) + '/../' +  matdb_dir):
		matdb_dir=os.path.dirname(base_file) + '/../' +  matdb_dir
		#print("Relative path used  ")
	elif os.path.isdir( os.path.dirname(base_file) + '/' +  matdb_dir):
		matdb_dir=os.path.dirname(base_file) + '/' +  matdb_dir
		#print("Relative path used")		
	else:
		print("Error...MaterialDatabaseTempDir can not be located")
	
		
	MatD={}	
	if matdb_dir != "":
		print("Reading material information from: " + matdb_dir)
		files= os.listdir(matdb_dir)
		MatFileD={}
		for file in files:
			if file[-3:] == 'bdf' and os.path.basename(file)[0] != ".":
				mxpxD=read_mat_and_props(matdb_dir + '\\' + file)
				MatFileD[file.replace('.bdf','')]=mxpxD
	
	print("")
	
	return MatFileD
		
		
		
		
		
	if 1==0:	
		# make sure any TABLES1 tabels get unique numbering
		tables1_id=100
		for file in files:
			print() 
			if file[-3:] == 'bdf' and os.path.basename(file)[0] != ".":
				file_h=open(matdb_dir + "/" + file,'r')
				lines=file_h.readlines()
				file_h.close
				
				n=0
				N=len(lines)
				while n < N:
					line=lines[n]
					if line.find("MAT1") == 0:
						MatD[file[:-4]]=[line]
						# # check if there is a continuity line on MAT1 (have to start with a "+")
						# if lines[n+1].find("+") == 0:
							# MatD[file[:-4]].append(lines[n+1])
							# n+=1
					elif line.find("MATS1") == 0:
						tables1_id +=1
						MatD[file[:-4]].append(line[:16] + "%8d" % (tables1_id) + line[24:])
					elif line.find("MATEP") == 0 and line[16:24].strip().upper() == "TABLE":
						tables1_id +=1
						MatD[file[:-4]].append(line[:32] + "%8d" % (tables1_id) + line[40:])
					elif line.find("MATHE") == 0 and line[16:24].strip().upper() == "TABLE":
						MatD[file[:-4]].append(line)
					elif line.find("MATT1") == 0:
						MatD[file[:-4]].append(line)
						# # check if there is a continuity line on MATT1 (have to start with a "+")
						# if lines[n+1].find("+") == 0:
							# MatD[file[:-4]].append(lines[n+1])
							# n+=
					elif line.find("MAT2") == 0:
						MatD[file[:-4]].append(line)
					
					elif line.find("TABLEM") == 0:
						MatD[file[:-4]].append(line)
						
							
					#elif line.find("MATG") == 0:
					#	MatD[file[:-4]]=[line[:16] + "%8d" % (tables1_id) + line[24:])]
					#	tables1_id +=1									
					#	MatD[file[:-4]].append(line[:32] + "%8d" % (tables1_id) + line[40:])
					elif line.find("TABLES1") == 0:
						MatD[file[:-4]].append(line[:8] + "%8d" % (tables1_id) + line[16:])

					else:
						MatD[file[:-4]].append(line)
					n+=1	
					
					
		#for key in MatD:
			#print(str(key) + " " + str(MatD[key]) )
			# create Apex Materials
			#if len(MatD[key]) == 3:
			#	CreateMaterial(key[:-4],MatD[key][0],MatD[key][1],MatD[key][2])
			#else:
			#	CreateMaterial(key[:-4],MatD[key][0],MatD[key][1],MatD[key][2],MatD[key][3],MatD[key][4])
	else:
		print("A material database was not specified or could not be found")		
		
	return MatD

def old_readMaterialsFromFile(SettingsD,TmpMat=False):
	# get files from directory
	if TmpMat== False:
		print('MaterialDatabaseDir')
		MatDbFlag='MaterialDatabaseDir'
	else:
		print('MaterialDatabaseTempDir')
		MatDbFlag='MaterialDatabaseTempDir'
		
	try:
		if os.path.dirname(SettingsD['System'][MatDbFlag]) == "":
			print("Material Database is located in Utils Directory ")
			matdb_dir =  os.path.dirname(__file__) + '\\' + SettingsD['System'][MatDbFlag]
			print(("***" + os.path.dirname(__file__) + "***"))
		else:
			matdb_dir =  SettingsD['System'][MatDbFlag]
			
	except:
		matdb_dir = ""
		
	MatD={}	
	if matdb_dir != "":
		print(("Material database directory: " + matdb_dir))
		files= os.listdir(matdb_dir)	
		
		# make sure any TABLES1 tabels get unique numbering
		tables1_id=100
		for file in files:
			print() 
			if file[-3:] == 'bdf' and os.path.basename(file)[0] != ".":
				file_h=open(matdb_dir + "/" + file,'r')
				lines=file_h.readlines()
				file_h.close
				
				n=0
				N=len(lines)
				while n < N:
					line=lines[n]
					if line.find("MAT1") == 0:
						MatD[file[:-4]]=[line]
						# # check if there is a continuity line on MAT1 (have to start with a "+")
						# if lines[n+1].find("+") == 0:
							# MatD[file[:-4]].append(lines[n+1])
							# n+=1
					elif line.find("MATS1") == 0:
						tables1_id +=1
						MatD[file[:-4]].append(line[:16] + "%8d" % (tables1_id) + line[24:])
					elif line.find("MATEP") == 0 and line[16:24].strip().upper() == "TABLE":
						tables1_id +=1
						MatD[file[:-4]].append(line[:32] + "%8d" % (tables1_id) + line[40:])
					elif line.find("MATHE") == 0 and line[16:24].strip().upper() == "TABLE":
						MatD[file[:-4]].append(line)
					elif line.find("MATT1") == 0:
						MatD[file[:-4]].append(line)
						# # check if there is a continuity line on MATT1 (have to start with a "+")
						# if lines[n+1].find("+") == 0:
							# MatD[file[:-4]].append(lines[n+1])
							# n+=
					elif line.find("MAT2") == 0:
						MatD[file[:-4]].append(line)
					
					elif line.find("TABLEM") == 0:
						MatD[file[:-4]].append(line)
						
							
					#elif line.find("MATG") == 0:
					#	MatD[file[:-4]]=[line[:16] + "%8d" % (tables1_id) + line[24:])]
					#	tables1_id +=1									
					#	MatD[file[:-4]].append(line[:32] + "%8d" % (tables1_id) + line[40:])
					elif line.find("TABLES1") == 0:
						MatD[file[:-4]].append(line[:8] + "%8d" % (tables1_id) + line[16:])

					else:
						MatD[file[:-4]].append(line)
					n+=1	
					
					
		#for key in MatD:
			#print(str(key) + " " + str(MatD[key]) )
			# create Apex Materials
			#if len(MatD[key]) == 3:
			#	CreateMaterial(key[:-4],MatD[key][0],MatD[key][1],MatD[key][2])
			#else:
			#	CreateMaterial(key[:-4],MatD[key][0],MatD[key][1],MatD[key][2],MatD[key][3],MatD[key][4])
	else:
		print("A material database was not specified or could not be found")		
		
	return MatD



def start_sesam_post(sesam_exe,template_file,bdf_file,SettingsD,groups_ses_file=""):	
	#patran_exe=r"C:\MSC.Software\Patran_x64\20170\bin\patran.exe -skin"
	tmp_dir="C:\\Temp\\OutTmp"
	# check (again)  if association of FE geom is requested
	try:
		AssociateFemFlag = SettingsD['SESAM_EXPORT']['AssociateFEM']
		print(("AssociateFemFlag " + AssociateFemFlag))
		if AssociateFemFlag.upper() == "TRUE" or AssociateFemFlag.upper() == "YES":
			AssociateFemFlag=True
			out_parasolid=bdf_file[:-3] + 'x_t'
		else:
			AssociateFemFlag=False
			out_parasolid=""	
	except:
		AssociateFemFlag=False
	
	try:
		ConvertBdf2FemFlag = SettingsD['SESAM_EXPORT']['ConvertBDF2FEM']
		if ConvertBdf2FemFlag.upper() == "TRUE" or ConvertBdf2FemFlag.upper() == "YES":
			ConvertBdf2FemFlag=True
		else:
			ConvertBdf2FemFlag=False
			#out_parasolid=""

	except:
		ConvertBdf2FemFlag=False
	
	
	if ConvertBdf2FemFlag==True:
		#Start Sesam Converter
		fem_file=RunSesamConverter(SettingsD,bdf_file,tmp_dir)
	
	#print "out_parasolid: " + out_parasolid
	print(AssociateFemFlag)
	print(ConvertBdf2FemFlag)
	
	

	if not os.path.exists(tmp_dir):
		os.makedirs(tmp_dir)

	# delete content of temp_dir
	filelist = [ f for f in os.listdir(tmp_dir) ]
	for f in filelist:
		os.remove(tmp_dir + '\\' + f)
		
	#copy out file to tmp_dir, we have to keep directory names short
	shutil.copy2(bdf_file, tmp_dir + "\\")
	tmp_bdf=tmp_dir + "\\" +  os.path.basename(bdf_file)
	
	
	if AssociateFemFlag == True:
		#print "I am in Assiciate FEM"
		#copy geom file to tmp_dir
		print(("out_parasolid: " +  out_parasolid ))
		#os.rename(out_parasolid, "C:/Temp/OutTmp/sesam_Assembly1.X_T")
		if os.path.isfile(out_parasolid):
			shutil.copy2(out_parasolid, tmp_dir + "\\")
		elif os.path.isdir(out_parasolid):
			# script exports Ties as parasolid subfolders
			shutil.copy2(out_parasolid + "/" +os.path.basename(out_parasolid)[:-3] + "X_T", tmp_dir + "\\" )
		else:
			print("Something is wrong, parasolid not copied")
		tmp_parasolid=tmp_dir + '\\' + os.path.basename(out_parasolid)
			
		# add lines to ses_file
		groups_ses_file_h=open(groups_ses_file,'r')
		group_lines=groups_ses_file_h.readlines()
		groups_ses_file_h.close()
	
		xtra_ses_file="C:\\temp\\OutTmp\\xtra_lines.ses"		
		xtra_ses_file_h=open(xtra_ses_file,'w')
		
		
		
		xtra_ses_file_h.write('pref_geo_set_scale_factor(1000.)\n')
		#
		xtra_ses_file_h.write('p3_express_options_file( "express.opt", FALSE, 3, 1, 52054, TRUE, [1000.00,  @\n')
		xtra_ses_file_h.write('0., 0., 0., 1000.00, 0., 0., 0., 1000.00, 0., 0., 0.], 0, ["0"], [[-1]], [ @\n')
		xtra_ses_file_h.write('[-1]], [[-1]], TRUE, [0], TRUE, [0], TRUE, [0], 3, 1, 3 )\n')
		xtra_ses_file_h.write('INTEGER uil_file_import_ps_tr_xmt_count(5)\n')
		xtra_ses_file_h.write('INTEGER uil_file_import_ps_patran_count(4)\n')
		xtra_ses_file_h.write('INTEGER uil_file_import_ps_tra_n_layers\n')
		xtra_ses_file_h.write('INTEGER uil_file_import_ps_tr_layer_ids(5,256)\n')
		xtra_ses_file_h.write('REAL uil_file_import_ps_tr_model_tol(2)\n')
		xtra_ses_file_h.write('p3_ug_xmt_import_v2( "' + tmp_parasolid + '", "", "express.opt",  @\n')
		xtra_ses_file_h.write('TRUE, 0, uil_file_import_ps_tr_xmt_count, uil_file_import_ps_patran_count,  @\n')
		xtra_ses_file_h.write('uil_file_import_ps_tra_n_layers, uil_file_import_ps_tr_layer_ids,  @\n')
		xtra_ses_file_h.write('uil_file_import_ps_tr_model_tol, TRUE, "", FALSE )\n')
		xtra_ses_file_h.write('sgm_assoc_element_surface_v1( "Elm 1:#", "Surface 1:#", 17 )\n')
		#xtra_ses_file_h.write('sgm_assoc_element_curve_v1( "Elm 1:#", "Surface 1:#.1:#", 9 )\n') 

		for line in group_lines:
			xtra_ses_file_h.write(line)
			
		xtra_ses_file_h.close()
	
	else:
		xtra_ses_file=groups_ses_file
	#	print "What?"

	close_flag=False
	ses_file=write_ses_for_patran_import(template_file,tmp_dir,bdf_file,close_flag,xtra_ses_file)

	#os.system("cd C:\\tmp\\OutTmp & C:\\MSC.Software\\Patran_x64\\20170\\bin\\patran.exe -skin -sfp " + ses_file)
	os.system("cd C:\\Temp\\OutTmp & " + sesam_exe + " -skin -sfp " + ses_file )

	return	

def ReadRbars2(inl1, FindRbarL=[]):
	RbarD = {}
	for lin in inl1:
		if lin[0:5] == 'RBAR ':
			ElemId = int(lin[8:16])
			if FindRbarL != []:  # Only add Nodes from FindNode-List
				if ElemId not in FindRbarL:
					continue
			RbarD[ElemId] = [int(lin[16:24]), int(lin[24:32])]
	return RbarD

def process_Rutman_fasteners(bdf_file, SettingsD):
	# Associate cord system to Rutman fasteners and set the proper dofs on the RBARs
	
	print(list(SettingsD.keys()))

	try:

		if SettingsD['System']['ProcessRutmanFasteners'].upper()  == "TRUE":
			ProcessRutmanFasteners = True
		else:
			ProcessRutmanFasteners = False
	except:
		ProcessRutmanFasteners = False
	
	print(("Hej " + str(ProcessRutmanFasteners) ))
	
	RutmanLabel="RF"
	if ProcessRutmanFasteners:
		RutmandD={}
		#RutmandD[label]=[cord_id,[CBARS],[RBARS],[CBUSHES], top_node ]

		# read existing bdf file
		print(" I am reading " + bdf_file)
		bdf_h = open(bdf_file, 'r')
		lines = bdf_h.readlines()
		bdf_h.close()
		
		N=len(lines)
		n=0
		
		while n<N:
			line= lines[n]
			
			# Find CORDS to get the RF-label
			# $ Standard Coordinate System: RF-Point 1 (2)
			if line.find("$ Standard Coordinate System:") == 0:
				tmp_label=line.split(":")[1].strip()
				if tmp_label.find(RutmanLabel) == 0:
					cord_id=int(lines[n+1][8:16])
					RutmandD[tmp_label]=[cord_id,[],[],[],"" ]
				n+=2
			else:
				n+=1
		
		# Now - make a new loop look for RBARs, CBARS and CBUSHES where the RUTMAN label appears in the title
		
		
		n=0
		while n<N:
			line= lines[n]
			
			
			if line.find("CBAR") == 0:
				print((lines[n-1].split(":")[1].strip()))
				tmp_label=lines[n-1].split(":")[1].strip()
				for key in list(RutmandD.keys()):
					if tmp_label.find(key) == 0 :
						print("found a rutman CBAR")
						tmp_id=int(line[8:16])
						tmp_node1=int(line[24:32])
						tmp_node2=int(line[32:40])
						RutmandD[key][1].append([tmp_id,tmp_node1,tmp_node2])
			elif line.find("RBAR") == 0:
				tmp_label=lines[n-1].split(":")[1].strip()
				for key in list(RutmandD.keys()):
					if tmp_label.find(key) == 0 :
						print("found a rutman RBAR")
						tmp_id=int(line[8:16])
						tmp_node1=int(line[16:24])
						tmp_node2=int(line[24:32])
						RutmandD[key][2].append([tmp_id,tmp_node1,tmp_node2])
			elif line.find("CBUSH") == 0:
				tmp_label=lines[n-1].split(":")[1].strip()
				for key in list(RutmandD.keys()):
					if tmp_label.find(key) == 0 :
						print("found a rutman CBUSH")
						tmp_id=int(line[8:16])
						tmp_node1=int(line[24:32])
						tmp_node2=int(line[32:40])
						RutmandD[key][3].append([tmp_id,tmp_node1,tmp_node2])	
			elif line.find("SPC") == 0:
				tmp_label=lines[n-1].split(":")[1].strip()
				for key in list(RutmandD.keys()):
					if tmp_label.find(key) == 0 :
						print("found a rutman SPC")
						tmp_id=int(line[16:24])
						RutmandD[key][4]=tmp_id	

			
			else:
				pass
			n+=1
		

		# Now... write back bdf with following changes:
		# 1. Rutman RBARs are connecting only dofs 123
		# 2. Rutman Grids get analysis system
		
		# Form a new Dict with GridIDs vs analysis CordIds
		# and a list with RBARs and corresponding dofs
		gridD={}

		RbarD={}
		
		for key in list(RutmandD.keys()):
			cid=RutmandD[key][0]
			top_node=RutmandD[key][4]
			#CBARS
			for item in RutmandD[key][1]:				
				gridD[item[1]]=cid
				gridD[item[2]]=cid
			#RBARS
			tmp_RbarD={}
			for item in RutmandD[key][2]:
				gridD[item[1]]=cid
				gridD[item[2]]=cid
				# Create a RbarDict so that we can figure out what dofs nned to be asasigned				
				tmp_RbarD[item[0]]=[ item[1],item[2],"" ]
			
			# process the tmp_RbarD
			# 1. find the RBAR with the top node:
			TopRbarFoundFlag=False
			while len(list(tmp_RbarD.keys()) )  > 0 :
				if len(list(tmp_RbarD.keys()) ) == 1:
					print("this is the last RBAR")
					dofs=56
					rbar=list(tmp_RbarD.keys())[0]
					RbarD[rbar]=dofs
					del tmp_RbarD[rbar]
				else:
					for rbar in tmp_RbarD:
						if top_node == tmp_RbarD[rbar][0]:
							# assign "new" top node to find next rbar
							top_node=tmp_RbarD[rbar][1]
							if TopRbarFoundFlag==False:
								dofs=1456
								TopRbarFoundFlag=True
							else:
								dofs=156
							RbarD[rbar]=dofs
							del tmp_RbarD[rbar]
							print(("found top rbar 1 " + str(rbar) ))
							break
						elif top_node == tmp_RbarD[rbar][1]:
							# assign "new" top node to find next rbar
							top_node=tmp_RbarD[rbar][0]
							if TopRbarFoundFlag==False:
								dofs=1456
								TopRbarFoundFlag=True
							else:
								dofs=156
							RbarD[rbar]=dofs						
							del tmp_RbarD[rbar]
							print(("found top rbar 2 "  + str(rbar) ))
							break
						else:
							pass


			#CBUSHES
			for item in RutmandD[key][3]:
				gridD[item[1]]=cid
				gridD[item[2]]=cid
		
		print(RbarD)
		print(gridD)
		
		orig_file_name= os.path.dirname(bdf_file) + "\\orig_" + os.path.basename(bdf_file)
		if not os.path.isfile(orig_file_name):
			os.rename(bdf_file, orig_file_name)
			
		# write back
		bdf_h=open(bdf_file,'w')
		for line in lines:
			if line.find("GRID")==0:
				tmp_id=int(line[8:16])
				if tmp_id in list(gridD.keys()):
					if line[48:56].strip() == "":
						if len(line) <57:
							bdf_h.write("%-48s" % (line[:48].replace("\n","")) + "%8d\n" % (gridD[tmp_id])  )
						else:
							bdf_h.write("%-48s" % (line[:48]) + "%8d" % (gridD[tmp_id]) + line[56:] )
							
					else:
						# Grid already has a coordinate system... use this
						bdf_h.write(line)
				
				else:
					bdf_h.write(line)
				
			elif line.find("RBAR")==0:
				tmp_id=int(line[8:16])
				if tmp_id in list(RbarD.keys()):
					bdf_h.write(line[:32] + "%8s%16s%8s\n" % (RbarD[tmp_id],"",RbarD[tmp_id])  )

				else:
					bdf_h.write(line)
					
			else:
				bdf_h.write(line)
					
		bdf_h.close()		
		return 	RutmandD
	
	else:
		return {}

	
def update_bdf_after_contact_body_consolidation(bdf_file,BCBodyTableD,BSurfD,BConectD):
	bdf_file_h=open(bdf_file,'r')
	lines=bdf_file_h.readlines()
	bdf_file_h.close()
	
	####
	#print("#######")
	#print(BConectD)
	
	# rename old bdf file
	old_bdf_file = os.path.dirname(bdf_file) + '/pre_consolidated_' + os.path.basename(bdf_file)
	try:
		os.rename(bdf_file,old_bdf_file) 
	except FileExistsError:
		print("File Exists")
	
	new_bdf_file=bdf_file
	
	new_bdf_file_h=open(new_bdf_file,'w')
	
	BConectL=sorted(BConectD.keys())
	BSurfIdL=sorted(BSurfD.keys())
	
	# get all referenced BCONPRG_ids
	# get all referenced BCBODY's
	BConprgIdL=[]
	BCbodyL=[]
	for key in BConectL:
		BConprgIdL.append(BConectD[key][2])
		BCbodyL += BConectD[key][0]
	# get all referenced BCBODY's
	
	WriteBctable0=False
	n=0
	N=len(lines)
	while n < N:
		if lines[n].find('BSURF') == 0:
		
			# remove original bsurf
			bsurf_id=int(lines[n][8:16].strip())
			# skip all lines until next card
			new_bdf_file_h.write('$' + lines[n])
			while lines[n + 1].find("+") == 0:
				# omit all BSURF cards
				new_bdf_file_h.write('$' + lines[n+1])
				n += 1
			n+=1
			
			# print the bsurf id
			if bsurf_id in BSurfIdL:
				bsurfL=BSurfD[bsurf_id]
				print(("bsurf_id",bsurf_id))
				new_bdf_file_h.write("%-8s%-8d" % ("BSURF",bsurf_id) )
				i=0
				j=0
				for item in bsurfL:
					if i== len(bsurfL) -1:
						new_bdf_file_h.write("%8d\n" % (item) )
					elif (j == 6 and i==6) or (j == 7 and i>6):
						new_bdf_file_h.write("%8d%-8s\n%-8s" % (item,"+","+") )
						j=0
						i+=1
					else:
						new_bdf_file_h.write("%8d" % (item) )
						j+=1
						i+=1
				

				
		elif lines[n].find('BCONECT') == 0:
			bconect_id=int(lines[n][8:16].strip())
			if bconect_id not in BConectL:
				new_bdf_file_h.write('$' + lines[n])
			else:
				#new_bdf_file_h.write(lines[n])
				bconL=BConectD[bconect_id]
				body1=bconL[0][0]
				body2=bconL[0][1]
				contact_type=bconL[1]
				bconprg_id=bconL[2]
				bconprp_id=bconL[3]
				pconprp_str=str(bconprp_id)
				#new_bdf_file_h.write("%s\n" % ("$ Contact Pair " + str(bcon_id) +  " after consolidation #" +  contact_type) )
				new_bdf_file_h.write("%-8s%-8d%-8d%-8s%-8d%-8d\n" % ("BCONECT",bconect_id,bconprg_id,pconprp_str,body1,body2) )
				
				
				
			n+=1
			
		elif lines[n].find('BCBODY') == 0:
			bcbody_id=int(lines[n][8:16].strip())
			if bcbody_id not in BCbodyL:
				new_bdf_file_h.write('$' + lines[n])
			else:
				new_bdf_file_h.write(lines[n])
			n+=1
		
		elif lines[n].find('BCTABL') == 0:
			if int(lines[n][8:16])==0:
				WriteBctable0=True
			# skip all lines until next card
			new_bdf_file_h.write('$' + lines[n])
			while lines[n + 1].find("+") == 0:
				# omit all BSURF cards
				new_bdf_file_h.write('$' + lines[n+1])
				n += 1
			n+=1
		elif lines[n].find('ENDDATA') == 0:
			n+=1
			
		elif lines[n].find('BCONPRG') == 0:
			# Only write BCONPRG's that are referenced
			bconprg_id=int(lines[n][8:16].strip())
			# remove unreferenced
			if bconprg_id not in BConprgIdL:
				# skip all lines until next card
				new_bdf_file_h.write('$' + lines[n])
				while lines[n + 1].find("+") == 0:
					# omit all BCONPRG cards
					new_bdf_file_h.write('$' + lines[n+1])
					n += 1
				n+=1
			else:
				new_bdf_file_h.write(lines[n])
				while lines[n + 1].find("+") == 0:
					# BCONPRG cards
					new_bdf_file_h.write(lines[n+1])
					n += 1
				n+=1
				
		else:
			new_bdf_file_h.write(lines[n] )
			n+=1
					
	
	#print BCTABLE stuff
	i=0
	j=0
	
	# Stress free initital contact
	if WriteBctable0==True and len(BConectL) > 0 :
		new_bdf_file_h.write("%-8s%-8d" % ("BCTABL1",0) )
		for bcon_id in  BConectL:
			if i== len(BConectL) -1:
				new_bdf_file_h.write("%8d\n" % (bcon_id) )
			elif (j == 6 and i==6) or (j == 7 and i>6):
				new_bdf_file_h.write("%8d%-8s\n%-8s" % (bcon_id,"+","+") )
				j=0
				i+=1
			else:
				new_bdf_file_h.write("%8d" % (bcon_id) )
				j+=1
				i+=1
	# BCTABLE 1
	i=0
	j=0
	print("I am here this is Export version")
	if len(BConectL) > 0 :
		#print("WHAAAAAAAT")
		#print(BConectL)
		#print("###")
		new_bdf_file_h.write("%-8s%-8d" % ("BCTABL1",1) )
		for bcon_id in  BConectL:
			if i== len(BConectL) -1:
				new_bdf_file_h.write("%8d\n" % (bcon_id) )
			elif (j == 6 and i==6) or (j == 7 and i>6):
				new_bdf_file_h.write("%8d%-8s\n%-8s" % (bcon_id,"+","+") )
				j=0
				i+=1
			else:
				new_bdf_file_h.write("%8d" % (bcon_id) )
				j+=1
				i+=1
	#
	new_bdf_file_h.write("ENDDATA\n")		
			
	new_bdf_file_h.close()

	return new_bdf_file
	
def createSuperElementsPerPart(bdf_full_name,SettingsD={}):
# process glue to eq nodes- > export list of merged nodes
	
	# 1. merge db files- to get node list... 
	
	AssyD, prefixD, new_tmp_bdf, top_bdf_file = read_hierarchy(bdf_full_name, SettingsD)
	# AssyD, prefixD, new_tmp_bdf=read_hierarchy(bdf_full_name.replace('/','\\'),SettingsD)
	#if AssyD != {} and len(AssyD.keys()) ==1:
	
	print(" ----- prefixD -----")
	print(prefixD)
	print("--- end prefixD ----")
	print()
	print(" ----- AssyD -----")
	print(AssyD)
	print("--- end AssyD ----")
	
	print("top_bdf_file",top_bdf_file)

	# rename original file to orig_
	#old_bdf = os.path.dirname(bdf_full_name) + '\\orig_' + os.path.basename(bdf_full_name)
	#save_orig_file_if_not_new(old_bdf, bdf_full_name)
	# rename tmp_bdf (new flat)
	#os.rename(new_tmp_bdf, bdf_full_name)
	
	# The SE0 prefix is same as prefix with id 0 in SE_D[se_file]=[file_path + '\\' +se_file]:
	# The master file directory is used for relative paths
	for key in prefixD.keys():
		tmp_prefix= prefixD[key]

		if tmp_prefix.split(' ')[0] == '0':
			se0_prefix=tmp_prefix.split(' ')[1]
			master_dir=os.path.dirname(key).replace('/','\\') + '\\'
			break
	
	print("se0_prefix",se0_prefix)
	print("master_dir",master_dir)
	
	
	file_h=open(new_tmp_bdf,'r')
	bdf_lines=file_h.readlines()
	file_h.close()

	TieNodeL, RbarElemL = getTieNodes(bdf_lines)
	merge_list = createMergeList(TieNodeL)
	
	OmitD = {}
	# 2. Get All ASET nodes
	AsetD={}
	#Dict with (PropType + id)  as key and Prop bdf line as value
	PropD={}
	#Dict with (Mid)  as key and MAT bdf line as value
	MatD={}
	PropVsMatD={}
	# Cords 
	# only get for forces for now cord id vs CORD lines
	CordD={}
	
	n = 0
	# only support single line props for now
	while n < len(bdf_lines):
		if  bdf_lines[n].find('ASET1') == 0:
			aset_node = int( bdf_lines[n][16:24])
			AsetD[aset_node]=int( bdf_lines[n][8:16])
		elif bdf_lines[n].find('PS') == 0 or bdf_lines[n].find('PB') == 0: 	
			key=bdf_lines[n][0:8].strip() + '_' + str(bdf_lines[n][8:16].strip())
			PropD[key] = bdf_lines[n]
			PropVsMatD[key]=int(bdf_lines[n][16:24])
		elif bdf_lines[n].find('MAT') == 0: 
			MatD[int(bdf_lines[n][8:16])]=bdf_lines[n]
		elif bdf_lines[n].find('CORD') == 0: 
			CordD[int(bdf_lines[n][8:16])]=bdf_lines[n] + bdf_lines[n+1]
			n+=1
		
		n+=1	
	OmitD['ASET1']=AsetD.keys()
	

	
	# 3 ... then go through all include files and replace nodes
	# ... and retrieve the SE's
	ProcessedFileL=[]
	
	print("AssyD.keys()", AssyD.keys())
	
	SE_id=10
	SE_D={}
	AsetGridsL=[]
	
	SExtraCardsD={}	
	#PropD={}  MatD={} PropVsMatD={} CordD={}
	
	
	
	
	for master_file in AssyD.keys():
		file_list=AssyD[master_file]
		file_list.append(master_file)
		print("")
		print("File_list: ",file_list )
		print("")
		for inc_bdf_file in file_list:
			if inc_bdf_file not in ProcessedFileL:
				print("Processing: " + inc_bdf_file )
				
				# Need to assign files to se prefix
				
				file_base_name=os.path.basename(inc_bdf_file)
				abs_file_path=os.path.dirname(inc_bdf_file)
					
				# remove absolute path up to master bdf
				file_path=abs_file_path.replace(master_dir,'')
					
				se_file=file_base_name.replace('_Loads_Constraints','').replace('_Materials','').replace('_Coordinate_Systems','').replace('_Properties','')
				
				if inc_bdf_file != master_file:
					print("se_file",se_file[:-4])
					#file_base_name=os.path.basename(inc_bdf_file)
					#abs_file_path=os.path.dirname(inc_bdf_file)
					
					# remove absolute path up to master bdf
					#file_path=abs_file_path.replace(master_dir,'')
					
					#se_file=file_base_name.replace('_Loads_Constraints','').replace('_Materials','').replace('_Coordinate_Systems','').replace('_Properties','')
					
					#print("se_file",se_file[:-4])
					
					if not se_file in SE_D.keys():
						# append base bdf (no suffix)
						SE_D[se_file]=[abs_file_path + '\\' + se_file]
						SExtraCardsD[se_file]=[]
						
					if inc_bdf_file.find(se_file[:-4]) > -1 and file_base_name != se_file:
						SE_D[se_file].append(inc_bdf_file)
					else:
						print(SE_D[se_file],se_file[:-4],file_base_name,se_file )
					
				#old_bdf = os.path.dirname(bdf_file) + '\\orig_' + os.path.basename(bdf_file)
				#save_orig_file_if_not_new(old_bdf, bdf_file)
				
				inc_file_h=open(inc_bdf_file,'r')
				tmp_lines=inc_file_h.readlines()
				inc_file_h.close()
				
				# get all nodes associated to Aset (SE0) and copy to residual
				processed_prop_keys=[]
				processed_mat_ids=[]
				processed_cord_ids=[]
				for line in tmp_lines:
					if line.find("GRID") ==0:
						if int(line[8:16]) in AsetD.keys():
							AsetGridsL.append(line)
					
					elif line.find("FORCE") == 0:
						tmp_cid=int(line[24:32])
						if tmp_cid in CordD.keys() and tmp_cid not in processed_cord_ids:
							SExtraCardsD[se_file].append(CordD[tmp_cid] )
						
							
			
					elif line.find("CQ")==0 or line.find("CT")==0 or line.find("CP")==0 or line.find("CH")==0 or line.find("CB")==0:
						if line.find("CQ")==0 or line.find("CTRIA")==0:
							tmp_key='PSHELL_' + line[16:24].strip()
						elif line.find("CH")==0 or line.find("CTET")==0 or line.find("CPENT")==0 :
							tmp_key='PSOLID_' + line[16:24].strip()
						elif line.find("CBEAM")==0:
							tmp_key='PBEAM_' + line[16:24].strip()
						elif line.find("CBAR")==0:
							tmp_key='PBARL_' + line[16:24].strip() # no support for numeric sections	
							
						# get corresponding bulk data cards and add to list
						if tmp_key in PropD.keys() and tmp_key not in processed_prop_keys:
							SExtraCardsD[se_file].append(PropD[tmp_key])  
							processed_prop_keys.append(tmp_key)
							mat_id=PropVsMatD[key]
							if mat_id not in processed_mat_ids:
								SExtraCardsD[se_file].append(MatD[mat_id] )
									

				
				OmitD['RBAR'] = RbarElemL
				UpdateBdf(inc_bdf_file, tmp_lines, merge_list, OmitD, comment_duplicate_flag=False)
				ProcessedFileL.append(ProcessedFileL)
			else:
				print(inc_bdf_file + " already processed")
		
	# Update Top_bdf_file  (in this case should equal the only master bdf file
	top_file_h=open(top_bdf_file,'r')
	lines= top_file_h.readlines()
	top_file_h.close()
	
	# figure out which file belongs to SE0 and which are part SE's
	# assign SE (this will controlled by user later)
	Bdf_2_SE_D={}

	SE_id=10
	print("SE_D.keys():", SE_D.keys())
	
	root_se=se0_prefix + '.bdf'
	
	for key in SE_D.keys():
		if key==root_se:
			Bdf_2_SE_D[0]=root_se
		else:
			Bdf_2_SE_D[SE_id]=key
			SE_id+=10
	
	# GET SPC id and Load id from SPCs
	# if node for LOAD or SPC has an ASET,  they need to be removed in previous step, and moved to residual
	# for now assume 1 load id and 1 spc id per part
	SE_2_LOAD_D={}
	
	for se_id in Bdf_2_SE_D.keys():
		bdf_file=Bdf_2_SE_D[se_id]
		include_files=SE_D[bdf_file]
		for tmp_file in include_files:
			tmp_load_T=[]
			tmp_spc_T=[]
			if tmp_file.find('Loads_Constraints') > 0:
				print("File: " +  tmp_file + " found...extract Load_id and SPC_id" )
				tmp_file_h=open(tmp_file,'r')
				tmp_lines=tmp_file_h.readlines()
				tmp_file_h.close()
				for tmp_line in tmp_lines:
					if tmp_line.find('FORCE') ==0 or tmp_line.find('PLOAD') ==0 or tmp_line.find('SPCD') ==0:
						tmp_load_id=int(tmp_line[8:16])
						if tmp_load_id not in tmp_load_T:
							tmp_load_T.append(tmp_load_id)
					elif tmp_line.find('SPC ') == 0 or tmp_line.find('SPC1 ') == 0:
						# check if there are SPCADD's in here...
						tmp_spc_id=int(tmp_line[8:16])
						if tmp_spc_id not in tmp_spc_T:
							tmp_spc_T.append(tmp_spc_id)
					else:
						pass

			else:
				pass
	
		# assign loads and SPC's to SE
		SE_2_LOAD_D[se_id]=[tmp_load_T,tmp_spc_T]
		
		
	
	# write back
	top_file_h=open(top_bdf_file,'w')
	n=0
	N=len(lines)
	while n < N:
		if lines[n].find('SUBCASE') ==0:
			se_list=sorted(Bdf_2_SE_D.keys())
			# skipping SEO for now
			for se_id in se_list[1:]:

				top_file_h.write('SUBCASE ' + str(se_id) + '\n' )
				top_file_h.write('   SUPER = ' + str(se_id) + '\n'  )
				
				if len(SE_2_LOAD_D[se_id][0]) == 0:
					print("No Internal loads for SE: " +str(se_id) + '\n' )
				elif len(SE_2_LOAD_D[se_id][0]) == 1:
					top_file_h.write('   LOAD = ' + str(SE_2_LOAD_D[se_id][0][0]) + '\n' )
				else:
					print("WARNING: there are multiple loadiongs for se: " + str(se_id) + " this is currently not suppreted")
					
				if len(SE_2_LOAD_D[se_id][1]) == 0:
					print("No SPCs for SE: " +str(se_id) )
				elif len(SE_2_LOAD_D[se_id][1]) == 1:
					top_file_h.write('   SPC = ' + str(SE_2_LOAD_D[se_id][1][0]) + '\n')
				else:
					print("WARNING: there are multiple loadiongs for se: " + str(se_id) + " this is currently not suppreted")
			
			# now print the SE 0 SUBCASE...renumber SUBCASE id
			top_file_h.write('SUBCASE 100000\n')
			top_file_h.write('   SET 99=0\n')
			top_file_h.write('   SUPER 99\n')
		
		elif lines[n].find('LOAD =') > -1 or lines[n].find('SPC =') > -1:
			# the load and spc cards are writte with their Super elements
			pass
			
		elif lines[n].find('BEGIN BULK') == 0:
			top_file_h.write(lines[n])
			#for aset_id in AsetD.keys() :
			#	top_file_h.write("%-8s%8d%8d\n" % ("ASET",AsetD[aset_id],aset_id) )
			for aset_grid_line in AsetGridsL:
				top_file_h.write(aset_grid_line)
				
		elif lines[n].find('include') == 0:		
			# only print include files that are not linked to SE
			if lines[n].find('\\' + se0_prefix) > 0:
				top_file_h.write(lines[n].strip() + "\n" )
			else:
				pass
				
		elif lines[n].find('ENDDATA') == 0:
			# print BEGIN SUPER for each SE
			for se_id in se_list[1:]:
				top_file_h.write('BEGIN SUPER = ' + str(se_id) + '\n'  )
				se_file=Bdf_2_SE_D[se_id]
				for include_file in SE_D[se_file]:
					top_file_h.write("include '" + include_file.strip().replace(master_dir,'') + "'" + "\n" )
				for xtra_line in SExtraCardsD[se_file]:
					top_file_h.write(xtra_line)
			# write ENDDATA		
			top_file_h.write(lines[n])
				
				
		else:
			top_file_h.write(lines[n])
			
		n+=1
		
def new_process_generate_se(bdf_file, SettingsD, super_elem_start_id, no_of_modes = 10, dmigop2unit = 26, reduction_type = 'DMIGOP2', execute_nastran = False):
	SpcId = 888888

	# RunNastran=True
	# NoOfModes=10
	# SuperElemGenTemplate=se_generate_template.txt
	# OutputType=DMIGOP2
	# DefaultSuperElemId=10

	# read existing bdf file
	print(" I am reading " + bdf_file)
	bdf_h = open(bdf_file, 'r')
	bdf_lines = bdf_h.readlines()
	bdf_h.close()

	# take out the Case Control Cards Added my Apex
	# if line.find('SOL 101') > -1 or line.find('CEND') > -1 or line.find('BCONTACT') > -1:
	try:
		bdf_lines.remove('SOL 101\n')
	except:
		pass
	try:
		bdf_lines.remove('SOL 103\n')
	except:
		pass

	try:
		bdf_lines.remove('CEND\n')
	except:
		pass
	try:
		bdf_lines.remove('	BCONTACT = 1\n')
	except:
		pass
	try:
		bdf_lines.remove('BEGIN BULK\n')
	except:
		pass
	# ForceStr=ReadForces(bdf_lines,LoadId)
	
	SpcStr, SpcL = ReadSPCs(bdf_lines, SpcId)
	ConctactStrL, BConprgD = ReadContacts(bdf_lines)

	#DisplaySet = ReadDisplayElems(bdf_lines, "Display.bdf")

	aset_nodes, asetD = find_aset_nodes(bdf_lines)
	# print aset_nodes
	print(asetD)
	# Find all nodes to find max node id
	NodeD = ReadGrids(bdf_lines)
	# print "NodeD"
	# print NodeD
	max_id = max(NodeD.keys())
	print("max id: " + str(max_id))

	# get all RBE3s  attached to an aset node (with or without zero mass)
	AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)
	AsetRbe2D = ReadRbe2s(bdf_lines, aset_nodes)
	#

	# regenerate bdf_lines with updated RBE3s
	bdf_h = open(bdf_file, 'r')
	bdf_lines = bdf_h.readlines()
	bdf_h.close()

	# also need new AsetRbe3D for PLOTELs
	AsetRbe3D = ReadRbe3s(bdf_lines, aset_nodes)
	
	template_lines=[]
	template_lines.append('TITLE= Deck exported from Apex  GrizzlyUtils  \n')
	template_lines.append('SUBTITLE= SE xx, reduction\n')
	template_lines.append('ECHO = SORT(PARAM)\n')
	template_lines.append('AUTOSPC (NOPRINT, EPS=1.E-6)=YES\n')
	template_lines.append('$  SPC = xxx    added automatically\n')
	template_lines.append('$SET 1001=33\n')
	template_lines.append('$DISP(PRINT,SORT2,PHASE)=1001\n')
	template_lines.append('EXTSEOUT(ASMBULK,EXTBULK,EXTID=xx,DMIGOP2=YY)\n')
	template_lines.append('METHOD=100\n')
	template_lines.append('BEGIN BULK\n')
	template_lines.append('PARAM,MAXRATIO,1.E+7\n')
	template_lines.append('PARAM,GRDPNT,0\n')
	template_lines.append('PARAM,POST,-1\n')
	template_lines.append('PARAM,COUPMASS,1\n')
	template_lines.append('PARAM,WTMASS,1.0\n')
	template_lines.append('PARAM,USETPRT,0\n')
	template_lines.append('PARAM,USETSTR,A\n')
	template_lines.append('$\n')
	template_lines.append('$\n')
	template_lines.append('PARAM,AUTOQSET,YES\n')
	template_lines.append('\n')
	template_lines.append('$SPOINT,1001,THRU,1010\n')
	template_lines.append('$QSET1,0,1001,THRU,1010	\n')
		
	# write back

	print(" I am writing " + bdf_file)
	bdf_h = open(bdf_file, 'w')
	for line in template_lines:

		#  make sure mnf line and cont line is contained within 80 chars
		if line.find('ASSIGN OUTPUT2') == 0:
			#print "I am in 1"
			tmp_line = line.replace('UNIT=YY', 'UNIT=' + '%d' % (dmigop2unit)).replace('sexx', 'se' + '%d' % (
				super_elem_start_id))
			bdf_h.write(tmp_line.strip() + "\n")
		elif line.find('EXTSEOUT') == 0:
			tmp_line = line.replace('EXTID=xx', 'EXTID=' + '%d' % (super_elem_start_id)).replace('DMIGOP2=YY',
																								 'DMIGOP2=' + '%d' % (
																									 dmigop2unit))
			bdf_h.write(tmp_line.strip() + "\n")

		elif line.find('METHOD') > -1:
			# check if there is a contact table, then, write BCONTACT card just above subcase level
			if len(ConctactStrL) != 0:
				print("Generating ContactStrL")
				for lin in ConctactStrL:
					if lin[0:7] == 'BCTABL1':
						print(lin)
						BCTabLId = (int(lin[8:16]))
						bdf_h.write("%-8s = %d\n" % ("BCONTACT", BCTabLId))
			# end check BCTABLE
			bdf_h.write(line)
			# bdf_h.write("   LOAD=" + str(LoadId) + "\n")
			if len(SpcStr) != 0:
				bdf_h.write("   SPC=" + str(SpcId) + "\n")
			if len(ConctactStrL) == 1:
				bdf_h.write("   " + ConctactStrL[0])
			elif len(ConctactStrL) > 1:
				bdf_h.write("   " + ConctactStrL[0])
				bdf_h.write(
					"$ Warning: File exported from Apex contained several BCTABL1 cards, only one supported in this version. first occurrence is used\n")
		elif line.find('LOAD = yyy') > -1 or line.find('SPC = xxx') > -1:
			print("skip placeholder comment")


		elif line.find('BEGIN BULK') > -1:
			# check if there are plot-sets
			PlotelL = []

			bdf_h.write(line)
			# Obsolete - write Aset cards
			#write_ASET(aset_nodes,bdf_h)
			# write PLOTELs


			# write PARAMs
			# bdf_h.write("%-8s%8s%8s\n" % ("PARAM","AUTOQSET","YES"))
			# bdf_h.write("%-8s%8s%8d\n" % ("PARAM","GRDPNT",0))
			# added to mnf header file
			# bdf_h.write("DTI,UNITS,1,MGG,N,MM,S\n")
			# EIGRL to extract requested no of modes
			bdf_h.write("%-8s%8d%8s%8s%8d\n" % ("EIGRL", 100, "", "", no_of_modes))

			# bdf_h.write(ForceStr + "\n")
			bdf_h.write(SpcStr + "\n")
			
		else:
			bdf_h.write(line)

	replace_nodes = []

	MergeD = {}
	for aset_node in aset_nodes:
		MergeD[aset_node] = asetD[aset_node][0]
	# also need to replace any grids that are already taken

	for aset_node in aset_nodes:
		if asetD[aset_node][0] == aset_node:
			# no renumber for this
			print("Aset node with id: " + str(aset_node) + " doesn't need renumbering")
		# delete from MergeD change Aug 18th
		# del MergeD[aset_node]

		else:
			#print "I am in  2"
			#print asetD
			# check if new node is "taken" then assign new id to the taken node
			print(asetD[aset_node][0])
			print("===")
			if asetD[aset_node][0] in list(NodeD.keys()):
				#print "I am in 2b"
				max_id += 1
				MergeD[asetD[aset_node][0]] = max_id
				replace_nodes.append(asetD[aset_node][0])

			# for aset_node in aset_nodes:
		replace_nodes.append(aset_node)

	# append PloteL to bdf_lines, handle renumbering in MergeGrids
	# check if last line is an ENDDATA
	if bdf_lines[-1].find('ENDDATA') == 0:
		del bdf_lines[-1]
		bdf_lines.extend(PlotelL)
		bdf_lines.append('ENDDATA\n')
	else:
		bdf_lines.extend(PlotelL)

	# print replace_nodes
	# print MergeD

	# merge grids
	# print "aaa"
	# print MergeD
	print("#####replace nodes####")
	print(replace_nodes)
	#
	print("####MergeD####")
	print(MergeD)
	


	print("sol103 nastran cards added")



	if execute_nastran == 'True':
		# apply the nastran options for mnf gerneatio
		xtra_args = []

		print("...running nastran")
		# xtra_args=[]
		out_msg = run_nastran(bdf_file, SettingsD, xtra_args)
	# out_msg=run_nastran(bdf_file)
	else:
		out_msg = "**** SE reductrion file created"
	#
	return out_msg


	#print(BonectD)
	#print((list(new_BSurfD.keys())))	

##########################	
	
if __name__ == "__main__":
	pass
