File indexing completed on 2026-04-10 07:49:32
0001
0002 """
0003 tests/CSGOptiXSimtraceTest.py
0004 ==============================
0005
0006 * see notes/issues/simtrace-shakedown.rst
0007
0008
0009 See also:
0010
0011 csg/tests/CSGFoundry_MakeCenterExtentGensteps_Test.sh
0012
0013
0014 This allows interactive visualization of workstation
0015 generated intersect data fphoton.npy on remote machines such as
0016 user laptops that support pyvista.
0017
0018
0019 FEAT envvar controlling intersect coloring and legend titles
0020 --------------------------------------------------------------
0021
0022 pid
0023 uses cf.primIdx_meshname_dict()
0024 bnd
0025 uses cf.sim.bndnamedict
0026 ins
0027 uses cf.insnamedict
0028
0029 instance identity : less different feature colors normally
0030 but interesting to see what is in which instance and what is in ins0 the global instance,
0031 the legend names are for example : ins37684 ins42990 ins0 ins43029
0032
0033
0034 ISEL envvar selects simtrace geometry intersects by their features, according to frequency order
0035 -------------------------------------------------------------------------------------------------------
0036
0037 FEAT=ins ISEL=0
0038 only show the instance with the most intersects
0039 FEAT=ins ISEL=0,1
0040 only show the two instances with the most and 2nd most intersects
0041 FEAT=bnd ISEL=0,1
0042 ditto for boundaries
0043
0044 FEAT=pid ISEL=0,1
0045
0046
0047
0048 pyvista GUI keys
0049 ----------------------
0050
0051 * https://docs.pyvista.org/api/plotting/plotting.html
0052
0053 * to zoom out/in : slide two fingers up/down on trackpad.
0054 * to pan : hold down shift and one finger tap-lock, then move finger around
0055
0056
0057 Too many items in the legend
0058 -----------------------------
0059
0060 When not using MASK=pos the legend may be filled with feature item lines
0061 that are not visible in the frame
0062
0063
0064 FramePhotons vs Photons
0065 ---------------------------
0066
0067 Using frame photons is a trick to effectively see results
0068 from many more photons that have to pay the costs for transfers etc..
0069 Frame photons lodge photons onto a frame of pixels limiting
0070 the maximumm number of photons to handle.
0071
0072 ISEL allows plotting of a selection of feature values only, picked by descending frequency index
0073 -------------------------------------------------------------------------------------------------
0074
0075 ::
0076
0077 cx ; ./cxs_Hama.sh grab
0078 cx ; ./cxs_Hama.sh ana
0079
0080 Old instructions, not recently exercised::
0081
0082 cx ; ./cxs_grab.sh ## NO LONGER USED ?
0083
0084 ISEL=0,1 ./cxs.sh # ISEL=0,1 picks the 2 most frequent feature values (eg boundaries when FEAT=bnd)
0085 ISEL=0,1,2,3,4 ./cxs.sh
0086
0087 ISEL=Hama ./cxs.sh # select boundaries via strings in the bndnames, assuming FEAT=bnd
0088 ISEL=NNVT ./cxs.sh
0089 ISEL=Pyrex ./cxs.sh
0090 ISEL=Pyrex,Water ./cxs.sh
0091
0092
0093 """
0094 import os, sys, logging, numpy as np
0095 np.set_printoptions(suppress=True, edgeitems=5, linewidth=200,precision=3)
0096 log = logging.getLogger(__name__)
0097
0098 from opticks.ana.eget import efloatlist_, elookce_, elook_epsilon_, eint_
0099
0100 SIZE = np.array([1280, 720])
0101 XCOMPARE_SIMPLE = "XCOMPARE_SIMPLE" in os.environ
0102 XCOMPARE = "XCOMPARE" in os.environ
0103 GUI = not "NOGUI" in os.environ
0104 MP = not "NOMP" in os.environ
0105 PV = not "NOPV" in os.environ
0106 PVGRID = not "NOPVGRID" in os.environ
0107 LEGEND = not "NOLEGEND" in os.environ
0108 SIMPLE = "SIMPLE" in os.environ
0109 MASK = os.environ.get("MASK", "pos")
0110 FEAT = os.environ.get("FEAT", "pid" )
0111 ALLOWED_MASK = ("pos", "t", "non" )
0112 assert MASK in ALLOWED_MASK, "MASK %s is not in ALLOWED_MASK list %s " % (MASK, str(ALLOWED_MASK))
0113 GSPLOT = eint_("GSPLOT", "0")
0114 PIDX = eint_("PIDX", "0")
0115
0116
0117 from opticks.CSG.CSGFoundry import CSGFoundry
0118 from opticks.ana.p import *
0119 from opticks.ana.fold import Fold
0120 from opticks.ana.feature import SimtraceFeatures
0121 from opticks.ana.simtrace_positions import SimtracePositions
0122 from opticks.ana.simtrace_plot import SimtracePlot
0123 from opticks.ana.framegensteps import FrameGensteps
0124 from opticks.ana.npmeta import NPMeta
0125 from opticks.sysrap.sframe import sframe , X, Y, Z
0126 from opticks.ana.pvplt import *
0127
0128 import matplotlib
0129 if GUI == False:
0130 log.info("set pdf backend as GUI False")
0131 matplotlib.use("agg")
0132 pass
0133
0134 if MP:
0135 try:
0136 import matplotlib.pyplot as mp
0137 except ImportError:
0138 mp = None
0139 pass
0140 else:
0141 mp = None
0142 pass
0143
0144 if PV:
0145 try:
0146 import pyvista as pv
0147 themes = ["default", "dark", "paraview", "document" ]
0148 pv.set_plot_theme(themes[1])
0149 except ImportError:
0150 pv = None
0151 pass
0152 else:
0153 pv = None
0154 pass
0155
0156 if GUI == False:
0157 log.info("disabling pv as GUI False")
0158 pv = None
0159 pass
0160
0161
0162
0163 def pvplt_simple(xyz, label):
0164 """
0165 :param xyz: (n,3) shaped array of positions
0166 :param label: to place on plot
0167
0168 KEEP THIS SIMPLE : FOR DEBUGGING WHEN LESS BELLS AND WHISTLES IS AN ADVANTAGE
0169 """
0170 pl = pv.Plotter(window_size=SIZE*2 )
0171 pl.add_text( "pvplt_simple %s " % label, position="upper_left")
0172 pl.add_points( xyz, color="white" )
0173 pl.show_grid()
0174 cp = pl.show() if GUI else None
0175 return cp
0176
0177
0178
0179 def xcompare_simple( pos, x_gpos, x_lpos, local=True ):
0180 """
0181 :param pos: SimtracePositions instance
0182 :param x_gpos: global photon step positions
0183 :param x_lpos: local photon step positions
0184 """
0185 pl = pv.Plotter(window_size=SIZE*2 )
0186 if local == False:
0187 pl.add_points( pos.gpos[:,:3], color="white" )
0188 pvplt_add_contiguous_line_segments(pl, x_gpos[:,:3])
0189 else:
0190 pl.add_points( pos.lpos[:,:3], color="white" )
0191 pvplt_add_contiguous_line_segments(pl, x_lpos[:,:3])
0192 pass
0193 pl.show_grid()
0194
0195 outpath = "/tmp/xcompare_simple.png"
0196 log.info("outpath %s " % outpath)
0197
0198
0199 cp = pl.show(screenshot=outpath) if GUI else None
0200 return pl
0201
0202
0203 def simple(pl, pos):
0204 """
0205 :param pos: SimtracePositions instance
0206 """
0207 pvplt_simple(pl, pos.gpos[:,:3], "pos.gpos[:,:3]" )
0208 pvplt_simple(pl, pos.lpos[:,:3], "pos.lpos[:,:3]" )
0209
0210
0211 if __name__ == '__main__':
0212 logging.basicConfig(level=logging.INFO)
0213
0214 t = Fold.Load("$CFBASE/CSGOptiXSimtraceTest", symbol="t");
0215 x = Fold.Load("$CFBASE/CSGOptiXSimTest", symbol="x")
0216
0217 if not x is None:
0218 x_nib = seqnib_(x.seq[:,0])
0219 x_gpos_ = x.record[PIDX,:x_nib[PIDX],0,:3]
0220 x_gpos = np.ones( (len(x_gpos_), 4 ), dtype=np.float32 )
0221 x_gpos[:,:3] = x_gpos_
0222 x_lpos = np.dot( x_gpos, t.sframe.w2m )
0223 pass
0224
0225 SimtracePositions.Check(t.simtrace)
0226
0227 local = True
0228
0229 gs = FrameGensteps(t.genstep, t.sframe, local=local, symbol="gs" )
0230
0231 t_pos = SimtracePositions(t.simtrace, gs, t.sframe, local=local, mask=MASK, symbol="t_pos" )
0232
0233 if SIMPLE:
0234 pl = pvplt_plotter()
0235 simple(pl, t_pos)
0236 pl.show()
0237 raise Exception("SIMPLE done")
0238 pass
0239 if XCOMPARE_SIMPLE and not x is None:
0240 pl = xcompare_simple( t_pos, x_gpos, x_lpos, local=True )
0241
0242 pass
0243
0244 pf = SimtraceFeatures(t_pos, cf, featname=FEAT, symbol="pf" )
0245
0246 pl = SimtracePlot.MakePVPlotter()
0247
0248 plt = SimtracePlot(pl, pf.feat, gs, t.sframe, t_pos, outdir=os.path.join(t.base, "figs") )
0249
0250 if not x is None:
0251 plt.x_lpos = x_lpos
0252 pass
0253
0254 if not mp is None:
0255 plt.positions_mpplt()
0256 ax = plt.ax
0257 pass
0258
0259 if not pv is None:
0260 plt.positions_pvplt()
0261 pass
0262 pass