summaryrefslogtreecommitdiff
path: root/PyGan/data/rDragView_proc/vtu_tools.py
blob: 392c89a2369724cc6d7e078797180381533b41d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
#!/usr/bin/env python

# Purpose:
#  Functions related to creating vtu files.
# 
# Copyright:
#  Copyright (C) 2025 Polytechnique Montreal
#  This library is free software; you can redistribute it and/or modify it under the terms 
#  of the GNU Lesser General Public License as published by the Free Software Foundation; 
#  either version 2.1 of the License, or (at your option) any later version
# 
# Author(s): Atyab A. Calloo

import numpy as np
import vtk
from math import sqrt


def create_vtu(Geom, Mcro, Trck, Flux, test_name, verbose):
   """
   Uses Geom, Macr, Trck, Flux LCM objects to create the material and flux vtu
   objects that can then be viewed in Paraview. 

   Args:
      Geom            : geometry LCM object
      Macr            : macrolib LCM object
      Trck            : tracking LCM object
      Flux            : flux LCM object
      test_name       : file name of the c2m procedure to be computed
      verbose         : controls write output level
   """

   ### RECOVER STATEVECTORS FROM LCM OBJECTS
   SV_geom = Geom["STATE-VECTOR"]
   SV_mcro = Mcro["STATE-VECTOR"]
   SV_trck = Trck["STATE-VECTOR"]
   SV_flux = Flux["STATE-VECTOR"]
   GV_mcro = Mcro["GROUP"]

   ### RECOVER RELEVANT MATERIAL, TRACKING AND FLUX DATA
   n_mat = SV_mcro[1]
   n_reg = SV_trck[0]
   n_unk = SV_trck[1]
   n_dim = SV_trck[8]
   spltl = SV_trck[25]
   n_grp = SV_flux[0]
   geom_type = SV_trck[5] # 5 - Car2D; 7 - Car3D; 8 - Hex2D; 9 - Hex3D
   ### RECOVER OTHER DATA FROM LCM OBJECTS
   mat_array  = Trck["MATCOD"]

   ### INITIALIZE FLUX AND NUSIGF ARRAYS
   flx_array = np.zeros((n_grp,n_unk), dtype='f')
   nsf_mat_array = np.zeros((n_grp,n_mat), dtype='f')
   nsf_reg_array = np.zeros((n_grp,n_reg), dtype='f')
   ### PICK UP FLUX AND NUSIGF ARRAYS 
   for i_g in range(n_grp):
      flx_array[i_g,:] = Flux["FLUX"][i_g]
      nsf_mat_array[i_g,:] = GV_mcro[i_g]["NUSIGF"]
      ### CREATE NUSIGF ARRAY BY REGION
      for i_reg in range(n_reg):
         ind_mat = mat_array[i_reg]
         if ind_mat == 0 : 
            nsf_reg_array[i_g,i_reg] = 0
         elif ind_mat > 0 : 
            ind_mat=ind_mat-1
            nsf_reg_array[i_g,i_reg] = nsf_mat_array[i_g,ind_mat]

   ### REBUILD MESHES
   lx, meshx, ly, meshy, lz, meshz = rebuild_meshes(Geom)

   ### FIND HEXAGON CENTRES IF NEEDED
   if geom_type==8 or geom_type==9:
      side = Geom["SIDE"][0]
      n_hex=int(n_reg/(lz*3*spltl**2))
      n_ring = int((sqrt(  float((4*n_hex-1)/3)  )+1.)/2.)
      hex_centres = compute_hex_centres(n_hex,n_ring,side)

   ### CALL TO MAKE_VTU GEOM-DEPENDENT
   nscat = SV_trck[6]
   ielem = SV_trck[7]
   if (geom_type == 5) or (geom_type == 7):
      if verbose > 0: 
         print('\n>>>>>>> GENERATING VTUs FOR ',test_name,'.')
         print('-'*74 + '\n')
      make_vtu_cartesian_geom(lx,meshx,ly,meshy,lz,meshz,mat_array,"material",test_name )
      for i_g in range(n_grp):
         make_vtu_cartesian_geom(lx,meshx,ly,meshy,lz,meshz,flx_array[i_g,:],"flux",test_name ,nscat=nscat,ielem=ielem,i_g=i_g)
   elif (geom_type == 8) or (geom_type == 9):
      if verbose > 0: 
         print('\n>>>>>>> GENERATING VTUs FOR ',test_name,'.')
         print('-'*74 + '\n')
      make_vtu_hexagonal_geom(n_hex,side,hex_centres,mat_array,"material",test_name ,spltl=spltl,meshz=meshz,lz=lz)
      for i_g in range(n_grp):
         make_vtu_hexagonal_geom(n_hex,side,hex_centres,flx_array[i_g,:],"flux",test_name ,meshz=meshz,lz=lz,nscat=nscat,ielem=ielem,spltl=spltl,i_g=i_g)

   print("\n>>>>>>> VTU CREATION COMPLETED!")
   print('-'*74 + '\n')


########################################################################
def rebuild_meshes(Geom):
   """
   Uses Geom LCM objects to return number of mesh elements and mesh arrays along each 
   axis, taking split into account.

   Args:
      Geom            : geometry LCM object
   Returns:
      lx              : number of mesh elements along x
      meshx           : mesh array along x
      ly              : number of mesh elements along y
      meshy           : mesh array along y
      lz              : number of mesh elements along z
      meshz           : mesh array along z
   """

   SV_geom = Geom["STATE-VECTOR"]
   geom_type = SV_geom[0] # 5 - Car2D; 7 - Car3D; 8 - Hex2D; 9 - Hex3D

   mesh_dict = {}

   ### FOR CARTESIAN GEOMETRIES
   if geom_type==5 or geom_type==7:
      lx    = SV_geom[2]
      meshx = Geom["MESHX"]
      spltx = Geom["SPLITX"]
      lx, meshx = get_mesh_oneaxis(lx,meshx,spltx)
      ly    = SV_geom[3]
      meshy = Geom["MESHY"]
      splty = Geom["SPLITY"]
      ly, meshy = get_mesh_oneaxis(ly,meshy,splty)
   else:
      lx   =1
      meshx=1
      ly   =1
      meshy=1

   ### FOR 3D GEOMETRIES
   if geom_type==7 or geom_type==9:
      lz    = SV_geom[4]
      meshz = Geom["MESHZ"]
      spltz = Geom["SPLITZ"]
      lz, meshz = get_mesh_oneaxis(lz,meshz,spltz)
   else:
      lz   =1
      meshz=1
      spltz=1

   return lx, meshx, ly, meshy, lz, meshz


########################################################################
def get_mesh_oneaxis(lx,mesh,splt):
   """
   Compute number of mesh elements and mesh arrays taking split into account.

   Args:
      lx              : number of mesh elements along axis w/o splits
      meshx           : mesh array along axis w/o splits
      splt            : split value along axis
   Returns:
      lx              : number of mesh elements along axis w/ splits
      meshx           : mesh array along axis w/ splits
   """

   tmp_mesh=np.empty(1, dtype='f') 
   val = 0.
   tmp_mesh[0] = val
   for i in range(len(splt)):
      step=(mesh[i+1]-mesh[i])/splt[i]
      for j in range(splt[i]):
         val += step
         tmp_mesh = np.append(tmp_mesh,val)

   lx=len(tmp_mesh)-1

   return lx, tmp_mesh


########################################################################
def compute_hex_centres(n_hex,n_ring,side):
   """
   Compute centres of hexagons within hexagonal domain. Centre hexagon is zeroth
   ring.
   Args:
      n_hex           : number of hexagons in domain
      n_ring          : number of rings in domain; centre hexagon is zeroth ring
      side            : length of one side of hexagon
   Returns:
      hex_centres     : 2D array with x and y coordinates of hexagon centres
   """

   hex_centres = np.zeros((2,n_hex), dtype='f')
   ind_hex =0
   hex_centres[0, 0] = 0.0
   hex_centres[1, 0] = 0.0

   ### LOOP OVER EACH RING OF HEXAGONS
   for ind_ring in range(1, n_ring):
      numhex_ring = ind_ring * 6
      xcoord = ind_ring * 1.5 * side
      ycoord = ind_ring * (sqrt(3) / 2) * side

      hex_centres[0, ind_hex + 1] = xcoord
      hex_centres[1, ind_hex + 1] = ycoord
      ind_hex += 1

      ind_dir = 1
      ind_step = 0
      numhex_perdir = ind_ring

      ### LOOP OVER HEXAGONS IN CURRENT RING
      ### For each of the six major axis directions, move in the direction
      ### of that axis for numhex_perdir hexagons (which happens to be the
      ### same as the ring index).
      for i_hex in range(1, numhex_ring):
         ind_step += 1
         if ind_dir == 1:
            xcoord -= 1.5 * side
            ycoord += (sqrt(3) / 2) * side
         elif ind_dir == 2:
            xcoord -= 1.5 * side
            ycoord -= (sqrt(3) / 2) * side
         elif ind_dir == 3:
            ycoord -= sqrt(3) * side
         elif ind_dir == 4:
            xcoord += 1.5 * side
            ycoord -= (sqrt(3) / 2) * side
         elif ind_dir == 5:
            xcoord += 1.5 * side
            ycoord += (sqrt(3) / 2) * side
         elif ind_dir == 6:
            ycoord += sqrt(3) * side

         hex_centres[0, ind_hex + 1] = xcoord
         hex_centres[1, ind_hex + 1] = ycoord
         ind_hex += 1

         if ind_step == numhex_perdir:
            ind_dir += 1
            ind_step = 0

   return hex_centres


########################################################################
def writeXML(ugrid, file_name):
   """
   Save unstructured grid vtk object (ugrid) to file.
   Args:
      ugrid           : vtkUnstructuredGrid object
      file_name       : file name under which to save ugrid
   Returns:
      None
   """
   writer = vtk.vtkXMLUnstructuredGridWriter()
   writer.SetFileName(file_name)
   writer.SetInputData(ugrid)
   writer.Write()


########################################################################
def make_vtu_cartesian_geom(
   lx,meshx,ly,meshy,lz,meshz,
   data_np,
   dataset_name,test_name,
   **kwargs
   ):
   """
   Build vtk unstructured grid object for 2D or 3D Cartesian mesh and populate with
   given data, material number or flux.
   Args:
      lx              : number of mesh elements along x
      meshx           : mesh array along x
      ly              : number of mesh elements along y
      meshy           : mesh array along y
      lz              : number of mesh elements along z
      meshz           : mesh array along z
      data_np         : given data, material number or flux
      dataset_name    : data name, usually 'material' or 'flux'
      test_name       : file name of the computed c2m procedure in case of a prior
                        dragon run or given name by user or default name
   Returns:
      None
   """

   ### ASSIGN OPTIONAL ARGS
   nscat = kwargs.get("nscat", 1)
   ielem = kwargs.get("ielem", 1)
   i_g = kwargs.get("i_g", 0)
   if dataset_name=="rate": 
      nsf_reg=kwargs.get("nusigf", 1)

   ### 2D CONSIDERATIONS
   if lz==1: meshz = [0,0]
   ndim = 2 if lz==1 else 3

   ### INITIALISE VTK OBJECTS
   points = vtk.vtkPoints()
   element = vtk.vtkVoxel()
   cells = vtk.vtkCellArray()
   ugrid = vtk.vtkUnstructuredGrid()
   ### INITIALISE SCALAR CELL DATA
   DRAGONdataset = vtk.vtkFloatArray()
   DRAGONdataset.SetName(dataset_name)
   ugrid.GetCellData().SetScalars(DRAGONdataset)

   ### COMPUTE CONSTANTS
   flux_offset = 1 if dataset_name=="material" else nscat*(ielem)**ndim

   i_cell=-1
   ### LOOP OVER Z-AXIS MESH
   for kk in range(lz):

      ### LOOP OVER Y-AXIS MESH
      for jj in range(ly):
         ycoord = [meshy[jj], meshy[jj], meshy[jj+1], meshy[jj+1]]

         ### LOOP OVER X-AXIS MESH
         for ii in range(lx):
            xcoord = [meshx[ii], meshx[ii+1], meshx[ii+1], meshx[ii]]

            ### INSERT NEW POINTS AND CELLS (DO NOT MERGE THE TWO LOOPS BELOW!!!)
            for i_crd in range(0,4):
               points.InsertNextPoint( xcoord[i_crd],ycoord[i_crd], meshz[kk])
            for i_crd in range(0,4):
               points.InsertNextPoint( xcoord[i_crd],ycoord[i_crd], meshz[kk+1])
            for i in range(0, 8):
               i_cell += 1
               element.GetPointIds().SetId(i, i_cell)
            cells.InsertNextCell(element)

            ugrid.SetPoints(points)
            ugrid.SetCells(vtk.VTK_HEXAHEDRON, cells)

            ### SET SCALAR CELL DATA
            ind1 = (kk*lx*ly) + (jj*lx) + ii
            ind2 = (kk*lx*ly)*flux_offset + (jj*lx)*flux_offset + ii*flux_offset
            if dataset_name=="rate":
               DRAGONdataset.InsertTuple1(ind1, nsf_reg[ind1]*data_np[ind2])
            else:
               DRAGONdataset.InsertTuple1(ind1, data_np[ind2])

   print(test_name, dataset_name,' PROCESSED. SAVING ...')
   filename=test_name+'_'+dataset_name+str(i_g)+".vtu"
   ### SAVE VTU FILE
   writeXML(ugrid, filename)
   return ugrid


########################################################################
def make_vtu_hexagonal_geom(
   n_hex,side,hex_centres,
   data_np,
   dataset_name,test_name,
   **kwargs
   ):
   """
   Build vtk unstructured grid object for 2D or 3D hexagon mesh and populate with
   given data, material number or flux.
   Args:
      n_hex           : number of hexagons in domain
      side            : length of one side of hexagon
      hex_centres     : 2D array with x and y coordinates of hexagon centres
      data_np         : given data, material number or flux
      dataset_name    : data name, usually 'material' or 'flux'
      test_name       : file name of the computed c2m procedure in case of a prior
                        dragon run or given name by user or default name
   Returns:
      None
   """
   ### ASSIGN OPTIONAL ARGS
   spltl = kwargs.get("spltl", 1)
   lz = kwargs.get("lz", 1)
   meshz = kwargs.get("meshz", 1)
   nscat = kwargs.get("nscat", 1)
   ielem = kwargs.get("ielem", 1)
   i_g = kwargs.get("i_g", 0)

   ### 2D CONSIDERATIONS
   if lz==1: meshz = [0,0]
   ndim = 2 if lz==1 else 3

   ### INITIALISE VTK OBJECTS
   points = vtk.vtkPoints()
   lozenge = vtk.vtkVoxel()
   cells = vtk.vtkCellArray()
   ugrid = vtk.vtkUnstructuredGrid()
   ### INITIALISE SCALAR CELL DATA
   DRAGONdataset = vtk.vtkFloatArray()
   DRAGONdataset.SetName(dataset_name)
   ugrid.GetCellData().SetScalars(DRAGONdataset)

   ### COMPUTE CONSTANTS
   x_step=(1.0)*(side/spltl)
   y_step=(sqrt(3)/2)*(side/spltl)
   n_loz = 3*spltl**2
   flux_offset = 1 if dataset_name=="material" else nscat*(ielem)**ndim

   i_cell = -1
   ### LOOP OVER Z-AXIS LAYERS
   for i_z in range(0,lz):
      ### LOOP OVER HEXAGONS
      for i_hex in range(n_hex):
         ### FETCH CENTRE OF EACH HEX
         cx, cy = hex_centres[0, i_hex], hex_centres[1, i_hex]

         ### LOOP OVER EACH HEX SECTION (EACH OF THE THREE MAIN LOZENGES)
         for hex_section in range(3):
            ### SET INITIAL VALUES OF XCOORD AND YCOORD
            if hex_section == 0:
               x_coords = [0.0, x_step*0.5, x_step, x_step*0.5]
               y_coords = [0.0, -y_step, 0.0, y_step]
            elif hex_section == 1:
               x_coords = [-side, -side+x_step, -side+(x_step*1.5), -side+(x_step * 0.5)]
               y_coords = [0.0, 0.0, y_step, y_step]
            else:
               x_coords = [-side/2, (-side/2)+x_step, (-side/2)+(x_step*0.5), (-side/2)-(x_step*0.5)]
               y_coords = [-side*sqrt(3)/2, -side*sqrt(3)/2, (-side*sqrt(3)/2)+y_step, (-side*sqrt(3)/2)+y_step]

            ### LOOP OVER Y-MESH INSIDE LOZENGE, INCREMENT XCOORD, YCOORD
            for i_mshy in range(spltl):
               if i_mshy != 0:
                  if hex_section == 0:
                     x_coords = x_coords - ((x_step/2)*(spltl-2))
                     y_coords = y_coords + y_step*(spltl)
                  elif hex_section == 1:
                     x_coords = x_coords - ((x_step)*(spltl-1.5))
                     y_coords = y_coords + y_step
                  else:
                     x_coords = x_coords - ((x_step)*(spltl-0.5))
                     y_coords = y_coords + y_step

               ### LOOP OVER X-MESH INSIDE LOZENGE, INCREMENT XCOORD, YCOORD
               for i_mshx in range(spltl):
                  if i_mshx != 0:
                     x_coords = x_coords + (x_step/2) if hex_section == 0 else x_coords + (x_step)
                     y_coords = y_coords - (y_step) if hex_section == 0 else y_coords

                  ### INSERT NEW POINTS AND CELLS (DO NOT MERGE THE TWO LOOPS BELOW!!!)
                  for i_crd in range(0,4):
                     points.InsertNextPoint(x_coords[i_crd]+cx, y_coords[i_crd]+cy, meshz[i_z])
                  for i_crd in range(0,4):
                     points.InsertNextPoint(x_coords[i_crd]+cx, y_coords[i_crd]+cy, meshz[i_z+1])
                  for i in range(0, 8):
                     i_cell += 1
                     lozenge.GetPointIds().SetId(i, i_cell)
                  cells.InsertNextCell(lozenge)

         ugrid.SetPoints(points)
         ugrid.SetCells(vtk.VTK_HEXAHEDRON, cells)

         ### SET SCALAR CELL DATA
         for i_loz in range(0,n_loz):
            ind1 = (i_z*n_hex*n_loz) + (i_hex*n_loz) + i_loz
            ind2 = (i_z*n_hex*n_loz*flux_offset) + (i_hex*n_loz*flux_offset) + (i_loz*flux_offset)
            DRAGONdataset.InsertTuple1(ind1, data_np[ind2])

   print(test_name, dataset_name,' PROCESSED. SAVING ...')
   filename=test_name+'_'+dataset_name+str(i_g)+".vtu"
   ### SAVE VTU FILE
   writeXML(ugrid, filename)
   return ugrid