Re: Reg: 3D structure visualization

Mike Taverne <[email protected]> Mon, 24 Aug 2015 10:32:19 +0100
Newsgroups gmane.comp.science.photonic-bands
Message-ID <[email protected]>
This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--===============2672739944076909269==
Content-Type: multipart/signed; micalg=pgp-sha1;
 protocol="application/pgp-signature";
 boundary="xWnbqjKIDWf5gsTTOiXqHWb2bUwvctFT2"

This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--xWnbqjKIDWf5gsTTOiXqHWb2bUwvctFT2
Content-Type: multipart/mixed;
 boundary="------------060200060905030503080805"

This is a multi-part message in MIME format.
--------------060200060905030503080805
Content-Type: text/plain; charset=gbk
Content-Transfer-Encoding: quoted-printable

You can use h5tovtk, which is part of the h5utils package to convert the
epsilon.h5 file into a VTK file which can be visualized in paraview or
mayavi.

If you want more periods, you can use "mpb-data" to generate them.
See here for an example:
http://ab-initio.mit.edu/wiki/index.php/MPB_Data_Analysis_Tutorial#Visual=
izing_the_diamond_lattice_structure_and_bands

The example uses h5tov5d and vis5d, but you can easily use h5tovtk and
paraview instead.

One problem with h5tovtk is that it does not take into account the
direction of the lattice vectors.
If you want to visualize structures based on non-cartesian lattices, you
can use my h5tovts.py script, which creates a VTK structured grid as
output with axes based on the lattice vectors.
cf attachments.

I haven't used these scripts much for a while, so some things may be
broken. Let me know if you have any problems.
It also does not yet create multiple periods like mpb-data
unfortunately, and runs a bit slow. A C/C++ version would probably be
faster.

This could also interest you:
https://www.mail-archive.com/[email protected]/msg04989.html=


Regards,
Mike


On 22/08/15 14:29, vijaykumar gudelli wrote:
> Dear MPB-developers and users,
>=20
> I'm very new to MPB. I have succeeded in reproducing the tutorials and
> some of the examples too.
> Now I'm facing problem in the visualization of .h5 files with some test=

> case structure in 3D.  The structure file which I would like to generat=
e
> is similar to the Figure 5, of chapter 3 (page no. 33) "Photonic
> crystals Molding the flow of light" (Second edition). The problem here
> I'm facing is, how to visualize the file in the 3D? Is there any
> visualization tools available to view in 3D of file epsilon.h5?
> I found BEAM as one of the solution for it, here I can able to see all
> the files in the epsilon.h5 individually, but I couldn't get much of it=
s
> information about the 3D structure.
> Please direct me to get the solution for this problem.
> Any help in the regards is highly appreciated.
>=20
> Thanking you,
> --=20
> -- _Best_ _Regards_
> __
> V. K. GUDELLI
> [email protected] <mailto:[email protected]>
>=20
>=20
> _______________________________________________
> mpb-discuss mailing list
> [email protected]
> http://ab-initio.mit.edu/cgi-bin/mailman/listinfo/mpb-discuss
>=20



--------------060200060905030503080805
Content-Type: text/x-python;
 name="h5tovts.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="h5tovts.py"

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import os
import sys
import argparse

from h5utils import h5tovts

def h5tovts_argparse():

  parser =3D argparse.ArgumentParser(description =3D 'Convert an MPB-crea=
ted HDF5 file to a .vts file.')
  parser.add_argument('-v','--verbose', action=3D"count", dest=3D"verbosi=
ty", default=3D0, help=3D'verbosity level')

  parser.add_argument('h5file', action=3D"store", help=3D'input HDF5 file=
', nargs=3D'+')
  #parser.add_argument('vtsfile', action=3D"store", help=3D'output .vts f=
ile', nargs=3D'?')
  #parser.add_argument('-b','--basepath', action=3D"store", default=3DNon=
e, help=3D'basepath for output files')

  parser.add_argument('--size', nargs=3D3, type=3Dfloat, default=3DNone, =
help=3D'lattice size (only used for .h5 files without lattice vectors, i.=
e. MEEP output, not for MPB output.)')
  parser.add_argument('-d', '--dataset', help=3D'use dataset <name> in th=
e input files (default: first dataset)', metavar=3D'name')

  arguments =3D parser.parse_args()

  if arguments.verbosity > 0:
    print('---------')
    print(arguments)
    print('---------')
   =20
  if not len(sys.argv) > 1:
    parser.print_help()
  else:
    #if arguments.basepath is None:
      #arguments.basepath =3D os.path.splitext(arguments.h5file)[0]

    for h5file in arguments.h5file:
      basepath =3D os.path.splitext(h5file)[0]
      print('=3D=3D> {} -> {}.vts and {}.vti'.format(h5file, basepath, ba=
sepath))
      h5tovts(h5file, basepath, arguments.size, arguments.dataset, argume=
nts.verbosity)
 =20
  return

if __name__ =3D=3D '__main__':
  h5tovts_argparse()

--------------060200060905030503080805
Content-Type: text/x-python;
 name="h5utils.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
 filename="h5utils.py"

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

'''
This module provides various utilities to work with the HDF5 format (and =
the VTK formats).

Automatic documentation cannot be generated at the moment, since we use p=
ython3 by default.

This script only runs with python2 because the python VTK module is not y=
et fully ported to python3.

=2E. note:: VTK numbering is in increasing X, then Y, then Z.

=2E. todo:: Use virtual void vtkSTLReader::MergingOff() 	[virtual] - Turn=
 on/off merging of points/triangles.
=2E. todo:: Read in multiple separate STL files.
'''

from __future__ import division

import os
import sys
import vtk
import h5py
import time
import numpy
from numpy import array, zeros, sqrt, linspace
from numpy.linalg import norm
from vtk.util.numpy_support import numpy_to_vtk

class Lattice(object):
  '''
  The lattice class is normally used only for the geometry-lattice variab=
le, and specifies the three lattice directions of the crystal and the len=
gths of the corresponding lattice vectors.

  lattice
      Properties:=20

  basis1, basis2, basis3 [vector3]
      The three lattice directions of the crystal, specified in the carte=
sian basis. The lengths of these vectors are ignored--only their directio=
ns matter. The lengths are determined by the basis-size property, below. =
These vectors are then used as a basis for all other 3-vectors in the ctl=
 file. They default to the x, y, and z directions, respectively.=20

  basis-size [vector3]
      The components of basis-size are the lengths of the three basis vec=
tors, respectively. They default to unit lengths.=20

  size [vector3]
      The size of the lattice (i.e. the length of the lattice vectors Ri,=
 in which the crystal is periodic) in units of the basis vectors. Thus, t=
he actual lengths of the lattice vectors are given by the components of s=
ize multiplied by the components of basis-size. (Alternatively, you can t=
hink of size as the vector between opposite corners of the primitive cell=
, specified in the lattice basis.) Defaults to unit lengths.=20

  resolution [number or vector3]
      Specifies the computational grid resolution, in pixels per lattice =
unit (a lattice unit is one basis vector in a given direction).
      If resolution is a vector3, then specifies a different resolution f=
or each direction;
      otherwise the resolution is uniform.
      (The grid size is then the product of the lattice size and the reso=
lution, rounded up to the next positive integer.)
      Defaults to 10.
   =20
  If any dimension has the special size no-size, then the dimensionality =
of the problem is reduced by one; strictly speaking, the dielectric funct=
ion is taken to be uniform along that dimension. (In this case, the no-si=
ze dimension should generally be orthogonal to the other dimensions.)=20
   =20
  .. todo:: This class still needs some work. But works to currently stor=
e necessary variables. Use with care.
  .. todo:: merge somehow with BFDTD meshing classes? (need proper defini=
tion for resolution, etc)
  '''
  def __init__(self):
    self.basis1 =3D array([1,0,0])
    self.basis2 =3D array([0,1,0])
    self.basis3 =3D array([0,0,1])
    self.basis_size =3D array([1,1,1])
    self.size =3D array([1,1,1])
   =20
    self.xmesh =3D [0,1]
    self.ymesh =3D [0,1]
    self.zmesh =3D [0,1]
    self.setResolution(10, 10, 10)
   =20
    return
 =20
  def __str__(self):
    ret =3D 'lattice:\n'
    ret +=3D '  basis1 =3D {}\n'.format(self.basis1)
    ret +=3D '  basis2 =3D {}\n'.format(self.basis2)
    ret +=3D '  basis3 =3D {}\n'.format(self.basis3)
    ret +=3D '  basis_size =3D {}\n'.format(self.basis_size)
    ret +=3D '  size =3D {}'.format(self.size)
    return(ret)
 =20
  def getLatticeVectors(self):
    a1 =3D self.size[0]*self.basis_size[0]*self.basis1/norm(self.basis1)
    a2 =3D self.size[1]*self.basis_size[1]*self.basis2/norm(self.basis2)
    a3 =3D self.size[2]*self.basis_size[2]*self.basis3/norm(self.basis3)
    return (a1, a2, a3)
 =20
  def getBounds(self):
    (a1, a2, a3) =3D self.getLatticeVectors()

    Pmax =3D 0.5*a1 + 0.5*a2 + 0.5*a3
    Pmin =3D -Pmax

    xmin =3D Pmin[0]
    ymin =3D Pmin[1]
    zmin =3D Pmin[2]

    xmax =3D Pmax[0]
    ymax =3D Pmax[1]
    zmax =3D Pmax[2]
   =20
    return (xmin,xmax, ymin,ymax, zmin,zmax)
   =20
  def getXmeshDelta(self):
    return(numpy.diff(self.xmesh))
  def getYmeshDelta(self):
    return(numpy.diff(self.ymesh))
  def getZmeshDelta(self):
    return(numpy.diff(self.zmesh))
  def getMeshDelta(self):
    return(numpy.diff(self.xmesh),numpy.diff(self.ymesh),numpy.diff(self.=
zmesh))
   =20
  def getMinDeltas(self):
    dx =3D min(self.getXmeshDelta())
    dy =3D min(self.getYmeshDelta())
    dz =3D min(self.getZmeshDelta())
    return (dx,dy,dz)

  def getResolution(self):
    return [len(self.xmesh), len(self.ymesh), len(self.zmesh)]

  def getSpacing(self):
    (xmin,xmax, ymin,ymax, zmin,zmax) =3D self.getBounds()
    (Nx, Ny, Nz) =3D self.getResolution()
    dx =3D (xmax-xmin)/(Nx-1)
    dy =3D (ymax-ymin)/(Ny-1)
    dz =3D (zmax-zmin)/(Nz-1)
    return (dx,dy,dz)
       =20
  def setResolution(self, Nx, Ny, Nz):
    '''
    Sets up homogeneous X,Y,Z grids with Nx,Ny,Nz points.
    '''
    self.xmesh =3D linspace(-0.5, 0.5, Nx)
    self.ymesh =3D linspace(-0.5, 0.5, Ny)
    self.zmesh =3D linspace(-0.5, 0.5, Nz)
    return

  def getMesh(self):
    return (self.xmesh, self.ymesh, self.zmesh)

  def setXmesh(self, xmesh):
    self.xmesh =3D xmesh
    return

  def setYmesh(self, ymesh):
    self.ymesh =3D ymesh
    return

  def setZmesh(self, zmesh):
    self.zmesh =3D zmesh
    return

  def setSize(self, size):
    self.size =3D size

  def setBasisSize(self, basis_size):
    self.basis_size =3D basis_size

class FCClattice(Lattice):
  ''' Create a FCC lattice. '''
  def __init__(self):
    ''' Constructor '''
    super(FCClattice, self).__init__()
    # set up lattice
    self.basis1 =3D array([0, 1, 1])
    self.basis2 =3D array([1, 0, 1])
    self.basis3 =3D array([1, 1, 0])
    self.basis_size =3D array([sqrt(0.5), sqrt(0.5), sqrt(0.5)])

class BCClattice(Lattice):
  ''' Create a BCC lattice. '''
  def __init__(self):
    ''' Constructor '''
    super(BCClattice, self).__init__()
    # set up lattice
    self.basis1 =3D array([-1,  1,  1])
    self.basis2 =3D array([ 1, -1,  1])
    self.basis3 =3D array([ 1,  1, -1])
    self.basis_size =3D array([sqrt(3)/2, sqrt(3)/2, sqrt(3)/2])
   =20
def h5tovts(h5file, basepath, total_lattice_size=3DNone, dataset=3DNone, =
verbosity=3D0):
  '''
  * total_lattice_size : total length of the lattice vectors, i.e. **size=
*basis-size** in MPB terms. Overrides any values obtained from reading th=
e lattice vectors in the .h5 file.
 =20
  .. todo:: Emulate the -x/y/z options of mpb-data, i.e. create a periodi=
c structure from a unit-cell.
  '''
  # read in .h5 file
  with h5py.File(h5file, "r") as f:
    print('Reading from ' + h5file)
   =20
    # get description
    description =3D None
    if 'description' in f.keys():
      #description              Dataset {SCALAR}
      description =3D f['description'][...].tostring().decode("ascii").st=
rip('\0')
      print('description =3D {}'.format(description))

    # create list of datasets
    dataset_list =3D [k for k in f.keys() if k not in ['description','lat=
tice vectors', 'xmesh', 'ymesh', 'zmesh'] ]
    print('Available datasets: {}'.format(dataset_list))

    # choose first dataset if not specified
    if dataset is None:
      dataset =3D dataset_list[0]

    # set up data, Nx, Ny, Nz
    # TODO: Might cause conflict with size read from x/y/z mesh values...=
 Add warnings?
    print('Using dataset =3D {}'.format(dataset))
    data =3D f[dataset]
    (Nx, Ny, Nz) =3D data.shape
   =20
    # set up lattice
    mylattice =3D Lattice()
   =20
    if 'lattice vectors' in f.keys():
      #lattice\ vectors         Dataset {3, 3}
      lattice_vectors =3D f['lattice vectors'][...]
      mylattice.basis1 =3D lattice_vectors[0]
      mylattice.basis2 =3D lattice_vectors[1]
      mylattice.basis3 =3D lattice_vectors[2]
      mylattice.setSize( [norm(mylattice.basis1), norm(mylattice.basis2),=
 norm(mylattice.basis3)] )

    if total_lattice_size:
      mylattice.setSize(total_lattice_size)
   =20
    # generate homogeneous mesh by default
    mylattice.setResolution(Nx, Ny, Nz)

    if 'xmesh' in f.keys():
      mylattice.setXmesh(f['xmesh'][...])
    if 'ymesh' in f.keys():
      mylattice.setYmesh(f['ymesh'][...])
    if 'zmesh' in f.keys():
      mylattice.setZmesh(f['zmesh'][...])
   =20
    (a1, a2, a3) =3D mylattice.getLatticeVectors()
    (xmesh, ymesh, zmesh) =3D mylattice.getMesh()
    print('a1 =3D {}'.format(a1))
    print('a2 =3D {}'.format(a2))
    print('a3 =3D {}'.format(a3))
    print('data.shape =3D {} x {} x {}'.format(Nx, Ny, Nz))
    print('mesh.shape =3D {} x {} x {}'.format(len(xmesh), len(ymesh), le=
n(zmesh)))
    if Nx !=3D len(xmesh) or Ny !=3D len(ymesh) or Nz !=3D len(zmesh):
      raise Exception('Inconsistent number of cells between the dataset a=
nd the x/y/z meshs.')
   =20
    # create the vtkPoints structure for the coordinates
    points =3D vtk.vtkPoints()
    points.SetNumberOfPoints(Nx*Ny*Nz)

    # create the vtkFloatArray structures for the data
    dataset_dict =3D dict()
    for key in dataset_list:
      print('key =3D {}'.format(key))
      scalar =3D f[key][...].transpose().reshape(-1,1)
      vtk_data =3D numpy_to_vtk(scalar)
      #vtk_data =3D vtk.vtkFloatArray()
      vtk_data.SetName(key)
      #vtk_data.SetNumberOfTuples(Nx*Ny*Nz)
      dataset_dict[key] =3D (f[key], vtk_data, scalar)

    last_info_time =3D time.time()
   =20
    print('Starting loops')
    counter =3D 0
    # fill the vtkPoints and vtkFloatArray
    for k in range(Nz):
      for j in range(Ny):
        for i in range(Nx):
          offset =3D i + j*Nx + k*Nx*Ny

          # old system:
          #   coord =3D (i/(Nx-1) - 0.5)*a1 + (j/(Ny-1) - 0.5)*a2 + (k/(N=
z-1) - 0.5)*a3
          # new system:
          coord =3D xmesh[i]*a1 + ymesh[j]*a2 + zmesh[k]*a3

          points.SetPoint(offset, coord)
          #for key in dataset_dict.keys():
            #dataset_dict[key][1].SetTuple1(offset, dataset_dict[key][0][=
i,j,k])
         =20
          #InsertTuples =09
          #virtual void vtkAbstractArray::InsertTuples 	( 	vtkIdList *  	=
dstIds,
          #vtkIdList *  	srcIds,
          #vtkAbstractArray *  	source=20
          #) 		[pure virtual]

          if time.time() - last_info_time > 5:
            print('{} %'.format(100*offset/(Nx*Ny*Nz-1)))
            last_info_time =3D time.time()
 =20
          if verbosity>1:
            counter +=3D 1
            progress_str =3D 'Progress: {}/{}'.format(counter, Nx*Ny*Nz)
            #print(progress_str, end=3D'\r')
            print(progress_str)
            #subprocess.call(["printf", progress_str+'\r'])
   =20
    print('\nLoops done.')

    #for key in dataset_dict.keys():
      #h5_data =3D dataset_dict[key][0]
      #vtk_data =3D dataset_dict[key][1]
      #vtk_data.InsertTuples()
      #.SetTuple1(offset, [i,j,k])
   =20
    # create structured grid
    dataset_vts =3D vtk.vtkStructuredGrid()
    dataset_vts.SetDimensions(Nx, Ny, Nz)
    dataset_vts.SetPoints(points)
   =20
    # create vtkImageData
    dataset_vti =3D vtk.vtkImageData()
    dataset_vti.SetDimensions(Nx, Ny, Nz)
    (xmin,xmax, ymin,ymax, zmin,zmax) =3D mylattice.getBounds()
    dataset_vti.SetOrigin([xmin, ymin, zmin])
    dataset_vti.SetSpacing(mylattice.getSpacing())
   =20
    # add scalar data to the grids
    for key in dataset_dict.keys():
      dataset_vts.GetPointData().AddArray(dataset_dict[key][1])
      dataset_vti.GetPointData().AddArray(dataset_dict[key][1])
   =20
    dataset_vts.GetPointData().SetActiveScalars('data')
    dataset_vti.GetPointData().SetActiveScalars('data')

    # write out .vts file
    writer =3D vtk.vtkXMLStructuredGridWriter()
    writer.SetInputData(dataset_vts)
    writer.SetFileName(basepath + '.' + writer.GetDefaultFileExtension())=

    writer.Write()

    # write out .vti file
    writer =3D vtk.vtkXMLImageDataWriter()
    writer.SetInputData(dataset_vti)
    writer.SetFileName(basepath + '.' + writer.GetDefaultFileExtension())=

    writer.Write()

    return

def stltoh5(stlfile, basepath, epsilon_inside, epsilon_outside, lattice=3D=
Lattice(), verbosity=3D0):
 =20
  if not os.path.exists(stlfile):
    if sys.version_info.major =3D=3D 2:
      raise IOError('No such file or directory: {}'.format(stlfile)) # py=
2
    else:
      raise FileNotFoundError('No such file or directory: {}'.format(stlf=
ile)) # py3
 =20
  print('--> timer start')
  time_start =3D time.time()

  # read in .stl file
  reader =3D vtk.vtkSTLReader()
  reader.SetFileName(stlfile)
  reader.Update()
  polydata =3D reader.GetOutput()

  # write .vtp file
  writer =3D vtk.vtkXMLPolyDataWriter()
  writer.SetInputData(polydata)
  writer.SetFileName(basepath + '.' + writer.GetDefaultFileExtension())
  writer.Write()

  # set up implicit_function
  implicit_function =3D vtk.vtkImplicitPolyDataDistance()
  implicit_function.SetInput(polydata)

  print("--> Elapsed time: %.4f sec" % (time.time() - time_start))
 =20
  stl_to_vts_and_h5(implicit_function, basepath, lattice, epsilon_inside,=
 epsilon_outside)
  print("--> Elapsed time: %.4f sec" % (time.time() - time_start))
  return

def stl_to_vts_and_h5(implicit_function, outfile_basename, lattice, epsil=
on_inside, epsilon_outside):
  (Nx, Ny, Nz) =3D lattice.getResolution()
  (a1, a2, a3) =3D lattice.getLatticeVectors()
 =20
  points =3D vtk.vtkPoints()
  points.SetNumberOfPoints(Nx*Ny*Nz)

  scalars_vtk =3D vtk.vtkFloatArray()
  scalars_vtk.SetNumberOfTuples(Nx*Ny*Nz)
  scalars_numpy =3D zeros([Nx,Ny,Nz])

  print('=3D=3D=3D dims =3D=3D=3D')
  print(scalars_numpy.shape)
 =20
  last_info_time =3D time.time()

  print('=3D=3D=3D Loop start =3D=3D=3D')
  for k in range(Nz):
    for j in range(Ny):
      for i in range(Nx):
        coord =3D (i/(Nx-1) - 0.5)*a1 + (j/(Ny-1) - 0.5)*a2 + (k/(Nz-1) -=
 0.5)*a3
        offset =3D i + j*Nx + k*Nx*Ny
        points.SetPoint(offset, coord)
        if implicit_function.FunctionValue(coord) <=3D 0:
          value =3D epsilon_inside
        else:
          value =3D epsilon_outside
        scalars_vtk.SetTuple1(offset, value)
        scalars_numpy[i, j, k] =3D value
       =20
        if time.time() - last_info_time > 5:
          print('{} %'.format(100*offset/(Nx*Ny*Nz-1)))
          last_info_time =3D time.time()
 =20
  print('=3D=3D=3D Loop end =3D=3D=3D')

  dataset =3D vtk.vtkStructuredGrid()
  dataset.SetDimensions(Nx, Ny, Nz)
  dataset.SetPoints(points)
  dataset.GetPointData().SetScalars(scalars_vtk)

  writer =3D vtk.vtkXMLStructuredGridWriter()
  writer.SetInputData(dataset)
  writer.SetFileName(outfile_basename + '.' + writer.GetDefaultFileExtens=
ion())
  writer.Write()
 =20
  h5file =3D outfile_basename + '.h5'
  with h5py.File(h5file, "w") as f:
    print('writing to ' + h5file)
   =20
    dset =3D f.create_dataset('/data', scalars_numpy.shape, dtype=3Dnumpy=
=2Efloat64)
    dset[...] =3D scalars_numpy

    dset =3D f.create_dataset("description", (), dtype=3D"S29")
    dset[...] =3D 'dielectric function, epsilon'
   =20
    lattice_vectors =3D numpy.array([a1, a2, a3])
    print(lattice_vectors)

    dset =3D f.create_dataset('/lattice vectors', lattice_vectors.shape, =
dtype=3Dnumpy.float64)
    dset[...] =3D lattice_vectors
   =20
    # TODO: Add these fields:
    #epsilon.xx               Dataset {100, 100, 100}
    #epsilon.xy               Dataset {100, 100, 100}
    #epsilon.xz               Dataset {100, 100, 100}
    #epsilon.yy               Dataset {100, 100, 100}
    #epsilon.yz               Dataset {100, 100, 100}
    #epsilon.zz               Dataset {100, 100, 100}
    #epsilon_inverse.xx       Dataset {100, 100, 100}
    #epsilon_inverse.xy       Dataset {100, 100, 100}
    #epsilon_inverse.xz       Dataset {100, 100, 100}
    #epsilon_inverse.yy       Dataset {100, 100, 100}
    #epsilon_inverse.yz       Dataset {100, 100, 100}
    #epsilon_inverse.zz       Dataset {100, 100, 100}
 =20
  return

if __name__ =3D=3D '__main__':
	pass

--------------060200060905030503080805--

--xWnbqjKIDWf5gsTTOiXqHWb2bUwvctFT2
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBAgAGBQJV2uSjAAoJEAsx6g3o8ssJruYP/i/aRRVD1ds71OtqtZvx+HSr
i1zrjohvyyUWkMTJebrosq3JrMo6nfX0RPyuljQHlY3Ls1OViHoIq/wvjc7eajVy
myX8LxtYqHpo6IXnCpB51W74g19amx0WghP7GGT8bCTh5zfix1tlP3nN3TxygGXe
kj7nFNsNEL2+cE6QGml7rDU45ibtkgJm7rQwt1ok5+0POSqDrretrZtGBQm23+jE
jfbiiS4KU/XDRFCi0/+RIZqg8jZ0y+Z/pB323IEChz2uFu1y15N6eizl+6JCHMv8
Rv5pDjW7gllWwb+UYN8EHdbT7yJ0OyYgzasBddgLVjHIWi8KjbhxLk4z+EVtLwsJ
5UghDKnKTC+0hBw92Jjsni/XNTFaBJ5XDloXDvdsgWS0PpPZosS57iu5es2rkhM1
vyju0clR+XpaSR41ozB829LTo0emWk/unqhCWe/EWDNEtUwdLxFzDJuoZJHGa3rZ
iHVA2ImQg+4xQR12h1tK0Jr6O7CtfYErsup4ljAdJREsR9f02JYadb04mfYvua4F
JhVvFQ13GosZAFEDJW/pYLE4h8pr7GsaVVPvQIbI7tW/hosnEMN0HVoO17htmzOa
4Y5Kwm2mSyC9VrFRt0fwOYuKImpFPtqWolsooyiUC292Al7TZNLGD17aE5dESfgM
gp0iwRFSRX7TDJklVnx2
=BnTd
-----END PGP SIGNATURE-----

--xWnbqjKIDWf5gsTTOiXqHWb2bUwvctFT2--


--===============2672739944076909269==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: inline

X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KbXBiLWRpc2N1
c3MgbWFpbGluZyBsaXN0Cm1wYi1kaXNjdXNzQGFiLWluaXRpby5taXQuZWR1Cmh0dHA6Ly9hYi1p
bml0aW8ubWl0LmVkdS9jZ2ktYmluL21haWxtYW4vbGlzdGluZm8vbXBiLWRpc2N1c3M=

--===============2672739944076909269==--