Commit d1c44c51ee5a6d366acecb225cb0d8752aeeace8

Authored by Goutte
1 parent 952e3d8f

Enable Earth

Enable more precise configuration of each models' parameters
CHANGELOG.md
... ... @@ -5,13 +5,22 @@
5 5 - [ ] Add a README to the download tarball
6 6 - [ ] Set the log level to _error_ in production (see `web/run.py`)
7 7 - [ ] CRON statements to call the cache cleanup and warmup
8   -- [ ] Cache warmup (generate for today's default interval) `/cache/warmup`
9 8 - [ ] Give the future data another color
10 9  
11 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 24 ## 1.0.0-rc4
16 25  
17 26 - [x] Sort times series by closeness to the sun
... ...
README.md
... ... @@ -24,6 +24,7 @@ It also gathers NetCDF data from AMDA, and serves it as CSV to the plotter.
24 24 - `config.yml` : the main configuration file.
25 25 - `web/run.py` : the front controller, holding most of the code.
26 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 30 ## Install
... ...
config.yml
... ... @@ -98,15 +98,17 @@ targets:
98 98 - type: 'planet'
99 99 slug: 'earth'
100 100 name: 'Earth'
101   - title: 'Earth (coming soon)'
  101 + title: 'Earth'
102 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 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 112 default: true
111 113 - type: 'planet'
112 114 slug: 'mars'
... ... @@ -159,6 +161,8 @@ targets:
159 161 stopped_at: '2014-08-02T00:00:00'
160 162 - slug: 'p67_orb_all'
161 163 started_at: '2014-08-02T00:00:00'
  164 + parameters:
  165 + hee: 'XYZ_HEE'
162 166 models:
163 167 - slug: 'tao_ros_sw'
164 168 locked: false
... ... @@ -184,6 +188,8 @@ targets:
184 188 orbit:
185 189 models:
186 190 - slug: 'p67_orb_all'
  191 + parameters:
  192 + hee: 'XYZ_HEE'
187 193 locked: true
188 194 default: false
189 195  
... ...
web/run.py
... ... @@ -74,7 +74,7 @@ CACHE_DIR = get_path('../cache')
74 74 # innermost loop of `get_data_for_target`.
75 75 # The javascript knows the targets' properties under these names.
76 76 PROPERTIES = ('time', 'vrad', 'vtan', 'vtot', 'btan', 'temp', 'pdyn', 'dens',
77   - 'angl', 'xhee', 'yhee')
  77 + 'atse', 'xhee', 'yhee')
78 78  
79 79 # The parameters that the users can handle.
80 80 # The slug MUST be one of the properties above.
... ... @@ -119,8 +119,8 @@ PARAMETERS = {
119 119 'active': False,
120 120 'position': 50,
121 121 },
122   - 'angl': {
123   - 'slug': 'angl',
  122 + 'atse': {
  123 + 'slug': 'atse',
124 124 'name': 'Angle T-S-E',
125 125 'title': 'Angle Target-Sun-Earth.',
126 126 'units': 'deg',
... ... @@ -426,9 +426,10 @@ def get_data_for_target(target_config, started_at, stopped_at):
426 426 % (target_config['slug'], str(e)))
427 427 try:
428 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 434 def _sta_sto(_cnf, _sta, _sto):
434 435 if 'started_at' in _cnf:
... ... @@ -443,12 +444,36 @@ def get_data_for_target(target_config, started_at, stopped_at):
443 444 _s1 = _sto
444 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 467 precision = "%Y-%m-%dT%H" # model and orbits times are only equal-ish
447 468 orbit_data = {} # keys are datetime as str, values arrays of XY
448 469  
449 470 for orbit in orbits:
450 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 477 orbit_files = retrieve_amda_netcdf(
453 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 482 (target_config['name'], orbit_file))
458 483 cdf_handle = Dataset(orbit_file, "r", format="NETCDF4")
459 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 487 log.debug("%s: aggregating data from '%s'..." %
466 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 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 495 if s0 <= dtime <= s1:
474 496 dkey = round_time(dtime, 60*60).strftime(precision)
475 497 orbit_data[dkey] = datum_hee
... ... @@ -481,40 +503,66 @@ def get_data_for_target(target_config, started_at, stopped_at):
481 503 model_files = retrieve_amda_netcdf(
482 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 510 for model_file in model_files:
485   - # Time, StartTime, StopTime, V, B, N, T, Delta_angle, P_dyn
486 511 log.debug("%s: opening model NETCDF4 '%s'..." %
487 512 (target_config['name'], model_file))
488 513 cdf_handle = Dataset(model_file, "r", format="NETCDF4")
  514 + # log.debug(cdf_handle.variables.keys())
489 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 532 log.debug("%s: aggregating data from '%s'..." %
497 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 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 542 if s0 <= dtime <= s1:
508 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 554 x_hee = None
510 555 y_hee = None
  556 + if target_config['slug'] == 'earth':
  557 + x_hee = 1
  558 + y_hee = 0
511 559 if dkey in orbit_data:
512 560 x_hee = orbit_data[dkey][0]
513 561 y_hee = orbit_data[dkey][1]
514 562 all_data[dkey] = (
515 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 566 x_hee, y_hee
519 567 )
520 568 cdf_handle.close()
... ... @@ -565,7 +613,12 @@ def generate_csv_file_if_needed(target_slug, started_at, stopped_at):
565 613 stopped_at=stopped_at))
566 614 log.info("Generation of '%s' done." % filename)
567 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 619 log.error(e)
  620 + for trace in extract_tb(exc_traceback):
  621 + log.error(trace)
569 622 if isfile(local_csv_file):
570 623 log.warn("Removing failed CSV '%s'..." % local_csv_file)
571 624 removefile(local_csv_file)
... ... @@ -875,9 +928,9 @@ def download_targets_netcdf(targets, params, started_at, stopped_at):
875 928 nc_y[:] = values_y
876 929 log.debug("Writing NetCDF '%s'..." % nc_filename)
877 930  
878   - except Exception as e:
  931 + except Exception:
879 932 log.error("Failed to generate NetCDF '%s'." % nc_filename)
880   - raise e
  933 + raise
881 934 finally:
882 935 nc_handle.close()
883 936  
... ...
web/static/js/swapp.js
... ... @@ -199,12 +199,15 @@
199 199 var dtime;
200 200 dtime = timeFormat(d['time']);
201 201 configuration['parameters'].forEach(function(parameter){
202   - var id;
  202 + var id, val;
203 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 212 if (d['xhee'] && d['yhee']) {
210 213 return data['hee'].push({
... ... @@ -299,10 +302,13 @@
299 302 if (!(id in data)) {
300 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 313 this.time_series.forEach(function(ts){
308 314 ts.options['onMouseOver'] = function(){
... ...
web/static/js/swapp.ls
... ... @@ -169,7 +169,9 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
169 169 dtime = timeFormat(d['time'])
170 170 configuration['parameters'].forEach((parameter) ->
171 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 176 if d['xhee'] and d['yhee']
175 177 data['hee'].push({
... ... @@ -250,12 +252,14 @@ https://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE
250 252 container = @configuration['time_series_container']
251 253 id = parameter['id'] ; title = parameter['title']
252 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 264 @time_series.forEach((ts) ~> # returning true may be faster
261 265 ts.options['onMouseOver'] = ~>
... ...
web/view/home.html.jinja2
... ... @@ -11,7 +11,7 @@
11 11 <div class="target {{ target.slug }} {{ 'locked' if target.locked else 'active' }}"
12 12 data-target-slug="{{ target.slug }}">
13 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 15 title="{{ target.title }}"
16 16 alt="{{ target.name }}">
17 17 <img width="64px" height="64px"
... ...