metro_model.py 16.7 KB
Newer Older
1
2
# METRo : Model of the Environment and Temperature of Roads
# METRo is Free and is proudly provided by the Government of Canada
Miguel Tremblay's avatar
Miguel Tremblay committed
3
# Copyright (C) Her Majesty The Queen in Right of Canada, Environment Canada, 2006
Sasa Zhang's avatar
Sasa Zhang committed
4
#
5
#  Questions or bugs report: metro@ec.gc.ca
6
#  METRo repository: https://framagit.org/metroprojects/metro
7
#  Documentation: https://framagit.org/metroprojects/metro/wikis/home
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#
# Code contributed by:
#  Miguel Tremblay - Canadian meteorological center
#  Francois Fortin - Canadian meteorological center
#
#  $LastChangedDate$
#  $LastChangedRevision$
########################################################################
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


Sasa Zhang's avatar
Sasa Zhang committed
31
from executable_module.metro_module import Metro_module
32
import time
33
import numpy
34
35
36
37
38
39
40
41
42
import metro_logger
import metro_config
from toolbox import metro_util
from toolbox import metro_date
from toolbox import metro_constant
from data_module import metro_data_collection_output
from data_module import metro_data
from data_module import metro_infdata

Sasa Zhang's avatar
Sasa Zhang committed
43

44
45
46
47
48
_ = metro_util.init_translation('metro_model')

# To call the C function.
from model import macadam

Sasa Zhang's avatar
Sasa Zhang committed
49

50
51
class Metro_model(Metro_module):

Sasa Zhang's avatar
Sasa Zhang committed
52
    def get_receive_type(self):
53
54
        return Metro_module.DATATYPE_DATA_IN

Sasa Zhang's avatar
Sasa Zhang committed
55
    def get_send_type(self):
56
57
        return Metro_module.DATATYPE_DATA_OUT

Sasa Zhang's avatar
Sasa Zhang committed
58
    def start(self):
59
60
61
62
63
64
65
66
        Metro_module.start(self)
        pForecast = self.get_infdata_reference('FORECAST')
        forecast_data = pForecast.get_data_collection()
        pObservation = self.get_infdata_reference('OBSERVATION')
        observation_data = pObservation.get_data_collection()
        pStation = self.get_infdata_reference('STATION')
        station_data = pStation.get_data()

Sasa Zhang's avatar
Sasa Zhang committed
67
68
69
        if not metro_config.get_value('T_BYPASS_CORE'):
            self.__send_data_to_metro_core(forecast_data, observation_data, station_data)
            roadcast_data = self.__create_roadcast_collection(forecast_data, observation_data, station_data)
70
71
        else:
            roadcast_data = None
Sasa Zhang's avatar
Sasa Zhang committed
72
            metro_logger.print_message(metro_logger.LOGGER_MSG_INFORMATIVE, _("Bypassing METRo core, roadcast not created."))
73
74
75
76
77
78

        pForecast.set_data_collection(forecast_data)
        pObservation.set_data_collection(observation_data)
        pStation.set_data(station_data)

        # creer et ajouter infdata
Miguel Tremblay's avatar
Miguel Tremblay committed
79
        # Creation and adding infdata
Sasa Zhang's avatar
Sasa Zhang committed
80
        infdata_roadcast = metro_infdata.Metro_infdata('ROADCAST', metro_infdata.DATATYPE_METRO_DATA_COLLECTION)
81
82
        infdata_roadcast.set_data_collection(roadcast_data)
        self.add_infdata(infdata_roadcast)
Sasa Zhang's avatar
Sasa Zhang committed
83
84

    def stop(self):
85
86
        Metro_module.stop(self)

Sasa Zhang's avatar
Sasa Zhang committed
87
    def __get_nb_timesteps(self, forecast):
88
        wf_data = forecast.get_interpolated_data()
89
90
        npFT = wf_data.get_matrix_col('Time')
        return len(npFT)
91

Sasa Zhang's avatar
Sasa Zhang committed
92
    def __get_observation_lenght(self, observation):
93
94
        obs_data = observation.get_interpolated_data()
        lTime_obs = obs_data.get_matrix_col('Time').tolist()
Sasa Zhang's avatar
Sasa Zhang committed
95
        return len(lTime_obs)
96

Sasa Zhang's avatar
Sasa Zhang committed
97
98
    def __get_observation_delta_t(self, observation):
        return observation.get_attribute('DELTA_T')
99

Sasa Zhang's avatar
Sasa Zhang committed
100
    def __send_data_to_metro_core(self, forecast, observation, station):
101
102
103
104
        wf_data = forecast.get_interpolated_data()
        ro_data = observation.get_interpolated_data()
        cs_data = station

105
106
        # Start time from model is the last observation
        sStart_time = metro_config.get_value('DATA_ATTRIBUTE_LAST_OBSERVATION')
107
108
109
        fStart_time = metro_date.parse_date_string(sStart_time)

        iModel_start_y = metro_date.get_year(fStart_time)
Sasa Zhang's avatar
Sasa Zhang committed
110
111
        sMessage = _("year: [%s]") % iModel_start_y
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
112
113

        iModel_start_m = metro_date.get_month(fStart_time)
Sasa Zhang's avatar
Sasa Zhang committed
114
115
        sMessage = _("month: [%s]") % iModel_start_m
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
116
117

        iModel_start_d = metro_date.get_day(fStart_time)
Sasa Zhang's avatar
Sasa Zhang committed
118
119
        sMessage = _("day: [%s]") % iModel_start_d
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
120
121

        iModel_start_h = metro_date.get_hour(fStart_time)
Sasa Zhang's avatar
Sasa Zhang committed
122
123
        sMessage = _("hour: [%s]") % iModel_start_h
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
124
125
126

        dStation_header = cs_data.get_header()

Miguel Tremblay's avatar
Miguel Tremblay committed
127
        # test observation
128
        dObservation_header = ro_data.get_header()
Sasa Zhang's avatar
Sasa Zhang committed
129
130
        lObservation_data = ro_data.get_matrix()

131
        sMessage = "Observation_header=" + str(dObservation_header)
Sasa Zhang's avatar
Sasa Zhang committed
132
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
133
        sMessage = "Observation_data=" + str(lObservation_data)
Sasa Zhang's avatar
Sasa Zhang committed
134
135
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)

Miguel Tremblay's avatar
Miguel Tremblay committed
136
        # test forecast
137
        dForecast_header = wf_data.get_header()
Sasa Zhang's avatar
Sasa Zhang committed
138
        lForecast_data = wf_data.get_matrix()
139
140

        sMessage = "Forecast_header=" + str(dForecast_header)
Sasa Zhang's avatar
Sasa Zhang committed
141
142
143
144
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
        sMessage = "Forecast_data=" + str(lForecast_data)
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)

Miguel Tremblay's avatar
Miguel Tremblay committed
145
        # start roadlayer MATRIX
Sasa Zhang's avatar
Sasa Zhang committed
146
        npLayerType = cs_data.get_matrix_col('TYPE')
147
        lLayerType = npLayerType.astype(numpy.int32).tolist()
148
149
150
        lLayerThick = cs_data.get_matrix_col('THICKNESS').tolist()
        nNbrOfLayer = len(lLayerType)

Sasa Zhang's avatar
Sasa Zhang committed
151
152
        sMessage = _("Number of layer=") + str(nNbrOfLayer)
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
153
154
155
156
157
158

        # Append an empty box for the manuel mode
        lLayerType.append(0)
        lLayerThick.append(0.0)

        sMessage = _("roadlayer type=") + str(lLayerType)
Sasa Zhang's avatar
Sasa Zhang committed
159
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
160
        sMessage = _("roadlayer thick=") + str(lLayerThick)
Sasa Zhang's avatar
Sasa Zhang committed
161
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
162

Sasa Zhang's avatar
Sasa Zhang committed
163
164
        # end roadlayer MATRIX
        fTime = dStation_header['PRODUCTION_DATE']
165
        fTimeForecast = dForecast_header['PRODUCTION_DATE']
Sasa Zhang's avatar
Sasa Zhang committed
166

167
        fIssue_time = fStart_time
Sasa Zhang's avatar
Sasa Zhang committed
168

169
        sMessage = _("issue time=") + time.ctime(fIssue_time)
Sasa Zhang's avatar
Sasa Zhang committed
170
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
171
172
173
174
175
176

        wforiginal = forecast.get_original_data()

        ft = wforiginal.get_matrix_col('FORECAST_TIME')

        # Forecast
Sasa Zhang's avatar
Sasa Zhang committed
177
        # Get the interpolated values.
Sasa Zhang's avatar
Sasa Zhang committed
178
179
        wf_interpolated_data = forecast.get_interpolated_data()
        npAT = wf_interpolated_data.get_matrix_col('AT')
180
        lAT = npAT.tolist()
181
        lQP = wf_interpolated_data.get_matrix_col('QP').tolist()
182
183
        npWS = wf_interpolated_data.get_matrix_col('WS')
        lWS = npWS.tolist()
Sasa Zhang's avatar
Sasa Zhang committed
184
        npTD = wf_interpolated_data.get_matrix_col('TD')
185
        lTD = npTD.tolist()
186
187
188
        lAP = wf_interpolated_data.get_matrix_col('AP').tolist()
        lSF = wf_interpolated_data.get_matrix_col('SF').tolist()
        lIR = wf_interpolated_data.get_matrix_col('IR').tolist()
189
190
        npFA = wf_interpolated_data.get_matrix_col('FA')
        lFA = npFA.tolist()
191
192
        lPI = wf_interpolated_data.get_matrix_col('PI').astype(numpy.int32).tolist()
        lSC = wf_interpolated_data.get_matrix_col('SC').astype(numpy.int32).tolist()
193
194

        # Number of 30 seconds step.
195
        npFT = wf_interpolated_data.get_matrix_col('Time')
196
197
198
        nNbrTimeSteps = self.__get_nb_timesteps(forecast)
        lAH = wf_interpolated_data.get_matrix_col('AH').tolist()

Sasa Zhang's avatar
Sasa Zhang committed
199
        # Observation data
200
201
        ro_interpolated_data = observation.get_interpolated_data()
        lAT_obs = ro_interpolated_data.get_matrix_col('AT').tolist()
Sasa Zhang's avatar
Sasa Zhang committed
202
203
        lST_obs = ro_interpolated_data.get_matrix_col('ST').tolist()
        lSST_obs = ro_interpolated_data.get_matrix_col('SST').tolist()
204
        lTime_obs = ro_interpolated_data.get_matrix_col('Time').tolist()
205
206
        # Deep soil value given in command line
        bDeepTemp = metro_config.get_value('DEEP_SOIL_TEMP')
Sasa Zhang's avatar
Sasa Zhang committed
207
        dDeepTemp = float(metro_config.get_value('DEEP_SOIL_TEMP_VALUE'))
208

Sasa Zhang's avatar
Sasa Zhang committed
209
        nLenObservation = self.__get_observation_lenght(observation)
210
        fDeltaTMetroObservation = self.__get_observation_delta_t(observation)
Sasa Zhang's avatar
Sasa Zhang committed
211

212
        # Concatenate the information to send it to C.
Sasa Zhang's avatar
Sasa Zhang committed
213
214
215
216
217
        npSWO1 = observation.get_attribute('SST_VALID_INTERPOLATED').astype(numpy.int32)
        npSWO2 = observation.get_attribute('AT_VALID_INTERPOLATED').astype(numpy.int32)
        npSWO3 = observation.get_attribute('TD_VALID_INTERPOLATED').astype(numpy.int32)
        npSWO4 = observation.get_attribute('WS_VALID_INTERPOLATED').astype(numpy.int32)
        npSWO = numpy.zeros(4 * metro_constant.nNL)
218
        # Put all the arrays in one for the fortran code.
Sasa Zhang's avatar
Sasa Zhang committed
219
220
221
222
223
        for i in range(0, len(npSWO1)):
            npSWO[4 * i] = npSWO1[i]
            npSWO[4 * i + 1] = npSWO2[i]
            npSWO[4 * i + 2] = npSWO3[i]
            npSWO[4 * i + 3] = npSWO4[i]
224
        lSWO = npSWO.astype(numpy.int32).tolist()
Sasa Zhang's avatar
Sasa Zhang committed
225

226
        bNoObs = observation.get_attribute('NO_OBS')
Sasa Zhang's avatar
Sasa Zhang committed
227
228
229

        sMessage = _("------------station config START---------------------")
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
230
231
232
233

        tLatlon = dStation_header['COORDINATE']
        fLat = tLatlon[0]
        fLon = tLatlon[1]
Sasa Zhang's avatar
Sasa Zhang committed
234
235
        sMessage = _("lat,lon: ") + "(" + str(fLat) + ", " + str(fLon) + ")"
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
236
237

        bFlat = cs_data.get_station_type()
238
239
        dSstDepth = cs_data.get_sst_depth()

Sasa Zhang's avatar
Sasa Zhang committed
240
241
        sMessage = _("SST sensor depth: ") + str(dSstDepth)
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
242

243
        sMessage = _("------------station config END---------------------")
Sasa Zhang's avatar
Sasa Zhang committed
244
        metro_logger.print_message(metro_logger.LOGGER_MSG_DEBUG, sMessage)
245
        bSilent = not metro_config.get_value('INIT_LOGGER_SHELL_DISPLAY')
Sasa Zhang's avatar
Sasa Zhang committed
246
        metro_logger.print_message(metro_logger.LOGGER_MSG_EXECPRIMARY, _("Start sending data to METRo core"))
247
        bEchec = []
248

Sasa Zhang's avatar
Sasa Zhang committed
249
250
251
        macadam.Do_Metro(bFlat, fLat, fLon, lLayerThick, nNbrOfLayer, lLayerType, lAT, lQP, lWS, lAP, lSF, lIR, lFA,
                         lPI, lSC, lAT_obs, lST_obs, lSST_obs, lAH, lTime_obs, lSWO, bNoObs, fDeltaTMetroObservation,
                         nLenObservation, nNbrTimeSteps, bSilent, dSstDepth, bDeepTemp, dDeepTemp)
252
        bEchec = (macadam.get_echec())[0]
Sasa Zhang's avatar
Sasa Zhang committed
253
        # Check if the execution of the model was a success:
254
        if bEchec != 0:
Sasa Zhang's avatar
Sasa Zhang committed
255
256
            sError_message = _("Fatal error in METRo physical model.")
            metro_logger.print_message(metro_logger.LOGGER_MSG_STOP, sError_message)
257
        else:
Sasa Zhang's avatar
Sasa Zhang committed
258
            metro_logger.print_message(metro_logger.LOGGER_MSG_EXECPRIMARY, _("End of METRo core"))
259

Sasa Zhang's avatar
Sasa Zhang committed
260
    def __create_roadcast_collection(self, forecast, observation, station):
Miguel Tremblay's avatar
Miguel Tremblay committed
261
262

        # Creation of the Metro_data object for the roadcast
Sasa Zhang's avatar
Sasa Zhang committed
263
264
        lStandard_items = metro_config.get_value('XML_ROADCAST_PREDICTION_STANDARD_ITEMS')
        lExtended_items = metro_config.get_value('XML_ROADCAST_PREDICTION_EXTENDED_ITEMS')
Sasa Zhang's avatar
Sasa Zhang committed
265
        lItems = lStandard_items
266

Sasa Zhang's avatar
Sasa Zhang committed
267
        # FFTODO append all lExtended_items to metrodata
268
        roadcast = metro_data.Metro_data(lItems)
Sasa Zhang's avatar
Sasa Zhang committed
269

Miguel Tremblay's avatar
Miguel Tremblay committed
270
        # Extraction of forecast data used to create the roadcasts.
271
272
        wf_data = forecast.get_interpolated_data()

Sasa Zhang's avatar
Sasa Zhang committed
273
274
275
        # Generate the header of roadcast
        # extraction of information
        sRoadcast_version = metro_config.get_value('FILE_ROADCAST_CURRENT_VERSION')
276
277
278
        sRoadcast_station = station.get_station_name()
        fRoadcast_production_date = time.time()

Sasa Zhang's avatar
Sasa Zhang committed
279
        # Generate the roadcast matrix of data
Miguel Tremblay's avatar
Miguel Tremblay committed
280
        # Extraction of data used by metro_core in the computation
281
282
283
284
        iObservation_len = self.__get_observation_lenght(observation)
        fObservation_delta_t = self.__get_observation_delta_t(observation)
        iNb_timesteps = self.__get_nb_timesteps(forecast)

Miguel Tremblay's avatar
Miguel Tremblay committed
285
        # Extraction of roadcast data computed by metro_core
286
287
288
289
290
291
292
293
294
295
296
        lRA = (macadam.get_ra())[:iNb_timesteps]
        lSN = (macadam.get_sn())[:iNb_timesteps]
        lRC = (macadam.get_rc())[:iNb_timesteps]
        lST = (macadam.get_rt())[:iNb_timesteps]
        lFV = (macadam.get_fv())[:iNb_timesteps]
        lSF = (macadam.get_sf())[:iNb_timesteps]
        lIR = (macadam.get_ir())[:iNb_timesteps]
        lFC = (macadam.get_fc())[:iNb_timesteps]
        lFG = (macadam.get_g())[:iNb_timesteps]
        lBB = (macadam.get_bb())[:iNb_timesteps]
        lFP = (macadam.get_fp())[:iNb_timesteps]
Sasa Zhang's avatar
Sasa Zhang committed
297
        lSST = (macadam.get_sst())[:iNb_timesteps]
298

Sasa Zhang's avatar
Sasa Zhang committed
299
        if metro_config.get_value('TL'):
300
301
302
            # Temperature of levels under the ground.
            nNbrVerticalLevel = macadam.get_nbr_levels()
            lDepth = (macadam.get_depth())[:nNbrVerticalLevel]
Sasa Zhang's avatar
Sasa Zhang committed
303
304
305
            lTmpTL = (macadam.get_lt())[:nNbrVerticalLevel * iNb_timesteps]
            lTL = []
            for i in range(0, iNb_timesteps):
306
307
308
                begin = i * nNbrVerticalLevel
                end = begin + nNbrVerticalLevel
                lTL.append(lTmpTL[begin:end])
Sasa Zhang's avatar
Sasa Zhang committed
309
310
311
312
313
314
315
316
317
318
319

        # Adding the information to the header
        roadcast.set_header_value('VERSION', sRoadcast_version)
        roadcast.set_header_value('ROAD_STATION', sRoadcast_station)
        roadcast.set_header_value('PRODUCTION_DATE', fRoadcast_production_date)
        roadcast.set_header_value('LATITUDE', station.get_header()['COORDINATE'][0])
        roadcast.set_header_value('LONGITUDE', station.get_header()['COORDINATE'][1])
        roadcast.set_header_value('FILETYPE', 'roadcast')

        if metro_config.get_value('TL'):
            roadcast.set_header_value('VERTICAL_LEVELS', lDepth)
320
321
322
323
324

        # TODO MT: Le +30 est la pour que l'output soit au bon moment.
        #  Il y a eu un probleme dans la conversion entre le C et le fortran
        #  qui fait en sorte qu'il y a un decalage d'un indice.  Il faudra que
        #  ce soit corrige.
325
326
327
328
        npRT = wf_data.get_matrix_col('FORECAST_TIME')[:iNb_timesteps]
        npRT = npRT + 30
        npHH = wf_data.get_matrix_col('Time')[:iNb_timesteps]
        npAT = wf_data.get_matrix_col('AT')[:iNb_timesteps]
329
        npFA = wf_data.get_matrix_col('FA')[:iNb_timesteps]
330
        # 3.6 is to convert from m/s to km/h
Sasa Zhang's avatar
Sasa Zhang committed
331
        npWS = wf_data.get_matrix_col('WS')[:iNb_timesteps] * 3.6
332
333
334
335
        npTD = wf_data.get_matrix_col('TD')[:iNb_timesteps]
        npQP_SN = wf_data.get_matrix_col('SN')[:iNb_timesteps]
        npQP_RA = wf_data.get_matrix_col('RA')[:iNb_timesteps]
        npCC = wf_data.get_matrix_col('CC')[:iNb_timesteps]
336

337
        roadcast.init_matrix(iNb_timesteps, roadcast.get_real_nb_matrix_col())
338

Miguel Tremblay's avatar
Miguel Tremblay committed
339
        # Data added to the roadcast matrix
340
341
342
343
        roadcast.set_matrix_col('RA', lRA)
        roadcast.set_matrix_col('SN', lSN)
        roadcast.set_matrix_col('RC', lRC)
        roadcast.set_matrix_col('ST', lST)
344
345
346
347
348
349
350
        roadcast.set_matrix_col('ROADCAST_TIME', npRT)
        roadcast.set_matrix_col('HH', npHH)
        roadcast.set_matrix_col('AT', npAT)
        roadcast.set_matrix_col('WS', npWS)
        roadcast.set_matrix_col('TD', npTD)
        roadcast.set_matrix_col('QP-SN', npQP_SN)
        roadcast.set_matrix_col('QP-RA', npQP_RA)
351
352
353
354
355
        roadcast.set_matrix_col('IR', lIR)
        roadcast.set_matrix_col('SF', lSF)
        roadcast.set_matrix_col('FV', lFV)
        roadcast.set_matrix_col('FC', lFC)
        roadcast.set_matrix_col('FG', lFG)
356
        roadcast.set_matrix_col('FA', npFA.tolist())
357
358
        roadcast.set_matrix_col('BB', lBB)
        roadcast.set_matrix_col('FP', lFP)
359
        roadcast.set_matrix_col('CC', npCC)
360
        roadcast.set_matrix_col('SST', lSST)
361

Sasa Zhang's avatar
Sasa Zhang committed
362
        if metro_config.get_value('TL'):
363
364
            roadcast.append_matrix_multiCol('TL', lTL)

Miguel Tremblay's avatar
Miguel Tremblay committed
365
        # Creation of the object Metro_data_collection for the roadcast
Sasa Zhang's avatar
Sasa Zhang committed
366
367
        lStandard_attributes = metro_config.get_value('DATA_ATTRIBUTE_ROADCAST_STANDARD')
        lExtended_attributes = metro_config.get_value('DATA_ATTRIBUTE_ROADCAST_EXTENDED')
368
        lAttributes = lStandard_attributes + lExtended_attributes
Sasa Zhang's avatar
Sasa Zhang committed
369
        roadcast_collection = metro_data_collection_output.Metro_data_collection_output(roadcast, lAttributes)
370

Miguel Tremblay's avatar
Miguel Tremblay committed
371
        # Writing of data needed for the roadcast.
Sasa Zhang's avatar
Sasa Zhang committed
372
373
374
        roadcast_collection.set_attribute('OBSERVATION_LENGTH', iObservation_len)
        roadcast_collection.set_attribute('OBSERVATION_DELTAT_T', fObservation_delta_t)
        roadcast_collection.set_attribute('FORECAST_NB_TIMESTEPS', iNb_timesteps)
375
        return roadcast_collection