[D0025] NVH用の電磁力出力(表面力)

 

モータNVH用にトルク、電磁力をCSVファイル出力する。

import locale
import os
import designer
import subprocess
import csv
import time

CSV_VERSION = "1.0.0"
OUTPUT_FILE_PATH = "outputFilePath"
SET_PREFIX = "prefix"
TORQUE_COND_TITLE = "condition_title"
#for surface force
INVERSE_RESULT = "inverse_result"
TEETH_CENTER_SPECIFYING_TYPE = "teeth_center_specifying_type"
TEETH_CENTER_DIR_X = "teeth_center_dir_x"
TEETH_CENTER_DIR_Y = "teeth_center_dir_y"
TEETH_CENTER_DIR_Z = "teeth_center_dir_z"
TEETH_CENTER_POS_X = "teeth_center_pos_x"
TEETH_CENTER_POS_Y = "teeth_center_pos_y"
TEETH_CENTER_POS_Z = "teeth_center_pos_z"
NUM_POLES = "numPoles"
COORD_TYPE = "coord_type"
USE_COPY_CYCLE = "use_copy_cycle"
DIVIDE_TEETH_FORCE = "divide_teeth_force"
AXIAL_COORDINATES = "axial_coordnates"
OUTPUT_FORMAT_TYPE = "output_format_type"
OUTPUT_CASE_TYPE = "output_case_type"
SPECIFIED_CASES = "specified_cases"
EPSILON = 0.000001
OUTPUT_DIMENSION = 3

app = designer.GetApplication()

def main():
	app.SetShowProgressDialog(False)
	if (check_model(app) == False):
		return
	
	parameters = get_parameters_from_dialog(app)
	if (check_dialog_parameters(app, parameters) == False):
		return
	
	output_csv(app, parameters)

def check_model(app):

	model = app.GetCurrentModel()
	study = app.GetCurrentStudy()

	if (model.IsValid() == False):
		message = "No model."
		message_jp = "モデルがありません。"
		show_error_message(message, message_jp)
		return False

	setList = model.GetSetList()
	if (setList.NumSet()<1):
		message = "There is no set."
		message_jp = "セットがありません。"
		show_error_message(message, message_jp)
		return False

	if (study.IsValid() == False):
		message = "No study."
		message_jp = "スタディがありません。"
		show_error_message(message, message_jp)
		return False
	
	motionList = get_conditions(study, "RotationMotion")
	if (len(motionList)<1):
		message = "Current study has no rotation motion condition."
		message_jp = "現スタディは回転運動条件がありません。"
		show_error_message(message, message_jp)
		return False
	
	surf_forceList = get_conditions(study, "Torque_Surface")
	if (len(surf_forceList)<1):
		message = "Current study has no force condition."
		message_jp = "現スタディは表面力条件がありません。"
		show_error_message(message, message_jp)
		return False
	
	if (study.AnyCaseHasResult() == False):
		message = "Current study has no result."
		message_jp = "現スタディは結果がありません。"
		show_error_message(message, message_jp)
		return False
	
	if (has_zero_speed(app)==True):
		message = "There is the case that rotation speed is zero. Please review setting."
		message_jp = "回転速度がゼロのケースがあります。設定を見直してください。"
		show_error_message(message, message_jp)
		return False
	
	return True

def show_error_message(message, message_jp):
	msgdlg = app.CreateDialogBox()
	title = "Error"
	title_jp = "エラー"
	msgdlg.SetTranslation(title, title_jp)
	msgdlg.SetTranslation(message, message_jp)
	msgdlg.SetCancelButtonVisible(False)
	msgdlg.SetTitle(title)
	msgdlg.AddLabel(message)
	msgdlg.Show()

def show_warning_message(message, message_jp):
	msgdlg = app.CreateDialogBox()
	title = "Warning"
	title_jp = "警告"
	msgdlg.SetTranslation(title, title_jp)
	msgdlg.SetTranslation(message, message_jp)
	msgdlg.SetCancelButtonVisible(False)
	msgdlg.SetTitle(title)
	msgdlg.AddLabel(message)
	msgdlg.Show()

def get_parameters_from_dialog(app):
	dialog = app.CreateDialogBox()
	setup_param_input_dialog(app, dialog)
	dialog.Show()
	return dialog

def setup_param_input_dialog(app, dialog):
	model = app.GetCurrentModel()
	study = app.GetCurrentStudy()
	modelDimension = model.GetDimension()
	is2D = (modelDimension == 2)
	isCutPlane = study.IsCutPlaneStudy()
	
	dialogTitle = "JMAG-Designer: NVH Force Export Setting"
	dialogTitle_jp = "JMAG-Designer: NVH電磁力出力設定"
	dialog.SetTranslation(dialogTitle, dialogTitle_jp)

	label_1 = "Output File:"
	label_1_jp = "出力ファイル:"
	dialog.SetTranslation(label_1, label_1_jp)

	label_2 = "Set Prefix:"
	label_2_jp = "セットの先頭文字:"
	dialog.SetTranslation(label_2, label_2_jp)

	label_3 = "Torque Condition:"
	label_3_jp = "トルク条件:"
	dialog.SetTranslation(label_3, label_3_jp)
	
	# for surface force setting
	label_3_1 = "Invert the result of electromagnetic force"
	label_3_1_jp = "電磁力の結果を反転する"
	dialog.SetTranslation(label_3_1, label_3_1_jp)

	label_3_2 = "Center of Teeth:"
	label_3_2_jp = "ティースの中心軸:"
	dialog.SetTranslation(label_3_2, label_3_2_jp)

	label_3_2_a = "Same as torque condition:"
	label_3_2_a_jp = "トルク条件の軸と同じ"
	dialog.SetTranslation(label_3_2_a, label_3_2_a_jp)
	
	label_3_2_b = "Specify the central axis with a direction vector and a point on the axis"
	label_3_2_b_jp = "軸方向ベクトルと軸上の点で指定"
	dialog.SetTranslation(label_3_2_b, label_3_2_b_jp)

	label_3_2_b_dir = "Axis Direction:"
	label_3_2_b_dir_jp = "軸方向ベクトル:"
	dialog.SetTranslation(label_3_2_b_dir, label_3_2_b_dir_jp)

	label_3_2_b_pos = "Point on Axis:"
	label_3_2_b_pos_jp = "軸上の1点:"
	dialog.SetTranslation(label_3_2_b_pos, label_3_2_b_pos_jp)

	label_4 = "Number of Poles:"
	label_4_jp = "極数:"
	dialog.SetTranslation(label_4, label_4_jp)

#	label_5 = "Force Coordinates:"
#	label_5_jp = "節点力の座標系:"
#	dialog.SetTranslation(label_5, label_5_jp)

	label_5 = "Cycle:"
	label_5_jp = "周期:"
	dialog.SetTranslation(label_5, label_5_jp)

	label_6 = "Rectangular"
	label_6_jp = "直交座標系"
	dialog.SetTranslation(label_6, label_6_jp)

#	label_7 = "Cylindrical"
#	label_7_jp = "円筒座標系"
#	dialog.SetTranslation(label_7, label_7_jp)

	label_8 = "Copy 1 electrical cycle to mechanical cycle"
	label_8_jp = "電気角1周期分をコピーして機械角1周期分にする"
	dialog.SetTranslation(label_8, label_8_jp)

	label_16 = "Output Option:"
	label_16_jp = "出力オプション:"
	dialog.SetTranslation(label_16, label_16_jp)

	label_17 = "Divide the teeth force in the axial direction(Sample:0.0, 0.01, 0.02, 0.03)"
	label_17_jp = "ティースの電磁力を軸方向に分ける(例:0.0, 0.01, 0.02, 0.03)"
	dialog.SetTranslation(label_17, label_17_jp)

	label_9 = "Output Cases:"
	label_9_jp = "出力ケース:"
	dialog.SetTranslation(label_9, label_9_jp)

	label_10 = "All Cases"
	label_10_jp = "全ケース"
	dialog.SetTranslation(label_10, label_10_jp)

	label_11 = "Specified Cases (Example: 1, 2, 5-8)"
	label_11_jp = "指定ケース(例: 1, 2, 5-8)"
	dialog.SetTranslation(label_11, label_11_jp)

	label_12 = "Output Format:"
	label_12_jp = "出力フォーマット:"
	dialog.SetTranslation(label_12, label_12_jp)

	label_13 = "Single File Including Torque(Force Coordinates=Cylindrical)"
	label_13_jp = "トルクデータ含む1ファイル出力(電磁力の座標系=円筒座標系)"
	dialog.SetTranslation(label_13, label_13_jp)

	label_14 = "File Per Case: Time Domain Tooth Forces(Force Coordinates=Rectangular)"
	label_14_jp = "ケースごとにファイル出力:時刻依存性ティース電磁力(電磁力の座標系=直交座標系)"
	dialog.SetTranslation(label_14, label_14_jp)

	label_15 = "File Per Case: Frequency Domain Tooth Forces(Force Coordinates=Rectangular)"
	label_15_jp = "ケースごとにファイル出力:周波数依存性ティース電磁力(電磁力の座標系=直交座標系)"
	dialog.SetTranslation(label_15, label_15_jp)

	dialog.SetTitle(dialogTitle)
	dialog.AddLabel(label_1, 0, 1)
	dialog.AddSaveFilename(OUTPUT_FILE_PATH, "", "", "*.csv", 1, 2)
	dialog.AddLabel(label_2, 0, 1)
	dialog.AddString(SET_PREFIX, "", "TOOTH", 1, 2)

	dialog.AddLabel(label_3, 0, 1)
	dialog.AddComboBox(TORQUE_COND_TITLE, "", torque_condition_title_list(app), 0, 1, 2)
	# for surface force
	dialog.AddLabel("", 0, 1)
	dialog.AddCheckBox(INVERSE_RESULT, label_3_1, True, 1, 2)
	dialog.AddLabel(label_3_2, 0, 1)
	dialog.AddRadio(TEETH_CENTER_SPECIFYING_TYPE, label_3_2_a, 0, 1, 2)
	dialog.AddLabel("", 0, 1)
	dialog.AddRadio(TEETH_CENTER_SPECIFYING_TYPE, label_3_2_b, 1, 1, 2)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_3_2_b_dir, 1, 1)
	dialog.AddReal(TEETH_CENTER_DIR_X, "X:", 0.0, 2, 1)
	dialog.AddLabel("", 0, 2)
	dialog.AddReal(TEETH_CENTER_DIR_Y, "Y:", 0.0, 2, 1)
	dialog.AddLabel("", 0, 2)
	dialog.AddReal(TEETH_CENTER_DIR_Z, "Z:", 1.0, 2, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_3_2_b_pos, 1, 1)
	dialog.AddReal(TEETH_CENTER_POS_X, "X:", 0.0, 2, 1)
	dialog.AddLabel("m", 3, 1)
	dialog.AddLabel("", 0, 2)
	dialog.AddReal(TEETH_CENTER_POS_Y, "Y:", 0.0, 2, 1)
	dialog.AddLabel("m", 3, 1)
	dialog.AddLabel("", 0, 2)
	dialog.AddReal(TEETH_CENTER_POS_Z, "Z:", 0.0, 2, 1)
	dialog.AddLabel("m", 3, 1)
	# for surface force(end)

	dialog.AddLabel(label_4, 0, 1)
	dialog.AddInteger(NUM_POLES, "", 4, 1, 2)
	dialog.AddLabel(label_5)
	#dialog.AddRadio(COORD_TYPE, label_7, 1, 1)
	#dialog.AddRadio(COORD_TYPE, label_6, 0, 1)
	dialog.AddCheckBox(USE_COPY_CYCLE, label_8, True, 1, 2)

	if(not is2D and not isCutPlane):
		dialog.AddLabel(label_16, 0, 1)
		dialog.AddCheckBox(DIVIDE_TEETH_FORCE, label_17, False, 1, 2)
		dialog.AddLabel("", 0, 1)
		dialog.AddString(AXIAL_COORDINATES, "", "0.0, 0.01, 0.02", 1, 2)
		dialog.AddLabel("m", 3, 1)

	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_12, 0, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_13, 0, 1, 2)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_14, 1, 1, 2)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_15, 2, 1, 2)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_9, 0, 1)
	dialog.AddRadio(OUTPUT_CASE_TYPE, label_10, 0, 1, 1)
	dialog.AddRadio(OUTPUT_CASE_TYPE, label_11, 1, 1, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddIntegerList(SPECIFIED_CASES, "", "1", 1, 2)

def check_dialog_parameters(app, parameters):
	if (parameters.WasCancelled()):
		return False
	if (parameters.GetValue(OUTPUT_FILE_PATH)==""):
		message = "Output file path is invalid."
		message_jp = "出力ファイルパスが不正です。"
		show_error_message(message, message_jp)
		return False
	
	model = app.GetCurrentModel()
	setList = model.GetSetList()
	targetSetList = get_target_set(model, parameters)
	if (len(targetSetList)<1):
		message = "Can not find sequential number set by the specified prefix."
		message_jp = "指定された頭文字では連番セットが見つかりませんでした。"
		show_error_message(message, message_jp)
		return False

	if (parameters.GetValue(TEETH_CENTER_SPECIFYING_TYPE) == 1):
		if (parameters.GetValue(TEETH_CENTER_DIR_X) == 0.0):
			if (parameters.GetValue(TEETH_CENTER_DIR_Y) == 0.0):
				if (parameters.GetValue(TEETH_CENTER_DIR_Z) == 0.0):
					message = "Center of Teeth is invalid. Please set the axis direction so that it does not become a zero vector."
					message_jp = "ティースの中心軸が不正です。軸の方向が零ベクトルにならないように設定してください。"
					show_error_message(message, message_jp)
					return False
	
	if (parameters.GetValue(NUM_POLES) % 2 != 0):
		message = "The number of poles is invalid. Please set even number."
		message_jp = "極数が不正です。偶数を設定してください。"
		show_error_message(message, message_jp)
		return False
	
	coords = []
	is3D = (model.GetDimension()==3)
	isCutPlane = app.GetCurrentStudy().IsCutPlaneStudy()
	if (is3D and not isCutPlane and parameters.GetValue(DIVIDE_TEETH_FORCE) == 1):
		coords = [x.strip() for x in parameters.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
		if(len(coords)<2):
			message = "Coordinate values of output option is invalid. Please set two or more coordinate values by comma-delimited array."
			message_jp = "出力オプションの座標値が不正です。カンマ区切りで座標値を2つ以上設定してください。"
			show_error_message(message, message_jp)
			return False
		for i in range(len(coords)-1):
			value1 = 0.0
			value2 = 0.0
			try:
				value1 = float(coords[i])
				value2 = float(coords[i+1])
			except ValueError:
				message = "Coordinate values of output option is invalid. Please set the coordinate values as numerical value."
				message_jp = "出力オプションの座標値が不正です。座標値は数値を設定してください。"
				show_error_message(message, message_jp)
				return False

			if(value1 > value2):
				message = "Coordinate values of output option is invalid. Please set the coordinate values in ascending order."
				message_jp = "出力オプションの座標値が不正です。座標値は昇順に設定してください。"
				show_error_message(message, message_jp)
				return False

	outputCaseType = parameters.GetValue(OUTPUT_CASE_TYPE)
	caseIndexList = []
	if (outputCaseType == 1): # Specified Cases
		study = app.GetCurrentStudy()
		caseNoList = parameters.GetValueAsIntegerList(SPECIFIED_CASES)
		caseIndexList = get_caseIndexList_from_caseNoList(study, caseNoList)
		if (len(caseIndexList)==0):
			message = "There is no valid case. Please review the setting on Specified Cases."
			message_jp = "有効なケースがありません。指定ケースの設定を見直してください。"
			show_error_message(message, message_jp)
			return False
	else:
		study = app.GetCurrentStudy()
		numCases = study.GetDesignTable().NumCases()
		caseIndexList = range(numCases)
	
	hasNot1CycleStepCases = False
	hasNot1CycleStepCases = check_has_not_1cycle_step_cases(app, parameters, caseIndexList)
	if (hasNot1CycleStepCases):
		message = "Insufficient number of result time steps. The file output must have more than one cycle mechanical angle or more than one cycle electrical angle time step number. Please check the results of the specified cases."
		message_jp = "結果の時間ステップ数が不十分です。ファイル出力には1周期機械角分または1周期電気角分の時間ステップ数以上の結果が必要です。指定ケースの結果を見直してください。"
		show_error_message(message, message_jp)
		return False
	
	return True

def output_csv(app, dialog):
	outputFormatType = dialog.GetValue(OUTPUT_FORMAT_TYPE)
	
	if (outputFormatType == 0):
		output_single_file_including_torque(app, dialog)
	elif (outputFormatType == 1):
		output_time_domain_tooth_forces(app, dialog)
	elif (outputFormatType == 2):
		output_frequency_domain_tooth_forces(app, dialog)

def get_node_list_on_tooth1(targetStudy, targetSetList):
	removedList = []
	curset = targetSetList[0]
	selectedIds = []
	sel = curset.GetSelection()
	if sel.NumEdges()>0:
		for i in range(sel.NumEdges()):
			selectedIds.append(sel.EdgeID(i))
	elif sel.NumFaces()>0:
		for i in range(sel.NumFaces()):
			selectedIds.append(sel.FaceID(i))
		
	nodeIds = []
	for entityId in selectedIds:
		if sel.NumEdges()>0:
			nodeIds.extend(targetStudy.GetNodeIdsOnEdge(entityId))
		elif sel.NumFaces()>0:
			nodeIds.extend(targetStudy.GetNodeIdsOnFace(entityId))
		
	##Remove duplicate nodes
	nodeIds = list(set(nodeIds))
	##Remove nodes on boundary
	nodeIds = remove_nodes_on_boundary(targetStudy, nodeIds)
	return nodeIds


class SubModuleSetting:
	outputFilePath = ""
	setPrefix = ""
	selectedIndex = -1
	invertResult = False
	teethCenterType = 0
	teethCenterDirX = 0.0
	teethCenterDirY = 0.0
	teethCenterDirZ = 0.0
	teethCenterPosX = 0.0
	teethCenterPosY = 0.0
	teethCenterPosZ = 0.0
	outputFormatType = 0
	divideTeethForceFlag = 0
	aixalCoords = []
	is3D = 0
	def __init__(self, app, dialog):
		self.is3D = (app.GetCurrentModel().GetDimension()==3)
		self.outputFilePath = dialog.GetValue(OUTPUT_FILE_PATH).decode('utf-8')
		self.setPrefix = dialog.GetValue(SET_PREFIX).decode('utf-8')
		self.selectedIndex = dialog.GetValue(TORQUE_COND_TITLE)
		self.invertResult = dialog.GetValue(INVERSE_RESULT)
		self.teethCenterType = dialog.GetValue(TEETH_CENTER_SPECIFYING_TYPE)
		self.teethCenterDirX = dialog.GetValue(TEETH_CENTER_DIR_X)
		self.teethCenterDirY = dialog.GetValue(TEETH_CENTER_DIR_Y)
		self.teethCenterDirZ = dialog.GetValue(TEETH_CENTER_DIR_Z)
		self.teethCenterPosX = dialog.GetValue(TEETH_CENTER_POS_X)
		self.teethCenterPosY = dialog.GetValue(TEETH_CENTER_POS_Y)
		self.teethCenterPosZ = dialog.GetValue(TEETH_CENTER_POS_Z)
		self.outputFormatType = dialog.GetValue(OUTPUT_FORMAT_TYPE)
		if (not self.is3D or app.GetCurrentStudy().IsCutPlaneStudy()) :
			self.divideTeethForceFlag = 0
		else :
			self.divideTeethForceFlag = dialog.GetValue(DIVIDE_TEETH_FORCE)
			self.aixalCoords = [x.strip() for x in dialog.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
	def write_conf_file(self):
		file = open(self.get_file_path(), 'w')
		write_line2(file,"outputPath", self.outputFilePath)
		write_line2(file,"setPrefix", self.setPrefix)
		write_line2(file, "selectedIndex", str(self.selectedIndex))
		write_line2(file, "invertResult", str(self.invertResult))
		write_line2(file, "teethCenterType", str(self.teethCenterType))
		write_line2(file, "teethCenterDirX", str(self.teethCenterDirX))
		write_line2(file, "teethCenterDirY", str(self.teethCenterDirY))
		write_line2(file, "teethCenterDirZ", str(self.teethCenterDirZ))
		write_line2(file, "teethCenterPosX", str(self.teethCenterPosX))
		write_line2(file, "teethCenterPosY", str(self.teethCenterPosY))
		write_line2(file, "teethCenterPosZ", str(self.teethCenterPosZ))
		write_line2(file, "outputFormatType", str(self.outputFormatType))
		if(self.is3D and self.divideTeethForceFlag==1):
			file.write("stackpos")
			for coord in self.aixalCoords:
				file.write(", " + str(coord))
			file.write("\n")
		file.close()
	def get_file_path(self):
		dir = os.path.dirname(self.outputFilePath)
		filePath = os.path.join(dir, "NVHSubmod.conf")
		return filePath
		
class SubModuleRunner:
	modulePath = ""
	setting = []
	def __init__(self, app, dialog):
		moduleName = "NVHSurfaceForceExtractor"
		if (os.name == "nt"):
			moduleName += ".exe"
		moduleDir = app.GetAppDir()
		self.modulePath = moduleDir + "/" + moduleName
		self.setting = SubModuleSetting(app,dialog)
		self.setting.write_conf_file()

	def __del__(self):
		os.remove(self.setting.get_file_path())

	def readstep(self, stepFilePath):
		step = 0
		try:
			f = open(stepFilePath)
			line = f.readline()
			step = int(line.strip())
			f.close()
		except:
			pass

		return step

	def run(self, app, study, caseIndex, useMultiSlice, sliceIndex, targetSetList, steps):
		jplotPath = self.get_jplot_path(study, caseIndex, useMultiSlice, sliceIndex)
		workingDir = os.path.dirname(self.setting.outputFilePath)
		filename = "submodoutput" +str(caseIndex)+"_"+str(sliceIndex)+".csv"
		subModSettingPath = self.setting.get_file_path()
		subModOutputPath = os.path.join(workingDir , filename)
		tooth1NodeFilePath = os.path.join(workingDir, "tooth1.dat")
		tooth1NodeIds = get_node_list_on_tooth1(study, targetSetList)
		stepFilePath = os.path.join(workingDir , "step.txt")
		self.write_node_ids(len(targetSetList), tooth1NodeIds, tooth1NodeFilePath)
		#runResult = subprocess.run([self.modulePath, jplotPath, subModSettingPath, subModOutputPath, tooth1NodeFilePath], cwd=workingDir)
		runResult = subprocess.Popen([self.modulePath, jplotPath, subModSettingPath, subModOutputPath, tooth1NodeFilePath], cwd=workingDir, stdout=subprocess.PIPE)
		count = 0
		wasCanceled = False
		while runResult.poll() is None:
			time.sleep(0.5)

			if (app.UserProgressWasCanceled()):
				app.UserProgressFinish()
				wasCanceled = True
				runResult.stdout.close()
				runResult.kill()
				break

			if os.path.isfile(stepFilePath):
				try:
					step = 0
					step = self.readstep(stepFilePath)
					for i in range(count+1,step+1):
						app.UserProgressStep()
						count+=1
				except:
					pass

		if os.path.isfile(tooth1NodeFilePath):
			os.remove(tooth1NodeFilePath)
		if os.path.isfile(stepFilePath):
			os.remove(stepFilePath)
		if(wasCanceled):
			resultData = []
			return (resultData, wasCanceled)

		if (count < steps):
			for i in range(count+1,steps+1):
				app.UserProgressStep()

		with open(subModOutputPath) as f:
			reader = csv.reader(f)
			l = [row for row in reader]
		resultData = [[float(v) for v in row] for row in l]
		os.remove(subModOutputPath)
		return (resultData, wasCanceled)

	def get_jplot_path(self, study, caseIndex, useMultiSlice, sliceIndex):
		folder = study.CalculationFolder(caseIndex + 1)+os.path.sep
		filename = "Designer.jplot"
		if (useMultiSlice):
			filename = "Designer_slice_"+str(sliceIndex+1)+".jplot"
		jplotPath = os.path.join(folder, filename)
		return jplotPath

	def write_node_ids(self, nTooth, tooth1NodeIds, tooth1NodeFilePath):
		file = open(tooth1NodeFilePath, 'w')
		write_line(file, str(nTooth))
		for nodeId in tooth1NodeIds:
			write_line(file,str(nodeId))
		file.close()
		

def output_single_file_including_torque(app, dialog):
	outputPath = dialog.GetValue(OUTPUT_FILE_PATH)
	file = open(outputPath.decode('utf-8'), 'w')
	
	write_header(file, app, dialog)
	[caseIndexList, noResultCaseIndexList, speedList, avTorqList, startIndices, outX, outYT] = create_torque_data(app, dialog)
	write_torque(file, speedList, avTorqList, outX, outYT)
	wasCanceled = write_force(file, app, dialog, caseIndexList, speedList, avTorqList, startIndices, outX)
	
	file.close()
	
	ShowFinishMessage(app, wasCanceled)

def output_time_domain_tooth_forces(app, dialog):

	## Get app data
	targetStudy = app.GetCurrentStudy()
	targetModel = app.GetCurrentModel()

	## Get dialog values
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	numPoles = dialog.GetValue(NUM_POLES)
	coordType = 0 # Fixed to Global Rectangular
	targetSetList = get_target_set(targetModel, dialog)
	setPrefix = dialog.GetValue(SET_PREFIX).decode('utf-8')

	## Setup parameters
	outputDimension = OUTPUT_DIMENSION
	modelDimension = targetModel.GetDimension()
	is2D = (modelDimension == 2)
	isCutPlane = targetStudy.IsCutPlaneStudy()
	thickness = 0
	useMultiSlice = False
	numSlice = 1
	if (is2D):
		thickness = targetStudy.GetStudyProperties().GetValueWithUnit("ModelThickness", "m")
		useMultiSlice = use_multi_slice(targetStudy)
		numSlice = get_slice_number(targetStudy)
	caseIndexList = get_case_index_list(dialog, targetStudy)
	periodicity = get_periodicity(targetStudy)
	needConvertTwice = (not is2D) and has_symmetry_boundary(targetStudy, dialog)
	divideTeethForceFlag = 0
	if (not is2D and not isCutPlane):
		divideTeethForceFlag = dialog.GetValue(DIVIDE_TEETH_FORCE)
	stepsList = get_numsteps(app, caseIndexList)
	if (divideTeethForceFlag):
		needConvertTwice = False

	## Setup progress dialog
	setup_progress(app, outputDimension, caseIndexList, numSlice, stepsList)
	wasCanceled = False

	## Case loop
	numCases = len(caseIndexList)
	subModuleRunner = SubModuleRunner(app, dialog)
	allCaseData = []
	for caseLoopIndex in range(numCases):
		caseData = []
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)
		for sliceIndex in range(numSlice):
			[sliceData, wasCanceled] = subModuleRunner.run(app, targetStudy, caseIndex, useMultiSlice, sliceIndex, targetSetList, stepsList[caseLoopIndex])
			if(wasCanceled):
				return True

			if(is2D) :
				caseData.append(sliceData)
			else :
				if(divideTeethForceFlag) :
					coords = [x.strip() for x in dialog.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
					numSlice = len(coords)-1
					num_block = len(sliceData[0]) // numSlice
					for i in range(numSlice) :
						start = num_block*i
						end = num_block*(i+1)
						data = []
						for x in sliceData:
							data.append(x[start:end])
						caseData.append(data)
				else :
					caseData.append(sliceData)
		allCaseData.append(caseData)

	for caseLoopIndex in range(numCases):
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)

		if (targetStudy.CaseHasResult(caseIndex) == False):
			continue

		## Slice loop
		for sliceIndex in range(numSlice):

			# Write file
			outputPath = create_file_name(dialog.GetValue(OUTPUT_FILE_PATH), numCases, caseIndex+1, useMultiSlice, numSlice, sliceIndex+1)
			file = open(outputPath.decode('utf-8'), 'w')

			## Write header
			write_header_for_time_domain_tooth_force_only(file, setPrefix, len(targetSetList)*periodicity)
	
			## Get start index for last 1 cycle
			torqResultName = get_torq_result_name(app)
			dataset = targetStudy.GetDataSet(torqResultName, caseIndex+1)
			startIndex = get_last_1_cycle_start_index(dialog, targetStudy, dataset)
			timeData = list(dataset.GetColumn(0))
			timeData = timeData[startIndex:]
			timeData = create_1_electric_cycle_time_to_1_mechanical_cycle_time(useElectricCycle, numPoles, timeData)

			## Each Tooth loop
			fullModelF = []
			##Add Time
			fullModelF.append(timeData)
			
			nfullModelTarget = int(len(allCaseData[caseLoopIndex][sliceIndex][0])/3)
			for setIndex in range(nfullModelTarget):
				eachSetF = []
	
				## Component loop
				for compIndex in range(outputDimension):
					if (app.UserProgressWasCanceled()):
						app.UserProgressFinish()
						wasCanceled = True
						break

					colIndex = setIndex*3 + compIndex;
					data = [allCaseData[caseLoopIndex][sliceIndex][stepIndex][colIndex] for stepIndex in range(len(allCaseData[caseLoopIndex][sliceIndex]))]
					## Considering 1/2 model lengthwise if symmetry boundary is set on XY plane
					convertedData = convert_twice(needConvertTwice, data)
					
					## Get last 1 cycle data
					lastCycleF = convertedData[startIndex:]
					
					##Copy 1 electric cycle to 1 mechanical cycle
					lastCycleF = copy_1_electric_cycle_to_1_mechanical_cycle(useElectricCycle, numPoles, lastCycleF)
					
					##If 2D model times thickness
					if useMultiSlice:
						lastCycleF = [f /numSlice for f in lastCycleF]
					
					##Add each component data
					fullModelF.append(lastCycleF)
					
					if (wasCanceled == False):
						app.UserProgressStep()
											
			##Write TOOTH value of full model
			WriteByAllComponentsInSetForOneCase(file, fullModelF)
			
			file.close()
	
	app.UserProgressFinish()
	
	ShowFinishMessage(app, wasCanceled)

def output_frequency_domain_tooth_forces(app, dialog):
	## Get app data
	targetStudy = app.GetCurrentStudy()
	targetModel = app.GetCurrentModel()
	dataManager = app.GetDataManager()

	## Get dialog values
	numPoles = dialog.GetValue(NUM_POLES)
	coordType = 0 # Fixed to Global Rectangular
	targetSetList = get_target_set(targetModel, dialog)
	setPrefix = dialog.GetValue(SET_PREFIX).decode('utf-8')

	## Setup parameters
	outputDimension = OUTPUT_DIMENSION
	modelDimension = targetModel.GetDimension()
	is2D = (modelDimension == 2)
	isCutPlane = targetStudy.IsCutPlaneStudy()
	useMultiSlice = False
	numSlice = 1
	if (is2D):
		useMultiSlice = use_multi_slice(targetStudy)
		numSlice = get_slice_number(targetStudy)
	caseIndexList = get_case_index_list(dialog, targetStudy)
	periodicity = get_periodicity(targetStudy)
	needConvertTwice = (not is2D) and has_symmetry_boundary(targetStudy, dialog)
	divideTeethForceFlag = 0
	if (not is2D and not isCutPlane):
		divideTeethForceFlag = dialog.GetValue(DIVIDE_TEETH_FORCE)
	stepsList = get_numsteps(app, caseIndexList)
	if (divideTeethForceFlag):
		needConvertTwice = False
		
	## Setup progress dialog
	setup_progress(app, outputDimension, caseIndexList, numSlice, stepsList)
	wasCanceled = False

	## Case loop
	numCases = len(caseIndexList)
	subModuleRunner = SubModuleRunner(app, dialog)
	allCaseData = []
	for caseLoopIndex in range(numCases):
		caseData = []
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)
		for sliceIndex in range(numSlice):
			[sliceData, wasCanceled] = subModuleRunner.run(app, targetStudy, caseIndex, useMultiSlice, sliceIndex, targetSetList, stepsList[caseLoopIndex])
			if(wasCanceled):
				return True

			if(is2D) :
				caseData.append(sliceData)
			else :
				if(divideTeethForceFlag) :
					coords = [x.strip() for x in dialog.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
					numSlice = len(coords)-1
					num_block = len(sliceData[0]) // numSlice
					for i in range(numSlice) :
						start = num_block*i
						end = num_block*(i+1)
						data = []
						for x in sliceData:
							data.append(x[start:end])
						caseData.append(data)
				else :
					caseData.append(sliceData)
		allCaseData.append(caseData)

	for caseLoopIndex in range(numCases):
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)

		if (targetStudy.CaseHasResult(caseIndex) == False):
			continue

		## Slice loop
		for sliceIndex in range(numSlice):

			## Write file
			outputPath = create_file_name(dialog.GetValue(OUTPUT_FILE_PATH), numCases, caseIndex+1, useMultiSlice, numSlice, sliceIndex+1)
			file = open(outputPath.decode('utf-8'), 'w')
	
			## Write header
			write_header_for_frequency_domain_tooth_force_only(file, setPrefix, len(targetSetList)*periodicity)
	
			## Get start index for last 1 cycle
			torqResultName = get_torq_result_name(app)
			dataset = targetStudy.GetDataSet(torqResultName, caseIndex+1)
			startIndex = get_last_1_cycle_start_index(dialog, targetStudy, dataset)
			timeData = list(dataset.GetColumn(0))

			## Each Tooth loop
			fullModelF = []
			
			nfullModelTarget = int(len(allCaseData[caseLoopIndex][sliceIndex][0])/3)
			for setIndex in range(nfullModelTarget):
				eachSetF = []
	
				## Component loop
				for compIndex in range(outputDimension):
					if (app.UserProgressWasCanceled()):
						app.UserProgressFinish()
						wasCanceled = True
						break
					
					colIndex = setIndex*3 + compIndex
					forceData = [allCaseData[caseLoopIndex][sliceIndex][stepIndex][colIndex] for stepIndex in range(len(allCaseData[caseLoopIndex][sliceIndex]))]
					dataSetTitle = "fft_for_nvf_force_export"
					[fftDataSet, wasCanceled] = calc_force_with_FFT(dataManager, timeData, forceData, dataSetTitle, needConvertTwice, startIndex, useMultiSlice, numSlice)
					
					## Real/Imaginary Loop
					numFFTColmuns = fftDataSet.GetCols()
					if (numFFTColmuns < 3):
						continue

					if (setIndex == 0 and compIndex == 0):
						freqData = list(fftDataSet.GetColumn(0))
						## Add Time/Frequency
						fullModelF.append(freqData)

					for realImagIndex in range(1, numFFTColmuns):
						data = list(fftDataSet.GetColumn(realImagIndex))
						##  Add each component data
						fullModelF.append(data)
					
					if (wasCanceled == False):
						app.UserProgressStep()
					
					dataManager.DeleteDataSet(dataSetTitle)
				
			## Write TOOTH value of full model
			WriteByAllComponentsInSetForOneCase(file, fullModelF)
			
			file.close()
	
	app.UserProgressFinish()
	
	ShowFinishMessage(app, wasCanceled)

def write_header(file, app, dialog):
	targetModel = app.GetCurrentModel()
	targetStudy = app.GetCurrentStudy()
	is2D = (targetModel.GetDimension()==2)
	isCutPlane = targetStudy.IsCutPlaneStudy()
	setList = targetModel.GetSetList()
	targetSetList = get_target_set(targetModel, dialog)
	numPoles = dialog.GetValue(NUM_POLES)
	dimString = "3D"
	coordString = get_coordName(1) # Fixed to cylindrical in V18.1
	periodicity = get_periodicity(targetStudy)
	numTeeth = int(len(targetSetList)*periodicity)
	coords = []
	if(not is2D and not isCutPlane):
		coords = [x.strip() for x in dialog.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
	modelThickness = 0
	numSlice = 1
	
	if (is2D):
		dimString = "2D"
		modelThickness = targetStudy.GetStudyProperties().GetValueWithUnit("ModelThickness", "mm")
		numSlice = get_slice_number(targetStudy)
	
	write_line2(file, "CSV Version", CSV_VERSION)
	write_line2(file, "Dimension", dimString)
	write_line2(file, "Coordinate", coordString)
	if(is2D or isCutPlane or dialog.GetValue(DIVIDE_TEETH_FORCE)==0):
		write_line2(file, "Teeth", str(numTeeth))
	else :
		write_line3(file, "Teeth", str(numTeeth), str(len(coords)-1))
	if (is2D):
		write_line2(file, "StackLength(mm)", str(modelThickness))
		write_line2(file, "Slice", str(numSlice))
	file.write("\n")

def create_torque_data(app, dialog):
	##Initialize
	speedList = []
	avTorqList = []
	startIndices = []
	caseIndexList = []
	noResultCaseIndexList = []
	outX = []
	outYT = []
	
	##Get dialog parameters
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	numPoles = dialog.GetValue(NUM_POLES)
	outputCaseType = dialog.GetValue(OUTPUT_CASE_TYPE)
	caseNoList = dialog.GetValueAsIntegerList(SPECIFIED_CASES)
	
	##Get app data
	targetStudy = app.GetCurrentStudy()
	torqResultName = get_torq_result_name(app)
		
	##Create output case list
	numCases = targetStudy.GetDesignTable().NumCases()
	if (outputCaseType == 0):	# Output all cases
		caseIndexList = range(numCases)
	elif (outputCaseType == 1): # Output specified cases
		caseIndexList = get_caseIndexList_from_caseNoList(targetStudy, caseNoList)
	
	##Create each case data
	for caseIndex in caseIndexList:
	
		if (targetStudy.CaseHasResult(caseIndex) == False):
			noResultCaseIndexList.append(caseIndex)
			continue
		
		##Calculate 1 mechanical cycle time from rotation speed
		targetStudy.SetCurrentCase(caseIndex)
		speed = get_speed(targetStudy)
		if (useElectricCycle):
			T = 60.0/speed/numPoles*2
		else :
			T = 60.0/speed
		
		##Get torque data for 1 last cycle
		lastCycleTorq = []
		dataset = targetStudy.GetDataSet(torqResultName, caseIndex+1)
		numSteps = dataset.GetRows()
		if (dataset.GetCols() < 2 or numSteps==0):
			noResultCaseIndexList.append(caseIndex)
			continue
		time = list(dataset.GetColumn(0))
		torque = get_target_torque(dataset, dialog)
		
		lastCycleStartTime = time[-1] - T + EPSILON*(time[-1] - time[-2])
		index = len(time)-1
		t = time[index]
		while t > lastCycleStartTime:
			lastCycleTorq.insert(0, torque[index])
			index -= 1
			if (index < 0 or len(time) <= index):
				break
			t = time[index]
		
		##Calculate average torque
		aveTorq = sum(lastCycleTorq)/len(lastCycleTorq)
		
		##Copy 1 electric cycle to 1 mechanical cycle
		lastCycleTorq = copy_1_electric_cycle_to_1_mechanical_cycle(useElectricCycle, numPoles, lastCycleTorq,True)
		
		##Create angle data
		angleData = []
		deltaAngle = 360.0/(len(lastCycleTorq))
		for i in range(len(lastCycleTorq)):
			angleData.append(deltaAngle*i)
		
		##Set each case data
		speedList.append(speed)
		avTorqList.append(aveTorq)
		startIndices.append(index+1)
		outX.append(angleData)
		outYT.append(lastCycleTorq)
	
	##Remove no result case
	for index in noResultCaseIndexList:
		caseIndexList.remove(index)
	
	return (caseIndexList, noResultCaseIndexList, speedList, avTorqList, startIndices, outX, outYT)

def write_torque(file, speed, aveTorq, outX, outYT):
	##Write torque header
	write_line(file, "*Torque")
	
	##Write torque data per case
	for i in range(len(speed)):
		write_line2(file, "Speed(RPM)", str(speed[i]))
		write_line2(file, "Torque(Nm)", str(aveTorq[i]))
		for j in range(len(outX[i])):
			write_line2(file, str(outX[i][j]), str(outYT[i][j]))
		file.write("\n")

def write_force(file, app, dialog, caseIndexList, speedList, avTorqList, startIndices, outX):
	##Get Study value
	targetModel = app.GetCurrentModel()
	targetStudy = app.GetCurrentStudy()
	is2D = (targetModel.GetDimension()==2)
	isCutPlane = app.GetCurrentStudy().IsCutPlaneStudy()
	periodicity = get_periodicity(targetStudy)
	useMultiSlice = use_multi_slice(targetStudy)
	numSlice = 1
	if (useMultiSlice):
		numSlice = get_slice_number(targetStudy)
	needConvertTwice = (not is2D) and has_symmetry_boundary(targetStudy, dialog)
	
	##Get dialog parameters
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	numPoles = dialog.GetValue(NUM_POLES)
	coordType = 1 # Fixed to cylindrical in V18.1
	divideTeethForceFlag = 0
	if (not is2D and not isCutPlane):
		divideTeethForceFlag = dialog.GetValue(DIVIDE_TEETH_FORCE)
	if (divideTeethForceFlag):
		needConvertTwice = False
		
	##Pick up target set list
	targetSetList = get_target_set(targetModel, dialog)
	
	
	##Create data for each component and each case
	dimension = app.GetCurrentModel().GetDimension()
	numCases = targetStudy.GetDesignTable().NumCases()
	stepsList = get_numsteps(app, caseIndexList)

	##Setup progress dialog
	setup_progress(app, dimension, caseIndexList, numSlice, stepsList)
	wasCanceled = False
	
	subModuleRunner = SubModuleRunner(app, dialog)
	allCaseData = []
	for outputIndex in range(len(caseIndexList)):
		caseData = []
		caseIndex = caseIndexList[outputIndex]
		targetStudy.SetCurrentCase(caseIndex)
		for sliceIndex in range(numSlice):
			[sliceData, wasCanceled] = subModuleRunner.run(app, targetStudy, caseIndex, useMultiSlice, sliceIndex, targetSetList, stepsList[outputIndex])
			if(wasCanceled):
				file.close()
				return True

			if(is2D) :
				caseData.append(sliceData)
			else :
				if(divideTeethForceFlag) :
					list = [x.strip() for x in dialog.GetValue(AXIAL_COORDINATES).decode('utf-8').split(",")]
					numSlice = len(list)-1
					num_block = len(sliceData[0]) // numSlice
					for i in range(numSlice) :
						start = num_block*i
						end = num_block*(i+1)
						data = []
						for x in sliceData:
							data.append(x[start:end])
						caseData.append(data)
				else :
					caseData.append(sliceData)
		allCaseData.append(caseData)
	
	for compIndex in range(dimension):
		write_line2(file, "*Force", str(compIndex+1))

		for outputIndex in range(len(caseIndexList)):
			if (app.UserProgressWasCanceled()):
				app.UserProgressFinish()
				file.close()
				return True

			caseIndex = caseIndexList[outputIndex]
			targetStudy.SetCurrentCase(caseIndex)
			##Get nodeIds on each set

			write_line2(file, "Speed(RPM)", str(speedList[outputIndex]))
			write_line2(file, "Torque(Nm)", str(avTorqList[outputIndex]))
			
			fullModelF=[]
			for sliceIndex in range(numSlice):
				
				##Get TOOTH value
				nfullModelTarget = int(len(allCaseData[outputIndex][sliceIndex][0])/3)
				for setIndex in range(nfullModelTarget):					
					colIndex = setIndex*3 + compIndex
					data = [allCaseData[outputIndex][sliceIndex][stepIndex][colIndex] for stepIndex in range(len(allCaseData[outputIndex][sliceIndex]))]
					
					##Considering 1/2 model lengthwise if symmetry boundary is set on XY plane
					convertedData = convert_twice(needConvertTwice, data)
					
					##Get last 1 cycle data
					lastCycleF = convertedData[startIndices[outputIndex]:]
					
					##Copy 1 electric cycle to 1 mechanical cycle
					lastCycleF = copy_1_electric_cycle_to_1_mechanical_cycle(useElectricCycle, numPoles, lastCycleF,True)
					
					if (useMultiSlice):
						lastCycleF = [f / numSlice for f in lastCycleF]
					
					##Add each set data
					fullModelF.append(lastCycleF)
												
			##Write TOOTH value
			for i in range(len(outX[outputIndex])):
				file.write(str(outX[outputIndex][i]) + ",")
				for j in range(len(fullModelF)):
					file.write(str(fullModelF[j][i]))
					if j == len(fullModelF)-1:
						file.write("\n")
					else:
						file.write(",")
			file.write("\n")
	
			if (wasCanceled == False):
				app.UserProgressStep()

	app.UserProgressFinish()
	
	return wasCanceled

def write_line(file, str1):
	file.write(str1)
	file.write("\n")

def write_line2(file, str1, str2):
	file.write(str1)
	file.write(",")
	file.write(str2)
	file.write("\n")

def write_line3(file, str1, str2, str3):
	file.write(str1)
	file.write(",")
	file.write(str2)
	file.write(",")
	file.write(str3)
	file.write("\n")

def get_speed(targetStudy):
	result = 0;
	motionList = get_conditions(targetStudy, "RotationMotion")
	motion = motionList[0]
	return motion.GetValue("AngularVelocity")

def torque_condition_title_list(app):
	result = []
	torqueList = get_conditions(app.GetCurrentStudy(), "Torque_Surface")
	for cond in torqueList:
		result.append(cond.GetName())
	return result

def get_periodicity(targetStudy):
	result=1
	for i in range(targetStudy.NumConditions()):
		cond = targetStudy.GetCondition(i)
		if cond.GetScriptTypeName()=="RotationPeriodicBoundary":
			result = 360//int(cond.GetValue(u"Angle"))
			break
	return result

def get_coordName(coordType):
	result = "Global Rectangular"
	if (coordType == 0):	# 0: Rectangular
		result = "Global Rectangular"
	elif (coordType == 1):		# 1: Cylindrical
		result = "Cylindrical"
	return result

def get_component(coordType, compIndex):
	result = "X"
	if (coordType == 0):	# 0: Rectangular
		if (compIndex==0):
			result = "X"
		elif (compIndex==1):
			result = "Y"
		elif (compIndex==2):
			result = "Z"
	elif (coordType == 1):		# 1: Cylindrical
		if (compIndex==0):
			result = "Radial"
		elif (compIndex==1):
			result = "Theta"
		elif (compIndex==2):
			result = "Z"
	return result

def get_caseIndexList_from_caseNoList(targetStudy, caseNoList):
	result = []
	numCases = targetStudy.GetDesignTable().NumCases()
	for no in caseNoList:
		index = no-1
		if (index>-1 and index 0

def get_ms_conditions(study):
	result = []
	for i in range(study.NumConditions()):
		cond = study.GetCondition(i)
		typeName = cond.GetScriptTypeName()
		if (typeName == "MultiSlice"):
			result.append(cond)
	return result

def get_slice_number(study):
	result = 1
	condList = get_ms_conditions(study)
	if (len(condList)>0):
		cond = condList[0]
		if (cond.IsValid()):
			result = int(cond.GetValue("NumberOfSlices"))
	return result

def get_target_set(targetModel, dialog):
	targetSetList = []
	numSet = set()
	prefix = dialog.GetValue(SET_PREFIX).decode('utf-8')
	nPrefix = len(prefix)
	setList = targetModel.GetSetList()
	if (setList.IsValid() == False):
		return targetSetList
	for i in range(setList.NumSet()):
		entitySet = setList.GetSet(i)
		if entitySet.IsValid():
			setName = entitySet.GetName()
			if sys.version_info.major == 2:
				setName = setName.decode('utf-8')
			if setName.startswith(prefix) and len(setName) > nPrefix:
				numStr = setName[nPrefix:]
				if numStr.isdecimal():
					numSet.add(int(numStr))
					targetSetList.append(entitySet)
	if len(targetSetList)==0:
		return targetSetList
	if len(targetSetList)!=len(numSet):
		targetSetList.clear()
		return targetSetList

	numList = sorted(numSet)
	if numList[0]!= 1 or numList[-1]!=len(numSet):
		targetSetList.clear()
	
	return targetSetList

def is_japanese(app):
	lang = app.GetPreference("Language").decode('utf-8')
	if (lang == "Japanese"):
		return True
	elif (lang == "System"):
		localeInfo = locale.getlocale()
		if ("ja" in localeInfo[0].lower()):
			return True
		else:
			return False
	return False

def get_target_torque(dataset, dialog):
	result = []
	selectedIndex = dialog.GetValue(TORQUE_COND_TITLE)
	titleList = torque_condition_title_list(app)
	targetName = titleList[selectedIndex]
	for i in range(dataset.GetCols()):
		if (dataset.GetColumnName(i)==targetName):
			result = list(dataset.GetColumn(i))
	return result

def has_zero_speed(app):
	targetStudy = app.GetCurrentStudy()
	numCases = targetStudy.GetDesignTable().NumCases()
	for caseIndex in range(numCases):
		targetStudy.SetCurrentCase(caseIndex)
		if (targetStudy.HasResult()==False):
			continue
		motionList = get_conditions(targetStudy, "RotationMotion")
		motion = motionList[0]
		speed = motion.GetValue("AngularVelocity")
		if (speed==0.0):
			return True
	
	return False

def has_symmetry_boundary(study, dialog):
	axisType = dialog.GetValue(TEETH_CENTER_SPECIFYING_TYPE)
	axisDir_X = 0.0
	axisDir_Y = 0.0
	axisDir_Z = 0.0
	if axisType==0:
		torqueList = get_conditions(study, "Torque_Surface")
		selectedIndex = dialog.GetValue(TORQUE_COND_TITLE)
		axisDirVec = torqueList[selectedIndex].GetPoint("Axis")
		axisDir_X = axisDirVec.x()
		axisDir_Y = axisDirVec.y()
		axisDir_Z = axisDirVec.z()
	else:
		axisDir_X = dialog.GetValue(TEETH_CENTER_DIR_X)
		axisDir_Y = dialog.GetValue(TEETH_CENTER_DIR_Y)
		axisDir_Z = dialog.GetValue(TEETH_CENTER_DIR_Z)
	axisDir = -1
	if (axisDir_Y==0.0 and axisDir_Z==0.0):
		axisDir=0
	elif (axisDir_Z==0.0 and axisDir_X==0.0):
		axisDir=1
	elif (axisDir_X==0.0 and axisDir_Y==0.0):
		axisDir=2
	condList = get_conditions(study, "SymmetryBoundary")
	for symmetry in condList:
		if axisDir==0 and symmetry.HasTargetOnYZPlane():
			return True
		elif axisDir==1 and symmetry.HasTargetOnZXPlane():
			return True
		elif axisDir==2 and symmetry.HasTargetOnXYPlane():
			return True
	return False

def get_case_index_list(dialog, targetStudy):
	caseIndexList = []
	caseNoList = dialog.GetValueAsIntegerList(SPECIFIED_CASES)
	outputCaseType = dialog.GetValue(OUTPUT_CASE_TYPE)
	numCases = targetStudy.GetDesignTable().NumCases()
	if (outputCaseType == 0):	# Output all cases
		caseIndexList = range(numCases)
	elif (outputCaseType == 1): # Output specified cases
		caseIndexList = get_caseIndexList_from_caseNoList(targetStudy, caseNoList)
	return caseIndexList

def get_numsteps(app, caseIndexList):
	targetStudy = app.GetCurrentStudy()
	torqResultName = get_torq_result_name(app)

	stepsList = []
	for caseIndex in caseIndexList:
		if (targetStudy.CaseHasResult(caseIndex) == False):
			return True

		targetStudy.SetCurrentCase(caseIndex)
		dataset = targetStudy.GetDataSet(torqResultName, caseIndex+1)
		numSteps = dataset.GetRows()
		stepsList.append(numSteps)

	return stepsList

def check_has_not_1cycle_step_cases(app, parameters, caseIndexList):
	result = False
	useElectricCycle = parameters.GetValue(USE_COPY_CYCLE)
	numPoles = parameters.GetValue(NUM_POLES)
	targetStudy = app.GetCurrentStudy()
	torqResultName = get_torq_result_name(app)
	
	for caseIndex in caseIndexList:
		if (targetStudy.CaseHasResult(caseIndex) == False):
			return True
		
		##Calculate 1 mechanical cycle time from rotation speed
		targetStudy.SetCurrentCase(caseIndex)
		speed = get_speed(targetStudy)
		T = get_1_mechanical_cycle_time(speed, useElectricCycle, numPoles)
		
		##Get torque data for 1 last cycle
		lastCycleTorq = []
		dataset = targetStudy.GetDataSet(torqResultName, caseIndex+1)
		numSteps = dataset.GetRows()
		if (dataset.GetCols()<2 or numSteps==0):
			continue
		time = list(dataset.GetColumn(0))
		if (len(time)<=1):
			result = True
			break
		
		lastCycleStartTime = time[-1] - T + time[1]
		timeScale = time[1]
		if (lastCycleStartTime < -EPSILON * T):
			result = True
			break
	
	return result

def get_torq_result_name(app):
	targetStudy = app.GetCurrentStudy()
	useMultiSlice = use_multi_slice(targetStudy)
	result = "Torque"
	numSlice = 1
	if (useMultiSlice):
		numSlice = get_slice_number(targetStudy)
		if (is_japanese(app)):
			result = "トルク <モデル全体>"
		else:
			result = "Torque "
	return result

def create_file_name(originalFilePath, numCases, caseNo, useMultiSlice, numSlice, sliceNo):
	if (useMultiSlice or numSlice>1):
		if (numCases > 1):
			absFilePathWithoutExt = os.path.splitext(originalFilePath)[0]
			postFixLabel = b"_Case%d_Slice%d.csv" % (caseNo, sliceNo)
			return (absFilePathWithoutExt + postFixLabel)
		else:
			absFilePathWithoutExt = os.path.splitext(originalFilePath)[0]
			sliceLabel = b"_Slice%d.csv" % sliceNo
			return (absFilePathWithoutExt + sliceLabel)
	else:
		if (numCases > 1):
			absFilePathWithoutExt = os.path.splitext(originalFilePath)[0]
			caseLabel = b"_Case%d.csv" % caseNo
			return (absFilePathWithoutExt + caseLabel)
		else:
			return originalFilePath

def get_1_mechanical_cycle_time(speed, useElectricCycle, numPoles):
	if (speed == 0.0 or numPoles == 0):
		 return 0

	if (useElectricCycle):
		T = 60.0/speed/numPoles*2
	else :
		T = 60.0/speed

	return T

def setup_progress(app, dimension, caseIndexList, numSlice, stepsList):
	progressLabel = "Exporting to CSV File..."
	if (is_japanese(app)):
		progressLabel = "CSVファイルへ出力中..."
	app.SetupUserProgress(progressLabel)
	app.SetUserProgressUseCancel(True)
	total_steps = 0
	for steps in stepsList:
		total_steps += steps

	maxSteps = (numSlice * total_steps) + (dimension * len(caseIndexList))
	app.SetUserProgressMaxSteps(maxSteps)
	app.UserProgressStart()

def convert_twice(needConvertTwice, data):
	convertedData = []
	if needConvertTwice:
		convertedData = [n*2 for n in data]
	else:
		convertedData = data
	return convertedData

def copy_1_electric_cycle_to_1_mechanical_cycle(useElectricCycle, numPoles, originalData,includeFirst = False):
	result = []
	if (useElectricCycle):
		for i in range(numPoles//2):
			if (i == 0) or (includeFirst == True):
				result.extend(originalData)
			else:
				result.extend(originalData[1:])
	else:
		result = originalData
	return result

def create_1_electric_cycle_time_to_1_mechanical_cycle_time(useElectricCycle, numPoles, originalData):
	result = []
	offsetData = []
	numSize = len(originalData)
	if (numSize) < 2:
		return result
	
	deltaT = originalData[1] - originalData[0]
	startTime = originalData[0]
	offsetData = []
	for i in range(len(originalData)):
		offsetData.append(i*deltaT)
	lastT = offsetData[-1]

	if (useElectricCycle):
		for i in range(numPoles//2):
			tmp = []
			if (i == 0):
				tmp = offsetData
			else:
				offsetTime = lastT + deltaT
				tmp = [n + offsetTime for n in offsetData][:-1]
				lastT = tmp[-1]
			result.extend(tmp)
	else:
		result = originalData
	return result

def WriteByAllComponentsInSetForOneCase(file, fullModelF):
	for timeIndex in range(len(fullModelF[0])):
		for colIndex in range(len(fullModelF)):
			file.write(str(fullModelF[colIndex][timeIndex]))
			if (colIndex == len(fullModelF)-1):
				file.write("\n")
			else:
				file.write(",")

def get_last_1_cycle_start_index(dialog, targetStudy, dataset):
	startIndex = 0

	##Get dialog parameters
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	numPoles = dialog.GetValue(NUM_POLES)

	##Calculate 1 mechanical cycle time from rotation speed
	speed = get_speed(targetStudy)
	T = get_1_mechanical_cycle_time(speed, useElectricCycle, numPoles)
	
	##Get torque data for 1 last cycle
	lastCycleTorq = []
	numSteps = dataset.GetRows()
	if (dataset.GetCols()<2 or numSteps==0):
		return 0
	time = list(dataset.GetColumn(0))
	
	##Search last 1 cycle start index
	lastCycleStartTime = time[-1] - T
	index = len(time)-1
	t = time[index]
	while t > lastCycleStartTime - EPSILON:
		index -= 1
		if (index < 0 or len(time) <= index):
			break
		t = time[index]
	
	startIndex = index + 1
	return startIndex

def write_header_for_time_domain_tooth_force_only(file, setPrefix, numFullModelSets):
	numComponents = OUTPUT_DIMENSION
	file.write(",")

	##Write a line for set names
	for setIndex in range(numFullModelSets):
		for componentIndex in range(numComponents):
			if (componentIndex == 0):
				setName = setPrefix + " " + str(setIndex+1)
				file.write(setName)
				file.write(",")
			elif (setIndex == numFullModelSets-1 and componentIndex == numComponents-1):
				file.write("\n")
			else:
				file.write(",")

	##Write a line for time and each set components
	file.write("Time")
	file.write(",")
	for setIndex in range(numFullModelSets):
		for componentIndex in range(numComponents):
			componentName = "F" + get_component(0, componentIndex).lower()
			file.write(componentName)

			if (setIndex == numFullModelSets-1 and componentIndex == numComponents-1):
				file.write("\n")
			else:
				file.write(",")

def write_header_for_frequency_domain_tooth_force_only(file, setPrefix, numFullModelSets):
	numComponents = OUTPUT_DIMENSION
	file.write(",")

	##Write a line for set names
	for setIndex in range(numFullModelSets):
		for componentIndex in range(numComponents):
			for realImagIndex in range(2):
				if (componentIndex == 0 and realImagIndex == 0):
					setName = setPrefix + " " + str(setIndex+1)
					file.write(setName)
					file.write(",")
				elif (setIndex == numFullModelSets-1 and componentIndex == numComponents-1 and realImagIndex == 1):
					file.write("\n")
				else:
					file.write(",")

	##Write a line for time and each set components
	file.write("Frequency")
	file.write(",")
	for setIndex in range(numFullModelSets):
		for componentIndex in range(numComponents):
			coordComponentName = "F" + get_component(0, componentIndex).lower()
			for realImagIndex in range(2):
				if (realImagIndex == 0):
					componentName = coordComponentName + "(real)"
				else:
					componentName = coordComponentName + "(imag)"
				file.write(componentName)
				if (setIndex == numFullModelSets-1 and componentIndex == numComponents-1 and realImagIndex == 1):
					file.write("\n")
				else:
					file.write(",")

def ShowFinishMessage(app, wasCanceled):
	message = "The csv file was created.\n"
	message_jp = "CSVファイルが作成されました。"
	if (wasCanceled==True):
		message = "The csv file export was canceled.\n"
		message_jp = "CSVファイル出力がキャンセルされました。"
	confirmation = app.CreateDialogBox()
	confirmation.SetTranslation(message, message_jp)
	confirmation.AddLabel(message, 0, 2)
	confirmation.SetCancelButtonVisible(False)
	confirmation.Show()


main()

Download Python source code

ファイルご利用の注意点

JMAGスクリプトライブラリをご利用されるに際し、以下の利用規約をよくお読みいただき、ご同意の上ご利用下さるようお願い申し上げます。