X-Ray Sub-Module

x-ray utilities module

pyPenred.simulation.xray.anodeSim(density: SupportsFloat, composition: Sequence[tuple[SupportsInt, SupportsFloat]], anode_angle: SupportsFloat, kvp: SupportsFloat, histories: SupportsFloat = 10000.0, min_energy: SupportsFloat = -1.0, bins: SupportsInt = 200, pixel_size: SupportsFloat = 0.1, max_angle: SupportsFloat = 80.0, save_distributions: bool = True, verbose: SupportsInt = 1) tuple

Simulates an electron beam impinging on an anode with no filter.

Parameters:
  • density (float) – Anode density in g/cm^3.

  • composition (list[tuple[int, float]]) – List of (Z, weight fraction) for material composition.

  • anode_angle (float) – Anode angle in degrees.

  • kvp (float) – Beam KVP value.

  • histories (float, optional) – Number of histories to simulate.

  • min_energy (float, optional) – Minimum energy to record in keV. Defaults to kvp/10.

  • bins (int, optional) – Number of spectrum bins.

  • pixel_size (float, optional) – Pixel size in cm.

  • max_angle (float, optional) – Maximum angle for scattered particles, in degrees.

  • save_distributions (bool) – If enabled, saves the spectrum and spatial distributions in files ready to be used by deviceSim.

  • verbose (int, optional) – Verbosity level.

Returns:

A tuple with the following information, in order:
  • Energy limits for the generated spectrum (emin, emax) in keV

  • A 1D numpy array with the resulting gamma spectrum (prob/hist)

  • A 1D numpy array with the uncertainty for each gamma spectrum bin

  • X limits for spatial distribution (xmin, xmax) in cm

  • Y limits for spatial distribution (ymin, ymax) in cm

  • A 2D numpy array with the resulting gamma spatial distribution (prob/hist)

  • A 2D numpy array with the uncertainty for each spatial distribution bin

  • The distance between the anode collision point and the recorded spatial distribution center

Return type:

tuple

Raises:

ValueError – Incompatible value has been provided.

Example

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
import pyPenred

# Run the simulation and store the results
results = pyPenred.simulation.xray.anodeSim(density=10.0, composition=((74,1),), anode_angle=13, kvp=100.0, histories=1.0e5)

# Extract energy and spatial ranges
eLimits = results[0]
xLimits = results[3]
yLimits = results[4]

# Produce X data for spectrum plot
e_plot = np.linspace(eLimits[0], eLimits[1], len(results[1]))

# Plot the spectrum
plt.plot(e_plot, results[1])
plt.title("Anode spectrum")
plt.xlabel("keV")
plt.ylabel("Prob/hist")
plt.savefig('anode-spectrum.png')

# Plot the spatial distribution
plt.imshow(results[5],
           extent=[xLimits[0], xLimits[1], yLimits[0], yLimits[1]],
           aspect='auto',
           origin='lower',
           cmap='viridis')

plt.colorbar()
plt.xlabel("X cm")
plt.ylabel("Y cm")
plt.savefig('anode-spatial-distrib.png')
pyPenred.simulation.xray.deviceSim(source_position: Annotated[Sequence[SupportsFloat], 'FixedSize(3)'] = [0.0, 0.0, 0.0], focal_spot: SupportsFloat = 0.0, inherent_filter_width: SupportsFloat = -1.0, min_energy: SupportsFloat = 10.0, kvp: SupportsFloat = 100.0, anode_z: SupportsInt = 74, anode_angle: SupportsFloat = 13.0, spectrum_file: str = '', spatial_file: str = '', source_to_distribution: SupportsFloat = 0.68, source_to_filter: SupportsFloat = 7.0, source_to_detector: SupportsFloat = 14.0, filters: Sequence[tuple[SupportsFloat, SupportsFloat, Sequence[tuple[SupportsInt, SupportsFloat]]]] = [], histories: SupportsFloat = 100000.0, max_time: SupportsFloat = 600.0, detector_dx: SupportsFloat = 50.0, detector_dy: SupportsFloat = 50.0, xbins: SupportsInt = 100, ybins: SupportsInt = 100, ebins: SupportsInt = 200, threads: SupportsInt = 0, print_geometry: bool = False, seed_pair: SupportsInt = 0, tolerance: SupportsFloat = 0.01, user_geometry: dict = {}, geometry_materials: Sequence[tuple[SupportsFloat, Sequence[tuple[SupportsInt, SupportsFloat]]]] = [], only_check: bool = False, print_configuration: bool = False, verbose: SupportsInt = 1) tuple

Simulates an electron beam impinging on an anode and records the resulting photon spectrum and spatial distribution.

Parameters:
  • source_position (tuple[float, float, float]) – Position of the source in cm. This point is interpreted as the location at the anode where the beam impacts.

  • focal_spot (float) – Focal spot size of the beam in cm.

  • inherent_filter_width (float) – Width in cm for the inherent filter. Set to zero or a negative value to disable it.

  • min_energy (float) – Minimum photon energy to be recorded, in keV.

  • kvp (float) – Peak kilovoltage (kVp) of the beam.

  • anode_z (int) – Atomic number (Z) of the anode material.

  • anode_angle (float) – Angle of the anode in degrees.

  • spectrum_file (str) – Input file path to read the generated energy spectrum.

  • spatial_file (str) – Input file path to read the photon spatial distribution.

  • source_to_distribution (float) – Distance from source to the spatial distribution plane in cm.

  • source_to_filter (float) – Distance from source to the first filter in cm.

  • source_to_detector (float) – Distance from source to the detector in cm.

  • filters (list) – List of additional filters beyond the inherent one. Each filter should be defined as: (width_cm, density_g_cm3, [(Z1, fraction1), (Z2, fraction2), …])

  • histories (float) – Number of Monte Carlo histories (Beam electrons).

  • max_time (float) – Maximum simulation run time in seconds.

  • detector_dx (float) – Detector size in the X direction in cm.

  • detector_dy (float) – Detector size in the Y direction in cm.

  • xbins (int) – Number of bins (pixels) along the X axis.

  • ybins (int) – Number of bins (pixels) along the Y axis.

  • ebins (int) – Number of energy bins for the spectrum.

  • threads (int) – Number of threads to use. If 0, use all available CPU cores.

  • print_geometry (bool) – If True, saves the generated simulation geometry to a file.

  • seed_pair (int) – Seed pair index for RNG initialization. Must be less than 1001.

  • tolerance (float) – Desired relative error (error/value) for the simulation.

  • user_geometry (dict) – Configuration of additional geometry provided by the user.

  • geometry_materials (list) – List of extra materials used in the user-defined geometry. Each material should be defined as: (density, [(Z1, fraction1), (Z2, fraction2), …])

  • only_check (bool) – If True, validates the configuration and returns material count without running the simulation.

  • print_configuration (bool) – If True, saves the simulation configuration to a file named ‘simDevice.conf’.

  • verbose (int) – Verbosity level (higher values provide more output).

Returns:

if ‘only_check’ value is false, a tuple with the following information, in order:
  • Energy limits for the generated spectrum (emin, emax) in keV

  • A 1D numpy array with the detected gamma spectrum (prob/hist)

  • A 1D numpy array with the uncertainty for each gamma spectrum bin

  • X limits for spatial distribution (xmin, xmax) in cm

  • Y limits for spatial distribution (ymin, ymax) in cm

  • A 2D numpy array with the absorbed energy distribution, in eV, normalized per history. Notice that a perfect detector absorber is considered. The particle track is finished when the detector is reached.

  • A 2D numpy array with the uncertainty for each energy deposition distribution bin

  • A 2D numpy array with the detected gamma fluence distribution normalized per history

  • A 2D numpy array with the uncertainty for each fluence distribution bin

if ‘only_check’ is true, the number of materials used to construct the device geometry is returned as a tuple with only one element. This can be used to know the first avaible material index, for user defined geometries construction.

Return type:

tuple

Raises:

ValueError – Incompatible value has been provided.

Example

import matplotlib.pyplot as plt
import numpy as np
import pyPenred

results = pyPenred.simulation.xray.deviceSim(ebins=100, inherent_filter_width=0.15, anode_angle=16, source_to_detector=100.0, max_time=600, histories=1.0e8)

# Extract energy and spatial ranges
eLimits = results[0]
xLimits = results[3]
yLimits = results[4]

# Create X values for energy spectrum
e_plot = np.linspace(eLimits[0], eLimits[1], len(results[1]))

# Plot spectrum
plt.plot(e_plot, results[1])
plt.title("Device spectrum")
plt.xlabel("keV")
plt.ylabel("Prob/hist")
plt.savefig('device-spectrum.png')

plt.close()

# Plot energy deposition
plt.imshow(results[5],
           extent=[xLimits[0], xLimits[1], yLimits[0], yLimits[1]],
           aspect='auto',
           origin='lower',
           interpolation='none',
           cmap='viridis')

plt.title("Detector energy deposition")
plt.colorbar()
plt.xlabel("X cm")
plt.ylabel("Y cm")
plt.savefig('detector-energy-deposition.png')

plt.close()

# Plot fluence
plt.imshow(results[7],
           extent=[xLimits[0], xLimits[1], yLimits[0], yLimits[1]],
           aspect='auto',
           origin='lower',
           interpolation='none',
           cmap='viridis')

plt.title("Detected fluence")
plt.colorbar()
plt.xlabel("X cm")
plt.ylabel("Y cm")
plt.savefig('detector-fluence.png')