def test_find_with_region_model_and_time_filter(self): cal = Calendar() region_model_id = "neanidelv-ptgsk" n_cells = 10 tags = ["initial", "unverified"] state_vector = self._create_state_vector(n_cells) # now start state_repository test state_repository = YamlStateRepository(self._test_state_directory) # put in two states, record the unique state_id.. state_id_1 = state_repository.put_state(region_model_id, cal.time(YMDhms(2001, 1, 1, 0, 0, 0)), state_vector, tags) state_id_2 = state_repository.put_state(region_model_id, cal.time(YMDhms(2001, 1, 2, 0, 0, 0)), state_vector, tags) all_states = state_repository.find_state() neanidelv_states = state_repository.find_state(region_model_id) self.assertEqual(2, len(all_states)) self.assertEqual(2, len(neanidelv_states)) most_recent_state_before_time = state_repository.find_state(region_model_id, cal.time(YMDhms(2001, 1, 1, 0, 0, 0))) self.assertEqual(1, len(most_recent_state_before_time)) self.assertEqual(state_id_1, most_recent_state_before_time[0].state_id) self.assertEqual(0, len(state_repository.find_state(region_model_id, cal.time(YMDhms(2000, 12, 31, 23, 59, 59))))) self.assertEqual(state_id_2, state_repository.find_state(region_model_id, cal.time(YMDhms(2002, 1, 1, 0, 0, 0)))[ 0].state_id)
def ensemble_demo(): utc = Calendar() t_start = utc.time(YMDhms(2011, 9, 1)) t_fc_ens_start = utc.time(YMDhms(2015, 7, 26)) disp_start = utc.time(YMDhms(2015, 7, 20)) dt = deltahours(1) n_obs = int(round((t_fc_ens_start - t_start) / dt)) n_fc_ens = 30 n_disp = int(round(t_fc_ens_start - disp_start) / dt) + n_fc_ens + 24 * 7 obs_time_axis = Timeaxis(t_start, dt, n_obs + 1) fc_ens_time_axis = Timeaxis(t_fc_ens_start, dt, n_fc_ens) display_time_axis = Timeaxis(disp_start, dt, n_disp) q_obs_m3s_ts = observed_tistel_discharge(obs_time_axis.total_period()) ptgsk = create_tistel_simulator( PTGSKOptModel, tistel.geo_ts_repository(tistel.grid_spec.epsg())) initial_state = burn_in_state(ptgsk, t_start, utc.time(YMDhms(2012, 9, 1)), q_obs_m3s_ts) ptgsk.run(obs_time_axis, initial_state) current_state = adjust_simulator_state(ptgsk, t_fc_ens_start, q_obs_m3s_ts) q_obs_m3s_ts = observed_tistel_discharge(display_time_axis.total_period()) ens_repos = tistel.arome_ensemble_repository(tistel.grid_spec) ptgsk_fc_ens = create_tistel_simulator(PTGSKModel, ens_repos) sims = ptgsk_fc_ens.create_ensembles(fc_ens_time_axis, t_fc_ens_start, current_state) for sim in sims: sim.simulate() plt.hold(1) percentiles = [10, 25, 50, 75, 90] plot_percentiles(sims, percentiles, obs=q_obs_m3s_ts) plt.interactive(1) plt.show()
def test_raise_exception_when_no_data_in_request_period(self): utc_calendar = Calendar() netcdf_repository = self._construct_from_test_data() netcdf_repository.raise_if_no_data = True # yes, for now, just imagine this could work. self.assertIsNotNone(netcdf_repository) utc_period = UtcPeriod( utc_calendar.time(YMDhms(2017, 1, 1, 0, 0, 0)), # a period where there is no data in utc_calendar.time(YMDhms(2020, 12, 31, 0, 0, 0))) # the file supplied type_source_map = dict() type_source_map['temperature'] = TemperatureSource #def test_function(): # # return netcdf_repository.get_timeseries( # type_source_map, # geo_location_criteria=None, # utc_period=utc_period) #self.assertRaises(RuntimeError, test_function) self.assertRaises( RuntimeError, netcdf_repository.get_timeseries, type_source_map, **{ 'geo_location_criteria': None, 'utc_period': utc_period })
def continuous_calibration(): utc = Calendar() t_start = utc.time(YMDhms(2011, 9, 1)) t_fc_start = utc.time(YMDhms(2015, 10, 1)) dt = deltahours(1) n_obs = int(round((t_fc_start - t_start)/dt)) obs_time_axis = TimeAxisFixedDeltaT(t_start, dt, n_obs + 1) q_obs_m3s_ts = observed_tistel_discharge(obs_time_axis.total_period()) ptgsk = create_tistel_simulator(PTGSKOptModel, tistel.geo_ts_repository(tistel.grid_spec.epsg())) initial_state = burn_in_state(ptgsk, t_start, utc.time(YMDhms(2012, 9, 1)), q_obs_m3s_ts) num_opt_days = 30 # Step forward num_opt_days days and store the state for each day: recal_start = t_start + deltahours(num_opt_days*24) t = t_start state = initial_state opt_states = {t: state} while t < recal_start: ptgsk.run(TimeAxisFixedDeltaT(t, dt, 24), state) t += deltahours(24) state = ptgsk.reg_model_state opt_states[t] = state recal_stop = utc.time(YMDhms(2011, 10, 30)) recal_stop = utc.time(YMDhms(2012, 5, 30)) curr_time = recal_start q_obs_avg = TsTransform().to_average(t_start, dt, n_obs + 1, q_obs_m3s_ts) target_spec = TargetSpecificationPts(q_obs_avg, IntVector([0]), 1.0, KLING_GUPTA) target_spec_vec = TargetSpecificationVector([target_spec]) i = 0 times = [] values = [] p, p_min, p_max = construct_calibration_parameters(ptgsk) while curr_time < recal_stop: print(i) i += 1 opt_start = curr_time - deltahours(24*num_opt_days) opt_state = opt_states.pop(opt_start) p = ptgsk.region_model.get_region_parameter() p_opt = ptgsk.optimize(TimeAxisFixedDeltaT(opt_start, dt, 24*num_opt_days), opt_state, target_spec_vec, p, p_min, p_max, tr_stop=1.0e-5) ptgsk.region_model.set_region_parameter(p_opt) corr_state = adjust_simulator_state(ptgsk, curr_time, q_obs_m3s_ts) ptgsk.run(TimeAxisFixedDeltaT(curr_time, dt, 24), corr_state) curr_time += deltahours(24) opt_states[curr_time] = ptgsk.reg_model_state discharge = ptgsk.region_model.statistics.discharge([0]) times.extend(discharge.time(i) for i in range(discharge.size())) values.extend(list(np.array(discharge.v))) plt.plot(utc_to_greg(times), values) plot_results(None, q_obs=observed_tistel_discharge(UtcPeriod(recal_start, recal_stop))) set_calendar_formatter(Calendar()) #plt.interactive(1) plt.title("Continuously recalibrated discharge vs observed") plt.xlabel("Time in UTC") plt.ylabel(r"Discharge in $\mathbf{m^3s^{-1}}$", verticalalignment="top", rotation="horizontal") plt.gca().yaxis.set_label_coords(0, 1.1)
def test_construct_repository(self): utc_calendar = Calendar() netcdf_repository = self._construct_from_test_data() self.assertIsNotNone(netcdf_repository) utc_period = UtcPeriod( utc_calendar.time(YMDhms(2005, 1, 1, 0, 0, 0)), utc_calendar.time(YMDhms(2014, 12, 31, 0, 0, 0))) type_source_map = dict() type_source_map['temperature'] = TemperatureSource geo_ts_dict = netcdf_repository.get_timeseries( type_source_map, geo_location_criteria=None, utc_period=utc_period) self.assertIsNotNone(geo_ts_dict)
def test_returns_empty_ts_when_no_data_in_request_period(self): utc_calendar = Calendar() netcdf_repository = self._construct_from_test_data() self.assertIsNotNone(netcdf_repository) utc_period = UtcPeriod( utc_calendar.time(YMDhms(2017, 1, 1, 0, 0, 0)), # a period where there is no data in utc_calendar.time(YMDhms(2020, 12, 31, 0, 0, 0))) # the file supplied type_source_map = dict() type_source_map['temperature'] = TemperatureSource geo_ts_dict = netcdf_repository.get_timeseries( type_source_map, geo_location_criteria=None, utc_period=utc_period) self.assertIsNotNone(geo_ts_dict)
def test_get_timeseries_using_known_service_and_db_content(self): utc = Calendar() # always use Calendar() stuff met_stations = [ # this is the list of MetStations, the gis_id tells the position, the remaining tells us what properties we observe/forecast/calculate at the metstation (smg-ts) MetStationConfig( gis_id=598, temperature=u'/NeNi-Sylsjøen......-T0017V3KI0114', precipitation=u'/NeNi-Sylsjøen-2....-T0000D9BI0124'), MetStationConfig( gis_id=574, temperature=u'/NeNi-Stuggusjøen...-T0017V3KI0114', precipitation=u'/NeNi-Stuggusjøen...-T0000D9BI0124', radiation= u'/ENKI/STS/Radiation/Sim.-Stuggusjøen...-T0006A0B-0119') ] #note: the MetStationConfig can be constructed from yaml-config gis_location_repository = GisLocationService( ) # this provides the gis locations for my stations smg_ts_repository = SmGTsRepository( PROD, FC_PROD) # this provide the read function for my time-series geo_ts_repository = GeoTsRepository( epsg_id=32633, geo_location_repository=gis_location_repository, ts_repository=smg_ts_repository, met_station_list=met_stations, ens_config=None) #pass service info and met_stations self.assertIsNotNone(geo_ts_repository) utc_period = UtcPeriod(utc.time(YMDhms(2010, 1, 1, 0, 0, 0)), utc.time(YMDhms(2010, 1, 2, 0, 0, 0))) ts_types = ['temperature', 'precipitation', 'radiation'] geo_ts_dict = geo_ts_repository.get_timeseries( ts_types, utc_period=utc_period, geo_location_criteria=None) self.assertIsNotNone(geo_ts_dict) for ts_type in ts_types: self.assertTrue( ts_type in geo_ts_dict.keys(), "we ecpect to find an entry for each requested type (it could be empty list though" ) self.assertTrue( len(geo_ts_dict[ts_type]) > 0, "we expect to find the series that we pass in, given they have not changed the name in SmG PROD" )
def forecast_demo(): """Simple forecast demo using arome data from met.no. Initial state is bootstrapped by simulating one hydrological year (starting Sept 1. 2011), and then calculating the state August 31. 2012. This state is then used as initial state for simulating Sept 1, 2011, after scaling with observed discharge. The validity of this approach is limited by the temporal variation of the spatial distribution of the discharge state, q, in the Kirchner method. The model is then stepped forward until Oct 1, 2015, and then used to compute the discharge for 65 hours using Arome data. At last, the results are plotted as simple timeseries. """ utc = Calendar() t_start = utc.time(YMDhms(2011, 9, 1)) t_fc_start = utc.time(YMDhms(2015, 10, 1)) dt = deltahours(1) n_obs = int(round((t_fc_start - t_start) / dt)) n_fc = 65 obs_time_axis = Timeaxis(t_start, dt, n_obs) fc_time_axis = Timeaxis(t_fc_start, dt, n_fc) total_time_axis = Timeaxis(t_start, dt, n_obs + n_fc) q_obs_m3s_ts = observed_tistel_discharge(total_time_axis.total_period()) ptgsk = create_tistel_simulator( PTGSKOptModel, tistel.geo_ts_repository(tistel.grid_spec.epsg())) initial_state = burn_in_state(ptgsk, t_start, utc.time(YMDhms(2012, 9, 1)), q_obs_m3s_ts) ptgsk.run(obs_time_axis, initial_state) plot_results(ptgsk, q_obs_m3s_ts) current_state = adjust_simulator_state(ptgsk, t_fc_start, q_obs_m3s_ts) ptgsk_fc = create_tistel_simulator( PTGSKModel, tistel.arome_repository(tistel.grid_spec, t_fc_start)) ptgsk_fc.run(fc_time_axis, current_state) plt.figure() q_obs_m3s_ts = observed_tistel_discharge(fc_time_axis.total_period()) plot_results(ptgsk_fc, q_obs_m3s_ts) plt.interactive(1) plt.show()
def test_get_ensemble_forecast_using_known_service_and_db_content(self): utc = Calendar() met_stations = [MetStationConfig(gis_id=402, temperature=u'/LTM5-Nea...........-T0017A3P_EC00_ENS', precipitation=u'/LTM5-Nea...........-T0000A5P_EC00_ENS')] gis_location_repository = GisLocationService() smg_ts_repository = SmGTsRepository(PREPROD, FC_PREPROD) n_ensembles = 51 ens_station_list = [ EnsembleStation(402, n_ensembles, temperature_ens = lambda i:u'/LTM5-Nea...........-T0017A3P_EC00_E{0:02}'.format(i), precipitation_ens = lambda i:u'/LTM5-Nea...........-T0000A5P_EC00_E{0:02}'.format(i), wind_speed_ens = None, radiation_ens = None, relative_humidity_ens = None ), EnsembleStation(460, n_ensembles, temperature_ens = lambda i:u'/LTM5-Tya...........-T0017A3P_EC00_E{0:02}'.format(i), precipitation_ens = lambda i:u'/LTM5-Tya...........-T0000A5P_EC00_E{0:02}'.format(i), wind_speed_ens = None, radiation_ens = None, relative_humidity_ens = None ) ] ens_config = EnsembleConfig(n_ensembles, ens_station_list) geo_ts_repository = GeoTsRepository( epsg_id = 32633, geo_location_repository = gis_location_repository, ts_repository = smg_ts_repository, met_station_list = met_stations, ens_config = ens_config) self.assertIsNotNone(geo_ts_repository) utc_period = UtcPeriod(utc.time(YMDhms(2015, 10, 1, 0, 0, 0)),utc.time(YMDhms(2015, 10, 10, 0, 0, 0))) ts_types= ['temperature', 'precipitation'] ens_geo_ts_dict = geo_ts_repository.get_forecast_ensemble(ts_types, utc_period=utc_period, t_c=None, geo_location_criteria=None) self.assertIsNotNone(ens_geo_ts_dict) self.assertEqual(ens_config.n_ensembles, len(ens_geo_ts_dict)) for i in range(ens_config.n_ensembles): for ts_type in ts_types: self.assertTrue(ts_type in ens_geo_ts_dict[i].keys(), "we expect to find an entry for each requested type (it could be empty list though)") self.assertTrue(len(ens_geo_ts_dict[i][ts_type])>0, "we expect to find the series that we pass in by name in SmG PREPROD")
def test_get_forecast_using_known_service_and_db_content(self): utc = Calendar() met_stations=[ # this is the list of MetStations, the gis_id tells the position, the remaining tells us what properties we observe/forecast/calculate at the metstation (smg-ts) MetStationConfig(gis_id=402, temperature=u'/LTM5-Nea...........-T0017A3P_EC00_ENS', precipitation=u'/LTM5-Nea...........-T0000A5P_EC00_ENS') ] gis_location_repository = GisLocationService() smg_ts_repository = SmGTsRepository(PREPROD, FC_PREPROD) geo_ts_repository = GeoTsRepository( epsg_id = 32633, geo_location_repository = gis_location_repository, ts_repository = smg_ts_repository, met_station_list = met_stations, ens_config = None) self.assertIsNotNone(geo_ts_repository) utc_period = UtcPeriod(utc.time(YMDhms(2015, 10, 1, 0, 0, 0)), utc.time(YMDhms(2015, 10, 10, 0, 0, 0))) ts_types= ['temperature', 'precipitation'] geo_ts_dict = geo_ts_repository.get_forecast(ts_types, utc_period=utc_period, t_c=None, geo_location_criteria=None) self.assertIsNotNone(geo_ts_dict) for ts_type in ts_types: self.assertTrue(ts_type in geo_ts_dict.keys(), "we expect to find an entry for each requested type (it could be empty list though)") self.assertTrue(len(geo_ts_dict[ts_type])>0, "we expect to find the series that we pass in by name in SmG PREPROD")
def test_find_with_region_model_filter(self): cal = Calendar() utc_timestamp = cal.time(YMDhms(2001, 1, 1)) region_model_id = "neanidelv-ptgsk" n_cells = 10 tags = ["initial", "unverified"] state_vector = self._create_state_vector(n_cells) # now start state_repository test state_repository = YamlStateRepository(self._test_state_directory) # put in two states, record the unique state_id.. state_repository.put_state(region_model_id, utc_timestamp, state_vector, tags) state_repository.put_state("tokke-ptgsk", utc_timestamp, state_vector, tags) all_states = state_repository.find_state() neanidelv_states = state_repository.find_state(region_model_id) self.assertEqual(2, len(all_states)) self.assertEqual(1, len(neanidelv_states)) self.assertEqual(neanidelv_states[0].region_model_id, region_model_id)
def test_crudf_cycle(self): """ Verify we can create, store, read, find and delete state in the state repository """ # arrange, by creating one State cal = Calendar() utc_timestamp = cal.time(YMDhms(2001, 1, 1)) region_model_id = "neanidelv-ptgsk" n_cells = 10 tags = ["initial", "unverified"] state_vector = self._create_state_vector(n_cells) self.assertIsNotNone(state_vector, "we should have a valid state vector object at this spot") # now start state_repository test state_repository = YamlStateRepository(self._test_state_directory) # put in two states, record the unique state_id.. state_id_1 = state_repository.put_state(region_model_id, utc_timestamp, state_vector, tags) state_id_2 = state_repository.put_state(region_model_id, utc_timestamp, state_vector, tags) # assert that we got two unique state_id self.assertIsNotNone(state_id_1, "We expect back a unique id") self.assertIsNotNone(state_id_2, "We expect back a unique id") self.assertNotEqual(state_id_1, state_id_2, "storing two state, same model, same time, each state should be stored with a unique id") # now we should have two states in the repository state_infos = state_repository.find_state() self.assertEqual(2, len(state_infos), "We just stored two, expect two back..") # extra test, verify that we really stored the state (using kirchner q) state_1 = state_repository.get_state(state_id_1) self.assertEqual(n_cells, state_1.size(), "expect to get back state with same number of cells") for i in range(n_cells): self.assertAlmostEqual(state_1[i].kirchner.q, state_vector[i].kirchner.q, 3, "state repository should preserve state...") # now remove state state_repository.delete_state(state_id_1) # check that we got just one left, and that it is the correct one.. state_list = state_repository.find_state() self.assertEqual(1, len(state_list)) self.assertEqual(state_list[0].region_model_id, region_model_id) self.assertEqual(state_list[0].utc_timestamp, utc_timestamp) self.assertEqual(state_list[0].state_id, state_id_2)
def test_run_observed_then_arome_and_store(self): """ Start Tistel 2015.09.01, dummy state with some kirchner water use observations around Tistel (geo_ts_repository) and simulate forwared to 2015.10.01 (store discharge and catchment level precip/temp) then use arome forecast for 65 hours (needs arome for this period in arome-directory) finally store the arome results. """ utc = Calendar() # No offset gives Utc time_axis = TimeAxisFixedDeltaT(utc.time(YMDhms(2015, 9, 1, 0)), deltahours(1), 30 * 24) fc_time_axis = TimeAxisFixedDeltaT(utc.time(YMDhms(2015, 10, 1, 0)), deltahours(1), 65) interpolation_id = 0 ptgsk = DefaultSimulator("Tistel-ptgsk", interpolation_id, self.region_model_repository, self.geo_ts_repository, self.interpolation_repository, None) n_cells = ptgsk.region_model.size() ptgsk_state = DefaultStateRepository(ptgsk.region_model.__class__, n_cells) ptgsk.region_model.set_state_collection(-1, True) # collect state so we can inspect it s0 = ptgsk_state.get_state(0) for i in range(s0.size()): # add some juice to get started s0[i].kirchner.q = 0.5 ptgsk.run(time_axis, s0) print("Done simulation, testing that we can extract data from model") cids = api.IntVector() # we pull out for all the catchments-id if it's empty model = ptgsk.region_model # fetch out the model sum_discharge = model.statistics.discharge(cids) self.assertIsNotNone(sum_discharge) avg_temperature = model.statistics.temperature(cids) avg_precipitation = model.statistics.precipitation(cids) self.assertIsNotNone(avg_precipitation) self.assertIsNotNone(avg_temperature) for time_step in range(time_axis.size()): precip_raster = model.statistics.precipitation(cids, time_step) # example raster output self.assertEqual(precip_raster.size(), n_cells) avg_gs_lwc = model.gamma_snow_state.lwc(cids) # sca skaugen|gamma self.assertIsNotNone(avg_gs_lwc) # lwc surface_heat alpha melt_mean melt iso_pot_energy temp_sw avg_gs_output = model.gamma_snow_response.outflow(cids) self.assertIsNotNone(avg_gs_output) print("done. now save to db") # SmGTsRepository(PROD,FC_PROD) save_list = [ TsStoreItem(u'/test/x/shyft/tistel/discharge_m3s', lambda m: m.statistics.discharge(cids)), TsStoreItem(u'/test/x/shyft/tistel/temperature', lambda m: m.statistics.temperature(cids)), TsStoreItem(u'/test/x/shyft/tistel/precipitation', lambda m: m.statistics.precipitation(cids)), ] tss = TimeseriesStore(SmGTsRepository(PREPROD, FC_PREPROD), save_list) self.assertTrue(tss.store_ts(ptgsk.region_model)) print("Run forecast arome") endstate = ptgsk.region_model.state_t.vector_t() ptgsk.region_model.get_states(endstate) # get the state at end of obs ptgsk.geo_ts_repository = self.arome_repository # switch to arome here ptgsk.run_forecast(fc_time_axis, fc_time_axis.start, endstate) # now forecast print("Done forecast") fc_save_list = [ TsStoreItem(u'/test/x/shyft/tistel/fc_discharge_m3s', lambda m: m.statistics.discharge(cids)), TsStoreItem(u'/test/x/shyft/tistel/fc_temperature', lambda m: m.statistics.temperature(cids)), TsStoreItem(u'/test/x/shyft/tistel/fc_precipitation', lambda m: m.statistics.precipitation(cids)), TsStoreItem(u'/test/x/shyft/tistel/fc_radiation', lambda m: m.statistics.radiation(cids)), TsStoreItem(u'/test/x/shyft/tistel/fc_rel_hum', lambda m: m.statistics.rel_hum(cids)), TsStoreItem(u'/test/x/shyft/tistel/fc_wind_speed', lambda m: m.statistics.wind_speed(cids)), ] TimeseriesStore(SmGTsRepository(PREPROD, FC_PREPROD), fc_save_list).store_ts(ptgsk.region_model) print("Done save to db")
def test_get_ensemble_forecast_using_known_service_and_db_content( self): utc = Calendar() # always use Calendar() stuff met_stations = [ # this is the list of MetStations, the gis_id tells the position, the remaining tells us what properties we observe/forecast/calculate at the metstation (smg-ts) MetStationConfig( gis_id=598, temperature=u'/LTM5-Nea...........-T0017A3P_EC00_ENS', precipitation=u'/LTM5-Nea...........-T0000A5P_EC00_ENS') ] #note: the MetStationConfig can be constructed from yaml-config gis_location_repository = GisLocationService( ) # this provides the gis locations for my stations smg_ts_repository = SmGTsRepository( PROD, FC_PROD) # this provide the read function for my time-series n_ensembles = 51 ens_station_list = [ EnsembleStation( 598, n_ensembles, temperature_ens=lambda i: u'/LTM5-Nea...........-T0017A3P_EC00_E{0:02}'.format(i), precipitation_ens=lambda i: u'/LTM5-Nea...........-T0000A5P_EC00_E{0:02}'.format(i), wind_speed_ens=None, radiation_ens=None, relative_humidity_ens=None), EnsembleStation( 574, n_ensembles, temperature_ens=lambda i: u'/LTM5-Tya...........-T0017A3P_EC00_E{0:02}'.format(i), precipitation_ens=lambda i: u'/LTM5-Tya...........-T0000A5P_EC00_E{0:02}'.format(i), wind_speed_ens=None, radiation_ens=None, relative_humidity_ens=None) ] ens_config = EnsembleConfig(n_ensembles, ens_station_list) geo_ts_repository = GeoTsRepository( epsg_id=32633, geo_location_repository=gis_location_repository, ts_repository=smg_ts_repository, met_station_list=met_stations, ens_config=ens_config) #pass service info and met_stations self.assertIsNotNone(geo_ts_repository) utc_period = UtcPeriod(utc.time(YMDhms(2015, 10, 1, 0, 0, 0)), utc.time(YMDhms(2015, 10, 10, 0, 0, 0))) ts_types = ['temperature', 'precipitation'] ens_geo_ts_dict = geo_ts_repository.get_forecast_ensemble( ts_types, utc_period=utc_period, t_c=None, geo_location_criteria=None) self.assertIsNotNone(ens_geo_ts_dict) self.assertEqual(ens_config.n_ensembles, len(ens_geo_ts_dict)) for i in range(ens_config.n_ensembles): for ts_type in ts_types: self.assertTrue( ts_type in ens_geo_ts_dict[i].keys(), "we ecpect to find an entry for each requested type (it could be empty list though" ) self.assertTrue( len(ens_geo_ts_dict[i][ts_type]) > 0, "we expect to find the series that we pass in, given they have not changed the name in SmG PROD" )