@lewisjared what do you think of something like the following for writing?
import netcdf_scm
ts = test_cube.get_scm_timeseries()
from iris.pandas import as_cube
def to_cube(series, calendars={1: cf_units.CALENDAR_GREGORIAN}):
cube = as_cube(series, calendars=calendars)
cube.coord("index").rename("time")
return cube
def get_cubes(df, calendars={1: cf_units.CALENDAR_GREGORIAN}):
cube_list = []
for i, (v, c) in enumerate(ts.timeseries().iterrows()):
cube = to_cube(c, calendars=calendars)
cube.units = df.index.get_level_values("unit")[i]
cube.long_name = df.index.get_level_values("variable")[i]
metadata = {
level: df.index.get_level_values(level)[i]
for level in df.index.names
if level not in ["unit", "variable"]
}
metadata["netcdf_scm_info"] = "netcdf-scm v{}".format(netcdf_scm.__version__)
metadata.update(test_cube.cube.attributes)
cube.attributes = metadata
# we can add parameters like so although it might be smarter
# to add them as variables with dimension `run_no` so the mapping
# is simpler later...
cube.add_aux_coord(iris.coords.AuxCoord(
1.3,
long_name="example_scalar",
units="K"
))
cube.add_aux_coord(iris.coords.AuxCoord(
"string here",
long_name="example_generic",
units="no_unit"
))
cube_list.append(cube)
return cube_list
cube_list = get_cubes(ts.timeseries())
iris.save(
cube_list,
"tmp.nc",
local_keys=ts.timeseries().index.names # this lets us separate timeseries metadata from 'info'
)
It gives something like the below. It would be horrible to read without our readers but I think reading should be pretty trivial cause we know what to expect (loop over variables, store data and metadata for timeseries, get parameters, get info, make ScmDataFrame instance). The thing I'm really not sure about is how to save e.g. parameters vs. just metadata? Should we just put all the parameters in as variables and add an extra dimension, run_no
, which we can use to save them?
$ ncdump -h tmp.nc
netcdf tmp {
dimensions:
time = 120 ;
string11 = 11 ;
variables:
double toa_outgoing_longwave_flux(time) ;
toa_outgoing_longwave_flux:long_name = "toa_outgoing_longwave_flux" ;
toa_outgoing_longwave_flux:units = "W m^-2" ;
toa_outgoing_longwave_flux:activity_id = "CMIP" ;
toa_outgoing_longwave_flux:climate_model = "BCC-CSM2-MR" ;
toa_outgoing_longwave_flux:member_id = "r1i1p1f1" ;
toa_outgoing_longwave_flux:model = "unspecified" ;
toa_outgoing_longwave_flux:region = "World" ;
toa_outgoing_longwave_flux:scenario = "1pctCO2" ;
toa_outgoing_longwave_flux:coordinates = "example_generic example_scalar" ;
double time(time) ;
time:axis = "T" ;
time:units = "hours since 1970-01-01 00:00:00" ;
time:standard_name = "time" ;
time:calendar = "gregorian" ;
char example_generic(string11) ;
example_generic:units = "no_unit" ;
example_generic:long_name = "example_generic" ;
double example_scalar ;
example_scalar:units = "K" ;
example_scalar:long_name = "example_scalar" ;
double toa_outgoing_longwave_flux_0(time) ;
toa_outgoing_longwave_flux_0:long_name = "toa_outgoing_longwave_flux" ;
toa_outgoing_longwave_flux_0:units = "W m^-2" ;
toa_outgoing_longwave_flux_0:activity_id = "CMIP" ;
toa_outgoing_longwave_flux_0:climate_model = "BCC-CSM2-MR" ;
toa_outgoing_longwave_flux_0:member_id = "r1i1p1f1" ;
toa_outgoing_longwave_flux_0:model = "unspecified" ;
toa_outgoing_longwave_flux_0:region = "World|Northern Hemisphere" ;
toa_outgoing_longwave_flux_0:scenario = "1pctCO2" ;
toa_outgoing_longwave_flux_0:coordinates = "example_generic example_scalar" ;
double toa_outgoing_longwave_flux_1(time) ;
toa_outgoing_longwave_flux_1:long_name = "toa_outgoing_longwave_flux" ;
toa_outgoing_longwave_flux_1:units = "W m^-2" ;
toa_outgoing_longwave_flux_1:activity_id = "CMIP" ;
toa_outgoing_longwave_flux_1:climate_model = "BCC-CSM2-MR" ;
toa_outgoing_longwave_flux_1:member_id = "r1i1p1f1" ;
toa_outgoing_longwave_flux_1:model = "unspecified" ;
toa_outgoing_longwave_flux_1:region = "World|Southern Hemisphere" ;
toa_outgoing_longwave_flux_1:scenario = "1pctCO2" ;
toa_outgoing_longwave_flux_1:coordinates = "example_generic example_scalar" ;
// global attributes:
:branch_method = "branch" ;
:branch_time_in_child = 0. ;
:branch_time_in_parent = 0. ;
:cmor_version = "3.3.2" ;
:comment = "at the top of the atmosphere (to be compared with satellite measurements)" ;
:contact = "Dr. Tongwen Wu ([email protected])" ;
:creation_date = "2018-10-15T06:27:37Z" ;
:data_specs_version = "01.00.27" ;
:description = "DECK: 1pctCO2" ;
:experiment = "1 percent per year increase in CO2" ;
:experiment_id = "1pctCO2" ;
:external_variables = "areacella" ;
:forcing_index = 1 ;
:frequency = "mon" ;
:further_info_url = "https://furtherinfo.es-doc.org/CMIP6.BCC.BCC-CSM2-MR.1pctCO2.none.r1i1p1f1" ;
:grid = "T106" ;
:grid_label = "gn" ;
:history = "2018-10-15T06:27:35Z ; CMOR rewrote data to be consistent with CMIP6, CF-1.7 CMIP-6.2 and CF standards." ;
:initialization_index = 1 ;
:institution = "Beijing Climate Center, Beijing 100081, China" ;
:institution_id = "BCC" ;
:license = "CMIP6 model data produced by BCC is licensed under a Creative Commons Attribution ShareAlike 4.0 International License (https://creativecommons.org/licenses). Consult https://pcmdi.llnl.gov/CMIP6/TermsOfUse for terms of use governing CMIP6 output, including citation requirements and proper acknowledgment. Further information about this data, including some limitations, can be found via the further_info_url (recorded as a global attribute in this file) and at https:///pcmdi.llnl.gov/. The data producers and data providers make no warranty, either express or implied, including, but not limited to, warranties of merchantability and fitness for a particular purpose. All liabilities arising from the supply of the information (including any liability arising in negligence) are excluded to the fullest extent permitted by law." ;
:mip_era = "CMIP6" ;
:netcdf_scm_info = "netcdf-scm v1.0.0+41.gbb3285d.dirty" ;
:nominal_resolution = "100 km" ;
:original_name = "FLUT" ;
:parent_activity_id = "CMIP" ;
:parent_experiment_id = "piControl" ;
:parent_mip_era = "CMIP6" ;
:parent_source_id = "BCC-CSM2-MR" ;
:parent_time_units = "days since 1850-01-01" ;
:parent_variant_label = "r1i1p1f1" ;
:physics_index = 1 ;
:product = "model-output" ;
:realization_index = 1 ;
:realm = "atmos" ;
:references = "Model described by Tongwen Wu et al. (JGR 2013; JMR 2014; submmitted to GMD,2018). Also see http://forecast.bcccsm.ncc-cma.net/htm" ;
:run_variant = "forcing: GHG" ;
:source = "BCC-CSM 2 MR (2017): aerosol: none atmos: BCC_AGCM3_MR (T106; 320 x 160 longitude/latitude; 46 levels; top level 1.46 hPa) atmosChem: none land: BCC_AVIM2 landIce: none ocean: MOM4 (1/3 deg 10S-10N, 1/3-1 deg 10-30 N/S, and 1 deg in high latitudes; 360 x 232 longitude/latitude; 40 levels; top grid cell 0-10 m) ocnBgchem: none seaIce: SIS2" ;
:source_id = "BCC-CSM2-MR" ;
:source_type = "AOGCM" ;
:sub_experiment = "none" ;
:sub_experiment_id = "none" ;
:table_id = "Amon" ;
:table_info = "Creation Date:(30 July 2018) MD5:e53ff52009d0b97d9d867dc12b6096c7" ;
:title = "BCC-CSM2-MR output prepared for CMIP6" ;
:tracking_id = "hdl:21.14100/4f3fdd6c-bef7-4ec0-a692-5ca29f51e1ba" ;
:variable_id = "rlut" ;
:variant_label = "r1i1p1f1" ;
:Conventions = "CF-1.5" ;
}