Ejemplo n.º 1
0
def _notation_adf11_adas2cherab(rate_adas, filetype):
    """
    Converts adas unit, charge and numeric notation to cherab notation

    :param rate_adas: Nested dictionary of shape rate_adas[element][charge][te, ne, rates]
    :param filetype: string denoting adas adf11 file type to decide whether charge conversion is to be applied.
      Will be applied for file types: "scd", "ccd", "plt", "pls"
    :return: nested dictionary with cherab rates and units notation
    """

    # Charge correction will be applied if there is difference between adas and cherab charge notation
    if filetype in ["scd", "plt", "pls"]:
        charge_correction = int(-1)
    else:
        charge_correction = int(0)

    # adas units, charge and number notation to be changed to cherab notation
    rate_cherab = RecursiveDict()
    for i in rate_adas.keys():
        for j in rate_adas[i].keys():
            # convert from adas log10 in [cm**-3] notation to cherab [m**-3] electron density notation
            rate_cherab[i][j + charge_correction]["ne"] = PerCm3ToPerM3.to(
                10**rate_adas[i][j]["ne"])
            # convert from adas log10 to cherab electron temperature notation
            rate_cherab[i][j +
                           charge_correction]["te"] = 10**rate_adas[i][j]["te"]
            rate_cherab[i][j + charge_correction]["rates"] = Cm3ToM3.to(
                10**rate_adas[i][j]["rates"])

    return rate_cherab
Ejemplo n.º 2
0
def install_adf11ccd(donor_element,
                     donor_charge,
                     receiver_element,
                     file_path,
                     download=False,
                     repository_path=None,
                     adas_path=None):
    """
    Adds the thermal charge exchange rate defined in an ADF11 file to the repository.

    :param donor_element: Element donating the electron, for the case of ADF11 files it is
      neutral hydrogen.
    :param donor_charge: Charge of the donor atom/ion.
    :param receiver_element: Element receiving the electron.
    :param file_path: Path relative to ADAS root.
    :param download: Attempt to download file if not present (Default=True).
    :param repository_path: Path to the repository in which to install the rates (optional).
    :param adas_path: Path to ADAS files repository (optional).
    """

    print('Installing {}...'.format(file_path))
    path = _locate_adas_file(file_path, download, adas_path)
    if not path:
        raise ValueError('Could not locate the specified ADAS file.')

    # decode file and write out rates
    rate_adas = parse_adf11(receiver_element, path)
    rate_cherab = _notation_adf11_adas2cherab(
        rate_adas, "ccd")  # convert from adas to cherab notation

    # reshape rate dictionary to match cherab convention
    rate_cherab_ccd = RecursiveDict()
    rate_cherab_ccd[donor_element][donor_charge] = rate_cherab

    repository.update_thermal_cx_rates(rate_cherab_ccd, repository_path)
Ejemplo n.º 3
0
def _update_and_write_adf11(species, rate_data, path):

    # read in any existing rates
    try:
        with open(path, 'r') as f:
            content = RecursiveDict.from_dict(json.load(f))
    except FileNotFoundError:
        content = RecursiveDict()

    for charge, rates in rate_data.items():

        if not valid_charge(species, charge):
            raise ValueError(
                'The charge state is larger than the number of protons in the specified species.'
            )

        # sanitise and validate rate data
        te = np.array(rates['te'], np.float64)
        ne = np.array(rates['ne'], np.float64)
        rate_table = np.array(rates['rates'], np.float64)

        if ne.ndim != 1:
            raise ValueError('Density array must be a 1D array.')

        if te.ndim != 1:
            raise ValueError('Temperature array must be a 1D array.')

        if (ne.shape[0], te.shape[0]) != rate_table.shape:
            raise ValueError(
                'Electron temperature, density and rate data arrays have inconsistent sizes.'
            )

        # update file content with new rate
        content[charge] = {
            'te': te.tolist(),
            'ne': ne.tolist(),
            'rate': rate_table.tolist(),
        }

        # create directory structure if missing
        directory = os.path.dirname(path)
        if not os.path.isdir(directory):
            os.makedirs(directory)

        # write new data
        with open(path, 'w') as f:
            json.dump(content, f, indent=2, sort_keys=True)
Ejemplo n.º 4
0
Archivo: adf21.py Proyecto: cherab/core
def parse_adf21(beam_species, target_ion, target_charge, adf_file_path):
    """
    Opens and parses ADAS ADF21 data files.

    :param beam_species: Element object describing the beam species.
    :param target_ion: Element object describing the target ion species.
    :param target_charge: Ionisation level of the target species.
    :param adf_file_path: Path to ADF15 file from ADAS root.
    :return: Dictionary containing rates.
    """

    rate = RecursiveDict()
    with open(adf_file_path, 'r') as file:
        rate[beam_species][target_ion][target_charge] = parse_adas2x_rate(file)
    return rate
Ejemplo n.º 5
0
def update_wavelengths(wavelengths, repository_path=None):

    repository_path = repository_path or DEFAULT_REPOSITORY_PATH

    for element, ionisations in wavelengths.items():
        for ionisation, transitions in ionisations.items():

            # sanitise and validate
            if not isinstance(element, Element):
                raise TypeError('The element must be an Element object.')

            if not valid_ionisation(element, ionisation):
                raise ValueError('Ionisation level is larger than the number of protons in the element.')

            path = os.path.join(repository_path, 'wavelength/{}/{}.json'.format(element.symbol.lower(), ionisation))

            # read in any existing wavelengths
            try:
                with open(path, 'r') as f:
                    content = RecursiveDict.from_dict(json.load(f))
            except FileNotFoundError:
                content = RecursiveDict()

            # add/replace data for a transition
            for transition in transitions:
                key = encode_transition(transition)
                content[key] = float(wavelengths[element][ionisation][transition])

            # create directory structure if missing
            directory = os.path.dirname(path)
            if not os.path.isdir(directory):
                os.makedirs(directory)

            # write new data
            with open(path, 'w') as f:
                json.dump(content, f, indent=2, sort_keys=True)
Ejemplo n.º 6
0
def parse_adf22bme(beam_species, target_ion, target_ionisation, transition, adf_file_path):
    """
    Opens and parses ADAS ADF22 BME data files.

    :param beam_species: Element object describing the beam species.
    :param target_ion: Element object describing the target ion species.
    :param target_ionisation: Ionisation level of the target species.
    :param transition: Atomic transition tuple (upper level, lower level).
    :param adf_file_path: Path to ADF15 file from ADAS root.
    :return: Dictionary containing rates.
    """

    rate = RecursiveDict()
    with open(adf_file_path, 'r') as file:
        rate[beam_species][target_ion][target_ionisation][transition] = parse_adas2x_rate(file)
    return rate
Ejemplo n.º 7
0
Archivo: adf12.py Proyecto: cherab/core
def parse_adf12(donor_ion, donor_metastable, receiver_ion, receiver_charge,
                adf_file_path):
    """
    Opens and parses ADAS ADF12 data files.

    :param donor_ion: The donor ion element described by the rate file.
    :param donor_metastable: The donor ion metastable level.
    :param receiver_ion: The receiver ion element described by the rate file.
    :param receiver_charge: The receiver ion charge state described by the rate file.
    :param adf_file_path: Path to ADF15 file from ADAS root.
    :return: Dictionary containing rates.
    """

    rates = RecursiveDict()

    with open(adf_file_path, 'r') as file:

        rate_count = int(file.readline()[3:5])
        for i in range(rate_count):

            # parse block
            transition, rate = _parse_block(file)

            # add to repository update dictionary, converting density from cm^-3 to m^-3
            rates[donor_ion][receiver_ion][receiver_charge][transition][
                donor_metastable] = {
                    'eb': np.array(rate['ENER'], np.float64),
                    'ti': np.array(rate['TIEV'], np.float64),
                    'ni': PerCm3ToPerM3.to(np.array(rate['DENSI'],
                                                    np.float64)),
                    'z': np.array(rate['ZEFF'], np.float64),
                    'b': np.array(rate['BMAG'], np.float64),
                    'qeb': Cm3ToM3.to(np.array(rate['QENER'], np.float64)),
                    'qti': Cm3ToM3.to(np.array(rate['QTIEV'], np.float64)),
                    'qni': Cm3ToM3.to(np.array(rate['QDENSI'], np.float64)),
                    'qz': Cm3ToM3.to(np.array(rate['QZEFF'], np.float64)),
                    'qb': Cm3ToM3.to(np.array(rate['QBMAG'], np.float64)),
                    'ebref': rate['EBREF'],
                    'tiref': rate['TIREF'],
                    'niref': PerCm3ToPerM3.to(rate['NIREF']),
                    'zref': rate['ZEREF'],
                    'bref': rate['BREF'],
                    'qref': Cm3ToM3.to(rate['QEFREF'])
                }

    return rates
Ejemplo n.º 8
0
Archivo: adf15.py Proyecto: cherab/core
def _scrape_metadata_hydrogen(file, element, charge):
    """
    Scrapes transition and block information from the comments.
    """

    config = RecursiveDict()

    # start parsing from the beginning
    file.seek(0)
    lines = file.readlines()

    pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE'
    while not re.match(pec_index_header_match, lines[0], re.IGNORECASE):
        lines.pop(0)
    index_lines = lines

    for i in range(len(index_lines)):

        pec_hydrogen_transition_match = '^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*N=\s*([0-9]*) - N=\s*([0-9]*)\s*([A-Z]*)'
        match = re.match(pec_hydrogen_transition_match, index_lines[i],
                         re.IGNORECASE)
        if not match:
            continue

        block_num = int(match.groups()[0])
        wavelength = float(match.groups()[1]) / 10  # convert Angstroms to nm
        upper_level = int(match.groups()[2])
        lower_level = int(match.groups()[3])
        rate_type_adas = match.groups()[4]
        if rate_type_adas == 'EXCIT':
            rate_type = 'excitation'
        elif rate_type_adas == 'RECOM':
            rate_type = 'recombination'
        elif rate_type_adas == 'CHEXC':
            rate_type = 'cx_thermal'
        else:
            raise ValueError(
                "Unrecognised rate type - {}".format(rate_type_adas))

        config[rate_type][element][charge][(upper_level,
                                            lower_level)] = block_num
        config["wavelength"][element][charge][(upper_level,
                                               lower_level)] = wavelength

    return config
Ejemplo n.º 9
0
Archivo: adf15.py Proyecto: cherab/core
def parse_adf15(element, charge, adf_file_path, header_format=None):
    """
    Opens and parses ADAS ADF15 data files.

    :param element: Element described by ADF file.
    :param charge: Charge state described by ADF file.
    :param adf_file_path: Path to ADF15 file from ADAS root.
    :return: Dictionary containing rates.
    """

    if not isinstance(element, Element):
        raise TypeError('The element must be an Element object.')

    charge = int(charge)

    with open(adf_file_path, "r") as file:

        # for check header line
        header = file.readline()
        if not re.match('^\s*(\d*) {4}/(.*)/?\s*$', header):
            raise ValueError(
                'The specified path does not point to a valid ADF15 file.')

        # scrape transition information and wavelength
        # use simple electron configuration structure for hydrogen-like ions
        if header_format == 'hydrogen' or element == hydrogen:
            config = _scrape_metadata_hydrogen(file, element, charge)
        elif header_format == 'hydrogen-like' or element.atomic_number - charge == 1:
            config = _scrape_metadata_hydrogen_like(file, element, charge)
        else:
            config = _scrape_metadata_full(file, element, charge)

        # process rate data
        rates = RecursiveDict()
        for cls in ('excitation', 'recombination', 'thermalcx'):
            for element, charge_states in config[cls].items():
                for charge, transitions in charge_states.items():
                    for transition in transitions.keys():
                        block_num = config[cls][element][charge][transition]
                        rates[cls][element][charge][
                            transition] = _extract_rate(file, block_num)

    wavelengths = config['wavelength']
    return rates, wavelengths
Ejemplo n.º 10
0
def parse_adf15(element, ionisation, adf_file_path):
    """
    Opens and parses ADAS ADF15 data files.

    :param element: Element described by ADF file.
    :param ionisation: Ionisation described by ADF file.
    :param adf_file_path: Path to ADF15 file from ADAS root.
    :return: Dictionary containing rates.
    """

    if not isinstance(element, Element):
        raise TypeError('The element must be an Element object.')

    ionisation = int(ionisation)

    with open(adf_file_path, "r") as file:

        # for check header line
        header = file.readline()
        if not re.match('^\s*(\d*) {4}/(.*)/?\s*$', header):
            raise ValueError(
                'The specified path does not point to a valid ADF15 file.')

        # scrape transition information and wavelength
        config = _scrape_metadata(file, element, ionisation)

        # process rate data
        rates = RecursiveDict()
        for cls in ('excitation', 'recombination', 'thermalcx'):
            for element, ionisations in config[cls].items():
                for ionisation, transitions in ionisations.items():
                    for transition in transitions.keys():
                        block_num = config[cls][element][ionisation][
                            transition]
                        rates[cls][element][ionisation][
                            transition] = _extract_rate(file, block_num)

    wavelengths = config['wavelength']
    return rates, wavelengths
Ejemplo n.º 11
0
def add_thermal_cx_rate(donor_element,
                        donor_charge,
                        receiver_element,
                        rate,
                        repository_path=None):
    """
    Adds a single thermal charge exchange rate to the repository.

    If adding multiple rates, consider using the update_recombination_rates()
    function instead. The update function avoids repeatedly opening and closing
    the rate files.

    :param donor_element: Element donating the electron.
    :param donor_charge: Charge of the donating atom/ion
    :param receiver_element: Element receiving the electron
    :param rate: rates
    :param repository_path:
    :return:
    """

    rates2update = RecursiveDict()
    rates2update[donor_element][donor_charge][receiver_element] = rate

    update_thermal_cx_rates(rates2update, repository_path)
Ejemplo n.º 12
0
def update_pec_rates(rates, repository_path=None):
    """
    PEC rate file structure

    /pec/<class>/<element>/<ionisation>.json
    """

    valid_classes = ['excitation', 'recombination', 'thermalcx']

    repository_path = repository_path or DEFAULT_REPOSITORY_PATH

    for cls, elements in rates.items():
        for element, ionisations in elements.items():
            for ionisation, transitions in ionisations.items():

                # sanitise and validate
                cls = cls.lower()
                if cls not in valid_classes:
                    raise ValueError(
                        'Unrecognised pec rate class \'{}\'.'.format(cls))

                if not isinstance(element, Element):
                    raise TypeError('The element must be an Element object.')

                if not valid_ionisation(element, ionisation):
                    raise ValueError(
                        'Ionisation level is larger than the number of protons in the element.'
                    )

                path = os.path.join(
                    repository_path,
                    'pec/{}/{}/{}.json'.format(cls, element.symbol.lower(),
                                               ionisation))

                # read in any existing rates
                try:
                    with open(path, 'r') as f:
                        content = RecursiveDict.from_dict(json.load(f))
                except FileNotFoundError:
                    content = RecursiveDict()

                # add/replace data for a transition
                for transition in transitions:
                    key = encode_transition(transition)
                    data = rates[cls][element][ionisation][transition]

                    # sanitise/validate data
                    data['ne'] = np.array(data['ne'], np.float64)
                    data['te'] = np.array(data['te'], np.float64)
                    data['rate'] = np.array(data['rate'], np.float64)

                    if data['ne'].ndim != 1:
                        raise ValueError('Density array must be a 1D array.')

                    if data['te'].ndim != 1:
                        raise ValueError(
                            'Temperature array must be a 1D array.')

                    if (data['ne'].shape[0],
                            data['te'].shape[0]) != data['rate'].shape:
                        raise ValueError(
                            'Density, temperature and rate data arrays have inconsistent sizes.'
                        )

                    content[key] = {
                        'ne': data['ne'].tolist(),
                        'te': data['te'].tolist(),
                        'rate': data['rate'].tolist()
                    }

                # create directory structure if missing
                directory = os.path.dirname(path)
                if not os.path.isdir(directory):
                    os.makedirs(directory)

                # write new data
                with open(path, 'w') as f:
                    json.dump(content, f, indent=2, sort_keys=True)
Ejemplo n.º 13
0
def update_beam_emission_rates(rates, repository_path=None):
    """
    Beam emission rate file structure

    /beam/emission/<beam species>/<target ion>/<target_charge>.json

    File contains multiple rates, indexed by transition.
    """

    repository_path = repository_path or DEFAULT_REPOSITORY_PATH

    for beam_species, target_ions in rates.items():
        for target_ion, target_charge_states in target_ions.items():
            for target_charge, transitions in target_charge_states.items():

                # sanitise and validate arguments
                if not isinstance(beam_species, Element):
                    raise TypeError(
                        'The beam_species must be an Element object.')

                if not isinstance(target_ion, Element):
                    raise TypeError(
                        'The beam_species must be an Element object.')

                if not valid_charge(target_ion, target_charge):
                    raise ValueError(
                        'Charge state is larger than the number of protons in the target ion.'
                    )

                path = os.path.join(
                    repository_path, 'beam/emission/{}/{}/{}.json'.format(
                        beam_species.symbol.lower(), target_ion.symbol.lower(),
                        target_charge))

                # read in any existing rates
                try:
                    with open(path, 'r') as f:
                        content = RecursiveDict.from_dict(json.load(f))
                except FileNotFoundError:
                    content = RecursiveDict()

                # add/replace data for a transition
                for transition in transitions:
                    key = encode_transition(transition)
                    rate = rates[beam_species][target_ion][target_charge][
                        transition]

                    # sanitise and validate rate data
                    e = np.array(rate['e'], np.float64)
                    n = np.array(rate['n'], np.float64)
                    t = np.array(rate['t'], np.float64)
                    sen = np.array(rate['sen'], np.float64)
                    st = np.array(rate['st'], np.float64)

                    if e.ndim != 1:
                        raise ValueError(
                            'Beam energy array must be a 1D array.')

                    if n.ndim != 1:
                        raise ValueError('Density array must be a 1D array.')

                    if t.ndim != 1:
                        raise ValueError(
                            'Temperature array must be a 1D array.')

                    if (e.shape[0], n.shape[0]) != sen.shape:
                        raise ValueError(
                            'Beam energy, density and combined rate data arrays have inconsistent sizes.'
                        )

                    if t.shape != st.shape:
                        raise ValueError(
                            'Temperature and temperature rate data arrays have inconsistent sizes.'
                        )

                    # update file content with new rate
                    content[key] = {
                        'e': e.tolist(),
                        'n': n.tolist(),
                        't': t.tolist(),
                        'sen': sen.tolist(),
                        'st': st.tolist(),
                        'eref': float(rate['eref']),
                        'nref': float(rate['nref']),
                        'tref': float(rate['tref']),
                        'sref': float(rate['sref'])
                    }

                # create directory structure if missing
                directory = os.path.dirname(path)
                if not os.path.isdir(directory):
                    os.makedirs(directory)

                # write new data
                with open(path, 'w') as f:
                    json.dump(content, f, indent=2, sort_keys=True)
Ejemplo n.º 14
0
def update_beam_cx_rates(rates, repository_path=None):
    # organisation in repository:
    #   beam/cx/donor_ion/receiver_ion/receiver_ionisation.json
    # inside json file:
    #   transition: [list of donor_metastables with rates]

    def sanitise_and_validate(data, x_key, x_name, y_key, y_name):
        """
        Sanitises and validates pairs of rate data arrays.

        Converts arrays to numpy arrays and check that the dimensions of the
        supplied data are consistent.

        Arrays are converted in place, the supplied dictionary is modified.

        :param data: Rate data dictionary.
        :param x_key: Key of independent variable data.
        :param x_name: Name of data array for error reporting.
        :param y_key: Key of dependent variable data.
        :param y_name: Name of data array for error reporting.
        """

        # convert to numpy arrays
        data[x_key] = np.array(data[x_key], np.float64)
        data[y_key] = np.array(data[y_key], np.float64)

        # check dimensions
        if data[x_key].ndim != 1:
            raise ValueError('The {} array must be a 1D array.'.format(x_name))

        if data[y_key].ndim != 1:
            raise ValueError('The {} array must be a 1D array.'.format(y_name))

        if data[x_key].shape != data[y_key].shape:
            raise ValueError(
                'The {} and {} arrays have inconsistent lengths.'.format(
                    x_name, y_name))

    repository_path = repository_path or DEFAULT_REPOSITORY_PATH

    for donor, receivers in rates.items():
        for receiver, ionisations in receivers.items():
            for ionisation, transitions in ionisations.items():

                # sanitise and validate
                if not isinstance(donor, Element):
                    raise TypeError('The element must be an Element object.')

                if not isinstance(receiver, Element):
                    raise TypeError('The element must be an Element object.')

                if not valid_ionisation(receiver, ionisation):
                    raise ValueError(
                        'Ionisation level is larger than the number of protons in the element.'
                    )

                path = os.path.join(
                    repository_path,
                    'beam/cx/{}/{}/{}.json'.format(donor.symbol.lower(),
                                                   receiver.symbol.lower(),
                                                   ionisation))

                # read in any existing rates
                try:
                    with open(path, 'r') as f:
                        content = RecursiveDict.from_dict(json.load(f))
                except FileNotFoundError:
                    content = RecursiveDict()

                # json keys are strings, must convert metastable key to integer
                for transition, metastables in content.items():
                    content[transition] = RecursiveDict({
                        int(metastable): rate
                        for metastable, rate in metastables.items()
                    })

                # update content
                for transition, metastables in transitions.items():

                    # add/replace data for each metastable
                    transition_key = encode_transition(transition)
                    for metastable in metastables:

                        if not metastable >= 0:
                            raise ValueError(
                                'Donor metastable level cannot be less than zero.'
                            )

                        data = rates[donor][receiver][ionisation][transition][
                            metastable]

                        # sanitise/validate data
                        data['qref'] = float(data['qref'])
                        sanitise_and_validate(data, 'eb', 'beam energy', 'qeb',
                                              'beam energy effective rate')
                        sanitise_and_validate(
                            data, 'ti', 'ion temperature', 'qti',
                            'ion temperature effective rate')
                        sanitise_and_validate(data, 'ni', 'ion density', 'qni',
                                              'ion density effective rate')
                        sanitise_and_validate(data, 'z', 'Zeff', 'qz',
                                              'Zeff effective rate')
                        sanitise_and_validate(
                            data, 'b', 'B-field magnitude', 'qb',
                            'B-field magnitude effective rate')

                        content[transition_key][metastable] = {
                            'eb': data['eb'].tolist(),
                            'ti': data['ti'].tolist(),
                            'ni': data['ni'].tolist(),
                            'z': data['z'].tolist(),
                            'b': data['b'].tolist(),
                            'qref': data['qref'],
                            'qeb': data['qeb'].tolist(),
                            'qti': data['qti'].tolist(),
                            'qni': data['qni'].tolist(),
                            'qz': data['qz'].tolist(),
                            'qb': data['qb'].tolist(),
                        }

                # create directory structure if missing
                directory = os.path.dirname(path)
                if not os.path.isdir(directory):
                    os.makedirs(directory)

                # write new data
                with open(path, 'w') as f:
                    json.dump(content, f, indent=2, sort_keys=True)
Ejemplo n.º 15
0
def _scrape_metadata(file, element, ionisation):
    """
    Scrapes transition and block information from the comments.
    """

    config = RecursiveDict()

    # start parsing from the beginning
    file.seek(0)
    lines = file.readlines()

    # Use simple electron configuration structure for hydrogen
    if element == hydrogen:

        pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE'
        while not re.match(pec_index_header_match, lines[0], re.IGNORECASE):
            lines.pop(0)
        index_lines = lines

        for i in range(len(index_lines)):

            pec_hydrogen_transition_match = '^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*N=\s*([0-9]*) - N=\s*([0-9]*)\s*([A-Z]*)'
            match = re.match(pec_hydrogen_transition_match, index_lines[i],
                             re.IGNORECASE)
            if not match:
                continue

            block_num = int(match.groups()[0])
            wavelength = float(
                match.groups()[1]) / 10  # convert Angstroms to nm
            upper_level = int(match.groups()[2])
            lower_level = int(match.groups()[3])
            rate_type_adas = match.groups()[4]
            if rate_type_adas == 'EXCIT':
                rate_type = 'excitation'
            elif rate_type_adas == 'RECOM':
                rate_type = 'recombination'
            elif rate_type_adas == 'CHEXC':
                rate_type = 'cx_thermal'
            else:
                raise ValueError(
                    "Unrecognised rate type - {}".format(rate_type_adas))

            config[rate_type][element][ionisation][(upper_level,
                                                    lower_level)] = block_num
            config["wavelength"][element][ionisation][(
                upper_level, lower_level)] = wavelength

    # Use simple electron configuration structure for hydrogen-like ions
    elif element.atomic_number - ionisation == 1:

        pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE'
        while not re.match(pec_index_header_match, lines[0], re.IGNORECASE):
            lines.pop(0)
        index_lines = lines

        for i in range(len(index_lines)):

            pec_full_transition_match = '^C\s*([0-9]*)\.\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)'
            match = re.match(pec_full_transition_match, index_lines[i],
                             re.IGNORECASE)
            if not match:
                continue

            block_num = int(match.groups()[0])
            wavelength = float(
                match.groups()[1]) / 10  # convert Angstroms to nm
            upper_level = int(match.groups()[2])
            lower_level = int(match.groups()[3])
            rate_type_adas = match.groups()[4]
            if rate_type_adas == 'EXCIT':
                rate_type = 'excitation'
            elif rate_type_adas == 'RECOM':
                rate_type = 'recombination'
            elif rate_type_adas == 'CHEXC':
                rate_type = 'cx_thermal'
            else:
                raise ValueError(
                    "Unrecognised rate type - {}".format(rate_type_adas))

            config[rate_type][element][ionisation][(upper_level,
                                                    lower_level)] = block_num
            config["wavelength"][element][ionisation][(
                upper_level, lower_level)] = wavelength

    # Use full electron configuration structure for anything else
    else:

        configuration_lines = []
        configuration_dict = {}

        configuration_header_match = '^C\s*Configuration\s*\(2S\+1\)L\(w-1/2\)\s*Energy \(cm\*\*-1\)$'
        while not re.match(configuration_header_match, lines[0],
                           re.IGNORECASE):
            lines.pop(0)
        pec_index_header_match = '^C\s*ISEL\s*WAVELENGTH\s*TRANSITION\s*TYPE'
        while not re.match(pec_index_header_match, lines[0], re.IGNORECASE):
            configuration_lines.append(lines[0])
            lines.pop(0)
        index_lines = lines

        for i in range(len(configuration_lines)):

            configuration_string_match = "^C\s*([0-9]*)\s*((?:[0-9][SPDFG][0-9]\s)*)\s*\(([0-9]*\.?[0-9]*)\)([0-9]*)\(\s*([0-9]*\.?[0-9]*)\)"
            match = re.match(configuration_string_match,
                             configuration_lines[i], re.IGNORECASE)
            if not match:
                continue

            config_id = int(match.groups()[0])
            electron_configuration = match.groups()[1].rstrip().lower()
            spin_multiplicity = match.groups()[2]  # (2S+1)
            total_orbital_quantum_number = _L_LOOKUP[int(
                match.groups()[3])]  # L
            total_angular_momentum_quantum_number = match.groups()[4]  # J

            configuration_dict[config_id] = (
                electron_configuration + " " + spin_multiplicity +
                total_orbital_quantum_number +
                total_angular_momentum_quantum_number)

        for i in range(len(index_lines)):

            pec_full_transition_match = '^C\s*([0-9]*)\.?\s*([0-9]*\.[0-9]*)\s*([0-9]*)[\(\)\.0-9\s]*-\s*([0-9]*)[\(\)\.0-9\s]*([A-Z]*)'
            match = re.match(pec_full_transition_match, index_lines[i],
                             re.IGNORECASE)
            if not match:
                continue

            block_num = int(match.groups()[0])
            wavelength = float(
                match.groups()[1]) / 10  # convert Angstroms to nm
            upper_level_id = int(match.groups()[2])
            upper_level = configuration_dict[upper_level_id]
            lower_level_id = int(match.groups()[3])
            lower_level = configuration_dict[lower_level_id]
            rate_type_adas = match.groups()[4]
            if rate_type_adas == 'EXCIT':
                rate_type = 'excitation'
            elif rate_type_adas == 'RECOM':
                rate_type = 'recombination'
            elif rate_type_adas == 'CHEXC':
                rate_type = 'cx_thermal'
            else:
                raise ValueError(
                    "Unrecognised rate type - {}".format(rate_type_adas))

            config[rate_type][element][ionisation][(upper_level,
                                                    lower_level)] = block_num
            config["wavelength"][element][ionisation][(
                upper_level, lower_level)] = wavelength

    return config
Ejemplo n.º 16
0
def populate(download=True, repository_path=None, adas_path=None):
    """
    Populates the OpenADAS repository with a typical set of rates and wavelengths.

    If an ADAS file is not note found an attempt will be made to download the
    file from the OpenADAS website. This behaviour can be disabled by setting
    the download argument to False.

    :param download: Attempt to download the ADAS files if missing (default=True).
    :param repository_path: Alternate path for the OpenADAS repository (default=None).
    :param adas_path: Alternate path in which to search for ADAS files (default=None) .
    """

    # install a common selection of open adas files
    rates = {
        # 'adf11plt': (
        #     (hydrogen, "adf11/plt12/plt12_h.dat"),
        #     (helium, "adf11/plt96/plt96_he.dat"),
        #     (lithium, "adf11/plt96/plt96_li.dat"),
        #     (beryllium, "adf11/plt96/plt96_be.dat"),
        #     (carbon, "adf11/plt96/plt96_c.dat"),
        #     (nitrogen, "adf11/plt96/plt96_n.dat"),
        #     (oxygen,  "adf11/plt96/plt96_o.dat"),
        #     (neon, "adf11/plt96/plt96_ne.dat"),
        #     (argon, "adf11/plt40/plt40_ar.dat"),
        #     (krypton, "adf11/plt89/plt89_kr.dat")
        # ),
        # 'adf11prb': (
        #     (hydrogen, "adf11/prb12/prb12_h.dat"),
        #     (helium, "adf11/prb96/prb96_he.dat"),
        #     (lithium, "adf11/prb96/prb96_li.dat"),
        #     (beryllium, "adf11/prb96/prb96_be.dat"),
        #     (carbon, "adf11/prb96/prb96_c.dat"),
        #     (nitrogen, "adf11/prb96/prb96_n.dat"),
        #     (oxygen, "adf11/prb96/prb96_o.dat"),
        #     (neon, "adf11/prb96/prb96_ne.dat"),
        #     (argon, "adf11/prb89/prb89_ar.dat"),
        #     (krypton, "adf11/prb89/prb89_kr.dat")
        # ),
        'adf12': (
            # (donor, receiver, ionisation, donor_metastable, rate file)
            (hydrogen, 1, hydrogen,  1, 'adf12/qef93#h/qef93#h_h1.dat'),
            (hydrogen, 1, helium,    2, "adf12/qef93#h/qef93#h_he2.dat"),
            (hydrogen, 2, helium,    2, "adf12/qef97#h/qef97#h_en2_kvi#he2.dat"),
            (hydrogen, 1, beryllium, 4, "adf12/qef93#h/qef93#h_be4.dat"),
            (hydrogen, 2, beryllium, 4, "adf12/qef97#h/qef97#h_en2_kvi#be4.dat"),
            (hydrogen, 1, boron,     5, "adf12/qef93#h/qef93#h_b5.dat"),
            (hydrogen, 2, boron,     5, "adf12/qef97#h/qef97#h_en2_kvi#b5.dat"),
            (hydrogen, 1, carbon,    6, "adf12/qef93#h/qef93#h_c6.dat"),
            (hydrogen, 2, carbon,    6, "adf12/qef97#h/qef97#h_en2_kvi#c6.dat"),
            (hydrogen, 1, neon,      10, "adf12/qef93#h/qef93#h_ne10.dat"),
            (hydrogen, 2, neon,      10, "adf12/qef97#h/qef97#h_en2_kvi#ne10.dat")
        ),
        'adf15': (
            (hydrogen,  0, 'adf15/pec12#h/pec12#h_pju#h0.dat'),
            (helium,    0, 'adf15/pec96#he/pec96#he_pju#he0.dat'),
            (helium,    1, 'adf15/pec96#he/pec96#he_pju#he1.dat'),
            (beryllium, 0, 'adf15/pec96#be/pec96#be_pju#be0.dat'),
            (beryllium, 1, 'adf15/pec96#be/pec96#be_pju#be1.dat'),
            (beryllium, 2, 'adf15/pec96#be/pec96#be_pju#be2.dat'),
            (beryllium, 3, 'adf15/pec96#be/pec96#be_pju#be3.dat'),
            (carbon,    0, 'adf15/pec96#c/pec96#c_vsu#c0.dat'),
            (carbon,    1, 'adf15/pec96#c/pec96#c_vsu#c1.dat'),
            (carbon,    2, 'adf15/pec96#c/pec96#c_vsu#c2.dat'),
            # (neon,      0, 'adf15/pec96#ne/pec96#ne_pju#ne0.dat'),     #TODO: OPENADAS DATA CORRUPT
            # (neon,      1, 'adf15/pec96#ne/pec96#ne_pju#ne1.dat'),     #TODO: OPENADAS DATA CORRUPT
            (nitrogen,  0, 'adf15/pec96#n/pec96#n_vsu#n0.dat'),
            (nitrogen,  1, 'adf15/pec96#n/pec96#n_vsu#n1.dat'),
            # (nitrogen,  2, 'adf15/pec96#n/pec96#n_vsu#n2.dat'),    #TODO: OPENADAS DATA CORRUPT
        ),
        'adf21': (
            # (beam_species, target_ion, target_ionisation, rate file)
            (hydrogen, hydrogen,  1,  "adf21/bms97#h/bms97#h_h1.dat"),
            (hydrogen, helium,    2,  "adf21/bms97#h/bms97#h_he2.dat"),
            (hydrogen, lithium,   3,  "adf21/bms97#h/bms97#h_li3.dat"),
            (hydrogen, beryllium, 4,  "adf21/bms97#h/bms97#h_be4.dat"),
            (hydrogen, boron,     5,  "adf21/bms97#h/bms97#h_b5.dat"),
            (hydrogen, carbon,    6,  "adf21/bms97#h/bms97#h_c6.dat"),
            (hydrogen, nitrogen,  7,  "adf21/bms97#h/bms97#h_n7.dat"),
            (hydrogen, oxygen,    8,  "adf21/bms97#h/bms97#h_o8.dat"),
            (hydrogen, fluorine,  9,  "adf21/bms97#h/bms97#h_f9.dat"),
            (hydrogen, neon,      10, "adf21/bms97#h/bms97#h_ne10.dat"),
        ),
        'adf22bmp': (
            # (beam species, beam metastable, target ion, target ionisation, rate file)
            (hydrogen, 2, hydrogen,  1,  "adf22/bmp97#h/bmp97#h_2_h1.dat"),
            (hydrogen, 3, hydrogen,  1,  "adf22/bmp97#h/bmp97#h_3_h1.dat"),
            (hydrogen, 4, hydrogen,  1,  "adf22/bmp97#h/bmp97#h_4_h1.dat"),
            (hydrogen, 2, helium,    2,  "adf22/bmp97#h/bmp97#h_2_he2.dat"),
            (hydrogen, 3, helium,    2,  "adf22/bmp97#h/bmp97#h_3_he2.dat"),
            (hydrogen, 4, helium,    2,  "adf22/bmp97#h/bmp97#h_4_he2.dat"),
            (hydrogen, 2, lithium,   3,  "adf22/bmp97#h/bmp97#h_2_li3.dat"),
            (hydrogen, 3, lithium,   3,  "adf22/bmp97#h/bmp97#h_3_li3.dat"),
            (hydrogen, 4, lithium,   3,  "adf22/bmp97#h/bmp97#h_4_li3.dat"),
            (hydrogen, 2, beryllium, 4,  "adf22/bmp97#h/bmp97#h_2_be4.dat"),
            (hydrogen, 3, beryllium, 4,  "adf22/bmp97#h/bmp97#h_3_be4.dat"),
            (hydrogen, 4, beryllium, 4,  "adf22/bmp97#h/bmp97#h_4_be4.dat"),
            (hydrogen, 2, boron,     5,  "adf22/bmp97#h/bmp97#h_2_b5.dat"),
            (hydrogen, 3, boron,     5,  "adf22/bmp97#h/bmp97#h_3_b5.dat"),
            (hydrogen, 4, boron,     5,  "adf22/bmp97#h/bmp97#h_4_b5.dat"),
            (hydrogen, 2, carbon,    6,  "adf22/bmp97#h/bmp97#h_2_c6.dat"),
            (hydrogen, 3, carbon,    6,  "adf22/bmp97#h/bmp97#h_3_c6.dat"),
            (hydrogen, 4, carbon,    6,  "adf22/bmp97#h/bmp97#h_4_c6.dat"),
            (hydrogen, 2, nitrogen,  7,  "adf22/bmp97#h/bmp97#h_2_n7.dat"),
            (hydrogen, 3, nitrogen,  7,  "adf22/bmp97#h/bmp97#h_3_n7.dat"),
            (hydrogen, 4, nitrogen,  7,  "adf22/bmp97#h/bmp97#h_4_n7.dat"),
            (hydrogen, 2, oxygen,    8,  "adf22/bmp97#h/bmp97#h_2_o8.dat"),
            (hydrogen, 3, oxygen,    8,  "adf22/bmp97#h/bmp97#h_3_o8.dat"),
            (hydrogen, 4, oxygen,    8,  "adf22/bmp97#h/bmp97#h_4_o8.dat"),
            (hydrogen, 2, fluorine,  9,  "adf22/bmp97#h/bmp97#h_2_f9.dat"),
            (hydrogen, 3, fluorine,  9,  "adf22/bmp97#h/bmp97#h_3_f9.dat"),
            (hydrogen, 4, fluorine,  9,  "adf22/bmp97#h/bmp97#h_4_f9.dat"),
            (hydrogen, 2, neon,      10, "adf22/bmp97#h/bmp97#h_2_ne10.dat"),
            (hydrogen, 3, neon,      10, "adf22/bmp97#h/bmp97#h_3_ne10.dat"),
            (hydrogen, 4, neon,      10, "adf22/bmp97#h/bmp97#h_4_ne10.dat"),
        ),
        'adf22bme': (
            # (beam species, target_ion, target_ionisation, (initial_level, final_level), rate file)
            (hydrogen, hydrogen,  1,  (3, 2), "adf22/bme10#h/bme10#h_h1.dat"),
            (hydrogen, helium,    2,  (3, 2), "adf22/bme97#h/bme97#h_he2.dat"),
            (hydrogen, lithium,   3,  (3, 2), "adf22/bme97#h/bme97#h_li3.dat"),
            (hydrogen, beryllium, 4,  (3, 2), "adf22/bme97#h/bme97#h_be4.dat"),
            (hydrogen, boron,     5,  (3, 2), "adf22/bme97#h/bme97#h_b5.dat"),
            (hydrogen, carbon,    6,  (3, 2), "adf22/bme97#h/bme97#h_c6.dat"),
            (hydrogen, nitrogen,  7,  (3, 2), "adf22/bme97#h/bme97#h_n7.dat"),
            (hydrogen, oxygen,    8,  (3, 2), "adf22/bme97#h/bme97#h_o8.dat"),
            (hydrogen, fluorine,  9,  (3, 2), "adf22/bme97#h/bme97#h_f9.dat"),
            (hydrogen, neon,      10, (3, 2), "adf22/bme97#h/bme97#h_ne10.dat"),
            (hydrogen, argon,     18, (3, 2), "adf22/bme99#h/bme99#h_ar18.dat"),
        )
    }

    # add common wavelengths to the repository
    wavelengths = RecursiveDict()

    # H0
    wavelengths[hydrogen][0] = {
        (2, 1): 121.52,
        (3, 1): 102.53,
        (3, 2): 656.19,
        (4, 1): 97.21,
        (4, 2): 486.06,
        (4, 3): 1874.82,
        (5, 1): 94.93,
        (5, 2): 433.99,
        (5, 3): 1281.61,
        (5, 4): 4050.53,
        (6, 1): 93.74,
        (6, 2): 410.12,
        (6, 3): 1093.64,
        (6, 4): 2624.75,
        (6, 5): 7456.66,
        (7, 1): 93.04,
        (7, 2): 396.95,
        (7, 3): 1004.79,
        (7, 4): 2165.19,
        (7, 5): 4651.78,
        (7, 6): 12366.59,
        (8, 1): 92.58,
        (8, 2): 388.85,
        (8, 3): 954.45,
        (8, 4): 1944.26,
        (8, 5): 3738.95,
        (8, 6): 7499.27,
        (8, 7): 19053.71,
        (9, 1): 92.28,
        (9, 2): 383.49,
        (9, 3): 922.76,
        (9, 4): 1817.13,
        (9, 5): 3295.58,
        (9, 6): 5905.68,
        (9, 7): 11303.84,
        (9, 8): 27791.42,
        (10, 1): 92.06,
        (10, 2): 379.74,
        (10, 3): 901.35,
        (10, 4): 1735.94,
        (10, 5): 3037.9,
        (10, 6): 5126.46,
        (10, 7): 8756.3,
        (10, 8): 16202.13,
        (10, 9): 38853.14,
        (11, 1): 91.9,
        (11, 2): 377.01,
        (11, 3): 886.14,
        (11, 4): 1680.39,
        (11, 5): 2871.76,
        (11, 6): 4670.5,
        (11, 7): 7504.88,
        (11, 8): 12381.84,
        (11, 9): 22330.84,
        (11, 10): 52512.27,
        (12, 1): 91.77,
        (12, 2): 374.96,
        (12, 3): 874.92,
        (12, 4): 1640.47,
        (12, 5): 2757.09,
        (12, 6): 4374.58,
        (12, 7): 6769.08,
        (12, 8): 10498.98,
        (12, 9): 16873.36,
        (12, 10): 29826.65,
        (12, 11): 69042.22,
    }

    # todo: temporary - add more accurate values
    wavelengths[deuterium][0] = wavelengths[hydrogen][0]
    wavelengths[tritium][0] = wavelengths[hydrogen][0]

    # He1+
    wavelengths[helium][1] = {
        (2, 1): 30.378,     # 2p -> 1s
        (3, 1): 25.632,     # 3p -> 1s
        (3, 2): 164.04,     # 3d -> 2p
        (4, 2): 121.51,     # 4d -> 2p
        (4, 3): 468.71,     # 4f -> 3d
        (5, 3): 320.28,     # 5f -> 3d
        (5, 4): 1012.65,    # 5g -> 4f
        (6, 4): 656.20,     # 6g -> 4f
        (6, 5): 1864.20,    # 6h -> 5g
        (7, 5): 1162.53,    # from ADAS comment, unknown source
        (7, 6): 3090.55     # from ADAS comment, unknown source
    }

    # Be3+
    wavelengths[beryllium][3] = {
        (3, 1): 6.4065,     # 3p -> 1s
        (3, 2): 41.002,     # 3d -> 2p
        (4, 2): 30.373,     # 4d -> 2p
        (4, 3): 117.16,     # 4f -> 3d
        (5, 3): 80.092,     # 5f -> 3d
        (5, 4): 253.14,     # 5g -> 4f
        (6, 4): 164.03,     # 6g -> 4f
        (6, 5): 466.01,     # 6h -> 5g
        (7, 5): 290.62,     # from ADAS comment, unknown source
        (7, 6): 772.62,     # from ADAS comment, unknown source
        (8, 6): 468.53,     # from ADAS comment, unknown source
        (8, 7): 1190.42     # from ADAS comment, unknown source
    }

    # B4+
    wavelengths[boron][4] = {
        (3, 1): 4.0996,     # 3p -> 1s
        (3, 2): 26.238,     # 3d -> 2p
        (4, 2): 19.437,     # 4d -> 2p
        (4, 3): 74.980,     # 4f -> 3d
        (5, 3): 51.257,     # 5f -> 3d
        (5, 4): 162.00,     # 5g -> 4f
        (6, 4): 104.98,     # 6g -> 4f
        (6, 5): 298.24,     # 6h -> 5g
        (7, 5): 186.05,     # 7h -> 5g
        (7, 6): 494.48,     # 7i -> 6h
        (8, 6): 299.86,     # 8i -> 6h
        (8, 7): 761.87,     # 8k -> 7i
        (9, 7): 451.99,     # 9k -> 7i
        (9, 8): 1111.25     # from ADAS comment, unknown source
    }

    # C5+
    wavelengths[carbon][5] = {
        (4, 2): 13.496,     # 4d -> 2p
        (4, 3): 52.067,     # 4f -> 3d
        (5, 3): 35.594,     # 5f -> 3d
        (5, 4): 112.50,     # 5g -> 4f
        (6, 4): 72.900,     # 6g -> 4f
        (6, 5): 207.11,     # 6h -> 5g
        (7, 5): 129.20,     # from ADAS comment, unknown source
        (7, 6): 343.38,     # from ADAS comment, unknown source
        (8, 6): 208.23,     # from ADAS comment, unknown source
        (8, 7): 529.07,     # from ADAS comment, unknown source
        (9, 7): 313.87,     # from ADAS comment, unknown source
        (9, 8): 771.69,     # from ADAS comment, unknown source
        (10, 8): 449.89,    # from ADAS comment, unknown source
        (10, 9): 1078.86    # from ADAS comment, unknown source
    }

    # Ne9+
    wavelengths[neon][9] = {
        (6, 5): 74.54,      # from ADAS comment, unknown source
        (7, 6): 123.64,     # from ADAS comment, unknown source
        (8, 7): 190.50,     # from ADAS comment, unknown source
        (9, 8): 277.79,     # from ADAS comment, unknown source
        (10, 9): 388.37,    # from ADAS comment, unknown source
        (11, 10): 524.92,   # from ADAS comment, unknown source
        (12, 11): 690.16,   # from ADAS comment, unknown source
        (13, 12): 886.83,   # from ADAS comment, unknown source
        (6, 4): 26.24,      # from ADAS comment, unknown source
        (7, 5): 46.51,      # from ADAS comment, unknown source
        (8, 6): 74.98,      # from ADAS comment, unknown source
        (9, 7): 113.02,     # from ADAS comment, unknown source
        (10, 8): 162.00,    # from ADAS comment, unknown source
        (11, 9): 223.22,    # from ADAS comment, unknown source
        (12, 10): 298.15,   # from ADAS comment, unknown source
        (13, 11): 388.12    # from ADAS comment, unknown source
    }

    install_files(rates, download=download, repository_path=repository_path, adas_path=adas_path)
    repository.update_wavelengths(wavelengths, repository_path=repository_path)
Ejemplo n.º 17
0
Archivo: adf11.py Proyecto: cherab/core
def parse_adf11(element, adf_file_path):
    """
    Reads contents of open adas adf11 files

    :param element: Element described by ADF file.
    :param adf_file_path: Path to ADF11 file from ADAS root.
    :return: temperature, density, rates as numpy array
    """

    if not isinstance(element, Element):
        raise TypeError('The element must be an Element object.')

    with open(adf_file_path, "r") as source_file:

        lines = source_file.readlines()  # read file contents by lines
        tmp = re.split("\s{2,}",
                       lines[0].strip())  # split into relevant variables
        # exctract variables
        z_nuclear = int(tmp[0])
        n_densities = int(tmp[1])
        n_temperatures = int(tmp[2])
        z_min = int(tmp[3])
        z_max = int(tmp[4])
        element_name = tmp[5].strip('/').lower()
        projectname = tmp[6]

        if element.atomic_number != z_nuclear or element.name != element_name:
            raise ValueError(
                "The requested element '{}' does not match the element description in the"
                "specified ADF11 file, '{}'.".format(element.name,
                                                     element_name))

        # check if it is a resolved file
        if re.match("\s*[0-9]+", lines[3]):  # is it unresolved?
            startsearch = 2
        else:
            startsearch = 4  # skip vectors with info about resolved states

        # get temperature and density vectors
        for i in range(startsearch, len(lines)):
            if re.match("^\s*C{0}-{2,}", lines[i]):
                tmp = re.sub("\n*\s+", "\t", "".join(
                    lines[startsearch:i]).strip())  # replace unwanted chars
                tmp = np.fromstring(tmp, sep="\t",
                                    dtype=float)  # put into nunpy array
                densities = tmp[:n_densities]  # read density values
                temperatures = tmp[n_densities:]  # read temperature values
                startsearch = i
                break

        # process rate data
        rates = RecursiveDict()

        # get beginnig and end of requested rates data block and add it to xarray
        blockrates_start = None
        blockrates_stop = None
        for i in range(startsearch, len(lines)):

            if re.match("^\s*C*-{2,}",
                        lines[i]):  # is it a rates block header?

                # is it a first data block found?
                if not blockrates_start is None:
                    blockrates_stop = i  # end of the requested block

                    rates_table = re.sub("\n*\s+", "\t", "".join(
                        lines[blockrates_start:blockrates_stop]).strip()
                                         )  # replace unwanted chars
                    rates_table = np.fromstring(
                        rates_table, sep="\t", dtype=float).reshape(
                            (n_temperatures,
                             n_densities))  # transform into an array

                    rates[element][ion_charge]['ne'] = densities
                    rates[element][ion_charge]['te'] = temperatures
                    rates[element][ion_charge]['rates'] = np.swapaxes(
                        rates_table, 0, 1)

                    # if end of data block beak the loop or reassign start of data block for next stage
                    if re.match("^\s*C{1}-{2,}", lines[i]) or re.match("^\s*C{0,1}-{2,}", lines[i]) and \
                            re.match("^\s*C\n", lines[i + 1]):
                        break

                z1_pos = re.search("Z1\s*=*\s*[0-9]+\s*",
                                   lines[i]).group()  # get Z1 part
                ion_charge = int(
                    re.sub("Z1[\s*=]", "",
                           z1_pos))  # remove Z1 to avoid getting 1  later
                if not re.search("IGRD\s*=*\s*[0-9]+\s*",
                                 lines[i]) is None:  # get the IGRD part
                    igrd_pos = re.search("IGRD\s*=*\s*[0-9]+\s*",
                                         lines[i]).group()  # get the IGRD part
                else:
                    igrd_pos = "No spec"
                if not re.search("IPRT\s*=*\s*[0-9]+\s*", lines[i]) is None:
                    iptr_pos = re.search("IPRT\s*=*\s*[0-9]+\s*",
                                         lines[i]).group()  # get the IPRT part
                else:
                    iptr_pos = "No spec"
                blockrates_start = i + 1  # if block start not known, check if we are at the right position

        return rates