ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/pmapcontrib.c
Revision: 2.15
Committed: Thu Feb 8 19:55:02 2018 UTC (7 years, 2 months ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.14: +6 -4 lines
Log Message:
Made photon primary incident dirs optional via PMAP_PRIMARYDIR, bumped PMAP_FILEVER

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pmapcontrib.c,v 2.14 2017/08/14 21:12:10 rschregle Exp $";
3 #endif
4
5 /*
6 ======================================================================
7 Photon map for light source contributions
8
9 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
10 (c) Lucerne University of Applied Sciences and Arts,
11 supported by the Swiss National Science Foundation (SNSF, #147053)
12 ======================================================================
13
14 $Id: pmapcontrib.c,v 2.14 2017/08/14 21:12:10 rschregle Exp $
15 */
16
17
18 #include "pmapcontrib.h"
19 #include "pmapmat.h"
20 #include "pmapsrc.h"
21 #include "pmaprand.h"
22 #include "pmapio.h"
23 #include "pmapdiag.h"
24 #include "rcontrib.h"
25 #include "otypes.h"
26 #if NIX
27 #include <sys/mman.h>
28 #include <sys/wait.h>
29 #endif
30
31
32 static PhotonPrimaryIdx newPhotonPrimary (PhotonMap *pmap,
33 const RAY *primRay,
34 FILE *primHeap)
35 /* Add primary ray for emitted photon and save light source index, origin on
36 * source, and emitted direction; used by contrib photons. The current
37 * primary is stored in pmap -> lastPrimary. If the previous primary
38 * contributed photons (has srcIdx >= 0), it's appended to primHeap. If
39 * primRay == NULL, the current primary is still flushed, but no new primary
40 * is set. Returns updated primary counter pmap -> numPrimary. */
41 {
42 if (!pmap || !primHeap)
43 return 0;
44
45 /* Check if last primary ray has spawned photons (srcIdx >= 0, see
46 * newPhoton()), in which case we save it to the primary heap file
47 * before clobbering it */
48 if (pmap -> lastPrimary.srcIdx >= 0) {
49 if (!fwrite(&pmap -> lastPrimary, sizeof(PhotonPrimary), 1, primHeap))
50 error(SYSTEM, "failed writing photon primary in newPhotonPrimary");
51
52 pmap -> numPrimary++;
53 if (pmap -> numPrimary > PMAP_MAXPRIMARY)
54 error(INTERNAL, "photon primary overflow in newPhotonPrimary");
55 }
56
57 /* Mark unused with negative source index until path spawns a photon (see
58 * newPhoton()) */
59 pmap -> lastPrimary.srcIdx = -1;
60
61 if (primRay) {
62 FVECT dvec;
63
64 #ifdef PMAP_PRIMARYDIR
65 /* Reverse incident direction to point to light source */
66 dvec [0] = -primRay -> rdir [0];
67 dvec [1] = -primRay -> rdir [1];
68 dvec [2] = -primRay -> rdir [2];
69 pmap -> lastPrimary.dir = encodedir(dvec);
70 #endif
71 #ifdef PMAP_PRIMARYPOS
72 VCOPY(pmap -> lastPrimary.pos, primRay -> rop);
73 #endif
74 }
75
76 return pmap -> numPrimary;
77 }
78
79
80
81 #ifdef DEBUG_PMAP
82 static int checkPrimaryHeap (FILE *file)
83 /* Check heap for ordered primaries */
84 {
85 Photon p, lastp;
86 int i, dup;
87
88 rewind(file);
89 memset(&lastp, 0, sizeof(lastp));
90
91 while (fread(&p, sizeof(p), 1, file)) {
92 dup = 1;
93
94 for (i = 0; i <= 2; i++) {
95 if (p.pos [i] < thescene.cuorg [i] ||
96 p.pos [i] > thescene.cuorg [i] + thescene.cusize) {
97
98 sprintf(errmsg, "corrupt photon in heap at [%f, %f, %f]\n",
99 p.pos [0], p.pos [1], p.pos [2]);
100 error(WARNING, errmsg);
101 }
102
103 dup &= p.pos [i] == lastp.pos [i];
104 }
105
106 if (dup) {
107 sprintf(errmsg,
108 "consecutive duplicate photon in heap at [%f, %f, %f]\n",
109 p.pos [0], p.pos [1], p.pos [2]);
110 error(WARNING, errmsg);
111 }
112 }
113
114 return 0;
115 }
116 #endif
117
118
119
120 static PhotonPrimaryIdx buildPrimaries (PhotonMap *pmap, FILE **primaryHeap,
121 char **primaryHeapFname,
122 PhotonPrimaryIdx *primaryOfs,
123 unsigned numHeaps)
124 /* Consolidate per-subprocess photon primary heaps into the primary array
125 * pmap -> primaries. Returns offset for primary index linearisation in
126 * numPrimary. The heap files in primaryHeap are closed on return. */
127 {
128 PhotonPrimaryIdx heapLen;
129 unsigned heap;
130
131 if (!pmap || !primaryHeap || !primaryOfs || !numHeaps)
132 return 0;
133
134 pmap -> numPrimary = 0;
135
136 for (heap = 0; heap < numHeaps; heap++) {
137 primaryOfs [heap] = pmap -> numPrimary;
138
139 if (fseek(primaryHeap [heap], 0, SEEK_END) < 0)
140 error(SYSTEM, "failed photon primary seek in buildPrimaries");
141 pmap -> numPrimary += heapLen = ftell(primaryHeap [heap]) /
142 sizeof(PhotonPrimary);
143
144 pmap -> primaries = realloc(pmap -> primaries,
145 pmap -> numPrimary *
146 sizeof(PhotonPrimary));
147 if (!pmap -> primaries)
148 error(SYSTEM, "failed photon primary alloc in buildPrimaries");
149
150 rewind(primaryHeap [heap]);
151 if (fread(pmap -> primaries + primaryOfs [heap], sizeof(PhotonPrimary),
152 heapLen, primaryHeap [heap]) != heapLen)
153 error(SYSTEM, "failed reading photon primaries in buildPrimaries");
154
155 fclose(primaryHeap [heap]);
156 unlink(primaryHeapFname [heap]);
157 }
158
159 return pmap -> numPrimary;
160 }
161
162
163
164 /* Defs for photon emission counter array passed by sub-processes to parent
165 * via shared memory */
166 typedef unsigned long PhotonContribCnt;
167
168 /* Indices for photon emission counter array: num photons stored and num
169 * emitted per source */
170 #define PHOTONCNT_NUMPHOT 0
171 #define PHOTONCNT_NUMEMIT(n) (1 + n)
172
173
174
175 void distribPhotonContrib (PhotonMap* pm, unsigned numProc)
176 {
177 EmissionMap emap;
178 char errmsg2 [128], shmFname [PMAP_TMPFNLEN];
179 unsigned srcIdx, proc;
180 int shmFile, stat, pid;
181 double *srcFlux, /* Emitted flux per light source */
182 srcDistribTarget; /* Target photon count per source */
183 PhotonContribCnt *photonCnt; /* Photon emission counter array */
184 unsigned photonCntSize = sizeof(PhotonContribCnt) *
185 PHOTONCNT_NUMEMIT(nsources);
186 FILE **primaryHeap = NULL;
187 char **primaryHeapFname = NULL;
188 PhotonPrimaryIdx *primaryOfs = NULL;
189
190 if (!pm)
191 error(USER, "no photon map defined in distribPhotonContrib");
192
193 if (!nsources)
194 error(USER, "no light sources in distribPhotonContrib");
195
196 if (nsources > MAXMODLIST)
197 error(USER, "too many light sources in distribPhotonContrib");
198
199 /* Allocate photon flux per light source; this differs for every
200 * source as all sources contribute the same number of distributed
201 * photons (srcDistribTarget), hence the number of photons emitted per
202 * source does not correlate with its emitted flux. The resulting flux
203 * per photon is therefore adjusted individually for each source. */
204 if (!(srcFlux = calloc(nsources, sizeof(double))))
205 error(SYSTEM, "can't allocate source flux in distribPhotonContrib");
206
207 /* ===================================================================
208 * INITIALISATION - Set up emission and scattering funcs
209 * =================================================================== */
210 emap.samples = NULL;
211 emap.src = NULL;
212 emap.maxPartitions = MAXSPART;
213 emap.partitions = (unsigned char*)malloc(emap.maxPartitions >> 1);
214 if (!emap.partitions)
215 error(USER, "can't allocate source partitions in distribPhotonContrib");
216
217 /* Initialise contrib photon map */
218 initPhotonMap(pm, PMAP_TYPE_CONTRIB);
219 initPhotonHeap(pm);
220 initPhotonEmissionFuncs();
221 initPhotonScatterFuncs();
222
223 /* Per-subprocess / per-source target counts */
224 pm -> distribTarget /= numProc;
225 srcDistribTarget = nsources ? (double)pm -> distribTarget / nsources : 0;
226
227 if (!pm -> distribTarget)
228 error(INTERNAL, "no photons to distribute in distribPhotonContrib");
229
230 /* Get photon ports if specified */
231 if (ambincl == 1)
232 getPhotonPorts();
233
234 /* Get photon sensor modifiers */
235 getPhotonSensors(photonSensorList);
236
237 #if NIX
238 /* Set up shared mem for photon counters (zeroed by ftruncate) */
239 strcpy(shmFname, PMAP_TMPFNAME);
240 shmFile = mkstemp(shmFname);
241
242 if (shmFile < 0 || ftruncate(shmFile, photonCntSize) < 0)
243 error(SYSTEM, "failed shared mem init in distribPhotonContrib");
244
245 photonCnt = mmap(NULL, photonCntSize, PROT_READ | PROT_WRITE,
246 MAP_SHARED, shmFile, 0);
247
248 if (photonCnt == MAP_FAILED)
249 error(SYSTEM, "failed shared mem mapping in distribPhotonContrib");
250 #else
251 /* Allocate photon counters statically on Windoze */
252 if (!(photonCnt = malloc(photonCntSize)))
253 error(SYSTEM, "failed trivial malloc in distribPhotonContrib");
254
255 for (srcIdx = 0; srcIdx < PHOTONCNT_NUMEMIT(nsources); srcIdx++)
256 photonCnt [srcIdx] = 0;
257 #endif /* NIX */
258
259 if (verbose) {
260 sprintf(errmsg, "\nIntegrating flux from %d sources", nsources);
261
262 if (photonPorts) {
263 sprintf(errmsg2, " via %d ports", numPhotonPorts);
264 strcat(errmsg, errmsg2);
265 }
266
267 strcat(errmsg, "\n");
268 eputs(errmsg);
269 }
270
271 /* =============================================================
272 * FLUX INTEGRATION - Get total flux emitted from sources/ports
273 * ============================================================= */
274 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
275 unsigned portCnt = 0;
276 srcFlux [srcIdx] = 0;
277 emap.src = source + srcIdx;
278
279 do { /* Need at least one iteration if no ports! */
280 emap.port = emap.src -> sflags & SDISTANT ? photonPorts + portCnt
281 : NULL;
282 photonPartition [emap.src -> so -> otype] (&emap);
283
284 if (verbose) {
285 sprintf(errmsg, "\tIntegrating flux from source %s ",
286 source [srcIdx].so -> oname);
287
288 if (emap.port) {
289 sprintf(errmsg2, "via port %s ",
290 photonPorts [portCnt].so -> oname);
291 strcat(errmsg, errmsg2);
292 }
293
294 sprintf(errmsg2, "(%lu partitions)\n", emap.numPartitions);
295 strcat(errmsg, errmsg2);
296 eputs(errmsg);
297 #if NIX
298 fflush(stderr);
299 #endif
300 }
301
302 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
303 emap.partitionCnt++) {
304 initPhotonEmission(&emap, pdfSamples);
305 srcFlux [srcIdx] += colorAvg(emap.partFlux);
306 }
307
308 portCnt++;
309 } while (portCnt < numPhotonPorts);
310
311 if (srcFlux [srcIdx] < FTINY) {
312 sprintf(errmsg, "source %s has zero emission",
313 source [srcIdx].so -> oname);
314 error(WARNING, errmsg);
315 }
316 }
317
318 /* Allocate & init per-subprocess primary heap files */
319 primaryHeap = calloc(numProc, sizeof(FILE*));
320 primaryHeapFname = calloc(numProc, sizeof(char*));
321 primaryOfs = calloc(numProc, sizeof(PhotonPrimaryIdx));
322 if (!primaryHeap || !primaryHeapFname || !primaryOfs)
323 error(SYSTEM, "failed primary heap allocation in "
324 "distribPhotonContrib");
325
326 for (proc = 0; proc < numProc; proc++) {
327 primaryHeapFname [proc] = malloc(PMAP_TMPFNLEN);
328 if (!primaryHeapFname [proc])
329 error(SYSTEM, "failed primary heap file allocation in "
330 "distribPhotonContrib");
331
332 mktemp(strcpy(primaryHeapFname [proc], PMAP_TMPFNAME));
333 if (!(primaryHeap [proc] = fopen(primaryHeapFname [proc], "w+b")))
334 error(SYSTEM, "failed opening primary heap file in "
335 "distribPhotonContrib");
336 }
337
338 /* Record start time for progress reports */
339 repStartTime = time(NULL);
340
341 if (verbose) {
342 sprintf(errmsg, "\nPhoton distribution @ %d procs\n", numProc);
343 eputs(errmsg);
344 }
345
346 /* MAIN LOOP */
347 for (proc = 0; proc < numProc; proc++) {
348 #if NIX
349 if (!(pid = fork())) {
350 /* SUBPROCESS ENTERS HERE; opened and mmapped files inherited */
351 #else
352 if (1) {
353 /* No subprocess under Windoze */
354 #endif
355 /* Local photon counters for this subprocess */
356 unsigned long lastNumPhotons = 0, localNumEmitted = 0;
357 double photonFluxSum = 0; /* Accum. photon flux */
358
359 /* Seed RNGs from PID for decorellated photon distribution */
360 pmapSeed(randSeed + proc, partState);
361 pmapSeed(randSeed + proc, emitState);
362 pmapSeed(randSeed + proc, cntState);
363 pmapSeed(randSeed + proc, mediumState);
364 pmapSeed(randSeed + proc, scatterState);
365 pmapSeed(randSeed + proc, rouletteState);
366
367 /* =============================================================
368 * 2-PASS PHOTON DISTRIBUTION
369 * Pass 1 (pre): emit fraction of target photon count
370 * Pass 2 (main): based on outcome of pass 1, estimate remaining
371 * number of photons to emit to approximate target
372 * count
373 * ============================================================= */
374 for (srcIdx = 0; srcIdx < nsources; srcIdx++) {
375 unsigned portCnt, passCnt = 0, prePassCnt = 0;
376 float srcPreDistrib = preDistrib;
377 double srcNumEmit = 0; /* # to emit from source */
378 unsigned long srcNumDistrib = pm -> numPhotons; /* # stored */
379
380 if (srcFlux [srcIdx] < FTINY)
381 continue;
382
383 while (passCnt < 2) {
384 if (!passCnt) {
385 /* INIT PASS 1 */
386 if (++prePassCnt > maxPreDistrib && !proc) {
387 /* Warn if no photons contributed after sufficient
388 * iterations; only output from subprocess 0 to reduce
389 * console clutter */
390 sprintf(errmsg,
391 "source %s: too many prepasses, skipped",
392 source [srcIdx].so -> oname);
393 error(WARNING, errmsg);
394 break;
395 }
396
397 /* Num to emit is fraction of target count */
398 srcNumEmit = srcPreDistrib * srcDistribTarget;
399 }
400 else {
401 /* INIT PASS 2 */
402 double srcPhotonFlux, avgPhotonFlux;
403
404 /* Based on the outcome of the predistribution we can now
405 * figure out how many more photons we have to emit from
406 * the current source to meet the target count,
407 * srcDistribTarget. This value is clamped to 0 in case
408 * the target has already been exceeded in pass 1.
409 * srcNumEmit and srcNumDistrib is the number of photons
410 * emitted and distributed (stored) from the current
411 * source in pass 1, respectively. */
412 srcNumDistrib = pm -> numPhotons - srcNumDistrib;
413 srcNumEmit *= srcNumDistrib
414 ? max(srcDistribTarget/srcNumDistrib, 1) - 1
415 : 0;
416
417 if (!srcNumEmit)
418 /* No photons left to distribute in main pass */
419 break;
420
421 srcPhotonFlux = srcFlux [srcIdx] / srcNumEmit;
422 avgPhotonFlux = photonFluxSum / (srcIdx + 1);
423
424 if (avgPhotonFlux > 0 &&
425 srcPhotonFlux / avgPhotonFlux < FTINY) {
426 /* Skip source if its photon flux is grossly below the
427 * running average, indicating negligible contributions
428 * at the expense of excessive distribution time; only
429 * output from subproc 0 to reduce console clutter */
430 sprintf(errmsg,
431 "source %s: itsy bitsy photon flux, skipped",
432 source [srcIdx].so -> oname);
433 error(WARNING, errmsg);
434 srcNumEmit = 0;
435 }
436
437 /* Update sum of photon flux per light source */
438 photonFluxSum += srcPhotonFlux;
439 }
440
441 portCnt = 0;
442 do { /* Need at least one iteration if no ports! */
443 emap.src = source + srcIdx;
444 emap.port = emap.src -> sflags & SDISTANT
445 ? photonPorts + portCnt : NULL;
446 photonPartition [emap.src -> so -> otype] (&emap);
447
448 if (verbose && !proc) {
449 /* Output from subproc 0 only to avoid race condition
450 * on console I/O */
451 if (!passCnt)
452 sprintf(errmsg, "\tPREPASS %d on source %s ",
453 prePassCnt, source [srcIdx].so -> oname);
454 else
455 sprintf(errmsg, "\tMAIN PASS on source %s ",
456 source [srcIdx].so -> oname);
457
458 if (emap.port) {
459 sprintf(errmsg2, "via port %s ",
460 photonPorts [portCnt].so -> oname);
461 strcat(errmsg, errmsg2);
462 }
463
464 sprintf(errmsg2, "(%lu partitions)\n",
465 emap.numPartitions);
466 strcat(errmsg, errmsg2);
467 eputs(errmsg);
468 #if NIX
469 fflush(stderr);
470 #endif
471 }
472
473 for (emap.partitionCnt = 0; emap.partitionCnt < emap.numPartitions;
474 emap.partitionCnt++) {
475 double partNumEmit;
476 unsigned long partEmitCnt;
477
478 /* Get photon origin within current source partishunn
479 * and build emission map */
480 photonOrigin [emap.src -> so -> otype] (&emap);
481 initPhotonEmission(&emap, pdfSamples);
482
483 /* Number of photons to emit from ziss partishunn;
484 * scale according to its normalised contribushunn to
485 * the emitted source flux */
486 partNumEmit = srcNumEmit * colorAvg(emap.partFlux) /
487 srcFlux [srcIdx];
488 partEmitCnt = (unsigned long)partNumEmit;
489
490 /* Probabilistically account for fractional photons */
491 if (pmapRandom(cntState) < partNumEmit - partEmitCnt)
492 partEmitCnt++;
493
494 /* Update local and shared global emission counter */
495 photonCnt [PHOTONCNT_NUMEMIT(srcIdx)] += partEmitCnt;
496 localNumEmitted += partEmitCnt;
497
498 /* Integer counter avoids FP rounding errors during
499 * iteration */
500 while (partEmitCnt--) {
501 RAY photonRay;
502
503 /* Emit photon according to PDF (if any), allocate
504 * associated primary ray, and trace through scene
505 * until absorbed/leaked; emitPhoton() sets the
506 * emitting light source index in photonRay */
507 emitPhoton(&emap, &photonRay);
508 #if 1
509 if (emap.port)
510 /* !!! PHOTON PORT REJECTION SAMPLING HACK: set
511 * !!! photon port as fake hit object for
512 * !!! primary ray to check for intersection in
513 * !!! tracePhoton() */
514 photonRay.ro = emap.port -> so;
515 #endif
516 newPhotonPrimary(pm, &photonRay, primaryHeap[proc]);
517 /* Set subprocess index in photonRay for post-
518 * distrib primary index linearisation; this is
519 * propagated with the primary index in photonRay
520 * and set for photon hits by newPhoton() */
521 PMAP_SETRAYPROC(&photonRay, proc);
522 tracePhoton(&photonRay);
523 }
524
525 /* Update shared global photon count */
526 photonCnt [PHOTONCNT_NUMPHOT] += pm -> numPhotons -
527 lastNumPhotons;
528 lastNumPhotons = pm -> numPhotons;
529 #if !NIX
530 /* Synchronous progress report on Windoze */
531 if (!proc && photonRepTime > 0 &&
532 time(NULL) >= repLastTime + photonRepTime) {
533 unsigned s;
534 repComplete = pm -> distribTarget * numProc;
535 repProgress = photonCnt [PHOTONCNT_NUMPHOT];
536
537 for (repEmitted = 0, s = 0; s < nsources; s++)
538 repEmitted += photonCnt [PHOTONCNT_NUMEMIT(s)];
539
540 pmapDistribReport();
541 }
542 #endif
543 }
544
545 portCnt++;
546 } while (portCnt < numPhotonPorts);
547
548 if (pm -> numPhotons == srcNumDistrib) {
549 /* Double predistrib factor in case no photons were stored
550 * for this source and redo pass 1 */
551 srcPreDistrib *= 2;
552 }
553 else {
554 /* Now do pass 2 */
555 passCnt++;
556 }
557 }
558 }
559
560 /* Flush heap buffa one final time to prevent data corruption */
561 flushPhotonHeap(pm);
562 /* Flush final photon primary to primary heap file */
563 newPhotonPrimary(pm, NULL, primaryHeap [proc]);
564 /* Heap files closed automatically on exit
565 fclose(pm -> heap);
566 fclose(primaryHeap [proc]); */
567
568 #ifdef DEBUG_PMAP
569 sprintf(errmsg, "Proc %d total %ld photons\n", proc,
570 pm -> numPhotons);
571 eputs(errmsg);
572 fflush(stderr);
573 #endif
574
575 #if NIX
576 /* Terminate subprocess */
577 exit(0);
578 #endif
579 }
580 else if (pid < 0)
581 error(SYSTEM, "failed to fork subprocess in distribPhotonContrib");
582 }
583
584 #if NIX
585 /* PARENT PROCESS CONTINUES HERE */
586 #ifdef SIGCONT
587 /* Enable progress report signal handler */
588 signal(SIGCONT, pmapDistribReport);
589 #endif
590 /* Wait for subprocesses to complete while reporting progress */
591 proc = numProc;
592 while (proc) {
593 while (waitpid(-1, &stat, WNOHANG) > 0) {
594 /* Subprocess exited; check status */
595 if (!WIFEXITED(stat) || WEXITSTATUS(stat))
596 error(USER, "failed photon distribution");
597
598 --proc;
599 }
600
601 /* Nod off for a bit and update progress */
602 sleep(1);
603
604 /* Asynchronous progress report from shared subprocess counters */
605 repComplete = pm -> distribTarget * numProc;
606 repProgress = photonCnt [PHOTONCNT_NUMPHOT];
607
608 for (repEmitted = 0, srcIdx = 0; srcIdx < nsources; srcIdx++)
609 repEmitted += photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
610
611 /* Get global photon count from shmem updated by subprocs */
612 pm -> numPhotons = photonCnt [PHOTONCNT_NUMPHOT];
613
614 if (photonRepTime > 0 && time(NULL) >= repLastTime + photonRepTime)
615 pmapDistribReport();
616 #ifdef SIGCONT
617 else signal(SIGCONT, pmapDistribReport);
618 #endif
619 }
620 #endif /* NIX */
621
622 /* ================================================================
623 * POST-DISTRIBUTION - Set photon flux and build kd-tree, etc.
624 * ================================================================ */
625 #ifdef SIGCONT
626 /* Reset signal handler */
627 signal(SIGCONT, SIG_DFL);
628 #endif
629 free(emap.samples);
630
631 if (!pm -> numPhotons)
632 error(USER, "empty contribution photon map");
633
634 /* Load per-subprocess primary rays into pm -> primary array */
635 /* Dumb compilers apparently need the char** cast */
636 pm -> numPrimary = buildPrimaries(pm, primaryHeap,
637 (char**)primaryHeapFname,
638 primaryOfs, numProc);
639 if (!pm -> numPrimary)
640 error(INTERNAL, "no primary rays in contribution photon map");
641
642 /* Set photon flux per source */
643 for (srcIdx = 0; srcIdx < nsources; srcIdx++)
644 srcFlux [srcIdx] /= photonCnt [PHOTONCNT_NUMEMIT(srcIdx)];
645 #if NIX
646 /* Photon counters no longer needed, unmap shared memory */
647 munmap(photonCnt, sizeof(*photonCnt));
648 close(shmFile);
649 unlink(shmFname);
650 #else
651 free(photonCnt);
652 #endif
653
654 if (verbose) {
655 eputs("\nBuilding contribution photon map...\n");
656 #if NIX
657 fflush(stderr);
658 #endif
659 }
660
661 /* Build underlying data structure; heap is destroyed */
662 buildPhotonMap(pm, srcFlux, primaryOfs, numProc);
663
664 /* Free per-subprocess primary heap files */
665 for (proc = 0; proc < numProc; proc++)
666 free(primaryHeapFname [proc]);
667
668 free(primaryHeapFname);
669 free(primaryHeap);
670 free(primaryOfs);
671
672 if (verbose)
673 eputs("\n");
674 }