ShapeMesh

New Members

class ShapeMesh(config=None, strict=None, initialize=None)[source]

Bases: xicsrt.optics._ShapeObject.ShapeObject

A shape that uses a mesh grid instead of an analytical shape.

Programming Notes

Raytracing of mesh optics is fundamentally slow, because of the need to find which mesh face is intersected by each ray. For the simplest implementations this requires testing each ray against each mesh face leading to the speed scaling as the number of mesh faces (or equivilently mesh_density^2).

Some optimization of the basic calculation been completed. The mesh_intersect_1 method implements the Möller–Trumbore algorithm and is the fastest pure python algorithm found so far. Other variations that have been tried are saved in the archive folder, along with some documentation on performance.

To further improve performance this class (optionally) also makes use of pre-selection with a coarse mesh. First the intersection with the coarse mesh is found for each ray. Then only the 8 nearby faces on the full mesh are checked for the final intersection location. This method improves the performance to (num_faces_coarse + 8).

The current algorithm for pre-selection (mesh refinement) is not perfect in that the nearby faces are not always appropriately chosen leading to a small number of rays being ‘lost’. These errors can be minimized by increasing the resolution of the coarse mesh and ensuring that the grid spacing is approximately equal in the x and y directions.

Further performance improvement could be gained by using numba or jax. This would allow the Möller–Trumbore algorithm to be implemented as a loop (instead of being vectorized) where the calculation can be terminated early when no hit is found.

Todo

XicsrtOpticMesh: Improve the pre-selection (mesh refinement algorithm) to eliminate ray losses. The current method is as follows:

  1. Calculate intersection with coarse grid.
  2. Find the point on the fine grid closest to the intersection.
  3. Test all faces on the fine grid that contain this point.

The problem is that the closest point may not always be part of the face that actually has the intersection. This can happen if the fine and coarse grid have very different densities, but also even in the perfect case if the ray hits near the edge of a face and the grid density is not even in the x and y directions.

What is needed is a better selection of nearby faces. There is also a potential to improve computational speed slightly by testing fewer faces on the fine grid.

Configuration Options:

mesh_points

mesh_faces

mesh_normals

mesh_coarse_points

mesh_coarse_faces

mesh_coarse_normals

mesh_interpolate

mesh_refine

xsize
The size of this element along the xaxis direction. Typically corresponds to the ‘width’ of the optic.
ysize
The size of this element along the yaxis direction. Typically corresponds to the ‘height’ of the optic.
zsize
The size of this element along the zaxis direction. Typically not required, but if needed will correspond to the ‘depth’ of the optic.
pixel_size : float (None)
The pixel size, used for binning rays into images. This is currently a single number signifying square pixels.
aperture : dict or array (None)
Define one or more apertures to to apply to this optic. Each aperture is defined as a dictionary with the following keys: shape, size, origin, logic. The origin and logic field keys are optional. The interpretation of size will depend on the provided shape.
trace_local : bool (False)
If true: transform rays to optic local coordinates before raytracing, do raytracing in local coordinates, then transform back to global coordinates. The default is ‘false’ as most built-in optics can perform raytracing in global coordinates. This option is convenient for optics with complex geometry for which intersection and reflection equations are easier or more clear to program in fixed local coordinates.
check_size : bool (true)
Perform a check for whether the rays intersect the optic within the defined bounds (usually defined by ‘xsize’ and ‘ysize’). If set to False all rays with a defined reflection/transmission condition will be traced if an intersection can be determined.
check_aperture : bool (true)
Perform a check for whether the rays intersect the optic within the defined bounds (usually defined by ‘xsize’ an ‘ysize’). If set to False all rays with a defined reflection/transmission condition will be traced if an intersection can be determined.
filters
No documentation yet. Please help improve XICSRT!
origin
The x,y,x origin of this element in global coordinates.
zaxis
A unit-vector defining the z-axis of the element in global coordinates. For most optics: z-axis defines the surface normal direction.
xaxis : (optional)
A unit-vector defining the x-axis of the element in global coordinates. For most optics: x-axis defines the ‘width’ direction. If xaxis is not provided it will be automatically generated by: cross(zaxis, [0,1,0]). The yaxis is always automatically generated and defined by: cross(zaxis, xaxis)
class_name
Automatically generated.
yo_mama
Is a wonderful person!
default_config()[source]

mesh_points mesh_faces mesh_normals

mesh_coarse_points mesh_coarse_faces mesh_coarse_normals

mesh_interpolate mesh_refine

check_param()[source]

Check the internal parameters prior to initialization. This will be called after setup and before initialize.

initialize()[source]

Initialize the object.

intersect(rays)[source]

Calculate ray intersections with the mesh.

mesh_interpolate(X, mesh, mask)[source]
mesh_initialize()[source]

Pre-calculate a number of mesh properties that are needed in the other mesh methods.

mesh_intersect_1(rays, mesh)[source]

Find the intersection of rays with the mesh using the Möller–Trumbore algorithm.

mesh_intersect_2(rays, mesh, mask, faces_idx, faces_mask)[source]

Check for ray intersection with a list of mesh faces.

Programming Notes

Because of the mesh indexing, the arrays here have different dimensions than in mesh_intersect_1, and need a different vectorization.

At the moment I am using an less efficient mesh intersection method. This should be updated to use the same method as mesh_intersect_1, but with the proper vectorization.

mesh_normals(hits, mesh, mask)[source]
mesh_get_index(hits, faces)[source]

Match faces to face indexes, with a loop over faces.

find_point_faces(p_idx, faces, mask=None)[source]

Find all of the the faces that include a given mesh point.

find_near_faces(X, mesh, mask)[source]

New Private Members

class ShapeMesh[source]
_mesh_precalc(points, normals, faces)[source]

Inherited Members

class ShapeMesh[source]
__init__(config=None, strict=None, initialize=None)

Initialize self. See help(type(self)) for accurate signature.

_mesh_precalc(points, normals, faces)[source]
aim_to_point(aim_point, xaxis=None)

Set the Z-Axis to aim at a particular point.

check_aperture(X_local, mask)

Check if the ray intersection is within the aperture as set by the ‘aperture’ config option.

Note

This method expects to be given the ray intersections in local coordinates. Generally this method should not be called directly instead use check_bounds.

check_bounds(X, mask)
check_config()

Check the config before copying to the internal param. This is called during object instantiation (__init__) and therefore before setup is called.

check_param()[source]

Check the internal parameters prior to initialization. This will be called after setup and before initialize.

check_size(X_local, mask)

Check if the ray intersection is within the optic bounds as set by the xsize, ysize and zsize config options.

Note

This method expects to be given the ray intersections in local coordinates. Generally this method should not be called directly, instead use check_bounds.

default_config()[source]

mesh_points mesh_faces mesh_normals

mesh_coarse_points mesh_coarse_faces mesh_coarse_normals

mesh_interpolate mesh_refine

find_near_faces(X, mesh, mask)[source]
find_point_faces(p_idx, faces, mask=None)[source]

Find all of the the faces that include a given mesh point.

get_config()
get_default_xaxis(zaxis)

Get the X-axis using a default definition.

In order to fully define the orientation of a component both, a z-axis and an x-axis are expected. For certain types of components the x-axis definition is unimportant and can be defined using a default definition.

initialize()[source]

Initialize the object.

interact(rays, xloc, norm, mask)
intersect(rays)[source]

Calculate ray intersections with the mesh.

intersect_location(rays)

Calculate the surface location at the ray intersections.

This base-class just returns a copy of the ray origin.

intersect_normal(xloc, mask)

Calculate the surface normal at the ray intersection locations.

Normals are not defined for this base-class; an array of np.nan will always be returned.

location_from_distance(rays, dist, mask=None)

Calculate 3D locations given a distance along the rays.

make_image(rays)

Collect the rays that intersect with this optic into a pixel array that can be used to generate an intersection image.

Programming Notes

It is important that this calculation is compatible with intersect_check in terms of floating point errors. The simple way to achieve this is to ensure that both use the same calculation method.

mesh_get_index(hits, faces)[source]

Match faces to face indexes, with a loop over faces.

mesh_initialize()[source]

Pre-calculate a number of mesh properties that are needed in the other mesh methods.

mesh_interpolate(X, mesh, mask)[source]
mesh_intersect_1(rays, mesh)[source]

Find the intersection of rays with the mesh using the Möller–Trumbore algorithm.

mesh_intersect_2(rays, mesh, mask, faces_idx, faces_mask)[source]

Check for ray intersection with a list of mesh faces.

Programming Notes

Because of the mesh indexing, the arrays here have different dimensions than in mesh_intersect_1, and need a different vectorization.

At the moment I am using an less efficient mesh intersection method. This should be updated to use the same method as mesh_intersect_1, but with the proper vectorization.

mesh_normals(hits, mesh, mask)[source]
point_to_external(point_local)
point_to_local(point_external)
ray_to_external(ray_local, copy=False)
ray_to_local(ray_external, copy=False)
set_orientation(zaxis, xaxis=None)
setup()

Perform any setup actions that are needed prior to initialization.

to_ndarray(vector_in)
to_vector_array(vector_in)

Convert a vector to a numpy vector array (if needed).

trace(rays)

The main method that performs raytracing for this optic.

Raytracing here may be done in global or local coordinates depending on the how the optic is designed and the value of the configuration option: ‘trace_local’.

trace_global(rays)

This is method that is called by the dispacher to perform ray-tracing for this optic. Rays into and out of this method are always in global coordinates.

It may be convenient for some optics object to do raytracing in local coordinates rather than in global coordinates. This method facilitates this by implementing the ‘trace_local’ configuration option.

update_config(config_new, **kwargs)

Overwrite any config values in this object with the ones given. This will be done recursively for all nested dictionaries.

vector_to_external(vector)
vector_to_local(vector)