+
+
+
+
+
\ No newline at end of file
diff --git a/aj.tmat.h5 b/aj.tmat.h5
new file mode 100644
index 0000000..aee6659
Binary files /dev/null and b/aj.tmat.h5 differ
diff --git a/am.tmat.h5 b/am.tmat.h5
new file mode 100644
index 0000000..917f66d
Binary files /dev/null and b/am.tmat.h5 differ
diff --git a/ap.tmat.h5 b/ap.tmat.h5
new file mode 100644
index 0000000..808d497
Binary files /dev/null and b/ap.tmat.h5 differ
diff --git a/ar.tmat.h5 b/ar.tmat.h5
new file mode 100644
index 0000000..05e8d0b
Binary files /dev/null and b/ar.tmat.h5 differ
diff --git a/export_R.R b/export_R.R
new file mode 100644
index 0000000..eb8a7f6
--- /dev/null
+++ b/export_R.R
@@ -0,0 +1,125 @@
+## ----eval=FALSE---------------------------------------------------------------
+library(rhdf5) # note: dev version to support complex
+library(uuid) # uuid
+library(glue) # string interpolation
+library(purrr) # mapping functions
+
+## dummy data
+
+# possibly multiple wavelengths
+wavelength <- seq(400, 800, by=50)
+Nl <- length(wavelength)
+
+Lmax <- 3
+qmax <- 2*(Lmax*(Lmax+1)+Lmax) # T-matrix size
+
+# dummy 30x30 matrix values for each wavelength
+# note the byrow due to HDF5 expecting
+# row-major ordering vs R's default column-major
+tdata <- matrix(1:qmax^2 + 1i*(1:qmax^2), qmax, qmax, byrow=TRUE)
+
+tmatrix <- array(NA_complex_, c(Nl,qmax,qmax))
+for(i in seq_len(Nl))
+ tmatrix[i,,] <- tdata
+
+tmatrix[1,1:3,1:3]
+
+modes <- list(l = rep(NA_integer_, qmax),
+ m = rep(NA_integer_, qmax),
+ polarization = rep(NA_character_, qmax))
+
+i <- 1
+for (li in 1:Lmax){
+ for (mi in -li:li){
+ for (si in c("electric", "magnetic")){
+ modes$l[i] <- li
+ modes$m[i] <- mi
+ modes$polarization[i] <- si
+ i <- i+1
+ }
+ }
+}
+
+embedding <- list('relative_permeability' = 1.0,
+ 'relative_permittivity' = 1.33^2)
+scatterer <- list(material = list('relative_permeability' = 1.0,
+ 'relative_permittivity' =
+ rep(-11.4+1.181i,Nl)),
+ geometry = list('radiusxy' = 20.0,
+ 'radiusz' = 40.0))
+
+computation <- list(method_parameters = list('Lmax' = Lmax,
+ 'Ntheta' = 100),
+ files = list(script = paste(readLines('export_R.R'), collapse = "\n")))
+
+
+
+## ----eval=FALSE---------------------------------------------------------------
+library(rhdf5) # note: requires dev version to support complex arrays
+# cf https://support.bioconductor.org/p/9156305/#9156408
+# install.packages("remotes")
+# remotes::install_github("grimbough/rhdf5@devel")
+# may need 'crypto' library from openSSL to compile
+# e.g. via brew install openssl on macos
+
+
+## ----eval=FALSE---------------------------------------------------------------
+f <- 'ar.tmat.h5'
+software = sprintf("SMARTIES=1.1, R=%s, rhdf5=%s", paste0(R.version$major, R.version$minor), packageVersion("rhdf5"))
+
+unlink(f) # delete previous file if it exists
+h5createFile(f)
+h5closeAll() # in case connections open
+
+h5write(wavelength, file=f, name="/vacuum_wavelength")
+
+h5write(tmatrix, file=f, name="/tmatrix", native=TRUE) # store rowwise
+
+h5write(modes, file=f, name='/modes')
+h5write(embedding, file=f, name="/embedding")
+h5write(scatterer, file=f, name="/scatterer")
+h5write(computation, file=f, name='/computation')
+
+## write attributes
+fid <- H5Fopen(f)
+# root level
+h5writeAttribute("Au prolate spheroid in water", fid, "name")
+h5writeAttribute("Computation using SMARTIES, a numerically robust EBCM implementation for spheroids", fid, "description")
+h5writeAttribute("gold, spheroid, ebcm, passive, reciprocal, czinfinity, mirrorxyz", fid, "keywords")
+h5writeAttribute("v0.01", fid, "storage_format_version")
+
+# wavelength
+did <- H5Dopen(fid, "vacuum_wavelength")
+h5writeAttribute("nm", did, "unit")
+H5Dclose(did)
+
+# embedding
+gid <- H5Gopen(fid, "embedding")
+h5writeAttribute("H2O, Water", gid, "name")
+h5writeAttribute("non-dispersive", gid, "keywords")
+H5Gclose(gid)
+
+# material
+gid <- H5Gopen(fid, "scatterer/material")
+h5writeAttribute("Au, Gold", gid, "name")
+h5writeAttribute("dispersive, plasmonic", gid, "keywords")
+h5writeAttribute("Au from Raschke et al 10.1103/PhysRevB.86.235147", gid, "reference")
+H5Gclose(gid)
+
+# geometry
+gid <- H5Gopen(fid, "scatterer/geometry")
+h5writeAttribute("nm", gid, "unit")
+h5writeAttribute("spheroid", gid, "shape")
+h5writeAttribute("homogeneous spheroid with symmetry axis z", gid, "name")
+H5Gclose(gid)
+
+# computation
+gid <- H5Gopen(fid, "computation")
+h5writeAttribute("EBCM, Extended Boundary Condition Method", gid, "method")
+h5writeAttribute("Computation using SMARTIES, a numerically robust EBCM implementation for spheroids", gid, "description")
+h5writeAttribute(software, gid, "software")
+h5writeAttribute("SMARTIES", gid, "name")
+H5Gclose(gid)
+
+H5Fclose(fid)
+
diff --git a/export_R.html b/export_R.html
new file mode 100644
index 0000000..5a763b8
--- /dev/null
+++ b/export_R.html
@@ -0,0 +1,867 @@
+
+
+
+
+
+
+
+
+
+
+
+Tmat.h5 Test Website – Export to ‘.tmat.h5’ format with R
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Export to ‘.tmat.h5’ format with R
+
+
+
+
+
+
+
+
Author
+
+
baptiste
+
+
+
+
+
Published
+
+
July 4, 2024
+
+
+
+
+
+
+
+
+
+
+
+
This document showcases a basic R script to export T-matrices in the .tmat.h5 HDF5 format. For illustration, we start by producing a dummy dataset. This minimal reproducible example file is available for download as a standalone script: export_R.R.
+
+
Mockup input data
+
Consistent with the other examples, we generate a 50x30x30 array of dummy data, which repeats 50 times (50 wavelengths) a matrix of 900 entries ranging from \(1+1i\) (first element, top left) to \(900 + 900i\) (last element, bottom right). Note the expectation of row-major ordering in HDF5. The 3x3 top-left block is
The rhdf5 package has support for a variety of R objects, including lists which are automatically written as grouped objects in HDF5.
+
+
library(rhdf5) # note: dev version to support complex
+library(uuid) # uuid
+library(glue) # string interpolation
+library(purrr) # mapping functions
+
+## dummy data
+
+# possibly multiple wavelengths
+wavelength <-seq(400, 800, by=50)
+Nl <-length(wavelength)
+
+Lmax <-3
+qmax <-2*(Lmax*(Lmax+1)+Lmax) # T-matrix size
+
+# dummy 30x30 matrix values for each wavelength
+# note the byrow due to HDF5 expecting
+# row-major ordering vs R's default column-major
+tdata <-matrix(1:qmax^2+1i*(1:qmax^2), qmax, qmax, byrow=TRUE)
+
+tmatrix <-array(NA_complex_, c(Nl,qmax,qmax))
+for(i inseq_len(Nl))
+ tmatrix[i,,] <- tdata
+
+tmatrix[1,1:3,1:3]
+
+modes <-list(l =rep(NA_integer_, qmax),
+m =rep(NA_integer_, qmax),
+polarization =rep(NA_character_, qmax))
+
+i <-1
+for (li in1:Lmax){
+for (mi in-li:li){
+for (si inc("electric", "magnetic")){
+ modes$l[i] <- li
+ modes$m[i] <- mi
+ modes$polarization[i] <- si
+ i <- i+1
+ }
+ }
+}
+
+embedding <-list('relative_permeability'=1.0,
+'relative_permittivity'=1.33^2)
+scatterer <-list(material =list('relative_permeability'=1.0,
+'relative_permittivity'=
+rep(-11.4+1.181i,Nl)),
+geometry =list('radiusxy'=20.0,
+'radiusz'=40.0))
+
+computation <-list(method_parameters =list('Lmax'= Lmax,
+'Ntheta'=100),
+files =list(script =paste(readLines('export_R.R'), collapse ="\n")))
+
+
+
+
Saving to HDF5
+
The rhdf5 package provides support for reading/writing HDF5 files into/from R objects. Until my recent request, complex arrays were not supported, but this is now implemented in the dev version of the package.
+
+
library(rhdf5) # note: requires dev version to support complex arrays
+# cf https://support.bioconductor.org/p/9156305/#9156408
+# install.packages("remotes")
+# remotes::install_github("grimbough/rhdf5@devel")
+# may need 'crypto' library from openSSL to compile
+# e.g. via brew install openssl on macos
+
+
We can then write the different objects defined above using hwrite. Note the important native = TRUE parameter for the T-matrix data: R stores arrays column-wise, while HDF5 uses row-major ordering. To avoid confusion between different programming languages, we suggest sticking with the native HDF5 convention (native = TRUE ensures that the array is written transposed).
+
Attributes don’t seem to have a convenient high-level interface, and unfortunately they need to be written slightly differently for the root level, datasets, and groups.
+
+
+
+
+
\ No newline at end of file
diff --git a/export_julia.html b/export_julia.html
new file mode 100644
index 0000000..a1c533b
--- /dev/null
+++ b/export_julia.html
@@ -0,0 +1,839 @@
+
+
+
+
+
+
+
+
+
+
+
+Tmat.h5 Test Website – Export to ‘.tmat.h5’ format with Julia
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Export to ‘.tmat.h5’ format with Julia
+
+
+
+
+
+
+
+
Author
+
+
baptiste
+
+
+
+
+
Published
+
+
July 4, 2024
+
+
+
+
+
+
+
+
+
+
+
+
This document showcases a basic Julia script to export T-matrices in the .tmat.h5 HDF5 format. For illustration, we start by producing a dummy dataset. This minimal reproducible example file is available for download as a standalone script: export_julia.jl.
+
+
Mockup input data
+
Consistent with the other examples, we generate a 50x30x30 array of dummy data, which repeats 50 times (50 wavelengths) a matrix of 900 entries ranging from \(1+1i\) (first element, top left) to \(900 + 900i\) (last element, bottom right). Note the expectation of row-major ordering in HDF5. The 3x3 top-left block is
For convenience, we can store compound objects as named tuples, which will then be mapped into HDF5 groups during export.
+
+
## mockup data
+usingPkg, HDF5
+
+# possibly multiple wavelengths
+wavelength =collect(400:50:800)
+Nl =length(wavelength)
+Lmax =3
+qmax =2*(Lmax*(Lmax+1)+Lmax)
+
+# dummy 30x30 matrix values for each wavelength
+# note the row-major ordering
+tdata =transpose(reshape(collect(1:qmax^2) +collect(1:qmax^2) *1im, (qmax,qmax)))
+tmatrix =zeros(ComplexF64,(Nl,qmax,qmax))
+for i=1:Nl
+ tmatrix[i,:,:] = tdata
+end
+
+print(tmatrix[1,1:3,1:3])
+
+l =zeros(Int64, qmax)
+m =zeros(Int64, qmax)
+s =Vector{String}(undef,qmax)
+let
+i=1
+for li =1:Lmax
+for mi =-li:li
+for si = ["electric", "magnetic"]
+ l[i] = li
+ m[i] = mi
+ s[i] = si
+ i = i+1
+end
+end
+end
+end
+
+
+
+
Saving to HDF5
+
Complex arrays are stored with r and i fields as usual. Note that Julia has column-major ordering, unlike HDF5, and we should therefore explicitly transpose the matrix before saving it, to avoid confusion if the data are to be read in another language. Another potential source of error is that the HDF5.jl library orders the array data differently, compared to h5py.
This document showcases a basic Matlab script to export T-matrices in the .tmat.h5 HDF5 format. For illustration, we start by producing a dummy dataset. This minimal reproducible example file is available for download as a standalone script: export_matlab.m.
+
+
Mockup input data
+
Consistent with the other examples, we generate a 50x30x30 array of dummy data, which repeats 50 times (50 wavelengths) a matrix of 900 entries ranging from \(1+1i\) (first element, top left) to \(900 + 900i\) (last element, bottom right). Note the expectation of row-major ordering in HDF5. The 3x3 top-left block is
The easyh5 library takes care of most of the details for us, when objects are stored in Matlab structures. There are a couple of caveats, illustrated below, such as polarization being handled separately, and attributes being added at a later stage.
+
+
% possibly multiple wavelengths
+wavelength= (400:50:800)';
+Nl=length(wavelength);
+
+Lmax=3;
+qmax=2*(Lmax*(Lmax+1)+Lmax);% T-matrix size
+
+% dummy 30x30 matrix values for each wavelength
+% note the transpose due to HDF5 expecting
+% row-major ordering vs matlab's default column-major
+tdata=transpose(reshape((1:qmax^2) +1i*(1:qmax^2), [qmax,qmax]));
+
+tmatrix=zeros(Nl,qmax,qmax);
+fori=1:Nl
+tmatrix(i,:,:) =tdata;
+end
+
+squeeze(tmatrix(1,1:3,1:3))
+
+% modes, but note that polarization is turned into strings separately
+i=1;
+forl=1:3
+form=-l:l
+fors=1:2
+modes.l(i) =int64(l);
+modes.m(i) =int64(m);
+modes.s(i) =int64(s);
+i=i+1;
+end
+end
+end
+polars= ["electric","magnetic"];
+polarization=polars(modes.s);
+modes=rmfield(modes,'s');
+
+% dummy 'analytical zeros' for e.g. EBCM methods
+% [zerosq, zerosqp] = ndgrid(1:2:30, 1:2:30);
+% zeros = struct('q', zerosq, 'qp', zerosqp);
+
+% materials
+embedding=struct('relative_permeability',1.0,...
+'relative_permittivity',1.33^2);
+particle=struct('relative_permeability',1.0,...
+'relative_permittivity',repmat(-11.4+1.181i, [Nl,1]));
+
+% geometry
+geometry=struct('radiusxy',20.0,'radiusz',40.0);
+
+scatterer=struct('material',particle,...
+'geometry',geometry);
+
+% details about computation
+
+method_parameters=struct('Lmax',int64(3),...
+'Ntheta',int64(100));
+
+computation=struct('method_parameters',method_parameters);
+% 'analytical_zeros', zeros can be added here
+
+script=convertCharsToStrings(fileread('export_matlab.m'));
+
+% combined (almost all) information into one struct
+s=struct('tmatrix',tmatrix,...
+'vacuum_wavelength',wavelength,...
+'embedding',embedding,...
+'scatterer',scatterer,...
+'modes',modes,...
+'computation',computation);
+
+
+
+
Saving to HDF5
+
+
addpath(genpath('../easyh5/'));
+
+
saveh5 does most of the work, but we have to write polarization and script separately as strings within structs seem to trip easyh5.
+
+
f='am.tmat.h5';
+[h5major,h5minor,h5rel] =H5.get_libversion();% HDF5 version
+matlabv=version;% Matlab version
+software=sprintf('SMARTIES=1.1, matlab=%s, HDF5=%d.%d.%d',matlabv,h5major,h5minor,h5rel);
+
+saveh5(s,f,'ComplexFormat', {'r','i'},'rootname','','Compression','deflate');
+
+% deal with string objects manually
+h5create(f,'/computation/files/script',size(script),'Datatype','string')
+h5write(f,'/computation/files/script',script)
+
+h5create(f,'/modes/polarization',size(polarization),'Datatype','string')
+h5write(f,'/modes/polarization',polarization)
+
+% root attributes
+h5writeatt(f,'/','name','Au prolate spheroid in water');
+h5writeatt(f,'/','storage_format_version','v0.01');
+h5writeatt(f,'/','description',...
+'Computation using SMARTIES, a numerically robust EBCM implementation for spheroids');
+h5writeatt(f,'/','keywords','gold, spheroid, ebcm, passive, reciprocal, czinfinity, mirrorxyz');
+
+% object and group attributes
+h5writeatt(f,'/vacuum_wavelength','unit','nm');
+
+h5writeatt(f,'/embedding','keywords','non-dispersive');
+h5writeatt(f,'/embedding','name','H2O, Water');
+
+h5writeatt(f,'/scatterer/material','name','Au, Gold');
+h5writeatt(f,'/scatterer/material','reference','Au from Raschke et al 10.1103/PhysRevB.86.235147');
+h5writeatt(f,'/scatterer/material','keywords','dispersive, plasmonic');
+
+h5writeatt(f,'/scatterer/geometry','name','homogeneous spheroid with symmetry axis z');
+h5writeatt(f,'/scatterer/geometry','unit','nm');
+h5writeatt(f,'/scatterer/geometry','shape','spheroid')
+
+h5writeatt(f,'/computation','description','Computation using SMARTIES, a numerically robust EBCM implementation for spheroids');
+h5writeatt(f,'/computation','name','SMARTIES');
+h5writeatt(f,'/computation','method','EBCM, Extended Boundary Condition Method');
+h5writeatt(f,'/computation','software',software);
This document showcases a basic Matlab script to export T-matrices in the .tmat.h5 HDF5 format. For illustration, we start by producing a dummy dataset. This minimal reproducible example file is available for download as a standalone script: export_python.py.
+
+
Mockup input data
+
Consistent with the other examples, we generate a 50x30x30 array of dummy data, which repeats 50 times (50 wavelengths) a matrix of 900 entries ranging from \(1+1i\) (first element, top left) to \(900 + 900i\) (last element, bottom right). Note the expectation of row-major ordering in HDF5. The 3x3 top-left block is
The Python library h5py provides a convenient object-oriented interface to save python objects in the required HDF5 structure, so we don’t need particular care to organise them on the Python side.
+
+
import numpy as np
+import os, sys
+import h5py
+#possibly multiple wavelengths
+wavelength = np.arange(400,850, 50)
+Nl =len(wavelength)
+lmax =3
+qmax =2*(lmax*(lmax+1)+lmax)
+# dummy 30x30 matrix values repeated for each wavelength
+tdata = np.reshape(np.arange(1,901,1) +1j*np.arange(1,901,1), (qmax,qmax))
+tmatrix = np.zeros((Nl,qmax,qmax), dtype="complex")
+for i inrange(Nl):
+ tmatrix[i,:,:] = tdata
+
+print(tmatrix[0,0:3,0:3])
+
+l = np.zeros(qmax)
+m = np.zeros(qmax)
+s = np.array([b'xxxxxxic']*len(l))
+i=0
+for li inrange(1,lmax+1):
+for mi inrange(-li, li+1):
+for si in [b'electric', b'magnetic']:
+ l[i] = li
+ m[i] = mi
+ s[i] = si
+ i = i+1
+
+
+
+
Saving to HDF5
+
The Python library h5py provides a convenient interface to generate groups, datasets, and attributes with simple assigments:
+
+
# Saving to HDF5
+f ='ap.tmat.h5';
+with h5py.File(f, "w") as f:
+ f["vacuum_wavelength"] = np.asarray(wavelength)
+ f["vacuum_wavelength"].attrs["unit"] ="nm"
+ f["tmatrix"] = tmatrix
+ f['modes/l'] = l
+ f['modes/m'] = m
+ f['modes/polarization'] = s
+ emb = f.create_group(f"embedding")
+ emb["relative_permittivity"] =1.33**2
+ emb["relative_permeability"] =1.0
+ emb.attrs["name"] ="H2O, Water"
+ emb.attrs["keywords"] ="non-dispersive"
+ sca_mat = f.create_group("scatterer/material")
+ sca_mat.attrs["name"] ='Au, Gold'
+ sca_mat.attrs["keywords"] ="dispersive, plasmonic"
+ sca_mat.attrs["reference"] ="Au from Raschke et al 10.1103/PhysRevB.86.235147"
+ sca_mat["relative_permittivity"] = np.ones(len(wavelength), dtype =complex)*(-11.4+1.181j)
+ sca_mat["relative_permeability"] =1.0
+ sca_gr = f.create_group("scatterer/geometry")
+ sca_gr["radiusxy"] =20.0
+ sca_gr["radiusz"] =40.0
+ sca_gr.attrs['unit'] ='nm'
+ sca_gr.attrs['shape'] ='spheroid'
+ sca_gr.attrs['name'] ='homogeneous spheroid with symmetry axis z'
+ mpar = f.create_group("computation/method_parameters")
+#f["computation/analytical_zeros"] = analytical_zeros # not needed in this example
+ mpar["Lmax"] = lmax
+ mpar["Ntheta"] =100
+ f.create_group("computation/files")
+withopen(__file__, "r") as scriptfile:
+ f[f"computation/files/{os.path.basename(__file__)}"] = scriptfile.read()
+ f["computation"].attrs["method"] ="EBCM, Extended Boundary Condition Method"
+ f["computation"].attrs["description"] ="Computation using SMARTIES, a numerically robust EBCM implementation for spheroids"
+ f["computation"].attrs["software"] =f"SMARTIES=1.1, python={sys.version.split()[0]}, h5py={h5py.__version__}"
+ f["computation"].attrs["name"] ="SMARTIES"
+ f.attrs['name'] ='Au prolate spheroid in water'
+ f.attrs['keywords'] ='gold, spheroid, ebcm, passive, reciprocal, czinfinity, mirrorxyz'
+ f.attrs['description'] ='Computation using SMARTIES, a numerically robust EBCM implementation for spheroids'
+ f.attrs['storage_format_version'] ='v0.01'