[D0024] NVH用の電磁力出力(節点力)

 

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

import locale
import os
import designer
import math
from bisect import bisect_left

def CanUseScript():
	app = designer.GetApplication()
	studies = GetCanUseScriptStudies(app, False)
	res = len(studies) > 0
	return res

def GetCanUseScriptStudies(app, onlyHasResult):
	model = app.GetCurrentModel()
	study_list = []
	if app.NumAnalysisGroups() > 0:
		analysisGroup = app.GetCurrentAnalysisGroup()
		designTable = analysisGroup.GetDesignTable()
		start_study = app.GetCurrentStudy()
		for i in range(analysisGroup.NumStudies()):
			study = analysisGroup.GetStudy(i)
			app.SetCurrentStudy(study.GetName())
			if CanUseScriptModelStudy(model, study, designTable):
				study_list.append(study)
		app.SetCurrentStudy(start_study.GetName())
	else:
		study = app.GetCurrentStudy()
		designTable = study.GetDesignTable()
		if CanUseScriptModelStudy(model, study, designTable):
			study_list.append(study)
	if not onlyHasResult:
		return study_list
	has_result_list = []
	for i in range(len(study_list)):
		if study_list[i].AnyCaseHasResult():
			has_result_list.append(study_list[i])
	return has_result_list

	

def CanUseScriptModelStudy(model, study, designTable):
	if (model.IsValid() == False):
		debugShowErrorMessage_expr("No model.")
		return False
	if (study.IsValid() == False):
		debugShowErrorMessage_expr("No study.")
		return False

	## Analysis Type Check
	is2DTR = study.GetScriptTypeName() == "Transient2D"
	is3DTR = study.GetScriptTypeName() == "Transient"
	if ((not is2DTR) and (not is3DTR)):
		debugShowErrorMessage_expr("Current study is unsupported analysis type.")
		return False

	## Motion Check
	motion = get_valid_motion(study)
	if motion is None:
		debugShowErrorMessage_expr("Current study has no rotation motion condition with constant velocity.")
		return False
	caseIndexList = get_active_case_indices_expr(study)

	## Pole Check
	if has_invalid_pole_expr(designTable, caseIndexList):
		debugShowErrorMessage_expr("There is the case that the parametric variable \"POLES\" does not exist or has an incorrect value.")
		return False
		
	## Torque Condition Check
	torqueCond= findTorqueCondition_expr(study)
	if (torqueCond is None):
		debugShowErrorMessage_expr("Current study has no torque condition.")
		return False

	## Nodal Force Table Check
	if is2DTR:
		cd_R =findNodalForceCalculationDefinition_expr(study, "Radial")
		cd_T =findNodalForceCalculationDefinition_expr(study, "Theta")
		cd_X =findNodalForceCalculationDefinition_expr(study, "X")
		cd_Y =findNodalForceCalculationDefinition_expr(study, "Y")
		if (cd_R is None) or (cd_T is None) or (cd_X is None) or (cd_Y is None) :
			debugShowErrorMessage_expr("NodalForce graph is not found.")
			return False
	elif is3DTR:
		cd_R =findNodalForceCalculationDefinition_expr(study, "Radial")
		cd_T =findNodalForceCalculationDefinition_expr(study, "Theta")
		cd_X =findNodalForceCalculationDefinition_expr(study, "X")
		cd_Y =findNodalForceCalculationDefinition_expr(study, "Y")
		cd_Z =findNodalForceCalculationDefinition_expr(study, "Z")
		if (cd_R is None) or (cd_T is None) or (cd_X is None) or (cd_Y is None) or (cd_Z is None) :
			debugShowErrorMessage_expr("NodalForce graph is not found.")
			return False
	return True


CSV_VERSION = "1.0.0"
OUTPUT_FILE_PATH = "outputFilePath"
SET_PREFIX = "prefix"
TORQUE_COND_TITLE = "condition_title"
NUM_POLES = "numPoles"
COORD_TYPE = "coord_type"
USE_COPY_CYCLE = "use_copy_cycle"
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_designer():
	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
	
	torqueList = get_conditions(study, "Torque")
	if (len(torqueList)<1):
		message = "Current study has no torque condition."
		message_jp = "現スタディはトルク条件がありません。"
		show_error_message(message, message_jp)
		return False
	
	forceList = get_conditions(study, "Force")
	if (len(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):
	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)

	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_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_16 = "Single File Including Torque(Force Coordinates=Cylindrical)[v22.2 and earlier]"
	#label_16_jp = "トルクデータ含む1ファイル出力(節点力の座標系=円筒座標系)[v22.2以前の形式]"
	#dialog.SetTranslation(label_16, label_16_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, 1)
	dialog.AddLabel(label_2, 0, 1)
	dialog.AddString(SET_PREFIX, "", "TOOTH", 1, 1)
	dialog.AddLabel(label_3, 0, 1)
	dialog.AddComboBox(TORQUE_COND_TITLE, "", torque_condition_title_list(app), 0, 1)
	dialog.AddLabel(label_4, 0, 1)
	dialog.AddInteger(NUM_POLES, "", 4, 1, 1)
	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, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_12, 0, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_13, 0, 1)
	#dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_16, 1, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_14, 1, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_15, 2, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_9, 0, 1)
	dialog.AddRadio(OUTPUT_CASE_TYPE, label_10, 0, 1)
	dialog.AddRadio(OUTPUT_CASE_TYPE, label_11, 1, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddIntegerList(SPECIFIED_CASES, "", "1", 1, 1)

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 set by the specified prefix."
		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
	
	outputCaseType = parameters.GetValue(OUTPUT_CASE_TYPE)
	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
	
	hasNot1CycleStepCases = False
	hasNot1CycleStepCases = check_has_not_1cycle_step_cases(app, parameters)
	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, True)
	elif (outputFormatType == 1):
		output_time_domain_tooth_forces(app, dialog)
	elif (outputFormatType == 2):
		output_frequency_domain_tooth_forces(app, dialog)
	
def get_case_table(study, is2D, caseIndexList, nSet):
	numTeethList = []
	modelThicknessList = []
	numSliceList = []
	startCase = study.GetCurrentCase()
	for i in range(len(caseIndexList)):
		caseIndex = caseIndexList[i]
		study.SetCurrentCase(caseIndex)
		periodicity = get_periodicity(study)
		numTeethList.append(nSet * periodicity)
		if is2D:
			modelThicknessList.append(study.GetStudyProperties().GetValueWithUnit("ModelThickness", "mm"))
		else:
			modelThicknessList.append(0.0)
		if is2D and use_multi_slice(study):
			numSliceList.append(get_slice_number(study))
		else:
			numSliceList.append(0)
	study.SetCurrentCase(startCase)
	return (numTeethList, modelThicknessList, numSliceList)

def output_single_file_including_torque(app, dialog, is100Format):
	isJapanese = is_japanese(app)
	is2D = (app.GetCurrentModel().GetDimension() == 2)
	targetSetList = get_target_set(app.GetCurrentModel(), dialog)
	study = app.GetCurrentStudy()
	(caseIndexList, noResultCaseIndexList, speedList, avTorqList, startIndices, outX, outYT) = create_torque_data(app, dialog)
	(numTeethList, modelThicknessList, numSliceList) = get_case_table(study, is2D, caseIndexList, len(targetSetList))

	outputPath = dialog.GetValue(OUTPUT_FILE_PATH)
	file = open(outputPath.decode('utf-8'), 'w')
	if is100Format:
		write_header_100(file, numTeethList[0], is2D, modelThicknessList[0], max(numSliceList) > 1 , numSliceList[0])
	else:
		write_header_110(file, is2D, caseIndexList, speedList, avTorqList, numTeethList, modelThicknessList, numSliceList)
	write_torque(file, caseIndexList, speedList, avTorqList, outX, outYT, is100Format)
	wasCanceled = write_force(file, app, dialog, caseIndexList, speedList, avTorqList, startIndices, outX, is100Format)
	
	file.close()
	
	ShowFinishMessage(app, wasCanceled)

def output_time_domain_tooth_forces(app, dialog):

	## Get app data
	isJap = is_japanese(app)
	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)
	thickness = 0
	useMultiSlice = False
	numSlice = 1
	if (is2D):
		thickness = targetStudy.GetStudyProperties().GetValueWithUnit("ModelThickness", "m")
		useMultiSlice = use_multi_slice(targetStudy)
		numSlice = get_slice_number(targetStudy)
		if (useMultiSlice):
			thickness = 1.0*thickness/numSlice
	caseIndexList = get_case_index_list(dialog, targetStudy)
	periodicity = get_periodicity(targetStudy)
	needConvertTwice = (not is2D) and has_symmetry_boundary_on_XYPlane(targetStudy)

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

	## Case loop
	numCases = len(caseIndexList)
	for caseLoopIndex in range(numCases):
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)
		removedList = get_removed_node_list_on_boundary(targetStudy, targetSetList)

		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, 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
			partialModelF = []
			for setIndex in range(len(targetSetList)):
				eachSetF = []
	
				## Component loop
				for compIndex in range(outputDimension):
					if (app.UserProgressWasCanceled()):
						app.UserProgressFinish()
						wasCanceled = True
						break
					
					##Setup probe
					title = "Probe"
					probe = create_probe(title, targetStudy, useMultiSlice, sliceIndex, isJap, coordType, compIndex)
					
					[data, wasCanceled] = calc_force(targetStudy, removedList[setIndex], probe)
					
					## 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 is2D:
						lastCycleF = [f * thickness for f in lastCycleF]
					
					##Add each component data
					eachSetF.append(lastCycleF)
					
					if (wasCanceled == False):
						app.UserProgressStep()
					
					targetStudy.DeleteProbe(title)
						
				partialModelF.append(eachSetF)
			
			fullModelF = []
			##Add Time
			fullModelF.append(timeData)
			
			##Copy partial model teeth to full model
			for i in range (periodicity):
				if i==0:
					fullModelF.append(partialModelF)
				else:
					rad = math.radians(360.0 * i / periodicity)
					cos = math.cos(rad)
					sin = math.sin(rad)
					rotPartial = []
					for setIndex in range(len(targetSetList)):
						rotEachSet = []
						rotfx = [cos * x - sin * y for x,y in zip(partialModelF[setIndex][0], partialModelF[setIndex][1])]
						rotfy = [sin * x + cos * y for x,y in zip(partialModelF[setIndex][0], partialModelF[setIndex][1])]
						rotEachSet.append(rotfx)
						rotEachSet.append(rotfy)
						rotEachSet.append(partialModelF[setIndex][2])
						rotPartial.append(rotEachSet)
					fullModelF.append(rotPartial)
			##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
	isJap = is_japanese(app)
	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)
	thickness = 0
	useMultiSlice = False
	numSlice = 1
	if (is2D):
		thickness = targetStudy.GetStudyProperties().GetValueWithUnit("ModelThickness", "m")
		useMultiSlice = use_multi_slice(targetStudy)
		numSlice = get_slice_number(targetStudy)
		if (useMultiSlice):
			thickness = 1.0*thickness/numSlice
	caseIndexList = get_case_index_list(dialog, targetStudy)
	periodicity = get_periodicity(targetStudy)
	needConvertTwice = (not is2D) and has_symmetry_boundary_on_XYPlane(targetStudy)

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

	## Case loop
	numCases = len(caseIndexList)
	for caseLoopIndex in range(numCases):
		caseIndex = caseIndexList[caseLoopIndex]
		targetStudy.SetCurrentCase(caseIndex)
		removedList = get_removed_node_list_on_boundary(targetStudy, targetSetList)

		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, 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)
	
			## Each Tooth loop
			partialModelF = []
			freqData = []
			for setIndex in range(len(targetSetList)):
				eachSetF = []
	
				## Component loop
				for compIndex in range(outputDimension):
					if (app.UserProgressWasCanceled()):
						app.UserProgressFinish()
						wasCanceled = True
						break
					
					## Setup probe
					title = "Probe"
					probe = create_probe(title, targetStudy, useMultiSlice, sliceIndex, isJap, coordType, compIndex)
					
					dataSetTitle = "fft_for_nvf_force_export"
					[fftDataSet, wasCanceled] = calc_force_with_FFT(dataManager, targetStudy, removedList[setIndex], probe, dataSetTitle, needConvertTwice, startIndex, is2D, thickness)
					
					## Real/Imaginary Loop
					numFFTColmuns = fftDataSet.GetCols()
					if (numFFTColmuns < 3):
						continue
					for realImagIndex in range(1, numFFTColmuns):
						data = list(fftDataSet.GetColumn(realImagIndex))
						##  Add each component data
						eachSetF.append(data)
					
					if (setIndex == 0 and compIndex == 0):
						freqData = list(fftDataSet.GetColumn(0))
					
					if (wasCanceled == False):
						app.UserProgressStep()
					
					targetStudy.DeleteProbe(title)
					dataManager.DeleteDataSet(dataSetTitle)
						
				partialModelF.append(eachSetF)
				
			fullModelF = []
			## Add Time/Frequency
			fullModelF.append(freqData)
			
			## Copy partial model teeth to full model
			for i in range (periodicity):
				if i==0:
					fullModelF.append(partialModelF)
				else:
					rad = math.radians(360.0 * i / periodicity)
					cos = math.cos(rad)
					sin = math.sin(rad)
					rotPartial = []
					for setIndex in range(len(targetSetList)):
						rotEachSet = []
						rotfxr = [cos * x - sin * y for x,y in zip(partialModelF[setIndex][0], partialModelF[setIndex][2])]
						rotfyr = [sin * x + cos * y for x,y in zip(partialModelF[setIndex][0], partialModelF[setIndex][2])]
						rotfxi = [cos * x - sin * y for x,y in zip(partialModelF[setIndex][1], partialModelF[setIndex][3])]
						rotfyi = [sin * x + cos * y for x,y in zip(partialModelF[setIndex][1], partialModelF[setIndex][3])]
						rotEachSet.append(rotfxr)
						rotEachSet.append(rotfxi)
						rotEachSet.append(rotfyr)
						rotEachSet.append(rotfyi)
						rotEachSet.append(partialModelF[setIndex][4])
						rotEachSet.append(partialModelF[setIndex][5])
						rotPartial.append(rotEachSet)
					fullModelF.append(rotPartial)

	
			## Write TOOTH value of full model
			WriteByAllComponentsInSetForOneCase(file, fullModelF)
			
			file.close()
	
	app.UserProgressFinish()
	
	ShowFinishMessage(app, wasCanceled)

def write_header_100(file, numTeeth, is2D, modelThickness, useMultiSlice, numSlice):
	dimString = ""
	if (is2D):
		dimString = "2D"
	else:
		dimString = "3D"
	write_line2(file, "CSV Version", "1.0.0")
	write_line2(file, "Dimension", dimString)
	write_line2(file, "Coordinate", get_coordName(1))
	write_line2(file, "Teeth", str(numTeeth))
	if (is2D):
		write_line2(file, "StackLength(mm)", str(modelThickness))
		if useMultiSlice:
			write_line2(file, "Slice", str(numSlice))
		else:
			write_line2(file, "Slice", "1")
	file.write("\n")

def write_header_110(file, is2D, caseIndexList, speedList, avTorqList, numTeethList, modelThicknessList, numSliceList):
	dimString = ""
	if (is2D):
		dimString = "2D"
	else:
		dimString = "3D"

	write_line2(file, "CSV Version", "1.1.0")
	write_line2(file, "Dimension", dimString)
	write_line2(file, "Coordinate", get_coordName(1))
	write_line2(file, "Number of Cases", str(len(caseIndexList)))
	file.write("\n")

	write_line(file, "*Case Table")
	file.write("Case,Speed(RPM),Torque(Nm),Teeth,StackLength(mm),Slice\n")
	for i in range(len(caseIndexList)):
		line = str(caseIndexList[i] + 1)
		line += "," + str(speedList[i])
		line += "," + str(avTorqList[i])
		line += "," + str(numTeethList[i])
		line += "," + str(modelThicknessList[i])
		line += "," + str(numSliceList[i])
		write_line(file, line)
	file.write("\n")

def write_header(file, app, dialog):
	targetModel = app.GetCurrentModel()
	targetStudy = app.GetCurrentStudy()
	is2D = (targetModel.GetDimension()==2)
	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)
	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)
	write_line2(file, "Teeth", str(numTeeth))
	if (is2D):
		write_line2(file, "StackLength(mm)", str(modelThickness))
		write_line2(file, "Slice", str(numSlice))
	file.write("\n")

def calc_ave_torque(lastCycleTorq):
	return sum(lastCycleTorq[1:]) / (len(lastCycleTorq)-1)
	

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 = calc_ave_torque(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, caseIndexList, speed, aveTorq, outX, outYT, is100Format):
	##Write torque header
	write_line(file, "*Torque")
	
	##Write torque data per case
	for i in range(len(caseIndexList)):
		if is100Format:
			write_line2(file, "Speed(RPM)", str(speed[i]))
			write_line2(file, "Torque(Nm)", str(aveTorq[i]))
		else:
			write_line2(file, "Case", str(caseIndexList[i] + 1))
		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, is100Format):
	##Get Study value
	targetModel = app.GetCurrentModel()
	targetStudy = app.GetCurrentStudy()
	isJap = is_japanese(app)
	is2D = (targetModel.GetDimension()==2)
	thickness = 0
	if (is2D):
		thickness = targetStudy.GetStudyProperties().GetValueWithUnit("ModelThickness", "m")
	periodicity = get_periodicity(targetStudy)
	useMultiSlice = use_multi_slice(targetStudy)
	numSlice = 1
	if (useMultiSlice):
		numSlice = get_slice_number(targetStudy)
		thickness = 1.0*thickness/numSlice
	needConvertTwice = (not is2D) and has_symmetry_boundary_on_XYPlane(targetStudy)
	
	##Get dialog parameters
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	numPoles = dialog.GetValue(NUM_POLES)
	coordType = 1 # Fixed to cylindrical in V18.1
	
	##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()
	
	##Setup progress dialog
	setup_progress(app, dimension, caseIndexList, numSlice, targetSetList)
	wasCanceled = False
	componentLabels = ["Radius", "Theta", "Z"]
	for compIndex in range(dimension):
		if is100Format:
			write_line2(file, "*Force", str(compIndex+1))
		elif (not is100Format) and compIndex == 0:
			write_line(file, "*Force")

		for outputIndex in range(len(caseIndexList)):
			caseIndex = caseIndexList[outputIndex]
			targetStudy.SetCurrentCase(caseIndex)
			##Get nodeIds on each set
			removedList = get_removed_node_list_on_boundary(targetStudy, targetSetList)
			
			if is100Format:
				write_line2(file, "Speed(RPM)", str(speedList[outputIndex]))
				write_line2(file, "Torque(Nm)", str(avTorqList[outputIndex]))
			else:
				write_line2(file, "Case", str(caseIndex + 1))
				write_line2(file, "Component", str(componentLabels[compIndex]))

			fullModelF=[]
			for sliceIndex in range(numSlice):
				
				##Set probe
				title = "Probe"
				probe = create_probe(title, targetStudy, useMultiSlice, sliceIndex, isJap, coordType, compIndex)
				
				##Calculate TOOTH value
				partialModelF = []
				for setIndex in range(len(targetSetList)):
					if (app.UserProgressWasCanceled()):
						app.UserProgressFinish()
						file.close()
						return True
					
					[data, wasCanceled] = calc_force(targetStudy, removedList[setIndex], probe)
					
					##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 2D model times thickness (How about multi-slice model?)
					if is2D:
						lastCycleF = [f * thickness for f in lastCycleF]
					
					##Add each set data
					partialModelF.append(lastCycleF)
					
					if (wasCanceled == False):
						app.UserProgressStep()
				
				##Copy partial model teeth to full model
				for i in range (periodicity):
					for j in range (len(targetSetList)):
						fullModelF.append(partialModelF[j])
				
				targetStudy.DeleteProbe(title)
				
				if (wasCanceled):
					file.close()
					return True
				
			##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")
	
	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_lineList(file, str1, numList):
	line = str1
	for i in range(len(numList)):
		line += ("," + str(numList[i]))
	file.write(line)
	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")
	for cond in torqueList:
		result.append(cond.GetName())
	return result

def get_periodicity(targetStudy):
	cond = get_first_condition(targetStudy, "RotationPeriodicBoundary")
	if (not cond is None) and (cond.IsValid()) and (not cond.GetValue("AutoPeriodicFromWinding")):
		return 360//int(cond.GetValue(u"Angle"))
	designTable = targetStudy.GetDesignTable()
	if designTable.HasEquation("MODEL_DIVISION"):
		modelDiv = designTable.GetEquation("MODEL_DIVISION").GetValue()
		if modelDiv > 0:
			return modelDiv
	if designTable.HasEquation("POLES") and designTable.HasEquation("SLOTS"):
		poles = int(designTable.GetEquation("POLES").GetValue())
		slots = int(designTable.GetEquation("SLOTS").GetValue())
		if poles > 0 and slots > 0:
			return math.gcd(poles, slots)
	return 1

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 index1):
			tmp = list(dataProbe.GetColumn(1))
			if (i==0):
				result = tmp
			else :
				result = [x+y for (x,y) in zip(result, tmp)]
	
	return (result, wasCanceled)

def calc_force_with_FFT(dataManager, targetStudy, removed, probe, dataSetTitle, needConvertTwice, startIndex, is2D, thickness):
	wasCanceled = False
	forceData = []
	
	##Set probe on the nodeIds
	if (len(removed)<1):
		return (probe.GetDataSet(), wasCanceled)
	
	tmp = []
	for i in range(len(removed)):
		
		if (app.UserProgressWasCanceled()):
			wasCanceled = True
			return (probe.GetDataSet(), wasCanceled)
		
		id = removed[i]
		probe.ClearPoints()
		probe.SetId(0, id)
		probe.RenamePoint(0, "Node_"+str(id))
		probe.Build()
		dataProbe = probe.GetDataSet()
		
		##Sum on all nodes
		if (dataProbe.GetCols()>1):
			tmp = list(dataProbe.GetColumn(1))
			if (i==0):
				forceData = tmp
			else :
				forceData = [x+y for (x,y) in zip(forceData, tmp)]
	
	## Considering 1/2 model lengthwise if symmetry boundary is set on XY plane
	convertedForceData = convert_twice(needConvertTwice, forceData)
	
	## Get last 1 cycle data
	lastCycleF = convertedForceData[startIndex:]
	timeData = list(dataProbe.GetColumn(0))
	lastCycleT = timeData[startIndex:]
	
	## If 2D model times thickness
	if is2D:
		lastCycleF = [f * thickness for f in lastCycleF]
	
	xData = lastCycleT
	yData = lastCycleF
	xyPairList = []
	xyPair = []
	
	for i in range(len(xData)):
		xyPair = [xData[i],yData[i]]
		xyPairList.append(xyPair)
	dataSet = dataManager.CreateFromDataSet(dataSetTitle, "xtitle", "ytitle", xyPairList)
	fftDataSet = dataManager.CreateFFT(dataSet, 0, "RealAndImaginary", 0, xData[0], xData[-1])
	
	return (fftDataSet, wasCanceled)

def remove_nodes_on_boundary(study, nodeIds):
	condList = get_conditions(study, "RotationPeriodicBoundary")
	if (len(condList)==0):
		return nodeIds
	
	result = []
	cond = condList[0]
	for id in nodeIds:
		if (cond.NodeIsOnTarget(id)==False):
			result.append(id)
	return result

def get_first_condition(study, condScriptTypeName):
	for i in range(study.NumConditions()):
		cond = study.GetCondition(i)
		typeName = cond.GetScriptTypeName()
		if (typeName == condScriptTypeName):
			return cond
	return None

def get_conditions(study, condScriptTypeName):
	result = []
	for i in range(study.NumConditions()):
		cond = study.GetCondition(i)
		typeName = cond.GetScriptTypeName()
		if (typeName == condScriptTypeName):
			result.append(cond)
	return result

def use_multi_slice(study):
	condList = get_ms_conditions(study)
	return len(condList) > 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 = []
	prefix = dialog.GetValue(SET_PREFIX).decode('utf-8')
	setList = targetModel.GetSetList()
	if (setList.IsValid() == False):
		return targetSetList
	
	for i in range(setList.NumSet()):
		set = setList.GetSet(i)
		if set.IsValid():
			setName = set.GetName()
			if sys.version_info.major == 2:
				setName = setName.decode('utf-8')
			if setName.startswith(prefix):
				targetSetList.append(set)
	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_on_XYPlane(study):
	condList = get_conditions(study, "SymmetryBoundary")
	for symmetry in condList:
		if 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 check_has_not_1cycle_step_cases(app, parameters):
	result = False
	useElectricCycle = parameters.GetValue(USE_COPY_CYCLE)
	numPoles = parameters.GetValue(NUM_POLES)
	targetStudy = app.GetCurrentStudy()
	torqResultName = get_torq_result_name(app)
	
	caseIndexList = get_case_index_list(parameters, targetStudy)
	for caseIndex in caseIndexList:
		if (targetStudy.CaseHasResult(caseIndex) == False):
			continue
		
		##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 get_removed_node_list_on_boundary(targetStudy, targetSetList):
	removedList = []
	for curset in targetSetList:
		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
		removedList.append(remove_nodes_on_boundary(targetStudy, nodeIds))

	return removedList

def create_file_name(originalFilePath, numCases, caseNo, useMultiSlice, sliceNo):
	if (useMultiSlice):
		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, targetSetList):
	progressLabel = "Exporting to CSV File..."
	if (is_japanese(app)):
		progressLabel = "CSVファイルへ出力中..."
	app.SetupUserProgress(progressLabel)
	app.SetUserProgressUseCancel(True)
	maxSteps = dimension * len(caseIndexList) * numSlice * len(targetSetList)
	app.SetUserProgressMaxSteps(maxSteps)
	app.UserProgressStart()

def create_probe(title, targetStudy, useMultiSlice, sliceIndex, isJap, coordType, compIndex):
	probe = targetStudy.CreateProbe(title)
	probe.SetAutoRecalculate(False)
	subTitle = ""
	if (useMultiSlice):
		if (isJap):
			subTitle = " <断面 " + str(sliceIndex+1) + ">"
		else:
			subTitle = " "
	probe.SetResultType("NodalForce", subTitle)
	probe.SetResultCoordinate(get_coordName(coordType))
	probe.SetComponent(get_component(coordType, compIndex))
	probe.ClearPoints()
	probe.SetProbeType(1)
	probe.SetUseElementValue(True)
	probe.SetMoveWithPart(True)
	return probe

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 periodicIndex in range(len(fullModelF)):
			if (periodicIndex == 0): # 0 is time column
				file.write(str(fullModelF[periodicIndex][timeIndex]))
				file.write(",")
			else:
				for eachSetIndex in range(len(fullModelF[periodicIndex])):
					for eachCompIndex in range(len(fullModelF[periodicIndex][eachSetIndex])):
						file.write(str(fullModelF[periodicIndex][eachSetIndex][eachCompIndex][timeIndex]))
						if (periodicIndex == len(fullModelF)-1 and eachSetIndex == len(fullModelF[periodicIndex])-1 and eachCompIndex == len(fullModelF[periodicIndex][eachSetIndex])-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()


#---------------------------------------------------------------
# for express
#---------------------------------------------------------------

SCENARIO_TITLE_expr = "scnario_title"
CSV_VERSION_expr = "1.1.0"

def get_valid_motion(study):
	motionList = get_conditions(study, "RotationMotion")
	if (len(motionList)<1):
		return False
	for i in range(len(motionList)):
		motion_type = motionList[i].GetValue("MotionGroupType")
		if motion_type == 0:
			return motionList[i]
	return None

def has_zero_speed_expr(study, caseIndexList):
	startCase = study.GetCurrentCase()
	res = False
	for i in range(len(caseIndexList)):
		caseIndex = caseIndexList[i]
		study.SetCurrentCase(caseIndex)
		speed = get_speed(study)
		if (speed==0.0):
			res = True
			break
	study.SetCurrentCase(startCase)
	return res

def getPoleParametricEquation_expr(designTable):
	return designTable.GetEquation("POLES")

def has_invalid_pole_expr(designTable, caseIndexList):
	if not designTable.HasEquation("POLES"):
		return True
	poleParametricEquation = getPoleParametricEquation_expr(designTable)
	if (poleParametricEquation is None) or (not poleParametricEquation.IsValid()):
		return True
	for i in range(len(caseIndexList)):
		if poleParametricEquation.GetValue(caseIndexList[i]) < 1:
			return True
	return False

def get_pole_expr(designTable, caseIndex):
	poleParametricEquation = getPoleParametricEquation_expr(designTable)
	return int(poleParametricEquation.GetValue(caseIndex))

def get_slot_expr(designTable, caseIndex):
	if designTable.HasEquation("SLOTS"):
		return int(designTable.GetEquation("SLOTS").GetValue(caseIndex))
	else:
		return 0

def findNodalForceCalculationDefinition_expr(study, component):
	n = study.NumCalculationDefinitions()
	for i in range(n):
		calc_def = study.GetCalculationDefinition(i)
		if (calc_def.GetResultType() != "NodalForce"):
			continue
		calc_def_compo = calc_def.GetComponent()
		if (calc_def.GetComponent() == component):
			return calc_def
	return None

def getTargetTorqueResultName_expr(isMultiSlice, isJapanese):
	if not isMultiSlice:
		return "Torque"
	elif isJapanese:
		return "トルク <モデル全体>"
	else:
		return "Torque "

def checkTargetTorqueConditionTitle_expr(condName):
	targetTorqueConditionTitleJp = "トルク"
	targetTorqueConditionTitleEn = "Torque"
	targetTorqueConditionTitleUtf8 = (b'\xe3\x83\x88\xe3\x83\xab\xe3\x82\xaf').decode('utf-8')
	if condName == targetTorqueConditionTitleJp:
		return True
	elif condName == targetTorqueConditionTitleEn:
		return True
	elif condName ==targetTorqueConditionTitleUtf8:
		return True
	return False
	

def findTorqueCondition_expr(study):
	torqConditions = get_conditions(study, "Torque")
	for i in range(len(torqConditions)):
		condName = torqConditions[i].GetName()
		if checkTargetTorqueConditionTitle_expr(condName):
			return torqConditions[i]
	return None

def findTorqueDataSet_expr(study, caseIndex, isJapanese):
	isMultiSlice = not (get_first_condition(study, "MultiSlice") is None)
	torqResultName = getTargetTorqueResultName_expr(isMultiSlice, isJapanese)
	dataSet = study.GetDataSet(torqResultName, caseIndex+1)
	if (dataSet is None):
		return [None, -1]
	elif (not dataSet.IsValid()):
		return [None, -1]
	for i in range(dataSet.GetCols()):
		column_name = dataSet.GetColumnName(i)
		if checkTargetTorqueConditionTitle_expr(column_name):
			return [dataSet, i]
	return [None, -1]

def debugShowErrorMessage_expr(message):
	#debug.Print(message)
	#show_error_message(message, message)
	return

def copy_cycles_expr(originalData, numExtendCycle):
	result = []
	for i in range(numExtendCycle):
		if (i == 0):
			result.extend(originalData)
		else:
			result.extend(originalData[1:])
	return result

def extend_cycles_expr(timeData, numExtendCycle):
	result = []
	incremental = timeData[-1] - timeData[0]
	for i in range(numExtendCycle):
		if (i == 0):
			result.extend(timeData)
		else:
			ext = timeData[1:]
			ext = [t + incremental * i for t in ext]
			result.extend(ext)
	return result

def get_1_cycle_time_expr(speed, numPoles, useElectricCycle):
	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 find_nearest_index(fullTime, startTime):
	index = bisect_left(fullTime, startTime)
	if index==0:
		return 0
	if index == len(fullTime):
		return len(fullTime) - 1
	prev = startTime - fullTime[index-1]
	tmp = fullTime[index] - startTime
	if prev <= tmp:
		return index-1
	else:
		return index

def getUseElectricCycleCheckBoxLabel_expr(isJapanese):
	if isJapanese:
		return "電気角1周期分または1/2周期分をコピーして機械角1周期分にする"
	else:
		return "Copy 1 or 1/2 electrical cycle to mechanical cycle"

def get_last_cycle_start_index_expr(speed, numPoles, useElectricCycle, fullTime):
	##Calculate 1 electric cycle time from rotation speed
	full_time_range = fullTime[-1] - fullTime[0]
	cycle_time = get_1_cycle_time_expr(speed, numPoles, useElectricCycle)
	lastCycleStartTime = 0.0
	numExtendCycle = 1
	if useElectricCycle:
		isFullCycle = (full_time_range > cycle_time - EPSILON)
		isHalfCycle = ((not isFullCycle) and full_time_range > cycle_time/2 - EPSILON)
		if isFullCycle:
			lastCycleStartTime = fullTime[-1] - cycle_time
			numExtendCycle = numPoles//2
		elif isHalfCycle:
			lastCycleStartTime = fullTime[-1] - cycle_time / 2.0
			numExtendCycle = numPoles//2 * 2
		else:
			message = "Insufficient number of result time steps. The file output must have more than the half cycle electrical angle time step number. Please check the results of the specified cases."
			message_jp = "結果の時間ステップ数が不十分です。ファイル出力には半周期電気角分の時間ステップ数以上の結果が必要です。指定ケースの結果を見直してください。"
			show_error_message(message, message_jp)
			return (-1, 0)
	else:
		if (full_time_range > cycle_time - EPSILON):
			lastCycleStartTime = fullTime[-1] - cycle_time
			numExtendCycle = 1
		else:
			message = "Insufficient number of result time steps. The file output must have more than one cycle mechanical angle time step number.\n"\
			"If the target scenario is torque ripple or the custom scenario that calculates more than half a period of electrical angles,\n"\
			"Please check the box ["+getUseElectricCycleCheckBoxLabel_expr(False) +"]."

			message_jp = "結果の時間ステップ数が不十分です。ファイル出力には1周期機械角分の時間ステップ数以上の結果が必要です。\n"\
			"対象シナリオがトルクリップルまたは電気角半周期分以上を計算しているカスタムシナリオの場合、\n"\
			"["+getUseElectricCycleCheckBoxLabel_expr(True) + "]にチェックを入れてください。"

			show_error_message(message, message_jp)
			return (-1, 0)

	index = find_nearest_index(fullTime, lastCycleStartTime)
	return (index, numExtendCycle)

def create_torque_data_expr(study, designTable, useElectricCycle, caseIndexList, isJapanese):
	##Initialize
	speedList = []
	avTorqList = []
	startIndices = []
	numExtendCycleList = []
	insufficientResultCaseIndexList = []
	outX = []
	outYT = []
	
	startCase = study.GetCurrentCase()

	##Create each case data
	for caseIndex in caseIndexList:
		if check_progress_wascanceled_expr(app):
			break

		if (study.CaseHasResult(caseIndex) == False):
			insufficientResultCaseIndexList.append(caseIndex)
			continue
		study.SetCurrentCase(caseIndex)
		speed = get_speed(study)
		numPoles = get_pole_expr(designTable, caseIndex)
		##Calculate 1 electric or mechanical cycle time from rotation speed
		T = get_1_cycle_time_expr(speed, numPoles, useElectricCycle)
		
		##Get torque data for 1 last cycle
		lastCycleTorq = []
		[dataset, torqueColumn]= findTorqueDataSet_expr(study, caseIndex, isJapanese)
		time = list(dataset.GetColumn(0))
		torque = list(dataset.GetColumn(torqueColumn))
		(start_index, numExtendCycle) = get_last_cycle_start_index_expr(speed, numPoles, useElectricCycle, time)
		if start_index < 0:
			insufficientResultCaseIndexList.append(caseIndex)
			break
		lastCycleTorq = torque[start_index:]
		
		##Calculate average torque
		aveTorq = calc_ave_torque(lastCycleTorq)
		
		##Copy 1 electric cycle to 1 mechanical cycle
		if numExtendCycle > 1:
			lastCycleTorq = copy_cycles_expr(lastCycleTorq, numExtendCycle)

		##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(start_index)
		numExtendCycleList.append(numExtendCycle)
		outX.append(angleData)
		outYT.append(lastCycleTorq)

	study.SetCurrentCase(startCase)
	return (caseIndexList, insufficientResultCaseIndexList, speedList, avTorqList, startIndices, numExtendCycleList, outX, outYT)

def get_thickness_factor_expr(study, dimension, numSlice):
	thickness_factor = 1.0
	#if (dimension == 2):
	#	thickness_factor = study.GetStudyProperties().GetValueWithUnit("ModelThickness", "m")
	#else:
	#	if (has_symmetry_boundary_on_XYPlane(study)):
	#		thickness_factor = 2.0
	#if (numSlice > 1):
	#	thickness_factor /= numSlice
	return thickness_factor

def create_partial_force_data_expr(study, componentKey, lastCycleStartIndex, numExtendCycle):
	##Create data for selected component and selected case
	dataTable =findNodalForceCalculationDefinition_expr(study, componentKey).GetDataSet()
	rows = dataTable.GetRows()
	nSet = dataTable.GetCols() - 1

	partialModelF=[]
	##Calculate TOOTH value
	for setIndex in range(nSet):
		lastCycleF = []
		##Get last 1 cycle data
		for i in range(lastCycleStartIndex, rows):
			lastCycleF.append(dataTable.GetValue(i, setIndex+1))
		##Copy 1 electric cycle to 1 mechanical cycle
		if numExtendCycle > 1:
			lastCycleF = copy_cycles_expr(lastCycleF, numExtendCycle)

		##Add each set data
		partialModelF.append(lastCycleF)
	return partialModelF

def create_fft_force_data_expr(study, dataManager, componentKey, dimension, lastCycleStartIndex):
	##Create data for selected component and selected case
	dataTable =findNodalForceCalculationDefinition_expr(study, componentKey).GetDataSet()
	rows = dataTable.GetRows()
	lastCycleTF = []
	for i in range(lastCycleStartIndex, rows):
		lastCycleTF.append([dataTable.GetValue(i, 0)])
	tmin = lastCycleTF[0][0]
	tmax = lastCycleTF[-1][0]
	nSet = dataTable.GetCols() - 1
	freqList=[]
	dataSetTitle = "fft_for_nvf_force_export"

	##Calculate TOOTH value
	partialModelRealF = []
	partialModelImageF = []
	for setIndex in range(nSet):
		# update lastCycleTF
		for i in range(lastCycleStartIndex, rows):
			lastCycleTF[i-lastCycleStartIndex].append(dataTable.GetValue(i, setIndex+1))
	dataSetTF = dataManager.CreateFromDataSet(dataSetTitle, "xtitle", "ytitle", lastCycleTF)
	for i in range(1, dataSetTF.GetCols()):
		fftDataSet = dataManager.CreateFFT(dataSetTF, i-1, "RealAndImaginary", 0, tmin, tmax)
		if len(freqList)==0:
			freqList = fftDataSet.GetColumn(0)
		partialModelRealF.append(fftDataSet.GetColumn(1))
		partialModelImageF.append(fftDataSet.GetColumn(2))
		dataManager.DeleteDataSetObject(fftDataSet)
	dataManager.DeleteDataSetObject(dataSetTF)
	return (freqList, partialModelRealF, partialModelImageF)

def create_time_data_expr(speed, numPoles, useElectricCycle, dataSetForHeader):
	fullTimeData = dataSetForHeader.GetColumn(0)
	(lastCycleStartIndex, numExtendCycle) = get_last_cycle_start_index_expr(speed, numPoles, useElectricCycle, fullTimeData)
	oneCycleTimeData = fullTimeData[lastCycleStartIndex:]
	if numExtendCycle > 1:
		oneCycleTimeData = extend_cycles_expr(oneCycleTimeData, numExtendCycle)
	return (lastCycleStartIndex, numExtendCycle, oneCycleTimeData)

def write_format1_force_expr(file, study, designTable, dimension, useElectricCycle, caseIndexList, speedList, avTorqList, startIndices, numExtendCycleList, outX, is100Format):
	##Get Study value
	startCase = study.GetCurrentCase()
	##Write data for each component
	componentKeys = ["Radial", "Theta", "Z"]
	componentLabels = ["Radius", "Theta", "Z"]
	for compIndex in range(dimension):
		if check_progress_wascanceled_expr(app):
			break
		if is100Format:
			write_line2(file, "*Force", str(compIndex+1))
		elif (not is100Format) and compIndex == 0:
			write_line(file, "*Force")
			
		componentKey = componentKeys[compIndex]
		##Write data for each case
		for outputIndex in range(len(caseIndexList)):
			if check_progress_wascanceled_expr(app):
				break
			else:
				app.UserProgressStep()
			caseIndex = caseIndexList[outputIndex]
			lastCycleStartIndex = startIndices[outputIndex]
			numExtendCycle = numExtendCycleList[outputIndex]
			study.SetCurrentCase(caseIndex)
			slots = get_slot_expr(designTable, caseIndex)
			partialModelF = create_partial_force_data_expr(study, componentKey, lastCycleStartIndex, numExtendCycle)
			periodicity = slots // len(partialModelF) 
			fullModelF = partialModelFToFullModelFWithCopy_expr(partialModelF, periodicity)

			if is100Format:
				write_line2(file, "Speed(RPM)", str(speedList[outputIndex]))
				write_line2(file, "Torque(Nm)", str(avTorqList[outputIndex]))
			else:
				write_line2(file, "Case", str(caseIndex + 1))
				write_line2(file, "Component", str(componentLabels[compIndex]))
				
			##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")
	study.SetCurrentCase(startCase)

def get_active_case_indices_expr(study):
	designTable = study.GetDesignTable()
	numCase = designTable.NumCases()
	caseIndexList = []
	for i in range(numCase):
		if designTable.IsActive(i) and study.CaseHasResult(i):
			caseIndexList.append(i)
	return caseIndexList

def get_param_from_input_dialog_expr(app):
	dialog = app.CreateDialogBox()
	scenario_titles = [s.GetName() for s in GetCanUseScriptStudies(app, True)]
	setup_param_input_dialog_expr(dialog, scenario_titles)
	dialogShowResponse = dialog.Show()
	useElectricCycle = dialog.GetValue(USE_COPY_CYCLE)
	outputPath = dialog.GetValue(OUTPUT_FILE_PATH)
	outputFormatType = dialog.GetValue(OUTPUT_FORMAT_TYPE)
	selectedStudyListIndex = dialog.GetValue(SCENARIO_TITLE_expr)
	return (dialogShowResponse, selectedStudyListIndex, useElectricCycle, outputPath, outputFormatType)

def setup_param_input_dialog_expr(dialog, scenario_titles):
	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_3 = "Target:"
	label_3_jp = "対象:"
	dialog.SetTranslation(label_3, label_3_jp)

	label_8 = getUseElectricCycleCheckBoxLabel_expr(False)
	label_8_jp = getUseElectricCycleCheckBoxLabel_expr(True)
	dialog.SetTranslation(label_8, label_8_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_16 = "Single File Including Torque(Force Coordinates=Cylindrical)[v22.2 and earlier]"
	#label_16_jp = "トルクデータ含む1ファイル出力(節点力の座標系=円筒座標系)[v22.2以前の形式]"
	#dialog.SetTranslation(label_16, label_16_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, 1)
	dialog.AddLabel(label_3, 0, 1)
	dialog.AddComboBox(SCENARIO_TITLE_expr, "", scenario_titles, 0, 1)
	dialog.AddCheckBox(USE_COPY_CYCLE, label_8, True, 1, 1)
	dialog.AddLabel("", 0, 1)
	dialog.AddLabel(label_12, 0, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_13, 0, 1)
	#dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_16, 1, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_14, 1, 1)
	dialog.AddRadio(OUTPUT_FORMAT_TYPE, label_15, 2, 1)

def get_case_table_expr(study, designTable, is2D, caseIndexList):
	numTeethList = []
	modelThicknessList = []
	numSliceList = []
	startCase = study.GetCurrentCase()
	for i in range(len(caseIndexList)):
		caseIndex = caseIndexList[i]
		study.SetCurrentCase(caseIndex)
		slots = get_slot_expr(designTable, caseIndex)
		numTeethList.append(slots)
		if is2D:
			modelThicknessList.append(study.GetStudyProperties().GetValueWithUnit("ModelThickness", "mm"))
		else:
			modelThicknessList.append(0.0)
		if is2D and use_multi_slice(study):
			numSliceList.append(get_slice_number(study))
		else:
			numSliceList.append(0)
	study.SetCurrentCase(startCase)
	return (numTeethList, modelThicknessList, numSliceList)

def getStudyAndDesignTable_expr(app, selectedStudyListIndex):
	study = GetCanUseScriptStudies(app, True)[selectedStudyListIndex]
	if app.NumAnalysisGroups() > 0:
		return (study, app.GetCurrentAnalysisGroup().GetDesignTable())
	else:
		return (study, study.GetDesignTable())

def output_single_file_including_torque_expr(app, selectedStudyListIndex, useElectricCycle, outputPath, is100Format):
	isJapanese = is_japanese(app)
	dimension = app.GetCurrentModel().GetDimension() 
	is2D = (dimension == 2)
	startStudy = app.GetCurrentStudy()
	(study, designTable) = getStudyAndDesignTable_expr(app, selectedStudyListIndex)
	app.SetCurrentStudy(study.GetName())
	caseIndexList = get_active_case_indices_expr(study)
	start_progress_expr(app, len(caseIndexList) * dimension)
	(caseIndexList, insufficientResultCaseIndexList, speedList, avTorqList, startIndices, numExtendCycleList, outX, outYT) = create_torque_data_expr(study, designTable, useElectricCycle, caseIndexList, isJapanese)
	if len(insufficientResultCaseIndexList) > 0:
		return False
	if check_progress_wascanceled_expr(app):
		return False
	(numTeethList, modelThicknessList, numSliceList) = get_case_table_expr(study, designTable, is2D, caseIndexList)
	if check_progress_wascanceled_expr(app):
		return False
	result = True
	try:
		file = open(outputPath.decode('utf-8'), 'w')
		if is100Format:
			write_header_100(file, numTeethList[0], is2D, modelThicknessList[0], max(numSliceList) > 1 , numSliceList[0])
		else:
			write_header_110(file, is2D, caseIndexList, speedList, avTorqList, numTeethList, modelThicknessList, numSliceList)

		if not check_progress_wascanceled_expr(app):
			write_torque(file, caseIndexList, speedList, avTorqList, outX, outYT, is100Format)
		if not check_progress_wascanceled_expr(app):
			write_format1_force_expr(file, study, designTable, dimension, useElectricCycle, caseIndexList, speedList, avTorqList, startIndices, numExtendCycleList, outX, is100Format)
		file.close()
	except Exception as e:
		app.UserProgressFinish()
		show_error_message(str(e), str(e))
		result = False
	app.SetCurrentStudy(startStudy.GetName())
	return result

def getColumnTitles(calcDefForHeader, periodicity):
	dataSetForHeader =calcDefForHeader.GetDataSet()
	n_set = dataSetForHeader.GetCols() - 1
	titles = []
	isAllDefault = True
	isSequencial = True
	setName0 = dataSetForHeader.GetColumnName(1)
	tmpPrefix = ""
	if len(setName0) > 1:
		tmpPrefix = setName0[0:-1]
	for setIndex in range(n_set):
		setName = dataSetForHeader.GetColumnName(setIndex+1)
		titles.append(setName)
		if isAllDefault:
			if setName!="合計" and setName!="Sum":
				isAllDefault = False
		if isSequencial:
			sequencialName = tmpPrefix + str(setIndex+1)
			if setName != sequencialName:
				isSequencial = False
	if isAllDefault:
		titles = []
		prefix = "SET"
		if calcDefForHeader.NumSets() > 0:
			prefix = calcDefForHeader.GetSet(0).GetName()
		for i in range(n_set * periodicity):
			titles.append(prefix + str(i+1))
	elif isSequencial:
		titles = []
		for i in range(n_set * periodicity):
			titles.append(tmpPrefix + str(i+1))
	else:
		for periodicityIndex in range(1, periodicity):
			for setIndex in range(n_set):
				titles.append(titles[setIndex] + "_" + str(periodicityIndex))
	return titles

def partialModelFToFullModelFWithCopy_expr(partialModelF, periodicity):
	fullModelF = []
	for periodicIndex in range(periodicity):
		fullModelF.extend(partialModelF)
	return fullModelF


def partialModelFToFullModelFWithRotate_expr(partialModelF_X, partialModelF_Y, periodicity):
	n_set = len(partialModelF_X)
	fullModelF_X = partialModelF_X
	fullModelF_Y = partialModelF_Y
	for periodicIndex in range(1, periodicity):
		rad = math.radians(360.0 * periodicIndex / periodicity)
		cos = math.cos(rad)
		sin = math.sin(rad)
		for setIndex in range(n_set):
			rotfx = [cos * x - sin * y for x,y in zip(partialModelF_X[setIndex], partialModelF_Y[setIndex])]
			rotfy = [sin * x + cos * y for x,y in zip(partialModelF_X[setIndex], partialModelF_Y[setIndex])]
			fullModelF_X.append(rotfx)
			fullModelF_Y.append(rotfy)
	return (fullModelF_X, fullModelF_Y)


def output_time_domain_tooth_forces_expr(app, selectedStudyListIndex, useElectricCycle, outputPath):
	isJapanese = is_japanese(app)
	dimension = app.GetCurrentModel().GetDimension()
	startStudy = app.GetCurrentStudy()
	(study, designTable) = getStudyAndDesignTable_expr(app, selectedStudyListIndex)
	app.SetCurrentStudy(study.GetName())
	caseIndexList = get_active_case_indices_expr(study)

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

	## Setup parameters
	useMultiSlice = False
	numSlice = 1
	if (dimension==2):
		numSlice = get_slice_number(study)
		useMultiSlice = numSlice > 1

	## Case loop
	result = True
	startCase = study.GetCurrentCase()
	numCases = len(caseIndexList)
	start_progress_expr(app, numCases)
	for caseLoopIndex in range(numCases):
		if check_progress_wascanceled_expr(app):
			break
		else:
			app.UserProgressStep()
		caseIndex = caseIndexList[caseLoopIndex]
		study.SetCurrentCase(caseIndex)

		##Get last 1 cycle times
		calcDefForHeader = findNodalForceCalculationDefinition_expr(study, "X")
		dataSetForHeader = calcDefForHeader.GetDataSet()
		n_set = dataSetForHeader.GetCols() - 1
		speed = get_speed(study)
		numPoles = get_pole_expr(designTable, caseIndex)
		numSlots = get_slot_expr(designTable, caseIndex)
		periodicity = numSlots//n_set
		(lastCycleStartIndex, numExtendCycle, oneCycleTimeData) = create_time_data_expr(speed, numPoles, useElectricCycle, dataSetForHeader)
		if lastCycleStartIndex < 0:
			result = False
			break
		partialModelF_X = create_partial_force_data_expr(study, "X", lastCycleStartIndex, numExtendCycle)
		partialModelF_Y = create_partial_force_data_expr(study, "Y", lastCycleStartIndex, numExtendCycle)
		(fullModelF_X, fullModelF_Y) = partialModelFToFullModelFWithRotate_expr(partialModelF_X, partialModelF_Y, periodicity)
		fullModelF_Z = []
		if (dimension==3):
			partialModelF_Z = create_partial_force_data_expr(study, "Z", lastCycleStartIndex, numExtendCycle)
			fullModelF_Z = partialModelFToFullModelFWithCopy_expr(partialModelF_Z, periodicity)

		##make the 1st line for set names
		header_1st = ""
		columnTitles = getColumnTitles(calcDefForHeader, periodicity)
		for i in range(len(columnTitles)):
			header_1st += "," + columnTitles[i] + ",,"
			
		##make the 2nd line for time and each set components
		header_2nd = "Time"
		for periodicIndex in range(periodicity):
			for setIndex in range(n_set):
				setName = dataSetForHeader.GetColumnName(setIndex+1)
				header_2nd += ",Fx,Fy,Fz"

		## Slice loop
		for sliceIndex in range(numSlice):
			# Write file
			dividedOutputPath = create_file_name(outputPath, numCases, caseIndex+1, useMultiSlice, sliceIndex+1)
			try:
				file = open(dividedOutputPath.decode('utf-8'), 'w')

				## Write header
				file.write(header_1st)
				file.write("\n")
				file.write(header_2nd)
				file.write("\n")

				##Write the all TOOTH value of the model
				for i in range(len(oneCycleTimeData)):
					line=str(oneCycleTimeData[i])
					for j in range(n_set*periodicity):
						line += ","
						line += str(fullModelF_X[j][i])
						line += ","
						line += str(fullModelF_Y[j][i])
						line += ","
						if dimension==3:
							line += str(fullModelF_Z[j][i])
						else:
							line += "0.0"
					file.write(line)
					file.write("\n")
				file.close()
			except Exception as e:
				app.UserProgressFinish()
				show_error_message(str(e), str(e))
				result = False
			if not result:
				break
		if not result:
			break
	study.SetCurrentCase(startCase)
	app.SetCurrentStudy(startStudy.GetName())
	return result

def output_frequency_domain_tooth_forces_expr(app, selectedStudyListIndex, useElectricCycle, outputPath):
	isJapanese = is_japanese(app)
	dimension = app.GetCurrentModel().GetDimension()
	dataManager = app.GetDataManager()
	startStudy = app.GetCurrentStudy()
	(study, designTable) = getStudyAndDesignTable_expr(app, selectedStudyListIndex)
	app.SetCurrentStudy(study.GetName())
	caseIndexList = get_active_case_indices_expr(study)

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

	## Setup parameters
	useMultiSlice = False
	numSlice = 1
	if (dimension==2):
		numSlice = get_slice_number(study)
		useMultiSlice = numSlice > 1

	## Case loop
	startCase = study.GetCurrentCase()
	numCases = len(caseIndexList)
	start_progress_expr(app, numCases)
	result = True
	for caseLoopIndex in range(numCases):
		if check_progress_wascanceled_expr(app):
			break
		else:
			app.UserProgressStep()

		caseIndex = caseIndexList[caseLoopIndex]
		study.SetCurrentCase(caseIndex)

		##Get last 1 cycle times
		calcDefForHeader = findNodalForceCalculationDefinition_expr(study, "X")
		dataSetForHeader = calcDefForHeader.GetDataSet()
		n_set = dataSetForHeader.GetCols() - 1
		numSlots = get_slot_expr(designTable, caseIndex)
		periodicity = numSlots//n_set

		(lastCycleStartIndex, numExtendCycle) = get_last_cycle_start_index_expr(get_speed(study), get_pole_expr(designTable, caseIndex), useElectricCycle, dataSetForHeader.GetColumn(0))
		if lastCycleStartIndex < 0:
			result = False
			break
		(freqList, partialModelF_Xr, partialModelF_Xi) = create_fft_force_data_expr(study, dataManager, "X", dimension, lastCycleStartIndex)
		(freqList, partialModelF_Yr, partialModelF_Yi) = create_fft_force_data_expr(study, dataManager, "Y", dimension, lastCycleStartIndex)
		(fullModelF_Xr, fullModelF_Yr) = partialModelFToFullModelFWithRotate_expr(partialModelF_Xr, partialModelF_Yr, periodicity)
		(fullModelF_Xi, fullModelF_Yi) = partialModelFToFullModelFWithRotate_expr(partialModelF_Xi, partialModelF_Yi, periodicity)
		fullModelF_Zr = []
		fullModelF_Zi = []
		if (dimension==3):
			(freqList, partialModelF_Zr, partialModelF_Zi) = create_fft_force_data_expr(study, useElectricCycle, "Z", dimension, lastCycleStartIndex)
			for periodicIndex in range(periodicity):
				fullModelF_Zr.extend(partialModelF_Zr)
				fullModelF_Zi.extend(partialModelF_Zi)


		##make the 1st line for set names
		header_1st = ""
		columnTitles = getColumnTitles(calcDefForHeader, periodicity)
		for i in range(len(columnTitles)):
			header_1st += "," + columnTitles[i] + ",,,,,"

		##make the 2nd line for time and each set components
		header_2nd = "Frequency"
		for periodicIndex in range(periodicity):
			for setIndex in range(n_set):
				header_2nd += ",Fx(real),Fx(imag),Fy(real),Fy(imag),Fz(real),Fz(imag)"

		## Slice loop
		slice_start = 0
		for sliceIndex in range(numSlice):
			# Write file
			dividedOutputPath = create_file_name(outputPath, numCases, caseIndex+1, useMultiSlice, sliceIndex+1)
			try:
				file = open(dividedOutputPath.decode('utf-8'), 'w')

				## Write header
				file.write(header_1st)
				file.write("\n")
				file.write(header_2nd)
				file.write("\n")

				##Write the all TOOTH value of the model
				slice_start = sliceIndex * n_set * periodicity * 2
				slice_end = (sliceIndex+1) * n_set * periodicity * 2
				for i in range(len(freqList)):
					line=str(freqList[i])
					for j in range(n_set*periodicity):
						line += ","
						line += str(fullModelF_Xr[j][i])
						line += ","
						line += str(fullModelF_Xi[j][i])
						line += ","
						line += str(fullModelF_Yr[j][i])
						line += ","
						line += str(fullModelF_Yi[j][i])
						line += ","
						if dimension==3:
							line += str(fullModelF_Zr[j][i])
							line += ","
							line += str(fullModelF_Zi[j+1][i])
						else:
							line += "0.0,0.0"
					file.write(line)
					file.write("\n")
				file.close()
			except Exception as e:
				app.UserProgressFinish()
				show_error_message(str(e), str(e))
				result = False
			slice_start += n_set*2
			if not result:
				break
		if not result:
			break
	study.SetCurrentCase(startCase)
	app.SetCurrentStudy(startStudy.GetName())
	return result

def hasActiveCase_expr(study):
	caseIndexList = get_active_case_indices_expr(study)
	return len(caseIndexList) > 0

def pre_scenario_mode_work():
	if CanUseScript():
		return True
	else:
		show_error_message("Unsuppoerted Synario", "未対応のシナリオです。")
		return False

def check_post_state_expr(app):
	for study in GetCanUseScriptStudies(app, True):
		caseIndexList = get_active_case_indices_expr(study)
		if len(caseIndexList) > 0:
			return (True, "", "")
	return (False, "No case is selected for which the result exists.", "結果が存在するケースが選択されていません。")

def setup_progress_expr(app):
	progressLabel = "Exporting to CSV File..."
	if (is_japanese(app)):
		progressLabel = "CSVファイルへ出力中..."
	app.SetupUserProgress(progressLabel)
	app.SetUserProgressUseCancel(True)

def start_progress_expr(app, maxSteps):
	#return
	app.SetUserProgressMaxSteps(maxSteps)
	app.UserProgressStart()

def end_progress_expr(app, resFlg):
	#ShowFinishMessage(app, not resFlg)
	wasCanceled = app.UserProgressWasCanceled()
	app.UserProgressFinish()
	ShowFinishMessage(app, wasCanceled or (not resFlg))

def check_progress_wascanceled_expr(app):
	#return False
	return app.UserProgressWasCanceled()

def post_scenario_mode_work():
	app = designer.GetApplication()
	(resCheck, error_msg_en, error_msg_jp) = check_post_state_expr(app)
	if not resCheck:
		show_error_message(error_msg_en, error_msg_jp)
		return
	(dialogShowResponse, selectedStudyListIndex, useElectricCycle, outputPath, outputFormatType) = get_param_from_input_dialog_expr(app)
	if dialogShowResponse != 1:
		return
	elif len(outputPath)==0:
		show_error_message("Output file path is invalid.","出力ファイルパスが不正です。")
		return
	setup_progress_expr(app)
	resFlg = False
	if outputFormatType==0:
		resFlg = output_single_file_including_torque_expr(app, selectedStudyListIndex, useElectricCycle, outputPath, True)
	elif outputFormatType==1:
		resFlg = output_time_domain_tooth_forces_expr(app, selectedStudyListIndex, useElectricCycle, outputPath)
	elif outputFormatType==2:
		resFlg = output_frequency_domain_tooth_forces_expr(app, selectedStudyListIndex, useElectricCycle, outputPath)
	end_progress_expr(app, resFlg)

def run_scenario_mode():
	startStudy = designer.GetApplication().GetCurrentStudy()
	if pre_scenario_mode_work():
		post_scenario_mode_work()
	designer.GetApplication().SetCurrentStudy(startStudy.GetName())

def run_prefix_tooth_set_mode():
	main_designer()

# 0: prefix_tooth_set_mode (designer)
# 1: scenario_mode (express)
#-1: quit
def selectMode(app):
	if app.IsExpressMode():
		return 1
	if CanUseScript():
		return 1
	if check_model(app):
		return 0
	return -1
mode = selectMode(app)
if mode==0:
	run_prefix_tooth_set_mode()
elif mode==1:
	run_scenario_mode()

Download Python source code

ファイルご利用の注意点

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