Commit d1c44c51ee5a6d366acecb225cb0d8752aeeace8

Authored by Goutte
1 parent 952e3d8f

Enable Earth

Enable more precise configuration of each models' parameters
@@ -5,13 +5,22 @@ @@ -5,13 +5,22 @@
5 - [ ] Add a README to the download tarball 5 - [ ] Add a README to the download tarball
6 - [ ] Set the log level to _error_ in production (see `web/run.py`) 6 - [ ] Set the log level to _error_ in production (see `web/run.py`)
7 - [ ] CRON statements to call the cache cleanup and warmup 7 - [ ] CRON statements to call the cache cleanup and warmup
8 -- [ ] Cache warmup (generate for today's default interval) `/cache/warmup`  
9 - [ ] Give the future data another color 8 - [ ] Give the future data another color
10 9
11 An heliospheric propagation 1D MHD model for solar wind prediction at planets, probes and comets. 10 An heliospheric propagation 1D MHD model for solar wind prediction at planets, probes and comets.
12 11
13 12
14 13
  14 +## 1.0.0-rc5
  15 +
  16 +- [ ] Rework the images of Rosetta and Juno
  17 +- [ ] Cache warmup (generate for today's default interval) `/cache/warmup`
  18 +- [ ] Improve the generated CDF for SAMP
  19 +- [ ] Enable p67
  20 +- [x] Enable Earth
  21 +- [x] Enable more precise configuration of each models' parameters
  22 +
  23 +
15 ## 1.0.0-rc4 24 ## 1.0.0-rc4
16 25
17 - [x] Sort times series by closeness to the sun 26 - [x] Sort times series by closeness to the sun
@@ -24,6 +24,7 @@ It also gathers NetCDF data from AMDA, and serves it as CSV to the plotter. @@ -24,6 +24,7 @@ It also gathers NetCDF data from AMDA, and serves it as CSV to the plotter.
24 - `config.yml` : the main configuration file. 24 - `config.yml` : the main configuration file.
25 - `web/run.py` : the front controller, holding most of the code. 25 - `web/run.py` : the front controller, holding most of the code.
26 - `web/view/home.html.jinja2` : the HTML template. 26 - `web/view/home.html.jinja2` : the HTML template.
  27 +- `web/static/js/swapp.ls` : most of the javascript client-side.
27 28
28 29
29 ## Install 30 ## Install
@@ -98,15 +98,17 @@ targets: @@ -98,15 +98,17 @@ targets:
98 - type: 'planet' 98 - type: 'planet'
99 slug: 'earth' 99 slug: 'earth'
100 name: 'Earth' 100 name: 'Earth'
101 - title: 'Earth (coming soon)' 101 + title: 'Earth'
102 orbit: 102 orbit:
103 - models:  
104 - - slug: 'earth_orb_all'  
105 - semimajor: 0  
106 - semiminor: 0 103 + models: [] # Earth has no orbit models, we hard-coded it to (1, 0)
107 models: 104 models:
108 - - slug: 'tao_earth_sw'  
109 - locked: true 105 + - slug: 'omni_hour_all'
  106 + - slug: 'ace_swepam_real'
  107 + parameters:
  108 + dens: 'Dens'
  109 + vtot: 'Vel'
  110 + temp: 'Temp'
  111 + locked: false
110 default: true 112 default: true
111 - type: 'planet' 113 - type: 'planet'
112 slug: 'mars' 114 slug: 'mars'
@@ -159,6 +161,8 @@ targets: @@ -159,6 +161,8 @@ targets:
159 stopped_at: '2014-08-02T00:00:00' 161 stopped_at: '2014-08-02T00:00:00'
160 - slug: 'p67_orb_all' 162 - slug: 'p67_orb_all'
161 started_at: '2014-08-02T00:00:00' 163 started_at: '2014-08-02T00:00:00'
  164 + parameters:
  165 + hee: 'XYZ_HEE'
162 models: 166 models:
163 - slug: 'tao_ros_sw' 167 - slug: 'tao_ros_sw'
164 locked: false 168 locked: false
@@ -184,6 +188,8 @@ targets: @@ -184,6 +188,8 @@ targets:
184 orbit: 188 orbit:
185 models: 189 models:
186 - slug: 'p67_orb_all' 190 - slug: 'p67_orb_all'
  191 + parameters:
  192 + hee: 'XYZ_HEE'
187 locked: true 193 locked: true
188 default: false 194 default: false
189 195
@@ -74,7 +74,7 @@ CACHE_DIR = get_path('../cache') @@ -74,7 +74,7 @@ CACHE_DIR = get_path('../cache')
74 # innermost loop of `get_data_for_target`. 74 # innermost loop of `get_data_for_target`.
75 # The javascript knows the targets' properties under these names. 75 # The javascript knows the targets' properties under these names.
76 PROPERTIES = ('time', 'vrad', 'vtan', 'vtot', 'btan', 'temp', 'pdyn', 'dens', 76 PROPERTIES = ('time', 'vrad', 'vtan', 'vtot', 'btan', 'temp', 'pdyn', 'dens',
77 - 'angl', 'xhee', 'yhee') 77 + 'atse', 'xhee', 'yhee')
78 78
79 # The parameters that the users can handle. 79 # The parameters that the users can handle.
80 # The slug MUST be one of the properties above. 80 # The slug MUST be one of the properties above.
@@ -119,8 +119,8 @@ PARAMETERS = { @@ -119,8 +119,8 @@ PARAMETERS = {
119 'active': False, 119 'active': False,
120 'position': 50, 120 'position': 50,
121 }, 121 },
122 - 'angl': {  
123 - 'slug': 'angl', 122 + 'atse': {
  123 + 'slug': 'atse',
124 'name': 'Angle T-S-E', 124 'name': 'Angle T-S-E',
125 'title': 'Angle Target-Sun-Earth.', 125 'title': 'Angle Target-Sun-Earth.',
126 'units': 'deg', 126 'units': 'deg',
@@ -426,9 +426,10 @@ def get_data_for_target(target_config, started_at, stopped_at): @@ -426,9 +426,10 @@ def get_data_for_target(target_config, started_at, stopped_at):
426 % (target_config['slug'], str(e))) 426 % (target_config['slug'], str(e)))
427 try: 427 try:
428 orbits = target_config['orbit']['models'] 428 orbits = target_config['orbit']['models']
429 - except Exception as e:  
430 - abort(500, "Invalid orbit configuration for '%s' : %s"  
431 - % (target_config['slug'], str(e))) 429 + except KeyError as e:
  430 + orbits = []
  431 + # abort(500, "Invalid orbit configuration for '%s' : %s"
  432 + # % (target_config['slug'], str(e)))
432 433
433 def _sta_sto(_cnf, _sta, _sto): 434 def _sta_sto(_cnf, _sta, _sto):
434 if 'started_at' in _cnf: 435 if 'started_at' in _cnf:
@@ -443,12 +444,36 @@ def get_data_for_target(target_config, started_at, stopped_at): @@ -443,12 +444,36 @@ def get_data_for_target(target_config, started_at, stopped_at):
443 _s1 = _sto 444 _s1 = _sto
444 return _s0, _s1 445 return _s0, _s1
445 446
  447 + def _read_var(_nc, _keys, _key, mandatory=False):
  448 + try:
  449 + return _nc.variables[_keys[_key]]
  450 + except KeyError:
  451 + pass
  452 + if mandatory:
  453 + raise Exception("No variable '%s' found in NetCDF." % _keys[_key])
  454 + return [None] * len(_nc.variables['Time']) # slow -- use numpy!
  455 +
  456 + # Override them using the configuration, maybe also put these in config ?
  457 + default_nc_keys = {
  458 + 'hee': 'HEE',
  459 + 'vtot': 'V',
  460 + 'magn': 'B',
  461 + 'temp': 'T',
  462 + 'dens': 'N',
  463 + 'pdyn': 'P_dyn',
  464 + 'atse': 'Delta_angle',
  465 + }
  466 +
446 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish 467 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish
447 orbit_data = {} # keys are datetime as str, values arrays of XY 468 orbit_data = {} # keys are datetime as str, values arrays of XY
448 469
449 for orbit in orbits: 470 for orbit in orbits:
450 s0, s1 = _sta_sto(orbit, started_at, stopped_at) 471 s0, s1 = _sta_sto(orbit, started_at, stopped_at)
451 472
  473 + nc_keys = default_nc_keys.copy()
  474 + if 'parameters' in orbit:
  475 + nc_keys.update(orbit['parameters'])
  476 +
452 orbit_files = retrieve_amda_netcdf( 477 orbit_files = retrieve_amda_netcdf(
453 target_config['slug'], orbit['slug'], s0, s1 478 target_config['slug'], orbit['slug'], s0, s1
454 ) 479 )
@@ -457,19 +482,16 @@ def get_data_for_target(target_config, started_at, stopped_at): @@ -457,19 +482,16 @@ def get_data_for_target(target_config, started_at, stopped_at):
457 (target_config['name'], orbit_file)) 482 (target_config['name'], orbit_file))
458 cdf_handle = Dataset(orbit_file, "r", format="NETCDF4") 483 cdf_handle = Dataset(orbit_file, "r", format="NETCDF4")
459 times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms 484 times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms
460 - try:  
461 - data_hee = cdf_handle.variables['HEE']  
462 - except KeyError:  
463 - data_hee = cdf_handle.variables['XYZ_HEE'] # p67 uses this 485 + data_hee = _read_var(cdf_handle, nc_keys, 'hee', mandatory=True)
464 486
465 log.debug("%s: aggregating data from '%s'..." % 487 log.debug("%s: aggregating data from '%s'..." %
466 (target_config['name'], orbit_file)) 488 (target_config['name'], orbit_file))
467 - for time, datum_hee in zip(times, data_hee): 489 + for ltime, datum_hee in zip(times, data_hee):
468 try: 490 try:
469 - dtime = datetime_from_list(time)  
470 - except Exception as e:  
471 - log.error("Failed to parse time from %s." % time)  
472 - raise e 491 + dtime = datetime_from_list(ltime)
  492 + except Exception:
  493 + log.error("Failed to parse time from %s." % ltime)
  494 + raise
473 if s0 <= dtime <= s1: 495 if s0 <= dtime <= s1:
474 dkey = round_time(dtime, 60*60).strftime(precision) 496 dkey = round_time(dtime, 60*60).strftime(precision)
475 orbit_data[dkey] = datum_hee 497 orbit_data[dkey] = datum_hee
@@ -481,40 +503,66 @@ def get_data_for_target(target_config, started_at, stopped_at): @@ -481,40 +503,66 @@ def get_data_for_target(target_config, started_at, stopped_at):
481 model_files = retrieve_amda_netcdf( 503 model_files = retrieve_amda_netcdf(
482 target_config['slug'], model['slug'], s0, s1 504 target_config['slug'], model['slug'], s0, s1
483 ) 505 )
  506 + nc_keys = default_nc_keys.copy()
  507 + if 'parameters' in model:
  508 + nc_keys.update(model['parameters'])
  509 +
484 for model_file in model_files: 510 for model_file in model_files:
485 - # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn  
486 log.debug("%s: opening model NETCDF4 '%s'..." % 511 log.debug("%s: opening model NETCDF4 '%s'..." %
487 (target_config['name'], model_file)) 512 (target_config['name'], model_file))
488 cdf_handle = Dataset(model_file, "r", format="NETCDF4") 513 cdf_handle = Dataset(model_file, "r", format="NETCDF4")
  514 + # log.debug(cdf_handle.variables.keys())
489 times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms 515 times = cdf_handle.variables['Time'] # YYYY DOY HH MM SS .ms
490 - data_v = cdf_handle.variables['V']  
491 - data_b = cdf_handle.variables['B']  
492 - data_t = cdf_handle.variables['T']  
493 - data_n = cdf_handle.variables['N']  
494 - data_p = cdf_handle.variables['P_dyn']  
495 - data_d = cdf_handle.variables['Delta_angle'] 516 + data_v = _read_var(cdf_handle, nc_keys, 'vtot')
  517 + data_b = _read_var(cdf_handle, nc_keys, 'magn')
  518 + data_t = _read_var(cdf_handle, nc_keys, 'temp')
  519 + data_n = _read_var(cdf_handle, nc_keys, 'dens')
  520 + data_p = _read_var(cdf_handle, nc_keys, 'pdyn')
  521 + data_a = _read_var(cdf_handle, nc_keys, 'atse')
  522 +
  523 + # Usually:
  524 + # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn
  525 + # Earth:
  526 + # Time, BartelsNumber, ImfID, SwID, ImfPoints,
  527 + # SwPoints, B_M_av, B_Vec_av, B_Theta_av,
  528 + # B_Phi_av, B, T, N, V, Vlat, Vlon,
  529 + # Alpha, RamP, E, Beta, Ma, Kp, R, DST,
  530 + # AE, Flux, Flag, F10_Index, StartTime, StopTime
  531 +
496 log.debug("%s: aggregating data from '%s'..." % 532 log.debug("%s: aggregating data from '%s'..." %
497 (target_config['name'], model_file)) 533 (target_config['name'], model_file))
498 - for time, datum_v, datum_b, datum_t, datum_n, datum_p, datum_d \  
499 - in zip(times, data_v, data_b, data_t, data_n, data_p, data_d):  
500 - vrad = datum_v[0]  
501 - vtan = datum_v[1] 534 + for ltime, datum_v, datum_b, datum_t, datum_n, datum_p, datum_a \
  535 + in zip(times, data_v, data_b, data_t, data_n, data_p, data_a):
  536 +
502 try: 537 try:
503 - dtime = datetime_from_list(time)  
504 - except Exception as e:  
505 - log.error("Failed to parse time from %s." % time)  
506 - raise e 538 + dtime = datetime_from_list(ltime)
  539 + except Exception:
  540 + log.error("Failed to parse time from %s." % ltime)
  541 + raise
507 if s0 <= dtime <= s1: 542 if s0 <= dtime <= s1:
508 dkey = round_time(dtime, 60*60).strftime(precision) 543 dkey = round_time(dtime, 60*60).strftime(precision)
  544 +
  545 + if hasattr(datum_v, '__len__'):
  546 + vrad = datum_v[0]
  547 + vtan = datum_v[1]
  548 + vtot = sqrt(vrad * vrad + vtan * vtan)
  549 + else: # eg: Earth
  550 + vrad = None
  551 + vtan = None
  552 + vtot = datum_v
  553 +
509 x_hee = None 554 x_hee = None
510 y_hee = None 555 y_hee = None
  556 + if target_config['slug'] == 'earth':
  557 + x_hee = 1
  558 + y_hee = 0
511 if dkey in orbit_data: 559 if dkey in orbit_data:
512 x_hee = orbit_data[dkey][0] 560 x_hee = orbit_data[dkey][0]
513 y_hee = orbit_data[dkey][1] 561 y_hee = orbit_data[dkey][1]
514 all_data[dkey] = ( 562 all_data[dkey] = (
515 dtime.strftime("%Y-%m-%dT%H:%M:%S+00:00"), 563 dtime.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
516 - vrad, vtan, sqrt(vrad * vrad + vtan * vtan),  
517 - datum_b, datum_t, datum_n, datum_p, datum_d, 564 + vrad, vtan, vtot,
  565 + datum_b, datum_t, datum_n, datum_p, datum_a,
518 x_hee, y_hee 566 x_hee, y_hee
519 ) 567 )
520 cdf_handle.close() 568 cdf_handle.close()
@@ -565,7 +613,12 @@ def generate_csv_file_if_needed(target_slug, started_at, stopped_at): @@ -565,7 +613,12 @@ def generate_csv_file_if_needed(target_slug, started_at, stopped_at):
565 stopped_at=stopped_at)) 613 stopped_at=stopped_at))
566 log.info("Generation of '%s' done." % filename) 614 log.info("Generation of '%s' done." % filename)
567 except Exception as e: 615 except Exception as e:
  616 + from sys import exc_info
  617 + from traceback import extract_tb
  618 + exc_type, exc_value, exc_traceback = exc_info()
568 log.error(e) 619 log.error(e)
  620 + for trace in extract_tb(exc_traceback):
  621 + log.error(trace)
569 if isfile(local_csv_file): 622 if isfile(local_csv_file):
570 log.warn("Removing failed CSV '%s'..." % local_csv_file) 623 log.warn("Removing failed CSV '%s'..." % local_csv_file)
571 removefile(local_csv_file) 624 removefile(local_csv_file)
@@ -875,9 +928,9 @@ def download_targets_netcdf(targets, params, started_at, stopped_at): @@ -875,9 +928,9 @@ def download_targets_netcdf(targets, params, started_at, stopped_at):
875 nc_y[:] = values_y 928 nc_y[:] = values_y
876 log.debug("Writing NetCDF '%s'..." % nc_filename) 929 log.debug("Writing NetCDF '%s'..." % nc_filename)
877 930
878 - except Exception as e: 931 + except Exception:
879 log.error("Failed to generate NetCDF '%s'." % nc_filename) 932 log.error("Failed to generate NetCDF '%s'." % nc_filename)
880 - raise e 933 + raise
881 finally: 934 finally:
882 nc_handle.close() 935 nc_handle.close()
883 936
web/static/js/swapp.js
@@ -199,12 +199,15 @@ @@ -199,12 +199,15 @@
199 var dtime; 199 var dtime;
200 dtime = timeFormat(d['time']); 200 dtime = timeFormat(d['time']);
201 configuration['parameters'].forEach(function(parameter){ 201 configuration['parameters'].forEach(function(parameter){
202 - var id; 202 + var id, val;
203 id = parameter['id']; 203 id = parameter['id'];
204 - return data[id].push({  
205 - x: dtime,  
206 - y: parseFloat(d[id])  
207 - }); 204 + val = parseFloat(d[id]);
  205 + if (!isNaN(val)) {
  206 + return data[id].push({
  207 + x: dtime,
  208 + y: val
  209 + });
  210 + }
208 }); 211 });
209 if (d['xhee'] && d['yhee']) { 212 if (d['xhee'] && d['yhee']) {
210 return data['hee'].push({ 213 return data['hee'].push({
@@ -299,10 +302,13 @@ @@ -299,10 +302,13 @@
299 if (!(id in data)) { 302 if (!(id in data)) {
300 console.error("No data for id '" + id + "'.", data); 303 console.error("No data for id '" + id + "'.", data);
301 } 304 }
302 - return this$.time_series.push(new TimeSeries(id, title, target, data[id], this$.parameters[id].active, container, {  
303 - 'started_at': this$.started_at,  
304 - 'stopped_at': this$.stopped_at  
305 - })); 305 + console.log(target['name'], id, data[id]);
  306 + if (data[id].length) {
  307 + return this$.time_series.push(new TimeSeries(id, title, target, data[id], this$.parameters[id].active, container, {
  308 + 'started_at': this$.started_at,
  309 + 'stopped_at': this$.stopped_at
  310 + }));
  311 + }
306 }); 312 });
307 this.time_series.forEach(function(ts){ 313 this.time_series.forEach(function(ts){
308 ts.options['onMouseOver'] = function(){ 314 ts.options['onMouseOver'] = function(){
web/static/js/swapp.ls
@@ -169,7 +169,9 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE @@ -169,7 +169,9 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
169 dtime = timeFormat(d['time']) 169 dtime = timeFormat(d['time'])
170 configuration['parameters'].forEach((parameter) -> 170 configuration['parameters'].forEach((parameter) ->
171 id = parameter['id'] 171 id = parameter['id']
172 - data[id].push({x: dtime, y: parseFloat(d[id])}) 172 + val = parseFloat(d[id])
  173 + if not isNaN(val)
  174 + data[id].push({x: dtime, y: val})
173 ) 175 )
174 if d['xhee'] and d['yhee'] 176 if d['xhee'] and d['yhee']
175 data['hee'].push({ 177 data['hee'].push({
@@ -250,12 +252,14 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE @@ -250,12 +252,14 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
250 container = @configuration['time_series_container'] 252 container = @configuration['time_series_container']
251 id = parameter['id'] ; title = parameter['title'] 253 id = parameter['id'] ; title = parameter['title']
252 if id not of data then console.error("No data for id '#{id}'.", data) 254 if id not of data then console.error("No data for id '#{id}'.", data)
253 - @time_series.push(new TimeSeries(  
254 - id, title, target, data[id], @parameters[id].active, container, {  
255 - 'started_at': @started_at,  
256 - 'stopped_at': @stopped_at,  
257 - }  
258 - )) 255 + console.log(target['name'], id, data[id])
  256 + if data[id].length
  257 + @time_series.push(new TimeSeries(
  258 + id, title, target, data[id], @parameters[id].active, container, {
  259 + 'started_at': @started_at,
  260 + 'stopped_at': @stopped_at,
  261 + }
  262 + ))
259 ) 263 )
260 @time_series.forEach((ts) ~> # returning true may be faster 264 @time_series.forEach((ts) ~> # returning true may be faster
261 ts.options['onMouseOver'] = ~> 265 ts.options['onMouseOver'] = ~>
web/view/home.html.jinja2
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 <div class="target {{ target.slug }} {{ 'locked' if target.locked else 'active' }}" 11 <div class="target {{ target.slug }} {{ 'locked' if target.locked else 'active' }}"
12 data-target-slug="{{ target.slug }}"> 12 data-target-slug="{{ target.slug }}">
13 <img width="64px" height="64px" 13 <img width="64px" height="64px"
14 - src="{{ static('img/target/'~target.slug~'_128.png') }}" 14 + src="{{ static('img/target/'~target.slug~'_256.png') }}"
15 title="{{ target.title }}" 15 title="{{ target.title }}"
16 alt="{{ target.name }}"> 16 alt="{{ target.name }}">
17 <img width="64px" height="64px" 17 <img width="64px" height="64px"