Koehler source

13 views
Skip to first unread message

asassa01

unread,
Nov 14, 2025, 9:49:59 PMNov 14
to mcx-...@googlegroups.com
Dear Qianqian,
is it available on MCX this type of source?

image.png

A planar source (either rectangular or circular) where each point emits in a cone.
Thanks!
Angelo

Qianqian Fang

unread,
Nov 14, 2025, 11:47:07 PMNov 14
to mcx-...@googlegroups.com, asassa01

hi Angelo,

you can use cfg.angleinvcdf in combination with an area source to control the launch angular distribution - for a uniform cone with an half-angle of pi/4, you can do this

cfg.angleinvcdf = linspace(0, 1 / 4, 5); % launch angle is uniformly distributed between [0 and pi/4] with interpolation (odd-number length)

this is similar to this built-in example

https://github.com/fangq/mcx/blob/v2025.10/mcxlab/examples/demo_mcxlab_launchangle.m#L26-L32

when cfg.angleinvcdf has odd number element, launch angles are linearly interpolated between each angle value in the vector, creating a continuous distribution

when cfg.angleinvcdf has even number of element, each angle element in the vector are sampled without interpolation.

Qianqian

--
You received this message because you are subscribed to the Google Groups "mcx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mcx-users+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mcx-users/CAJNg7ozsqRNiL-kiCgfO5LrTAMPYorHNkE4pviQGu4jj7jhqsA%40mail.gmail.com.

Qianqian Fang

unread,
Nov 15, 2025, 3:13:47 PMNov 15
to asassa01, mcx-...@googlegroups.com

hi Angelo,

there are two mechanism controlling the launch angles.


1. Mechanism 1: cfg.srcdir(4)

the 4th element of srcdir defines the focal length if the source is an area source (disk, planar, fourier, gaussian, pattern ...), positive focal length makes a converging beam, negative makes a diverging beam; 

there are two special settings: if srcdir(4) = nan, the launch angles follows isotropic distribution; if srcdir(4)=-inf, it follows the Lambertian distribution (cosine); of course, if srcdir(4) = inf, it is a collimated beam just based on focal length

srcdir(4) only controls the launch angles for area sources, has no effect to point source (pencil, isotropic, cone, zgaussian). 

Mechanism 1 is relatively simple, easy to use, but has limited control.


2. Mechanism 2: cfg.angleinvcdf

cfg.angleinvcdf asks users to manually specify the distribution of the launch angle (zenith angle relative to srcdir(1:3)) via a discretized inverse cumulative distribution function (CDF) of the zenith angle. 

angleinvcdf applies to all source types, regardless it is an area or point source; when angleinvcdf is used, Mechanism 1 above is disabled.

angleinvcdf is a 1D vector  [ a1, a2, a3, ...a_N ] contains normalized (0-1 value, 1 means Pi) monotonically increasing angles

when angleinvcdf is used to generate launch angles, mcx first produces a 0-1 uniformly distributed random variable U, and map it to the full vector indices [0, (N - 1)] and use it to lookup the value in angleinvcdf to determine the angle to be launched. there are two ways to map U to the angle values:


1. when N is an odd number, the launch angle is interpolated between the two adjacent angles on either side of U, producing continuous distributions;

for example, if angleinvcdf=[0.2, 0.5, 0.8], where N=3, if U=0.25, converting to the index axis, we get index = 0.25*(3-1) = 0.5, you can see index 0.5 means it is half-way between the first (idx=0) and the 2nd element (idx=1), so the launch angle is determined to be theta = (0.2*0.5)/2*pi = 0.35pi; if U=0.2, then, we have index=0.4, we need to interpolate the 1st/2nd element using a 6:4 ratio, i.e. theta = (0.6*0.2 + 0.4*0.5) * pi = 0.32*pi

based on this method, if your anginvcdf is produced by a linspace() of odd number length, your distribution stays exactly the same as long as the linspace length is an odd number.


2. when N is an even number, we just round-down the index and use it to get the angleinvcdf element as the angle. this produces an discrete angular distribution.

for example, if angleinvcdf = [0.2, 0.3, 0.4, 0.4], when mapping 0-1 distributed U to the index axis, we see that 50% of the chance the photon will launch at 0.4*pi, 25% of the chance will launch at 0.2*pi, and 25% of the chance will launch at 0.3*pi. again, this only determines the zenith angle theta; the azimuth angle phi is always a 0-2*pi uniformly distributed random angle. combing these two angles, angleinvcdf=[0.2,0.3,0.5,0.5] will produce 3 concentric cone-shaped light sheets, with the 0.4*pi cone taking 50% of the brightness, and the other two cones taking 1/4 of the total brightness.


regardless which mechanism you use, all the computed launch angles are zenith angles relative to the user specified source direction: srcdir(1:3). You can steer the beam by changing srcdir(1:3), and all the above settings will keep its beam profile but steered following srcdir.


let me know if this is clear.


Qianqian


On 11/15/25 07:37, asassa01 wrote:
Thanks so much! My bad, I missed to look at this part in the documentation of MCX!!
So, if I want a "DISK" source with a given radius (the radius is set by srcparam1(1) (in grid unit)) and where each point in the disk emits in a uniform half cone (as in your example) with z axis, I must define cfg.srcdir=[0 0 1], right? (no 4th element). If I want the cone with a different axis, I should define a different cfg.srcdir accordingly, right? Also, in your example the number "5" controls the interpolation, and can be chosen as a different odd number?
Thanks!!
Angelo

Qianqian Fang

unread,
Nov 15, 2025, 5:00:38 PMNov 15
to mcx-...@googlegroups.com, asassa01

sorry, I have one correction and one clarification:


first, the below statements were actually incorrect.

1. when N is an odd number, the launch angle is interpolated
2. when N is an even number, we just round-down the index and use it to get the angleinvcdf element as the angle.

using even/odd length of angleinvcdf to decide interpolation or discrete angle was the first implementation, but shortly it was replaced by using cfg.srcdir(4) = 0 for interpolation, and 1 for discrete angle, added in this commit

https://github.com/fangq/mcx/commit/ca1bf2b9da826994d7d1be12f5aa6c4ec4aa1076


because Mechanism 1 was disabled when cfg.angleinvcdf is defined, so cfg.srcdir(4) was re-purposed to control interpolation - it is more intuitive compared to using odd/even length.



regardless which mechanism you use, all the computed launch angles are zenith angles relative to the user specified source direction: srcdir(1:3). 


the above statement is not exactly correct. for most sources where the source-determined launch angle follows srcdir(1:3), the above statement is true.

however, some sources requires to compute a new direction at launch, including cone, zgaussian, isotropic, arcsine, and hyperboloid, line and slit (when launchsrc->param2.{x,y}>0). 

for these sources, the angleinvcdf-determined zenith angle is actually applied on top of the randomly sampled source-induced launch directions.

for example, if you define cfg.angleinvcdf for a cone beam, the launch vector is first randomly sample the uniform cone based on the source, then, it further steer based on angleinvcdf. it is really a convolution of the two effects.

Reply all
Reply to author
Forward
0 new messages