Wide field detector set up

62 views
Skip to first unread message

Xiuxiu Zhang

unread,
Nov 5, 2025, 10:12:02 AM11/5/25
to mmc-users

Dear Dr. Qianqian Fang and mmcusers,

Thank you for developing and continuously improving the MMC software suite. I was recently introduced to it and have been exploring the example codes.

While reviewing demo_wide_det.m, I noticed two sections related to the detector setup:

  1. detdef = struct('srctype', 'planar', 'srcpos', [10, 10, -1], 'srcdir', [0 0 1], ...

                'srcparam1', [40 0 0 40], 'srcparam2', [0 40 0 40]);

[cfg.node, cfg.elem] = mmcadddet(node, elem, detdef);

  1. cfg.detpos = [10.0, 10.0, -1.0, 0];

cfg.detparam1 = [40.0 0.0 0.0 40];

cfg.detparam2 = [0.0 40.0 0.0 40];

I was wondering what the difference is between these two definitions. I also noticed that cfg.saveexit is set to 2 in this demo, and when I tested it, the resulting detphoton size appeared to depend on the definition in section (2).

Lastly, could you please clarify what distinguishes a wide-field detector from a normal planar detector in this context?

Thank you very much for your time and for creating such a powerful and well-documented tool.

Best regards,

Xiuxiu

Qianqian Fang

unread,
Nov 10, 2025, 12:57:03 PM11/10/25
to mmc-...@googlegroups.com, Xiuxiu Zhang

hi Xiuxiu, sorry for the delay

see my replies below

On 11/5/25 09:28, Xiuxiu Zhang wrote:

You don't often get email from zha...@seas.upenn.edu. Learn why this is important

Dear Dr. Qianqian Fang and mmcusers,

Thank you for developing and continuously improving the MMC software suite. I was recently introduced to it and have been exploring the example codes.


supporting widefield source (i.e. photons are launched or received from outside of the target domain) or detector is more complex in mmc than mcx. 

in mmc, the mesh must be modified in order to cover the source or detector if they are located outside of the object mesh. for details, please see this previous BOE paper

https://opg.optica.org/boe/fulltext.cfm?uri=boe-7-1-171

we call this mesh modification (extending from the domain's original mesh to include the source/detector aperture) as "mesh retessellation" - this is a required pre-processing step for all widefield mmc simulations.


While reviewing demo_wide_det.m, I noticed two sections related to the detector setup:

  1. detdef = struct('srctype', 'planar', 'srcpos', [10, 10, -1], 'srcdir', [0 0 1], ...

                'srcparam1', [40 0 0 40], 'srcparam2', [0 40 0 40]);

[cfg.node, cfg.elem] = mmcadddet(node, elem, detdef);


the above two lines performs the mesh retesellation - and returns the extended mesh in cfg.node and cfg.elem. you can plot them before (plotmesh(node, elem)) and after (plotmesh(cfg.node, cfg.elem)) this step to see the difference.

a side note, for wide field sources, you call mmcaddsrc().


  1. cfg.detpos = [10.0, 10.0, -1.0, 0];

cfg.detparam1 = [40.0 0.0 0.0 40];

cfg.detparam2 = [0.0 40.0 0.0 40];


these tells mmc simulation about the detector position and x/y spans. These parameters must match the parameter used for extending the mesh above.


I was wondering what the difference is between these two definitions.


you need both steps for widefield detector to work


I also noticed that cfg.saveexit is set to 2 in this demo, and when I tested it, the resulting detphoton size appeared to depend on the definition in section (2).


if you don't set cfg.saveexit to 2, all individual photons arriving at the above wideifeld detector's "aperture" will be captured and stored as the "detected photon" data, but for widefield detectors, this file can be huge - even you can save those, you still need to load them and post-process.

when setting cfg.saveexit to 2, this triggers mmc to use a simplified output mode - all photons arriving at the detectors are binned and summed to output a 2D array of intensities, as if it is a CCD/CMOS detector.

an alternative to cfg.saveexit=2 is to use the diffuse reflectance output on the domain surface by setting cfg.issaveref=1 - in this case, you obtain light intensities on all surface triangles,

https://github.com/fangq/mmc/blob/v2025.10/mmclab/mmclab.m#L161-L167

this does not require you to add a widefield detector. how you use this surface light intensity to obtain any widefield detector readings using integration as postprocessing, or if an integration is overkill (which is often the case), you can simply interpolate the surface diffuse reflectance at the detector pixel projection on the surface.


Lastly, could you please clarify what distinguishes a wide-field detector from a normal planar detector in this context?


in our software, we call any detectors (or sources) that are located outside of the target domain (voxel, mesh) as "widefield". for example, even a pencil beam that is launching photon from outside of the domain is a widefield source.

this definition is not necessarily translatable to other context, but we use it consistently in our tools/documentations.

let me know if these explanations help


Qianqian


Thank you very much for your time and for creating such a powerful and well-documented tool.

Best regards,

Xiuxiu

--
You received this message because you are subscribed to the Google Groups "mmc-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mmc-users+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mmc-users/a809158b-39f3-4499-bf95-5fa46c9ae833n%40googlegroups.com.

Xiuxiu Zhang

unread,
Nov 10, 2025, 1:25:04 PM11/10/25
to mmc-users
Thanks for your reponse, Dr. Fang. 

when setting cfg.saveexit to 2, this triggers mmc to use a simplified output mode - all photons arriving at the detectors are binned and summed to output a 2D array of intensities, as if it is a CCD/CMOS detector.

Is there any parameter I could adjust so that the output array has finer resolution- pixels were smaller? Instead of 40*40 in this case, for instance, 80*80?

Thanks,
Xiuxiu

Qianqian Fang

unread,
Nov 10, 2025, 6:06:48 PM11/10/25
to mmc-...@googlegroups.com, Xiuxiu Zhang

I realized that this information is never explained in the demo.

I just added an explanation on how to set the pxiel count (via detparam1(4) and detparam2(4)), see this commit

https://github.com/fangq/mmc/commit/f65b5a79f60bf7017a047cc93c97d7561f686c04

the help info is copied below:


%      cfg.detparam1:   a 4-element vector: [u1, u2, u3, Nx], define the x-axis of a widefield
%                       detector, [u1,u2,u3] is the x-axis vector; Nx is the pixel count in the x-axis
%      cfg.detparam2:   a 4-element vector: [v1, v2, v3, Ny], define the y-axis of a widefield
%                       detector, [v1,v2,v3] is the y-axis vector; Ny is the pixel count in the y-axis

%      cfg.issaveexit: [0]-whether to save the position (x,y,z) and (vx,vy,vz) for a detected photon
%                      When issaveexit is set to 2, this signals mmclab to output a 2D array
%                      a widefield detector defined by cfg.detparam1 and cfg.detparam2.
%                      Specifically, instead of output individual detected photon data,
%                      the exiting weight of all detected photons are binned and summed
%                      into a detparam1(4)-by-detparam2(4) pixelated output, covering
%                      the quadrilateral area with 4 corners defined by (only 1:3 elem are used)
%                      [detpos, detpos+detparam1, detpos+detparam1+detaram2, detpos+detparam2].
%                      When a widefield detector is used, detector radii are ignored.

Xiuxiu Zhang

unread,
Mar 5, 2026, 12:17:59 PM (10 days ago) Mar 5
to mmc-users
Dear Dr. Fang, 

Following up on the widefield detector setup, I have a few questions:

1. For cfg.detparam1 and cfg.detparam2, do the Nx and Ny components need to be specified when using cfg.issaveexit = 1?

2. When I generate a mesh box using
[node, face, elem] = meshabox([0 0 0],[20 20 20],0.05,1)

it seems that I am unable to add a widefield detector with
detdef = struct('srctype', 'planar', 'srcpos', [0, 0, 21], 'srcdir', [0 0 -1], ...
                'srcparam1', [20 0 0 20], 'srcparam2', [0 20 0 20]);

[cfg.node, cfg.elem] = mmcadddet(node, elem, detdef);


In this case, plotmesh and mmclab do not run or produce any results. However, when I generate the mesh using
[node, face, elem] = latticegrid([0 20], [0 20], [0 20]);
c0(:, 4) = [0.05];
[node, elem] = surf2mesh(node, face, [], [], 1, [], c0);


the mesh works and the simulation runs normally. Is there a reason why meshabox behaves differently in this context?

3. After setting up the widefield detector and running the simulation, the generated detid values are 305158 and 305276. Since I only defined one detector, I expected the detector ID to be 1. Could you explain why these large detector IDs appear?

Looking forward to your response. Thanks!

Best regards,
Xiuxiu

Qianqian Fang

unread,
Mar 6, 2026, 1:27:52 PM (9 days ago) Mar 6
to mmc-...@googlegroups.com, Xiuxiu Zhang

hi Xiuxiu,

see my replies below


On 3/5/26 12:17, Xiuxiu Zhang wrote:
Dear Dr. Fang, 

Following up on the widefield detector setup, I have a few questions:

1. For cfg.detparam1 and cfg.detparam2, do the Nx and Ny components need to be specified when using cfg.issaveexit = 1?


widefield detection is only triggered when setting cfg.issaveexit=2, see the demo_wide_det.m sample script under mmclab/example

if you set cfg.issaveexit=2, then yes, both cfg.detparam1(4) and cfg.detparam2(4) must be set as Nx and Ny to determine the number of pixels of the widefield detector



2. When I generate a mesh box using
[node, face, elem] = meshabox([0 0 0],[20 20 20],0.05,1)

it seems that I am unable to add a widefield detector with
detdef = struct('srctype', 'planar', 'srcpos', [0, 0, 21], 'srcdir', [0 0 -1], ...
                'srcparam1', [20 0 0 20], 'srcparam2', [0 20 0 20]);
[cfg.node, cfg.elem] = mmcadddet(node, elem, detdef);


In this case, plotmesh and mmclab do not run or produce any results. However, when I generate the mesh using
[node, face, elem] = latticegrid([0 20], [0 20], [0 20]);
c0(:, 4) = [0.05];
[node, elem] = surf2mesh(node, face, [], [], 1, [], c0);


the mesh works and the simulation runs normally. Is there a reason why meshabox behaves differently in this context?


the difference is in the label column of elem - when using surf2mesh with c0 as seeds, the output elem contains a 5th column, with label 1 for all elements.

in comparison, the elem produced by meshabox does not automatically label the elements, therefore only has 4 columns.

because mmcadddet calls the meshrefine() function in iso2mesh, which is a general-purpose mesh refinement function, it does not make mm-specific assumption on mmc mesh labels - if the input mesh to mmcadddet has the 5 column, mmcadddet will properly tag the refined elements covering the widefield det with the correct label (-2); but if your input mesh has only 4 columns, it does not add the needed det/src element labels (-1/-2).


to fix your meshabox based input, you just need to add

elem(:,5)=1

it should produce the same output as the latticegrid one.

I also committed this change to force it to add labels - although it could be problematic if the domain's base label is not 1

https://github.com/fangq/mmc/commit/4cbf7b1b289cd71833cd54c8caf6725a6db0a689



3. After setting up the widefield detector and running the simulation, the generated detid values are 305158 and 305276. Since I only defined one detector, I expected the detector ID to be 1. Could you explain why these large detector IDs appear?


when you run mmclab with issaveexit=2, the 2nd output, detp, should only have a single field, data, which is a 3D array (Nx by Ny by Nt),

where do you see detID?


Xiuxiu Zhang

unread,
Mar 6, 2026, 6:09:32 PM (9 days ago) Mar 6
to mmc-users
Hi Dr. Fang, 

From your message on 11.10, 
"if you don't set cfg.saveexit to 2, all individual photons arriving at the above wideifeld detector's "aperture" will be captured and stored as the "detected photon" data, but for widefield detectors, this file can be huge - even you can save those, you still need to load them and post-process." 

My interpretation was that I could therefore add a widefield detector using mmcadddet and set cfg.issaveexit = 1 in order to obtain the pathlengths of the detected photons. That is why I currently have detp with detid values.

If I instead set cfg.issaveexit = 2 so that the widefield detection runs properly, is there still a way to retrieve the pathlengths of all detected photons?

Thanks,
Xiuxiu
Reply all
Reply to author
Forward
0 new messages