def test_templated_input_2Darrays(self): template = '\n'.join([ "Anchor", "0 0 0 0 0", "0 0 0 0 0" ]) outfile = open(self.templatename, 'w') outfile.write(template) outfile.close() gen = InputFileGenerator() gen.set_template_file(self.templatename) gen.set_generated_file(self.filename) gen.mark_anchor('Anchor') var = array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) gen.transfer_2Darray(var, 1, 2, 1, 5) gen.generate() infile = open(self.filename, 'r') result = infile.read() infile.close() answer = '\n'.join([ "Anchor", "1 2 3 4 5", "6 7 8 9 10" ]) self.assertEqual(answer, result)
def test_templated_input_same_anchors(self): template = '\n'.join( ["CQUAD4 1 3.456", "CQUAD4 2 4.123", "CQUAD4 3 7.222", "CQUAD4 4"]) outfile = open(self.templatename, 'w') outfile.write(template) outfile.close() gen = InputFileGenerator() gen.set_template_file(self.templatename) gen.set_generated_file(self.filename) gen.set_delimiters(', ') gen.mark_anchor('CQUAD4') gen.transfer_var('x', 0, 2) gen.mark_anchor('CQUAD4') gen.transfer_var('y', 0, 3) gen.mark_anchor('CQUAD4', 2) gen.transfer_var('z', 0, 2) gen.generate() infile = open(self.filename, 'r') result = infile.read() infile.close() answer = '\n'.join( ["CQUAD4 x 3.456", "CQUAD4 2 y", "CQUAD4 3 7.222", "CQUAD4 z"]) self.assertEqual(answer, result)
def test_templated_input_arrays(self): template = '\n'.join([ "Anchor", "0 0 0 0 0" ]) outfile = open(self.templatename, 'w') outfile.write(template) outfile.close() gen = InputFileGenerator() gen.set_template_file(self.templatename) gen.set_generated_file(self.filename) gen.mark_anchor('Anchor') gen.transfer_array(array([1, 2, 3, 4.75, 5.0]), 1, 3, 5, sep=' ') gen.generate() infile = open(self.filename, 'r') result = infile.read() infile.close() answer = '\n'.join([ "Anchor", "0 0 1.0 2.0 3.0 4.75 5.0" ]) self.assertEqual(answer, result)
def generate_configuration_file(configuration_file_path: str, overwrite: bool = False): """ Generates a sample configuration file. :param configuration_file_path: the path of file to be written :param overwrite: if True, the file will be written, even if it already exists :raise FastFileExistsError: if overwrite==False and configuration_file_path already exists """ if not overwrite and pth.exists(configuration_file_path): raise FastFileExistsError( "Configuration file %s not written because it already exists. " "Use overwrite=True to bypass." % configuration_file_path, configuration_file_path, ) if not pth.exists(pth.split(configuration_file_path)[0]): os.mkdir(pth.split(configuration_file_path)[0]) parser = InputFileGenerator() root_folder = resources.__path__[0] for i in range(2): root_folder = pth.split(root_folder)[0] package_path = "[\"" + root_folder.replace('\\','\\\\') + "\"]" with path(resources, SAMPLE_FILENAME) as input_template_path: parser.set_template_file(str(input_template_path)) # noinspection PyTypeChecker parser.set_generated_file(configuration_file_path) parser.reset_anchor() parser.mark_anchor("module_folders") parser.transfer_var(package_path, 0, 3) parser.generate() _LOGGER.info("Sample configuration written in %s", configuration_file_path)
def compute(self, inputs, outputs): x = inputs['RadLen'] var = '{0};'.format(float(x)) # generate the input file for esatan thermal analysis generator = InputFileGenerator() generator.set_template_file('radiator_templ.txt') generator.set_generated_file('radiator.d') generator.mark_anchor("$CONSTANTS") generator.transfer_var(var, 4, 3) generator.generate() # the parent compute function actually runs the external code super(Radiator, self).compute(inputs, outputs) # parse the output file from the external code and set the value of T_max parser = FileParser() parser.set_file('radiator.out') parser.mark_anchor("+RADIATOR ") T_max = parser.transfer_var(5, 6) outputs['T_max'] = T_max
def _write_script_file(self, reynolds, mach, tmp_profile_file_path, tmp_result_file_path, alpha_start, alpha_end, step): parser = InputFileGenerator() with path(local_resources, _INPUT_FILE_NAME) as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(self.stdin) parser.mark_anchor("RE") parser.transfer_var(float(reynolds), 1, 1) parser.mark_anchor("M") parser.transfer_var(float(mach), 1, 1) parser.mark_anchor("ITER") parser.transfer_var(self.options[OPTION_ITER_LIMIT], 1, 1) parser.mark_anchor("ASEQ") parser.transfer_var(alpha_start, 1, 1) parser.transfer_var(alpha_end, 2, 1) parser.transfer_var(step, 3, 1) parser.reset_anchor() parser.mark_anchor("/profile") parser.transfer_var(tmp_profile_file_path, 0, 1) parser.mark_anchor("/polar_result") parser.transfer_var(tmp_result_file_path, 0, 1) parser.generate()
def test_templated_input_same_anchors(self): template = '\n'.join([ "CQUAD4 1 3.456", "CQUAD4 2 4.123", "CQUAD4 3 7.222", "CQUAD4 4" ]) outfile = open(self.templatename, 'w') outfile.write(template) outfile.close() gen = InputFileGenerator() gen.set_template_file(self.templatename) gen.set_generated_file(self.filename) gen.set_delimiters(', ') gen.mark_anchor('CQUAD4') gen.transfer_var('x', 0, 2) gen.mark_anchor('CQUAD4') gen.transfer_var('y', 0, 3) gen.mark_anchor('CQUAD4', 2) gen.transfer_var('z', 0, 2) gen.generate() infile = open(self.filename, 'r') result = infile.read() infile.close() answer = '\n'.join([ "CQUAD4 x 3.456", "CQUAD4 2 y", "CQUAD4 3 7.222", "CQUAD4 z" ]) self.assertEqual(answer, result)
def test_templated_input(self): template = '\n'.join([ "Junk", "Anchor", " A 1, 2 34, Test 1e65", " B 4 Stuff", "Anchor", " C 77 False Inf 333.444" ]) outfile = open(self.templatename, 'w') outfile.write(template) outfile.close() gen = InputFileGenerator() gen.set_template_file(self.templatename) gen.set_generated_file(self.filename) gen.set_delimiters(', ') gen.mark_anchor('Anchor') gen.transfer_var('CC', 2, 0) gen.transfer_var(3.0, 1, 3) gen.reset_anchor() gen.mark_anchor('Anchor', 2) gen.transfer_var('NaN', 1, 4) gen.reset_anchor() gen.transfer_var('55', 3, 2) gen.mark_anchor('C 77') gen.transfer_var(1.3e-37, -3, 6) gen.clearline(-5) gen.mark_anchor('Anchor', -1) gen.transfer_var('8.7', 1, 5) gen.generate() infile = open(self.filename, 'r') result = infile.read() infile.close() answer = '\n'.join([ "", "Anchor", " A 1, 3.0 34, Test 1.3e-37", " B 55 Stuff", "Anchor", " C 77 False NaN 8.7" ]) self.assertEqual(answer, result) # Test some errors try: gen.mark_anchor('C 77', 3.14) except ValueError as err: msg = "The value for occurrence must be an integer" self.assertEqual(str(err), msg) else: self.fail('ValueError expected') try: gen.mark_anchor('C 77', 0) except ValueError as err: msg = "0 is not valid for an anchor occurrence." self.assertEqual(str(err), msg) else: self.fail('ValueError expected') try: gen.mark_anchor('ZZZ') except RuntimeError as err: msg = "Could not find pattern ZZZ in template file template.dat" self.assertEqual(str(err), msg) else: self.fail('RuntimeError expected')
def compute(self, inputs, outputs): # Create result folder first (if it must fail, let it fail as soon as possible) result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(pth.join(result_folder_path, 'ClAlphaWING'), exist_ok=True) # Get inputs (and calculate missing ones) x0_wing = inputs["data:geometry:wing:MAC:leading_edge:x:local"] l0_wing = inputs["data:geometry:wing:MAC:length"] width_max = inputs["data:geometry:fuselage:maximum_width"] y1_wing = width_max / 2.0 y2_wing = inputs["data:geometry:wing:root:y"] l2_wing = inputs["data:geometry:wing:root:chord"] y4_wing = inputs["data:geometry:wing:tip:y"] l4_wing = inputs["data:geometry:wing:tip:chord"] sweep_0_wing = inputs["data:geometry:wing:sweep_0"] fa_length = inputs["data:geometry:wing:MAC:at25percent:x"] sref_wing = inputs['data:geometry:wing:area'] span_wing = inputs['data:geometry:wing:span'] height_max = inputs["data:geometry:fuselage:maximum_height"] if self.options["low_speed_aero"]: altitude = 0.0 atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:low_speed:mach"] else: altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:cruise:mach"] # Initial parameters calculation x_wing = fa_length - x0_wing - 0.25 * l0_wing z_wing = -(height_max - 0.12 * l2_wing) * 0.5 span2_wing = y4_wing - y2_wing viscosity = atm.kinematic_viscosity rho = atm.density v_inf = max(atm.speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes reynolds = v_inf * l0_wing / viscosity # OPENVSP-SCRIPT: Geometry generation ###################################################### # I/O files -------------------------------------------------------------------------------- tmp_directory = self._create_tmp_directory() # avoid to dump void xternal_code_comp_error.out error file self.stderr = pth.join(tmp_directory.name, _STDERR_FILE_NAME) if self.options[OPTION_OPENVSP_EXE_PATH]: target_directory = pth.abspath( self.options[OPTION_OPENVSP_EXE_PATH]) else: target_directory = tmp_directory.name input_file_list = [ pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME), pth.join(target_directory, self.options['wing_airfoil_file']) ] tmp_result_file_path = pth.join(target_directory, _INPUT_AERO_FILE_NAME + '0.csv') output_file_list = [tmp_result_file_path] self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (populating temp directory and generate batch file) ----------------------- # Copy resource in temp directory if needed if not (self.options[OPTION_OPENVSP_EXE_PATH]): # noinspection PyTypeChecker copy_resource_folder(openvsp3201, target_directory) # noinspection PyTypeChecker copy_resource(resources, self.options['wing_airfoil_file'], target_directory) # Create corresponding .bat file self.options["command"] = [pth.join(target_directory, 'vspscript.bat')] command = pth.join(target_directory, VSPSCRIPT_EXE_NAME) + ' -script ' \ + pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME) + ' >nul 2>nul\n' batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") batch_file.write(command) batch_file.close() # standard SCRIPT input file --------------------------------------------------------------- parser = InputFileGenerator() with path(local_resources, _INPUT_SCRIPT_FILE_NAME) as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[0]) parser.mark_anchor("x_wing") parser.transfer_var(float(x_wing), 0, 5) parser.mark_anchor("z_wing") parser.transfer_var(float(z_wing), 0, 5) parser.mark_anchor("y1_wing") parser.transfer_var(float(y1_wing), 0, 5) for i in range(3): parser.mark_anchor("l2_wing") parser.transfer_var(float(l2_wing), 0, 5) parser.reset_anchor() parser.mark_anchor("span2_wing") parser.transfer_var(float(span2_wing), 0, 5) parser.mark_anchor("l4_wing") parser.transfer_var(float(l4_wing), 0, 5) parser.mark_anchor("sweep_0_wing") parser.transfer_var(float(sweep_0_wing), 0, 5) parser.mark_anchor("airfoil_0_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_1_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_2_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("csv_file") parser.transfer_var(self._rewrite_path(tmp_result_file_path), 0, 3) parser.generate() # Run SCRIPT -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'ClAlphaWING', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'ClAlphaWING', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # OPENVSP-AERO: aero calculation ############################################################ # I/O files -------------------------------------------------------------------------------- # Duplicate .csv file for multiple run input_file_list = [tmp_result_file_path] for idx in range(len(_INPUT_AOAList) - 1): shutil.copy( tmp_result_file_path, pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) input_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) output_file_list = [] for idx in range(len(_INPUT_AOAList)): input_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.vspaero') output_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.polar') output_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME) + '0.lod') self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (create batch file) ------------------------------------------------------- self.options["command"] = [pth.join(target_directory, 'vspaero.bat')] batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") for idx in range(len(_INPUT_AOAList)): command = pth.join(target_directory, VSPAERO_EXE_NAME) + ' ' \ + pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx) + ' >nul 2>nul\n') batch_file.write(command) batch_file.close() # standard AERO input file ----------------------------------------------------------------- parser = InputFileGenerator() for idx in range(len(_INPUT_AOAList)): with path(local_resources, _INPUT_AERO_FILE_NAME + '.vspaero') as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[len(_INPUT_AOAList) + idx]) parser.reset_anchor() parser.mark_anchor("Sref") parser.transfer_var(float(sref_wing), 0, 3) parser.mark_anchor("Cref") parser.transfer_var(float(l0_wing), 0, 3) parser.mark_anchor("Bref") parser.transfer_var(float(span_wing), 0, 3) parser.mark_anchor("X_cg") parser.transfer_var(float(fa_length), 0, 3) parser.mark_anchor("Mach") parser.transfer_var(float(mach), 0, 3) parser.mark_anchor("AOA") parser.transfer_var(float(_INPUT_AOAList[idx]), 0, 3) parser.mark_anchor("Vinf") parser.transfer_var(float(v_inf), 0, 3) parser.mark_anchor("Rho") parser.transfer_var(float(rho), 0, 3) parser.mark_anchor("ReCref") parser.transfer_var(float(reynolds), 0, 3) parser.generate() # Run AERO -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Post-processing -------------------------------------------------------------------------- result_cl = [] for idx in range(len(_INPUT_AOAList)): cl, _, _, _ = self._read_polar_file(output_file_list[idx]) result_cl.append(cl) # Fuselage correction k_fus = 1 + 0.025 * width_max / span_wing - 0.025 * (width_max / span_wing)**2 cl_0 = float(result_cl[0] * k_fus) cl_1 = float(result_cl[1] * k_fus) # Calculate derivative cl_alpha = (cl_1 - cl_0) / ( (_INPUT_AOAList[1] - _INPUT_AOAList[0]) * math.pi / 180) # Get lift curve y_vector, cl_vector = self._read_lod_file(output_file_list[-1]) real_length = min(SPAN_MESH_POINT_OPENVSP, len(y_vector)) if real_length < len(y_vector): warnings.warn( "Defined maximum span mesh in constants.py exceeded!") if self.options["low_speed_aero"]: outputs['data:aerodynamics:aircraft:low_speed:CL0_clean'] = cl_0 outputs['data:aerodynamics:aircraft:low_speed:CL_alpha'] = cl_alpha if real_length >= len(y_vector): outputs[ 'data:aerodynamics:wing:low_speed:Y_vector'] = np.zeros( SPAN_MESH_POINT_OPENVSP) outputs[ 'data:aerodynamics:wing:low_speed:CL_vector'] = np.zeros( SPAN_MESH_POINT_OPENVSP) outputs['data:aerodynamics:wing:low_speed:Y_vector'][ 0:real_length] = y_vector outputs['data:aerodynamics:wing:low_speed:CL_vector'][ 0:real_length] = cl_vector else: outputs[ 'data:aerodynamics:aircraft:wing:Y_vector'] = np.linspace( y_vector[0], y_vector[1], SPAN_MESH_POINT_OPENVSP) outputs['data:aerodynamics:aircraft:wing:CL_vector'] = \ np.interp(outputs['data:aerodynamics:aircraft:low_speed:Y_vector'], y_vector, cl_vector) else: outputs['data:aerodynamics:aircraft:cruise:CL0_clean'] = cl_0 outputs['data:aerodynamics:aircraft:cruise:CL_alpha'] = cl_alpha # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'ClAlphaWING', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'ClAlphaWING', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # Delete temporary directory tmp_directory.cleanup()
def compute(self, inputs, outputs): # Create result folder first (if it must fail, let it fail as soon as possible) result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(result_folder_path, exist_ok=True) # Get inputs and initialise outputs thickness_ratio = inputs["data:geometry:wing:thickness_ratio"] length = inputs["xfoil:length"] mach = inputs["xfoil:mach"] reynolds = inputs["xfoil:unit_reynolds"]*length # Pre-processing (populating temp directory) ----------------------------------------------- # XFoil exe tmp_directory = self._create_tmp_directory() if self.options[OPTION_XFOIL_EXE_PATH]: # if a path for Xfoil has been provided, simply use it self.options["command"] = [self.options[OPTION_XFOIL_EXE_PATH]] else: # otherwise, copy the embedded resource in tmp dir # noinspection PyTypeChecker copy_resource(xfoil699, XFOIL_EXE_NAME, tmp_directory.name) self.options["command"] = [pth.join(tmp_directory.name, XFOIL_EXE_NAME)] # I/O files self.stdin = pth.join(tmp_directory.name, _INPUT_FILE_NAME) self.stdout = pth.join(tmp_directory.name, _STDOUT_FILE_NAME) self.stderr = pth.join(tmp_directory.name, _STDERR_FILE_NAME) # profile file tmp_profile_file_path = pth.join(tmp_directory.name, _TMP_PROFILE_FILE_NAME) profile = get_profile( file_name=self.options["wing_airfoil_file"], thickness_ratio=thickness_ratio, ).get_sides() # noinspection PyTypeChecker np.savetxt( tmp_profile_file_path, profile.to_numpy(), fmt="%.15f", delimiter=" ", header="Wing", comments="", ) # standard input file tmp_result_file_path = pth.join(tmp_directory.name, _TMP_RESULT_FILE_NAME) parser = InputFileGenerator() with path(local_resources, _INPUT_FILE_NAME) as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(self.stdin) parser.mark_anchor("RE") parser.transfer_var(float(reynolds), 1, 1) parser.mark_anchor("M") parser.transfer_var(float(mach), 1, 1) parser.mark_anchor("ITER") parser.transfer_var(self.options[OPTION_ITER_LIMIT], 1, 1) parser.mark_anchor("ASEQ") parser.transfer_var(self.options[OPTION_ALPHA_START], 1, 1) parser.transfer_var(self.options[OPTION_ALPHA_END], 2, 1) parser.reset_anchor() parser.mark_anchor("/profile") parser.transfer_var(tmp_profile_file_path, 0, 1) parser.mark_anchor("/polar_result") parser.transfer_var(tmp_result_file_path, 0, 1) parser.generate() # Run XFOIL -------------------------------------------------------------------------------- self.options["external_input_files"] = [self.stdin, tmp_profile_file_path] self.options["external_output_files"] = [tmp_result_file_path] super().compute(inputs, outputs) # Post-processing -------------------------------------------------------------------------- result_array = self._read_polar(tmp_result_file_path) cl_max_2d = self._get_max_cl(result_array["alpha"], result_array["CL"]) real_length = min(POLAR_POINT_COUNT, len(result_array["alpha"])) if real_length < len(result_array["alpha"]): warnings.warn("Defined maximum polar point count in constants.py exceeded!") outputs["xfoil:alpha"] = np.linspace(result_array["alpha"][0], result_array["alpha"][-1], POLAR_POINT_COUNT) outputs["xfoil:CL"] = np.interp(outputs["xfoil:alpha"], result_array["alpha"], result_array["CL"]) outputs["xfoil:CD"] = np.interp(outputs["xfoil:alpha"], result_array["alpha"], result_array["CD"]) outputs["xfoil:CDp"] = np.interp(outputs["xfoil:alpha"], result_array["alpha"], result_array["CDp"]) outputs["xfoil:CDp"] = np.interp(outputs["xfoil:alpha"], result_array["alpha"], result_array["CM"]) else: outputs["xfoil:alpha"] = np.zeros(POLAR_POINT_COUNT) outputs["xfoil:CL"] = np.zeros(POLAR_POINT_COUNT) outputs["xfoil:CD"] = np.zeros(POLAR_POINT_COUNT) outputs["xfoil:CDp"] = np.zeros(POLAR_POINT_COUNT) outputs["xfoil:CM"] = np.zeros(POLAR_POINT_COUNT) outputs["xfoil:alpha"][0:real_length] = result_array["alpha"] outputs["xfoil:CL"][0:real_length] = result_array["CL"] outputs["xfoil:CD"][0:real_length] = result_array["CD"] outputs["xfoil:CDp"][0:real_length] = result_array["CDp"] outputs["xfoil:CM"][0:real_length] = result_array["CM"] outputs["xfoil:CL_max_2D"] = cl_max_2d # Getting output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": if pth.exists(tmp_result_file_path): polar_file_path = pth.join( result_folder_path, self.options[OPTION_RESULT_POLAR_FILENAME] ) shutil.move(tmp_result_file_path, polar_file_path) if pth.exists(self.stdout): stdout_file_path = pth.join(result_folder_path, _STDOUT_FILE_NAME) shutil.move(self.stdout, stdout_file_path) if pth.exists(self.stderr): stderr_file_path = pth.join(result_folder_path, _STDERR_FILE_NAME) shutil.move(self.stderr, stderr_file_path) tmp_directory.cleanup()
def compute(self, inputs, outputs): # Create result folder first (if it must fail, let it fail as soon as possible) result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(pth.join(result_folder_path, 'ClCmHT'), exist_ok=True) # Get inputs (and calculate missing ones) x0_wing = inputs["data:geometry:wing:MAC:leading_edge:x:local"] l0_wing = inputs["data:geometry:wing:MAC:length"] width_max = inputs["data:geometry:fuselage:maximum_width"] y1_wing = width_max / 2.0 y2_wing = inputs["data:geometry:wing:root:y"] l2_wing = inputs["data:geometry:wing:root:chord"] y4_wing = inputs["data:geometry:wing:tip:y"] l4_wing = inputs["data:geometry:wing:tip:chord"] sweep_0_wing = inputs["data:geometry:wing:sweep_0"] fa_length = inputs["data:geometry:wing:MAC:at25percent:x"] sref_wing = inputs['data:geometry:wing:area'] span_wing = inputs['data:geometry:wing:span'] height_max = inputs["data:geometry:fuselage:maximum_height"] sweep_25_htp = inputs["data:geometry:horizontal_tail:sweep_25"] span_htp = inputs["data:geometry:horizontal_tail:span"] / 2.0 root_chord_htp = inputs["data:geometry:horizontal_tail:root:chord"] tip_chord_htp = inputs["data:geometry:horizontal_tail:tip:chord"] lp_htp = inputs[ "data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"] l0_htp = inputs["data:geometry:horizontal_tail:MAC:length"] x0_htp = inputs[ "data:geometry:horizontal_tail:MAC:at25percent:x:local"] height_htp = inputs["data:geometry:horizontal_tail:z:from_wingMAC25"] mach = inputs["data:aerodynamics:low_speed:mach"] altitude = 0.0 # Compute remaining inputs x_wing = fa_length - x0_wing - 0.25 * l0_wing z_wing = -(height_max - 0.12 * l2_wing) * 0.5 span2_wing = y4_wing - y2_wing distance_htp = fa_length + lp_htp - 0.25 * l0_htp - x0_htp atm = Atmosphere(altitude) viscosity = atm.kinematic_viscosity rho = atm.density v_inf = max(atm.speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes reynolds = v_inf * l0_wing / viscosity # OPENVSP-SCRIPT: Geometry generation ###################################################### # I/O files -------------------------------------------------------------------------------- tmp_directory = self._create_tmp_directory() # avoid to dump void xternal_code_comp_error.out error file self.stderr = pth.join(tmp_directory.name, _STDERR_FILE_NAME) if self.options[OPTION_OPENVSP_EXE_PATH]: target_directory = pth.abspath( self.options[OPTION_OPENVSP_EXE_PATH]) else: target_directory = tmp_directory.name input_file_list = [ pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME), pth.join(target_directory, self.options['wing_airfoil_file']), pth.join(target_directory, self.options['htp_airfoil_file']) ] tmp_result_file_path = pth.join(target_directory, _INPUT_AERO_FILE_NAME + '0.csv') output_file_list = [tmp_result_file_path] self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (populating temp directory) ----------------------------------------------- # Copy resource in temp directory if needed if not (self.options[OPTION_OPENVSP_EXE_PATH]): # noinspection PyTypeChecker copy_resource_folder(openvsp3201, target_directory) # noinspection PyTypeChecker copy_resource(resources, self.options['wing_airfoil_file'], target_directory) # noinspection PyTypeChecker copy_resource(resources, self.options['htp_airfoil_file'], target_directory) # Create corresponding .bat file self.options["command"] = [pth.join(target_directory, 'vspscript.bat')] command = pth.join(target_directory, VSPSCRIPT_EXE_NAME) + ' -script ' \ + pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME) + ' >nul 2>nul\n' batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") batch_file.write(command) batch_file.close() # standard SCRIPT input file ---------------------------------------------------------------- parser = InputFileGenerator() with path(local_resources, _INPUT_SCRIPT_FILE_NAME) as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[0]) parser.mark_anchor("x_wing") parser.transfer_var(float(x_wing), 0, 5) parser.mark_anchor("z_wing") parser.transfer_var(float(z_wing), 0, 5) parser.mark_anchor("y1_wing") parser.transfer_var(float(y1_wing), 0, 5) for i in range(3): parser.mark_anchor("l2_wing") parser.transfer_var(float(l2_wing), 0, 5) parser.reset_anchor() parser.mark_anchor("span2_wing") parser.transfer_var(float(span2_wing), 0, 5) parser.mark_anchor("l4_wing") parser.transfer_var(float(l4_wing), 0, 5) parser.mark_anchor("sweep_0_wing") parser.transfer_var(float(sweep_0_wing), 0, 5) parser.mark_anchor("airfoil_0_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_1_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_2_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("distance_htp") parser.transfer_var(float(distance_htp), 0, 5) parser.mark_anchor("height_htp") parser.transfer_var(float(height_htp), 0, 5) parser.mark_anchor("span_htp") parser.transfer_var(float(span_htp), 0, 5) parser.mark_anchor("root_chord_htp") parser.transfer_var(float(root_chord_htp), 0, 5) parser.mark_anchor("tip_chord_htp") parser.transfer_var(float(tip_chord_htp), 0, 5) parser.mark_anchor("sweep_25_htp") parser.transfer_var(float(sweep_25_htp), 0, 5) parser.mark_anchor("airfoil_3_file") parser.transfer_var(self._rewrite_path(input_file_list[2]), 0, 3) parser.mark_anchor("airfoil_4_file") parser.transfer_var(self._rewrite_path(input_file_list[2]), 0, 3) parser.mark_anchor("csv_file") parser.transfer_var(self._rewrite_path(tmp_result_file_path), 0, 3) parser.generate() # Run SCRIPT -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'ClCmHT', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'ClCmHT', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # OPENVSP-AERO: aero calculation ############################################################ # I/O files -------------------------------------------------------------------------------- # Duplicate .csv file for multiple run input_file_list = [tmp_result_file_path] for idx in range(len(_INPUT_AOAList) - 1): shutil.copy( tmp_result_file_path, pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) input_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) output_file_list = [] for idx in range(len(_INPUT_AOAList)): input_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.vspaero') output_file_list.append( pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.lod') self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (create batch file) ------------------------------------------------------- self.options["command"] = [pth.join(target_directory, 'vspaero.bat')] batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") for idx in range(len(_INPUT_AOAList)): command = pth.join(target_directory, VSPAERO_EXE_NAME) + ' ' \ + pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx) + ' >nul 2>nul\n') batch_file.write(command) batch_file.close() # standard AERO input file ----------------------------------------------------------------- parser = InputFileGenerator() for idx in range(len(_INPUT_AOAList)): with path(local_resources, _INPUT_AERO_FILE_NAME + '.vspaero') as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[len(_INPUT_AOAList) + idx]) parser.reset_anchor() parser.mark_anchor("Sref") parser.transfer_var(float(sref_wing), 0, 3) parser.mark_anchor("Cref") parser.transfer_var(float(l0_wing), 0, 3) parser.mark_anchor("Bref") parser.transfer_var(float(span_wing), 0, 3) parser.mark_anchor("X_cg") parser.transfer_var(float(fa_length), 0, 3) parser.mark_anchor("Mach") parser.transfer_var(float(mach), 0, 3) parser.mark_anchor("AOA") parser.transfer_var(float(_INPUT_AOAList[idx]), 0, 3) parser.mark_anchor("Vinf") parser.transfer_var(float(v_inf), 0, 3) parser.mark_anchor("Rho") parser.transfer_var(float(rho), 0, 3) parser.mark_anchor("ReCref") parser.transfer_var(float(reynolds), 0, 3) parser.generate() # Run AERO -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Post-processing -------------------------------------------------------------------------- result_cl = [] result_cm1 = [] result_cm2 = [] for idx in range(len(_INPUT_AOAList)): cl_htp, cm_htp, cm_wing = self._read_lod_file( output_file_list[idx]) result_cl.append(cl_htp) result_cm1.append(cm_htp) result_cm2.append(cm_wing) outputs[ 'data:aerodynamics:horizontal_tail:low_speed:alpha'] = np.array( _INPUT_AOAList) outputs['data:aerodynamics:horizontal_tail:low_speed:CL'] = np.array( result_cl) outputs['data:aerodynamics:horizontal_tail:low_speed:CM'] = np.array( result_cm1) outputs['data:aerodynamics:wing:low_speed:alpha'] = np.array( _INPUT_AOAList) outputs['data:aerodynamics:wing:low_speed:CM'] = np.array(result_cm2) # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'ClCmHT', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'ClCmHT', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # Delete temporary directory tmp_directory.cleanup()
def compute(self, inputs, outputs): # Results Folder creation if needed -------------------------------------------------------- result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(result_folder_path, exist_ok=True) # Creation of the temporary directory ------------------------------------------------------ tmp_dir = TemporaryDirectory() if self.options[OPTION_AVL_EXE_PATH] != "": copy_resource(xfoil_resources, _PROFILE_FILE_NAME, tmp_dir.name) self.options["command"] = [self.options[OPTION_AVL_EXE_PATH]] else: copy_resource(avl336, _AVL_EXE_NAME, tmp_dir.name) copy_resource(xfoil_resources, _PROFILE_FILE_NAME, tmp_dir.name) self.options["command"] = [pth.join(tmp_dir.name, _AVL_EXE_NAME)] self.stdin = pth.join(tmp_dir.name, _STDIN_FILE_NANE) self.stdout = pth.join(tmp_dir.name, _STDOUT_FILE_NAME) self.stderr = pth.join(tmp_dir.name, _STDERR_FILE_NAME) # AVL geometry file (.avl) creation -------------------------------------------------------- input_geom_file = pth.join(tmp_dir.name, _AVL_GEOM_NAME) profile_file = pth.join(tmp_dir.name, _PROFILE_FILE_NAME) self._get_avl_geom_file(inputs, input_geom_file, _PROFILE_FILE_NAME) # AVL session file creation ---------------------------------------------------------------- s_ref = inputs["data:geometry:wing:area"] b_ref = inputs["data:geometry:wing:span"] c_ref = inputs["data:geometry:wing:MAC:length"] mach = inputs["data:aerostructural:load_case:mach"] alt = inputs["data:aerostructural:load_case:altitude"] rho = Atm(alt).density vtas = Atm(alt).speed_of_sound * mach q = 0.5 * rho * vtas ** 2 m_lc = inputs["data:aerostructural:load_case:weight"] nz = inputs["data:aerostructural:load_case:load_factor"] cl = nz * m_lc * g / (q * s_ref) tmp_result_file = pth.join(tmp_dir.name, self.options[OPTION_RESULT_AVL_FILENAME]) parser = InputFileGenerator() with path(ressources, _STDIN_FILE_NANE) as stdin_template: parser.set_template_file(stdin_template) parser.set_generated_file(self.stdin) # Update session file with target values: parser.mark_anchor("LOAD") parser.transfer_var(input_geom_file, 1, 1) parser.mark_anchor("OPER") parser.transfer_var(float(cl), 1, 3) parser.mark_anchor("M") parser.transfer_var(float(mach), 1, 2) parser.transfer_var(float(vtas), 2, 2) parser.transfer_var(float(rho), 3, 2) parser.mark_anchor("W") parser.transfer_var(tmp_result_file, 1, 1) parser.generate() # Check for input and output file presence ------------------------------------------------- self.options["external_input_files"] = [ self.stdin, input_geom_file, profile_file, ] self.options["external_output_files"] = [tmp_result_file] # Launch AVL ------------------------------------------------------------------------------- super().compute(inputs, outputs) # Gather results --------------------------------------------------------------------------- parser_out = FileParser() parser_out.set_file(tmp_result_file) parser_out.mark_anchor("Alpha =") aoa = parser_out.transfer_var(0, 3) parser_out.mark_anchor("CLtot =") outputs["data:aerostructural:aerodynamic:CL"] = parser_out.transfer_var(0, 3) parser_out.mark_anchor("CDind =") outputs["data:aerostructural:aerodynamic:CDi"] = parser_out.transfer_var(0, 6) outputs["data:aerostructural:aerodynamic:Oswald_Coeff"] = parser_out.transfer_var(2, 6) for (comp, sect) in zip(self.options["components"], self.options["components_sections"]): size = sect # default number of section for non symmetric components if comp in ("wing", "horizontal_tail", "strut"): size = sect * 2 # number of sections doubled for symmetric components elif comp == "fuselage": size = 4 # particular case for fuselage due to specific VLM modelling avl_comp = AVL_COMPONENT_NAMES[comp] parser_out.mark_anchor(avl_comp) comp_coef = parser_out.transfer_2Darray(0, 3, size - 1, 8) # Reorganise result array to have coefficient in direct axis order comp_coef[:, :] = comp_coef[:, [1, 3, 0, 5, 2, 4]] # Comvert coefficients into forces and moments comp_coef[:, :3] *= q * s_ref comp_coef[:, [3, 5]] *= q * s_ref * b_ref comp_coef[:, 4] *= q * s_ref * c_ref # Change forces and moment from aerodynamic to body axis r_mat = self._get_rotation_matrix(aoa * degree, axis="y") comp_coef[:, :3] = np.dot(comp_coef[:, :3], r_mat) comp_coef[:, 3:] = np.dot(comp_coef[:, 3:], r_mat) # Moments in std axis ie X fwd outputs["data:aerostructural:aerodynamic:" + comp + ":forces"] = comp_coef # outputs["data:aerostructural:aerodynamic:forces"] = q * s_ref * surface_coef # Store input and result files if necessary ------------------------------------------------ if self.options[OPTION_RESULT_FOLDER_PATH]: if pth.exists(tmp_result_file): forces_file = pth.join(result_folder_path, self.options[OPTION_RESULT_AVL_FILENAME]) shutil.move(tmp_result_file, forces_file) if pth.exists(input_geom_file): geometry_file = pth.join(result_folder_path, _AVL_GEOM_NAME) shutil.move(input_geom_file, geometry_file) if pth.exists(self.stdin): stdin_path = pth.join(result_folder_path, _STDIN_FILE_NANE) shutil.move(self.stdin, stdin_path) if pth.exists(self.stdout): stdout_path = pth.join(result_folder_path, _STDOUT_FILE_NAME) shutil.move(self.stdout, stdout_path) if pth.exists(self.stderr): stderr_path = pth.join(result_folder_path, _STDERR_FILE_NAME) shutil.move(self.stderr, stderr_path) tmp_dir.cleanup()
def compute(self, inputs, outputs): # Create result folder first (if it must fail, let it fail as soon as possible) result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(result_folder_path, exist_ok=True) # Get inputs reynolds = inputs["xfoil:reynolds"] mach = inputs["xfoil:mach"] thickness_ratio = inputs["data:geometry:wing:thickness_ratio"] # Pre-processing (populating temp directory) ----------------------------------------------- # XFoil exe tmp_directory = self._create_tmp_directory() if self.options[OPTION_XFOIL_EXE_PATH]: # if a path for Xfoil has been provided, simply use it self.options["command"] = [self.options[OPTION_XFOIL_EXE_PATH]] else: # otherwise, copy the embedded resource in tmp dir copy_resource(xfoil699, XFOIL_EXE_NAME, tmp_directory.name) self.options["command"] = [ pth.join(tmp_directory.name, XFOIL_EXE_NAME) ] # I/O files self.stdin = pth.join(tmp_directory.name, _INPUT_FILE_NAME) self.stdout = pth.join(tmp_directory.name, _STDOUT_FILE_NAME) self.stderr = pth.join(tmp_directory.name, _STDERR_FILE_NAME) # profile file tmp_profile_file_path = pth.join(tmp_directory.name, _TMP_PROFILE_FILE_NAME) profile = get_profile(file_name=self.options[OPTION_PROFILE_NAME], thickness_ratio=thickness_ratio).get_sides() np.savetxt( tmp_profile_file_path, profile.to_numpy(), fmt="%.15f", delimiter=" ", header="Wing", comments="", ) # standard input file tmp_result_file_path = pth.join(tmp_directory.name, _TMP_RESULT_FILE_NAME) parser = InputFileGenerator() with path(resources, _INPUT_FILE_NAME) as input_template_path: parser.set_template_file(input_template_path) parser.set_generated_file(self.stdin) # Fills numeric values parser.mark_anchor("RE") parser.transfer_var(float(reynolds), 1, 1) parser.mark_anchor("M") parser.transfer_var(float(mach), 1, 1) parser.mark_anchor("ITER") parser.transfer_var(self.options[OPTION_ITER_LIMIT], 1, 1) parser.mark_anchor("ASEQ") parser.transfer_var(self.options[OPTION_ALPHA_START], 1, 1) parser.transfer_var(self.options[OPTION_ALPHA_END], 2, 1) # Fills string values # If a provide path contains the string that is used as next anchor, the process # will fail. Doing these replacements at the end prevent this to happen. parser.reset_anchor() parser.mark_anchor("LOAD") parser.transfer_var(tmp_profile_file_path, 1, 1) parser.mark_anchor("PACC", -2) parser.transfer_var(tmp_result_file_path, 1, 1) parser.generate() # Run XFOIL -------------------------------------------------------------------------------- self.options["external_input_files"] = [ self.stdin, tmp_profile_file_path ] self.options["external_output_files"] = [tmp_result_file_path] super().compute(inputs, outputs) # Post-processing -------------------------------------------------------------------------- result_array = self._read_polar(tmp_result_file_path) outputs["xfoil:CL_max_2D"] = self._get_max_cl(result_array["alpha"], result_array["CL"]) # Getting output files if needed if self.options[OPTION_RESULT_FOLDER_PATH]: if pth.exists(tmp_result_file_path): polar_file_path = pth.join( result_folder_path, self.options[OPTION_RESULT_POLAR_FILENAME]) shutil.move(tmp_result_file_path, polar_file_path) if pth.exists(self.stdin): stdin_file_path = pth.join(result_folder_path, _INPUT_FILE_NAME) shutil.move(self.stdin, stdin_file_path) if pth.exists(self.stdout): stdout_file_path = pth.join(result_folder_path, _STDOUT_FILE_NAME) shutil.move(self.stdout, stdout_file_path) if pth.exists(self.stderr): stderr_file_path = pth.join(result_folder_path, _STDERR_FILE_NAME) shutil.move(self.stderr, stderr_file_path) tmp_directory.cleanup()
def compute(self, inputs, outputs): # Create result folder first (if it must fail, let it fail as soon as possible) result_folder_path = self.options[OPTION_RESULT_FOLDER_PATH] if result_folder_path != "": os.makedirs(pth.join(result_folder_path, 'OSWALD'), exist_ok=True) # Get inputs (and calculate missing ones) x0_wing = inputs["data:geometry:wing:MAC:leading_edge:x:local"] l0_wing = inputs["data:geometry:wing:MAC:length"] width_max = inputs["data:geometry:fuselage:maximum_width"] y1_wing = width_max / 2.0 y2_wing = inputs["data:geometry:wing:root:y"] l2_wing = inputs["data:geometry:wing:root:chord"] y4_wing = inputs["data:geometry:wing:tip:y"] l4_wing = inputs["data:geometry:wing:tip:chord"] sweep_0_wing = inputs["data:geometry:wing:sweep_0"] fa_length = inputs["data:geometry:wing:MAC:at25percent:x"] sref_wing = inputs['data:geometry:wing:area'] span_wing = inputs['data:geometry:wing:span'] height_max = inputs["data:geometry:fuselage:maximum_height"] if self.options["low_speed_aero"]: altitude = 0.0 atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:low_speed:mach"] else: altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:cruise:mach"] # Initial parameters calculation x_wing = fa_length - x0_wing - 0.25 * l0_wing z_wing = -(height_max - 0.12 * l2_wing) * 0.5 span2_wing = y4_wing - y2_wing viscosity = atm.kinematic_viscosity rho = atm.density v_inf = max(atm.speed_of_sound * mach, 0.1) # avoid V=0 m/s crashes reynolds = v_inf * l0_wing / viscosity # OPENVSP-SCRIPT: Geometry generation ###################################################### # I/O files -------------------------------------------------------------------------------- tmp_directory = self._create_tmp_directory() # avoid to dump void xternal_code_comp_error.out error file self.stderr = pth.join(tmp_directory.name, _STDERR_FILE_NAME) if self.options[OPTION_OPENVSP_EXE_PATH]: target_directory = pth.abspath(self.options[OPTION_OPENVSP_EXE_PATH]) else: target_directory = tmp_directory.name input_file_list = [pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME), pth.join(target_directory, self.options['wing_airfoil_file'])] tmp_result_file_path = pth.join(target_directory, _INPUT_AERO_FILE_NAME + '0.csv') output_file_list = [tmp_result_file_path] self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (populating temp directory and generate batch file) ----------------------- # Copy resource in temp directory if needed if not (self.options[OPTION_OPENVSP_EXE_PATH]): # noinspection PyTypeChecker copy_resource_folder(openvsp3201, target_directory) # noinspection PyTypeChecker copy_resource(resources, self.options['wing_airfoil_file'], target_directory) # Create corresponding .bat file self.options["command"] = [pth.join(target_directory, 'vspscript.bat')] command = pth.join(target_directory, VSPSCRIPT_EXE_NAME) + ' -script ' \ + pth.join(target_directory, _INPUT_SCRIPT_FILE_NAME) + ' >nul 2>nul\n' batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") batch_file.write(command) batch_file.close() # standard SCRIPT input file --------------------------------------------------------------- parser = InputFileGenerator() with path(local_resources, _INPUT_SCRIPT_FILE_NAME) as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[0]) parser.mark_anchor("x_wing") parser.transfer_var(float(x_wing), 0, 5) parser.mark_anchor("z_wing") parser.transfer_var(float(z_wing), 0, 5) parser.mark_anchor("y1_wing") parser.transfer_var(float(y1_wing), 0, 5) for i in range(3): parser.mark_anchor("l2_wing") parser.transfer_var(float(l2_wing), 0, 5) parser.reset_anchor() parser.mark_anchor("span2_wing") parser.transfer_var(float(span2_wing), 0, 5) parser.mark_anchor("l4_wing") parser.transfer_var(float(l4_wing), 0, 5) parser.mark_anchor("sweep_0_wing") parser.transfer_var(float(sweep_0_wing), 0, 5) parser.mark_anchor("airfoil_0_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_1_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("airfoil_2_file") parser.transfer_var(self._rewrite_path(input_file_list[1]), 0, 3) parser.mark_anchor("csv_file") parser.transfer_var(self._rewrite_path(tmp_result_file_path), 0, 3) parser.generate() # Run SCRIPT -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'OSWALD', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'OSWALD', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # OPENVSP-AERO: aero calculation ############################################################ # I/O files -------------------------------------------------------------------------------- # Duplicate .csv file for multiple run input_file_list = [tmp_result_file_path] for idx in range(len(_INPUT_AOAList) - 1): shutil.copy(tmp_result_file_path, pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) input_file_list.append(pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx + 1) + '.csv')) output_file_list = [] for idx in range(len(_INPUT_AOAList)): input_file_list.append(pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.vspaero') output_file_list.append(pth.join(target_directory, _INPUT_AERO_FILE_NAME) + str(idx) + '.polar') self.options["external_input_files"] = input_file_list self.options["external_output_files"] = output_file_list # Pre-processing (create batch file) ------------------------------------------------------- self.options["command"] = [pth.join(target_directory, 'vspaero.bat')] batch_file = open(self.options["command"][0], "w+") batch_file.write("@echo off\n") for idx in range(len(_INPUT_AOAList)): command = pth.join(target_directory, VSPAERO_EXE_NAME) + ' ' \ + pth.join(target_directory, _INPUT_AERO_FILE_NAME + str(idx) + ' >nul 2>nul\n') batch_file.write(command) batch_file.close() # standard AERO input file ----------------------------------------------------------------- parser = InputFileGenerator() for idx in range(len(_INPUT_AOAList)): with path(local_resources, _INPUT_AERO_FILE_NAME + '.vspaero') as input_template_path: parser.set_template_file(str(input_template_path)) parser.set_generated_file(input_file_list[len(_INPUT_AOAList) + idx]) parser.reset_anchor() parser.mark_anchor("Sref") parser.transfer_var(float(sref_wing), 0, 3) parser.mark_anchor("Cref") parser.transfer_var(float(l0_wing), 0, 3) parser.mark_anchor("Bref") parser.transfer_var(float(span_wing), 0, 3) parser.mark_anchor("X_cg") parser.transfer_var(float(fa_length), 0, 3) parser.mark_anchor("Mach") parser.transfer_var(float(mach), 0, 3) parser.mark_anchor("AOA") parser.transfer_var(float(_INPUT_AOAList[idx]), 0, 3) parser.mark_anchor("Vinf") parser.transfer_var(float(v_inf), 0, 3) parser.mark_anchor("Rho") parser.transfer_var(float(rho), 0, 3) parser.mark_anchor("ReCref") parser.transfer_var(float(reynolds), 0, 3) parser.generate() # Run AERO -------------------------------------------------------------------------------- super().compute(inputs, outputs) # Post-processing -------------------------------------------------------------------------- result_oswald = [] for idx in range(len(_INPUT_AOAList)): _, _, oswald, _ = self._read_polar_file(output_file_list[idx]) result_oswald.append(oswald) # Fuselage correction k_fus = 1 - 2 * (width_max / span_wing) ** 2 # Full aircraft correction: Wing lift is 105% of total lift. # This means CDind = (CL*1.05)^2/(piAe) -> e' = e/1.05^2 coef_e = float(result_oswald[0] * k_fus / 1.05 ** 2) coef_k = 1. / (math.pi * span_wing ** 2 / sref_wing * coef_e) if self.options["low_speed_aero"]: outputs["data:aerodynamics:aircraft:low_speed:induced_drag_coefficient"] = coef_k else: outputs["data:aerodynamics:aircraft:cruise:induced_drag_coefficient"] = coef_k # Getting input/output files if needed if self.options[OPTION_RESULT_FOLDER_PATH] != "": for file_path in input_file_list: new_path = pth.join(result_folder_path, 'OSWALD', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) for file_path in output_file_list: new_path = pth.join(result_folder_path, 'OSWALD', pth.split(file_path)[1]) if pth.exists(file_path): shutil.copyfile(file_path, new_path) # Delete temporary directory tmp_directory.cleanup()
def compute(self, inputs, outputs): eff = inputs['eff'] length = inputs['length'] alp = inputs['alp'] eps = inputs['eps'] GL107_200 = 1 / inputs['R_m'] GL111_300 = 1 / inputs['R_p'] GL103_400 = inputs['GlTether'] GL102_601 = 1 / inputs['R_s'] GL107_111 = inputs['ci1'] GL107_109 = inputs['ci2'] GL103_109 = inputs['ci3'] GL101_109 = inputs['ci4'] GL109_111 = inputs['ci5'] GL101_103 = inputs['ci6'] GL103_111 = inputs['ci7'] GL101_107 = inputs['ci8'] GL103_105 = inputs['ci9'] GL101_105 = inputs['ci10'] GL105_111 = inputs['ci11'] GL105_107 = inputs['ci12'] r_bat = inputs['r_bat'] ht_gain = inputs['ht_gain'] q_s = inputs['q_s'] eff = '{0};'.format(float(eff)) length = '{0};'.format(float(length)) ht_gain = '{0};'.format(float(ht_gain)) r_bat = '{0};'.format(float(r_bat)) q_s = '{0};'.format(float(q_s)) alp = '{0},'.format(float(alp)) eps = '{0},'.format(float(eps)) GL107_200 = '{0};'.format(float(GL107_200)) GL111_300 = '{0};'.format(float(GL111_300)) GL103_400 = '{0};'.format(float(GL103_400)) GL102_601 = '{0};'.format(float(GL102_601)) GL107_111 = '{0};'.format(float(GL107_111)) GL107_109 = '{0};'.format(float(GL107_109)) GL103_109 = '{0};'.format(float(GL103_109)) GL101_109 = '{0};'.format(float(GL101_109)) GL109_111 = '{0};'.format(float(GL109_111)) GL101_103 = '{0};'.format(float(GL101_103)) GL103_111 = '{0};'.format(float(GL103_111)) GL101_107 = '{0};'.format(float(GL101_107)) GL103_105 = '{0};'.format(float(GL103_105)) GL101_105 = '{0};'.format(float(GL101_105)) GL105_111 = '{0};'.format(float(GL105_111)) GL105_107 = '{0};'.format(float(GL105_107)) # generate the input file for RU_tm thermal analysis generator = InputFileGenerator() generator.set_template_file('./esatan/RU_template.txt') generator.set_generated_file('./esatan/RU_tm.d') generator.mark_anchor("$LOCALS") generator.transfer_var(length, 21, 3) generator.transfer_var(eff, 23, 3) generator.transfer_var(r_bat, 25, 3) generator.transfer_var(ht_gain, 27, 3) generator.transfer_var(q_s, 29, 3) generator.mark_anchor("$NODES") generator.transfer_var(alp, 53, 6) generator.transfer_var(eps, 53, 9) generator.transfer_var(alp, 61, 6) generator.transfer_var(eps, 61, 9) generator.transfer_var(alp, 69, 6) generator.transfer_var(eps, 69, 9) generator.transfer_var(alp, 77, 6) generator.transfer_var(eps, 77, 9) generator.transfer_var(alp, 85, 6) generator.transfer_var(eps, 85, 9) generator.transfer_var(alp, 93, 6) generator.transfer_var(eps, 93, 9) generator.mark_anchor("Generated conductors") generator.transfer_var(GL107_200, 3, 3) generator.transfer_var(GL111_300, 4, 3) generator.transfer_var(GL107_111, 5, 3) generator.transfer_var(GL107_109, 6, 3) generator.transfer_var(GL103_109, 7, 3) generator.transfer_var(GL101_109, 8, 3) generator.transfer_var(GL109_111, 9, 3) generator.transfer_var(GL101_103, 10, 3) generator.transfer_var(GL103_111, 11, 3) generator.transfer_var(GL101_107, 12, 3) generator.transfer_var(GL103_105, 13, 3) generator.transfer_var(GL101_105, 14, 3) generator.transfer_var(GL105_111, 15, 3) generator.transfer_var(GL105_107, 16, 3) generator.transfer_var(GL102_601, 17, 3) generator.transfer_var(GL103_400, 45, 3) generator.generate() # the parent compute function actually runs the external code super(RUextCodeComp, self).compute(inputs, outputs) # parse the output file from the external code and set the value of T_max parser = FileParser() parser.set_file('./esatan/RU_TM.out') parser.mark_anchor("+RU_TM") tBat = parser.transfer_var(7, 3) tMain = parser.transfer_var(34, 3) tProp = parser.transfer_var(35, 3) tTether = parser.transfer_var(36, 3) tBPanel = parser.transfer_var(41, 3) tDPanel = parser.transfer_var(43, 3) outputs['tBat'] = tBat outputs['tMain'] = tMain outputs['tProp'] = tProp outputs['tTether'] = tTether outputs['tBPanel'] = tBPanel outputs['tDPanel'] = tDPanel