Пример #1
0
    def __init__(self, file_io, model, gas, dust, source,
                 bg_source, dust_source, detector, server, parse_args):
        """Initialisation of the command options.

        Args:
            file_io: Handles file input/output and all
                necessary paths.
            model: Handles the model space including various
                quantities such as the density distribution.
            gas: Handles the gas species with its parameters.
            dust: Handles the dust composition with its parameters.
            source: Handles the radiation of a radiation source
                such as stars or interstellar radiation fields.
            bg_source: Handles the radiation of a background source.
            dust_source: Handles the radiation of the dust grains.
            detector: Handles the position and wavelength
                at which a detector observes
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """

        self.file_io = file_io
        self.model = model
        self.gas = gas
        self.dust = dust
        self.source = source
        self.bg_source = bg_source
        self.dust_source = dust_source
        self.detector = detector
        self.server = server
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()
Пример #2
0
    def __init__(self, basic_plot_routines, model, file_io, parse_args):
        """Initialisation of plot parameters.

        Args:
            model: Handles the model space including various
                quantities such as the density distribution.
            file_io: Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """
        self.basic_plots = basic_plot_routines
        self.model = model
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()

        # Stokes parameter names dependent on an index
        self.stokes_parameter = {
            0: 'I',
            1: 'Q',
            2: 'U',
            3: 'V',
        }
Пример #3
0
    def __init__(self, model, file_io, parse_args):
        """Initialisation of all usable options.

        Notes:
            To create your own stellar source, add its name to the dictionary
            and write a class with its options as a derived class of class StellarSource.

        Args:
            model: Handles the model space including various
                quantities such as the density distribution.
            file_io : Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """
        self.model = model
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        self.math = Math()

        #: dict: Dictionary with all usable stellar sources
        self.sources_dict = {
            'isrf': ISRF,
            'mathis_isrf': MathisISRF,
            't_tauri': TTauri,
            'herbig_ae': HerbigAe,
            'binary': BinaryStar,
            'sun': Sun,
            'laser': Laser,
        }
        update_sources_dict(self.sources_dict)
Пример #4
0
    def __init__(self, file_io, parse_args):
        """Initialisation of the dust parameters.

        Args:
            file_io : Handles file input/output and all
                necessary paths.
        """
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()
Пример #5
0
class CustomPlots:
    """This class contains modified plot routines for
    special purposes and images.
    """
    def __init__(self, basic_plot_routines, model, file_io, parse_args):
        """Initialisation of plot parameters.

        Args:
            model: Handles the model space including various
                quantities such as the density distribution.
            file_io: Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """
        self.basic_plots = basic_plot_routines
        self.model = model
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()

        # Stokes parameter names dependent on an index
        self.stokes_parameter = {
            0: 'I',
            1: 'Q',
            2: 'U',
            3: 'V',
        }

    def plot_1(self):
        """Plot line spectrum from POLARIS simulations.
        """
        # Set data input to Jy to calculate the total flux
        self.file_io.cmap_unit = 'total'
        # Read spectrum data
        plot_data, header = self.file_io.read_spectrum(
            'line_spectrum_species_0001_line_0001')
        # Create pdf file if show_plot is not chosen
        self.file_io.init_plot_output('line_spectrum_species_0001_line_0001')
        velocity = []
        for vch in range(header['nr_channels']):
            # Get velocity of current channel
            velocity.append(1e-3 * self.math.get_velocity(
                vch, header['nr_channels'], header['max_velocity']))
        for i_quantity in range(4):
            # Create Matplotlib figure
            plot = Plot(self.model,
                        self.parse_args,
                        xlabel=r'$\mathit{v}\ [\si{\kilo\metre\per\second}]$',
                        ylabel=self.file_io.get_quantity_labels(i_quantity),
                        extent=[velocity[0], velocity[-1], None, None],
                        with_cbar=False)
            # Plot spectrum as line
            plot.plot_line(velocity, plot_data[i_quantity, :], marker='.')
            # Save figure to pdf file or print it on screen
            plot.save_figure(self.file_io)
Пример #6
0
    def __init__(self, file_io, parse_args, model=None):
        """Initialisation of all usable options.

        Notes:
            To create your own dust composition, add its name to the dictionary
            and write a class with its options as a derived class of class Dust.

        Args:
            file_io : Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
            model: Handles the model space including various
                quantities such as the density distribution.
        """
        self.file_io = file_io
        self.parse_args = parse_args
        self.model = model

        # Get math module
        self.math = Math()

        #: dict: Dictionary with all usable dust compositions
        self.dust_dict = {
            'none': NoDust,
            'graphite_oblate': GraphiteOblate,
            'graphite_perpend': GraphitePerpend,
            'graphite_parallel': GraphiteParallel,
            'silicate_oblate': SilicateOblate,
            'silicate': Silicate,
            'mrn_oblate': MrnOblate,
            'mrn': Mrn,
            'CM20': CM20,
            'aPyM5': aPyM5,
            'aOlM5': aOlM5,
            'themis': Themis,
            'pah_neutral': PAH0,
            'pah_ion': PAH1,
        }
        update_dust_dict(self.dust_dict)
Пример #7
0
    def __init__(self, parse_args):
        """Initialisation of all usable options.

        Notes:
            To create your own model, add its name to the dictionary
            and write a class with its options as a derived class of class Model.

        Args:
            parse_args (ArgumentParser) : Provides all parameters chosen
            by user when executing PolarisTools.
        """
        self.parse_args = parse_args

        # Get math module
        self.math = Math()

        # dict: Dictionary with all usable models
        self.model_dict = {
            'default': Model,
            'disk': Disk,
            'sphere': Sphere,
            'globule': BokGlobule,
        }
        update_model_dict(self.model_dict)
Пример #8
0
    def __init__(self, file_io, parse_args, model=None):
        """Initialisation of all usable options.

        Notes:
            To create your own gas species, add its name to the dictionary
            and write a class with its options as a derived class of class Gas.

        Args:
            model: Handles the model space including various
                quantities such as the density distribution.
            file_io : Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """
        self.file_io = file_io
        self.parse_args = parse_args
        self.model = model

        # Get math module
        self.math = Math()

        #: dict[str: int]: Index of different level population  methods
        self.lvl_pop = {
            'LTE': 1,
            'FEP': 2,
            'LVG': 3,
        }

        #: dict: Dictionary with all usable gas species
        self.gas_dict = {
            'none': NoGas,
            'CO': GasCO,
            '13CO': Gas13CO,
            'C18O': GasC18O,
            'HCO': GasHCO,
            'SO': GasSO,
            'OH': GasOH,
            'CN': GasCN,
            'H1': GasH1,
        }
        update_gas_dict(self.gas_dict)
Пример #9
0
    def __init__(self, file_io, parse_args, parameter):
        """Initialisation of the dust creator parameters.

        Args:
            file_io : Handles file input/output and all
                necessary paths.
            parameter (dict): Dictionary with parameter to create dust grain catalog.
        """
        self.file_io = file_io
        self.parse_args = parse_args
        self.parameter = parameter

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()

        # Elements to create scattering matrix
        self.elements = np.zeros(16)
        self.elements[0] = 1  # S11
        self.elements[1] = 2  # S12
        self.elements[2] = 0  # S13
        self.elements[3] = 0  # S14
        self.elements[4] = 2  # S21
        self.elements[5] = 1  # S22
        self.elements[6] = 0  # S23
        self.elements[7] = 0  # S24
        self.elements[8] = 0  # S31
        self.elements[9] = 0  # S32
        self.elements[10] = 3  # S33
        self.elements[11] = 4  # S34
        self.elements[12] = 0  # S41
        self.elements[13] = 0  # S42
        self.elements[14] = -4  # S43
        self.elements[15] = 3  # S44

        self.nr_matrix_elements = 4
Пример #10
0
class CmdPolaris:
    """This class creates ".cmd" files which can be used to execute POLARIS.
    """

    def __init__(self, file_io, model, gas, dust, source,
                 bg_source, dust_source, detector, server, parse_args):
        """Initialisation of the command options.

        Args:
            file_io: Handles file input/output and all
                necessary paths.
            model: Handles the model space including various
                quantities such as the density distribution.
            gas: Handles the gas species with its parameters.
            dust: Handles the dust composition with its parameters.
            source: Handles the radiation of a radiation source
                such as stars or interstellar radiation fields.
            bg_source: Handles the radiation of a background source.
            dust_source: Handles the radiation of the dust grains.
            detector: Handles the position and wavelength
                at which a detector observes
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """

        self.file_io = file_io
        self.model = model
        self.gas = gas
        self.dust = dust
        self.source = source
        self.bg_source = bg_source
        self.dust_source = dust_source
        self.detector = detector
        self.server = server
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()

    def write_common_part(self, cmd_file):
        """Writes commands into the command file.

        Args:
            cmd_file: Instance of the command file.
        """

        # Overwrite default values with user input
        if self.parse_args.mass_fraction is not None:
            mass_fraction = self.parse_args.mass_fraction
        else:
            mass_fraction = self.model.parameter['mass_fraction']
        if self.parse_args.scattering is not None:
            scattering = self.parse_args.scattering
        else:
            scattering = self.dust.parameter['scattering']
        if self.parse_args.midplane_points is not None:
            midplane_points = self.parse_args.midplane_points
        else:
            midplane_points = self.model.parameter['midplane_points']
        if self.parse_args.midplane_zoom is not None:
            midplane_zoom = self.parse_args.midplane_zoom
        else:
            midplane_zoom = self.model.parameter['midplane_zoom']
        if self.parse_args.mu is not None:
            mu = self.parse_args.mu
        else:
            mu = self.math.const['avg_gas_mass']
        if self.parse_args.nr_threads is not None:
            nr_threads = self.parse_args.nr_threads
        else:
            nr_threads = -1

        cmd_file.write('<common>\n')
        cmd_file.write(self.dust.get_command())
        cmd_file.write('\n')
        if midplane_points is not None:
            cmd_file.write('\t<write_inp_midplanes>\t' +
                        str(midplane_points) + '\n')
            cmd_file.write('\t<write_out_midplanes>\t' +
                        str(midplane_points) + '\n')
        if self.parse_args.midplane_3d_param is not None:
            if 0 <= len(self.parse_args.midplane_3d_param) <= 4 and \
                    1 <= int(self.parse_args.midplane_3d_param[0]) <= 3:
                cmd_file.write('\t<write_3d_midplanes>')
                for i in range(len(self.parse_args.midplane_3d_param)):
                    if 1 < i < 4:
                        cmd_file.write(
                            '\t' + str(self.math.parse(self.parse_args.midplane_3d_param[i], 'length')))
                    else:
                        cmd_file.write(
                            '\t' + str(self.parse_args.midplane_3d_param[i]))
                cmd_file.write('\n')
        if midplane_zoom is not None:
            cmd_file.write('\t<midplane_zoom>\t\t' + str(midplane_zoom) + '\n')
        if self.parse_args.midplane_rad_field:
            cmd_file.write('\t<write_radiation_field>\t1\n')
        cmd_file.write('\n')
        cmd_file.write('\t<mass_fraction>\t\t' + str(mass_fraction) + '\n')
        cmd_file.write('\t<mu>\t\t\t' + str(mu) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<phase_function>\tPH_' + str(scattering) + '\n')
        cmd_file.write('\n')
        if self.parse_args.healpix_orientation is not None:
            cmd_file.write('\t<healpix_orientation>\t' + str(self.parse_args.healpix_orientation) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<nr_threads>\t\t' + str(nr_threads) + '\n')
        cmd_file.write('</common>\n')
        cmd_file.write('\n')

    def write_temp_part(self, cmd_file, add_rat=False):
        """Writes commands for temperature calculation to the command file.

        Args:
            add_rat (bool): Also calculate radiative torques?
        """

        # Overwrite default values with user input
        conv_dens, conv_len, conv_mag, conv_vel = self.get_conv_factors()

        cmd_file.write('<task> 1\n')
        if self.source is not None and self.source.parameter['nr_photons'] > 0:
            cmd_file.write(self.source.get_command())
        cmd_file.write('\n')
        if add_rat:
            cmd_file.write('\t<cmd>\t\t\tCMD_TEMP_RAT\n')
        else:
            cmd_file.write('\t<cmd>\t\t\tCMD_TEMP\n')
        cmd_file.write('\n')
        if self.parse_args.grid_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_filename
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif self.parse_args.grid_cgs_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_cgs_filename
            cmd_file.write('\t<path_grid_cgs>\t\t"' + grid_path + '"\n')
        else:
            raise ValueError('No input grid defined/found!')
        cmd_file.write('\t<path_out>\t\t"' +
                       self.file_io.path['simulation_type'] + '"\n')
        cmd_file.write('\n')
        cmd_file.write('\t<dust_offset>\t\t' +
                       str(int(self.parse_args.dust_offset)) + '\n')
        cmd_file.write('\n')
        if self.parse_args.adj_tgas is not None:
            cmd_file.write('\t<adj_tgas>\t\t' +
                           str(self.parse_args.adj_tgas) + '\n')
        if self.parse_args.sub_dust:
            cmd_file.write('\t<sub_dust>\t1\n')
        if self.parse_args.full_dust_temp:
            cmd_file.write('\t<full_dust_temp>\t1\n')
        if self.parse_args.radiation_field:
            cmd_file.write('\t<radiation_field>\t1\n')
        if self.parse_args.temp_a_max is not None:
            cmd_file.write('\t<stochastic_heating>\t' +
                           str(self.parse_args.temp_a_max) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<conv_dens>\t\t' + str(conv_dens) + '\n')
        cmd_file.write('\t<conv_len>\t\t' + str(conv_len) + '\n')
        cmd_file.write('\t<conv_mag>\t\t' + str(conv_mag) + '\n')
        cmd_file.write('\t<conv_vel>\t\t' + str(conv_vel) + '\n')
        cmd_file.write('</task>\n')
        cmd_file.write('\n')

    def write_rat_part(self, cmd_file):
        """Writes commands for radiative torque calculation to the command file.

        Args:
            cmd_file: Instance of the command file.
        """

        # Overwrite default values with user input
        conv_dens, conv_len, conv_mag, conv_vel = self.get_conv_factors()

        cmd_file.write('<task> 1\n')
        if self.source is not None and self.source.parameter['nr_photons'] > 0:
            cmd_file.write(self.source.get_command())
        cmd_file.write('\n')
        cmd_file.write('\t<cmd>\t\t\tCMD_RAT\n')
        cmd_file.write('\n')
        if self.parse_args.grid_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_filename
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif self.parse_args.grid_cgs_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_cgs_filename
            cmd_file.write('\t<path_grid_cgs>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + 'temp/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp_rat/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + \
                'temp_rat/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        else:
            raise ValueError('No input grid defined/found!')
        cmd_file.write('\t<path_out>\t\t"' +
                       self.file_io.path['simulation_type'] + '"\n')
        cmd_file.write('\n')
        cmd_file.write('\t<conv_dens>\t\t' + str(conv_dens) + '\n')
        cmd_file.write('\t<conv_len>\t\t' + str(conv_len) + '\n')
        cmd_file.write('\t<conv_mag>\t\t' + str(conv_mag) + '\n')
        cmd_file.write('\t<conv_vel>\t\t' + str(conv_vel) + '\n')
        cmd_file.write('</task>\n')
        cmd_file.write('\n')

    def write_dust_mc_part(self, cmd_file):
        """Writes commands for Monte-Carlo radiative transfer to the command file.

        Args:
            cmd_file: Instance of the command file.
        """

        # Overwrite default values with user input
        if self.parse_args.peel_off is not None:
            peel_off = self.parse_args.peel_off
        else:
            peel_off = self.model.parameter['peel_off']
        if self.parse_args.enfsca is not None:
            enfsca = self.parse_args.enfsca
        else:
            enfsca = self.model.parameter['enforced_scattering']
        if self.parse_args.rot_axis_1 is not None:
            rot_axis_1 = self.parse_args.rot_axis_1
        else:
            rot_axis_1 = self.detector.parameter['rot_axis_1']
        if self.parse_args.rot_axis_2 is not None:
            rot_axis_2 = self.parse_args.rot_axis_2
        else:
            rot_axis_2 = self.detector.parameter['rot_axis_2']
        conv_dens, conv_len, conv_mag, conv_vel = self.get_conv_factors()

        # Make rotation axis to unit length vectors
        if np.linalg.norm(rot_axis_1) != 1:
            rot_axis_1 /= np.linalg.norm(rot_axis_1)
        if np.linalg.norm(rot_axis_2) != 1:
            rot_axis_2 /= np.linalg.norm(rot_axis_2)

        cmd_file.write('<task> 1\n')
        cmd_file.write('\n')
        cmd_file.write(self.detector.get_dust_scattering_command())
        cmd_file.write('\t<axis1>\t' + str(rot_axis_1[0]) + '\t' + str(rot_axis_1[1])
                       + '\t' + str(rot_axis_1[2]) + '\n')
        cmd_file.write('\t<axis2>\t' + str(rot_axis_2[0]) + '\t' + str(rot_axis_2[1])
                       + '\t' + str(rot_axis_2[2]) + '\n')
        cmd_file.write('\n')

        if self.source is not None and self.source.parameter['nr_photons'] > 0:
            cmd_file.write(self.source.get_command())
        if self.dust_source.parameter['nr_photons'] > 0:
            cmd_file.write(self.dust_source.get_command())
            cmd_file.write('\n')
        cmd_file.write('\n')
        cmd_file.write('\t<cmd>\t\t\tCMD_DUST_SCATTERING\n')
        cmd_file.write('\n')
        if self.parse_args.grid_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_filename
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif self.parse_args.grid_cgs_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_cgs_filename
            cmd_file.write('\t<path_grid_cgs>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + 'temp/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp_rat/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + \
                'temp_rat/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        else:
            raise ValueError('No input grid defined/found!')
        cmd_file.write('\t<path_out>\t\t"' +
                       self.file_io.path['simulation_type'] + '"\n')
        cmd_file.write('\n')
        cmd_file.write('\t<peel_off>\t\t' + str(int(peel_off)) + '\n')
        cmd_file.write('\t<enfsca>\t\t' + str(int(enfsca)) + '\n')
        if not peel_off:
            cmd_file.write('\t<acceptance_angle>\t' +
                           str(self.detector.parameter['acceptance_angle']) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<conv_dens>\t\t' + str(conv_dens) + '\n')
        cmd_file.write('\t<conv_len>\t\t' + str(conv_len) + '\n')
        cmd_file.write('\t<conv_mag>\t\t' + str(conv_mag) + '\n')
        cmd_file.write('\t<conv_vel>\t\t' + str(conv_vel) + '\n')
        cmd_file.write('</task>\n')
        cmd_file.write('\n')

    def write_dust_part(self, cmd_file):
        """Writes commands for raytrace simulations to the command file.

        Args:
            cmd_file: Instance of the command file.
        """

        # Overwrite default values with user input
        if self.parse_args.max_subpixel_lvl is not None:
            max_subpixel_lvl = self.parse_args.max_subpixel_lvl
        else:
            max_subpixel_lvl = self.detector.parameter['max_subpixel_lvl']
        if self.parse_args.rot_axis_1 is not None:
            rot_axis_1 = self.parse_args.rot_axis_1
        else:
            rot_axis_1 = self.detector.parameter['rot_axis_1']
        if self.parse_args.rot_axis_2 is not None:
            rot_axis_2 = self.parse_args.rot_axis_2
        else:
            rot_axis_2 = self.detector.parameter['rot_axis_2']
        conv_dens, conv_len, conv_mag, conv_vel = self.get_conv_factors()

        # Make rotation axis to unit length vectors
        if np.linalg.norm(rot_axis_1) != 1:
            rot_axis_1 /= np.linalg.norm(rot_axis_1)
        if np.linalg.norm(rot_axis_2) != 1:
            rot_axis_2 /= np.linalg.norm(rot_axis_2)

        cmd_file.write('<task> 1\n')
        cmd_file.write('\n')
        cmd_file.write(self.detector.get_dust_emission_command())
        cmd_file.write('\t<axis1>\t' + str(rot_axis_1[0]) + '\t' + str(rot_axis_1[1])
                       + '\t' + str(rot_axis_1[2]) + '\n')
        cmd_file.write('\t<axis2>\t' + str(rot_axis_2[0]) + '\t' + str(rot_axis_2[1])
                       + '\t' + str(rot_axis_2[2]) + '\n')
        cmd_file.write('\n')
        if self.source is not None and self.source.parameter['nr_photons'] > 0:
            cmd_file.write(self.source.get_command())
        cmd_file.write('\n')
        cmd_file.write(self.bg_source.get_command())
        cmd_file.write('\n')
        cmd_file.write('\t<cmd>\t\t\tCMD_DUST_EMISSION\n')
        if self.parse_args.grid_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_filename
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif self.parse_args.grid_cgs_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_cgs_filename
            cmd_file.write('\t<path_grid_cgs>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'rat/grid_rat.dat'):
            grid_path = self.file_io.path['simulation'] + 'rat/grid_rat.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + 'temp/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp_rat/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + \
                'temp_rat/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        else:
            raise ValueError('No input grid defined/found!')
        cmd_file.write('\t<path_out>\t\t"' +
                       self.file_io.path['simulation_type'] + '"\n')
        if 'dust_' in self.parse_args.simulation_type and \
                self.parse_args.simulation_type not in ['dust_mc', 'dust_full']:
            for align in self.parse_args.simulation_type.replace('dust_', '').split('_'):
                cmd_file.write('\t<align>\t\t\tALIG_' +
                               str(align).upper() + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<max_subpixel_lvl>\t' + str(max_subpixel_lvl) + '\n')
        if self.parse_args.f_highj is not None:
            cmd_file.write('\t<f_highJ>\t\t' +
                           str(self.parse_args.f_highj) + '\n')
        if self.parse_args.f_c is not None:
            cmd_file.write('\t<f_c>\t\t\t' + str(self.parse_args.f_c) + '\n')
        cmd_file.write('\n')
        if self.parse_args.no_rt_scattering:
            cmd_file.write('\t<rt_scattering>\t0\n')
            cmd_file.write('\n')
        elif self.dust_source.parameter['nr_photons'] > 0:
            cmd_file.write(self.dust_source.get_command())
            cmd_file.write('\n')
        if self.parse_args.temp_a_max is not None:
            cmd_file.write('\t<stochastic_heating>\t' +
                           str(self.parse_args.temp_a_max) + '\n')
            cmd_file.write('\n')
        if self.parse_args.split_dust_emission is not None:
            cmd_file.write('\t<split_dust_emission>\t' +
                           str(int(self.parse_args.split_dust_emission)) + '\n')
            cmd_file.write('\n')
        cmd_file.write('\t<conv_dens>\t\t' + str(conv_dens) + '\n')
        cmd_file.write('\t<conv_len>\t\t' + str(conv_len) + '\n')
        cmd_file.write('\t<conv_mag>\t\t' + str(conv_mag) + '\n')
        cmd_file.write('\t<conv_vel>\t\t' + str(conv_vel) + '\n')
        cmd_file.write('</task>\n')
        cmd_file.write('\n')

    def write_line_part(self, cmd_file):
        """Writes commands for the radiative linetransfer to the command file.

        Args:
            cmd_file: Instance of the command file.
        """

        # Overwrite default values with user input
        if self.parse_args.turbulent_velocity is not None:
            turbulent_velocity = self.math.parse(
                self.parse_args.turbulent_velocity, 'velocity')
        else:
            turbulent_velocity = self.model.parameter['turbulent_velocity']
        if self.parse_args.max_subpixel_lvl is not None:
            max_subpixel_lvl = self.parse_args.max_subpixel_lvl
        else:
            max_subpixel_lvl = self.detector.parameter['max_subpixel_lvl']
        if self.parse_args.rot_axis_1 is not None:
            rot_axis_1 = self.parse_args.rot_axis_1
        else:
            rot_axis_1 = self.detector.parameter['rot_axis_1']
        if self.parse_args.rot_axis_2 is not None:
            rot_axis_2 = self.parse_args.rot_axis_2
        else:
            rot_axis_2 = self.detector.parameter['rot_axis_2']
        conv_dens, conv_len, conv_mag, conv_vel = self.get_conv_factors()

        # Make rotation axis to unit length vectors
        if np.linalg.norm(rot_axis_1) != 1:
            rot_axis_1 /= np.linalg.norm(rot_axis_1)
        if np.linalg.norm(rot_axis_2) != 1:
            rot_axis_2 /= np.linalg.norm(rot_axis_2)

        cmd_file.write('<task> 1\n')
        cmd_file.write(self.gas.get_command())
        cmd_file.write('\n')
        cmd_file.write(self.bg_source.get_command())
        cmd_file.write('\n')
        cmd_file.write(self.detector.get_line_command())
        cmd_file.write('\t<axis1>\t' + str(rot_axis_1[0]) + '\t' + str(rot_axis_1[1])
                       + '\t' + str(rot_axis_1[2]) + '\n')
        cmd_file.write('\t<axis2>\t' + str(rot_axis_2[0]) + '\t' + str(rot_axis_2[1])
                       + '\t' + str(rot_axis_2[2]) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<cmd>\t\tCMD_LINE_EMISSION\n')
        cmd_file.write('\n')
        if self.parse_args.grid_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_filename
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif self.parse_args.grid_cgs_filename is not None:
            grid_path = self.file_io.path['model'] + \
                self.parse_args.grid_cgs_filename
            cmd_file.write('\t<path_grid_cgs>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + 'temp/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        elif os.path.isfile(self.file_io.path['simulation'] + 'temp_rat/grid_temp.dat'):
            grid_path = self.file_io.path['simulation'] + \
                'temp_rat/grid_temp.dat'
            cmd_file.write('\t<path_grid>\t\t"' + grid_path + '"\n')
        else:
            raise ValueError('No input grid defined/found!')
        cmd_file.write('\t<path_out>\t\t"' +
                       self.file_io.path['simulation_type'] + '"\n')
        cmd_file.write('\n')
        if self.parse_args.no_vel_maps:
            cmd_file.write('\t<vel_maps>\t\t0\n')
        else:
            cmd_file.write('\t<vel_maps>\t\t1\n')
        if self.parse_args.kepler and self.source.parameter['kepler_usable']:
            cmd_file.write(
                '\t<kepler_star_mass>\t' + str(self.source.parameter['mass'] / self.math.const['M_sun']) + '\n')
        elif self.model.parameter['vel_is_speed_of_sound']:
            cmd_file.write('\t<vel_is_speed_of_sound>\t1\n')
        cmd_file.write('\t<max_subpixel_lvl>\t' + str(max_subpixel_lvl) + '\n')
        cmd_file.write(
            '\t<turbulent_velocity>\t' + str(turbulent_velocity) + '\n')
        cmd_file.write('\n')
        cmd_file.write('\t<conv_dens>\t\t' + str(conv_dens) + '\n')
        cmd_file.write('\t<conv_len>\t\t' + str(conv_len) + '\n')
        cmd_file.write('\t<conv_mag>\t\t' + str(conv_mag) + '\n')
        cmd_file.write('\t<conv_vel>\t\t' + str(conv_vel) + '\n')
        cmd_file.write('</task>\n')
        cmd_file.write('\n')

    def write_run_file(self, run_file):
        """Writes a run file to execute POLARIS directly or remotely on a server.
        Putting on a queue is also possible.

        Args:
            run_file: Instance of the run bash script file.
        """
        self.server.check_for_walltime()
        run_file.write('#!/bin/bash\n')
        run_file.write(self.server.get_command())
        # For MAC PCs with newest OS (they are not allowing the change of the LD library path in .bashrc)
        if 'darwin' in sys.platform:
            run_file.write('export LD_LIBRARY_PATH="' + self.file_io.path['polaris'] + 'CCfits/.libs/:' +
                           self.file_io.path['polaris'] + 'cfitsio:${LD_LIBRARY_PATH}"\n')
            run_file.write('export DYLD_LIBRARY_PATH="' + self.file_io.path['polaris'] + 'CCfits/.libs/:' +
                           self.file_io.path['polaris'] + 'cfitsio:${DYLD_LIBRARY_PATH}"\n')
        if self.parse_args.save_output:
            run_file.write(self.file_io.path['bin'] + 'polaris ' + self.file_io.path['simulation_type'] +
                           'POLARIS.cmd ' + '| tee ' + self.file_io.path['simulation_type'] + 'POLARIS.out\n')
            run_file.write(r"sed -i 's/\r$//; s/\r/\r\n/g; /\r/d' " +
                           self.file_io.path['simulation_type'] + 'POLARIS.out\n')
        else:
            run_file.write(self.file_io.path['bin'] + 'polaris ' + self.file_io.path['simulation_type'] +
                           'POLARIS.cmd' + '\n')
        run_file.close()

    def execute_run_file(self):
        """Execute POLARIS with the command file.
        """
        os.system('chmod +x ' + self.file_io.path['simulation'] + 'run.sh')
        os.system('sh ' + self.file_io.path['simulation'] + 'run.sh')

    def queue_run_file(self):
        """Push POLARIS run file to a server queue.
        """
        os.system('chmod +x ' + self.file_io.path['simulation'] + 'run.sh')
        if self.server.parameter['queue_system'] == 'SBATCH':
            os.system('sbatch ' + self.file_io.path['simulation'] + 'run.sh')
        elif self.server.parameter['queue_system'] == 'PBS':
            os.system('qsub ' + self.file_io.path['simulation'] + 'run.sh')
        else:
            raise AttributeError('Queuing system not known!')

    def get_conv_factors(self):
        """Get the right conversion factors.
        """
        if self.parse_args.conv_dens is not None:
            conv_dens = self.parse_args.conv_dens
        elif self.parse_args.grid_filename is not None and \
                self.parse_args.grid_cgs_filename is not None:
            conv_dens = self.model.conv_parameter['conv_dens']
        else:
            conv_dens = 1
        if self.parse_args.conv_len is not None:
            conv_len = self.parse_args.conv_len
        elif self.parse_args.grid_filename is not None and \
                self.parse_args.grid_cgs_filename is not None:
            conv_len = self.model.conv_parameter['conv_len']
        else:
            conv_len = 1
        if self.parse_args.conv_mag is not None:
            conv_mag = self.parse_args.conv_mag
        elif self.parse_args.grid_filename is not None and \
                self.parse_args.grid_cgs_filename is not None:
            conv_mag = self.model.conv_parameter['conv_mag']
        else:
            conv_mag = 1
        if self.parse_args.conv_vel is not None:
            conv_vel = self.parse_args.conv_vel
        elif self.parse_args.grid_filename is not None and \
                self.parse_args.grid_cgs_filename is not None:
            conv_vel = self.model.conv_parameter['conv_vel']
        else:
            conv_vel = 1
        return conv_dens, conv_len, conv_mag, conv_vel
Пример #11
0
class DustChooser:
    """The DustChooser class provides the composition of the chosen dust
    species.
    """
    def __init__(self, file_io, parse_args, model=None):
        """Initialisation of all usable options.

        Notes:
            To create your own dust composition, add its name to the dictionary
            and write a class with its options as a derived class of class Dust.

        Args:
            file_io : Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
            model: Handles the model space including various
                quantities such as the density distribution.
        """
        self.file_io = file_io
        self.parse_args = parse_args
        self.model = model

        # Get math module
        self.math = Math()

        #: dict: Dictionary with all usable dust compositions
        self.dust_dict = {
            'none': NoDust,
            'graphite_oblate': GraphiteOblate,
            'graphite_perpend': GraphitePerpend,
            'graphite_parallel': GraphiteParallel,
            'silicate_oblate': SilicateOblate,
            'silicate': Silicate,
            'mrn_oblate': MrnOblate,
            'mrn': Mrn,
            'CM20': CM20,
            'aPyM5': aPyM5,
            'aOlM5': aOlM5,
            'themis': Themis,
            'pah_neutral': PAH0,
            'pah_ion': PAH1,
        }
        update_dust_dict(self.dust_dict)

    def get_module(self):
        """Chooses dust class from user input

        Note:
            Parameters set by PolarisTools overwrite preset values in the
            separate dust classes.

        Returns:
            Instance of chosen dust composition.
        """
        if self.parse_args.dust_composition in self.dust_dict.keys():
            dust_composition = self.parse_args.dust_composition
        elif self.parse_args.dust_composition is None:
            if self.parse_args.simulation_type in ['line', 'zeeman']:
                dust_composition = 'none'
            elif self.model is not None and self.model.parameter[
                    'dust_composition'] is not None:
                dust_composition = self.model.parameter['dust_composition']
            else:
                dust_composition = 'none'
        else:
            raise ValueError(
                'dust component not known! You can add a new component in dust.py'
            )

        dust = self.dust_dict[dust_composition](self.file_io, self.parse_args)
        # Overwrite preset parameters from user input
        self.update_user_parameters(dust)
        return dust

    def get_module_from_name(self, dust_name, user_input=True):
        """Chooses dust class from user input

        Args:
            user_input (bool): Update parameters with user input?

        Note:
            Parameters set by PolarisTools overwrite preset values in the
            separate dust classes.

        Returns:
            Instance of chosen dust composition.
        """
        if dust_name not in self.dust_dict.keys():
            raise ValueError(
                'dust component not known! You can add a new component in dust.py'
            )
        dust = self.dust_dict[dust_name](self.file_io, self.parse_args)
        # Overwrite preset parameters from user input
        if user_input:
            self.update_user_parameters(dust)
        return dust

    def update_user_parameters(self, dust):
        """Overwrite preset parameters from user input.

        Args:
            dust : Instance of chosen dust composition.
        """
        if self.parse_args.dust_size is not None:
            dust.parameter['amin'] = self.math.parse(
                self.parse_args.dust_size[0], 'length')
            dust.parameter['amax'] = self.math.parse(
                self.parse_args.dust_size[1], 'length')
        if self.parse_args.dust_size_distribution is not None:
            if len(self.parse_args.dust_size_distribution) < 2:
                raise ValueError('Defined dust size distribution not usable!')
            else:
                dust.parameter[
                    'size_keyword'] = self.parse_args.dust_size_distribution[0]
                if 'plaw' in dust.parameter['size_keyword']:
                    min_len = 1
                    if '-ed' in dust.parameter['size_keyword']:
                        min_len += 3
                    if '-cv' in dust.parameter['size_keyword']:
                        min_len += 3
                elif 'logn' in dust.parameter['size_keyword']:
                    min_len = 2
                else:
                    min_len = 0
                if min_len != len(self.parse_args.dust_size_distribution) - 1:
                    raise ValueError(
                        'Defined dust size distribution not usable!')
                else:
                    dust.parameter['size_parameter'] = []
                    for i in range(min_len):
                        dust.parameter['size_parameter'].append(
                            self.math.parse(
                                self.parse_args.dust_size_distribution[i + 1],
                                'length'))
Пример #12
0
class SourceChooser:
    """The StellarSourceChooser class provides the chosen stellar source
    such as a single star in the centre of the model space.
    """
    def __init__(self, model, file_io, parse_args):
        """Initialisation of all usable options.

        Notes:
            To create your own stellar source, add its name to the dictionary
            and write a class with its options as a derived class of class StellarSource.

        Args:
            model: Handles the model space including various
                quantities such as the density distribution.
            file_io : Handles file input/output and all
                necessary paths.
            parse_args : Provides all parameters chosen
                by user when executing PolarisTools.
        """
        self.model = model
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        self.math = Math()

        #: dict: Dictionary with all usable stellar sources
        self.sources_dict = {
            'isrf': ISRF,
            'mathis_isrf': MathisISRF,
            't_tauri': TTauri,
            'herbig_ae': HerbigAe,
            'binary': BinaryStar,
            'sun': Sun,
            'laser': Laser,
        }
        update_sources_dict(self.sources_dict)

    def get_module(self):
        """Chooses radiation source from user input

        Note:
            Parameters set by PolarisTools overwrite preset values in the
            separate radiation source classes.

        Returns:
            Instance of chosen stellar source.
        """
        if self.parse_args.radiation_source in self.sources_dict.keys():
            source_name = self.parse_args.radiation_source
        elif self.parse_args.radiation_source is None:
            if self.model is not None and self.model.parameter[
                    'radiation_source'] is not None:
                source_name = self.model.parameter['radiation_source']
            else:
                return None
        else:
            raise ValueError(
                'stellar source not known! You can add a new source in source.py'
            )
        radiation_source = self.sources_dict[source_name](self.file_io,
                                                          self.parse_args)
        # Overwrite default values with user input
        radiation_source.update_parameter(
            self.parse_args.rad_source_extra_parameter)
        if self.parse_args.nr_photons is not None:
            radiation_source.parameter['nr_photons'] = int(
                self.parse_args.nr_photons)
        if self.parse_args.rad_source_position is not None:
            radiation_source.parameter['position'] = [
                self.math.parse(self.parse_args.rad_source_position[i],
                                'length') for i in range(3)
            ]
        if self.parse_args.rad_source_direction is not None:
            radiation_source.parameter['direction'] = [
                self.math.parse(self.parse_args.rad_source_direction[i],
                                'length') for i in range(3)
            ]
        if self.parse_args.rad_source_temperature is not None:
            radiation_source.parameter[
                'temperature'] = self.parse_args.rad_source_temperature
        if self.parse_args.rad_source_luminosity is not None:
            radiation_source.parameter['luminosity'] = self.math.parse(
                self.parse_args.rad_source_luminosity, 'luminosity')
            radiation_source.parameter['radius'] = 0.
        elif self.parse_args.rad_source_radius is not None:
            radiation_source.parameter['radius'] = self.math.parse(
                self.parse_args.rad_source_radius, 'length')
        if self.parse_args.rad_source_power is not None:
            radiation_source.parameter['power'] = self.math.parse(
                self.parse_args.rad_source_power, 'luminosity')
        if self.parse_args.rad_source_center_wl is not None:
            radiation_source.parameter['center_wl'] = self.math.parse(
                self.parse_args.rad_source_center_wl, 'length')
        if self.parse_args.rad_source_fwhm is not None:
            radiation_source.parameter['fwhm'] = self.math.parse(
                self.parse_args.rad_source_fwhm, 'length')
        if self.parse_args.rad_source_mass is not None:
            radiation_source.parameter['mass'] = self.math.parse(
                self.parse_args.rad_source_mass, 'mass')
        return radiation_source

    def get_module_from_name(self, radiation_source_name):
        """Chooses radiation source from name string

        Args:
            radiation_source_name (str): Name of the stellar source.

        Returns:
            Instance of chosen radiation source .
        """
        if radiation_source_name in self.sources_dict.keys():
            radiation_source = self.sources_dict[radiation_source_name](
                self.file_io, self.parse_args)
        else:
            raise ValueError('No source with the name ' +
                             str(radiation_source_name) + '!')
        return radiation_source
Пример #13
0
class ModelChooser:
    """The ModelChooser class provides the chosen model.
    """
    def __init__(self, parse_args):
        """Initialisation of all usable options.

        Notes:
            To create your own model, add its name to the dictionary
            and write a class with its options as a derived class of class Model.

        Args:
            parse_args (ArgumentParser) : Provides all parameters chosen
            by user when executing PolarisTools.
        """
        self.parse_args = parse_args

        # Get math module
        self.math = Math()

        # dict: Dictionary with all usable models
        self.model_dict = {
            'default': Model,
            'disk': Disk,
            'sphere': Sphere,
            'globule': BokGlobule,
        }
        update_model_dict(self.model_dict)

    def get_module(self):
        """Chooses model class from user input

            Note:
                Parameters set by PolarisTools overwrite preset values in the
                separate model classes.

            Returns:
                Instance of chosen model.
        """
        if self.parse_args.model_name in self.model_dict.keys():
            model = self.model_dict[self.parse_args.model_name]()
        elif self.parse_args.model_name is not None:
            raise ValueError(
                'Model name not known! You can add a new model in model.py.')
        else:
            model = self.model_dict['default']()
        # Set user input variables
        if 'grid_type' in vars(self.parse_args).keys():
            model.update_parameter(self.parse_args.extra_parameter)
            if self.parse_args.grid_type is not None:
                model.parameter['grid_type'] = self.parse_args.grid_type
            if self.parse_args.gas_mass is not None:
                model.parameter['gas_mass'] = self.math.parse(
                    self.parse_args.gas_mass, 'mass')
            if self.parse_args.sidelength is not None:
                model.octree_parameter['sidelength'] = self.math.parse(
                    self.parse_args.sidelength, 'length')
            if self.parse_args.max_tree_level is not None:
                model.octree_parameter[
                    'max_tree_level'] = self.parse_args.max_tree_level
            if self.parse_args.inner_radius is not None:
                model.parameter['inner_radius'] = self.math.parse(
                    self.parse_args.inner_radius, 'length')
            if self.parse_args.outer_radius is not None:
                model.parameter['outer_radius'] = self.math.parse(
                    self.parse_args.outer_radius, 'length')
            if self.parse_args.z_max is not None:
                model.cylindrical_parameter['z_max'] = self.math.parse(
                    self.parse_args.z_max, 'length')
            if self.parse_args.n_r is not None:
                model.spherical_parameter['n_r'] = self.parse_args.n_r
                model.cylindrical_parameter['n_r'] = self.parse_args.n_r
            if self.parse_args.n_ph is not None:
                model.spherical_parameter['n_ph'] = self.parse_args.n_ph
                model.cylindrical_parameter['n_ph'] = self.parse_args.n_ph
            if self.parse_args.n_th is not None:
                model.spherical_parameter['n_th'] = self.parse_args.n_th
            if self.parse_args.n_z is not None:
                model.cylindrical_parameter['n_z'] = self.parse_args.n_z
            if self.parse_args.sf_r is not None:
                model.spherical_parameter['sf_r'] = self.parse_args.sf_r
                model.cylindrical_parameter['sf_r'] = self.parse_args.sf_r
            if self.parse_args.sf_ph is not None:
                model.spherical_parameter['sf_ph'] = self.parse_args.sf_ph
                model.cylindrical_parameter['sf_ph'] = self.parse_args.sf_ph
            if self.parse_args.sf_th is not None:
                model.spherical_parameter['sf_th'] = self.parse_args.sf_th
            if self.parse_args.sf_z is not None:
                model.cylindrical_parameter['sf_z'] = self.parse_args.sf_z
            if self.parse_args.split_first_cell is not None:
                model.spherical_parameter[
                    'split_first_cell'] = self.parse_args.split_first_cell
                model.cylindrical_parameter[
                    'split_first_cell'] = self.parse_args.split_first_cell
        elif 'distance' in vars(self.parse_args).keys():
            if self.parse_args.distance is not None:
                model.parameter['distance'] = self.math.parse(
                    self.parse_args.distance, 'length')
        # Set the grid extent if global extent is set
        if model.parameter['grid_type'] == 'octree' and model.parameter[
                'outer_radius'] is not None:
            model.octree_parameter['sidelength'] = 2. * \
                model.parameter['outer_radius']
        elif model.parameter['grid_type'] == 'spherical':
            if model.parameter['inner_radius'] is not None:
                model.spherical_parameter['inner_radius'] = model.parameter[
                    'inner_radius']
            if model.parameter['outer_radius'] is not None:
                model.spherical_parameter['outer_radius'] = model.parameter[
                    'outer_radius']
        elif model.parameter['grid_type'] == 'cylindrical':
            if model.parameter['inner_radius'] is not None:
                model.cylindrical_parameter['inner_radius'] = model.parameter[
                    'inner_radius']
            if model.parameter['outer_radius'] is not None:
                model.cylindrical_parameter['outer_radius'] = model.parameter[
                    'outer_radius']
                if model.cylindrical_parameter['z_max'] is None:
                    model.cylindrical_parameter['z_max'] = model.parameter[
                        'outer_radius']
        # Convert radius in various units
        if model.parameter['grid_type'] == 'octree':
            model.tmp_parameter[
                'radius_x_m'] = model.octree_parameter['sidelength'] / 2.
            model.tmp_parameter[
                'radius_y_m'] = model.octree_parameter['sidelength'] / 2.
        elif model.parameter['grid_type'] == 'spherical':
            model.tmp_parameter['radius_x_m'] = model.spherical_parameter[
                'outer_radius']
            model.tmp_parameter['radius_y_m'] = model.spherical_parameter[
                'outer_radius']
        elif model.parameter['grid_type'] == 'cylindrical':
            model.tmp_parameter['radius_x_m'] = model.cylindrical_parameter[
                'outer_radius']
            model.tmp_parameter['radius_y_m'] = model.cylindrical_parameter[
                'outer_radius']
        else:
            raise ValueError('Grid type not known!')
        model.tmp_parameter['radius_x_arcsec'] = self.math.length_conv(
            model.tmp_parameter['radius_x_m'], 'arcsec',
            model.parameter['distance'])
        model.tmp_parameter['radius_y_arcsec'] = self.math.length_conv(
            model.tmp_parameter['radius_y_m'], 'arcsec',
            model.parameter['distance'])
        model.tmp_parameter['radius_x_pc'] = self.math.length_conv(
            model.tmp_parameter['radius_x_m'], 'pc')
        model.tmp_parameter['radius_y_pc'] = self.math.length_conv(
            model.tmp_parameter['radius_y_m'], 'pc')
        model.tmp_parameter['radius_x_au'] = self.math.length_conv(
            model.tmp_parameter['radius_x_m'], 'au')
        model.tmp_parameter['radius_y_au'] = self.math.length_conv(
            model.tmp_parameter['radius_y_m'], 'au')
        model.tmp_parameter['radius_x_ae'] = model.tmp_parameter['radius_x_au']
        model.tmp_parameter['radius_y_ae'] = model.tmp_parameter['radius_y_au']
        return model
Пример #14
0
class GasCreator:
    """The GasCreator class is able to create various gas species for POLARIS.
    """
    def __init__(self, file_io, parse_args):
        """Initialisation of the dust parameters.

        Args:
            file_io : Handles file input/output and all
                necessary paths.
        """
        self.file_io = file_io
        self.parse_args = parse_args

        # Get math module
        from polaris_tools_modules.math import Math
        self.math = Math()

    def convert_database(self, database, database_code):
        """Converts files fom the cdms/jpl database into the LAMBDA database format.

        Args:
            database (str): Name of the database from which we take the data to convert it.
                (Possible options: jpl, cdms)
            database_code (int): Unique code of the gas species/atom database file.

        Notes:
            JPL: http://spec.jpl.nasa.gov/
            CDMS: http://www.astro.uni-koeln.de/cdms/catalog
            LAMBDA: http://home.strw.leidenuniv.nl/~moldata/
        """
        # Init name of species and partition function with default values
        species_name = ''
        rot_spin_partition_func = 0
        if database == 'cdms':
            # Load partition functions from cdms database
            doc_cdms = open(
                self.file_io.path[database] + 'c' +
                str(database_code).zfill(6) + '.out', 'r')
            # Search for the entry with the partition function at T = 300 K
            i_line = 0
            for line in doc_cdms.readlines():
                if '300.000' in line:
                    rot_spin_partition_func = float(line.split()[1])
                if 'ID=' in line:
                    species_name = line.partition(', ID=')[0]
                if i_line == 0:
                    species_name = line.split()[0]
                i_line += 1
            doc_cdms.close()
        elif database == 'jpl':
            # Load partition functions from documentation of the chosen species from jpl database
            doc_jpl = open(
                self.file_io.path[database] + 'd' +
                str(database_code).zfill(6) + '.cat', 'r')
            # Search for the entry with the partition function at T = 300 K
            for line in doc_jpl.readlines():
                if 'Q(300.0)=' in line:
                    rot_spin_partition_func = float(line.split()[-1])
                if 'Name:' in line:
                    species_name = line.split()[-1]
            doc_jpl.close()
        else:
            raise ValueError('Database: ' + database + ' not known!')

        # If the partition function was not loaded correctly
        if species_name == '' or rot_spin_partition_func == 0:
            raise EnvironmentError(
                'Name of the species and partition function are not sufficiently loaded!'
            )

        # Init dictionary where each entry is one energy level
        energy_level = dict(
            # Transition index [1/cm]
            ENERGIES=[],
            # Degeneracy of the level
            WEIGHT=[],
            # Related quantum numbers
            QN_1=[],
            QN_2=[],
            QN_3=[],
            QN_4=[],
            QN_5=[],
            QN_6=[],
        )

        if os.path.isfile(self.file_io.path[database] + 'c' +
                          str(database_code).zfill(6) + '.egy'):
            # Load database file
            data = open(
                self.file_io.path[database] + 'c' +
                str(database_code).zfill(6) + '.egy', 'r')
            # Get the energy levels from database
            for line in data.readlines():
                formatted_line = line.split()
                # Add the energy level to the dictionary
                energy_level['ENERGIES'].append(abs(float(formatted_line[2])))
                energy_level['WEIGHT'].append(
                    int(formatted_line[5].replace(':', '')))
                if len(formatted_line) >= 7:
                    energy_level['QN_1'].append(int(formatted_line[6]))
                else:
                    energy_level['QN_1'].append(-1)
                if len(formatted_line) >= 8:
                    energy_level['QN_2'].append(int(formatted_line[7]))
                else:
                    energy_level['QN_2'].append(-1)
                if len(formatted_line) >= 9:
                    energy_level['QN_3'].append(int(formatted_line[8]))
                else:
                    energy_level['QN_3'].append(-1)
                if len(formatted_line) >= 10:
                    energy_level['QN_4'].append(int(formatted_line[9]))
                else:
                    energy_level['QN_4'].append(-1)
                if len(formatted_line) >= 11:
                    energy_level['QN_5'].append(int(formatted_line[10]))
                else:
                    energy_level['QN_5'].append(-1)
                if len(formatted_line) >= 12:
                    energy_level['QN_6'].append(int(formatted_line[11]))
                else:
                    energy_level['QN_6'].append(-1)

        else:
            # Load database file
            data = open(
                self.file_io.path[database] + 'c' +
                str(database_code).zfill(6) + '.cat', 'r')
            # Per default, the quantum number are integers
            half_value_qn = False
            # Get the upper energy level from each transition
            for line in data.readlines():
                #: float: Energy of lower transition [1/cm]
                energy_lower = float(line[31:41])
                #: float: Energy of upper transition [1/cm]
                energy_upper = energy_lower + float(
                    line[0:13]) * 1e-2 * 1e6 / self.math.const['c']
                # Obtain each available quantum number and calculate the weight from it
                if line[55:57].rstrip(
                        '\n') == '  ' or not line[55:57].rstrip('\n'):
                    qn_1 = -1
                else:
                    qn_1 = int(line[55:57].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[57:59].rstrip(
                        '\n') == '  ' or not line[57:59].rstrip('\n'):
                    qn_2 = -1
                else:
                    qn_2 = int(line[57:59].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[59:61].rstrip(
                        '\n') == '  ' or not line[59:61].rstrip('\n'):
                    qn_3 = -1
                else:
                    qn_3 = int(line[59:61].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[61:63].rstrip(
                        '\n') == '  ' or not line[61:63].rstrip('\n'):
                    qn_4 = -1
                else:
                    qn_4 = int(line[61:63].rstrip('\n'))
                if line[63:65].rstrip(
                        '\n') == '  ' or not line[63:65].rstrip('\n'):
                    qn_5 = -1
                else:
                    qn_5 = int(line[63:65].rstrip('\n'))
                if line[65:67].rstrip(
                        '\n') == '  ' or not line[65:67].rstrip('\n'):
                    qn_6 = -1
                else:
                    qn_6 = int(line[65:67].rstrip('\n'))
                weight = self.math.get_weight_from_qn(database_code, qn_1,
                                                      qn_2, qn_3, qn_4, qn_5,
                                                      qn_6)
                # For the upper energy level, if the calculated weight is not same as the weight of the transition,
                # the quantum numbers are not integers but .5 values.
                if weight != int(line[41:44]):
                    if weight - int(line[41:44]) == 1:
                        half_value_qn = True
                    else:
                        print(weight, int(line[41:44]))
                        raise ValueError(
                            'weight from quantum numbers does not fit with weight of spectral transition!'
                        )
                # Add the energy level to the dictionary
                energy_level['ENERGIES'].append(abs(energy_upper))
                energy_level['QN_1'].append(int(qn_1))
                energy_level['QN_2'].append(int(qn_2))
                energy_level['QN_3'].append(int(qn_3))
                energy_level['QN_4'].append(int(qn_4))
                energy_level['QN_5'].append(int(qn_5))
                energy_level['QN_6'].append(int(qn_6))
                # Adjust the weight according to the format of the quantum numbers
                if half_value_qn:
                    energy_level['WEIGHT'].append(weight - 1)
                else:
                    energy_level['WEIGHT'].append(weight)
            data.close()

            # Load database file
            data = open(
                self.file_io.path[database] + 'c' +
                str(database_code).zfill(6) + '.cat', 'r')
            # Get the lower energy level from each transition
            for line in data.readlines():
                #: float: Energy of lower transition [1/cm]
                energy_lower = float(line[31:41])
                # Obtain each available quantum number and calculate the weight from it
                if line[67:69].rstrip(
                        '\n') == '  ' or not line[67:69].rstrip('\n'):
                    qn_1 = -1
                else:
                    qn_1 = int(line[67:69].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[69:71].rstrip(
                        '\n') == '  ' or not line[69:71].rstrip('\n'):
                    qn_2 = -1
                else:
                    qn_2 = int(line[69:71].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[71:73].rstrip(
                        '\n') == '  ' or not line[71:73].rstrip('\n'):
                    qn_3 = -1
                else:
                    qn_3 = int(line[71:73].rstrip('\n'))
                # Obtain each available quantum number and calculate the weight from it
                if line[73:75].rstrip(
                        '\n') == '  ' or not line[73:75].rstrip('\n'):
                    qn_4 = -1
                else:
                    qn_4 = int(line[73:75].rstrip('\n'))
                if line[73:75].rstrip(
                        '\n') == '  ' or not line[73:75].rstrip('\n'):
                    qn_5 = -1
                else:
                    qn_5 = int(line[73:75].rstrip('\n'))
                if line[75:77].rstrip(
                        '\n') == '  ' or not line[75:77].rstrip('\n'):
                    qn_6 = -1
                else:
                    qn_6 = int(line[75:77].rstrip('\n'))
                weight = self.math.get_weight_from_qn(database_code, qn_1,
                                                      qn_2, qn_3, qn_4, qn_5,
                                                      qn_6)
                # Add the energy level to the dictionary
                energy_level['ENERGIES'].append(abs(energy_lower))
                energy_level['QN_1'].append(int(qn_1))
                energy_level['QN_2'].append(int(qn_2))
                energy_level['QN_3'].append(int(qn_3))
                energy_level['QN_4'].append(int(qn_4))
                energy_level['QN_5'].append(int(qn_5))
                energy_level['QN_6'].append(int(qn_6))
                # Adjust the weight according to the format of the quantum numbers
                if half_value_qn:
                    energy_level['WEIGHT'].append(weight - 1)
                else:
                    energy_level['WEIGHT'].append(weight)
            data.close()

            # Algorithm to sort the energy levels with their energy
            for i_level in range(len(energy_level['ENERGIES'])):
                print(i_level, "/", len(energy_level['ENERGIES']))
                for j_level in range(i_level + 1,
                                     len(energy_level['ENERGIES'])):
                    # If the next energy level has a higher energy than the current one, switch them
                    if energy_level['ENERGIES'][i_level] > energy_level[
                            'ENERGIES'][j_level]:
                        # Switch each entry of the dictionary
                        for keys in energy_level.keys():
                            tmp = energy_level[keys][j_level]
                            energy_level[keys][j_level] = energy_level[keys][
                                i_level]
                            energy_level[keys][i_level] = tmp

            # Delete duplicates of the energy levels
            index = len(energy_level['ENERGIES']) - 1
            while index != 0:
                index -= 1
                # If all quantum numbers are the same, the energy level is the same
                # However, the energy value might vary slightly
                if energy_level['QN_1'][index] == energy_level['QN_1'][index + 1] \
                        and energy_level['QN_2'][index] == energy_level['QN_2'][index + 1] \
                        and energy_level['QN_3'][index] == energy_level['QN_3'][index + 1] \
                        and energy_level['QN_4'][index] == energy_level['QN_4'][index + 1] \
                        and energy_level['QN_5'][index] == energy_level['QN_5'][index + 1] \
                        and energy_level['QN_6'][index] == energy_level['QN_6'][index + 1]:
                    del energy_level['ENERGIES'][index + 1]
                    del energy_level['WEIGHT'][index + 1]
                    del energy_level['QN_1'][index + 1]
                    del energy_level['QN_2'][index + 1]
                    del energy_level['QN_3'][index + 1]
                    del energy_level['QN_4'][index + 1]
                    del energy_level['QN_5'][index + 1]
                    del energy_level['QN_6'][index + 1]

        # Create a new entry for the index of the energy levels
        energy_level['LEVEL'] = []
        for i_level in range(len(energy_level['ENERGIES'])):
            energy_level['LEVEL'].append(i_level + 1)

        # Init dictionary where each entry is one spectral line transition
        radiative_transitions = dict(
            # Transition index
            TRANS=[],
            # Index of upper transition
            UP=[],
            # Index of lower transition
            LOW=[],
            # Einstein coefficient A [1/s]
            EINSTEINA=[],
            # Frequency of transition [GHz]
            FREQ=[],
            # Energy of transition [K]
            E=[],
        )

        # Load database file
        data = open(
            self.file_io.path[database] + 'c' + str(database_code).zfill(6) +
            '.cat', 'r')
        # Go through each transition again
        i_trans = 0
        for line in data.readlines():
            i_trans += 1
            # Set index of transition
            radiative_transitions['TRANS'].append(i_trans)
            #: float: Energy of lower transition [1/cm]
            energy_lower = float(line[31:41])
            #: float: Energy of upper transition [1/cm]
            energy_upper = energy_lower + float(
                line[0:13]) * 1e-2 * 1e6 / self.math.const['c']
            # Find index of lower energy level
            if energy_lower == 0.:
                # If the lower energy level is the zero level, it has the index 1
                radiative_transitions['LOW'].append(1)
            else:
                # Init comparison values
                min_value_lower = 999.9
                min_index_lower = 0
                # The lower energy level is found by taking the one with the closest energy
                for i_level in range(len(energy_level['LEVEL'])):
                    if abs(energy_level['ENERGIES'][i_level] -
                           energy_lower) < min_value_lower:
                        min_value_lower = abs(
                            energy_level['ENERGIES'][i_level] - energy_lower)
                        min_index_lower = i_level
                # Set index of lower transition
                radiative_transitions['LOW'].append(
                    energy_level['LEVEL'][min_index_lower])
            # Init comparison values
            min_value_upper = 999.9
            min_index_upper = 0
            # The upper energy level is found by taking the one with the closest energy
            for i_level in range(len(energy_level['LEVEL'])):
                if abs(energy_level['ENERGIES'][i_level] -
                       energy_upper) < min_value_upper:
                    min_value_upper = abs(energy_level['ENERGIES'][i_level] -
                                          energy_upper)
                    min_index_upper = i_level
            # Set index of upper transition
            radiative_transitions['UP'].append(
                energy_level['LEVEL'][min_index_upper])
            #: float: Frequency of lower transition [Hz]
            freq_lower = energy_lower * 1e2 * self.math.const['c']
            #: float: Frequency of upper transition [Hz]
            freq_upper = freq_lower + float(line[0:13]) * 1e6
            #: float: Energy of lower transition
            energy_lower = freq_lower * self.math.const['h']
            exp_val_lower = np.exp(-energy_lower /
                                   (self.math.const['k_B'] * 300))
            #: float: Energy of upper transition
            energy_upper = freq_upper * self.math.const['h']
            exp_val_upper = np.exp(-energy_upper /
                                   (self.math.const['k_B'] * 300))
            #: float: Line intensity at 300K
            line_int = 10**float(line[21:29])
            #: float: Squared value of the transition frequency [MHz]
            trans_freq_sq = float(line[0:13])**2
            #: float: Upper state degeneracy
            upper_deg = float(line[41:44])
            #: float: Einstein coefficient A
            einstein_a = line_int * trans_freq_sq * (
                rot_spin_partition_func /
                upper_deg) * 1 / (exp_val_lower - exp_val_upper) * 2.7964e-16
            # Set einstein coefficient A
            radiative_transitions['EINSTEINA'].append(einstein_a)
            # Set frequency of transition [GHz]
            radiative_transitions['FREQ'].append(float(line[0:13]) * 1e-3)
            #: float: Transition frequency [Hz]
            trans_freq = float(line[0:13]) * 1e6
            #: float: Brightness temperature
            bright_temp = self.math.const['h'] * trans_freq / self.math.const[
                'k_B']
            # Set Energy of transition in Kelvin
            radiative_transitions['E'].append(bright_temp)
        data.close()

        # Write the data as Leiden database file
        leiden_file = open(
            self.file_io.path['gas'] + species_name + '_' + database + '.dat',
            'w')
        leiden_file.write('!GAS_SPECIES\n')
        leiden_file.write(species_name + '\n')
        leiden_file.write('!MOLECULAR WEIGHT\n')
        leiden_file.write(str(database_code).zfill(6)[0:3].upper() + '\n')
        leiden_file.write('!NUMBER OF ENERGY LEVELS\n')
        leiden_file.write(str(len(energy_level['LEVEL'])) + '\n')
        leiden_file.write('!LEVEL + ENERGIES[cm^-1] + WEIGHT + QNUM\n')
        for i_level in range(len(energy_level['LEVEL'])):
            qn_string = ''
            if energy_level['QN_1'][i_level] != -1:
                qn_string += str(energy_level['QN_1'][i_level]).rjust(2)
            if energy_level['QN_2'][i_level] != -1:
                qn_string += str(energy_level['QN_2'][i_level]).rjust(2)
            if energy_level['QN_3'][i_level] != -1:
                qn_string += str(energy_level['QN_3'][i_level]).rjust(2)
            if energy_level['QN_4'][i_level] != -1:
                qn_string += str(
                    energy_level['QN_4'][i_level]).rjust(2) + '    '
            if energy_level['QN_5'][i_level] != -1:
                qn_string += str(
                    energy_level['QN_5'][i_level]).rjust(2) + '    '
            if energy_level['QN_6'][i_level] != -1:
                qn_string += str(
                    energy_level['QN_6'][i_level]).rjust(2) + '    '

            leiden_file.write(
                '{:5d}'.format(energy_level['LEVEL'][i_level]) +
                '{:16.8f}'.format(energy_level['ENERGIES'][i_level]) +
                '{:9.1f}'.format(energy_level['WEIGHT'][i_level]) + '     ' +
                qn_string + '\n')
        leiden_file.write('!NUMBER OF RADIATIVE TRANSITIONS\n')
        leiden_file.write(str(len(radiative_transitions['TRANS'])) + '\n')
        leiden_file.write(
            '!TRANS + UP + LOW + EINSTEINA[s^-1] + FREQ[GHz] + E_u[K]\n')
        for i_trans in range(len(radiative_transitions['TRANS'])):
            leiden_file.write(
                '{:5d}'.format(radiative_transitions['TRANS'][i_trans]) +
                '{:6d}'.format(radiative_transitions['UP'][i_trans]) +
                '{:6d}'.format(radiative_transitions['LOW'][i_trans]) +
                '{:12.3e}'.format(radiative_transitions['EINSTEINA'][i_trans])
                + '{:16.7f}'.format(radiative_transitions['FREQ'][i_trans]) +
                '{:10.2f}'.format(radiative_transitions['E'][i_trans]) + '\n')
        leiden_file.close()

    def create_zeeman_file(self):
        """Create Zeeman database file which can be used by POLARIS.
        """
        if self.parse_args.gas_species.upper() == 'CN':
            j_upper_list = [0.5, 0.5, 0.5, 1.5, 1.5, 1.5, 1.5]
            f_upper_list = [0.5, 1.5, 1.5, 1.5, 2.5, 0.5, 1.5]
            j_lower_list = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
            f_lower_list = [1.5, 0.5, 1.5, 0.5, 1.5, 0.5, 1.5]
            transition_index = [11, 12, 13, 14, 15, 16, 17]

            zeeman_file = open(self.file_io.path['gas'] + 'cn_zeeman.dat', 'w')
            zeeman_file.write('!Gas species name\n')
            zeeman_file.write('CN\n')
            zeeman_file.write(
                '!Gas species radius for collision calculations\n')
            # The gas_species radius is calculated by adding the covalent
            # radii of all atoms together.
            zeeman_file.write(
                str(self.math.covalent_radii['C'] +
                    self.math.covalent_radii['N']) + '\n')
            zeeman_file.write('!Number of transitions with Zeeman effect\n')
            zeeman_file.write(str(len(j_upper_list)) + '\n')

            zeeman_shifts = np.zeros(len(j_upper_list))
            for i_trans in range(len(j_upper_list)):
                lande_upper = self.math.lande_g_cn(1, j_upper_list[i_trans],
                                                   f_upper_list[i_trans])
                lande_lower = self.math.lande_g_cn(0, j_lower_list[i_trans],
                                                   f_lower_list[i_trans])
                zeeman_file.write('!Transition index in Leiden database\n')
                zeeman_file.write(str(transition_index[i_trans]) + '\n')
                zeeman_file.write('!Lande factor of upper level\n')
                zeeman_file.write(str(lande_upper) + '\n')
                zeeman_file.write('!Lande factor of lower level\n')
                zeeman_file.write(str(lande_lower) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the upper level\n')
                zeeman_file.write(str((f_upper_list[i_trans] * 2) + 1) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the lower level\n')
                zeeman_file.write(str((f_lower_list[i_trans] * 2) + 1) + '\n')

                if f_lower_list[i_trans] == f_upper_list[i_trans]:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_zero(
                            f, m_f, transition)
                elif (f_lower_list[i_trans] - f_upper_list[i_trans]) == +1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_plus(
                            f, m_f, transition)
                elif (f_lower_list[i_trans] - f_upper_list[i_trans]) == -1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_minus(
                            f, m_f, transition)

                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans == i_upper_trans:
                            zeeman_file.write(
                                '!Line strength of pi transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans) + ')\n')
                            rel_str_pi = 2.0 * relative_strength(
                                f_upper_list[i_trans], i_upper_trans, 0)
                            zeeman_file.write(str(rel_str_pi) + '\n')
                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == +1:
                            zeeman_file.write(
                                '!Line strength of sigma_+ transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans + 1) + ')\n')
                            rel_str_sigma_p = relative_strength(
                                f_upper_list[i_trans], i_upper_trans, +1)
                            zeeman_file.write(str(rel_str_sigma_p) + '\n')
                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == -1:
                            zeeman_file.write(
                                '!Line strength of sigma_- transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans - 1) + ')\n')
                            rel_str_sigma_m = relative_strength(
                                f_upper_list[i_trans], i_upper_trans, -1)
                            zeeman_file.write(str(rel_str_sigma_m) + '\n')
                            zeeman_shifts[i_trans] += 2.0 * rel_str_sigma_m * (
                                (lande_upper * i_upper_trans) -
                                (lande_lower * i_lower_trans))
            zeeman_file.close()
            zeeman_shifts = np.multiply(
                zeeman_shifts,
                self.math.const['mu_B'] / self.math.const['h'] * 1e-10 * 2.0)
            print('2*nu/B =', zeeman_shifts[0], 'Hz/muG')
        elif self.parse_args.gas_species.upper() == 'SO':
            n_upper_list = [2, 2, 3, 4, 5]
            j_upper_list = [3, 1, 4, 3, 6]
            n_lower_list = [1, 1, 2, 3, 4]
            j_lower_list = [2, 2, 3, 2, 5]
            transition_index = [3, 8, 10, 28, 34]

            zeeman_file = open(self.file_io.path['gas'] + 'so_zeeman.dat', 'w')
            zeeman_file.write('!Gas species name\n')
            zeeman_file.write('SO\n')
            zeeman_file.write(
                '!Gas species radius for collision calculations\n')
            # The gas_species radius is calculated by adding the covalent
            # radii of all atoms together.
            zeeman_file.write(
                str(self.math.covalent_radii['S'] +
                    self.math.covalent_radii['O']) + '\n')
            zeeman_file.write('!Number of transitions with Zeeman effect\n')
            zeeman_file.write(str(len(j_upper_list)) + '\n')

            zeeman_shifts = np.zeros(len(j_upper_list))
            for i_trans in range(len(j_upper_list)):
                lande_upper = self.math.lande_g_so(n_upper_list[i_trans],
                                                   j_upper_list[i_trans])
                lande_lower = self.math.lande_g_so(n_lower_list[i_trans],
                                                   j_lower_list[i_trans])
                zeeman_file.write('!Transition index in Leiden database\n')
                zeeman_file.write(str(transition_index[i_trans]) + '\n')
                zeeman_file.write('!Lande factor of upper level\n')
                zeeman_file.write(str(lande_upper) + '\n')
                zeeman_file.write('!Lande factor of lower level\n')
                zeeman_file.write(str(lande_lower) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the upper level\n')
                zeeman_file.write(str((j_upper_list[i_trans] * 2) + 1) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the lower level\n')
                zeeman_file.write(str((j_lower_list[i_trans] * 2) + 1) + '\n')

                if j_lower_list[i_trans] == j_upper_list[i_trans]:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_zero(
                            f, m_f, transition)
                elif (j_lower_list[i_trans] - j_upper_list[i_trans]) == +1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_plus(
                            f, m_f, transition)
                elif (j_lower_list[i_trans] - j_upper_list[i_trans]) == -1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_minus(
                            f, m_f, transition)

                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans == i_upper_trans:
                            zeeman_file.write(
                                '!Line strength of pi transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans) + ')\n')
                            rel_str_pi = 2.0 * relative_strength(
                                j_upper_list[i_trans], i_upper_trans, 0)
                            zeeman_file.write(str(rel_str_pi) + '\n')
                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == +1:
                            zeeman_file.write(
                                '!Line strength of sigma_+ transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans + 1) + ')\n')
                            rel_str_sigma_p = relative_strength(
                                j_upper_list[i_trans], i_upper_trans, +1)
                            zeeman_file.write(str(rel_str_sigma_p) + '\n')
                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == -1:
                            zeeman_file.write(
                                '!Line strength of sigma_- transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans - 1) + ')\n')
                            rel_str_sigma_m = relative_strength(
                                j_upper_list[i_trans], i_upper_trans, -1)
                            zeeman_file.write(str(rel_str_sigma_m) + '\n')
                            zeeman_shifts[i_trans] += 2.0 * rel_str_sigma_m * (
                                (lande_upper * i_upper_trans) -
                                (lande_lower * i_lower_trans))
            zeeman_file.close()
            zeeman_shifts = np.multiply(
                zeeman_shifts,
                self.math.const['mu_B'] / self.math.const['h'] * 1e-10 * 2.0)
            print('2*nu/B =', zeeman_shifts[0], 'Hz/muG')
        elif self.parse_args.gas_species.upper() == 'CCS':
            n_upper_list = [0, 1, 2, 3]
            j_upper_list = [1, 2, 3, 4]
            n_lower_list = [1, 0, 1, 2]
            j_lower_list = [0, 1, 2, 3]
            transition_index = [4, 6, 11, 15]

            zeeman_file = open(self.file_io.path['gas'] + 'ccs_zeeman.dat',
                               'w')
            zeeman_file.write('!Gas species name\n')
            zeeman_file.write('CCS\n')
            zeeman_file.write(
                '!Gas species radius for collision calculations\n')
            # The gas_species radius is calculated by adding the covalent
            # radii of all atoms together.
            zeeman_file.write(
                str(2 * self.math.covalent_radii['C'] +
                    self.math.covalent_radii['S']) + '\n')
            zeeman_file.write('!Number of transitions with Zeeman effect\n')
            zeeman_file.write(str(len(j_upper_list)) + '\n')

            zeeman_shifts = np.zeros(len(j_upper_list))
            for i_trans in range(len(j_upper_list)):
                lande_upper = self.math.lande_g_ccs(n_upper_list[i_trans],
                                                    j_upper_list[i_trans])
                lande_lower = self.math.lande_g_ccs(n_lower_list[i_trans],
                                                    j_lower_list[i_trans])
                zeeman_file.write('!Transition index in Leiden database\n')
                zeeman_file.write(str(transition_index[i_trans]) + '\n')
                zeeman_file.write('!Lande factor of upper level\n')
                zeeman_file.write(str(lande_upper) + '\n')
                zeeman_file.write('!Lande factor of lower level\n')
                zeeman_file.write(str(lande_lower) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the upper level\n')
                zeeman_file.write(str((j_upper_list[i_trans] * 2) + 1) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the lower level\n')
                zeeman_file.write(str((j_lower_list[i_trans] * 2) + 1) + '\n')

                if j_lower_list[i_trans] == j_upper_list[i_trans]:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_zero(
                            f, m_f, transition)
                elif (j_lower_list[i_trans] - j_upper_list[i_trans]) == +1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_plus(
                            f, m_f, transition)
                elif (j_lower_list[i_trans] - j_upper_list[i_trans]) == -1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_minus(
                            f, m_f, transition)

                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans == i_upper_trans:
                            zeeman_file.write(
                                '!Line strength of pi transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans) + ')\n')
                            rel_str_pi = 2.0 * relative_strength(
                                j_upper_list[i_trans], i_upper_trans, 0)
                            zeeman_file.write(str(rel_str_pi) + '\n')
                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == +1:
                            zeeman_file.write(
                                '!Line strength of sigma_+ transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans + 1) + ')\n')
                            rel_str_sigma_p = relative_strength(
                                j_upper_list[i_trans], i_upper_trans, +1)
                            zeeman_file.write(str(rel_str_sigma_p) + '\n')
                for i_upper_trans in np.arange(
                        -float(j_upper_list[i_trans]),
                        float(j_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(j_lower_list[i_trans]),
                            float(j_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == -1:
                            zeeman_file.write(
                                '!Line strength of sigma_- transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans - 1) + ')\n')
                            rel_str_sigma_m = relative_strength(
                                j_upper_list[i_trans], i_upper_trans, -1)
                            zeeman_file.write(str(rel_str_sigma_m) + '\n')
                            zeeman_shifts[i_trans] += 2.0 * rel_str_sigma_m * (
                                (lande_upper * i_upper_trans) -
                                (lande_lower * i_lower_trans))
            zeeman_file.close()
            zeeman_shifts = np.multiply(
                zeeman_shifts,
                self.math.const['mu_B'] / self.math.const['h'] * 1e-10 * 2.0)
            print('2*nu/B =', zeeman_shifts[0], 'Hz/muG')
        elif self.parse_args.gas_species.upper() == 'H1':
            l_upper_list = [0]
            f_upper_list = [1]
            l_lower_list = [0]
            f_lower_list = [0]
            transition_index = [1]

            zeeman_file = open(self.file_io.path['gas'] + 'h1_zeeman.dat', 'w')
            zeeman_file.write('!Gas species name\n')
            zeeman_file.write('H1\n')
            zeeman_file.write(
                '!Gas species radius for collision calculations\n')
            # The gas_species radius is calculated by adding the covalent
            # radii of all atoms together.
            zeeman_file.write(str(self.math.covalent_radii['H']) + '\n')
            zeeman_file.write('!Number of transitions with Zeeman effect\n')
            zeeman_file.write(str(len(l_upper_list)) + '\n')

            zeeman_shifts = np.zeros(len(l_upper_list))
            for i_trans in range(len(l_upper_list)):
                lande_upper = self.math.lande_g_h1(l_upper_list[i_trans],
                                                   f_upper_list[i_trans])
                lande_lower = self.math.lande_g_h1(l_lower_list[i_trans],
                                                   f_lower_list[i_trans])
                zeeman_file.write('!Transition index in Leiden database\n')
                zeeman_file.write(str(transition_index[i_trans]) + '\n')
                zeeman_file.write('!Lande factor of upper level\n')
                zeeman_file.write(str(lande_upper) + '\n')
                zeeman_file.write('!Lande factor of lower level\n')
                zeeman_file.write(str(lande_lower) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the upper level\n')
                zeeman_file.write(str((f_upper_list[i_trans] * 2) + 1) + '\n')
                zeeman_file.write(
                    '!Number of Zeeman sublevels in the lower level\n')
                zeeman_file.write(str((f_lower_list[i_trans] * 2) + 1) + '\n')

                if f_lower_list[i_trans] == f_upper_list[i_trans]:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_zero(
                            f, m_f, transition)
                elif (f_lower_list[i_trans] - f_upper_list[i_trans]) == +1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_plus(
                            f, m_f, transition)
                elif (f_lower_list[i_trans] - f_upper_list[i_trans]) == -1:

                    def relative_strength(f, m_f, transition):
                        return self.math.relative_line_strength_minus(
                            f, m_f, transition)

                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans == i_upper_trans:
                            zeeman_file.write(
                                '!Line strength of pi transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans) + ')\n')
                            rel_str_pi = 2.0 * relative_strength(
                                f_upper_list[i_trans], i_upper_trans, 0)
                            zeeman_file.write(str(rel_str_pi) + '\n')
                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == +1:
                            zeeman_file.write(
                                '!Line strength of sigma_+ transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans + 1) + ')\n')
                            rel_str_sigma_p = relative_strength(
                                f_upper_list[i_trans], i_upper_trans, +1)
                            zeeman_file.write(str(rel_str_sigma_p) + '\n')
                for i_upper_trans in np.arange(
                        -float(f_upper_list[i_trans]),
                        float(f_upper_list[i_trans] + 1), 1):
                    for i_lower_trans in np.arange(
                            -float(f_lower_list[i_trans]),
                            float(f_lower_list[i_trans] + 1), 1):
                        if i_lower_trans - i_upper_trans == -1:
                            zeeman_file.write(
                                '!Line strength of sigma_- transition (M\'=' +
                                str(i_upper_trans) + ' -> M\'\'=' +
                                str(i_upper_trans - 1) + ')\n')
                            rel_str_sigma_m = relative_strength(
                                f_upper_list[i_trans], i_upper_trans, -1)
                            zeeman_file.write(str(rel_str_sigma_m) + '\n')
                            zeeman_shifts[i_trans] += 2.0 * rel_str_sigma_m * (
                                (lande_upper * i_upper_trans) -
                                (lande_lower * i_lower_trans))
            zeeman_file.close()
            zeeman_shifts = np.multiply(
                zeeman_shifts,
                self.math.const['mu_B'] / self.math.const['h'] * 1e-10 * 2.0)
            print('2*nu/B =', zeeman_shifts[0], 'Hz/muG')

    def create_hydrogen_file(self):
        """Create LAMBDA database file for H1.
        """
        n_max = 20
        alpha = self.math.const['e']**2 / (
            4 * np.pi * self.math.const['epsilon_0'] *
            self.math.const['hbar'] * self.math.const['c'])
        energy_level = dict(
            # ID of the energy level
            LEVEL=[],
            # Transition index [1/cm]
            ENERGIES=[],
            # Degeneracy of the level
            WEIGHT=[],
            # Related quantum numbers
            QN=[],
        )
        energy_level_zero = \
            -self.math.const['m_e'] * self.math.const['c'] ** 2 * (
                    1 - (1 + (alpha / (1 - 0.5 - 0.5 + np.sqrt((0.5 + 0.5) ** 2 - alpha ** 2))) ** 2) ** (-0.5))
        gamma = 2.7928
        energy_level_zero += (self.math.const['m_e'] / self.math.const['m_p']) * alpha ** 4 * self.math.const['m_e'] * \
                             self.math.const['c'] ** 2 * 4 * gamma / 3 * (- 3 / 2.)
        i_level = 0
        for n in range(1, n_max + 1):
            j_max = (n - 1) + 0.5
            for j in np.arange(0.5, j_max + 1):
                energy_level_tmp = -self.math.const['m_e'] * self.math.const[
                    'c']**2 * (1 - (1 + (alpha / (n - j - 0.5 + np.sqrt(
                        (j + 0.5)**2 - alpha**2)))**2)**(-0.5))
                if n == 1 and j == 0.5:
                    n_extra = 2
                else:
                    n_extra = 1
                for f in range(n_extra):
                    i_level += 1
                    if n_extra == 2:
                        gamma = 2.7928
                        hf_energy = (self.math.const['m_e'] / self.math.const['m_p']) * alpha ** 4 * \
                                    self.math.const['m_e'] * self.math.const['c'] ** 2 * 4 * gamma / \
                                    (3 * n ** 3) * (f * (f + 1) - 3 / 2.)
                        energy_level['QN'].append('{:2d}'.format(n) +
                                                  '{:2d}'.format(int(j)) +
                                                  '{:2d}'.format(int(f)))
                    else:
                        hf_energy = 0.
                        energy_level['QN'].append('{:2d}'.format(n) +
                                                  '{:2d}'.format(int(j)) +
                                                  ' -')
                    wave_number = (
                        (energy_level_tmp + hf_energy - energy_level_zero) *
                        1e-2 / (self.math.const['h'] * self.math.const['c']))
                    energy_level['ENERGIES'].append(wave_number)
                    energy_level['WEIGHT'].append(((j * 2) + 1) * 2)

                    energy_level['LEVEL'].append(i_level)

        radiative_transitions = dict(
            # Transition index
            TRANS=[],
            # Index of upper transition
            UP=[],
            # Index of lower transition
            LOW=[],
            # Einstein coefficient A [1/s]
            EINSTEINA=[],
            # Frequency of transition [GHz]
            FREQ=[],
            # Energy of transition [K]
            E=[],
        )
        radiative_transitions['TRANS'].append(1)
        radiative_transitions['LOW'].append(1)
        radiative_transitions['UP'].append(2)
        radiative_transitions['EINSTEINA'].append(2.876e-15)
        radiative_transitions['FREQ'].append(1.4204058)
        radiative_transitions['E'].append(0.07)

        leiden_file = open(self.file_io.path['gas'] + 'H1_converted.dat', 'w')
        leiden_file.write('!GAS SPECIES\n')
        leiden_file.write('H1' + '\n')
        leiden_file.write('!MOLECULAR WEIGHT\n')
        leiden_file.write('1\n')
        leiden_file.write('!NUMBER OF ENERGY LEVELS\n')
        leiden_file.write(str(len(energy_level['LEVEL'])) + '\n')
        leiden_file.write('!LEVEL + ENERGIES[cm^-1] + WEIGHT + QNUM\n')
        for i_level in range(len(energy_level['LEVEL'])):
            leiden_file.write(
                '{:5d}'.format(energy_level['LEVEL'][i_level]) +
                '{:16.8f}'.format(energy_level['ENERGIES'][i_level]) +
                '{:9.1f}'.format(energy_level['WEIGHT'][i_level]) + '     ' +
                str(energy_level['QN'][i_level]) + '\n')
        leiden_file.write('!NUMBER OF RADIATIVE TRANSITIONS\n')
        leiden_file.write(str(len(radiative_transitions['TRANS'])) + '\n')
        leiden_file.write(
            '!TRANS + UP + LOW + EINSTEINA[s^-1] + FREQ[GHz] + E_u[K]\n')
        for i_trans in range(len(radiative_transitions['TRANS'])):
            leiden_file.write(
                '{:5d}'.format(radiative_transitions['TRANS'][i_trans]) +
                '{:5d}'.format(radiative_transitions['UP'][i_trans]) +
                '{:5d}'.format(radiative_transitions['LOW'][i_trans]) +
                '{:12.3e}'.format(radiative_transitions['EINSTEINA'][i_trans])
                + '{:17.7f}'.format(radiative_transitions['FREQ'][i_trans]) +
                '{:10.2f}'.format(radiative_transitions['E'][i_trans]) + '\n')
        leiden_file.close()