File indexing completed on 2026-04-09 07:48:52
0001
0002 """
0003 CSGFoundryAB.py
0004 =================
0005
0006 """
0007
0008 import numpy as np, os, textwrap, builtins
0009 from opticks.ana.fold import Fold, STR
0010 from opticks.CSG.CSGFoundry import CSGFoundry, CSGPrim, CSGNode
0011 from opticks.sysrap.stree import sn, snode, stree
0012
0013
0014 class CSGFoundryAB(object):
0015 def __init__(self, a, b, _ip=0):
0016 self.a = a
0017 self.b = b
0018 self.ip = _ip
0019
0020 self.check_4x4("node")
0021 self.check_4x4("prim")
0022
0023 qwns = "npa nbb pbb".split()
0024 for qwn in qwns:
0025 self.compare_qwn(qwn)
0026 pass
0027
0028 def _set_ip(self, ip):
0029 self._ip = ip
0030
0031 a = self.a
0032 b = self.b
0033
0034 ann = a.pr.numNode[ip]
0035 bnn = b.pr.numNode[ip]
0036 ano = a.pr.nodeOffset[ip]
0037 bno = b.pr.nodeOffset[ip]
0038 alv = a.pr.meshIdx[ip]
0039 blv = b.pr.meshIdx[ip]
0040 amn = a.meshname[alv]
0041 bmn = b.meshname[blv]
0042 anode = a.node[ano:ano+ann]
0043 bnode = b.node[bno:bno+bnn]
0044
0045 atc = a.ni.typecode[ano:ano+ann]
0046 btc = b.ni.typecode[bno:bno+bnn]
0047
0048 aco = a.ni.comptran[ano:ano+ann] >> 31
0049 bco = b.ni.comptran[bno:bno+bnn] >> 31
0050
0051 self.alv = alv
0052 self.blv = blv
0053 self.amn = amn
0054 self.bmn = bmn
0055 self.anode = anode
0056 self.bnode = bnode
0057 self.atc = atc
0058 self.btc = btc
0059 self.aco = aco
0060 self.bco = bco
0061
0062 def __repr__(self):
0063 return "AB %d alv %s:%s blv %s:%s " % ( self._ip, self.alv, self.amn, self.blv, self.bmn )
0064
0065 def brief(self):
0066 a = self.a
0067 b = self.b
0068 lines = []
0069 lines.append("CSGFoundryAB.brief")
0070 lines.append(a.brief())
0071 lines.append(b.brief())
0072 return "\n".join(lines)
0073
0074 def _get_ip(self):
0075 return self._ip
0076
0077 ip = property(_get_ip, _set_ip)
0078
0079
0080 def check_pr(self):
0081 a = self.a
0082 b = self.b
0083 assert a.pr.dtype.names == b.pr.dtype.names
0084
0085 lines = []
0086 lines.append("CSGFoundryAB.check_pr")
0087 expr_ = "len(np.where(a.pr.%(name)s != b.pr.%(name)s )[0])"
0088 for name in a.pr.dtype.names:
0089 expr = expr_ % locals()
0090 lines.append("%80s : %s " % (expr, eval(expr)))
0091 pass
0092 return "\n".join(lines)
0093
0094 def check_prim_lv(self):
0095 a = self.a
0096 b = self.b
0097 a_lv = a.prim.view(np.int32)[:,1,1]
0098 b_lv = b.prim.view(np.int32)[:,1,1]
0099 assert np.all(a_lv==b_lv)
0100
0101 assert np.all(np.unique(a_lv)==np.unique(b_lv))
0102 assert np.all(np.unique(a_lv,return_counts=True)[1]==np.unique(b_lv,return_counts=True)[1])
0103
0104 a_lvu = np.unique(a_lv)
0105 b_lvu = np.unique(b_lv)
0106 assert np.all(np.arange(len(a_lvu))==a_lvu)
0107 assert np.all(np.arange(len(b_lvu))==b_lvu)
0108 assert np.all(a.meshname==b.meshname)
0109
0110 def check_node_tr(self):
0111 """
0112 """
0113 a = self.a
0114 b = self.b
0115
0116 atr = a.node[:,3,3].view(np.int32) & 0x7fffffff
0117 btr = b.node[:,3,3].view(np.int32) & 0x7fffffff
0118
0119 np.c_[np.unique(atr,return_counts=True)]
0120 np.c_[np.unique(btr,return_counts=True)]
0121
0122 def check_node_index(self):
0123 """
0124 For A the node index increments until 15927,
0125 thats probably the repeated unbalanced nodes
0126 """
0127 a = self.a
0128 b = self.b
0129
0130 aidx = a.node[:,1,3].view(np.int32)
0131 aidx_ = aidx[:15927]
0132 assert np.all( aidx_ == np.arange(len(aidx_)) )
0133
0134 bidx = b.node[:,1,3].view(np.int32)
0135 assert np.all( bidx == np.arange(len(bidx)) )
0136
0137
0138 def descLVDetail(self, lvid):
0139 a = self.a
0140 b = self.b
0141 lines = []
0142 lines.append("CSGFoundryAB.descLVDetail")
0143 lines.append(a.descLVDetail(lvid))
0144 lines.append(b.descLVDetail(lvid))
0145 return "\n".join(lines)
0146
0147 def check_prim(self):
0148 """
0149 np.all(a.prim.view(np.int32)[:,:2].reshape(-1,8)==b.prim.view(np.int32)[:,:2].reshape(-1,8))
0150 """
0151 a = self.a
0152 b = self.b
0153 ab = self
0154 setattr(self,"prim_numNode",np.where(a.prim[:,0,0].view(np.int32)!=b.prim[:,0,0].view(np.int32))[0])
0155
0156 lines = []
0157 lines.append("CSGFoundryAB.check_prim")
0158 EXPR = filter(None,textwrap.dedent(r"""
0159
0160 #(numNode,nodeOffset,tranOffset,planOffset)(sbtIndexOffset,meshIdx,repeatIdx,primIdx)
0161 a.prim.view(np.int32)[:,:2].reshape(-1,8)
0162 b.prim.view(np.int32)[:,:2].reshape(-1,8)
0163
0164 #(numNode,nodeOffset,tranOffset,planOffset)
0165 a.prim[:,0].view(np.int32)
0166 #(numNode,nodeOffset,tranOffset,planOffset)
0167 b.prim[:,0].view(np.int32)
0168 len(np.where(a.prim[:,0]!=b.prim[:,0])[0])
0169 #numNode
0170 len(np.where(a.prim[:,0,0].view(np.int32)!=b.prim[:,0,0].view(np.int32))[0])
0171 len(ab.prim_numNode)
0172 np.c_[np.unique(a.primname[ab.prim_numNode],return_counts=True)]
0173 #nodeOffset
0174 len(np.where(a.prim[:,0,1].view(np.int32)!=b.prim[:,0,1].view(np.int32))[0])
0175 #tranOffset
0176 len(np.where(a.prim[:,0,2].view(np.int32)!=b.prim[:,0,2].view(np.int32))[0])
0177 #planOffset
0178 len(np.where(a.prim[:,0,3].view(np.int32)!=b.prim[:,0,3].view(np.int32))[0])
0179 #(sbtIndexOffset,meshIdx,repeatIdx,primIdx)
0180 a.prim[:,1].view(np.int32)
0181 #(sbtIndexOffset,meshIdx,repeatIdx,primIdx)
0182 b.prim[:,1].view(np.int32)
0183 len(np.where(a.prim[:,1]!=b.prim[:,1])[0])
0184
0185 # A : (sbtIndexOffset,meshIdx,repeatIdx,primIdx) where prim_numNode discrepant
0186 a.prim[ab.prim_numNode,1].view(np.int32)
0187 # B : (sbtIndexOffset,meshIdx,repeatIdx,primIdx) where prim_numNode discrepant
0188 b.prim[ab.prim_numNode,1].view(np.int32)
0189
0190 # A : (numNode,nodeOffset,tranOffset,planOffset) where prim_numNode discrepant
0191 a.prim[ab.prim_numNode,0].view(np.int32)
0192
0193 # B : (numNode,nodeOffset,tranOffset,planOffset) where prim_numNode discrepant
0194 b.prim[ab.prim_numNode,0].view(np.int32)
0195
0196 """).split("\n"))
0197 for expr in EXPR:
0198 val = str(eval(expr)) if not expr.startswith("#") else ""
0199 fmt = "%-80s \n%s\n" if len(val.split("\n")) > 1 else "%-80s : %s"
0200 lines.append(fmt % (expr, val))
0201 pass
0202 return "\n".join(lines)
0203
0204
0205 def check_inst(self):
0206 a = self.a
0207 b = self.b
0208 ab = self
0209 setattr(self, "inst", np.max(np.abs(a.inst[:,:,:3]-b.inst[:,:,:3]).reshape(-1,12),axis=1))
0210
0211 lines = []
0212 lines.append("CSGFoundryAB.check_inst")
0213 EXPR = filter(None,textwrap.dedent(r"""
0214 np.all(a.inst[:,0,3].view(np.int32)==b.inst[:,0,3].view(np.int32))
0215 np.all(a.inst[:,1,3].view(np.int32)==b.inst[:,1,3].view(np.int32))
0216 np.all(a.inst[:,2,3].view(np.int32)==b.inst[:,2,3].view(np.int32))
0217 np.all(a.inst[:,3,3].view(np.int32)==b.inst[:,3,3].view(np.int32))
0218 np.all(a.inst[:,:,3].view(np.int32)==b.inst[:,:,3].view(np.int32))
0219 # deviation counts in the 12 floats of the inst transform
0220 len(np.where(ab.inst>1e-4)[0])
0221 len(np.where(ab.inst>1e-3)[0])
0222 len(np.where(ab.inst>1e-2)[0])
0223 """).split("\n"))
0224
0225 for expr in EXPR:
0226 val = str(eval(expr)) if not expr.startswith("#") else ""
0227 fmt = "%-80s \n%s\n" if len(val.split("\n")) > 1 else "%-80s : %s"
0228 lines.append(fmt % (expr, val))
0229 pass
0230 return "\n".join(lines)
0231
0232 def check_solid(self):
0233 """
0234 [:,1] houses (numPrim, primOffset, type, padding)
0235 """
0236 a = self.a
0237 b = self.b
0238 lines = []
0239 lines.append("CSGFoundryAB.check_solid")
0240 EXPR = filter(None,textwrap.dedent(r"""
0241 np.all(a.solid[:,1]==b.solid[:,1])
0242 np.c_[a.solid[:,1,:2],b.solid[:,1,:2]]#(numPrim,primOffset)
0243 """).split("\n"))
0244 for expr in EXPR:
0245 val = str(eval(expr)) if not expr.startswith("#") else ""
0246 fmt = "%-80s \n%s\n" if len(val.split("\n")) > 1 else "%-80s : %s"
0247 lines.append(fmt % (expr, val))
0248 pass
0249 return "\n".join(lines)
0250
0251 def check_names(self):
0252 lines = []
0253 lines.append("CSGFoundryAB.check_names")
0254 EXPR = filter(None,textwrap.dedent(r"""
0255 np.all(a.mmlabel==b.mmlabel)
0256 np.all(a.primname==b.primname)
0257 np.all(a.meshname==b.meshname)
0258 """).split("\n"))
0259 for expr in EXPR:
0260 val = str(eval(expr)) if not expr.startswith("#") else ""
0261 fmt = "%-80s \n%s\n" if len(val.split("\n")) > 1 else "%-80s : %s"
0262 lines.append(fmt % (expr, val))
0263 pass
0264 return "\n".join(lines)
0265
0266
0267
0268 def check_4x4(self, name):
0269 a = self.a
0270 b = self.b
0271 ab = self
0272 am = getattr(a, name)
0273 bm = getattr(b, name)
0274
0275 lines = []
0276 lines.append("CSGFoundryAB.check_4x4 %s " % name )
0277 lines.append(" a.%s.shape : %s " % (name, str(am.shape)) )
0278 lines.append(" b.%s.shape : %s " % (name, str(bm.shape)) )
0279 if am.shape == bm.shape:
0280 abm = np.max(np.abs(am-bm).reshape(-1,16), axis=1 )
0281 setattr(self,name, abm )
0282 expr = "len(np.where(ab.%(name)s>0.01)[0])" % locals()
0283 lines.append(" %s : %s " % (expr, str(eval(expr))))
0284 else:
0285 setattr(self,name,None)
0286 pass
0287 return STR("\n".join(lines))
0288
0289 def compare_qwn(self, qwn="npa"):
0290 a = self.a
0291 b = self.b
0292 ab = self
0293 aq = getattr(a, qwn)
0294 bq = getattr(b, qwn)
0295 setattr(ab, qwn, np.max(np.abs(aq-bq), axis=1 ))
0296
0297
0298 def check_tran(self):
0299 lines = []
0300 lines.append(self.check_4x4("tran"))
0301 lines.append(self.check_4x4("itra"))
0302 return "\n".join(lines)
0303
0304
0305
0306
0307 def checktran(a, b, ip, order="A" ):
0308 """
0309 ::
0310
0311 at,bt,atran,btran,dtran = checktran(a,b,2000,"S")
0312
0313 # order A:asis, S:sum_sort, U:unique
0314 #
0315
0316 """
0317 ann = a.pr.numNode[ip]
0318 bnn = b.pr.numNode[ip]
0319
0320 ano = a.pr.nodeOffset[ip]
0321 bno = b.pr.nodeOffset[ip]
0322
0323 atr = ( a.ni.comptran[ano:ano+ann] & 0x7fffffff ) - 1
0324 btr = ( b.ni.comptran[bno:bno+bnn] & 0x7fffffff ) - 1
0325
0326 at = atr[atr>-1]
0327 bt = btr[btr>-1]
0328
0329 if len(at) == len(bt):
0330 if order == "U":
0331 atran = np.unique( a.tran[at], axis=0 )
0332 btran = np.unique( b.tran[bt], axis=0 )
0333 elif order == "S":
0334
0335 a_tran = a.tran[at]
0336 b_tran = b.tran[bt]
0337
0338 ss_atran = np.argsort(np.sum(a_tran, axis=(1,2)))
0339 ss_btran = np.argsort(np.sum(b_tran, axis=(1,2)))
0340
0341 atran = a_tran[ss_atran]
0342 btran = b_tran[ss_btran]
0343 elif order == "A":
0344 atran = a.tran[at]
0345 btran = b.tran[bt]
0346 pass
0347 dtran = np.sum( np.abs( atran - btran), axis=(1,2) )
0348 else:
0349 dtran = None
0350 pass
0351 return at,bt,atran,btran,dtran
0352
0353
0354
0355 def checkprim(a, b, ip, dump=False, order="A"):
0356 """
0357 :param a: cf
0358 :param b: cf
0359 :param ip: prim index
0360 :param dump:
0361 :param order: control tran order with one of "ASIS" "SS" "U"
0362
0363 * A : default
0364 * S : sorting by sum of 16 elements
0365 * U : apply np.unique
0366
0367 """
0368 fmt = "ip:%(ip)4d "
0369 alv = a.pr.meshIdx[ip]
0370 blv = b.pr.meshIdx[ip]
0371 slv = "/" if alv == blv else "*"
0372 fmt += "lv:%(alv)3d%(slv)s%(blv)3d "
0373
0374 ann = a.pr.numNode[ip]
0375 bnn = b.pr.numNode[ip]
0376 snn = "/" if ann == bnn else "*"
0377 fmt += "nn:%(ann)3d%(snn)s%(bnn)3d "
0378
0379 ano = a.pr.nodeOffset[ip]
0380 bno = b.pr.nodeOffset[ip]
0381 sno = "/" if ano == bno else "%"
0382 fmt += "no:%(ano)5d%(sno)s%(bno)5d "
0383
0384 anode = a.node[ano:ano+ann]
0385 bnode = b.node[bno:bno+bnn]
0386
0387 amn = a.meshname[alv]
0388 bmn = b.meshname[blv]
0389 smn = "/" if amn == bmn else "*"
0390
0391 atc = a.ni.typecode[ano:ano+ann]
0392 btc = b.ni.typecode[bno:bno+bnn]
0393
0394 aco = a.ni.comptran[ano:ano+ann] >> 31
0395 bco = b.ni.comptran[bno:bno+bnn] >> 31
0396
0397 at,bt,atran,btran,dtran = checktran(a,b,ip,order=order)
0398
0399 if not dtran is None:
0400 wtran = np.where( dtran > 1e-2 )[0]
0401 ltran = len(wtran)
0402 else:
0403 ltran = -1
0404 pass
0405 stran = ":" if ltran == 0 else "*"
0406 fmt += " %(order)s tr%(stran)s%(ltran)2d "
0407
0408 fmt += "mn:%(amn)40s%(smn)s%(bmn)40s "
0409
0410
0411 if ip == 0: print(fmt)
0412 line = fmt % locals()
0413 if "*" in line or dump:
0414 print(line)
0415 pass
0416
0417 if snn == "/":
0418 expr = "np.c_[atran, btran], np.c_[atc, aco, btc, bco], dtran"
0419 return expr, np.c_[atran, btran], np.c_[atc, aco, btc, bco], dtran
0420 else:
0421 return None
0422 pass
0423
0424
0425 def checkprims(a, b, ip0=-1, ip1=-1, order="A"):
0426 """
0427 ::
0428
0429 In [1]: checkprims(a,b)
0430 ip:2375 lv: 93/ 93 nn: 15*127 no:15209/15209 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0431 ip:2376 lv: 93/ 93 nn: 15*127 no:15224%15336 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0432 ip:2377 lv: 93/ 93 nn: 15*127 no:15239%15463 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0433 ip:2378 lv: 93/ 93 nn: 15*127 no:15254%15590 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0434 ip:2379 lv: 93/ 93 nn: 15*127 no:15269%15717 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0435 ip:2380 lv: 93/ 93 nn: 15*127 no:15284%15844 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0436 ip:2381 lv: 93/ 93 nn: 15*127 no:15299%15971 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0437 ip:2382 lv: 93/ 93 nn: 15*127 no:15314%16098 mn: solidSJReceiverFastern/ solidSJReceiverFastern
0438 ip:3126 lv: 99/ 99 nn: 31*1023 no:23372%24268 mn: uni1/ uni1
0439
0440 """
0441 if ip0 < 0 and len(a.pr.numNode) == len(b.pr.numNode):
0442 ip0 = 0
0443 ip1 = len(a.pr.numNode)
0444 pass
0445 for i in range(ip0, ip1):
0446 checkprim(a,b, i, order=order)
0447 pass
0448
0449
0450 def eprint(_lines, _globals, _locals ):
0451 """
0452 Double hash "##" on the line suppresses printing the repr
0453 of the evaluation.
0454
0455 Lines are first search for the position of the first "=" character.
0456 If present the position is used to extract the key and expression
0457 to be evaluated in the scope provided by the arguments.
0458 The key with value is planted into the calling scope using builtins.
0459
0460
0461 HMM : PARSE NEEDS TO DETECT PARAMETER EQUAL
0462
0463 ACTUALLY MAYBE EASIER TO PREFIX WITH "_ =" IF NO "KEY =" prefix is found
0464
0465 np.c_[np.unique(a.npa[a.pno[a.pnn>1],0].view(np.int32),return_counts=True)]
0466
0467
0468 TODO : support semicolon on the lines
0469 """
0470 lines = list(filter(None, textwrap.dedent(_lines).split("\n") ))
0471
0472 print("-" * 100)
0473 print("\n".join(lines))
0474 print("-" * 100)
0475
0476 for line in lines:
0477 eq = line.find("=")
0478 eeq = line.find("==")
0479 no_key = eq == -1 or ( eq > -1 and eq == eeq )
0480 if no_key:
0481 exp = line
0482 val = eval(exp, _globals, _locals )
0483 print(exp)
0484 print(repr(val))
0485 elif eq > -1:
0486 key, exp = line[:eq].strip(), line[1+eq:]
0487 val = eval(exp, _globals, _locals )
0488
0489 setattr(builtins, key, val)
0490 print(line)
0491 if line.find("##") == -1:
0492 print(repr(val))
0493 pass
0494 else:
0495 print(line)
0496 pass
0497 pass
0498 print("-" * 100)
0499
0500
0501
0502 if __name__ == '__main__':
0503
0504 np.set_printoptions(edgeitems=10)
0505
0506 CSGPrim.Type()
0507
0508 A = Fold.Load("$A_CFBASE/CSGFoundry", symbol="A")
0509 B = Fold.Load("$B_CFBASE/CSGFoundry", symbol="B")
0510
0511 print(repr(A))
0512 print(repr(B))
0513
0514 a = CSGFoundry.Load("$A_CFBASE", symbol="a")
0515 b = CSGFoundry.Load("$B_CFBASE", symbol="b")
0516 print(a.brief())
0517 print(b.brief())
0518
0519 ab = CSGFoundryAB(a,b)
0520
0521 print(ab.check_names())
0522 print(ab.check_pr())
0523 print(ab.check_prim())
0524 print(ab.check_solid())
0525 print(ab.check_tran())
0526 print(ab.check_inst())
0527
0528 print(ab.brief())
0529
0530 a_meshname = np.char.partition(a.meshname.astype("U"),"0x")[:,0]
0531 b_meshname = b.meshname.astype("U")
0532 assert np.all( a_meshname == b_meshname )
0533
0534 a_primname = np.char.partition(a.primname.astype("U"),"0x")[:,0]
0535 b_primname = b.primname.astype("U")
0536
0537 an = A.SSim.stree._csg.sn
0538 bn = B.SSim.stree._csg.sn
0539
0540
0541 a_tc = a.node[:,3,2].view(np.int32)
0542 b_tc = b.node[:,3,2].view(np.int32)
0543 w_tc = np.where( a_tc != b_tc )[0]
0544
0545
0546 eprint(r"""
0547
0548 w_solid = np.where( a.solid != b.solid )[0] ##
0549 w_solid.shape
0550
0551 np.all( a.nbd == b.nbd ) # node boundary : ONLY REMAINING DEVIANT
0552
0553 np.all( a.nix == b.nix ) # nodeIdx local to the compound solid
0554 w_nix = np.where(a.nix != b.nix)[0] ##
0555 w_nix.shape
0556
0557 w_npa3 = np.where( np.abs(a.npa - b.npa) > 1e-3 )[0] ## node param deviations
0558 w_npa3.shape
0559
0560 w_nbb3 = np.where( np.abs(a.nbb - b.nbb) > 3e-2 )[0] ## node bbox deviations
0561 w_nbb3.shape
0562 w_nbb2 = np.where( np.abs(a.nbb - b.nbb) > 1e-2 )[0] ## node bbox deviations
0563 w_nbb2.shape
0564
0565 w_nbb = np.where( a.nbb != b.nbb ) ##
0566 np.abs( a.nbb[w_nbb] - b.nbb[w_nbb] ).max() # max deviation in bbox
0567
0568
0569 np.all( a.ntc == b.ntc ) # node typecode
0570 np.all( a.ncm == b.ncm ) # node complement
0571 np.all( a.ntr == b.ntr ) # node transform idx + 1
0572
0573 np.all( a.pnn == b.pnn ) # prim numNode
0574 np.all( a.pno == b.pno ) # prim nodeOffset
0575 np.all( a.pto == b.pto ) # prim tranOffset
0576 np.all( a.ppo == b.ppo ) # prim planOffset
0577
0578 np.all( a.psb == b.psb ) # prim sbtIndexOffset
0579 np.all( a.plv == b.plv ) # prim lvid/meshIdx
0580 np.all( a.prx == b.prx ) # prim ridx/repeatIdx
0581 np.all( a.pix == b.pix ) # primIdx
0582
0583
0584 w_pbb3 = np.where( np.abs(a.pbb-b.pbb) > 1e-3 )[0] ## prim bbox deviations
0585 w_pbb3.shape
0586
0587 w_pbb2 = np.where( np.abs(a.pbb-b.pbb) > 1e-2 )[0] ## prim bbox deviations
0588 w_pbb2.shape
0589
0590
0591 np.all( a.snp == b.snp ) # solid numPrim
0592 np.all( a.spo == b.spo ) # solid primOffset
0593
0594 np.all( a.sce == b.sce ) # solid center_extent
0595
0596 w_sce = np.where( np.abs( a.sce - b.sce ) > 1e-3 )[0] # solid center_extent
0597 w_sce.shape
0598
0599 w_npa = np.where( a.npa != b.npa )[0] ## int32 3,7,15 in first param slot
0600 w_npa.shape # these are subNum on compound root nodes : and new workflow omits it
0601
0602 np.all( a.npa == b.npa ) # after setting crn_subnum in b the node param match exactly
0603
0604
0605 tab = np.c_[np.unique(a.npa[a.pno[a.pnn>1],0].view(np.int32),return_counts=True)] ## subNum picked from node param 0 of compound root nodes
0606 tab
0607
0608 tab2 = np.c_[np.unique( a.crn_subnum, return_counts=True )] ## encapsulate subnum using "crn" compound root node
0609 tab2
0610
0611 _ = np.c_[np.unique(a.pnn[a.pnn>1], return_counts=True )] # looks like subnum same as the prim num nodes
0612
0613 np.all( a.crn_subnum == b.crn_subnum)
0614 np.all( a.crn_suboff == b.crn_suboff)
0615
0616
0617 an = A.SSim.extra.GGeo.bnd_names ## A side still GGeo in charge
0618 bn = B.SSim.stree.standard.bnd_names ## B side using stree
0619 w_bnd_names = np.where( an != bn )[0] ##
0620 w_bnd_names
0621
0622 np.c_[an[w_bnd_names],bn[w_bnd_names]] # discrepant bnd names
0623
0624 """, globals(), locals() )
0625 pass
0626