Back to home page

EIC code displayed by LXR

 
 

    


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

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 env/graphics/ciexyz/planck.py
0023 ==================================
0024 
0025 Generated photons feature a plateau from 80nm to 205nm followed 
0026 by step up to join the curve.  
0027 
0028 * pushing out generation to 10M, 100M doesnt change this feature
0029   other than scaling the plateau level
0030 
0031 * low bins of pk.pdf feature very small numbers..., 
0032   attempt to avoid numerical issues using arbitrary scaling moves the cut 
0033   to 210nm, also a step structure in the generated distribution is 
0034   apparent 
0035 
0036 * problem presumably from too great a probability range... so that 
0037   never get to generate any at lower bins somehow convolved with 
0038   numerical precision to cause steps ? 
0039 
0040 ::
0041 
0042     In [11]: pk.cdf[0]
0043     Out[11]: 0.0
0044 
0045     In [12]: pk.cdf[1]
0046     Out[12]: 1.0708856422955714e-17
0047 
0048     In [13]: pk.cdf[-1]
0049     Out[13]: 0.99999999999999045
0050 
0051 
0052     In [18]: u.min()
0053     Out[18]: 3.6289631744068629e-07
0054 
0055     In [19]: u.max()
0056     Out[19]: 0.99999976714026029
0057 
0058     In [20]: u[u>3.6289631744068629e-07].min()
0059     Out[20]: 1.9915794777780604e-06
0060 
0061 
0062 Huh 200nm is on the plateau but the cdf is not outrageously small there ?::
0063 
0064     In [27]: pk.cdf[15001]
0065     Out[27]: 0.0065557782251502248
0066 
0067     In [28]: np.where(np.logical_and(w > 200.,w<200.1) )
0068     Out[28]: (array([15001, 15002, 15003, 15004, 15005, 15006, 15007, 15008, 15009, 15010]),)
0069 
0070 
0071 Moving the low wavelength up from 80nm to 200nm avoids the objectionable plateau and cliff,
0072 but there is still a stepping structure though.
0073 
0074 
0075 **RESOLVED** 
0076 
0077     The interpolation was using too coarse a binning that 
0078     worked fine for most of the distribution, but not good enough 
0079     for the low wavelength turn on 
0080 
0081 
0082 
0083 
0084 * http://www.yale.edu/ceo/Documentation/ComputingThePlanckFunction.pdf
0085 
0086 ::
0087 
0088 
0089              2hc^2 w^-5
0090    Bw(T) =   ----------------
0091               e^(beta) - 1
0092 
0093 
0094              hc/kT
0095     beta =   -----
0096               w
0097 
0098 
0099 Where e^(beta) >> 1  (Wien approximation) Plank tends to 
0100 
0101 
0102    Bw(T) = 2hc^2 w^-5 e^(-beta)
0103              
0104 
0105 
0106 
0107 
0108 Need inverse CDF of Planck to put into texture
0109 follow ggeo-/GProperty<T>::createInverseCDF
0110 
0111 * even domain on 0:1 (needed for quick texture lookup)
0112 * sample the CDF across this domain  
0113 
0114 * env/geant4/geometry/collada/g4daeview/sample_cdf.py
0115 
0116 """
0117 
0118 import os
0119 import matplotlib.pyplot as plt
0120 import numpy as np
0121 
0122 try:
0123     from scipy.constants import h,c,k
0124 except ImportError:
0125     h = 6.62606957e-34
0126     c = 299792458.0
0127     k = 1.3806488e-23
0128 
0129 
0130 def planck(nm, K=6500., arbitrary=True):
0131     if arbitrary:
0132        wav = nm
0133        a = np.power(200,5)
0134     else:
0135        wav = nm/1e9 
0136        a = 2.0*h*c*c
0137     pass
0138 
0139     hc_over_kT = (h*c)/(k*K)
0140 
0141     b = hc_over_kT*1e9/nm
0142 
0143     return a/(np.power(wav,5) * np.expm1(b))  
0144 
0145 
0146 def planck_plot():
0147     w = np.arange(100,1000,1)
0148     for temp in np.arange(4000,10000,500):
0149         intensity = planck(w, temp)
0150         plt.plot(w, intensity, '-') 
0151 
0152 
0153 
0154 def construct_inverse_cdf_0( bva, bdo,  N ):
0155     """
0156     :param bva: basis values normalized to unity
0157     :param bdo: basis domain 
0158     :param N: number of bins to use across 0:1 range
0159 
0160     Note the np.interp xp<->fp inversion,
0161     
0162     (x)    ido: freshly minted 0:1 domain  
0163     (xp)  cbva: monotonic CDF in range 0:1, 
0164     (fp)   bdo: basis domain  
0165 
0166     """
0167     assert np.allclose( bva.sum(), 1.0 )
0168     assert len(bva) == len(bdo)
0169 
0170     cbva = np.cumsum(bva)   # NB no careful mid bin handling yet
0171 
0172     ido = np.linspace(0,1,N)  
0173     
0174     iva = np.interp( ido, cbva, bdo )  
0175   
0176     return iva, ido 
0177 
0178 
0179 
0180 class Planck(object):
0181     def __init__(self, w, K=6500, N_idom=4096):
0182 
0183         bb = planck(w, K=K)
0184 
0185         # interpret into bins (so one less entry)
0186         avg = (bb[:-1]+bb[1:])/2.   # average of bin end values
0187         wid = np.diff(w)            # bin width
0188         mid = (w[:-1]+w[1:])/2.     # bin middle
0189 
0190         pdf = avg*wid
0191 
0192         pdf /= pdf.sum()  # NB norming later avoids last bin excursion in generated distribution
0193 
0194         dom = w
0195         cdf = np.empty(len(dom))
0196         cdf[0] = 0.                        # lay down zero probability 1st bin 
0197         np.cumsum(pdf, out=cdf[1:])
0198         
0199         idom = np.linspace(0,1, N_idom)  
0200         icdf = np.interp( idom, cdf, dom )  
0201 
0202  
0203         self.avg = avg
0204         self.wid = wid
0205         self.mid = mid
0206         self.pdf = pdf
0207 
0208         self.cdf = cdf
0209         self.dom = dom
0210 
0211         self.idom = idom
0212         self.icdf = icdf
0213 
0214 
0215     def __call__(self, u ):
0216         gen = np.interp( u, self.idom, self.icdf )   
0217         self.u = u
0218         self.gen = gen 
0219         return gen
0220 
0221 
0222 
0223 def cf_gsrclib():
0224     p = os.path.expandvars("$TMP/ggeo/GSourceLibTest/gsrclib.npy")
0225     sl = np.load(p)
0226     return sl[0,:,0]
0227     
0228 
0229 
0230 if __name__ == '__main__':
0231 
0232     plt.ion()
0233 
0234     w = np.arange(80.,801,.1, dtype=np.float64)
0235 
0236     pk = Planck(w, K=6500)
0237 
0238     u = np.random.rand(1000000)
0239 
0240     gen = pk(u)    
0241  
0242 
0243     nm = 100
0244     wb = w[::nm]
0245     hn, hd = np.histogram(gen, wb)
0246     assert np.all(hd == wb)
0247     assert len(hn) == len(wb) - 1   # looses one bin 
0248 
0249     s_avg = pk.pdf * hn.sum() * nm
0250 
0251 
0252     # hmm lands spot on with -post
0253 
0254     plt.plot( hd[:-1], hn , drawstyle="steps-post")  # -pre -mid -post
0255 
0256     plt.plot( pk.mid, s_avg ) 
0257 
0258     plt.axis([w.min()-100, w.max()+100, 0, s_avg.max()*1.1])
0259 
0260 
0261     plt.show()
0262 
0263 
0264 
0265