def radio_action(self, file_name): """ On click function for radio button :param file_name: name of the fits file """ # Get pixels and header from a fits file self.pixels, _ = mylib.get_pixels("../data/%s" % (file_name)) # Process to the fit of the background _, _, _, self.background, self.dispersion = mylib.modelling_parameters(self.pixels) # Remove the background filtered_pixels = mylib.remove_background(self.pixels, self.background, self.dispersion) # Update the picture self.axis.cla() # delete the previous imshow self.axis.imshow(filtered_pixels) self.axis.set_title('Use of both slider and radio button') self.fig.canvas.draw() # redraw # Define the slider widget threshold_min = max(0, self.background - 6.0 * self.dispersion) threshold_max = self.background + 10.0*self.dispersion threshold_init = self.background + 6.0*self.dispersion if self.my_widget is not None: self.slider_axis.cla() self.my_widget = widgets.Slider(self.slider_axis, 'threshold', threshold_min, \ threshold_max, valinit=threshold_init)
def radio_action(self, file_name): """ On click function for radio button :param file_name: name of the fits file """ # Get pixels and header from a fits file self.pixels, _ = mylib.get_pixels("../data/%s" % (file_name)) # Process to the fit of the background _, _, _, self.background, self.dispersion = mylib.modelling_parameters( self.pixels) # Remove the background filtered_pixels = mylib.remove_background(self.pixels, self.background, self.dispersion) # Update the picture self.axis.cla() # delete the previous imshow self.axis.imshow(filtered_pixels) self.axis.set_title('Use of both slider and radio button') self.fig.canvas.draw() # redraw # Define the slider widget threshold_min = max(0, self.background - 6.0 * self.dispersion) threshold_max = self.background + 10.0 * self.dispersion threshold_init = self.background + 6.0 * self.dispersion if self.my_widget is not None: self.slider_axis.cla() self.my_widget = widgets.Slider(self.slider_axis, 'threshold', threshold_min, \ threshold_max, valinit=threshold_init)
def update(event): """ Action on slider change :param event: the event :return: """ # Find the clusters with the selected threshold cluster_list = mylib.find_clusters(header, pixels, background + thresh_slider.val) # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider if not cluster_list: # If max recursion depth reached, cluster_list is empty nb_clusters = "Error: max recursion reached" else: nb_clusters = "Number of clusters found : %i" % len(cluster_list) pads.set_title("Pixels above background + %d sigma. %s" \ % (thresh_slider.val / dispersion, nb_clusters)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow( mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw()
def update(event): """ Action on slider change :param event: the event :return: """ # Find the clusters with the selected threshold cluster_list = mylib.find_clusters(header, pixels, background + thresh_slider.val) # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider if not cluster_list: # If max recursion depth reached, cluster_list is empty nb_clusters = "Error: max recursion reached" else: nb_clusters = "Number of clusters found : %i" % len(cluster_list) pads.set_title("Pixels above background + %d sigma. %s" \ % (thresh_slider.val / dispersion, nb_clusters)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow(mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw()
def event_handler(header, pixels, background, dispersion): """ Event handler :param header: header of the FITS file opened :param pixels: The image to display :param background: mean value of the image background :param dispersion: dispersion of the background, fitted on the image :return: """ # plot zone fig, pads = plt.subplots() # Add margins to gain space for the slider plt.subplots_adjust(left=0.25, bottom=0.25) # Plot the image a first time with no threshold (0) pads.imshow(mylib.remove_background(pixels, background, 0)) # pad containing the slider : left, bot, width, height pad_slider = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg='white') # Default value for slider: 0 # Minimal value: 0 # Maximal value: max value in 'pixels' - background thresh_slider = widg.Slider(pad_slider, "Threshold", 0, np.max(pixels) - background, valinit=0) # Initialize the displayed text pads.set_title("Pixels above background + %d sigma" \ % (thresh_slider.val / dispersion)) def update(event): """ Action on slider change :param event: the event :return: """ # Find the clusters with the selected threshold cluster_list = mylib.find_clusters(header, pixels, background + thresh_slider.val) # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider if not cluster_list: # If max recursion depth reached, cluster_list is empty nb_clusters = "Error: max recursion reached" else: nb_clusters = "Number of clusters found : %i" % len(cluster_list) pads.set_title("Pixels above background + %d sigma. %s" \ % (thresh_slider.val / dispersion, nb_clusters)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow(mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw() thresh_slider.on_changed(update)
def main(): """ Take datas (2D numpy.array) from the fits file specific.fits and display the associated picture without background, the threshold can be ajusted with a widget, calculate the number of clusters """ # Get pixels and header from a fits file pixels, _ = mylib.get_pixels("../data/specific.fits") # Process to the fit of the background _, _, _, background, dispersion = mylib.modelling_parameters(pixels) # Remove the background filtered_pixels = mylib.remove_background(pixels, background, dispersion) # Display the picture without the background fig, axis = plt.subplots() plt.subplots_adjust(left=0.1, bottom=0.25) axis.imshow(filtered_pixels) axis.set_title('Picture without background') # Define the slider axis and the widget associated slider_axis = plt.axes([0.2, 0.1, 0.6, 0.105]) threshold_min = background threshold_max = background + 30.0 * dispersion threshold_init = background + 6.0 * dispersion my_widget = widgets.Slider(slider_axis, 'threshold', threshold_min, \ threshold_max, valinit=threshold_init) # slider_action function definition def slider_action(threshold): """ Function called when the threshold value is changed, the filtered_pixels array is recalculated and the canvas is redrawn """ axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(pixels, background, \ dispersion, threshold=threshold) axis.imshow(filtered_pixels) try: # if too many recursion, say that threshold is too low cluster_array = mylib.get_cluster_array(pixels, background, dispersion, threshold) axis.set_title('Picture without background : number of clusters = %s' % \ (len(cluster_array))) except RuntimeError: axis.set_title('Picture without background : threshold is too low') fig.canvas.draw() # redraw # Use event handler my_widget.on_changed(slider_action) plt.show() return 0
def main(): """ We read a FITS file, find the clusters, and convert their centroid coordinates to WCS coordinates. Then we display the WCS coordinates on the image, following the mouse movement. """ input_file_path = "/Users/npac09/PycharmProjects/npac09/data/specific.fits" output_file_path = "/Users/npac09/PycharmProjects/npac09/src/ex4.txt" # open file and retrieve data and header header, pixels = mylib.open_fits(input_file_path) # creation of the histogram from the data bin_number = 200 bin_values, bin_boundaries = np.histogram(pixels.ravel(), bin_number) # apply the fit (no need for the amplitude (first parameter)) _, background, dispersion = mylib.gaussian_fit(bin_boundaries[:-1], bin_values) # We define the threshold at 6 standard deviations above the mean bkg value threshold = background + (6.0 * dispersion) # plot fig, pads = plt.subplots() # visualization of the image after bkg removal pads.imshow(mylib.remove_background(pixels, background, threshold)) # find the clusters. cluster_list = mylib.find_clusters(header, pixels, threshold) cluster_dico = mylib.build_cluster_dico(cluster_list) # find the maximum-integral cluster max_integral_key = mylib.find_max_integral_cluster(cluster_list) # call the event handler event_handler(fig, header, pixels) # display plt.show() # write result to output file try: with open(output_file_path, 'w') as output_file: output_file.write('right ascension: %.3f, declination: %.3f' \ % (cluster_dico[max_integral_key][0].centroid_wcs)) except IOError: print "File not found :", output_file_path return 2 return 0
def slider_action(self, threshold): """ On changed function for slider :param threshold: value of the threshold """ self.axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(self.pixels, self.background, \ self.dispersion, threshold=threshold) self.axis.imshow(filtered_pixels) self.axis.set_title('Use of both slider and radio button') self.fig.canvas.draw() # redraw
def main(): """ Take datas (2D numpy.array) from the fits file specific.fits, find the clusters array, find there WCS coordinates, display it """ # Get pixels and header from a fits file pixels, header = mylib.get_pixels("../data/specific.fits") # Process to the fit of the background _, _, _, background, dispersion = mylib.modelling_parameters(pixels) # Get the list of clusters my_wcs = library.WCS(header) # Instantiate WCS conversion object cluster_array = mylib.get_cluster_array(pixels, background, \ dispersion, my_wcs=my_wcs) # Remove the background filtered_pixels = mylib.remove_background(pixels, background, dispersion) # Display the picture fig, axis = plt.subplots() axis.imshow(filtered_pixels) # Move function definition def move(event): """ Function called when the event 'motion_notify_event' occurs, the wcs coordinates of the pixel pointed by the mouse are then displayed at its location """ x_position = event.xdata y_position = event.ydata if x_position != None: # if we are not outside the picture rad, dec = my_wcs.convert_to_radec(x_position, y_position) text = axis.text(x_position, y_position, 'ra: %.6f, dec: %.6f' % \ (rad, dec), fontsize=14, color='white') event.canvas.draw() text.remove() # Use event handler fig.canvas.mpl_connect('motion_notify_event', move) plt.show() # Find the cluster with the greatest integral main_clust = mylib.find_main_centroid(cluster_array) # Write informations about WCS coordinates on ex4.txt file results = 'right ascension: %.3f, declination: %.3f' % \ (main_clust.centroid_wcs[0], main_clust.centroid_wcs[1]) with open("ex4.txt", 'w') as output_file: output_file.write(results) return 0
def slider_action(self, threshold): """ On changed function for slider :param threshold: value of the threshold """ self.axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(self.pixels, self.background, \ self.dispersion, threshold=threshold) self.axis.imshow(filtered_pixels) self.axis.set_title('Use of both slider and radio button') self.fig.canvas.draw() # redraw
def main(): """ Take datas (2D numpy.array) from the fits file specific.fits, find the clusters array, find there WCS coordinates, display it """ # Get pixels and header from a fits file pixels, header = mylib.get_pixels("../data/specific.fits") # Process to the fit of the background _, _, _, background, dispersion = mylib.modelling_parameters(pixels) # Get the list of clusters my_wcs = library.WCS(header) # Instantiate WCS conversion object cluster_array = mylib.get_cluster_array(pixels, background, \ dispersion, my_wcs=my_wcs) # Remove the background filtered_pixels = mylib.remove_background(pixels, background, dispersion) # Display the picture fig, axis = plt.subplots() axis.imshow(filtered_pixels) # Move function definition def move(event): """ Function called when the event 'motion_notify_event' occurs, the wcs coordinates of the pixel pointed by the mouse are then displayed at its location """ x_position = event.xdata y_position = event.ydata if x_position != None: # if we are not outside the picture rad, dec = my_wcs.convert_to_radec(x_position, y_position) text = axis.text(x_position, y_position, 'ra: %.6f, dec: %.6f' % \ (rad, dec), fontsize=14, color='white') event.canvas.draw() text.remove() # Use event handler fig.canvas.mpl_connect('motion_notify_event', move) plt.show() # Find the cluster with the greatest integral main_clust = mylib.find_main_centroid(cluster_array) # Write informations about WCS coordinates on ex4.txt file results = 'right ascension: %.3f, declination: %.3f' % \ (main_clust.centroid_wcs[0], main_clust.centroid_wcs[1]) with open("ex4.txt", 'w') as output_file: output_file.write(results) return 0
def main(): """ We read a FITS file, find the clusters, and convert their centroid coordinates to WCS coordinates. Then we display the WCS coordinates on the image, following the mouse movement. """ input_file_path = "/Users/npac09/PycharmProjects/npac09/data/specific.fits" output_file_path = "/Users/npac09/PycharmProjects/npac09/src/ex4.txt" # open file and retrieve data and header header, pixels = mylib.open_fits(input_file_path) # creation of the histogram from the data bin_number = 200 bin_values, bin_boundaries = np.histogram(pixels.ravel(), bin_number) # apply the fit (no need for the amplitude (first parameter)) _, background, dispersion = mylib.gaussian_fit(bin_boundaries[:-1], bin_values) # We define the threshold at 6 standard deviations above the mean bkg value threshold = background + (6.0 * dispersion) # plot fig, pads = plt.subplots() # visualization of the image after bkg removal pads.imshow(mylib.remove_background(pixels, background, threshold)) # find the clusters. cluster_list = mylib.find_clusters(header, pixels, threshold) cluster_dico = mylib.build_cluster_dico(cluster_list) # find the maximum-integral cluster max_integral_key = mylib.find_max_integral_cluster(cluster_list) # call the event handler event_handler(fig, header, pixels) # display plt.show() # write result to output file try: with open(output_file_path, 'w') as output_file: output_file.write('right ascension: %.3f, declination: %.3f' \ % (cluster_dico[max_integral_key][0].centroid_wcs)) except IOError: print "File not found :", output_file_path return 2 return 0
def event_handler(pixels, background, dispersion): """ Event handler :param pixels: The image to display :param background: mean value of the image background :param dispersion: dispersion of the background, fitted on the image :return: """ # plot zone fig, pads = plt.subplots() # Add margins to gain space for the slider plt.subplots_adjust(left=0.25, bottom=0.25) # Plot the image a first time with no threshold (0) pads.imshow(mylib.remove_background(pixels, background, 0)) # pad containing the slider : left, bot, width, height pad_slider = plt.axes([0.25, 0.1, 0.65, 0.03], axisbg='white') # Default value for slider: 0 # Minimal value: 0 # Maximal value: max value in 'pixels' - background thresh_slider = widg.Slider(pad_slider, "Threshold", 0, np.max(pixels) - background, valinit=0) # Initialize the displayed text pads.set_title("pixels above background + %d sigma" \ % (thresh_slider.val / dispersion)) def update(event): """ Action on slider change :param event: the event :return: """ # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider pads.set_title("pixels above background + %d sigma" \ % (thresh_slider.val / dispersion)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow( mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw() thresh_slider.on_changed(update)
def slider_action(threshold): """ Function called when the threshold value is changed, the filtered_pixels array is recalculated and the canvas is redrawn """ axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(pixels, background, \ dispersion, threshold=threshold) axis.imshow(filtered_pixels) axis.set_title('Picture without background : threshold = %s' % \ (threshold)) fig.canvas.draw() # redraw
def slider_action(threshold): """ Function called when the threshold value is changed, the filtered_pixels array is recalculated and the canvas is redrawn """ axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(pixels, background, \ dispersion, threshold=threshold) axis.imshow(filtered_pixels) axis.set_title('Picture without background : threshold = %s' % \ (threshold)) fig.canvas.draw() # redraw
def main(): """ Reading a FITS file and determining the background parameters. """ input_file_path = "/Users/npac09/PycharmProjects/npac09/data/specific.fits" output_file_path = "/Users/npac09/PycharmProjects/npac09/src/ex2.txt" # open file and retrieve data _, pixels = mylib.open_fits(input_file_path) # creation of the histogram from the data bin_number = 200 bin_values, bin_boundaries = np.histogram(pixels.ravel(), bin_number) bin_lower_boundaries = bin_boundaries[:-1] # apply the fit maxvalue, background, dispersion = mylib.gaussian_fit( bin_lower_boundaries, bin_values) threshold = 6.0 * dispersion # visualization of the histogram and the fit _, pads = plt.subplots(1, 3) # 0: image before bkg removal; # 1: image after bkg removal; 2: histogram and fit pads[2].plot(bin_lower_boundaries, bin_values, 'b+:', label='data') pads[2].plot(bin_lower_boundaries, \ mylib.gaussian(bin_lower_boundaries, maxvalue, background, dispersion), \ 'r.:', label='fit') pads[2].legend() pads[2].set_title('Flux distribution') pads[2].set_xlabel('Amplitude') pads[2].set_ylabel('Frequency') # visualization of the image before and after bkg removal pads[0].imshow(pixels) pads[1].imshow(mylib.remove_background(pixels, background, threshold)) plt.show() # write result to output file try: with open(output_file_path, 'w') as output_file: output_file.write('background: %d, dispersion: %d' % (int(background), int(dispersion))) except IOError: print "File not found :", output_file_path return 2 return 0
def main(): """ Reading a FITS file and determining the background parameters. """ input_file_path = "/Users/npac09/PycharmProjects/npac09/data/specific.fits" output_file_path = "/Users/npac09/PycharmProjects/npac09/src/ex2.txt" # open file and retrieve data _, pixels = mylib.open_fits(input_file_path) # creation of the histogram from the data bin_number = 200 bin_values, bin_boundaries = np.histogram(pixels.ravel(), bin_number) bin_lower_boundaries = bin_boundaries[:-1] # apply the fit maxvalue, background, dispersion = mylib.gaussian_fit(bin_lower_boundaries, bin_values) threshold = 6.0 * dispersion # visualization of the histogram and the fit _, pads = plt.subplots(1, 3) # 0: image before bkg removal; # 1: image after bkg removal; 2: histogram and fit pads[2].plot(bin_lower_boundaries, bin_values, 'b+:', label='data') pads[2].plot(bin_lower_boundaries, \ mylib.gaussian(bin_lower_boundaries, maxvalue, background, dispersion), \ 'r.:', label='fit') pads[2].legend() pads[2].set_title('Flux distribution') pads[2].set_xlabel('Amplitude') pads[2].set_ylabel('Frequency') # visualization of the image before and after bkg removal pads[0].imshow(pixels) pads[1].imshow(mylib.remove_background(pixels, background, threshold)) plt.show() # write result to output file try: with open(output_file_path, 'w') as output_file: output_file.write('background: %d, dispersion: %d' % (int(background), int(dispersion))) except IOError: print "File not found :", output_file_path return 2 return 0
def slider_action(threshold): """ Function called when the threshold value is changed, the filtered_pixels array is recalculated and the canvas is redrawn """ axis.cla() # delete the previous imshow filtered_pixels = mylib.remove_background(pixels, background, \ dispersion, threshold=threshold) axis.imshow(filtered_pixels) try: # if too many recursion, say that threshold is too low cluster_array = mylib.get_cluster_array(pixels, background, dispersion, threshold) axis.set_title('Picture without background : number of clusters = %s' % \ (len(cluster_array))) except RuntimeError: axis.set_title('Picture without background : threshold is too low') fig.canvas.draw() # redraw
def update(event): """ Action on slider change :param event: the event :return: """ # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider pads.set_title("pixels above background + %d sigma" \ % (thresh_slider.val / dispersion)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow( mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw()
def update(event): """ Action on slider change :param event: the event :return: """ # clear the previous image pads.cla() # Display the number of sigmas above threshold selected with the slider pads.set_title("pixels above background + %d sigma" \ % (thresh_slider.val / dispersion)) # Display the image with selected background removed # (we remove 'thresh_slider.val', with a threshold = 'thresh_slider.val - background' pads.imshow(mylib.remove_background(pixels, background, thresh_slider.val)) # Draw the image fig.canvas.draw()
def main(): """ Take datas (2D numpy.array) from the fits file specific.fits and display the associated picture without background """ # Get pixels and header from a fits file pixels, _ = mylib.get_pixels("../data/specific.fits") # Process to the fit of the background x_array, y_array, maxvalue, background, dispersion = \ mylib.modelling_parameters(pixels) # Remove the background filtered_pixels = mylib.remove_background(pixels, background, dispersion) # Plot both histograms and picture without background _, axis = plt.subplots(1, 2) # Plot histogram of data luminosity and result of the fit axis[0].plot(x_array, y_array, 'b+:', label='data') axis[0].plot(x_array, mylib.modelling_function( \ x_array, maxvalue, background, dispersion), \ 'r.:', label='fit') axis[0].legend() axis[0].set_title('Flux distribution') axis[0].set_xlabel('Amplitude') axis[0].set_ylabel('Frequency') # Display the picture without the background axis[1].imshow(filtered_pixels) axis[1].set_title('Picture without background') plt.show() # Write informations about background and dispersion on ex2.txt file results = 'background: %i, dispersion: %i' % (background, dispersion) with open("ex2.txt", 'w') as output_file: output_file.write(results) return 0
def main(): """ We read a FITS file, find the clusters, and convert their centroid coordinates to WCS coordinates. Then we display the celestial objects names on the image, at click on the corresponding object on the image. """ input_file_path = "/Users/npac09/PycharmProjects/npac09/data/specific.fits" output_file_path = "/Users/npac09/PycharmProjects/npac09/src/ex5.txt" # open file and retrieve data and header header, pixels = mylib.open_fits(input_file_path) # creation of the histogram from the data bin_number = 200 bin_values, bin_boundaries = np.histogram(pixels.ravel(), bin_number) # apply the fit (no need for the amplitude (first parameter)) _, background, dispersion = mylib.gaussian_fit(bin_boundaries[:-1], bin_values) # We define the threshold at 6 standard deviations above the mean bkg value threshold = background + (6.0 * dispersion) # define an accetance radius around a given position to get the name from Simbad radius = 0.003 cluster_list = mylib.find_clusters(header, pixels, threshold) cluster_dico = mylib.build_cluster_dico(cluster_list, radius) # plot fig, pads = plt.subplots() # Display the image without background pads.imshow(mylib.remove_background(pixels, background, threshold)) # Display the boxes around clusters for cluster in cluster_list: pads.add_patch( patches.Rectangle((cluster.box_xmin, cluster.box_ymin), cluster.box_xmax - cluster.box_xmin, cluster.box_ymax - cluster.box_ymin, fill=False, color='white')) # find the maximum-integral cluster max_integral_key = mylib.find_max_integral_cluster(cluster_list) # call the event handler event_handler(fig, header, pixels, cluster_list, cluster_dico) plt.show() # write result to output file try: with open(output_file_path, 'w') as output_file: output_file.write('celestial object: %s' % cluster_dico[max_integral_key][1]) except IOError: print "File not found :", output_file_path return 2 return 0
def main(): """ Take datas (2D numpy.array) from the fits file specific.fits, find the clusters array, find there names, display it """ # Get pixels and header from a fits file pixels, header = mylib.get_pixels("../data/specific.fits") # Process to the fit of the background _, _, _, background, dispersion = mylib.modelling_parameters(pixels) # Get the list of clusters my_wcs = library.WCS(header) # Instantiate WCS conversion object cluster_array = mylib.get_cluster_array(pixels, background, \ dispersion, my_wcs=my_wcs) # Remove the background filtered_pixels = mylib.remove_background(pixels, background, dispersion) # Display the picture fig, axis = plt.subplots() axis.imshow(filtered_pixels) # Put bounding boxes for cluster in cluster_array: axis.add_patch(patches.Rectangle(cluster.bounding_box[0], \ cluster.bounding_box[1], \ cluster.bounding_box[2], \ fill=False, \ color='white')) # On_click function definition def on_click(event): """ Function called when the event 'button_release_event' occurs, if the click was made on a bounding box, the name of the corresponding celestial object is displayed """ x_position = event.xdata y_position = event.ydata if x_position != None: # if we are not outside the picture for clust in cluster_array: if clust.is_in_bounding(x_position, y_position): text = axis.text(x_position, y_position, clust.star_name, \ fontsize=14, color='white') event.canvas.draw() text.remove() # Use event handler fig.canvas.mpl_connect('button_release_event', on_click) plt.show() # Find the cluster with the greatest integral main_clust = mylib.find_main_centroid(cluster_array) # Write the name of the main celestial object on ex5.txt file results = 'celestial object: %s' % (main_clust.star_name) with open("ex5.txt", 'w') as output_file: output_file.write(results) return 0