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 ggeo.py
0004 =========
0005 
0006 See also GNodeLib.py 
0007 
0008 TODO:
0009 
0010 * connection between solids and PV names 
0011 
0012 
0013 
0014 
0015 
0016 
0017 Dumping using single node index and triplet RPO (ridx/pidx/oidx repeat/placement/offset) indexing::
0018 
0019      ggeo.py 0         # world volume 
0020      ggeo.py 1/0/0     # first placement of outer volume of first repeat   
0021      ggeo.py 1/        # missing elements of the triplet default to 0 
0022 
0023      ggeo.py 2/0/0     # first placement of outer volume of second repeat     ridx/pidx/oidx
0024 
0025 When using the triplet form of node specification wildcards are 
0026 accepted in the first ridx slot (eg 5:9) and third oidx slot (eg *), 
0027 for example dumping all all volumes in ridx 5,6,7,8 with::
0028 
0029     epsilon:issues blyth$ ggeo.py 5:9/0/* --brief 
0030     nidx: 69668 triplet: 5000000 sh:5f0014 sidx:    0   nrpo( 69668     5     0     0 )  shape(  95  20                       base_steel0x360d8f0                            Water///Steel) 
0031     nidx: 69078 triplet: 6000000 sh:5e0014 sidx:    0   nrpo( 69078     6     0     0 )  shape(  94  20                             uni10x34cdcb0                            Water///Steel) 
0032     nidx: 68488 triplet: 7000000 sh:5d0014 sidx:    0   nrpo( 68488     7     0     0 )  shape(  93  20                   sStrutBallhead0x352a360                            Water///Steel) 
0033     nidx: 70258 triplet: 8000000 sh:600010 sidx:    0   nrpo( 70258     8     0     0 )  shape(  96  16                     uni_acrylic30x35ff3d0                          Water///Acrylic) 
0034 
0035 
0036 The --names option dumps PV and LV names, for example dumping PV, LV names of the first instance placement 
0037 of all volumes in ridx 1 thru 4::
0038 
0039     epsilon:GItemList blyth$ ggeo.py 1:5/ --names
0040     nrpo( 176632     1     0     0 )                        PMT_3inch_log_phys0x4437d00                             PMT_3inch_log0x4436df0  114 PMT_3inch_pmt_solid0x4436210 
0041     nrpo( 176633     1     0     1 )                       PMT_3inch_body_phys0x4437230                        PMT_3inch_body_log0x4436ce0  112 PMT_3inch_body_solid_ell_ell_helper0x44364d0 
0042     nrpo( 176634     1     0     2 )                     PMT_3inch_inner1_phys0x44372b0                      PMT_3inch_inner1_log0x4436f00  110 PMT_3inch_inner1_solid_ell_helper0x4436560 
0043     nrpo( 176635     1     0     3 )                     PMT_3inch_inner2_phys0x4437360                      PMT_3inch_inner2_log0x4437010  111 PMT_3inch_inner2_solid_ell_helper0x4436640 
0044     nrpo( 176636     1     0     4 )                       PMT_3inch_cntr_phys0x4437410                        PMT_3inch_cntr_log0x4437120  113 PMT_3inch_cntr_solid0x44366d0 
0045     nrpo(  70960     2     0     0 )                         pLPMT_NNVT_MCPPMT0x3cbba60                    NNVTMCPPMTlMaskVirtual0x3cb41a0  103 NNVTMCPPMTsMask_virtual0x3cb3b40 
0046     nrpo(  70961     2     0     1 )                           NNVTMCPPMTpMask0x3c9fe00                           NNVTMCPPMTlMask0x3c9fc80   98 NNVTMCPPMTsMask0x3c9fa80 
0047     nrpo(  70962     2     0     2 )            NNVTMCPPMT_PMT_20inch_log_phys0x3c9fe80                 NNVTMCPPMT_PMT_20inch_log0x3caec40  102 NNVTMCPPMT_PMT_20inch_pmt_solid0x3ca9320 
0048     nrpo(  70963     2     0     3 )           NNVTMCPPMT_PMT_20inch_body_phys0x3caefa0            NNVTMCPPMT_PMT_20inch_body_log0x3caeb60  101 NNVTMCPPMT_PMT_20inch_body_solid0x3cad240 
0049     nrpo(  70964     2     0     4 )         NNVTMCPPMT_PMT_20inch_inner1_phys0x3caf030          NNVTMCPPMT_PMT_20inch_inner1_log0x3caed60   99 NNVTMCPPMT_PMT_20inch_inner1_solid_1_Ellipsoid0x3503950 
0050     nrpo(  70965     2     0     5 )         NNVTMCPPMT_PMT_20inch_inner2_phys0x3caf0f0          NNVTMCPPMT_PMT_20inch_inner2_log0x3caee80  100 NNVTMCPPMT_PMT_20inch_inner2_solid0x3cae8f0 
0051     nrpo(  70966     3     0     0 )                    pLPMT_Hamamatsu_R128600x3cbbae0               HamamatsuR12860lMaskVirtual0x3c9a5c0  109 HamamatsuR12860sMask_virtual0x3c99fb0 
0052     nrpo(  70967     3     0     1 )                      HamamatsuR12860pMask0x3c9b320                      HamamatsuR12860lMask0x3c9b1a0  104 HamamatsuR12860sMask0x3c9afa0 
0053     nrpo(  70968     3     0     2 )       HamamatsuR12860_PMT_20inch_log_phys0x3c9b3b0            HamamatsuR12860_PMT_20inch_log0x3c93920  108 HamamatsuR12860_PMT_20inch_pmt_solid_1_90x3cb68e0 
0054     nrpo(  70969     3     0     3 )      HamamatsuR12860_PMT_20inch_body_phys0x345b3c0       HamamatsuR12860_PMT_20inch_body_log0x3c93830  107 HamamatsuR12860_PMT_20inch_body_solid_1_90x3ca7680 
0055     nrpo(  70970     3     0     4 )    HamamatsuR12860_PMT_20inch_inner1_phys0x3c94040     HamamatsuR12860_PMT_20inch_inner1_log0x345b160  105 HamamatsuR12860_PMT_20inch_inner1_solid_I0x3c96fa0 
0056     nrpo(  70971     3     0     5 )    HamamatsuR12860_PMT_20inch_inner2_phys0x3c94100     HamamatsuR12860_PMT_20inch_inner2_log0x345b290  106 HamamatsuR12860_PMT_20inch_inner2_solid_1_90x3c93610 
0057     nrpo( 304636     4     0     0 )     mask_PMT_20inch_vetolMaskVirtual_phys0x4433460          mask_PMT_20inch_vetolMaskVirtual0x3ca10e0  126 mask_PMT_20inch_vetosMask_virtual0x3ca0a80 
0058     nrpo( 304637     4     0     1 )                 mask_PMT_20inch_vetopMask0x3ca1e40                 mask_PMT_20inch_vetolMask0x3ca1cb0  121 mask_PMT_20inch_vetosMask0x3ca1aa0 
0059     nrpo( 304638     4     0     2 )                  PMT_20inch_veto_log_phys0x3ca5fa0                       PMT_20inch_veto_log0x3ca5470  125 PMT_20inch_veto_pmt_solid_1_20x3ca38b0 
0060     nrpo( 304639     4     0     3 )                 PMT_20inch_veto_body_phys0x3ca57a0                  PMT_20inch_veto_body_log0x3ca5360  124 PMT_20inch_veto_body_solid_1_20x3ca4230 
0061     nrpo( 304640     4     0     4 )               PMT_20inch_veto_inner1_phys0x3ca5820                PMT_20inch_veto_inner1_log0x3ca5580  122 PMT_20inch_veto_inner1_solid0x3ca4f10 
0062     nrpo( 304641     4     0     5 )               PMT_20inch_veto_inner2_phys0x3ca58d0                PMT_20inch_veto_inner2_log0x3ca5690  123 PMT_20inch_veto_inner2_solid0x3ca5130 
0063 
0064 Same for ridx 5 thru 8::
0065 
0066     epsilon:GItemList blyth$ ggeo.py 5:9/ --names 
0067     nrpo(  69668     5     0     0 )                               lUpper_phys0x35b5ac0                                    lUpper0x35b5a00   95 base_steel0x360d8f0 
0068     nrpo(  69078     6     0     0 )                           lFasteners_phys0x34ce040                                lFasteners0x34cdf00   94 uni10x34cdcb0 
0069     nrpo(  68488     7     0     0 )                               lSteel_phys0x352c890                                    lSteel0x352c760   93 sStrutBallhead0x352a360 
0070     nrpo(  70258     8     0     0 )                            lAddition_phys0x35ff770                                 lAddition0x35ff5f0   96 uni_acrylic30x35ff3d0 
0071 
0072 Names for first two placements of ridx 5 thru 8::
0073 
0074     epsilon:GItemList blyth$ ggeo.py 5:9/0:2 --names 
0075     nrpo(  69668     5     0     0 )                               lUpper_phys0x35b5ac0                                    lUpper0x35b5a00   95 base_steel0x360d8f0 
0076     nrpo(  69669     5     1     0 )                               lUpper_phys0x35b5bb0                                    lUpper0x35b5a00   95 base_steel0x360d8f0 
0077     nrpo(  69078     6     0     0 )                           lFasteners_phys0x34ce040                                lFasteners0x34cdf00   94 uni10x34cdcb0 
0078     nrpo(  69079     6     1     0 )                           lFasteners_phys0x34ce140                                lFasteners0x34cdf00   94 uni10x34cdcb0 
0079     nrpo(  68488     7     0     0 )                               lSteel_phys0x352c890                                    lSteel0x352c760   93 sStrutBallhead0x352a360 
0080     nrpo(  68489     7     1     0 )                               lSteel_phys0x352a4a0                                    lSteel0x352c760   93 sStrutBallhead0x352a360 
0081     nrpo(  70258     8     0     0 )                            lAddition_phys0x35ff770                                 lAddition0x35ff5f0   96 uni_acrylic30x35ff3d0 
0082     nrpo(  70259     8     1     0 )                            lAddition_phys0x35ff870                                 lAddition0x35ff5f0   96 uni_acrylic30x35ff3d0 
0083 
0084 
0085 Using suppression of some prolific names can dump all interesting names at once:: 
0086 
0087     epsilon:ana blyth$ ggeo.sh 0:10/ --names --suppress
0088     python3 /Users/blyth/opticks/ana/ggeo.py 0:10/ --names --suppress
0089     [2021-04-24 13:57:42,444] p41483 {/Users/blyth/opticks/ana/ggeo.py:777} INFO - using suppression (HBeam|ixture|anchor|Steel2|Plane|Wall|Receiver|Strut0x|sBar0x) specificed by envvar OPTICKS_GGEO_SUPPRESS 
0090       nrpo(      0     0     0     0 )                                 lWorld0x33e33d0_PV                                    lWorld0x33e33d0  130 sWorld0x33e3370 
0091       nrpo(      1     0     0     1 )                                  pTopRock0x33f3c00                                  lTopRock0x33f3b30   12 sTopRock0x33f3aa0 
0092       nrpo(      2     0     0     2 )                                  pExpHall0x33f40b0                                  lExpHall0x33f3fb0   11 sExpHall0x33f3f20 
0093       nrpo(      3     0     0     3 )                        lUpperChimney_phys0x4e6e210                             lUpperChimney0x4e6c7a0    3 Upper_Chimney0x4e6c340 
0094       nrpo(      4     0     0     4 )                           pUpperChimneyLS0x4e6cbc0                           lUpperChimneyLS0x4e6c8a0    0 Upper_LS_tube0x4e6c450 
0095       nrpo(      5     0     0     5 )                        pUpperChimneySteel0x4e6cc90                        lUpperChimneySteel0x4e6c9b0    1 Upper_Steel_tube0x4e6c570 
0096       nrpo(      6     0     0     6 )                        pUpperChimneyTyvek0x4e6cd60                        lUpperChimneyTyvek0x4e6cac0    2 Upper_Tyvek_tube0x4e6c690 
0097       nrpo(      7     0     0     7 )                               pTopTracker0x4e7f260                                    lAirTT0x4e713c0   10 sAirTT0x4e71260 
0098       nrpo(  65717     0     0   197 )                                  pBtmRock0x33f9200                                  lBtmRock0x33f89c0  129 sBottomRock0x33f4390 
0099       nrpo(  65718     0     0   198 )                               pPoolLining0x33f9160                               lPoolLining0x33f90a0  128 sPoolLining0x33f8a80 
0100       nrpo(  65719     0     0   199 )                           pOuterWaterPool0x3490fa0                           lOuterWaterPool0x33f9470  127 sOuterWaterPool0x33f9360 
0101       nrpo(  67840     0     0  2320 )                          pCentralDetector0x3492d70                            lReflectorInCD0x34916c0  120 sReflectorInCD0x3491470 
0102       nrpo(  67841     0     0  2321 )                               pInnerWater0x3492b80                               lInnerWater0x3491d30  119 sInnerWater0x3491ae0 
0103       nrpo(  67842     0     0  2322 )                                  pAcrylic0x3492c20                                  lAcrylic0x34923a0   90 sAcrylic0x3492150 
0104       nrpo(  67843     0     0  2323 )                                   pTarget0x3492cc0                                   lTarget0x3492a10   89 sTarget0x34927c0 
0105       nrpo( 304632     0     0  3080 )                        lLowerChimney_phys0x4e706b0                             lLowerChimney0x4e6eac0  118 sWaterTube0x4e6e9b0 
0106       nrpo( 304633     0     0  3081 )                      pLowerChimneyAcrylic0x4e6f220                      lLowerChimneyAcrylic0x4e6ece0  115 sChimneyAcrylic0x4e6ebc0 
0107       nrpo( 304634     0     0  3082 )                           pLowerChimneyLS0x4e6f2e0                           lLowerChimneyLS0x4e6eef0  116 sChimneyLS0x4e6ede0 
0108       nrpo( 304635     0     0  3083 )                        pLowerChimneySteel0x4e6f3b0                        lLowerChimneySteel0x4e6f110  117 sChimneySteel0x4e6eff0 
0109       nrpo( 176632     1     0     0 )                        PMT_3inch_log_phys0x43c2530                             PMT_3inch_log0x43c1620  114 PMT_3inch_pmt_solid0x43c0a40 
0110       nrpo( 176633     1     0     1 )                       PMT_3inch_body_phys0x43c1a60                        PMT_3inch_body_log0x43c1510  112 PMT_3inch_body_solid_ell_ell_helper0x43c0d00 
0111       nrpo( 176634     1     0     2 )                     PMT_3inch_inner1_phys0x43c1ae0                      PMT_3inch_inner1_log0x43c1730  110 PMT_3inch_inner1_solid_ell_helper0x43c0d90 
0112       nrpo( 176635     1     0     3 )                     PMT_3inch_inner2_phys0x43c1b90                      PMT_3inch_inner2_log0x43c1840  111 PMT_3inch_inner2_solid_ell_helper0x43c0e70 
0113       nrpo( 176636     1     0     4 )                       PMT_3inch_cntr_phys0x43c1c40                        PMT_3inch_cntr_log0x43c1950  113 PMT_3inch_cntr_solid0x43c0f00 
0114       nrpo(  70961     2     0     1 )                           NNVTMCPPMTpMask0x3c2cad0                           NNVTMCPPMTlMask0x3c2c950   98 NNVTMCPPMTsMask0x3c2c750 
0115       nrpo(  70962     2     0     2 )            NNVTMCPPMT_PMT_20inch_log_phys0x3c2cb50                 NNVTMCPPMT_PMT_20inch_log0x3c2a6b0  102 NNVTMCPPMT_PMT_20inch_pmt_solid0x3c21980 
0116       nrpo(  70963     2     0     3 )           NNVTMCPPMT_PMT_20inch_body_phys0x3c2aa10            NNVTMCPPMT_PMT_20inch_body_log0x3c2a5d0  101 NNVTMCPPMT_PMT_20inch_body_solid0x3c258a0 
0117       nrpo(  70964     2     0     4 )         NNVTMCPPMT_PMT_20inch_inner1_phys0x3c2aaa0          NNVTMCPPMT_PMT_20inch_inner1_log0x3c2a7d0   99 NNVTMCPPMT_PMT_20inch_inner1_solid_1_Ellipsoid0x3497520 
0118       nrpo(  70965     2     0     5 )         NNVTMCPPMT_PMT_20inch_inner2_phys0x3c2ab60          NNVTMCPPMT_PMT_20inch_inner2_log0x3c2a8f0  100 NNVTMCPPMT_PMT_20inch_inner2_solid0x3c2a360 
0119       nrpo(  70967     3     0     1 )                      HamamatsuR12860pMask0x3c394b0                      HamamatsuR12860lMask0x3c39330  104 HamamatsuR12860sMask0x3c39130 
0120       nrpo(  70968     3     0     2 )       HamamatsuR12860_PMT_20inch_log_phys0x3c39540            HamamatsuR12860_PMT_20inch_log0x3c36c90  108 HamamatsuR12860_PMT_20inch_pmt_solid_1_90x3c4a970 
0121       nrpo(  70969     3     0     3 )      HamamatsuR12860_PMT_20inch_body_phys0x33eeec0       HamamatsuR12860_PMT_20inch_body_log0x3c36ba0  107 HamamatsuR12860_PMT_20inch_body_solid_1_90x3c28080 
0122       nrpo(  70970     3     0     4 )    HamamatsuR12860_PMT_20inch_inner1_phys0x3c373b0     HamamatsuR12860_PMT_20inch_inner1_log0x33eec60  105 HamamatsuR12860_PMT_20inch_inner1_solid_I0x3c32bc0 
0123       nrpo(  70971     3     0     5 )    HamamatsuR12860_PMT_20inch_inner2_phys0x3c37470     HamamatsuR12860_PMT_20inch_inner2_log0x33eed90  106 HamamatsuR12860_PMT_20inch_inner2_solid_1_90x3c36980 
0124       nrpo( 304637     4     0     1 )                 mask_PMT_20inch_vetopMask0x3c2eb60                 mask_PMT_20inch_vetolMask0x3c2e9d0  121 mask_PMT_20inch_vetosMask0x3c2e7c0 
0125       nrpo( 304638     4     0     2 )                  PMT_20inch_veto_log_phys0x3c3e950                       PMT_20inch_veto_log0x3c3de20  125 PMT_20inch_veto_pmt_solid_1_20x3c305d0 
0126       nrpo( 304639     4     0     3 )                 PMT_20inch_veto_body_phys0x3c3e150                  PMT_20inch_veto_body_log0x3c3dd10  124 PMT_20inch_veto_body_solid_1_20x3c3cc50 
0127       nrpo( 304640     4     0     4 )               PMT_20inch_veto_inner1_phys0x3c3e1d0                PMT_20inch_veto_inner1_log0x3c3df30  122 PMT_20inch_veto_inner1_solid0x3c3d8c0 
0128       nrpo( 304641     4     0     5 )               PMT_20inch_veto_inner2_phys0x3c3e280                PMT_20inch_veto_inner2_log0x3c3e040  123 PMT_20inch_veto_inner2_solid0x3c3dae0 
0129       nrpo(  68488     5     0     0 )                               lSteel_phys0x34c07b0                                    lSteel0x34c0680   93 sStrutBallhead0x34be280 
0130       nrpo(  69078     6     0     0 )                           lFasteners_phys0x3461f60                                lFasteners0x3461e20   94 uni10x3461bd0 
0131       nrpo(  69668     7     0     0 )                               lUpper_phys0x35499e0                                    lUpper0x3549920   95 base_steel0x35a1810 
0132       nrpo(  70258     8     0     0 )                            lAddition_phys0x3593690                                 lAddition0x3593510   96 uni_acrylic30x35932f0 
0133       nrpo(     10     9     0     0 )                               pPanel_0_f_0x4e7c3c0                                    lPanel0x4e71970    7 sPanel0x4e71750 
0134       nrpo(     11     9     0     1 )                                pPanelTape0x4e7c6a0                                lPanelTape0x4e71b00    6 sPanelTape0x4e71a70 
0135     [2021-04-24 13:57:42,496] p41483 {/Users/blyth/opticks/ana/ggeo.py:601} INFO - supressed 3193 volumes 
0136     epsilon:ana blyth$ 
0137 
0138 
0139 
0140 
0141 
0142 
0143 A convenient visualization workflow is to use the above python triple indexing to find PV names to target, eg::
0144 
0145     OTracerTest --targetpvn lFasteners_phys   ## do not include the 0x reference in the targetted name, as it will differ between machines/invokations 
0146     OTracerTest --target    69078             ## using raw indices is NOT advisable as they go stale very quickly with changed geometry
0147 
0148 Volume idsmry dumping::
0149 
0150     epsilon:ana blyth$ ggeo.py 3199 -i
0151     iden(  3199    5000000     2f001b     -1 )  nrpo(  3199     5     0     0 )  shape(  47  27                pmt-hemi0xc0fed900x3e85f00                       MineralOil///Pyrex) 
0152     iden(  3200    5000001     2e001c     -1 )  nrpo(  3200     5     0     1 )  shape(  46  28            pmt-hemi-vac0xc21e2480x3e85290                           Pyrex///Vacuum) 
0153     iden(  3201    5000002     2b001d     -1 )  nrpo(  3201     5     0     2 )  shape(  43  29        pmt-hemi-cathode0xc2f1ce80x3e842d0                        Vacuum///Bialkali) 
0154     iden(  3202    5000003     2c001e     -1 )  nrpo(  3202     5     0     3 )  shape(  44  30            pmt-hemi-bot0xc22a9580x3e844c0                    Vacuum///OpaqueVacuum) 
0155     iden(  3203    5000004     2d001e     -1 )  nrpo(  3203     5     0     4 )  shape(  45  30         pmt-hemi-dynode0xc346c500x3e84610                    Vacuum///OpaqueVacuum) 
0156     iden(  3204        57f     30001f     -1 )  nrpo(  3204     0     0  1407 )  shape(  48  31             AdPmtCollar0xc2c52600x3e86030          MineralOil///UnstStainlessSteel) 
0157     iden(  3205    5000100     2f001b     -1 )  nrpo(  3205     5     1     0 )  shape(  47  27                pmt-hemi0xc0fed900x3e85f00                       MineralOil///Pyrex) 
0158     iden(  3206    5000101     2e001c     -1 )  nrpo(  3206     5     1     1 )  shape(  46  28            pmt-hemi-vac0xc21e2480x3e85290                           Pyrex///Vacuum) 
0159     iden(  3207    5000102     2b001d     -1 )  nrpo(  3207     5     1     2 )  shape(  43  29        pmt-hemi-cathode0xc2f1ce80x3e842d0                        Vacuum///Bialkali) 
0160 
0161 
0162 ::
0163 
0164     In [52]: gc(10)
0165     gc.identity[10]  nidx/midx/bidx/sidx  [530   8   3   1]  
0166     gc.mlibnames[10] : sPlane0x47c46c0 
0167     gc.blibnames[10] : Air///Air 
0168 
0169     gt : gc.transforms0[530]
0170     [[    0.       1.       0.       0.  ]
0171      [   -1.       0.       0.       0.  ]
0172      [    0.       0.       1.       0.  ]
0173      [20133.6  -6711.2  23504.15     1.  ]]
0174 
0175     tr : transform
0176     [[    0.       1.       0.       0.  ]
0177      [   -1.       0.       0.       0.  ]
0178      [    0.       0.       1.       0.  ]
0179      [20133.6  -6711.2  23504.15     1.  ]]
0180 
0181     it : inverted transform
0182     [[     0.       -1.        0.        0.  ]
0183      [     1.        0.        0.        0.  ]
0184      [    -0.        0.        1.        0.  ]
0185      [  6711.2   20133.6  -23504.15      1.  ]]
0186 
0187     bb : bbox4
0188     [[ 16748.4492 -10141.8008  23497.5         1.    ]
0189      [ 23518.75    -3280.6001  23510.8008      1.    ]]
0190 
0191     cbb : (bb[0]+bb[1])/2.
0192     [20133.5996 -6711.2004 23504.1504     1.    ]
0193 
0194     c4 : center4
0195     [20133.5996 -6711.2002 23504.1504     1.    ]
0196 
0197     ce : center_extent
0198     [20133.6    -6711.2    23504.15    3430.6003]
0199 
0200     ic4 : np.dot( c4, it) : inverse transform applied to center4 
0201     [0. 0. 0. 1.]
0202 
0203     ibb : np.dot( bb, it) : inverse transform applied to bbox4 
0204     [[-3430.6006  3385.1504    -6.6504     1.    ]
0205      [ 3430.6001 -3385.1504     6.6504     1.    ]]
0206 
0207 
0208 """
0209 from __future__ import print_function
0210 import os, re, sys, logging, argparse
0211 log = logging.getLogger(__name__)
0212 import numpy as np
0213 from opticks.ana.blib import BLib
0214 from opticks.ana.key import key_
0215 from opticks.ana.OpticksIdentity import OpticksIdentity
0216 from opticks.ana.rsttable import RSTTable
0217 
0218 tx_load = lambda _:list(map(str.strip, open(_).readlines()))
0219 
0220 def Three2Four(a, w=1): 
0221     """
0222     :param a: array shaped with last dimension 3 
0223     :param w: 1 or 0 (points or vectors)
0224     :return r: corresponding array which last dimension increased from 3 to 4
0225     """
0226     s = list(a.shape)
0227     assert s[-1] == 3, "unexpected shape %r , last dimension must be 3" % s
0228     assert w in (1,0), "w must be 1 or 0" 
0229     s[-1] = 4 
0230     b = np.ones(s) if w == 1 else np.zeros(s)
0231     d = len(s)
0232     if d == 1:
0233         b[:3] = a
0234     if d == 2:
0235         b[:,:3] = a
0236     elif d == 3:
0237         b[:,:,:3] = a
0238     elif d == 4:
0239         b[:,:,:,:3] = a
0240     else:
0241         assert 0, "unexpected shape %r " % s
0242     pass
0243     r = b
0244     return r 
0245 
0246 
0247 
0248 class GGeo(object):
0249     KEY = key_(os.environ["OPTICKS_KEY"])
0250     KEYDIR = KEY.keydir
0251     VERSION = KEY.version
0252     DIGEST = KEY.digest
0253     SUPPRESS_ = os.environ.get("OPTICKS_GGEO_SUPPRESS", "") 
0254     SUPPRESS_PTN = "("+SUPPRESS_.replace(",","|")+")" 
0255     SUPPRESS = None if SUPPRESS_PTN == "()" else re.compile(SUPPRESS_PTN) 
0256 
0257 
0258     @classmethod
0259     def Suppress(cls, name):
0260         """
0261         :param name: 
0262         :return bool: True when the name contains one of the suppressed strings provided by envvar OPTICKS_GGEO_SUPPRESS
0263         """
0264         if cls.SUPPRESS is None:
0265             return False
0266         else:
0267             return not cls.SUPPRESS.search(name) is None 
0268         pass
0269 
0270     volume_names    = list(map(lambda _:"volume_%s" % _, "transforms center_extent bbox meshes nodeinfo identity".split()))
0271     placement_names = list(map(lambda _:"placement_%s"%_, "itransforms iidentity".split()))
0272     face_names      = list(map(lambda _:"face_%s"%_,  "sensors boundaries nodes indices".split()))
0273     vertex_names    = list(map(lambda _:"vertex_%s"%_,  "colors normals vertices".split()))
0274     names = volume_names + placement_names + face_names + vertex_names
0275 
0276     all_volume_names = list(map(lambda _:"all_volume_%s" % _, "nodeinfo identity center_extent bbox transforms inverse_transforms".split()))
0277 
0278     PV = "{keydir}/GNodeLib/all_volume_PVNames.txt"   # full node list of PV names
0279     LV = "{keydir}/GNodeLib/all_volume_LVNames.txt"   # full node list of LV names
0280     MS = "{keydir}/GItemList/GMeshLib.txt"            # list of unique solid names 
0281  
0282     @classmethod   
0283     def Path(cls, ridx, name, subdir="GMergedMesh", alldir="GNodeLib"): 
0284         """
0285         :param ridx: -1 for all volumes from GNodeLib, 0,1,2,3,... for GMergedMesh Composite "Solids"
0286         """
0287         keydir = cls.KEYDIR 
0288         if ridx == -1:
0289             fmt = "{keydir}/{alldir}/{name}.npy"
0290         elif ridx > -1: 
0291             fmt = "{keydir}/{subdir}/{ridx}/{name}.npy"
0292         else:
0293             assert 0
0294         pass 
0295         return os.path.expandvars(fmt.format(**locals()))
0296 
0297     @classmethod   
0298     def TxtPath(cls, name): 
0299         keydir = cls.KEYDIR 
0300         return os.path.expandvars("{keydir}/{name}".format(**locals()))
0301 
0302     @classmethod   
0303     def Array(cls, ridx, name): 
0304         path = cls.Path(ridx, name) 
0305         a = np.load(path)
0306         return a 
0307 
0308     @classmethod   
0309     def Txt(cls, name): 
0310         path = cls.TxtPath(name) 
0311         return np.array(tx_load(path)) 
0312         #return np.loadtxt(path, dtype="|S100")
0313 
0314     @classmethod   
0315     def Attn(cls, ridx, name): 
0316         return "_%s_%d" % (name, ridx) 
0317 
0318     def __init__(self, args=None):
0319         self.args = args
0320         self.suppress_count = 0 
0321         keydir = self.KEYDIR
0322         path = os.path.expandvars("{keydir}/GMergedMesh".format(**locals()))
0323         mmidx = sorted(map(int,os.listdir(path)))
0324         num_repeats = len(mmidx)
0325         self.num_repeats = num_repeats 
0326         blib = BLib(keydir)
0327         self.blib = np.array(blib.names().split("\n"))
0328         self.pv = np.loadtxt(self.PV.format(**locals()), dtype="|S100")
0329         self.lv = np.loadtxt(self.LV.format(**locals()), dtype="|S100")
0330         self.ms = np.loadtxt(self.MS.format(**locals()), dtype="|S100")
0331         self.msn = list(map(lambda _:_.decode('utf-8'),self.ms)) 
0332 
0333         self.mlib = self.get_txt("GItemList/GMeshLib.txt", "_mlib") 
0334         self.midx = (self.all_volume_identity[:,2] >> 16) & 0xffff  
0335         self.bidx = (self.all_volume_identity[:,2] >>  0) & 0xffff  
0336         self.mlibnames = self.mlib[self.midx]   # mesh/lv names
0337         self.blibnames = self.blib[self.bidx]   # boundary names
0338         self.mmidx = mmidx 
0339 
0340     #mlib = property(lambda self:self.get_txt("GItemList/GMeshLib.txt", "_mlib")) 
0341     #midx = property(lambda self:(self.all_volume_identity[:,2] >> 16) & 0xffff ) 
0342     #bidx = property(lambda self:(self.all_volume_identity[:,2] >>  0) & 0xffff ) 
0343     #mlibnames = property(lambda self:self.mlib[self.midx])   # mesh/lv names
0344     #blibnames = property(lambda self:self.blib[self.bidx])   # boundary names
0345 
0346     def get_array(self, ridx, name):
0347         """
0348         Array actually loaded only the first time
0349         """
0350         attn = self.Attn(ridx,name) 
0351         if getattr(self, attn, None) is None:
0352             a = self.Array(ridx, name)
0353             setattr(self, attn, a)
0354         pass
0355         return getattr(self, attn)
0356 
0357     def get_txt(self, name, attn):
0358         if getattr(self, attn, None) is None:
0359             a = self.Txt(name)
0360             setattr(self, attn, a)
0361         pass
0362         return getattr(self, attn)
0363 
0364     def summary0(self):
0365         log.info("num_repeats:{gg.num_repeats}".format(gg=self))
0366         for ridx in range(self.num_repeats):
0367             for name in self.names:
0368                 a = self.get_array(ridx,name)
0369                 print("{ridx:2d} {name:25s}  {shape!r}".format(ridx=ridx,name=name,shape=a.shape))
0370             pass
0371             print()
0372         pass 
0373 
0374     def get_tot_volumes(self):
0375         return self.get_num_volumes(-1)
0376     num_volumes = property(get_tot_volumes)
0377 
0378     def get_num_volumes(self, ridx):
0379         name = "all_volume_transforms" if ridx == -1 else "volume_transforms"
0380         a = self.get_array(ridx,name)
0381         return a.shape[0]
0382 
0383     def get_num_placements(self, ridx):
0384         if ridx == -1:
0385             return 1
0386         pass
0387         name = "placement_itransforms"
0388         a = self.get_array(ridx,name)
0389         return a.shape[0]
0390 
0391     def summary(self):
0392         """
0393         Array shapes
0394         """
0395         log.info("num_repeats:{gg.num_repeats}".format(gg=self))
0396 
0397         fmt = "{ridx:10d} {num_volumes:15d} {num_placements:15d} {num_placed_vol:15d}"
0398         print("%10s %15s %15s %15s" % ("ridx","num_volumes", "num_placements", "num_placed_vol"))
0399         tot_vol = 0 
0400         tot_vol_ = self.get_num_volumes(-1)
0401         for ridx in range(self.num_repeats):
0402             num_volumes = self.get_num_volumes(ridx)
0403             num_placements = self.get_num_placements(ridx)
0404             num_placed_vol = num_volumes*num_placements
0405             tot_vol += num_placed_vol
0406             print(fmt.format(**locals()))
0407         pass
0408         print("%10s %15s %15s %15d" % ("","","tot_vol:",tot_vol)) 
0409         print("%10s %15s %15s %15d" % ("","","tot_vol_:",tot_vol_)) 
0410         print()
0411 
0412         for name in self.names:
0413             shape = []
0414             for ridx in range(self.num_repeats):
0415                 a = self.get_array(ridx,name)
0416                 shape.append("{shape!r:20s}".format(shape=a.shape))
0417             pass     
0418             print("{name:25s} {shape}".format(name=name,shape="".join(shape)))
0419         pass
0420         for name in self.all_volume_names:
0421             ridx = -1
0422             a = self.get_array(ridx,name)
0423             print("{name:25s} {shape!r}".format(name=name,shape=a.shape))
0424         pass
0425 
0426     def get_all_transforms(self):
0427         """
0428         Access transforms of all volumes via triplet indexing.
0429         The ordering of the transforms is not the same as all_volume_transforms.
0430         However can still check the match using the identity info to find
0431         the node index.
0432         """
0433         log.info("get_all_transforms and do identity consistency check : triplet->node->triplet")
0434         tot_volumes = self.get_tot_volumes()
0435         tr = np.zeros([tot_volumes,4,4],dtype=np.float32)
0436         count = 0 
0437         for ridx in range(self.num_repeats):
0438             num_placements = self.get_num_placements(ridx)
0439             num_volumes = self.get_num_volumes(ridx)
0440             for pidx in range(num_placements):
0441                 for oidx in range(num_volumes):
0442                     nidx = self.get_node_index(ridx,pidx,oidx)
0443                     tr[nidx] = self.get_transform(ridx,pidx,oidx)
0444                     count += 1 
0445                 pass
0446             pass
0447         pass
0448         assert tot_volumes == count  
0449         all_volume_transforms = self.get_array(-1,"all_volume_transforms")
0450         assert np.allclose( all_volume_transforms, tr )
0451         return tr  
0452 
0453 
0454     def get_transform_n(self, nidx):
0455         all_volume_transforms = self.get_array(-1,"all_volume_transforms")
0456         return all_volume_transforms[nidx] 
0457 
0458     def get_inverse_transform_n(self, nidx):
0459         all_volume_inverse_transforms = self.get_array(-1,"all_volume_inverse_transforms")
0460         return all_volume_inverse_transforms[nidx] 
0461 
0462 
0463     def get_inverse_transform(self, ridx, pidx, oidx):
0464         """
0465         No triplet way to do this yet, have to go via node index
0466         """
0467         nidx = self.get_node_index(ridx,pidx,oidx)
0468         return self.get_inverse_transform_n(nidx)
0469 
0470     def get_transform(self, ridx, pidx, oidx):
0471         """
0472         :param ridx: repeat idx, 0 for remainder
0473         :param pidx: placement index of the instance, 0 for remainder
0474         :param oidx: offset index, within the instance or among the remainder
0475 
0476         DONE in get_all_transforms, verified both routes match for all nodes including the remainders
0477         """
0478         ## native (triplet access)
0479         placement_itransforms = self.get_array(ridx, "placement_itransforms") # identity for remainder
0480         volume_transforms = self.get_array(ridx, "volume_transforms")   # within the instance or from root for remainder
0481         itr = placement_itransforms[pidx]
0482         vtr = volume_transforms[oidx].reshape(4,4)
0483 
0484         ggt = np.dot( vtr, itr )  
0485 
0486         ## cross reference to the node index
0487         nidx = self.get_node_index(ridx,pidx,oidx)
0488 
0489         ## nodeindex access 
0490         ntr = self.get_transform_n(nidx)
0491 
0492         assert np.allclose( ggt, ntr )
0493         return ggt
0494 
0495     def get_node_index(self, ridx, pidx, oidx):
0496         """
0497         :param ridx: repeat index, 0 for remainder
0498         :param pidx: placement index of the instance, 0 for remainder
0499         :param oidx: offset index, within the instance or among the remainder
0500         :return nidx: all_volume node index 
0501 
0502         The node index obtained from the placement_identity is used
0503         to do a reverse conversion check using nrpo, looking up 
0504         the triplet identity from the node index. These indices
0505         are consistency checked with the inputs.
0506         """
0507         placement_iidentity = self.get_array(ridx, "placement_iidentity")  # eg shape  (672, 5, 4)
0508         iid = placement_iidentity[pidx, oidx]
0509         nidx = iid[0]    
0510 
0511         nidx2,ridx2,pidx2,oidx2 = self.nrpo[nidx]
0512         assert nidx2 == nidx 
0513         assert ridx2 == ridx 
0514         assert pidx2 == pidx 
0515 
0516         if oidx2 != oidx:
0517             log.debug("mismatch oidx2:%d(from nrpo/iid)  oidx:%d(from range(num_volumes)) " % (oidx2, oidx)) 
0518         pass
0519         #assert oidx2 == oidx 
0520         return nidx 
0521 
0522 
0523     def get_lvidx(self, ridx, pidx, oidx):
0524         nidx = self.get_node_index(ridx,pidx,oidx)
0525         midx = self.midx[nidx] 
0526         return midx
0527    
0528     def get_soname(self, ridx, pidx, oidx, trim=False):
0529         """
0530         :param ridx: repeat index
0531         :param pidx: placement index
0532         :param oidx: offset index within the repeated piece of geometry 
0533         :param trim: when True trims any 0xdeadbeef pointer suffix  
0534         """
0535         midx = self.get_lvidx(ridx, pidx, oidx)
0536         msn = self.msn[midx]
0537         return msn.split("0x")[0] if trim else msn  
0538 
0539     def make_nrpo(self):
0540         """
0541         See okc/OpticksIdentity::Decode
0542         """
0543         gg = self
0544         avi = gg.all_volume_identity
0545         tid = avi[:,1] 
0546         nrpo = OpticksIdentity.NRPO(tid)
0547         return nrpo
0548 
0549     def _get_nrpo(self):
0550         if getattr(self,'_nrpo',None) is None:
0551             setattr(self,'_nrpo',self.make_nrpo())
0552         return self._nrpo 
0553     nrpo = property(_get_nrpo)
0554 
0555     def get_triplet_index(self, nidx):
0556         """
0557          cf ggeo/GGeo::getIdentity
0558 
0559         :param nidx: all_volume node index 
0560         :return nidx,ridx,pidx,oidx:
0561         """
0562         return self.nrpo[nidx]
0563 
0564 
0565     all_volume_center_extent = property(lambda self:self.get_array(-1,"all_volume_center_extent"))
0566     all_volume_bbox          = property(lambda self:self.get_array(-1,"all_volume_bbox"))
0567     all_volume_identity      = property(lambda self:self.get_array(-1,"all_volume_identity"))
0568     all_volume_transforms    = property(lambda self:self.get_array(-1,"all_volume_transforms"))  
0569 
0570 
0571     volume_transforms0  = property(lambda self:self.get_array(0,"volume_transforms")) 
0572     volume_transforms1  = property(lambda self:self.get_array(1,"volume_transforms"))
0573     volume_transforms2  = property(lambda self:self.get_array(2,"volume_transforms"))
0574     volume_transforms3  = property(lambda self:self.get_array(3,"volume_transforms"))
0575     volume_transforms4  = property(lambda self:self.get_array(4,"volume_transforms"))
0576     volume_transforms5  = property(lambda self:self.get_array(5,"volume_transforms"))
0577 
0578     placement_itransforms0 = property(lambda self:self.get_array(0,"placement_itransforms"))
0579     placement_itransforms1 = property(lambda self:self.get_array(1,"placement_itransforms"))
0580     placement_itransforms2 = property(lambda self:self.get_array(2,"placement_itransforms"))
0581     placement_itransforms3 = property(lambda self:self.get_array(3,"placement_itransforms"))
0582     placement_itransforms4 = property(lambda self:self.get_array(4,"placement_itransforms"))
0583     placement_itransforms5 = property(lambda self:self.get_array(5,"placement_itransforms"))
0584 
0585     placement_iidentity0 = property(lambda self:self.get_array(0,"placement_iidentity"))
0586     placement_iidentity1 = property(lambda self:self.get_array(1,"placement_iidentity"))
0587     placement_iidentity2 = property(lambda self:self.get_array(2,"placement_iidentity"))
0588     placement_iidentity3 = property(lambda self:self.get_array(3,"placement_iidentity"))
0589     placement_iidentity4 = property(lambda self:self.get_array(4,"placement_iidentity"))
0590     placement_iidentity5 = property(lambda self:self.get_array(5,"placement_iidentity"))
0591 
0592 
0593     def __call__(self,*args):
0594         """
0595         A single integer argument is interpreted as a node index (nidx), 
0596         otherwise 2 or 3 args are interpreted as ridx,pidx,oidx with 
0597         oidx defaulting to zero if not provided.::
0598 
0599             gg(0,0,1000)   # triplet addressing to remainder volumes, NB all are ridx:0 pidx:0 
0600             gg(2792)       # same volume via node indexing 
0601 
0602             gg(5,0,2)      # first placement of cathode volume (DYB)
0603             gg(3201)       # same volume via node indexing   
0604 
0605             gg(5,671,2)    # last placement of cathode volume (DYB)
0606             gg(11410)      # same volume via node indexing 
0607 
0608         """
0609         log.debug("args %s len(args) %d " % (str(args), len(args))) 
0610 
0611         nidxs = []
0612 
0613         if len(args) == 1: 
0614             nidx = args[0]
0615             nidxs.append(nidx)
0616         elif len(args) == 2 or len(args) == 3:
0617             if len(args) == 2:
0618                 a_ridx,a_pidx = args
0619                 a_oidx = "*"
0620             elif len(args) == 3:
0621                 a_ridx,a_pidx,a_oidx = args
0622             else:
0623                 assert 0 
0624             pass 
0625             log.debug("a_ridx %s a_pidx %s a_oidx %s" % (a_ridx,a_pidx,a_oidx)) 
0626 
0627             if type(a_ridx) is int:
0628                 ridxs = [a_ridx]
0629             elif ":" in a_ridx:
0630                 ridxs = range(*map(int,a_ridx.split(":")))
0631             else:
0632                 assert 0, a_ridx
0633             pass
0634           
0635             log.debug("ridxs %s " % str(ridxs))
0636 
0637             if type(a_pidx) is int:
0638                 pidxs = [a_pidx]
0639             elif ":" in a_pidx:
0640                 pidxs = range(*map(int,a_pidx.split(":")))
0641             else:
0642                 assert 0, a_pidx
0643             pass
0644  
0645 
0646             for ridx in ridxs:
0647                 if a_oidx == "*":
0648                     num_volumes = self.get_num_volumes(ridx)
0649                     oidxs = range(num_volumes)
0650                 else:
0651                     oidxs = [a_oidx]
0652                 pass 
0653                 for pidx in pidxs:
0654                     for oidx in oidxs:
0655                         nidx = self.get_node_index(ridx,pidx,oidx)
0656                         nidxs.append(nidx)
0657                     pass
0658                 pass
0659             pass
0660         else:
0661             assert 0, "expecting argument of 1/2/3 integers"
0662         pass
0663 
0664         for nidx in nidxs:
0665             if self.args.nidx:
0666                 print(nidx)
0667             elif self.args.brief:
0668                 self.brief(nidx) 
0669             elif self.args.names:
0670                 self.names(nidx) 
0671             elif self.args.sonames:
0672                 self.sonames(nidx) 
0673             elif self.args.soidx:
0674                 self.soidx(nidx) 
0675             else: 
0676                 self.dump_node(nidx) 
0677             pass
0678         pass
0679 
0680         if self.suppress_count > 0:
0681             log.info("supressed %d volumes " % self.suppress_count )
0682         pass
0683 
0684 
0685     def idsmry(self, nidx):
0686         """
0687         volume identity summary 
0688         """
0689         iden = self.all_volume_identity[nidx]
0690         nidx2,triplet,shape,_ = iden  
0691         assert nidx == nidx2
0692         sidx = iden[-1].view(np.int32)  
0693         iden_s = "nidx:{nidx:6d} triplet:{triplet:8x} sh:{sh:6x} sidx:{sidx:5d} ".format(nidx=nidx,triplet=triplet,sh=shape,sidx=sidx)
0694 
0695         nrpo = self.nrpo[nidx]
0696         nrpo_s = "nrpo( %6d %5d %5d %5d )" % tuple(nrpo)
0697 
0698         midx = self.midx[nidx] 
0699         bidx = self.bidx[nidx] 
0700         shape_s = "shape( %3d %3d  %40s %40s)" % (midx,bidx, self.mlibnames[nidx], self.blibnames[nidx] )
0701         print( "%s  %s  %s " % (iden_s, nrpo_s, shape_s) )
0702 
0703 
0704     def names(self, nidx):
0705         pv = self.pv[nidx].decode('utf-8')  
0706 
0707         lv = self.lv[nidx].decode('utf-8')  
0708         midx = self.midx[nidx] 
0709         nrpo = self.nrpo[nidx]
0710         msn = self.msn[midx]
0711 
0712         would_suppress = self.Suppress(pv) or self.Suppress(lv) or self.Suppress(msn)
0713         suppress = self.args.suppress and would_suppress
0714         sup = "S" if would_suppress else " "
0715         if suppress:
0716             self.suppress_count += 1 
0717         else: 
0718             nrpo_s = "nrpo( %6d %5d %5d %5d )" % tuple(nrpo)
0719             print( "%1s %s %50s %50s  %3d %s " % (sup, nrpo_s, pv, lv, midx, msn) )
0720         pass  
0721 
0722     def sonames(self, nidx):
0723         midx = self.midx[nidx] 
0724         msn = self.msn[midx]
0725         if args.terse:
0726             print(msn)
0727         elif args.errout:
0728             print(midx,file=sys.stdout)
0729             print(msn, file=sys.stderr)
0730         else:
0731             print( "%3d %s " % (midx, msn) )
0732         pass
0733 
0734     def soidx(self, nidx):
0735         midx = self.midx[nidx] 
0736         msn = self.msn[midx]
0737         if args.terse:
0738             print(midx)
0739         elif args.errout:
0740             print(midx,file=sys.stdout)
0741             print(msn, file=sys.stderr)
0742         else:
0743             print( "%3d %s " % (midx, msn) )
0744         pass
0745 
0746 
0747     def bbsmry(self, nidx):
0748         gg = self
0749         bb = gg.all_volume_bbox[nidx]
0750         ce = gg.all_volume_center_extent[nidx]
0751         print(" {nidx:5d} {ce!s:20s} ".format(nidx=nidx,bb=bb,ce=ce))
0752 
0753     def brief(self,nidx):
0754         gg = self
0755         gg.idsmry(nidx)
0756 
0757     def dump_node(self,nidx):
0758         gg = self
0759         gg.idsmry(nidx)
0760 
0761         #print("gg.mlibnames[%d] : %s " % (i, gg.mlibnames[nidx]) )
0762         #print("gg.blibnames[%d] : %s " % (i, gg.blibnames[nidx]) )
0763 
0764         bb = gg.all_volume_bbox[nidx]
0765         ce = gg.all_volume_center_extent[nidx]
0766         c4 = ce.copy()
0767         c4[3] = 1.
0768         
0769         tr = gg.all_volume_transforms[nidx]
0770         it = np.linalg.inv(tr) 
0771         ibb = np.dot( bb, it )   ## apply inverse transform to the volumes bbox (mn,mx), should give symmetric (mn,mx)   
0772         cbb = (bb[0]+bb[1])/2.   ## center of bb should be same as c4
0773         assert np.allclose( c4, cbb )
0774 
0775         ic4 = np.dot( c4, it )   ## should be close to origin
0776         gt = gg.all_volume_transforms[nidx]  
0777 
0778         print("\ngt : gg.all_volume_transforms[%d]" % nidx)
0779         print(gt)
0780         print("\ntr : transform")
0781         print(tr)
0782         print("\nit : inverted transform")
0783         print(it)
0784         print("\nbb : bbox4")
0785         print(bb)
0786         print("\ncbb : (bb[0]+bb[1])/2.")
0787         print(cbb)
0788         print("\nc4 : center4")
0789         print(c4)
0790         print("\nce : center_extent")
0791         print(ce)
0792         print("\nic4 : np.dot( c4, it) : inverse transform applied to center4 : expect close to origin ")
0793         print(ic4)
0794         print("\nibb : np.dot( bb, it) : inverse transform applied to bbox4 : expect symmetric around origin")
0795         print(ibb)
0796 
0797     def consistency_check(self):
0798         gg = self
0799         log.info("consistency_check")
0800         gg.summary()
0801 
0802         tr = gg.get_all_transforms()
0803 
0804 
0805 
0806 def parse_args(doc, **kwa):
0807     np.set_printoptions(suppress=True, precision=3, linewidth=200)
0808     parser = argparse.ArgumentParser(doc)
0809     parser.add_argument(     "idx", nargs="*", help="Node index or triplet index of form \"1/0/0\" or \"1/\" to dump.")
0810     parser.add_argument(     "--nidx", default=False, action="store_true", help="Dump only the node index, useful when the input is triplet index." ) 
0811     parser.add_argument(     "--level", default="info", help="logging level" ) 
0812     parser.add_argument(  "-c","--check", action="store_true", help="Consistency check" ) 
0813     parser.add_argument(  "-i","--idsmry", action="store_true", help="Slice identity summary interpreting idx as slice range." ) 
0814     parser.add_argument(  "-b","--bbsmry", action="store_true", help="Slice bbox summary interpreting idx as slice range." ) 
0815     parser.add_argument(  "--brief", action="store_true", help="Brief summary of nodes selected by idx." ) 
0816     parser.add_argument(  "--names", action="store_true", help="Identity and PV/LV/SO names of  nodes selected by idx." ) 
0817     parser.add_argument(  "--mm",   action="store_true", help="MM Names" ) 
0818     parser.add_argument(  "--mmtrim",   action="store_true", help="MM Names with 0x addr trimmed" ) 
0819     parser.add_argument(  "--mmtrimpath",  default="$TMP/mm.txt", help="path to mmtrim names, used by CSGOptix/cxr_table.sh " ) 
0820     parser.add_argument(  "--mmsmry",   action="store_true", help="MM Names" ) 
0821     parser.add_argument(  "--sonames", action="store_true", help="Dump solid names for the nodes selected by idx." ) 
0822     parser.add_argument(  "--soidx", action="store_true", help="Dump solid_idx (aka: lvidx or meshidx/midx) for the nodes selected by node or triplet idx." ) 
0823     parser.add_argument(  "--suppress", action="store_true", help="Suppress dumping/listing volumes with names including a list of suppressed strings provided by envvar OPTICKS_GGEO_SUPPRESS")
0824     parser.add_argument(  "-t","--terse", action="store_true", help="Terse output" ) 
0825     parser.add_argument(  "-s","--errout", action="store_true", help="Split output writing to both stderr and stdout" ) 
0826     args = parser.parse_args()
0827     fmt = '[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s'
0828     logging.basicConfig(level=getattr(logging,args.level.upper()), format=fmt)
0829     if len(args.idx) == 0:
0830         args.idx = [0]
0831     pass
0832     return args  
0833 
0834 
0835 def misc(gg):
0836     bbox = gg.all_volume_bbox
0837     print("gg.all_volume_bbox\n",bbox)
0838     t000 = gg.get_transform(0,0,0)
0839     print("t000\n",t000)
0840 
0841 def triplet_(rpo):
0842     elem = []
0843     for s in rpo.split("/"):
0844         if s == "*" or ":" in s:
0845             elem.append(s)
0846         else:
0847             if s.isdigit():         # isnumeric is py3 only, so use isdigit as seems -ve not used here ?
0848                 elem.append(int(s))
0849             else:
0850                 elem.append(0)
0851             pass
0852         pass
0853     pass
0854     return elem 
0855 
0856 
0857 if __name__ == '__main__':
0858     args = parse_args(__doc__)
0859 
0860     log.info("[ GGeo")
0861     gg = GGeo(args)
0862     log.info("] GGeo")
0863 
0864     if args.suppress:
0865         log.info("using suppression %s specificed by envvar OPTICKS_GGEO_SUPPRESS " % gg.SUPPRESS_PTN ) 
0866     else:
0867         log.info("suppression %s specificed by envvar OPTICKS_GGEO_SUPPRESS can be enabled with --suppression option " % gg.SUPPRESS_PTN ) 
0868     pass
0869 
0870     if args.check:
0871         gg.consistency_check()
0872 
0873     elif args.mmsmry:
0874 
0875         labels = ["ridx","plc","vol", "component name", "note (%s)" % gg.DIGEST[:8] ]
0876         hfmt = ["%4s", "%6s", "%5s", "%-40s", "%-25s" ]
0877         rfmt = ["%4d", "%6d", "%5d", "%-40s", "%-25s" ]
0878         wids = [4,  6, 5, 40 , 25 ]
0879         pre  =  ["",  "",   "", "   ", "  " ]
0880         post =  ["",  "",   "", "   ", "  " ]
0881 
0882         mm_notes = {}
0883         mm_notes[0] = "non-repeated remainder"
0884         mm_notes[9] = "repeated parts of TT"
0885 
0886         t = np.empty( [len(gg.mmidx), len(labels)], dtype=np.object ) 
0887 
0888         for i, ridx in enumerate(gg.mmidx):
0889             pidx = 0 
0890             oidx = 0 
0891             num_vol = gg.get_num_volumes(ridx) 
0892             num_plc = gg.get_num_placements(ridx) 
0893             msn = gg.get_soname(ridx, pidx, oidx, trim=args.mmtrim)
0894             mm_name = "%d:%s" % (num_vol, msn)
0895             mm_note = mm_notes.get(i, "")
0896             row = tuple([ridx, num_plc, num_vol, mm_name, mm_note])
0897             line = " ".join(rfmt) % row
0898             print(line)
0899             t[i] = row 
0900         pass
0901         rst = RSTTable.Render(t, labels, wids, hfmt, rfmt, pre, post )
0902         print(rst) 
0903         pass
0904 
0905     elif args.mm or args.mmtrim:
0906         mm_names = []
0907         for ridx in gg.mmidx:
0908             pidx = 0 
0909             oidx = 0 
0910             num_vol = gg.get_num_volumes(ridx) 
0911             msn = gg.get_soname(ridx, pidx, oidx, trim=args.mmtrim)
0912             mm_name = "%d:%s" % (num_vol, msn)
0913             mm_names.append(mm_name)
0914         pass
0915         txt = "\n".join(mm_names)
0916         print(txt)
0917         mmtrimpath = os.path.expandvars(args.mmtrimpath) 
0918         log.info("writing this list to mmtrimpath %s " % mmtrimpath )
0919         open(mmtrimpath, "w").write(txt)   
0920 
0921     elif args.idsmry or args.bbsmry:
0922 
0923         beg = int(args.idx[0])
0924         end = int(args.idx[1]) if len(args.idx) > 1 else min(gg.num_volumes,int(args.idx[0])+50) 
0925         for idx in list(range(beg, end)):
0926             if args.idsmry:
0927                 gg.idsmry(idx)
0928             elif args.bbsmry:
0929                 gg.bbsmry(idx)
0930             else:
0931                 pass
0932             pass
0933         pass
0934     else:
0935         for idx in args.idx:
0936             if str(idx).isdigit():     # isnumeric is py3 only, as here dont need -ve ? switch to isdigit
0937                 gg(int(idx))
0938             else:
0939                 gg(*triplet_(idx))
0940             pass  
0941         pass
0942     pass
0943 
0944