"""
Material definition.
Example:
Create a material definition for copper::
mat = Material('copper', {29: 1.0}, 8.9)
with open('copper.mat.in', 'w') as fp:
mat.write_input(fp)
Using the ``material`` program of PENELOPE in the ``pendbase`` folder, run::
material.exe < copper.mat.in
There should now be a ``copper.mat`` in the ``pendbase`` folder.
"""
# Standard library modules.
import os
# Third party modules.
import pyxray
# Local modules.
# Globals and constants variables.
FILENAME_MAXLENGTH = 20
[docs]class Material(object):
"""
Creates a new material.
Args:
name (str): Name of material.
composition (dict): Composition in weight fraction.
The composition is specified by a dictionary.
The keys are atomic numbers and the values are weight fraction
between ]0.0, 1.0].
density_g_per_cm3 (float): Material's density in g/cm3.
mean_excitation_energy_eV (float, optional): mean excitation energy.
If ``None``, it will be calculated by PENELOPE.
oscillator_strength_fcb (float, optional):
oscillator strength of plasmon excitation.
If ``None``, it will be estimated by PENELOPE.
plasmon_energy_wcb_eV (float, optional): energy of plasmon excitation in eV.
If ``None``, it will be estimated by PENELOPE.
Attributes:
name (str): Name of material.
composition (dict): Composition in weight fraction.
The composition is specified by a dictionary.
The keys are atomic numbers and the values are weight fraction
between ]0.0, 1.0].
density_g_per_cm3 (float): Material's density in g/cm3.
mean_excitation_energy_eV (float, optional): mean excitation energy.
If ``None``, it will be calculated by PENELOPE.
oscillator_strength_fcb (float, optional):
oscillator strength of plasmon excitation.
If ``None``, it will be estimated by PENELOPE.
plasmon_energy_wcb_eV (float, optional): energy of plasmon excitation in eV.
If ``None``, it will be estimated by PENELOPE.
"""
def __init__(self, name, composition, density_g_per_cm3,
mean_excitation_energy_eV=None,
oscillator_strength_fcb=None,
plasmon_energy_wcb_eV=None):
self.name = name
self.filename = name[:16] + '.mat'
self.composition = composition.copy()
self.density_g_per_cm3 = float(density_g_per_cm3)
self.mean_excitation_energy_eV = mean_excitation_energy_eV
self.oscillator_strength_fcb = oscillator_strength_fcb
self.plasmon_energy_wcb_eV = plasmon_energy_wcb_eV
def __repr__(self):
return '<{0}({1})>'.format(self.__class__.__name__, self.name)
[docs] @classmethod
def read_material(cls, fileobj):
"""
Reads a PENELOPE generated material file (.mat).
Args:
fileobj (file object): file object opened with read access.
Returns:
Material: new material.
"""
first_line = fileobj.readline().strip()
assert first_line[:8] == 'PENELOPE'
line = fileobj.readline()
_label, value = line.split()
name = value.strip()
line = fileobj.readline()
_label, value = line.split('=')
value, _unit = value.split()
density_g_per_cm3 = float(value)
line = fileobj.readline()
_label, value = line.split('=')
element_count = int(value)
composition = {}
totalatomicmass = 0.0
for _ in range(element_count):
line = fileobj.readline()
part_z, part_af = line.split(',')
_label, value = part_z.split('=')
z = int(value)
_label, value = part_af.split('=')
atomicfraction = float(value)
atomicmass = atomicfraction * pyxray.element_atomic_weight(z)
composition[z] = atomicmass
totalatomicmass += atomicmass
for z, atomicmass in composition.items():
composition[z] = atomicmass / totalatomicmass
line = fileobj.readline()
_label, value = line.split('=')
value, _unit = value.split()
mean_excitation_energy_eV = float(value)
line = fileobj.readline()
label, _value = line.split('=')
assert label.strip() == 'Number of oscillators'
line = fileobj.readline()
_, value1, _, value2, _, _ = line.split()
oscillator_strength_fcb = float(value1)
plasmon_energy_wcb_eV = float(value2)
return cls(name, composition, density_g_per_cm3,
mean_excitation_energy_eV,
oscillator_strength_fcb,
plasmon_energy_wcb_eV)
def _create_lines(self):
"""
Creates the lines of the input file of the material program.
"""
lines = []
elements_count = len(self.composition)
density = self.density_g_per_cm3
# Select the manual composition option.
lines.append("1")
# Material name.
lines.append(str(self.name))
lines.append(str(elements_count))
# Case more than one element.
if elements_count > 1:
lines.append("2") # Select weight fraction option.
for z, wf in self.composition.items():
lines.append('{0:d} {1:.4f}'.format(z, wf))
elif elements_count == 1:
z = list(self.composition.keys())[0]
lines.append('{0:d}'.format(z))
else:
raise ValueError("No elements are defined in the material.")
# Mean excitation energy.
if self.mean_excitation_energy_eV is not None:
lines.append("1")
lines.append("{0:f}".format(self.mean_excitation_energy_eV))
else:
lines.append("2")
# Density
lines.append("{0:f}".format(density))
# Fcb and Wcb values.
if self.oscillator_strength_fcb is not None and \
self.plasmon_energy_wcb_eV is not None:
lines.append("1")
lines.append("{0:f} {1:f}".format(self.oscillator_strength_fcb,
self.plasmon_energy_wcb_eV))
else:
lines.append("2")
# set the name and path of the output file.
lines.append(self.filename)
return lines
@property
def filename(self):
return self._filename
@filename.setter
def filename(self, filename):
if len(filename) > FILENAME_MAXLENGTH:
raise ValueError("Filename is too long. Maximum {0} characters"
.format(FILENAME_MAXLENGTH))
self._filename = filename
VACUUM = Material('Vacuum', {}, 0.0)
"""
Material representing vacuum.
"""