Exemple #1
0
    def parameterised_material(self, name):
        """ The function that actually creates the material class. """

        if "x" in ParameterSystem().database.options(name):
            self.composition = [ParameterSystem().database.get(name, "x")]
        else:
            self.composition = []

        class SpecificMaterial(BaseMaterial):
            material_string = name
            composition = self.composition

            def __init__(self, T=300, **kwargs):
                BaseMaterial.__init__(self, T=T, **kwargs)

        if name.lower() in self.sources.keys():
            SpecificMaterial.material_directory = self.sources[name.lower()]
            extension = ''
            if len(SpecificMaterial.composition) == 0:
                extension = '.txt'

            SpecificMaterial.n_path = os.path.join(
                SpecificMaterial.material_directory, 'n' + extension)
            SpecificMaterial.k_path = os.path.join(
                SpecificMaterial.material_directory, 'k' + extension)

        SpecificMaterial.__name__ = name

        self.known_materials[name] = SpecificMaterial
        return SpecificMaterial
    def __getattr__(self, attrname):  # only used for unknown attributes.
        if attrname == "n":
            try:
                return self.n
            except:
                return self.n_interpolated
        if attrname == "k":
            try:
                return self.k
            except:
                return self.k_interpolated
        if attrname == "electron_affinity":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                # from http://en.wikipedia.org/wiki/Anderson's_rule and GaAs values
                return (0.17 +
                        4.59) * q - self.valence_band_offset - self.band_gap
        if attrname == "electron_mobility":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                return calculate_mobility(self.material_string, False, self.Nd,
                                          self.main_fraction)
        if attrname == "hole_mobility":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                return calculate_mobility(self.material_string, True, self.Na,
                                          self.main_fraction)
        if attrname == "Nc":
            return 2 * (2 * pi * self.eff_mass_electron_Gamma * m0 * kb *
                        self.T / h**2)**1.5
        if attrname == "Nv":
            # Strictly speaking, this is valid only for III-V, zinc-blend semiconductors
            mhh = self.eff_mass_hh_z
            mlh = self.eff_mass_lh_z
            Nvhh = 2 * (2 * pi * mhh * m0 * kb * self.T / h**2)**1.5
            Nvlh = 2 * (2 * pi * mlh * m0 * kb * self.T / h**2)**1.5
            return Nvhh + Nvlh
        if attrname == "ni":
            return np.sqrt(self.Nc * self.Nv * np.exp(-self.band_gap /
                                                      (kb * self.T)))
        if attrname == "radiative_recombination":
            inter = lambda E: self.n(E)**2 * self.alphaE(E) * np.exp(-E / (
                kb * self.T)) * E**2
            upper = self.band_gap + 10 * kb * self.T
            return 1.0 / self.ni**2 * 2 * pi / (h**3 * c**2) * quad(
                inter, 0, upper)[0]

        kwargs = {
            element: getattr(self, element)
            for element in self.composition
        }
        kwargs["T"] = self.T
        return ParameterSystem().get_parameter(self.material_string, attrname,
                                               **kwargs)
Exemple #3
0
def create_new_material(mat_name,
                        n_source,
                        k_source,
                        parameter_source=None,
                        overwrite=False):
    """
    This function adds a new material to Solcore's material_data folder, so that it can be called like a
    built-in material. It needs a name for the new material, and source files for the n and k data and other
    parameters which will be copied into the material_data/Custom folder.

    :param mat_name: the name of the new material
    :param n_source: path of the n values (txt file, first column wavelength in m, second column n)
    :param k_source: path of the n values (txt file, first column wavelength in m, second column k)
    :param: parameter_source: file with list of parameters for the new material
    """

    PARAMETER_PATH = os.path.join(config.user_folder, "custom_parameters.txt")
    if "custom" not in config.parameters():
        if not os.path.isfile(PARAMETER_PATH):
            open(PARAMETER_PATH, "a").close()
        config["Parameters", "custom"] = PARAMETER_PATH

    CUSTOM_PATH = os.path.join(config.user_folder, "custom_materials")

    # check if there is already a material with this name
    if (mat_name in sorted(ParameterSystem().database.sections())
            or mat_name in config.materials()) and not overwrite:
        answer = input(
            f"A material named {mat_name} already exists in the database."
            f"Do you want to overwrite it [y/n]?")
        if answer.lower() != "y":
            return

    # create a folder in the custom materials folders
    folder = os.path.join(CUSTOM_PATH, mat_name + "-Material")
    if not os.path.exists(folder) and folder != "":
        os.makedirs(folder)

    # copy n and k data files to the material's folder
    copyfile(n_source, os.path.join(folder, "n.txt"))
    copyfile(k_source, os.path.join(folder, "k.txt"))

    config["Materials", mat_name] = folder

    # append the parameters for the new material
    params = ConfigParser()
    params.optionxform = str
    if parameter_source is not None:
        params.read([PARAMETER_PATH, parameter_source])
        with open(PARAMETER_PATH, "w") as fp:
            params.write(fp)
    else:
        params.read([PARAMETER_PATH])
        params[mat_name] = {}
        with open(PARAMETER_PATH, "w") as fp:
            params.write(fp)
        print("Material created with optical constants n and k only.")

    ParameterSystem().read()
    def __getattr__(self, attrname):  # only used for unknown attributes
        if attrname == "n":
            try:
                return self.n
            except:
                return self.n_interpolated
        if attrname == "k":
            try:
                return self.k
            except:
                return self.k_interpolated
        if attrname == "electron_affinity":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                # from http://en.wikipedia.org/wiki/Anderson's_rule and GaAs values
                return (0.17 +
                        4.59) * q - self.valence_band_offset - self.band_gap
        if attrname == "electron_mobility":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                return calculate_mobility(self.material_string, False, self.Nd,
                                          self.main_fraction)
        if attrname == "hole_mobility":
            try:
                return ParameterSystem().get_parameter(self.material_string,
                                                       attrname)
            except:
                return calculate_mobility(self.material_string, True, self.Na,
                                          self.main_fraction)

        kwargs = {
            element: getattr(self, element)
            for element in self.composition
        }
        kwargs["T"] = self.T
        return ParameterSystem().get_parameter(self.material_string, attrname,
                                               **kwargs)
Exemple #5
0
UnitsSystem(config['Units'])

# And now we load some functions form it.
si = UnitsSystem().si
asUnit = UnitsSystem().asUnit
siUnits = UnitsSystem().siUnits
sensibleUnits = UnitsSystem().sensibleUnits
siUnitFromString = UnitsSystem().siUnitFromString
convert = UnitsSystem().convert
guess_dimension = UnitsSystem().guess_dimension
nmJ = UnitsSystem().nmJ
mJ = UnitsSystem().mJ
eVnm = UnitsSystem().eVnm
nmHz = UnitsSystem().nmHz
spectral_conversion_nm_ev = UnitsSystem().spectral_conversion_nm_ev
spectral_conversion_nm_hz = UnitsSystem().spectral_conversion_nm_hz
eV = UnitsSystem().eV

# And the same with the Parameter system
from solcore.parameter_system import ParameterSystem

ParameterSystem(config['Parameters'])
get_parameter = ParameterSystem().get_parameter


# And the same with the Materials system
from solcore.material_system import MaterialSystem

MaterialSystem(config['Materials'])
material = MaterialSystem().material
Exemple #6
0
    def material(self, name, sopra=False, nk_db=False):
        """ This function checks if the requested material exists and creates a class
        that contains its properties, assuming that the material does not exists in
        the database, yet.

        Such class will serve as the base class for all the derived materials based on that SpecificMaterial.
        For example, N-type GaAs and P-type GaAs use the same SpecificMaterial, just with a different doping, and the
        happens with GaAs at 300K or 200K.

        The derived materials based on a SpecificMaterial are instances of the SpecificMaterial class.

        >>> GaAs = solcore.material('GaAs')      # The SpecificMaterial class
        >>> n_GaAs = GaAs(Nd=1e23)               # Instance of the class
        >>> p_GaAs = GaAs(Na=1e22)               # Another instance of GaAs with different doping

        >>> AlGaAs = solcore.material('AlGaAs')  # The SpecificMaterial class
        >>> AlGaAs_1 = AlGaAs(Al=0.3)            # Instance of the class. For compounds, the variable element MUST be present
        >>> AlGaAs_2 = AlGaAs(Al=0.7, T=290)     # Different composition and T (the default is T=300K)


        The material is created from the parameters in the parameter_system and the n and k data if available. If the
        n and k data does not exists - at all or for that composition - then n=1 and k=0 at all wavelengths. Keep in
        mind that the available n and k data is valid only at room temperature.

        :param name: Name of the material
        :param sopra: If a SOPRA material must be used, rather than the normal database material, in case both exist.
        :return: A class of that material
        """

        suffix = ''
        # First we check if the material exists. If not, some help is provided
        try:
            if sopra:
                sopra_database(Material=name)
                suffix = '_sopra'
            elif nk_db:
                suffix = '_nk'
            else:
                ParameterSystem().database.options(name)
        except configparser.NoSectionError:
            try:
                sopra_database(Material=name)
                sopra = True
                suffix = '_sopra'
            except:
                valid_materials = sorted(ParameterSystem().database.sections())
                valid_materials.remove('Immediate Calculables')
                valid_materials.remove('Final Calculables')
                print(
                    '\nMaterial ERROR: "{}" is not in the semiconductors database or in the SOPRA database. Valid semiconductor materials are: '
                    .format(name))
                for v in valid_materials:
                    if "x" in ParameterSystem().database.options(v):
                        x = ParameterSystem().database.get(v, 'x')
                        print('\t {}  \tx = {}'.format(v, x))
                    else:
                        print('\t {}'.format(v))
                print(
                    '\nIn compounds, check that the order of the elements is the correct one (eg. GaInSb is OK but InGaSb'
                    ' is not).')
                val = input(
                    '\nDo you want to see the list of available SOPRA materials (y/n)?'
                )
                if val in 'Yy':
                    sopra_database.material_list()

                sys.exit()
        except solcore.absorption_calculator.sopra_db.SOPRAError:
            pass

        # Then we check if the material has already been created. If not, we create it.
        if name + suffix in self.known_materials:
            return self.known_materials[name + suffix]
        elif sopra:
            return self.sopra_material(name)
        elif nk_db:
            return self.nk_material(name)
        else:
            return self.parameterised_material(name)
Exemple #7
0
def create_new_material(mat_name, n_source, k_source, parameter_source = None):
    """
    This function adds a new material to Solcore's material_data folder, so that it can be called like a
    built-in material. It needs a name for the new material, and source files for the n and k data and other
    parameters which will be copied into the material_data/Custom folder.

    :param mat_name: the name of the new material
    :param n_source: path of the n values (txt file, first column wavelength in m, second column n)
    :param k_source: path of the n values (txt file, first column wavelength in m, second column k)
    :return: parameter_source: file with list of materials for the new material
    """

    CUSTOM_PATH = os.path.abspath(config['Others']['custom_mats'].replace('SOLCORE_ROOT', SOLCORE_ROOT))
    PARAMETER_PATH = os.path.abspath(config['Parameters']['custom'].replace('SOLCORE_ROOT', SOLCORE_ROOT))

    # check if there is already a material with this name
    if mat_name not in sorted(ParameterSystem().database.sections()):

        # create a folder in the custom materials folders
        folder = os.path.join(CUSTOM_PATH, mat_name + '-Material')
        if not os.path.exists(folder) and folder != "":
            os.makedirs(folder)
        else:
            print('This material already exists (or at least, a folder for it).')

        # copy n and k data files to the material's folder
        copyfile(n_source, os.path.join(folder, 'n.txt'))
        copyfile(k_source, os.path.join(folder, 'k.txt'))

        # create the parameter file if it doesn't already exist
        if not os.path.isfile(PARAMETER_PATH):
            open(PARAMETER_PATH, 'a').close()

        # append the parameters for the new material
        fout = open(PARAMETER_PATH, "r")
        existing_parameters = fout.read()
        fout.close()

        if not '[' + mat_name + ']' in existing_parameters:
            # make sure the names match
            if parameter_source is not None:
                fin = open(parameter_source, "r")
                parameters = fin.read() + '\n\n'
                parameters = sub("\[[^]]*\]", lambda x: x.group(0).replace(x.group(0), '[' + mat_name + ']'), parameters)
                fin.close()
            else:
                parameters = '[' + mat_name + ']\n\n'
                print('Material created with optical constants n and k only, no other parameters provided.')

            fout = open(PARAMETER_PATH, "a")
            fout.write(parameters)
            fout.close()
        else:
            print('There are already parameters for this material in the custom parameter file at ' + PARAMETER_PATH)

        # modify the user's config file (in their home folder) to include the relevant paths
        new_entry = mat_name + ' = ' + config['Others']['custom_mats'] + '/' + mat_name + '-Material\n'
        home_folder = os.path.expanduser('~')
        user_config = os.path.join(home_folder, '.solcore_config.txt')
        existing_config = open(user_config, 'r').read()
        if not new_entry in existing_config:

            add_source('Materials', mat_name, config['Others']['custom_mats'] + '/' + mat_name + '-Material')

        else:
            print('A path for this material was already added to the Solcore config file in the home directory.')

    else:
        print('There is already a material with this name - choose a different one.')
Exemple #8
0
# And now we load some functions form it.
si = us.si
asUnit = us.asUnit
siUnits = us.siUnits
sensibleUnits = us.sensibleUnits
siUnitFromString = us.siUnitFromString
convert = us.convert
guess_dimension = us.guess_dimension
nmJ = us.nmJ
mJ = us.mJ
eVnm = us.eVnm
nmHz = us.nmHz
spectral_conversion_nm_ev = us.spectral_conversion_nm_ev
spectral_conversion_nm_hz = us.spectral_conversion_nm_hz
eV = us.eV

# And the same with the Parameter system
from solcore.parameter_system import ParameterSystem

ps = ParameterSystem(config.parameters)
config.register_observer("Parameters", ps.read)
get_parameter = ps.get_parameter

# And the same with the Materials system
from solcore.material_system import MaterialSystem

ms = MaterialSystem(config.materials)
config.register_observer("Materials", ms.read)
material = ms.material