Source code for ansys_optical_automation.post_process.dpf_rayfile

import math
import os
import struct

from ansys_optical_automation.post_process.dpf_base import DataProcessingFramework

Photopic_Conversion_wavelength = [
    380,
    390,
    400,
    410,
    420,
    430,
    440,
    450,
    460,
    470,
    480,
    490,
    500,
    507,
    510,
    520,
    530,
    540,
    550,
    555,
    560,
    570,
    580,
    590,
    600,
    610,
    620,
    630,
    640,
    650,
    660,
    670,
    680,
    690,
    700,
    710,
    720,
    730,
    740,
    750,
    760,
    770,
]
Photopic_Conversion_value = [
    0.027,
    0.082,
    0.27,
    0.826,
    2.732,
    7.923,
    15.709,
    25.954,
    40.98,
    62.139,
    94.951,
    142.078,
    220.609,
    303.464,
    303.464,
    484.93,
    588.746,
    651.582,
    679.551,
    683,
    679.585,
    650.216,
    594.21,
    517.031,
    430.973,
    343.549,
    260.223,
    180.995,
    119.525,
    73.081,
    41.663,
    21.856,
    11.611,
    5.607,
    2.802,
    1.428,
    0.715,
    0.355,
    0.17,
    0.082,
    0.041,
    0.02,
]


[docs]class DpfRay: """ this class defines the ray property """ def __init__(self, x, y, z, l_dir, m_dir, n_dir, wavelength, e): self.__cardinal_coordinate_x = x self.__cardinal_coordinate_y = y self.__cardinal_coordinate_Z = z self.__vector_radiation_l = l_dir self.__vector_radiation_m = m_dir self.__vector_radiation_n = n_dir self.__ray_wavelength = wavelength self.__ray_energy = e @property def coordinate_x(self) -> float: """ return ray cardinal_coordinate_x Returns: cardinal_coordinate_x ------- """ return self.__cardinal_coordinate_x @property def coordinate_y(self) -> float: """ return ray cardinal_coordinate_y Returns: cardinal_coordinate_y ------- """ return self.__cardinal_coordinate_y @property def coordinate_z(self) -> float: """ return ray cardinal_coordinate_z Returns: cardinal_coordinate_z ------- """ return self.__cardinal_coordinate_Z @property def radiation_l(self) -> float: """ return ray vector_radiation_l Returns: vector_radiation_l ------- """ return self.__vector_radiation_l @property def radiation_m(self) -> float: """ return ray vector_radiation_m Returns: vector_radiation_m ------- """ return self.__vector_radiation_m @property def radiation_n(self) -> float: """ return ray vector_radiation_n Returns: vector_radiation_n ------- """ return self.__vector_radiation_n @property def wavelength(self) -> float: """ return ray wavelength Returns: wavelength ------- """ return self.__ray_wavelength @property def energy(self) -> float: """ return ray energy Returns: energy ------- """ return self.__ray_energy
[docs]class DpfRayfile(DataProcessingFramework): """ this class contains method to read extract ray data from given binary rayfile """ conversion_extension = {".ray": ".sdf", ".dat": ".ray", ".sdf": ".ray"} def __init__(self, file_path): DataProcessingFramework.__init__(self, extension=list(self.conversion_extension.keys())) self.__ray_numb = 0 self.__watt_value = 0 self.__lumen_value = 0 self.__rays = [] self.identifier = 0 self.description = "description" self.source_flux = 0 if file_path is not None: self.open_file(file_path) self.__binary = self.__is_binary() self.load_content() def __is_binary(self): """this method checks if a file is binary Returns ------- Boolean """ with open(self.file_path, "rb") as f: for block in f: if b"\0" in block: f.close() return True f.close() return False def __photopic_conversion(self, wavelength): """This method computes photopic to Radiometric conversion factor at the given wavelength Parameters ---------- wavelength : int wavelength in nm """ start = 0 end = len(Photopic_Conversion_wavelength) pos = 0 while start < end: mid = (start + end) // 2 # print(mid, Photopic_Conversion_wavelength[mid]) if Photopic_Conversion_wavelength[mid] == wavelength: pos = mid break if Photopic_Conversion_wavelength[mid] > wavelength: end = mid else: start = mid + 1 pos = pos if pos != 0 else start start_wave = Photopic_Conversion_wavelength[pos] end_wave = Photopic_Conversion_wavelength[pos + 1] start_photopic = Photopic_Conversion_value[pos] end_photopic = Photopic_Conversion_value[pos + 1] return ((wavelength - start_wave) / (end_wave - start_wave)) * (end_photopic - start_photopic) + start_photopic
[docs] def set_ray_count(self, raynumber): """ redfine raynumber Parameters ---------- raynumber : int Returns ------- """ self.__ray_numb = raynumber
[docs] def load_content(self): """ this method load the information from rayfile provided Returns ------- """ rayfile_type = self.file_path.split(".")[-1] if rayfile_type == "ray": content_size = os.fstat(self.dpf_instance.fileno()).st_size - 28 if content_size % 32 != 0: msg = "Provided rayfile is not generated from speos" raise ValueError(msg) self.__ray_numb = int(content_size / 32) self.__watt_value = struct.unpack("f", self.dpf_instance.read(4))[0] self.dpf_instance.read(4 * 5) self.__lumen_value = struct.unpack("f", self.dpf_instance.read(4))[0] for ray_idx in range(self.__ray_numb): x = struct.unpack("f", self.dpf_instance.read(4))[0] y = struct.unpack("f", self.dpf_instance.read(4))[0] z = struct.unpack("f", self.dpf_instance.read(4))[0] l_dir = struct.unpack("f", self.dpf_instance.read(4))[0] m_dir = struct.unpack("f", self.dpf_instance.read(4))[0] n_dir = struct.unpack("f", self.dpf_instance.read(4))[0] wav = round(struct.unpack("f", self.dpf_instance.read(4))[0] * 0.001, 3) e = struct.unpack("f", self.dpf_instance.read(4))[0] if wav <= 0: msg = "Error: ray wavelength of ray of " + str(ray_idx) + "cannot be <= 0" raise ValueError(msg) raylen = math.sqrt(l_dir * l_dir + m_dir * m_dir + n_dir * n_dir) if abs(raylen - 1) > 1e-3: msg = "Error: Vector length of " + str(ray_idx) + "the ray is unusual (" + str(raylen) + ")" raise ValueError(msg) if e < 0: msg = "Error: ray power of " + str(m_dir) + "th ray is < 0" raise ValueError(msg) elif e == 0: print("The " + str(ray_idx) + " th ray has 0 flux! \n This Ray was removed from data") self.__ray_numb -= 1 else: self.__rays.append(DpfRay(x, y, z, l_dir, m_dir, n_dir, wav, e)) self.dpf_instance.close() elif (rayfile_type == "dat" or rayfile_type == "sdf") and self.__binary: self.identifier = int.from_bytes( self.dpf_instance.read(4), byteorder="little" ) # Format version ID, current value is 1010 self.__ray_numb = int.from_bytes( self.dpf_instance.read(4), byteorder="little" ) # The number of rays in the file self.description = self.dpf_instance.read(100).decode() # A text description of the source self.source_flux = struct.unpack("f", self.dpf_instance.read(4))[ 0 ] # The total flux in watts of this source ray_set_flux = struct.unpack("f", self.dpf_instance.read(4))[ 0 ] # The flux in watts represented by this Ray Set wavelength = round(struct.unpack("f", self.dpf_instance.read(4))[0], 3) # The wavelength in micrometers, # 0 if a composite,converted to nanometer since this is the speos' source file format. self.dpf_instance.read(18 * 4) # Unused data ray_format_type = int.from_bytes(self.dpf_instance.read(4), byteorder="little") # The ray_format_type must be either 0 for flux only format(.dat), or 2 for the spectral color format(.sdf). flux_type = int.from_bytes( self.dpf_instance.read(4), byteorder="little" ) # If and only if the ray_format_type is 0, then the flux_type is 0 for watts, and 1 for lumens. # For the spectral color format(.sdf), the flux must be in watts, and the wavelength in micrometers. self.dpf_instance.read(4 * 2) # Unused data content_size = os.fstat(self.dpf_instance.fileno()).st_size - self.dpf_instance.tell() if ray_format_type == 0: if content_size % (7 * 4) != 0 or content_size // (7 * 4) != self.__ray_numb: msg = "Warning: Zemax file may be wrong format. File size does not match ray numbers said in header" raise ValueError(msg) wavelength = wavelength if wavelength != 0 else 0.550 photopic_conversion = self.__photopic_conversion(wavelength * 1000) # float(input( # 'You can find a luminous efficacy table here: ' # 'http://hyperphysics.phy-astr.gsu.edu/hbase/vision/efficacy.html ' # 'Please enter the Photopic Conversion value for this wavelength, 683 for wavelength at 550nm: ')) if flux_type == 0: self.__watt_value = ray_set_flux self.__lumen_value = ray_set_flux * photopic_conversion elif flux_type == 1: self.__watt_value = ray_set_flux / photopic_conversion self.__lumen_value = ray_set_flux else: msg = "flux_type is in wrong format" raise TypeError(msg) elif ray_format_type == 2: if content_size % (8 * 4) != 0 or content_size // (8 * 4) != self.__ray_numb: msg = "Zemax file may be wrong format. File size does not match ray numbers said in header." raise TypeError(msg) self.__watt_value = ray_set_flux else: msg = "ray_format_type " + str(ray_format_type) + " is in wrong format" raise TypeError(msg) for ray_idx in range(self.__ray_numb): x = struct.unpack("f", self.dpf_instance.read(4))[0] y = struct.unpack("f", self.dpf_instance.read(4))[0] z = struct.unpack("f", self.dpf_instance.read(4))[0] l_dir = struct.unpack("f", self.dpf_instance.read(4))[0] m_dir = struct.unpack("f", self.dpf_instance.read(4))[0] n_dir = struct.unpack("f", self.dpf_instance.read(4))[0] wav = wavelength if wavelength != 0 else 550 e = struct.unpack("f", self.dpf_instance.read(4))[0] if ray_format_type == 2: wav = round(struct.unpack("f", self.dpf_instance.read(4))[0], 3) if wav <= 0: msg = "Error: ray wavelength cannot be <= 0" raise ValueError(msg) raylen = math.sqrt(l_dir * l_dir + m_dir * m_dir + n_dir * n_dir) if abs(raylen - 1) > 1e-3: msg = "Error: Vector length of " + str(m_dir) + "th ray is unusual (" + str(raylen) + ")" raise ValueError(msg) # Check ray energy: if e < 0: msg = "Error: ray power of " + str(m_dir) + "th ray is < 0" raise ValueError(msg) elif e == 0: print("The " + str(ray_idx) + " the ray has 0 flux! \n This Ray was removed from data") self.__ray_number -= 1 else: self.__rays.append(DpfRay(x, y, z, l_dir, m_dir, n_dir, wav, e)) self.dpf_instance.close() else: if not self.__binary: msg = "Non binary files not supported \n Filepath:" + self.file_path raise TypeError(msg) else: msg = ( "Provided file type is not supported \n Filepath: " + self.file_path + "\n" + "For Speos rayfile you can try to open the file with the RayfileEditor and save the file" ) raise TypeError(msg)
@property def radiometric_power(self) -> float: """ this method return the Radiometric Power value Returns: Radiometric Power value ------- """ return self.__watt_value @property def photometric_power(self) -> float: """ this method return Photometric Power value Returns: Photometric Power value ------- """ return self.__lumen_value @property def rays_number(self) -> int: """ this method return the number of rays Returns: number of rays in rayfile ------- """ return self.__ray_numb @property def rays(self) -> [DpfRay]: """ this method return a list of rays Returns: a list of rays ------- """ return self.__rays
[docs] def export_to_zemax(self): """ this method convert the rayfile into zemax format Returns ------- None """ outfile = self.export_file() zemax_spectrum_file = open(outfile, "wb") zemax_spectrum_file.write(struct.pack("<I", 1010)) # Identifier zemax_spectrum_file.write(struct.pack("<I", self.rays_number)) # NbrRays description = "Converted from SPEOS .ray file." # if len(description) > 100: # description = description[:100] # else: # description = description.ljust(100) description = description.ljust(100) zemax_spectrum_file.write(description.encode("ascii")) # Description zemax_spectrum_file.write(struct.pack("f", self.radiometric_power)) # SourceFlux = radiometric flux in SPOES zemax_spectrum_file.write(struct.pack("f", self.radiometric_power)) # RaySetFlux = radiometric flux in SPOES zemax_spectrum_file.write(struct.pack("f", 0)) # Wavelength zemax_spectrum_file.write(struct.pack("f", 0)) # InclinationBeg zemax_spectrum_file.write(struct.pack("f", 0)) # InclinationEnd zemax_spectrum_file.write(struct.pack("f", 0)) # AzimuthBeg zemax_spectrum_file.write(struct.pack("f", 0)) # AzimuthEnd zemax_spectrum_file.write(struct.pack("<I", 4)) # DimensionUnits: M=0, IN=1, CM=2, FT=3, MM=4 zemax_spectrum_file.write(struct.pack("f", 0)) # LocX zemax_spectrum_file.write(struct.pack("f", 0)) # LocY zemax_spectrum_file.write(struct.pack("f", 0)) # LocZ zemax_spectrum_file.write(struct.pack("f", 0)) # RotX zemax_spectrum_file.write(struct.pack("f", 0)) # RotY zemax_spectrum_file.write(struct.pack("f", 0)) # RotZ zemax_spectrum_file.write(struct.pack("f", 0)) # ScaleX zemax_spectrum_file.write(struct.pack("f", 0)) # ScaleY zemax_spectrum_file.write(struct.pack("f", 0)) # ScaleZ zemax_spectrum_file.write(struct.pack("f", 0)) # unused1 (float) zemax_spectrum_file.write(struct.pack("f", 0)) # unused2 (float) zemax_spectrum_file.write(struct.pack("f", 0)) # unused3 (float) zemax_spectrum_file.write(struct.pack("f", 0)) # unused4 (float) zemax_spectrum_file.write( struct.pack("<I", 2) ) # ray_format_type is color since the SPEOS ray file always contains wavelength zemax_spectrum_file.write(struct.pack("<I", 0)) # flux_type is Watts zemax_spectrum_file.write(struct.pack("<I", 0)) # reversed1 (int) zemax_spectrum_file.write(struct.pack("<I", 0)) # reversed2 (int) for ray in self.rays: zemax_spectrum_file.write(struct.pack("f", ray.coordinate_x)) # x zemax_spectrum_file.write(struct.pack("f", ray.coordinate_y)) # y zemax_spectrum_file.write(struct.pack("f", ray.coordinate_z)) # z zemax_spectrum_file.write(struct.pack("f", ray.radiation_l)) # l zemax_spectrum_file.write(struct.pack("f", ray.radiation_m)) # m zemax_spectrum_file.write(struct.pack("f", ray.radiation_n)) # n zemax_spectrum_file.write(struct.pack("f", ray.energy)) # flux (Watts) zemax_spectrum_file.write(struct.pack("f", ray.wavelength)) # wavelength zemax_spectrum_file.close()
[docs] def export_to_speos(self): """ this method convert the rayfile into speos format Returns ------- None """ outfile = self.export_file() speos_ray_file = open(outfile, "wb") speos_ray_file.write(struct.pack("f", self.radiometric_power)) speos_ray_file.write(struct.pack("f", 2.0)) speos_ray_file.write(struct.pack("f", 2.0)) speos_ray_file.write(struct.pack("f", 2.0)) speos_ray_file.write(struct.pack("f", 2.0)) speos_ray_file.write(struct.pack("f", 2.0)) speos_ray_file.write(struct.pack("f", self.photometric_power)) for ray in self.rays: speos_ray_file.write(struct.pack("f", ray.coordinate_x)) speos_ray_file.write(struct.pack("f", ray.coordinate_y)) speos_ray_file.write(struct.pack("f", ray.coordinate_z)) speos_ray_file.write(struct.pack("f", ray.radiation_l)) speos_ray_file.write(struct.pack("f", ray.radiation_m)) speos_ray_file.write(struct.pack("f", ray.radiation_n)) speos_ray_file.write(struct.pack("f", ray.wavelength * 1000)) speos_ray_file.write(struct.pack("f", ray.energy)) speos_ray_file.close()
[docs] def export_file(self, export_folder_dir=None, convert=False): """ this method generates a file to be exported Parameters ---------- export_folder_dir : str ,optional defines path where to export the rayfile convert : Boolean , optional defines if the export is a conversion, default value is False Returns ------- outfile: str output file """ input_file_folder = os.path.dirname(self.file_path) input_file_name = os.path.basename(self.file_path).split(".")[0] input_file_extension = os.path.splitext(self.file_path)[1].lower()[0:] output_file_path = "" if export_folder_dir is not None: self.valid_dir(export_folder_dir) output_file_path = os.path.join(export_folder_dir, input_file_name) else: output_file_path = os.path.join(input_file_folder, input_file_name) if convert: if input_file_extension in self.conversion_extension: exported_file_extension = self.conversion_extension[input_file_extension] else: exported_file_extension = input_file_extension msg = "Provided file extension " + exported_file_extension + " is not supported" raise TypeError(msg) else: if input_file_extension in self.conversion_extension: exported_file_extension = input_file_extension else: exported_file_extension = input_file_extension msg = "Provided file extension" + exported_file_extension + "is not supported" raise TypeError(msg) outfile = output_file_path + exported_file_extension outfile_num = 1 while os.path.isfile(outfile): outfile = output_file_path + "_" + str(outfile_num) + exported_file_extension outfile_num += 1 return outfile