|
6 | 6 | import numpy as np |
7 | 7 | import matplotlib.pyplot as plt |
8 | 8 | import datetime as dt |
9 | | -import os, sys, json, re |
| 9 | +import os, sys, json, re, shutil |
10 | 10 | import multiprocessing as mp |
11 | 11 |
|
12 | 12 | from copy import copy |
@@ -497,7 +497,8 @@ class OptimizationResult: |
497 | 497 | def __init__(self, working_dir, stage): |
498 | 498 | self.working_dir = working_dir |
499 | 499 | self.stage = stage |
500 | | - self.metadata_json_paths = self._get_optr_jsons(working_dir, stage) |
| 500 | + self.metadata_json_paths = self._get_optr_jsons(working_dir, stage) |
| 501 | + self.total_sims = self._count_total_sims() |
501 | 502 | self.statvar_name = self._get_statvar_name(stage) |
502 | 503 | self.measured = self._get_measured(stage) |
503 | 504 | self.input_dir = self._get_input_dir(stage) |
@@ -511,6 +512,15 @@ def __init__(self, working_dir, stage): |
511 | 512 | params with their corresponding output sims.""".format(stage)) |
512 | 513 | print('\nThis optimization stage used the\ |
513 | 514 | following input parameter files:\n{}'.format('\n'.join(self.input_params))) |
| 515 | + |
| 516 | + def _count_total_sims(self): |
| 517 | + # total number of simulations of given stage in working directory |
| 518 | + tracked_dirs = [] |
| 519 | + for f in self.metadata_json_paths[self.stage]: |
| 520 | + with open(f) as fh: |
| 521 | + json_data = json.load(fh) |
| 522 | + tracked_dirs.extend(json_data.get('sim_dirs')) |
| 523 | + return len(tracked_dirs) |
514 | 524 |
|
515 | 525 | def _get_optr_jsons(self, work_dir, stage): |
516 | 526 | """ |
@@ -591,7 +601,7 @@ def _get_statvar_name(self, stage): |
591 | 601 |
|
592 | 602 | return var_name |
593 | 603 |
|
594 | | - def result_table(self, freq='daily', top_n=5, latex=False): |
| 604 | + def result_table(self, freq='daily', top_n=5, latex=False): |
595 | 605 | ##TODO: add stats for freq options annual (means or sum) |
596 | 606 |
|
597 | 607 | sim_dirs = self._get_sim_dirs(self.stage) |
@@ -619,8 +629,12 @@ def result_table(self, freq='daily', top_n=5, latex=False): |
619 | 629 | orig_mo = orig_statvar.groupby(orig_statvar.index.month).mean() |
620 | 630 |
|
621 | 631 | for i, sim in enumerate(sim_dirs): |
622 | | - sim_out = load_statvar(OPJ(sim, 'outputs', 'statvar.dat'))\ |
| 632 | + try: |
| 633 | + sim_out = load_statvar(OPJ(sim, 'outputs', 'statvar.dat'))\ |
623 | 634 | ['{}'.format(statvar_name)] |
| 635 | + except: # simulation might have been removed or missing |
| 636 | + continue |
| 637 | + |
624 | 638 | sim_out = sim_out[idx] |
625 | 639 | if freq == 'daily': |
626 | 640 | result_df.loc[sim_names[i]] = [\ |
@@ -668,9 +682,9 @@ def get_top_ranked_sims(self, sorted_df): |
668 | 682 | 'params_adjusted' : [] |
669 | 683 | } |
670 | 684 |
|
671 | | - json_paths = self.metadata_json_paths[self.stage] |
| 685 | + json_paths = self.metadata_json_paths[self.stage] |
672 | 686 |
|
673 | | - for i,el in enumerate(sorted_df.index): |
| 687 | + for el in sorted_df.drop('orig_params').index: |
674 | 688 | ret['dir_name'].append(el) |
675 | 689 | ret['param_path'].append(OPJ(self.working_dir,el,'inputs',\ |
676 | 690 | 'parameters')) |
@@ -685,4 +699,94 @@ def get_top_ranked_sims(self, sorted_df): |
685 | 699 |
|
686 | 700 | return ret |
687 | 701 |
|
| 702 | + def archive(self, remove_sims=True, metric_freq='daily'): |
| 703 | + """ |
| 704 | + Create archive directory to hold json files that contain |
| 705 | + information of adjusted parameters, model output, and performance |
| 706 | + metrics for each Optimizer simulation of the |
| 707 | + OptimizationResult.stage in the OptimizationResult.working_dir. |
| 708 | + |
| 709 | + Arguments: |
| 710 | + remove_sims (bool) : If True recursively delete all folders |
| 711 | + and files associated with original simulations of the |
| 712 | + OptimizationResult.stage in the |
| 713 | + OptimizationResult.working_dir, if False do not delete |
| 714 | + simulations. |
| 715 | + metric_freq (Str) : Frequency of output metric computation |
| 716 | + for recording of model performance. Can be 'daily' |
| 717 | + (default) or 'monthly'. Note, new results can be computed |
| 718 | + later with archived results. |
| 719 | + Returns: |
| 720 | + None |
| 721 | + """ |
| 722 | + # create output archive directory |
| 723 | + archive_dir = OPJ(self.working_dir,"{}_archived".format(self.stage)) |
| 724 | + if not os.path.isdir(archive_dir): |
| 725 | + os.mkdir(archive_dir) |
688 | 726 |
|
| 727 | + # create table and used to make mapping dic |
| 728 | + table = self.result_table(freq=metric_freq,\ |
| 729 | + top_n=self.total_sims, latex=False) |
| 730 | + map_dic = self.get_top_ranked_sims(table) |
| 731 | + |
| 732 | + # get measured optimization variable path |
| 733 | + first_json = self.metadata_json_paths[self.stage][0] |
| 734 | + |
| 735 | + with open(first_json) as json_file: |
| 736 | + json_data = json.load(json_file) |
| 737 | + measured_path = json_data.get('measured') |
| 738 | + |
| 739 | + # record info for each simulation and archive to JSONs |
| 740 | + # pandas series and numpy arrays are converted to Python lists |
| 741 | + # for JSON serialization |
| 742 | + for i, sim in enumerate(map_dic.get('dir_name')): |
| 743 | + json_path = OPJ(archive_dir, '{sim}.json'.format(sim=sim)) |
| 744 | + try: |
| 745 | + output_series = load_statvar(OPJ(self.working_dir, sim,\ |
| 746 | + 'outputs', 'statvar.dat'))\ |
| 747 | + [self.statvar_name] |
| 748 | + except: # simulation directory was already removed |
| 749 | + continue |
| 750 | + json_data = { |
| 751 | + 'param_names' : [], |
| 752 | + 'param_values' : [], |
| 753 | + 'original_param_path' : self.input_params, |
| 754 | + 'measured_path' : measured_path, |
| 755 | + 'output_name' : self.statvar_name, |
| 756 | + 'output_date_index' : output_series.\ |
| 757 | + index.astype(str).tolist(), |
| 758 | + 'output_values' : output_series.values.tolist(), |
| 759 | + 'metric_freq' : metric_freq, |
| 760 | + 'NSE' : table.loc[sim, 'NSE'], |
| 761 | + 'RMSE' : table.loc[sim, 'RMSE'], |
| 762 | + 'PBIAS' : table.loc[sim, 'PBIAS'], |
| 763 | + 'COEF_DET' : table.loc[sim, 'COEF_DET'] |
| 764 | + } |
| 765 | + |
| 766 | + for param in map_dic.get('params_adjusted')[i]: |
| 767 | + json_data['param_names'].append(param) |
| 768 | + json_data['param_values'].append(Parameters(\ |
| 769 | + map_dic.get('param_path')[i])[param]\ |
| 770 | + .tolist()) |
| 771 | + # save JSON file into archive directory |
| 772 | + with open(json_path, 'w') as outf: |
| 773 | + json.dump(json_data, outf, sort_keys = True, indent = 4,\ |
| 774 | + ensure_ascii = False) |
| 775 | + |
| 776 | + # recursively delete all simulation directories after archiving |
| 777 | + if remove_sims: |
| 778 | + path = OPJ(self.working_dir, sim) |
| 779 | + for dirpath, dirnames, filenames in os.walk(path,\ |
| 780 | + topdown=False): |
| 781 | + shutil.rmtree(dirpath, ignore_errors=True) |
| 782 | + else: |
| 783 | + continue |
| 784 | + |
| 785 | + # after archiving all simulations delete the original JSON metadata |
| 786 | + for meta_file in self.metadata_json_paths[self.stage]: |
| 787 | + try: |
| 788 | + os.remove(meta_file) |
| 789 | + except: |
| 790 | + continue |
| 791 | + |
| 792 | + |