Tag: Heatmap

PySLM: Exposure Map Generation & Visualisation

A curious feature was seeing the possibility to both generate and visualise the exposure points generated in along scan vectors. This also includes generating a pseudo ‘heatmap’ or exposure map of the energy deposited by the laser scanning across the surface. Variations of this idea, I have seen across a variety of commercial software – in particular Renishaw’s QuantAM, which is perhaps the source of inspiration for coming up with an approach.

Usually, there is not much indication as the relative exposure of energy from the laser into a layer based both the chosen laser parameters applied to the scan vectors of a specific LayerGeometry group and also the overall spatial distribution of scan vectors in a region. For instance, overlaps between scan vectors will in theory deposit more energy across time into a localised region . Ideally, the exposure of energy from the laser is very uniform across the regions.

It is debatable whether there is particular usefulness of visualising the exposure map of the laser – but in practice can be used to differentiate laser scan parameters used on models in parametric studies. With further thought, a cheap numerical simulation could potentially capture the transient heat distribution generated by the sequential deposition of these exposure points.

A Light Background on Laser Exposure:

Hopefully someone can further comment on of this and perhaps correct me…

SLM systems are built using fiber laser systems offering a very fine, high quality controlled laser beam. Two types of laser operation exist: continuous wave (CW) and pulsed.

In practice, with pulsed lasers used in AM platforms, the scan vector is decomposed across a number of exposure points at a set ‘pointDistance’ from each other along the scan vector. Each exposure irradiates energy into the powder-bed surface at a fixed energy (inferred from the average laserPower) for a set time period referred as a ‘pointExposureTime‘. After exposure, the galvo-mirrors instantaneously move to direct the beam to the next exposure point. The relative speed this occurs optically gives the illusion that the laser is continuously moving. Continuous wave, as the name describes, emits continuous irradiation whilst the beam traverses the surface. The laser speed parameter is used here.

Different systems use either pulsed, continuous, or even both modes of operation, offering different benefits. This in principle is the reason for BuildStyle class containing the following parameters to cover both situations:

  • Point Exposure Time
  • Point Distance
  • Laser Speed
  • Laser Power – used across both modes

Ultimately, this does not matter, but the proposed method obviously requires the user to set at minimum the pointDistance parameter in order for the following methodology to work.

Proposed Methodology

Taking an existing Layer containing LayerGeometry, the exposure points are generated individually for each scan vector. Given the potential number of scan vectors within a layer, it would be beneficial to perform this efficiently.

PySLM: Island Hatching for SLM
Example part that is hatched with a series of overlapping 5mm island. The scan order is indicated by the blue line.

The basic process to do this is based on the following. The scan vector \textbf{v} has a particular length. Based on the point exposure distance, we can equally divide along this a number of points. Using both v_o and its direction ,we can then project a given number of exposure points along this line.

PySLM: definition of the laser hatch vectors and their decomposition into exposure points.
Definition of a hath vector and decomposing each scan vector into a series of exposure points.

An attempt to do this more efficiently and exploit vectorisation built into numpy was achieved by the following procedure. The following properties of each scan vector (\textbf{v}) is obtained:

  • start point (v_o)
  • normalised scan vector direction (\hat{\textbf{v}})
  • scan vector length \lVert \textbf{v} \rVert

For each LayerGeometry object, these are obtained across each scan vector as follows assuming that it is a HatchGeometry object:

# Calculate the length of the hatch vector and the direction
coords = layerGeom.coords.reshape(-1, 2, 2)
delta = np.diff(coords, axis=1).reshape(-1, 2)
lineDist = np.hypot(delta[:, 0], delta[:, 1]).reshape(-1, 1)

# Take the first coordinate
v0 = coords[:, 1, :].reshape(-1, 2)

# Normalise each scan vector direction
vhat = -1.0 * delta / lineDist

The number of point exposure across each vector is calculated by simple division. Unfortunately there is rounding, so the exposure may be missing at the end of the scan vector.

# Calculate the number of exposure points across the hatch vector based on its length
numPoints = np.ceil(lineDist / pointDistance).astype(np.int)

The total number of exposure points is calculated and is used to pre-populate some arrays, where the terms are used to extrapolate the exposure point distance (Line 16) are generated. Line 10 is key here and idxArray stores a range to account for each exposure point along the scan vector

Unfortunately, there isn’t a convenient way vectorise this so it is performed across each scan vector. Finally, the exposure points are generated by extrapolating them from v_o along \hat{\textbf{v}}.

# Pre-populate some arrays to extrapolate the exposure points from
totalPoints = int(np.sum(numPoints))
idxArray = np.zeros([totalPoints, 1])
pntsArray = np.zeros([totalPoints, 2])
dirArray = np.zeros([totalPoints, 2])

idx = 0
for i in range(len(numPoints)):
    j = int(numPoints[i])
    idxArray[idx:idx + j, 0] = np.arange(0, j)
    pntsArray[idx:idx + j] = p0[i]
    dirArray[idx:idx + j] = vhat[i]
    idx += j

# Calculate the hatch exposure points
hatchExposurePoints = pntsArray + pointDistance * idxArray * dirArray

This generates a set of exposure points, which can be seen in the figure below:

PySLM: Exposure points generated across the scan vectors between adjacent islands.
Exposure points plotted along each hatch vector are shown. Note the overlapping area between adjacent islands.

Once the exposure points are generated, the deposited energy (laserPower x pointExposureTime) is added to each point and stored as a third column for later use.

Exposure Map Generation

Generating the exposure map is trivial. For a selected resolution chosen by the user, the bitmap slice is used currently to get the image to process with. The exposure coordinates are simply mapped, given an offset and resolution onto the image. The deposited energy is then summed accordingly across each pixel. The energy per area (latex]Q[/latex]), is calculated by diving by the aerial coverage of a single pixel.

PySLM: A pseudo 'heatmap' or exposure Map showing the deposited energy for an island hatch region processed using SLM.
Exposure / Heat Map showing the deposited energy applied to the position and laser parameters used. Notice the overlap region between islands show a higher energy deposition.

The resolution is arbitrarily selected. The above figure is generated with a resolution of 0.2 mm. It can be seen in this example that the there is a greater energy deposition across the the overlap regions between the islands. There are some ‘aliasing’ effects due to discretion onto a finer grid which is dependent on the resolution chosen.


The above exposure map generation might have little scientific utility by itself. Rather it can offer a method to potentially visualise the energy deposition across a layer.

However, many thermo-mechanical AM build simulation that incorporate thermal effects in addition to the inherent strain approach could potentially incorporate a reference exposure map across the layer rather than assuming a single aerial heat-flux applied to the layer. This could actually be carried out across a ‘packet’ or number of layers to better correspond with the geometry and scan strategies employed.

Documented Example

The code for this can be found in examples/example_heatmap.py on the github repository.