Skip to content

Commit

Permalink
fix #7346 source particle improvements (#7372)
Browse files Browse the repository at this point in the history
 - fix mouse over heatmap for reversed domains
 - moved heatmap zoom to inner svg to allow zoom over geometry outlines
 - fix VTKVectorFormula to use scalar values directly rather than recalculating
 - set lower bound on numSampleSourceParticles
 - changing source particle count updates display
 - only show source particles on a 2d tally slice if it originated within the slice boundaries
 - show 2d source particles with a length projected from 3d to 2d, rather than a fixed length
 - only show source outlines if a source particle is present on the selected 2d frame
  • Loading branch information
moellep authored Nov 19, 2024
1 parent 76f1b7c commit 8e0320c
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 67 deletions.
12 changes: 7 additions & 5 deletions sirepo/package_data/static/html/heatplot.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
<text class="main-title" ng-attr-x="{{ canvasSize.width / 2 }}" ng-attr-y="{{ -margin.top / 2 }}"></text>
<text class="sub-title" ng-attr-x="{{ canvasSize.width / 2 }}" y="0" dy="-.5em"></text>
<text class="select-cell-text" data-ng-if="enableSelection" ng-attr-x="{{ canvasSize.width - 8 }}" y="0" dy="-0.5em">Alt click to select a cell</text>
<rect class="mouse-rect mouse-zoom" ng-attr-width="{{ canvasSize.width }}" ng-attr-height="{{ canvasSize.height }}" style="pointer-events: all; fill: none;"></rect>
<g class="sr-overlay-data-group" ng-attr-width="{{ canvasSize.width }}" ng-attr-height="{{ canvasSize.height }}">
<g data-ng-if="enableSelection">
<text class="sr-heatmap-readout" x="8" y="-0.5em"></text>
<svg class="mouse-rect mouse-zoom" ng-attr-width="{{ canvasSize.width }}" ng-attr-height="{{ canvasSize.height }}" style="pointer-events: all">
<rect ng-attr-width="{{ canvasSize.width }}" ng-attr-height="{{ canvasSize.height }}" style="fill: none;"></rect>
<g class="sr-overlay-data-group" ng-attr-width="{{ canvasSize.width }}" ng-attr-height="{{ canvasSize.height }}">
<g data-ng-if="enableSelection">
<text class="sr-heatmap-readout" x="8" y="-0.5em"></text>
</g>
</g>
</g>
</svg>
<text class="x-base" text-anchor="end" ng-attr-x="{{ canvasSize.width + margin.right - 5 }}" ng-attr-y="{{ canvasSize.height + 40 }}"></text>
<g class="x axis" ng-attr-transform="translate(0, {{ canvasSize.height }})">
<text class="x-axis-label" ng-attr-x="{{ canvasSize.width / 2 }}" y="40"></text>
Expand Down
138 changes: 84 additions & 54 deletions sirepo/package_data/static/js/openmc.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ SIREPO.app.factory('tallyService', function(appState, openmcService, utilities,
maxField: 0,
sourceParticles: [],
};
let lastTallyView = {};
const invalidPlanePosition = -1e16;

function initMesh() {
Expand Down Expand Up @@ -616,7 +617,14 @@ SIREPO.app.factory('tallyService', function(appState, openmcService, utilities,
};

self.invalidatePlanePosition = () => {
//TODO(pjm): only clear planePos if the selected tally domain has changed
const tallyView = {
axis: appState.models.tallyReport.axis,
mesh: self.mesh,
};
if (appState.deepEquals(lastTallyView, tallyView)) {
return;
}
lastTallyView = tallyView;
appState.models.tallyReport.planePos = invalidPlanePosition;
};

Expand All @@ -641,8 +649,8 @@ SIREPO.app.factory('tallyService', function(appState, openmcService, utilities,
f.stop,
];
}
self.invalidatePlanePosition();
initMesh();
self.invalidatePlanePosition();
};

self.setSourceParticles = particles => {
Expand Down Expand Up @@ -961,7 +969,7 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
$scope.tallyService = tallyService;
const displayRanges = {};
let geometryOutlines;
let lastTally = [null, null, null];
let lastTally = {};
const sources = openmcService.getSourceVisualizations(
{
box: space => {
Expand Down Expand Up @@ -1000,17 +1008,14 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
if (! tallyService.fieldData || tallyService.isPlanePositionInvalid()) {
return;
}
const newTally = {...appState.models.openmcAnimation, ...appState.models.tallyReport};
if (
(lastTally[0] != appState.models.openmcAnimation.tally)
|| (lastTally[1] != appState.models.tallyReport.axis)
|| (lastTally[2] != appState.models.tallyReport.planePos)
(lastTally.tally !== newTally.tally)
|| (lastTally.axis !== newTally.axis)
|| (lastTally.planePos !== newTally.planePos)
|| (lastTally.numSampleSourceParticles !== newTally.numSampleSourceParticles)
) {
geometryOutlines = null;
const newTally = [
appState.models.openmcAnimation.tally,
appState.models.tallyReport.axis,
appState.models.tallyReport.planePos,
];
lastTally = newTally;
const o = appState.models.outlineAnimation;
o.tally = appState.models.openmcAnimation.tally;
Expand Down Expand Up @@ -1107,14 +1112,6 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
if (appState.models.openmcAnimation.showSources === '0') {
return [];
}
//TODO(pjm): rework this method
const [n, m, l] = tallyReportAxisIndices();
const dimIndex = n;
const dim = SIREPO.GEOMETRY.GeometryUtils.BASIS()[dimIndex];
const outlines = [];
const particleColors = SIREPO.UTILS.unique(
tallyService.getSourceParticles().map(p => particleColor(p))
);

function isPosOutsideMesh(pos, j, k) {
const r = tallyService.getMeshRanges();
Expand All @@ -1124,32 +1121,45 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
// we cannot set the color of an instance of a marker ref, so we will
// have to create them on the fly
function placeMarkers() {
const ns = 'http://www.w3.org/2000/svg';
let ds = d3.select('svg.sr-plot defs')
.selectAll('marker')
.data(particleColors);
.data(SIREPO.UTILS.unique(tallyService.getSourceParticles().map(p => p.energy)));
ds.exit().remove();
ds.enter()
.append(d => document.createElementNS(ns, 'marker'))
.append(d => document.createElementNS('http://www.w3.org/2000/svg', 'marker'))
.append('path')
.attr('d', 'M0,0 L0,4 L9,2 z');
ds.call(updateMarkers);
}

function particleColor(p) {
function particleColor(energy) {
return tallyService.sourceParticleColorScale(
appState.models.openmcAnimation.sourceColorMap
)(p.energy);
)(energy);
}

function particleId(p) {
return particleIdFromColor(particleColor(p));
return particleIdFromColor(particleColor(p.energy));
}

function particleIdFromColor(c) {
return `arrow-${c.slice(1)}`;
}

function particleRange(dim) {
const d = tallyService.tallyRange(dim);
const w = Math.abs((d.max - d.min) / d.steps) / 2;
const p = appState.models.tallyReport.planePos;
return [
p - w,
p + w,
];
}

function sourceColor(color) {
return appState.models.openmcAnimation.showSources === '1' ? color : 'none';
}

function toReversed(arr) {
// Array.toReversed() is not available in all active browsers
// not using a polyfill due to array iteration bugs
Expand All @@ -1162,49 +1172,74 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,

function updateMarkers(selection) {
selection
.attr('id', d => particleIdFromColor(d))
.attr('id', d => particleIdFromColor(particleColor(d)))
.attr('markerHeight', 8)
.attr('markerWidth', 8)
.attr('refX', 4)
.attr('refY', 2)
.attr('orient', 'auto')
.attr('markerUnits', 'strokeWidth')
.select('path')
.attr('fill', d => sourceColor(d));
.attr('fill', d => sourceColor(particleColor(d)));
}

sources.forEach((view, i) => {
const s = appState.models.settings.sources[i];
if (view instanceof SIREPO.VTK.SphereViews) {
view.setRadius(25 * vectorScaleFactor());
}
outlines.push({
name: `source-${s.particle}-${s.space._type}-${i}`,
color: sourceColor('#ff0000'),
data: view.shapePoints(dim).map(p => toReversed(p)),
doClose: true,
});
});
//TODO(pjm): rework this method
const dimIndex = tallyReportAxisIndices()[0];
const dim = SIREPO.GEOMETRY.GeometryUtils.BASIS()[dimIndex];
const outlines = [];

placeMarkers();
const r = vectorScaleFactor();
const [j, k] = SIREPO.GEOMETRY.GeometryUtils.nextAxisIndices(dim);
let [j, k] = SIREPO.GEOMETRY.GeometryUtils.nextAxisIndices(dim);
if (dim === 'x') {
//TODO(pjm): nextAxisIndices() is not correct for x dim for this app
[j, k] = [2, 1];
}
const pr = particleRange(dim);
tallyService.getSourceParticles().forEach((p, n) => {
const p0 = p.position[dimIndex] * openmcService.GEOMETRY_SCALE;
if (p0 < pr[0] || p0 > pr[1]) {
return;
}
const p1 = [p.position[j], p.position[k]].map(x => x * openmcService.GEOMETRY_SCALE);
// ignore sources outside the plotting range
if (isPosOutsideMesh(p1, j, k)) {
return;
}
// normalize in the plane and check if perpendicular
const d = Math.hypot(p.direction[j], p.direction[k]);
const p2 = d ? [p1[0] + r * p.direction[j] / d, p1[1] + r * p.direction[k] / d] : p1;
const ds = 1 - Math.abs(p.direction[dimIndex]);
const p2 = d ? [p1[0] + r * p.direction[j] / d * ds, p1[1] + r * p.direction[k] / d * ds] : p1;
let points = [p1, p2];
if (dim === 'x' || dim === 'y') {
points = points.map(p => toReversed(p));
}
outlines.push({
name: `${p.type}-${p.energy}eV-${n}`,
color: sourceColor(particleColor(p)),
color: sourceColor(particleColor(p.energy)),
dashes: p.type === 'PHOTON' ? '6 2' : '',
data: [p1, p2].map(p => toReversed(p)),
data: points,
marker: particleId(p),
});
});
if (outlines.length) {
sources.forEach((view, i) => {
const s = appState.models.settings.sources[i];
if (view instanceof SIREPO.VTK.SphereViews) {
view.setRadius(25 * vectorScaleFactor());
}
let points = view.shapePoints(dim);
if (dim === 'y') {
points = points.map(p => toReversed(p));
}
outlines.push({
name: `source-${s.particle}-${s.space._type}-${i}`,
color: sourceColor('#ff0000'),
data: points,
doClose: true,
});
});
}
return outlines;
}

Expand Down Expand Up @@ -1255,10 +1290,6 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
return v;
}

function sourceColor(color) {
return appState.models.openmcAnimation.showSources === '1' ? color : 'none';
}

function sumDisplay(val) {
const sumRange = appState.models.openmcAnimation.energyRangeSum;
if ($scope.energyFilter.space === 'log' && $scope.energyFilter.start > 0) {
Expand Down Expand Up @@ -1286,11 +1317,8 @@ SIREPO.app.directive('geometry2d', function(appState, frameCache, openmcService,
}

function updateDisplay() {
const axisChanged = appState.models.tallyReport.axis !== appState.applicationState().tallyReport.axis;
tallyService.updateTallyDisplay();
if (axisChanged) {
tallyService.invalidatePlanePosition();
}
tallyService.invalidatePlanePosition();
buildTallyReport();
}

Expand Down Expand Up @@ -1424,6 +1452,7 @@ SIREPO.app.directive('geometry3d', function(appState, openmcService, plotting, p
pointer: null,
THICKNESS: 30,
};
let firstRender = true;
let tallyBundle = null;
let tallyPolyData = null;
const voxelPoly = [
Expand All @@ -1441,7 +1470,8 @@ SIREPO.app.directive('geometry3d', function(appState, openmcService, plotting, p
addSources();
$rootScope.$broadcast('vtk.hideLoader');
const a = tallyService.getVisibleAxes();
if (a.includes(appState.models.tallyReport.axis)) {
if (firstRender && a.includes(appState.models.tallyReport.axis)) {
firstRender = false;
// default to the 2d axis, if visible
vtkScene.showSide(appState.models.tallyReport.axis);
if (appState.models.tallyReport.axis === vtkScene.resetSide){
Expand Down Expand Up @@ -1473,6 +1503,7 @@ SIREPO.app.directive('geometry3d', function(appState, openmcService, plotting, p
const particles = tallyService.getSourceParticles();
if (particles.length) {
particleBundle = coordMapper.buildVectorField(
particles.map(p => p.energy),
particles.map(p => p.direction.map(x => p.energy * x)),
particles.map(p => p.position),
vectorScaleFactor(),
Expand Down Expand Up @@ -3134,7 +3165,7 @@ SIREPO.viewLogic('tallySettingsView', function(appState, openmcService, panelSta
panelState.showField('openmcAnimation', 'energyRangeSum', ! ! openmcService.findFilter('energyFilter'));
panelState.showField('openmcAnimation', 'sourceNormalization', openmcService.canNormalizeScore(appState.models.openmcAnimation.score));
panelState.showField('openmcAnimation', 'numSampleSourceParticles', showSources);
panelState.showField('openmcAnimation', 'sourceColorMap', showSources && appState.models.openmcAnimation.numSampleSourceParticles);
panelState.showField('openmcAnimation', 'sourceColorMap', showSources);
panelState.showFields('tallyReport', [
'axis', is2D,
'planePos', is2D && tallyService.tallyRange(updateVisibleAxes(), true).steps > 1,
Expand Down Expand Up @@ -3180,7 +3211,6 @@ SIREPO.viewLogic('tallySettingsView', function(appState, openmcService, panelSta
'tallyReport.selectedGeometry',
'openmcAnimation.score',
'openmcAnimation.showSources',
'openmcAnimation.numSampleSourceParticles',
], showFields,
[
'openmcAnimation.tally',
Expand Down
12 changes: 7 additions & 5 deletions sirepo/package_data/static/js/sirepo-plotting-vtk.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,13 @@ class VTKVectorFormula {
};
}

constructor(vectors, colorMapName='jet') {
constructor(scalars, vectors, colorMapName='jet') {
this.scalars = scalars;
this.vectors = vectors;
this.colorMapName = colorMapName;
this.colorMap = SIREPO.PLOTTING.Utils.COLOR_MAP()[colorMapName];

this.magnitudes = this.vectors.map(x => Math.hypot(...x));
this.magnitudes = this.scalars;
this.directions = this.vectors.map((x, i) => x.map(y => y / this.magnitudes[i]));
this.norms = SIREPO.UTILS.normalize(this.magnitudes);

Expand Down Expand Up @@ -993,6 +994,7 @@ class VectorFieldBundle extends ActorBundle {
* @param {{}} actorProperties - a map of actor properties (e.g. 'color') to values
*/
constructor(
scalars,
vectors,
positions,
scaleFactor = 1.0,
Expand All @@ -1006,7 +1008,7 @@ class VectorFieldBundle extends ActorBundle {
transform,
actorProperties
);
this.formula = new VTKVectorFormula(vectors, colormapName);
this.formula = new VTKVectorFormula(scalars, vectors, colormapName);
this.polyData = vtk.Common.DataModel.vtkPolyData.newInstance();
this.polyData.getPoints().setData(
new window.Float32Array(positions.flat()),
Expand Down Expand Up @@ -1120,8 +1122,8 @@ class CoordMapper {
* @param {string} colormapName - name of a color map for the arrows
* @param {{}} actorProperties - a map of actor properties (e.g. 'color') to values
*/
buildVectorField(vectors, positions, scaleFactor=1.0, useTailAsOrigin=false, colormapName='jet', actorProperties={}) {
return new VectorFieldBundle(vectors, positions, scaleFactor, useTailAsOrigin, colormapName, this.transform, actorProperties);
buildVectorField(scalars, vectors, positions, scaleFactor, useTailAsOrigin=false, colormapName='jet', actorProperties={}) {
return new VectorFieldBundle(scalars, vectors, positions, scaleFactor, useTailAsOrigin, colormapName, this.transform, actorProperties);
}
}

Expand Down
4 changes: 2 additions & 2 deletions sirepo/package_data/static/js/sirepo-plotting.js
Original file line number Diff line number Diff line change
Expand Up @@ -3147,8 +3147,8 @@ SIREPO.app.directive('heatmap', function(appState, layoutService, plotting, util
const n = fp ? 0 : 1;
const dx = Math.abs((xRange[1] - xRange[0])) / (heatmap[0].length - n);
const dy = Math.abs((yRange[1] - yRange[0])) / (heatmap.length - n);
let i = (x - xRange[0]) / dx;
let j = (y - yRange[0]) / dy;
let i = Math.abs((x - xRange[0]) / dx);
let j = Math.abs((y - yRange[0]) / dy);
return [
fp ? Math.max(0, Math.floor(i)) : Math.round(i),
fp ? Math.max(0, Math.floor(j)) : Math.round(j),
Expand Down
2 changes: 1 addition & 1 deletion sirepo/package_data/static/json/openmc-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@
"bgColor": ["Background color", "Color", "#fff9ed"],
"colorMap": ["Tally color map", "ColorMap", "viridis"],
"energyRangeSum": ["Sum over energies [MeV]", "EnergyRange", [0, 0]],
"numSampleSourceParticles": ["Source particles to display", "Integer", 10, "", 0, 100],
"numSampleSourceParticles": ["Source particles to display", "Integer", 10, "", 1, 100],
"opacity": ["Global alpha", "Opacity", 1.0],
"showEdges": ["Show edges", "Boolean", "0"],
"showMarker": ["Show axis marker", "Boolean", "1"],
Expand Down
2 changes: 2 additions & 0 deletions sirepo/sim_data/openmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ def _fix_val(model, field):
cls.update_model_defaults(s, "source")
if "threshold" in dm.openmcAnimation:
del dm["openmcAnimation"]["threshold"]
if dm.openmcAnimation.numSampleSourceParticles < 1:
dm.openmcAnimation.numSampleSourceParticles = 1
for m, f in (
("tallyReport", "planePos"),
("openmcAnimation", "opacity"),
Expand Down

0 comments on commit 8e0320c

Please sign in to comment.