1+ # This allows creating ASAGI files from GMG structures, which can be used for example
2+ # as input for codes such as SeisSol or ExaHype
3+ #
4+ # see https://github.com/TUM-I5/ASAGI
5+ #
6+ #
7+
8+ using NCDatasets
9+ using NCDatasets: nc_create, NC_NETCDF4, NC_CLOBBER, NC_NOWRITE, nc_def_dim, nc_def_compound, nc_insert_compound, nc_def_var, nc_put_var, nc_close, NC_INT, nc_unsafe_put_var, libnetcdf, check, ncType, nc_open, nc_inq_vartype, nc_inq_compound_nfields, nc_inq_compound_size, nc_inq_compound_name, nc_inq_compound_fieldoffset,nc_inq_compound_fieldndims,nc_inq_compound_fielddim_sizes, nc_inq_compound_fieldname, nc_inq_compound_fieldindex, nc_inq_compound_fieldtype, nc_inq_compound, nc_inq_varid, nc_get_var!, nc_insert_array_compound, nc_def_vlen
10+
11+ export write_ASAGI, read_ASAGI
12+
13+ """
14+ write_ASAGI(fname::String, Data::CartData;
15+ fields::Union{Nothing, Tuple}=nothing,
16+ km_to_m::Bool=false)
17+
18+ Writes a CartData structure `Data` to an ASAGI file, which can be read by SeisSol or ExaHype.
19+ You can optionally pass a tuple with fields to be written. Note that we can only write individual (scalar) fields to disk,
20+ so vector or tensor fields needs to be split first
21+ """
22+ function write_ASAGI (fname:: String , Data:: CartData ;
23+ fields:: Union{Nothing, Tuple} = nothing ,
24+ km_to_m:: Bool = false )
25+
26+ nx,ny,nz = size (Data. x)
27+ x = Data. x. val[:,1 ,1 ]
28+ y = Data. y. val[1 ,:,1 ]
29+ z = Data. z. val[1 ,1 ,:]
30+ if km_to_m== true
31+ println (" convert to meters" )
32+ x = x.* 1000
33+ y = y.* 1000
34+ z = z.* 1000
35+ end
36+
37+ # Transfer data to a single array with NamedTuple entries
38+ material = fields_to_namedtuple (Data. fields, fields)
39+
40+ fname_asagi = fname* " _ASAGI.nc"
41+
42+ # ncid = nc_create(fname_asagi, NC_NETCDF4|NC_CLOBBER)
43+ ds = NCDataset (fname_asagi," c" , format= :netcdf4 )
44+
45+ # Write dimensions
46+ x_dimid = nc_def_dim (ds. ncid, " x" , nx)
47+ y_dimid = nc_def_dim (ds. ncid, " y" , ny)
48+ z_dimid = nc_def_dim (ds. ncid, " z" , nz)
49+
50+ v_x = defVar (ds," x" ,eltype (x),(" x" ,))
51+ v_y = defVar (ds," y" ,eltype (x),(" y" ,))
52+ v_z = defVar (ds," z" ,eltype (x),(" z" ,))
53+ v_x[:] = x
54+ v_y[:] = y
55+ v_z[:] = z
56+
57+ # add Tuple with data to file
58+ dimids = [x_dimid, y_dimid, z_dimid]
59+ T = eltype (material)
60+ typeid = nc_def_compound (ds. ncid, sizeof (T), " material" )
61+
62+ for i = 1 : fieldcount (T)
63+ # local dim_sizes
64+ offset = fieldoffset (T,i)
65+ nctype = ncType[fieldtype (T,i)]
66+ nc_insert_compound (
67+ ds. ncid, typeid, fieldname (T,i),
68+ offset, nctype)
69+ end
70+
71+ varid = nc_def_var (ds. ncid, " data" , typeid, reverse (dimids))
72+ nc_put_var (ds. ncid, varid, material)
73+
74+ # close file
75+ close (ds)
76+
77+ return fname_asagi
78+ end
79+
80+
81+
82+ # Transfer fields to a single array with NamedTuple entries
83+ function fields_to_namedtuple (fields:: NamedTuple , selected_fields)
84+ names = keys (fields)
85+ if ! isnothing (selected_fields)
86+ names = selected_fields
87+ end
88+ nfield = length (names)
89+ ndim = length (size (fields[1 ]))
90+
91+ s2 = NamedTuple {names} (zeros (nfield))
92+
93+ # check that they are all DiskArrays
94+ for ifield in 1 : nfield
95+ if ! isa (getproperty (fields,names[ifield]), Array)
96+ @show typeof (getproperty (fields,names[ifield]))
97+ error (" Field $(names[ifield]) is not an Array but instead a $(typeof (getproperty (fields,names[ifield]))) ; only Arrays are supported" )
98+ end
99+ end
100+
101+ material = Array {typeof(s2),ndim} (undef, size (fields[1 ]))
102+ for I in eachindex (material)
103+ data_local = []
104+ for ifield in 1 : nfield
105+ push! (data_local,getproperty (fields,names[ifield])[I])
106+
107+ end
108+
109+ local_tup = NamedTuple {names} (data_local)
110+
111+ material[I] = local_tup
112+ end
113+
114+ return material
115+ end
116+
117+ """
118+ data::CartData = read_ASAGI(fname_asagi::String)
119+
120+ This reads a 3D ASAGI NetCDF file, which is used as input for a number of codes such as SeisSol.
121+ It returns a CartData dataset
122+ """
123+ function read_ASAGI (fname_asagi:: String )
124+
125+ @assert fname_asagi[end - 2 : end ]== " .nc"
126+
127+ ds = NCDataset (fname_asagi," r" )
128+
129+ x = ds[" x" ][:];
130+ y = ds[" y" ][:];
131+ z = ds[" z" ][:];
132+
133+ nx,ny,nz = length (x),length (y),length (z)
134+
135+ data_set_names = keys (ds)
136+ id = findall (data_set_names.!= " x" .&& data_set_names.!= " y" .&& data_set_names.!= " z" )
137+ data_name = data_set_names[id]
138+
139+ varid = nc_inq_varid (ds. ncid,data_name[1 ])
140+ xtype = nc_inq_vartype (ds. ncid,varid)
141+
142+ # retrieve names of the fields
143+ numfields = nc_inq_compound_nfields (ds. ncid,xtype)
144+ cnames = Symbol .(nc_inq_compound_fieldname .(ds. ncid,xtype,0 : (numfields- 1 )))
145+
146+ types = []
147+ for fieldid = 0 : (numfields- 1 )
148+ local dim_sizes
149+ fT = NCDatasets. jlType[nc_inq_compound_fieldtype (ds. ncid,xtype,fieldid)]
150+
151+ fieldndims = nc_inq_compound_fieldndims (ds. ncid,xtype,fieldid)
152+
153+ if fieldndims == 0
154+ push! (types,fT)
155+ else
156+ dim_sizes = nc_inq_compound_fielddim_sizes (ds. ncid,xtype,fieldid)
157+ fT2 = NTuple{Int (dim_sizes[1 ]),fT}
158+ push! (types,fT2)
159+ end
160+ end
161+
162+ # Create a single NamedTuple with correct type and names
163+ data_element = ();
164+ for ifield= 1 : numfields
165+ data_element = (data_element... , types[ifield](0.0 ))
166+ end
167+
168+ T2 = typeof (NamedTuple {(cnames...,)} (data_element))
169+ data = Array {T2,3} (undef,nx,ny,nz)
170+ nc_get_var! (ds. ncid, varid, data)
171+
172+ # At this stage, data is an array of NamedTuple with correct names & types
173+ #
174+ # Now split them into separate fields.
175+ read_fields_data = ()
176+ for ifield= 1 : numfields
177+ data_1 = zeros (types[ifield],nx,ny,nz)
178+
179+ for I in CartesianIndices (data)
180+ loc = data[I]
181+ data_1[I] = getproperty (loc, cnames[ifield])
182+ end
183+
184+ read_fields_data = (read_fields_data... , data_1)
185+ end
186+ read_fields = NamedTuple {(cnames...,)} (read_fields_data)
187+
188+ close (ds)
189+
190+ return CartData (xyz_grid (x,y,z)... , read_fields)
191+ end
0 commit comments