Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:48

0001 #!/usr/bin/env python
0002 #
0003 # Copyright (c) 2019 Opticks Team. All Rights Reserved.
0004 #
0005 # This file is part of Opticks
0006 # (see https://bitbucket.org/simoncblyth/opticks).
0007 #
0008 # Licensed under the Apache License, Version 2.0 (the "License"); 
0009 # you may not use this file except in compliance with the License.  
0010 # You may obtain a copy of the License at
0011 #
0012 #   http://www.apache.org/licenses/LICENSE-2.0
0013 #
0014 # Unless required by applicable law or agreed to in writing, software 
0015 # distributed under the License is distributed on an "AS IS" BASIS, 
0016 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
0017 # See the License for the specific language governing permissions and 
0018 # limitations under the License.
0019 #
0020 
0021 """
0022 groupvel.py
0023 ============
0024 
0025 * C++ and NumPy implementations of the ported GROUPVEL_ 
0026   calculation are matching 
0027 
0028   NB the calc interps back to original bin, 
0029   to avoid bin shifting sins that G4 commits
0030 
0031 
0032 TODO:
0033 
0034 * compare actual G4 calc against mt port of the calc
0035   
0036 
0037 """
0038 
0039 import os, logging, numpy as np
0040 log = logging.getLogger(__name__)
0041 
0042 from opticks.ana.base import opticks_main
0043 from opticks.ana.proplib import PropLib
0044 
0045 # values from GConstantTest 
0046 hc = 1239.841875       # eV.nm
0047 c_light = 299.792458   # mm/ns
0048 wl = np.linspace(60,820,39)   # standard wavelength domain with increasing wavelenth (nm)
0049 
0050 
0051 def g4_bintrick(aa):
0052     """
0053     :param aa:
0054     :return bb:
0055 
0056     This curious bin shifting is used by G4 GROUPVEL calc
0057 
0058     In order not to loose a bin
0059     a tricky manoever of using the 1st and last bin and 
0060     the average of the body bins
0061     which means the first bin is half width, and last is 1.5 width
0062     
0063     ::
0064 
0065             0  +  1  +  2  +  3  +  4  +  5        <--- 6 original values
0066             |    /     /     /     /      |
0067             |   /     /     /     /       |
0068             0  1     2     3     4        5        <--- still 6 
0069   
0070     """
0071     bb = np.zeros_like(aa)
0072     bb[0] = aa[0]
0073     bb[1:-1] = (aa[1:-1] + aa[:-2])/2.
0074     bb[-1] = aa[-1]
0075     return bb
0076 
0077 
0078 def np_gradient(y, edge_order=1):
0079     """
0080     ::
0081 
0082         Take a look at np.gradient??
0083 
0084         The gradient is computed using second order accurate central differences
0085         in the interior and either first differences or second order accurate 
0086         one-sides (forward or backwards) differences at the boundaries. The
0087         returned gradient hence has the same shape as the input array.
0088 
0089           0    <--- (0,1)  
0090   
0091           1    <--- (0,2)/2
0092           2    <--- (1,3)/2
0093           3    <--  (2,4)/2
0094           4    <--  (3,5)/2
0095           5    <--  (4,6)/2
0096           6    <--  (5,7)/2
0097           7    <--  (6,8)/2
0098           8    <--  (7,9)/2
0099 
0100           9    <--  (8,9) 
0101 
0102 
0103         In [10]: y = np.arange(10, dtype='f')
0104 
0105 
0106         In [14]: y[:-2]   # skip bins N-2,N-1  :  ie N-2 values
0107         Out[14]: array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.], dtype=float32)
0108 
0109         In [13]: y[2:]   # skip bins 0,1 : ie N-2 values
0110         Out[13]: array([ 2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.], dtype=float32)
0111 
0112         In [15]: y[2:] - y[:-2]    # between N-2 values -> N-2 values
0113         Out[15]: array([ 2.,  2.,  2.,  2.,  2.,  2.,  2.,  2.], dtype=float32)
0114 
0115         In [16]: (y[2:] - y[:-2])/2.
0116         Out[16]: array([ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.], dtype=float32)
0117 
0118     """
0119     assert edge_order == 1
0120     out = np.zeros_like(y)
0121     out[0] = y[1] - y[0]
0122     out[1:-1] = (y[2:]-y[:-2])/2.0
0123     out[-1] = y[-1] - y[-2]
0124     return o
0125 
0126 
0127 
0128 def GROUPVEL_(ri_, dump=False):
0129     """
0130     Reproduce G4 GROUPVEL calc by migrating from wavelength to energy 
0131     domain and duplicating the tricky bin shift manoever
0132 
0133                 c         
0134     vg =  ---------------        # angular freq proportional to E for light     
0135             n + E dn/dE
0136 
0137     G4 using this energy domain approach approximating the dispersion part E dn/dE as shown below
0138 
0139                 c                  n1 - n0         n1 - n0               dn        dn    dE          
0140     vg =  -----------       ds = ------------  =  ------------     ~   ------  =  ---- ------- =  E dn/dE 
0141            nn +  ds               log(E1/E0)      log E1 - log E0      d(logE)     dE   dlogE        
0142 
0143 
0144     Start by convert to energy and flipping order of energy 
0145     and refractive values to correspond to increasing energy domain 
0146     """
0147 
0148     ri = ri_.copy()  # refractive index values on wavelength domain
0149     en = hc/wl[::-1]   # eV    
0150     ri = ri[::-1] 
0151 
0152     assert ri.shape == en.shape and len(ri) == len(en)
0153 
0154     n0,n1= ri[:-1],ri[1:]
0155     e0,e1 = en[:-1],en[1:]
0156 
0157     nn = g4_bintrick(ri)
0158     ee = g4_bintrick(en)
0159 
0160     ds = np.zeros_like(ri)  # dispersion term
0161     ds[1:] = (n1-n0)/np.log(e1/e0)  ## have lost a bin from the diff ... 
0162     ds[0] = ds[1]                   ## duping into first value
0163 
0164     # ds is native to midbin anyhow
0165 
0166     vg0 = c_light/nn
0167     vg = c_light/(nn + ds)
0168 
0169     msk = np.logical_or( vg < 0, vg > vg0)
0170     vgm = vg.copy()
0171     vgm[msk] = vg0[msk]
0172 
0173     vgi = np.interp( en, ee, vgm )   # linear interpolation onto original energy values
0174 
0175 
0176     if dump:
0177         labels = "hc/en,en,hc/ee,hc/ee-hc/en,ee,nn,ds,vg0,vg,msk,vgm,vgi"
0178         print "".join(map(lambda _:"%11s"%_, labels.split(",")))
0179         print np.dstack([hc/en,en,hc/ee,hc/ee-hc/en,ee,nn,ds,vg0,vg,msk,vgm,vgi])
0180 
0181     return vgi[::-1]
0182 
0183 
0184 
0185 
0186 class PropTree(object):
0187     def __init__(self):
0188         self.names = os.listdir(os.path.expandvars(self.base))
0189     def subdir(self, sub):
0190         return os.path.expandvars(os.path.join(self.base, sub))
0191     def path(self, sub, fname):
0192         return os.path.expandvars(os.path.join(self.base, sub, fname + ".npy"))
0193     def ary(self, sub, fname):
0194         return np.load(self.path(sub, fname))
0195 
0196 
0197 class replaceGROUPVEL(PropTree):
0198     """
0199     GMaterialLib::replaceGROUPVEL in debug mode writes the 
0200     GROUPVEL calculated from refractive index
0201 
0202     ::
0203 
0204        In [25]: rg.vg("GdDopedLS")
0205        Out[25]: 
0206        array([[  60.    ,  206.2414],
0207               [  80.    ,  206.2414],
0208               [ 100.    ,  206.2414],
0209               [ 120.    ,  198.1083],
0210               [ 140.    ,  181.5257],
0211 
0212 
0213     """
0214     base = "$TMP/replaceGROUPVEL"
0215     def ri_(self, matname):
0216         return self.ary(matname, "refractive_index")
0217     def vg_(self, matname):
0218         return self.ary(matname, "group_velocity")
0219 
0220     def check_all(self, dump=False):
0221         for name in self.names:
0222             self.check(name, dump=dump)
0223 
0224     def check(self, name="GdDopedLS", dump=True):
0225 
0226         rip = self.ri_(name)
0227 
0228         wl = rip[:,0]
0229         ri = rip[:,1]
0230 
0231         vgp = self.vg_(name)
0232         wl2 = vgp[:,0]
0233         vg = vgp[:,1]
0234 
0235         assert np.allclose(wl,wl2)
0236 
0237         vgpy = GROUPVEL_(ri)   ## python implementation of same calc 
0238         vgdf = vg-vgpy
0239 
0240         if dump:
0241             print np.dstack([wl,ri,vg,vgpy, vgdf])
0242 
0243         log.info("check %30s  %s " % (name, vgdf.max()) )  
0244 
0245 
0246 class CGROUPVELTest(PropTree):
0247     """
0248     CMaterialLib::saveGROUPVEL which is invoked by CGROUPVELTest  
0249     writes arrays with the G4 calculated GROUPVEL
0250     """
0251     base="$TMP/CGROUPVELTest"
0252     fnam="saveGROUPVEL"
0253 
0254     def ri(self, matname):
0255         a = self.ary(matname, self.fnam)
0256         return a[0,::-1,2]
0257 
0258     def vg(self, matname):
0259         a = self.ary(matname, self.fnam)
0260         return a[1,::-1,2]
0261 
0262 
0263 
0264 if __name__ == '__main__':
0265     ok = opticks_main()
0266 
0267     mlib = PropLib("GMaterialLib")
0268 
0269     name = "GdDopedLS"
0270     okmat = mlib(name)
0271     okri = okmat[0,:,0]  # refractive index values on wavelength domain, from GMaterialLib cache prior to postCache diddling 
0272 
0273 
0274     cg = CGROUPVELTest()
0275     rg = replaceGROUPVEL()
0276 
0277 
0278 
0279