Example #1
0
    def __init__(self, cf, method='morris'):
        """Initialization."""
        self.method = method
        # 1. SEIMS model related
        self.model = ParseSEIMSConfig(cf)
        # 2. Common settings of parameters sensitivity analysis
        if 'PSA_Settings' not in cf.sections():
            raise ValueError(
                "[PSA_Settings] section MUST be existed in *.ini file.")

        self.evaluate_params = list()
        if cf.has_option('PSA_Settings', 'evaluateparam'):
            eva_str = cf.get('PSA_Settings', 'evaluateparam')
            self.evaluate_params = StringClass.split_string(eva_str, ',')
        else:
            self.evaluate_params = ['Q']  # Default

        self.param_range_def = 'morris_param_rng.def'  # Default
        if cf.has_option('PSA_Settings', 'paramrngdef'):
            self.param_range_def = cf.get('PSA_Settings', 'paramrngdef')
        self.param_range_def = self.model.model_dir + os.path.sep + self.param_range_def
        if not FileClass.is_file_exists(self.param_range_def):
            raise IOError('Ranges of parameters MUST be provided!')

        if not (cf.has_option('PSA_Settings', 'psa_time_start')
                and cf.has_option('PSA_Settings', 'psa_time_end')):
            raise ValueError(
                "Start and end time of PSA MUST be specified in [PSA_Settings]."
            )
        try:
            # UTCTIME
            tstart = cf.get('PSA_Settings', 'psa_time_start')
            tend = cf.get('PSA_Settings', 'psa_time_end')
            self.psa_stime = StringClass.get_datetime(tstart)
            self.psa_etime = StringClass.get_datetime(tend)
        except ValueError:
            raise ValueError('The time format MUST be"YYYY-MM-DD HH:MM:SS".')
        if self.psa_stime >= self.psa_etime:
            raise ValueError("Wrong time settings in [PSA_Settings]!")

        # 3. Parameters settings for specific sensitivity analysis methods
        self.morris = None
        self.fast = None
        if self.method == 'fast':
            self.fast = FASTConfig(cf)
            self.psa_outpath = '%s/PSA_FAST_N%dM%d' % (
                self.model.model_dir, self.fast.N, self.fast.M)
        elif self.method == 'morris':
            self.morris = MorrisConfig(cf)
            self.psa_outpath = '%s/PSA_Morris_N%dL%d' % (
                self.model.model_dir, self.morris.N, self.morris.num_levels)
        # 4. (Optional) Plot settings for matplotlib
        self.plot_cfg = PlotConfig(cf)

        # Do not remove psa_outpath if already existed
        UtilClass.mkdir(self.psa_outpath)
        self.outfiles = PSAOutputs(self.psa_outpath)
Example #2
0
    def __init__(self, cf, method='morris'):
        """Initialization."""
        self.method = method
        # 1. SEIMS model related
        self.model = ParseSEIMSConfig(cf)
        # 2. Common settings of parameters sensitivity analysis
        if 'PSA_Settings' not in cf.sections():
            raise ValueError("[PSA_Settings] section MUST be existed in *.ini file.")

        self.evaluate_params = list()
        if cf.has_option('PSA_Settings', 'evaluateparam'):
            eva_str = cf.get('PSA_Settings', 'evaluateparam')
            self.evaluate_params = StringClass.split_string(eva_str, ',')
        else:
            self.evaluate_params = ['Q']  # Default

        self.param_range_def = 'morris_param_rng.def'  # Default
        if cf.has_option('PSA_Settings', 'paramrngdef'):
            self.param_range_def = cf.get('PSA_Settings', 'paramrngdef')
        self.param_range_def = self.model.model_dir + os.path.sep + self.param_range_def
        if not FileClass.is_file_exists(self.param_range_def):
            raise IOError('Ranges of parameters MUST be provided!')

        if not (cf.has_option('PSA_Settings', 'psa_time_start') and
                cf.has_option('PSA_Settings', 'psa_time_end')):
            raise ValueError("Start and end time of PSA MUST be specified in [PSA_Settings].")
        try:
            # UTCTIME
            tstart = cf.get('PSA_Settings', 'psa_time_start')
            tend = cf.get('PSA_Settings', 'psa_time_end')
            self.psa_stime = StringClass.get_datetime(tstart)
            self.psa_etime = StringClass.get_datetime(tend)
        except ValueError:
            raise ValueError('The time format MUST be"YYYY-MM-DD HH:MM:SS".')
        if self.psa_stime >= self.psa_etime:
            raise ValueError("Wrong time settings in [PSA_Settings]!")

        # 3. Parameters settings for specific sensitivity analysis methods
        self.morris = None
        self.fast = None
        if self.method == 'fast':
            self.fast = FASTConfig(cf)
            self.psa_outpath = '%s/PSA-FAST-N%dM%d' % (self.model.model_dir,
                                                       self.fast.N, self.fast.M)
        elif self.method == 'morris':
            self.morris = MorrisConfig(cf)
            self.psa_outpath = '%s/PSA-Morris-N%dL%dJ%d' % (self.model.model_dir,
                                                            self.morris.N,
                                                            self.morris.num_levels,
                                                            self.morris.grid_jump)

        # Do not remove psa_outpath if already existed
        UtilClass.mkdir(self.psa_outpath)
        self.outfiles = PSAOutputs(self.psa_outpath)
 def SimulationPeriod(self):
     if self._stime is not None and self._etime is not None:
         return self._stime, self._etime
     st = self.filein_tab.find_one(
         {ModelCfgFields.tag: ModelCfgFields.stime})[ModelCfgFields.value]
     et = self.filein_tab.find_one(
         {ModelCfgFields.tag: ModelCfgFields.etime})[ModelCfgFields.value]
     st = StringClass.get_datetime(st)
     et = StringClass.get_datetime(et)
     if self._stime is None or st > self._stime:
         self._stime = st
     if self._etime is None or et < self._etime:
         self._etime = et
     if st > self._etime > self._stime:
         self._stime = st
         self._etime = et
     return self._stime, self._etime
Example #4
0
    def __init__(self, cf, method='nsga2'):
        """Initialization."""
        # 1. SEIMS model related
        self.model = ParseSEIMSConfig(cf)

        # 2. Common settings of auto-calibration
        if 'CALI_Settings' not in cf.sections():
            raise ValueError(
                "[CALI_Settings] section MUST be existed in *.ini file.")
        self.param_range_def = 'cali_param_rng.def'
        if cf.has_option('CALI_Settings', 'paramrngdef'):
            self.param_range_def = cf.get('CALI_Settings', 'paramrngdef')
        self.param_range_def = self.model.model_dir + os.path.sep + self.param_range_def
        if not FileClass.is_file_exists(self.param_range_def):
            raise IOError('Ranges of parameters MUST be provided!')

        if not (cf.has_option('CALI_Settings', 'cali_time_start')
                and cf.has_option('CALI_Settings', 'cali_time_end')):
            raise ValueError("Start and end time of Calibration "
                             "MUST be specified in [CALI_Settings].")
        try:  # UTCTIME
            tstart = cf.get('CALI_Settings', 'cali_time_start')
            tend = cf.get('CALI_Settings', 'cali_time_end')
            self.cali_stime = StringClass.get_datetime(tstart)
            self.cali_etime = StringClass.get_datetime(tend)
            self.calc_validation = False
            if cf.has_option('CALI_Settings', 'vali_time_start') and \
                cf.has_option('CALI_Settings', 'vali_time_end'):
                tstart = cf.get('CALI_Settings', 'vali_time_start')
                tend = cf.get('CALI_Settings', 'vali_time_end')
                self.vali_stime = StringClass.get_datetime(tstart)
                self.vali_etime = StringClass.get_datetime(tend)
                self.calc_validation = True
        except ValueError:
            raise ValueError(
                'The time format MUST be "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".'
            )
        if self.cali_stime >= self.cali_etime or (
                self.calc_validation and self.vali_stime >= self.vali_etime):
            raise ValueError("Wrong time setted in [CALI_Settings]!")

        # 3. Parameters settings for specific optimization algorithm
        self.opt_mtd = method
        self.opt = None
        if self.opt_mtd == 'nsga2':
            self.opt = ParseNSGA2Config(cf, self.model.model_dir)
Example #5
0
 def read_simulation_timerange(self):
     """Read simulation time range from MongoDB."""
     client = ConnectMongoDB(self.hostname, self.port)
     conn = client.get_conn()
     db = conn[self.main_db]
     collection = db['FILE_IN']
     try:
         stime_str = collection.find_one({'TAG': 'STARTTIME'}, no_cursor_timeout=True)['VALUE']
         etime_str = collection.find_one({'TAG': 'ENDTIME'}, no_cursor_timeout=True)['VALUE']
         stime = StringClass.get_datetime(stime_str)
         etime = StringClass.get_datetime(etime_str)
         dlt = etime - stime + timedelta(seconds=1)
         self.timerange = (dlt.days * 86400. + dlt.seconds) / 86400. / 365.
     except NetworkTimeout or Exception:
         # In case of unexpected raise
         self.timerange = 1.  # set default
         pass
     client.close()
Example #6
0
    def __init__(self, cf, method='nsga2'):
        """Initialization."""
        # 1. SEIMS model related
        self.model = ParseSEIMSConfig(cf)

        # 2. Common settings of auto-calibration
        if 'CALI_Settings' not in cf.sections():
            raise ValueError("[CALI_Settings] section MUST be existed in *.ini file.")
        self.param_range_def = 'cali_param_rng.def'
        if cf.has_option('CALI_Settings', 'paramrngdef'):
            self.param_range_def = cf.get('CALI_Settings', 'paramrngdef')
        self.param_range_def = self.model.model_dir + os.path.sep + self.param_range_def
        if not FileClass.is_file_exists(self.param_range_def):
            raise IOError('Ranges of parameters MUST be provided!')

        if not (cf.has_option('CALI_Settings', 'cali_time_start') and
                cf.has_option('CALI_Settings', 'cali_time_end')):
            raise ValueError("Start and end time of Calibration "
                             "MUST be specified in [CALI_Settings].")
        try:  # UTCTIME
            tstart = cf.get('CALI_Settings', 'cali_time_start')
            tend = cf.get('CALI_Settings', 'cali_time_end')
            self.cali_stime = StringClass.get_datetime(tstart)
            self.cali_etime = StringClass.get_datetime(tend)
            self.calc_validation = False
            if cf.has_option('CALI_Settings', 'vali_time_start') and \
                cf.has_option('CALI_Settings', 'vali_time_end'):
                tstart = cf.get('CALI_Settings', 'vali_time_start')
                tend = cf.get('CALI_Settings', 'vali_time_end')
                self.vali_stime = StringClass.get_datetime(tstart)
                self.vali_etime = StringClass.get_datetime(tend)
                self.calc_validation = True
        except ValueError:
            raise ValueError('The time format MUST be "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".')
        if self.cali_stime >= self.cali_etime or (self.calc_validation and
                                                  self.vali_stime >= self.vali_etime):
            raise ValueError("Wrong time setted in [CALI_Settings]!")

        # 3. Parameters settings for specific optimization algorithm
        self.opt_mtd = method
        self.opt = None
        if self.opt_mtd == 'nsga2':
            self.opt = ParseNSGA2Config(cf, self.model.model_dir)
Example #7
0
 def read_simulation_timerange(self):
     """Read simulation time range from MongoDB."""
     client = ConnectMongoDB(self.hostname, self.port)
     conn = client.get_conn()
     db = conn[self.main_db]
     collection = db['FILE_IN']
     try:
         stime_str = collection.find_one({'TAG': 'STARTTIME'},
                                         no_cursor_timeout=True)['VALUE']
         etime_str = collection.find_one({'TAG': 'ENDTIME'},
                                         no_cursor_timeout=True)['VALUE']
         stime = StringClass.get_datetime(stime_str)
         etime = StringClass.get_datetime(etime_str)
         dlt = etime - stime + timedelta(seconds=1)
         self.timerange = (dlt.days * 86400. + dlt.seconds) / 86400. / 365.
     except NetworkTimeout or Exception:
         # In case of unexpected raise
         self.timerange = 1.  # set default
         pass
     client.close()
Example #8
0
def parse_datetime_from_ini(cf, section_name, option_name):
    # type: (ConfigParser, AnyStr, AnyStr) -> Optional[datetime]
    """Parse datetime from the `ConfigParser` object."""
    if not check_config_option(cf, section_name, option_name):
        return None
    time_str = cf.get(section_name, option_name)
    try:  # UTCTIME
        return StringClass.get_datetime(time_str)
    except ValueError:
        print('Warning: The time string %s is invalid, which MUST be '
              '"YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".' % time_str)
        return None
Example #9
0
def read_simulation_from_txt(
        ws,  # type: AnyStr
        plot_vars,  # type: List[AnyStr]
        subbsnID,  # type: int
        stime,  # type: datetime
        etime  # type: datetime
):
    # type: (...) -> (List[AnyStr], Dict[datetime, List[float]])
    """
    Read simulation data from text file according to subbasin ID.
    Returns:
        1. Matched variable names, [var1, var2, ...]
        2. Simulation data dict of all plotted variables, with UTCDATETIME.
           {Datetime: [value_of_var1, value_of_var2, ...], ...}
    """
    plot_vars_existed = list()
    sim_data_dict = OrderedDict()
    for i, v in enumerate(plot_vars):
        txtfile = ws + os.path.sep + v + '.txt'
        if not FileClass.is_file_exists(txtfile):
            print('WARNING: Simulation variable file: %s is not existed!' %
                  txtfile)
            continue
        data_items = read_data_items_from_txt(txtfile)
        found = False
        data_available = False
        for item in data_items:
            item_vs = StringClass.split_string(item[0], ' ', elim_empty=True)
            if len(item_vs) == 2:
                if int(item_vs[1]) == subbsnID and not found:
                    found = True
                elif int(item_vs[1]) != subbsnID and found:
                    break
            if not found:
                continue
            if len(item_vs) != 3:
                continue
            date_str = '%s %s' % (item_vs[0], item_vs[1])
            sim_datetime = StringClass.get_datetime(date_str,
                                                    "%Y-%m-%d %H:%M:%S")

            if stime <= sim_datetime <= etime:
                if sim_datetime not in sim_data_dict:
                    sim_data_dict[sim_datetime] = list()
                sim_data_dict[sim_datetime].append(float(item_vs[2]))
                data_available = True
        if data_available:
            plot_vars_existed.append(v)

    print('Read simulation from %s to %s done.' %
          (stime.strftime('%c'), etime.strftime('%c')))
    return plot_vars_existed, sim_data_dict
    def get_utcdatetime_from_field_values(flds, values, tsys, tzone=None):
        """Get datetime from field-value lists.

        Returns:
            utctime
        """
        cur_y = 0
        cur_m = 0
        cur_d = 0
        cur_hh = 0
        cur_mm = 0
        cur_ss = 0
        dt = None
        for i, fld in enumerate(flds):
            if StringClass.string_match(fld, DataValueFields.dt):
                dt = StringClass.get_datetime(values[i])
            elif StringClass.string_match(fld, DataValueFields.y):
                cur_y = int(values[i])
            elif StringClass.string_match(fld, DataValueFields.m):
                cur_m = int(values[i])
            elif StringClass.string_match(fld, DataValueFields.d):
                cur_d = int(values[i])
            elif StringClass.string_match(fld, DataValueFields.hour):
                cur_hh = int(values[i])
            elif StringClass.string_match(fld, DataValueFields.minute):
                cur_mm = int(values[i])
            elif StringClass.string_match(fld, DataValueFields.second):
                cur_ss = int(values[i])
        # Get datetime and utc/local transformation
        if dt is None:  # 'DATETIME' is not existed
            if cur_y < 1900 or cur_m <= 0 and cur_d <= 0:
                raise ValueError("Can not find TIME information from "
                                 "fields: %s" % ' '.join(fld for fld in flds))
            else:
                dt = datetime(cur_y, cur_m, cur_d, cur_hh, cur_mm, cur_ss)
        if not StringClass.string_match(tsys, 'UTCTIME'):
            if tzone is None:
                tzone = time.timezone // -3600  # positive value for EAST
            dt -= timedelta(minutes=tzone * 60)
        return dt
Example #11
0
def main():
    """TEST CODE"""
    lat_station = 31.45
    ssd_txt = r'C:\z_data\zhongTianShe\model_data_swat\climate\ssd_LY.txt'
    sr_txt = r'C:\z_data\zhongTianShe\model_data_swat\climate\sr_LY.txt'
    sr = list()
    f = open(ssd_txt, 'r')
    ssd_items = f.readlines()
    f.close()
    st_str = ssd_items[0].strip()
    st_time = StringClass.get_datetime(st_str)
    for i in range(1, len(ssd_items)):
        ssd_tmp = StringClass.extract_numeric_values_from_string(ssd_items[i])
        time_tmp = st_time + timedelta(days=i - 1)
        sr_tmp = ([round(rs(DateClass.day_of_year(time_tmp), v, lat_station * PI / 180.), 1)]
                  for v in ssd_tmp)
        sr.extend(sr_tmp)
    f = open(sr_txt, 'w')
    f.write(st_str + '\n')
    for sr_tmp in sr:
        f.write(','.join(str(v) for v in sr_tmp) + '\n')
    f.close()
Example #12
0
    def __init__(self, cf):
        """Initialization."""
        # 1. Directories
        self.model_dir = None
        self.scenario_id = -1
        if 'PATH' in cf.sections():
            self.model_dir = cf.get('PATH', 'model_dir')
            self.scenario_id = cf.getint('PATH', 'scenarioid')
            if self.scenario_id < 0:
                self.model_dir = self.model_dir + os.path.sep + 'OUTPUT'
            else:
                self.model_dir = self.model_dir + os.path.sep + 'OUTPUT' + str(
                    self.scenario_id)
        else:
            raise ValueError("[PATH] section MUST be existed in *.ini file.")
        if not FileClass.is_dir_exists(self.model_dir):
            raise ValueError("Please Check Directories defined in [PATH]")

        # 2. MongoDB configuration and database, collation, GridFS names
        self.hostname = '127.0.0.1'  # localhost by default
        self.port = 27017
        self.climate_db = ''
        self.bmp_scenario_db = ''
        self.spatial_db = ''
        if 'MONGODB' in cf.sections():
            self.hostname = cf.get('MONGODB', 'hostname')
            self.port = cf.getint('MONGODB', 'port')
            self.climate_db = cf.get('MONGODB', 'climatedbname')
            self.bmp_scenario_db = cf.get('MONGODB', 'bmpscenariodbname')
            self.spatial_db = cf.get('MONGODB', 'spatialdbname')
        else:
            raise ValueError(
                '[MONGODB] section MUST be existed in *.ini file.')
        if not StringClass.is_valid_ip_addr(self.hostname):
            raise ValueError('HOSTNAME illegal defined in [MONGODB]!')

        # 3. Parameters
        self.plt_subbsnid = -1
        self.plt_vars = list()
        if 'PARAMETERS' in cf.sections():
            self.plt_subbsnid = cf.getint('PARAMETERS', 'plot_subbasinid')
            plt_vars_str = cf.get('PARAMETERS', 'plot_variables')
        else:
            raise ValueError(
                "[PARAMETERS] section MUST be existed in *.ini file.")
        if self.plt_subbsnid < 0:
            raise ValueError(
                "PLOT_SUBBASINID must be greater or equal than 0.")
        if plt_vars_str != '':
            self.plt_vars = StringClass.split_string(plt_vars_str)
        else:
            raise ValueError("PLOT_VARIABLES illegal defined in [PARAMETERS]!")

        # 4. Optional_Parameters
        if 'OPTIONAL_PARAMETERS' in cf.sections():
            tstart = cf.get('OPTIONAL_PARAMETERS', 'time_start')
            tend = cf.get('OPTIONAL_PARAMETERS', 'time_end')
        else:
            raise ValueError(
                "[OPTIONAL_PARAMETERS] section MUST be existed in *.ini file.")
        try:
            # UTCTIME
            self.time_start = StringClass.get_datetime(tstart)
            self.time_end = StringClass.get_datetime(tend)
            if cf.has_option('OPTIONAL_PARAMETERS', 'vali_time_start') and \
                    cf.has_option('OPTIONAL_PARAMETERS', 'vali_time_end'):
                tstart = cf.get('OPTIONAL_PARAMETERS', 'vali_time_start')
                tend = cf.get('OPTIONAL_PARAMETERS', 'vali_time_end')
                self.vali_stime = StringClass.get_datetime(tstart)
                self.vali_etime = StringClass.get_datetime(tend)
            else:
                self.vali_stime = None
                self.vali_etime = None
        except ValueError:
            raise ValueError(
                'The time format MUST be "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".'
            )
        if self.time_start >= self.time_end:
            raise ValueError("Wrong time setted in [OPTIONAL_PARAMETERS]!")
        # 5. Switches
        self.lang_cn = False
        if 'SWITCH' in cf.sections():
            self.lang_cn = cf.getboolean('SWITCH', 'lang_cn')
Example #13
0
    def __init__(
        self,
        bin_dir='',  # type: AnyStr # The directory of SEIMS binary
        model_dir='',  # type: AnyStr # The directory of SEIMS model
        nthread=4,  # type: int # Thread number for OpenMP
        lyrmtd=0,  # type: int # Layering method, can be 0 (UP_DOWN) or 1 (DOWN_UP)
        host='127.0.0.1',  # type: AnyStr # MongoDB host address, default is `localhost`
        port=27017,  # type: int # MongoDB port, default is 27017
        db_name='',  # type: AnyStr  # Main spatial dbname which can diff from dirname
        scenario_id=-1,  # type: int # Scenario ID defined in `<model>_Scenario` database
        calibration_id=-1,  # type: int # Calibration ID used for model auto-calibration
        subbasin_id=0,  # type: int # Subbasin ID, 0 for whole watershed, 9999 for field version
        version='OMP',  # type: AnyStr # SEIMS version, can be `MPI` or `OMP` (default)
        nprocess=1,  # type: int # Process number for MPI
        mpi_bin='',  # type: AnyStr # Full path of MPI executable file, e.g., './mpirun`
        hosts_opt='-f',  # type: AnyStr # Option for assigning hosts,
        # e.g., `-f`, `-hostfile`, `-machine`, `-machinefile`
        hostfile='',  # type: AnyStr # File containing host names,
        # or file mapping process numbers to machines
        simu_stime=None,  # type: Optional[datetime, AnyStr] # Start time of simulation
        simu_etime=None,  # type: Optional[datetime, AnyStr] # End time of simulation
        out_stime=None,  # type: Optional[datetime, AnyStr] # Start time of outputs
        out_etime=None,  # type: Optional[datetime, AnyStr] # End time of outputs
        args_dict=None  # type: Dict[AnyStr, Optional[AnyStr, datetime, int]]
    ):
        # type: (...) -> None
        #  Derived from input arguments
        if args_dict is None:  # Preferred to use 'args_dict' if existed.
            args_dict = dict()
        bin_dir = args_dict['bin_dir'] if 'bin_dir' in args_dict else bin_dir
        model_dir = args_dict[
            'model_dir'] if 'model_dir' in args_dict else model_dir
        self.version = args_dict[
            'version'] if 'version' in args_dict else version
        suffix = '.exe' if sysstr == 'Windows' else ''
        if self.version == 'MPI':
            self.seims_exec = '%s/seims_mpi%s' % (bin_dir, suffix)
        else:
            self.seims_exec = '%s/seims_omp%s' % (bin_dir, suffix)
            if not FileClass.is_file_exists(
                    self.seims_exec):  # If not support OpenMP, use `seims`!
                self.seims_exec = '%s/seims%s' % (bin_dir, suffix)
        self.seims_exec = os.path.abspath(self.seims_exec)
        self.model_dir = os.path.abspath(model_dir)

        self.nthread = args_dict[
            'nthread'] if 'nthread' in args_dict else nthread
        self.lyrmtd = args_dict['lyrmtd'] if 'lyrmtd' in args_dict else lyrmtd
        self.host = args_dict['host'] if 'host' in args_dict else host
        self.port = args_dict['port'] if 'port' in args_dict else port
        self.db_name = args_dict['db_name'] if 'db_name' in args_dict \
            else os.path.split(self.model_dir)[1]
        self.scenario_id = args_dict[
            'scenario_id'] if 'scenario_id' in args_dict else scenario_id
        self.calibration_id = args_dict['calibration_id'] \
            if 'calibration_id' in args_dict else calibration_id
        self.subbasin_id = args_dict[
            'subbasin_id'] if 'subbasin_id' in args_dict else subbasin_id
        self.nprocess = args_dict[
            'nprocess'] if 'nprocess' in args_dict else nprocess
        self.mpi_bin = args_dict[
            'mpi_bin'] if 'mpi_bin' in args_dict else mpi_bin
        self.hosts_opt = args_dict[
            'hosts_opt'] if 'hosts_opt' in args_dict else hosts_opt
        self.hostfile = args_dict[
            'hostfile'] if 'hostfile' in args_dict else hostfile
        self.simu_stime = args_dict[
            'simu_stime'] if 'simu_stime' in args_dict else simu_stime
        self.simu_etime = args_dict[
            'simu_etime'] if 'simu_etime' in args_dict else simu_etime
        self.out_stime = args_dict[
            'out_stime'] if 'out_stime' in args_dict else out_stime
        self.out_etime = args_dict[
            'out_etime'] if 'out_etime' in args_dict else out_etime
        if is_string(
                self.simu_stime) and not isinstance(self.simu_stime, datetime):
            self.simu_stime = StringClass.get_datetime(self.simu_stime)
        if is_string(
                self.simu_etime) and not isinstance(self.simu_etime, datetime):
            self.simu_etime = StringClass.get_datetime(self.simu_etime)
        if is_string(
                self.out_stime) and not isinstance(self.out_stime, datetime):
            self.out_stime = StringClass.get_datetime(self.out_stime)
        if is_string(
                self.out_etime) and not isinstance(self.out_etime, datetime):
            self.out_etime = StringClass.get_datetime(self.out_etime)

        # Concatenate executable command
        self.cmd = self.Command
        self.run_success = False
        self.output_dir = self.OutputDirectory

        # Model data read from MongoDB
        self.outlet_id = -1
        self.subbasin_count = -1
        self.scenario_dbname = ''
        self.start_time = None
        self.end_time = None
        self.output_ids = list()  # type: List[AnyStr]
        self.output_items = dict()  # type: Dict[AnyStr, Union[List[AnyStr]]]

        # Data maybe used after model run
        self.timespan = dict(
        )  # type: Dict[AnyStr, Dict[AnyStr, Union[float, Dict[AnyStr, float]]]]
        self.obs_vars = list(
        )  # type: List[AnyStr]  # Observation types at the outlet
        self.obs_value = dict(
        )  # type: Dict[datetime, List[float]] # Observation value
        self.sim_vars = list(
        )  # type: List[AnyStr]  # Simulation types, part of `obs_vars`
        self.sim_value = dict(
        )  # type: Dict[datetime, List[float]] # Simulation value
        # The format of sim_obs_dict:
        #         {VarName: {'UTCDATETIME': [t1, t2, ..., tn],
        #                    'Obs': [o1, o2, ..., on],
        #                    'Sim': [s1, s2, ..., sn]},
        #         ...
        #         }
        self.sim_obs_dict = dict(
        )  # type: Dict[AnyStr, Dict[AnyStr, Union[float, List[Union[datetime, float]]]]]
        self.runtime = 0.
        self.runlogs = list()  # type: List[AnyStr]

        self.mongoclient = None  # type: Union[MongoClient, None]  # Set to None after use
Example #14
0
def calculate_95ppu(sim_obs_data, sim_data, outdir, gen_num,
                    vali_sim_obs_data=None, vali_sim_data=None,
                    plot_cfg=None  # type: Optional[PlotConfig]
                    ):
    """Calculate 95% prediction uncertainty and plot the hydrographs."""
    if plot_cfg is None:
        plot_cfg = PlotConfig()
    plt.rcParams['xtick.direction'] = 'out'
    plt.rcParams['ytick.direction'] = 'out'
    plt.rcParams['font.family'] = plot_cfg.font_name
    plt.rcParams['timezone'] = 'UTC'
    plt.rcParams['mathtext.fontset'] = 'custom'
    plt.rcParams['mathtext.it'] = 'STIXGeneral:italic'
    plt.rcParams['mathtext.bf'] = 'STIXGeneral:italic:bold'
    if len(sim_data) < 2:
        return
    var_name = sim_obs_data[0]['var_name']
    for idx, var in enumerate(var_name):
        plot_validation = False
        if vali_sim_obs_data and vali_sim_data and var in vali_sim_obs_data[0]['var_name']:
            plot_validation = True
        ylabel_str = var
        if var in ['Q', 'QI', 'QG', 'QS']:
            ylabel_str += ' (m$^3$/s)'
        elif 'CONC' in var.upper():  # Concentrate
            if 'SED' in var.upper():
                ylabel_str += ' (g/L)'
            else:
                ylabel_str += ' (mg/L)'
        elif 'SED' in var.upper():  # amount
            ylabel_str += ' (kg)'
        cali_obs_dates = sim_obs_data[0][var]['UTCDATETIME'][:]
        if is_string(cali_obs_dates[0]):
            cali_obs_dates = [StringClass.get_datetime(s) for s in cali_obs_dates]
        obs_dates = cali_obs_dates[:]
        order = 1  # By default, the calibration period is before the validation period.
        if plot_validation:
            vali_obs_dates = vali_sim_obs_data[0][var]['UTCDATETIME']
            if is_string(vali_obs_dates[0]):
                vali_obs_dates = [StringClass.get_datetime(s) for s in vali_obs_dates]
            if vali_obs_dates[-1] <= cali_obs_dates[0]:
                order = 0
                obs_dates = vali_obs_dates + obs_dates
            else:
                obs_dates += vali_obs_dates
        obs_data = sim_obs_data[0][var]['Obs'][:]
        if plot_validation:
            if order:
                obs_data += vali_sim_obs_data[0][var]['Obs'][:]
            else:
                obs_data = vali_sim_obs_data[0][var]['Obs'][:] + obs_data

        cali_sim_dates = list(sim_data[0].keys())
        if is_string(cali_sim_dates[0]):
            cali_sim_dates = [StringClass.get_datetime(s) for s in cali_sim_dates]
        sim_dates = cali_sim_dates[:]
        if plot_validation:
            vali_sim_dates = list(vali_sim_data[0].keys())
            if is_string(vali_sim_dates[0]):
                vali_sim_dates = [StringClass.get_datetime(s) for s in vali_sim_dates]
            if order:
                sim_dates += vali_sim_dates
            else:
                sim_dates = vali_sim_dates + sim_dates
        sim_data_list = list()
        caliBestIdx = -1
        caliBestNSE = -9999.
        for idx2, ind in enumerate(sim_data):
            tmp = numpy.array(list(ind.values()))
            tmp = tmp[:, idx]
            if sim_obs_data[idx2][var]['NSE'] > caliBestNSE:
                caliBestNSE = sim_obs_data[idx2][var]['NSE']
                caliBestIdx = idx2
            tmpsim = tmp.tolist()
            if plot_validation:
                tmp_data = numpy.array(list(vali_sim_data[idx2].values()))[:, idx].tolist()
                if order:
                    tmpsim += tmp_data
                else:
                    tmpsim = tmp_data + tmpsim
            sim_data_list.append(tmpsim)

        sim_best = numpy.array(list(sim_data[caliBestIdx].values()))[:, idx]
        sim_best = sim_best.tolist()
        if plot_validation:
            tmp_data = numpy.array(list(vali_sim_data[caliBestIdx].values()))[:, idx].tolist()
            if order:
                sim_best += tmp_data
            else:
                sim_best = tmp_data + sim_best
        sim_data_list = numpy.array(sim_data_list)
        ylows = numpy.percentile(sim_data_list, 2.5, 0, interpolation='nearest')
        yups = numpy.percentile(sim_data_list, 97.5, 0, interpolation='nearest')

        def calculate_95ppu_efficiency(obs_data_list, obs_dates_list, sim_dates_list):
            # type: (...) -> (float, float)
            count = 0
            ylows_obs = list()
            yups_obs = list()
            for oi, ov in enumerate(obs_data_list):
                try:
                    si = sim_dates_list.index(obs_dates_list[oi])
                    ylows_obs.append(ylows[si])
                    yups_obs.append(yups[si])
                    if ylows[si] <= ov <= yups[si]:
                        count += 1
                except Exception:
                    continue
            p = float(count) / len(obs_data_list)
            ylows_obs = numpy.array(ylows_obs)
            yups_obs = numpy.array(yups_obs)
            r = numpy.mean(yups_obs - ylows_obs) / numpy.std(numpy.array(obs_data_list))
            return p, r

        # concatenate text
        p_value, r_value = calculate_95ppu_efficiency(sim_obs_data[0][var]['Obs'],
                                                      cali_obs_dates,
                                                      list(sim_data[0].keys()))
        txt = 'P-factor: %.2f\nR-factor: %.2f\n' % (p_value, r_value)
        txt += u'某一最优模拟\n' if plot_cfg.plot_cn else 'One best simulation:\n'
        txt += '    $\mathit{NSE}$: %.2f\n' \
               '    $\mathit{RSR}$: %.2f\n' \
               '    $\mathit{PBIAS}$: %.2f%%\n' \
               '    $\mathit{R^2}$: %.2f' % (sim_obs_data[caliBestIdx][var]['NSE'],
                                             sim_obs_data[caliBestIdx][var]['RSR'],
                                             sim_obs_data[caliBestIdx][var]['PBIAS'],
                                             sim_obs_data[caliBestIdx][var]['R-square'])
        # concatenate text of validation if needed
        vali_txt = ''
        if plot_validation:
            p_value, r_value = calculate_95ppu_efficiency(vali_sim_obs_data[0][var]['Obs'],
                                                          vali_obs_dates,
                                                          list(vali_sim_data[0].keys()))
            vali_txt = 'P-factor: %.2f\nR-factor: %.2f\n\n' % (p_value, r_value)
            vali_txt += '    $\mathit{NSE}$: %.2f\n' \
                        '    $\mathit{RSR}$: %.2f\n' \
                        '    $\mathit{PBIAS}$: %.2f%%\n' \
                        '    $\mathit{R^2}$: %.2f' % (vali_sim_obs_data[caliBestIdx][var]['NSE'],
                                                      vali_sim_obs_data[caliBestIdx][var]['RSR'],
                                                      vali_sim_obs_data[caliBestIdx][var]['PBIAS'],
                                                      vali_sim_obs_data[caliBestIdx][var]['R-square'])
        # plot
        fig, ax = plt.subplots(figsize=(12, 4))
        ax.fill_between(sim_dates, ylows.tolist(), yups.tolist(),
                        color=(0.8, 0.8, 0.8), label='95PPU')
        observed_label = u'实测值' if plot_cfg.plot_cn else 'Observed points'
        ax.scatter(obs_dates, obs_data, marker='.', s=20,
                   color='g', label=observed_label)
        besesim_label = u'最优模拟' if plot_cfg.plot_cn else 'Best simulation'
        ax.plot(sim_dates, sim_best, linestyle='--', color='red', linewidth=1,
                label=besesim_label)
        ax.set_xlim(left=min(sim_dates), right=max(sim_dates))
        ax.set_ylim(bottom=0.)
        date_fmt = mdates.DateFormatter('%m-%d-%y')
        ax.xaxis.set_major_formatter(date_fmt)
        ax.tick_params(axis='x', bottom=True, top=False, length=5, width=2, which='major',
                       labelsize=plot_cfg.tick_fsize)
        ax.tick_params(axis='y', left=True, right=False, length=5, width=2, which='major',
                       labelsize=plot_cfg.tick_fsize)
        plt.xlabel(u'时间' if plot_cfg.plot_cn else 'Date time',
                   fontsize=plot_cfg.axislabel_fsize)
        plt.ylabel(ylabel_str, fontsize=plot_cfg.axislabel_fsize)
        # plot separate dash line
        delta_dt = (sim_dates[-1] - sim_dates[0]) // 9
        delta_dt2 = (sim_dates[-1] - sim_dates[0]) // 35
        sep_time = sim_dates[-1]
        time_pos = [sep_time - delta_dt]
        time_pos2 = [sep_time - 2 * delta_dt]
        ymax, ymin = ax.get_ylim()
        yc = abs(ymax - ymin) * 0.9
        if plot_validation:
            sep_time = vali_sim_dates[0] if vali_sim_dates[0] >= cali_sim_dates[-1] \
                else cali_sim_dates[0]
            cali_vali_labels = [(u'验证期' if plot_cfg.plot_cn else 'Calibration'),
                                (u'率定期' if plot_cfg.plot_cn else 'Validation')]
            if not order:
                cali_vali_labels.reverse()
            time_pos = [sep_time - delta_dt, sep_time + delta_dt2]
            time_pos2 = [sep_time - 2 * delta_dt, sep_time + delta_dt2]
            ax.axvline(sep_time, color='black', linestyle='dashed', linewidth=2)
            plt.text(time_pos[0], yc, cali_vali_labels[0],
                     fontdict={'style': 'italic', 'weight': 'bold',
                               'size': plot_cfg.label_fsize},
                     color='black')
            plt.text(time_pos[1], yc, cali_vali_labels[1],
                     fontdict={'style': 'italic', 'weight': 'bold',
                               'size': plot_cfg.label_fsize},
                     color='black')

        # add legend
        handles, labels = ax.get_legend_handles_labels()
        figorders = [labels.index('95PPU'), labels.index(observed_label),
                     labels.index(besesim_label)]
        ax.legend([handles[idx] for idx in figorders], [labels[idx] for idx in figorders],
                  fontsize=plot_cfg.legend_fsize, loc=2, framealpha=0.8)
        # add text
        cali_pos = time_pos[0] if order else time_pos[1]
        plt.text(cali_pos, yc * 0.5, txt, color='red', fontsize=plot_cfg.label_fsize - 1)
        if plot_validation:
            vali_pos = time_pos[1] if order else time_pos[0]
            plt.text(vali_pos, yc * 0.5, vali_txt, color='red', fontsize=plot_cfg.label_fsize - 1)
        # fig.autofmt_xdate(rotation=0, ha='center')
        plt.tight_layout()
        save_png_eps(plt, outdir, 'Gen%d_95ppu_%s' % (gen_num, var), plot_cfg)
        # close current plot in case of 'figure.max_open_warning'
        plt.cla()
        plt.clf()
        plt.close()
Example #15
0
    def __init__(self, cf):
        """Initialization."""
        # 1. Directories
        self.model_dir = None
        self.scenario_id = -1
        if 'PATH' in cf.sections():
            self.model_dir = cf.get('PATH', 'model_dir')
            self.scenario_id = cf.getint('PATH', 'scenarioid')
            if self.scenario_id < 0:
                self.model_dir = self.model_dir + os.path.sep + 'OUTPUT'
            else:
                self.model_dir = self.model_dir + os.path.sep + 'OUTPUT' + str(self.scenario_id)
        else:
            raise ValueError("[PATH] section MUST be existed in *.ini file.")
        if not FileClass.is_dir_exists(self.model_dir):
            raise ValueError("Please Check Directories defined in [PATH]")

        # 2. MongoDB configuration and database, collation, GridFS names
        self.hostname = '127.0.0.1'  # localhost by default
        self.port = 27017
        self.climate_db = ''
        self.bmp_scenario_db = ''
        self.spatial_db = ''
        if 'MONGODB' in cf.sections():
            self.hostname = cf.get('MONGODB', 'hostname')
            self.port = cf.getint('MONGODB', 'port')
            self.climate_db = cf.get('MONGODB', 'climatedbname')
            self.bmp_scenario_db = cf.get('MONGODB', 'bmpscenariodbname')
            self.spatial_db = cf.get('MONGODB', 'spatialdbname')
        else:
            raise ValueError('[MONGODB] section MUST be existed in *.ini file.')
        if not StringClass.is_valid_ip_addr(self.hostname):
            raise ValueError('HOSTNAME illegal defined in [MONGODB]!')

        # 3. Parameters
        self.plt_subbsnid = -1
        self.plt_vars = list()
        if 'PARAMETERS' in cf.sections():
            self.plt_subbsnid = cf.getint('PARAMETERS', 'plot_subbasinid')
            plt_vars_str = cf.get('PARAMETERS', 'plot_variables')
        else:
            raise ValueError("[PARAMETERS] section MUST be existed in *.ini file.")
        if self.plt_subbsnid < 0:
            raise ValueError("PLOT_SUBBASINID must be greater or equal than 0.")
        if plt_vars_str != '':
            self.plt_vars = StringClass.split_string(plt_vars_str)
        else:
            raise ValueError("PLOT_VARIABLES illegal defined in [PARAMETERS]!")

        # 4. Optional_Parameters
        if 'OPTIONAL_PARAMETERS' in cf.sections():
            tstart = cf.get('OPTIONAL_PARAMETERS', 'time_start')
            tend = cf.get('OPTIONAL_PARAMETERS', 'time_end')
        else:
            raise ValueError("[OPTIONAL_PARAMETERS] section MUST be existed in *.ini file.")
        try:
            # UTCTIME
            self.time_start = StringClass.get_datetime(tstart)
            self.time_end = StringClass.get_datetime(tend)
            if cf.has_option('OPTIONAL_PARAMETERS', 'vali_time_start') and \
                    cf.has_option('OPTIONAL_PARAMETERS', 'vali_time_end'):
                tstart = cf.get('OPTIONAL_PARAMETERS', 'vali_time_start')
                tend = cf.get('OPTIONAL_PARAMETERS', 'vali_time_end')
                self.vali_stime = StringClass.get_datetime(tstart)
                self.vali_etime = StringClass.get_datetime(tend)
            else:
                self.vali_stime = None
                self.vali_etime = None
        except ValueError:
            raise ValueError('The time format MUST be "YYYY-MM-DD" or "YYYY-MM-DD HH:MM:SS".')
        if self.time_start >= self.time_end:
            raise ValueError("Wrong time setted in [OPTIONAL_PARAMETERS]!")
        # 5. Switches
        self.lang_cn = False
        if 'SWITCH' in cf.sections():
            self.lang_cn = cf.getboolean('SWITCH', 'lang_cn')
Example #16
0
def calculate_95ppu(sim_obs_data,
                    sim_data,
                    outdir,
                    gen_num,
                    vali_sim_obs_data=None,
                    vali_sim_data=None):
    """Calculate 95% prediction uncertainty and plot the hydrographs."""
    plt.rcParams['xtick.direction'] = 'out'
    plt.rcParams['ytick.direction'] = 'out'
    plt.rcParams['font.family'] = 'Times New Roman'
    plt.rcParams['timezone'] = 'UTC'
    plt.rcParams['mathtext.fontset'] = 'custom'
    plt.rcParams['mathtext.it'] = 'STIXGeneral:italic'
    plt.rcParams['mathtext.bf'] = 'STIXGeneral:italic:bold'
    if len(sim_data) < 2:
        return
    var_name = sim_obs_data[0]['var_name']
    for idx, var in enumerate(var_name):
        plot_validation = False
        if vali_sim_obs_data and vali_sim_data and var in vali_sim_obs_data[0][
                'var_name']:
            plot_validation = True
        ylabel_str = var
        if var in ['Q', 'QI', 'QG', 'QS']:
            ylabel_str += ' (m$^3$/s)'
        elif 'CONC' in var.upper():  # Concentrate
            if 'SED' in var.upper():
                ylabel_str += ' (g/L)'
            else:
                ylabel_str += ' (mg/L)'
        else:  # amount
            ylabel_str += ' (kg)'
        cali_obs_dates = sim_obs_data[0][var]['UTCDATETIME'][:]
        if isinstance(cali_obs_dates[0], str) or isinstance(
                cali_obs_dates[0], text_type):
            cali_obs_dates = [
                StringClass.get_datetime(s) for s in cali_obs_dates
            ]
        obs_dates = cali_obs_dates[:]
        if plot_validation:
            vali_obs_dates = vali_sim_obs_data[0][var]['UTCDATETIME']
            if isinstance(vali_obs_dates[0], str) or isinstance(
                    vali_obs_dates[0], text_type):
                vali_obs_dates = [
                    StringClass.get_datetime(s) for s in vali_obs_dates
                ]
            obs_dates += vali_obs_dates
        obs_data = sim_obs_data[0][var]['Obs'][:]
        if plot_validation:
            obs_data += vali_sim_obs_data[0][var]['Obs']

        sim_dates = list(sim_data[0].keys())
        if isinstance(sim_dates[0], str) or isinstance(sim_dates[0],
                                                       text_type):
            sim_dates = [StringClass.get_datetime(s) for s in sim_dates]
        if plot_validation:
            vali_sim_dates = list(vali_sim_data[0].keys())
            if isinstance(vali_sim_dates[0], str) or isinstance(
                    vali_sim_dates[0], text_type):
                vali_sim_dates = [
                    StringClass.get_datetime(s) for s in vali_sim_dates
                ]
            sim_dates += vali_sim_dates
        sim_data_list = list()
        caliBestIdx = -1
        caliBestNSE = -9999.
        for idx2, ind in enumerate(sim_data):
            tmp = numpy.array(list(ind.values()))
            tmp = tmp[:, idx]
            if sim_obs_data[idx2][var]['NSE'] > caliBestNSE:
                caliBestNSE = sim_obs_data[idx2][var]['NSE']
                caliBestIdx = idx2
            tmpsim = tmp.tolist()
            if plot_validation:
                tmpsim += numpy.array(list(
                    vali_sim_data[idx2].values()))[:, idx].tolist()
            sim_data_list.append(tmpsim)

        sim_best = numpy.array(list(sim_data[caliBestIdx].values()))[:, idx]
        sim_best = sim_best.tolist()
        if plot_validation:
            sim_best += numpy.array(list(
                vali_sim_data[caliBestIdx].values()))[:, idx].tolist()
        sim_data_list = numpy.array(sim_data_list)
        ylows = numpy.percentile(sim_data_list,
                                 2.5,
                                 0,
                                 interpolation='nearest')
        yups = numpy.percentile(sim_data_list,
                                97.5,
                                0,
                                interpolation='nearest')

        def calculate_95ppu_efficiency(obs_data_list, obs_dates_list,
                                       sim_dates_list):
            count = 0
            ylows_obs = list()
            yups_obs = list()
            for oi, ov in enumerate(obs_data_list):
                try:
                    si = sim_dates_list.index(obs_dates_list[oi])
                    ylows_obs.append(ylows[si])
                    yups_obs.append(yups[si])
                    if ylows[si] <= ov <= yups[si]:
                        count += 1
                except Exception:
                    continue
            p = float(count) / len(obs_data_list)
            ylows_obs = numpy.array(ylows_obs)
            yups_obs = numpy.array(yups_obs)
            r = numpy.mean(yups_obs - ylows_obs) / numpy.std(
                numpy.array(obs_data_list))
            return p, r

        # concatenate text
        p_value, r_value = calculate_95ppu_efficiency(
            sim_obs_data[0][var]['Obs'], cali_obs_dates,
            list(sim_data[0].keys()))
        txt = 'P-factor: %.2f, R-factor: %.2f\n' % (p_value, r_value)
        txt += 'One of the best simulations:\n' \
               '    $\mathit{NSE}$: %.2f\n' \
               '    $\mathit{RSR}$: %.2f\n' \
               '    $\mathit{PBIAS}$: %.2f%%\n' \
               '    $\mathit{R^2}$: %.2f' % (sim_obs_data[caliBestIdx][var]['NSE'],
                                             sim_obs_data[caliBestIdx][var]['RSR'],
                                             sim_obs_data[caliBestIdx][var]['PBIAS'],
                                             sim_obs_data[caliBestIdx][var]['R-square'])
        # concatenate text of validation if needed
        vali_txt = ''
        if plot_validation:
            p_value, r_value = calculate_95ppu_efficiency(
                vali_sim_obs_data[0][var]['Obs'], vali_obs_dates,
                list(vali_sim_data[0].keys()))
            vali_txt = 'P-factor: %.2f, R-factor: %.2f\n\n' % (p_value,
                                                               r_value)
            vali_txt += '    $\mathit{NSE}$: %.2f\n' \
                        '    $\mathit{RSR}$: %.2f\n' \
                        '    $\mathit{PBIAS}$: %.2f%%\n' \
                        '    $\mathit{R^2}$: %.2f' % (vali_sim_obs_data[caliBestIdx][var]['NSE'],
                                                      vali_sim_obs_data[caliBestIdx][var]['RSR'],
                                                      vali_sim_obs_data[caliBestIdx][var]['PBIAS'],
                                                      vali_sim_obs_data[caliBestIdx][var][
                                                          'R-square'])
        # plot
        fig, ax = plt.subplots(figsize=(12, 4))
        ax.fill_between(sim_dates,
                        ylows.tolist(),
                        yups.tolist(),
                        color=(0.8, 0.8, 0.8),
                        label='95PPU')
        ax.scatter(obs_dates,
                   obs_data,
                   marker='.',
                   s=20,
                   color='g',
                   label='Observed points')
        ax.plot(sim_dates,
                sim_best,
                linestyle='--',
                color='red',
                label='Best simulation',
                linewidth=1)
        ax.set_xlim(left=min(sim_dates), right=max(sim_dates))
        ax.set_ylim(bottom=0.)
        date_fmt = mdates.DateFormatter('%m-%d-%y')
        ax.xaxis.set_major_formatter(date_fmt)
        ax.tick_params(axis='x',
                       bottom='on',
                       top='off',
                       length=5,
                       width=2,
                       which='major')
        ax.tick_params(axis='y',
                       left='on',
                       right='off',
                       length=5,
                       width=2,
                       which='major')
        plt.xlabel('Date', fontsize='small')
        plt.ylabel(ylabel_str, fontsize='small')
        # plot separate dash line
        delta_dt = (max(sim_dates) - min(sim_dates)) / 9
        delta_dt2 = (max(sim_dates) - min(sim_dates)) / 35
        sep_time = max(sim_dates) - delta_dt
        ymax, ymin = ax.get_ylim()
        yc = abs(ymax - ymin) * 0.9
        if plot_validation:
            sep_time = vali_sim_dates[
                0] if vali_sim_dates[0] > sim_dates[0] else sim_dates[0]
            ax.axvline(sep_time,
                       color='black',
                       linestyle='dashed',
                       linewidth=2)
            plt.text(sep_time - delta_dt,
                     yc,
                     'Calibration',
                     fontdict={
                         'style': 'italic',
                         'weight': 'bold'
                     },
                     color='black')
            plt.text(sep_time + delta_dt2,
                     yc,
                     'Validation',
                     fontdict={
                         'style': 'italic',
                         'weight': 'bold'
                     },
                     color='black')

        # add legend
        handles, labels = ax.get_legend_handles_labels()
        order = [
            labels.index('95PPU'),
            labels.index('Observed points'),
            labels.index('Best simulation')
        ]
        ax.legend([handles[idx] for idx in order],
                  [labels[idx] for idx in order],
                  fontsize='medium',
                  loc=2,
                  framealpha=0.8)
        # add text
        plt.text(sep_time - 2 * delta_dt, yc * 0.6, txt, color='red')
        if plot_validation:
            plt.text(sep_time + delta_dt2, yc * 0.6, vali_txt, color='red')
        # fig.autofmt_xdate(rotation=0, ha='center')
        plt.tight_layout()
        save_png_eps(plt, outdir, 'Gen%d_95ppu_%s' % (gen_num, var))
        # close current plot in case of 'figure.max_open_warning'
        plt.cla()
        plt.clf()
        plt.close()
def interpolate_observed_data_to_regular_interval(in_file, time_interval, start_time, end_time,
                                                  eliminate_zero=False,
                                                  time_sys_output='UTCTIME', day_divided_hour=0):
    """
    Interpolate not regular observed data to regular time interval data.

    Todo: Not tested yet!

    Args:
        in_file: input data file, the basic format is as follows:
                 line 1: #<time_system> [<time_zone>], e.g., #LOCALTIME 8, #UTCTIME
                 line 2: DATETIME,field1,field2,...
                 line 3: YYYY-mm-dd HH:MM:SS,field1_value,field2_value,...
                 line 4: ...
                 ...
                 Field name can be PCP, FLOW, SED
                 the unit is mm/h, m3/s, g/L (i.e., kg/m3), respectively.
        time_interval: time interval, unit is minute, e.g., daily output is 1440
        start_time: start time, the format must be 'YYYY-mm-dd HH:MM:SS', and the time system
                    is based on time_sys.
        end_time: end time, see also start_time.
        eliminate_zero: Boolean flag. If true, the time interval without original records will
                        not be output.
        time_sys_output: time system of output time_system, the format must be
                  '<time_system> [<time_zone>]', e.g.,
                  'LOCALTIME'
                  'LOCALTIME 8'
                  'UTCTIME' (default)
        day_divided_hour: If the time_interval is equal to N*1440, this parameter should be
                          carefully specified. The value must range from 0 to 23. e.g.,
                          day_divided_hour ==> day ranges (all expressed as 2013-02-03)
                          0  ==> 2013-02-03 00:00:00 to 2013-02-03 23:59:59 (default)
                          8  ==> 2013-02-03 08:00:00 to 2013-02-04 07:59:59
                          20 ==> 2013-02-03 20:00:00 to 2013-02-04 19:59:59
    Returns:
        The output data files are located in the same directory with the input file.
        The nomenclature is: <field name>_<time system>_<time interval>_<nonzero>, e.g.,
        pcp_utctime_1440_nonzero.csv, flow_localtime_60.csv.
        Note that `.txt` format is also supported.
    """
    FileClass.check_file_exists(in_file)
    time_sys_input, time_zone_input = HydroClimateUtilClass.get_time_system_from_data_file(in_file)
    data_items = read_data_items_from_txt(in_file)
    flds = data_items[0][:]
    data_items.remove(flds)
    if not 0 <= day_divided_hour <= 23:
        raise ValueError('Day divided hour must range from 0 to 23!')
    try:
        date_idx = flds.index('DATETIME')
        flds.remove('DATETIME')
    except ValueError:
        raise ValueError('DATETIME must be one of the fields!')
    # available field
    available_flds = ['FLOW', 'SED', 'PCP']

    def check_avaiable_field(cur_fld):
        """Check if the given field name is supported."""
        support_flag = False
        for fff in available_flds:
            if fff.lower() in cur_fld.lower():
                support_flag = True
                break
        return support_flag

    ord_data = OrderedDict()
    time_zone_output = time.timezone // 3600
    if time_sys_output.lower().find('local') >= 0:
        tmpstrs = StringClass.split_string(time_sys_output, [' '])
        if len(tmpstrs) == 2 and MathClass.isnumerical(tmpstrs[1]):
            time_zone_output = -1 * int(tmpstrs[1])
        time_sys_output = 'LOCALTIME'
    else:
        time_sys_output = 'UTCTIME'
        time_zone_output = 0
    for item in data_items:
        org_datetime = StringClass.get_datetime(item[date_idx])
        if time_sys_input == 'LOCALTIME':
            org_datetime += timedelta(hours=time_zone_input)  # now, org_datetime is UTC time.
        if time_sys_output == 'LOCALTIME':
            org_datetime -= timedelta(hours=time_zone_output)
        # now, org_datetime is consistent with the output time system
        ord_data[org_datetime] = list()
        for i, v in enumerate(item):
            if i == date_idx:
                continue
            if MathClass.isnumerical(v):
                ord_data[org_datetime].append(float(v))
            else:
                ord_data[org_datetime].append(v)
    # print(ord_data)
    itp_data = OrderedDict()
    out_time_delta = timedelta(minutes=time_interval)
    sdatetime = StringClass.get_datetime(start_time)
    edatetime = StringClass.get_datetime(end_time)
    item_dtime = sdatetime
    if time_interval % 1440 == 0:
        item_dtime = sdatetime.replace(hour=0, minute=0, second=0) + \
                     timedelta(minutes=day_divided_hour * 60)
    while item_dtime <= edatetime:
        # print(item_dtime)
        # if item_dtime.month == 12 and item_dtime.day == 31:
        #     print("debug")
        sdt = item_dtime  # start datetime of records
        edt = item_dtime + out_time_delta  # end datetime of records
        # get original data items
        org_items = list()
        pre_dt = list(ord_data.keys())[0]
        pre_added = False
        for i, v in list(ord_data.items()):
            if sdt <= i < edt:
                if not pre_added and pre_dt < sdt < i and sdt - pre_dt < out_time_delta:
                    # only add one item that less than sdt.
                    org_items.append([pre_dt] + ord_data.get(pre_dt))
                    pre_added = True
                org_items.append([i] + v)
            if i > edt:
                break
            pre_dt = i
        if len(org_items) > 0:
            org_items.append([edt])  # Just add end time for compute convenient
            if org_items[0][0] < sdt:
                org_items[0][0] = sdt  # set the begin datetime of current time interval
        # if eliminate time interval without original records
        # initial interpolated list
        itp_data[item_dtime] = [0.] * len(flds)
        if len(org_items) == 0:
            if eliminate_zero:
                itp_data.popitem()
            item_dtime += out_time_delta
            continue
        # core interpolation code
        flow_idx = -1
        for v_idx, v_name in enumerate(flds):
            if not check_avaiable_field(v_name):
                continue
            if 'SED' in v_name.upper():  # FLOW must be existed
                for v_idx2, v_name2 in enumerate(flds):
                    if 'FLOW' in v_name2.upper():
                        flow_idx = v_idx2
                        break
                if flow_idx < 0:
                    raise RuntimeError('To interpolate SED, FLOW must be provided!')
        for v_idx, v_name in enumerate(flds):
            if not check_avaiable_field(v_name):
                continue
            itp_value = 0.
            itp_auxiliary_value = 0.
            for org_item_idx, org_item_dtv in enumerate(org_items):
                if org_item_idx == 0:
                    continue
                org_item_dt = org_item_dtv[0]
                pre_item_dtv = org_items[org_item_idx - 1]
                pre_item_dt = pre_item_dtv[0]
                tmp_delta_dt = org_item_dt - pre_item_dt
                tmp_delta_secs = tmp_delta_dt.days * 86400 + tmp_delta_dt.seconds
                if 'SED' in v_name.upper():
                    itp_value += pre_item_dtv[v_idx + 1] * pre_item_dtv[flow_idx + 1] * \
                                 tmp_delta_secs
                    itp_auxiliary_value += pre_item_dtv[flow_idx + 1] * tmp_delta_secs
                else:
                    itp_value += pre_item_dtv[v_idx + 1] * tmp_delta_secs
            if 'SED' in v_name.upper():
                if MathClass.floatequal(itp_auxiliary_value, 0.):
                    itp_value = 0.
                    print('WARNING: Flow is 0 for %s, please check!' %
                          item_dtime.strftime('%Y-%m-%d %H:%M:%S'))
                itp_value /= itp_auxiliary_value
            elif 'FLOW' in v_name.upper():
                itp_value /= (out_time_delta.days * 86400 + out_time_delta.seconds)
            elif 'PCP' in v_name.upper():  # the input is mm/h, and output is mm
                itp_value /= 3600.
            itp_data[item_dtime][v_idx] = round(itp_value, 4)
        item_dtime += out_time_delta

    # for i, v in itp_data.items():
    #     print(i, v)
    # output to files
    work_path = os.path.dirname(in_file)
    header_str = '#' + time_sys_output
    if time_sys_output == 'LOCALTIME':
        header_str = header_str + ' ' + str(time_zone_output)
    for idx, fld in enumerate(flds):
        if not check_avaiable_field(fld):
            continue
        file_name = fld + '_' + time_sys_output + '_' + str(time_interval)
        if eliminate_zero:
            file_name += '_nonzero'
        file_name += '.csv'
        out_file = work_path + os.path.sep + file_name
        with open(out_file, 'w', encoding='utf-8') as f:
            f.write(header_str + '\n')
            f.write('DATETIME,' + fld + '\n')
            for i, v in list(itp_data.items()):
                cur_line = i.strftime('%Y-%m-%d %H:%M:%S') + ',' + str(v[idx]) + '\n'
                f.write(cur_line)
Example #18
0
def interpolate_observed_data_to_regular_interval(in_file, time_interval, start_time, end_time,
                                                  eliminate_zero=False,
                                                  time_sys_output='UTCTIME', day_divided_hour=0):
    """
    Interpolate not regular observed data to regular time interval data.
    Args:
        in_file: input data file, the basic format is as follows:
                 line 1: #<time_system> [<time_zone>], e.g., #LOCALTIME 8, #UTCTIME
                 line 2: DATETIME,field1,field2,...
                 line 3: YYYY-mm-dd HH:MM:SS,field1_value,field2_value,...
                 line 4: ...
                 ...
                 Field name can be PCP, FLOW, SED
                 the unit is mm/h, m3/s, g/L (i.e., kg/m3), respectively.
        time_interval: time interval, unit is minute, e.g., daily output is 1440
        start_time: start time, the format must be 'YYYY-mm-dd HH:MM:SS', and the time system
                    is based on time_sys.
        end_time: end time, see also start_time.
        eliminate_zero: Boolean flag. If true, the time interval without original records will
                        not be output.
        time_sys_output: time system of output time_system, the format must be
                  '<time_system> [<time_zone>]', e.g.,
                  'LOCALTIME'
                  'LOCALTIME 8'
                  'UTCTIME' (default)
        day_divided_hour: If the time_interval is equal to N*1440, this parameter should be
                          carefully specified. The value must range from 0 to 23. e.g.,
                          day_divided_hour ==> day ranges (all expressed as 2013-02-03)
                          0  ==> 2013-02-03 00:00:00 to 2013-02-03 23:59:59 (default)
                          8  ==> 2013-02-03 08:00:00 to 2013-02-04 07:59:59
                          20 ==> 2013-02-03 20:00:00 to 2013-02-04 19:59:59
    Returns:
        The output data files are located in the same directory with the input file.
        The nomenclature is: <field name>_<time system>_<time interval>_<nonzero>, e.g.,
        pcp_utctime_1440_nonzero.txt, flow_localtime_60.txt
    """
    FileClass.check_file_exists(in_file)
    time_sys_input, time_zone_input = HydroClimateUtilClass.get_time_system_from_data_file(in_file)
    data_items = read_data_items_from_txt(in_file)
    flds = data_items[0][:]
    data_items.remove(flds)
    if not 0 <= day_divided_hour <= 23:
        raise ValueError('Day divided hour must range from 0 to 23!')
    try:
        date_idx = flds.index('DATETIME')
        flds.remove('DATETIME')
    except ValueError:
        raise ValueError('DATETIME must be one of the fields!')
    # available field
    available_flds = ['FLOW', 'SED', 'PCP']

    def check_avaiable_field(cur_fld):
        """Check if the given field name is supported."""
        support_flag = False
        for fff in available_flds:
            if fff.lower() in cur_fld.lower():
                support_flag = True
                break
        return support_flag

    ord_data = OrderedDict()
    time_zone_output = time.timezone / -3600
    if time_sys_output.lower().find('local') >= 0:
        tmpstrs = StringClass.split_string(time_sys_output, [' '])
        if len(tmpstrs) == 2 and MathClass.isnumerical(tmpstrs[1]):
            time_zone_output = int(tmpstrs[1])
        time_sys_output = 'LOCALTIME'
    else:
        time_sys_output = 'UTCTIME'
        time_zone_output = 0
    for item in data_items:
        org_datetime = StringClass.get_datetime(item[date_idx])
        if time_sys_input == 'LOCALTIME':
            org_datetime -= timedelta(hours=time_zone_input)
        # now, org_datetime is UTC time.
        if time_sys_output == 'LOCALTIME':
            org_datetime += timedelta(hours=time_zone_output)
        # now, org_datetime is consistent with the output time system
        ord_data[org_datetime] = list()
        for i, v in enumerate(item):
            if i == date_idx:
                continue
            if MathClass.isnumerical(v):
                ord_data[org_datetime].append(float(v))
            else:
                ord_data[org_datetime].append(v)
    # print(ord_data)
    itp_data = OrderedDict()
    out_time_delta = timedelta(minutes=time_interval)
    sdatetime = StringClass.get_datetime(start_time)
    edatetime = StringClass.get_datetime(end_time)
    item_dtime = sdatetime
    if time_interval % 1440 == 0:
        item_dtime = sdatetime.replace(hour=0, minute=0, second=0) + \
                     timedelta(minutes=day_divided_hour * 60)
    while item_dtime <= edatetime:
        # print(item_dtime)
        # if item_dtime.month == 12 and item_dtime.day == 31:
        #     print("debug")
        sdt = item_dtime  # start datetime of records
        edt = item_dtime + out_time_delta  # end datetime of records
        # get original data items
        org_items = list()
        pre_dt = list(ord_data.keys())[0]
        pre_added = False
        for i, v in list(ord_data.items()):
            if sdt <= i < edt:
                if not pre_added and pre_dt < sdt < i and sdt - pre_dt < out_time_delta:
                    # only add one item that less than sdt.
                    org_items.append([pre_dt] + ord_data.get(pre_dt))
                    pre_added = True
                org_items.append([i] + v)
            if i > edt:
                break
            pre_dt = i
        if len(org_items) > 0:
            org_items.append([edt])  # Just add end time for compute convenient
            if org_items[0][0] < sdt:
                org_items[0][0] = sdt  # set the begin datetime of current time interval
        # if eliminate time interval without original records
        # initial interpolated list
        itp_data[item_dtime] = [0.] * len(flds)
        if len(org_items) == 0:
            if eliminate_zero:
                itp_data.popitem()
            item_dtime += out_time_delta
            continue
        # core interpolation code
        flow_idx = -1
        for v_idx, v_name in enumerate(flds):
            if not check_avaiable_field(v_name):
                continue
            if 'SED' in v_name.upper():  # FLOW must be existed
                for v_idx2, v_name2 in enumerate(flds):
                    if 'FLOW' in v_name2.upper():
                        flow_idx = v_idx2
                        break
                if flow_idx < 0:
                    raise RuntimeError('To interpolate SED, FLOW must be provided!')
        for v_idx, v_name in enumerate(flds):
            if not check_avaiable_field(v_name):
                continue
            itp_value = 0.
            itp_auxiliary_value = 0.
            for org_item_idx, org_item_dtv in enumerate(org_items):
                if org_item_idx == 0:
                    continue
                org_item_dt = org_item_dtv[0]
                pre_item_dtv = org_items[org_item_idx - 1]
                pre_item_dt = pre_item_dtv[0]
                tmp_delta_dt = org_item_dt - pre_item_dt
                tmp_delta_secs = tmp_delta_dt.days * 86400 + tmp_delta_dt.seconds
                if 'SED' in v_name.upper():
                    itp_value += pre_item_dtv[v_idx + 1] * pre_item_dtv[flow_idx + 1] * \
                                 tmp_delta_secs
                    itp_auxiliary_value += pre_item_dtv[flow_idx + 1] * tmp_delta_secs
                else:
                    itp_value += pre_item_dtv[v_idx + 1] * tmp_delta_secs
            if 'SED' in v_name.upper():
                if MathClass.floatequal(itp_auxiliary_value, 0.):
                    itp_value = 0.
                    print('WARNING: Flow is 0 for %s, please check!' %
                          item_dtime.strftime('%Y-%m-%d %H:%M:%S'))
                itp_value /= itp_auxiliary_value
            elif 'FLOW' in v_name.upper():
                itp_value /= (out_time_delta.days * 86400 + out_time_delta.seconds)
            elif 'PCP' in v_name.upper():  # the input is mm/h, and output is mm
                itp_value /= 3600.
            itp_data[item_dtime][v_idx] = round(itp_value, 4)
        item_dtime += out_time_delta

    # for i, v in itp_data.items():
    #     print(i, v)
    # output to files
    work_path = os.path.dirname(in_file)
    header_str = '#' + time_sys_output
    if time_sys_output == 'LOCALTIME':
        header_str = header_str + ' ' + str(time_zone_output)
    for idx, fld in enumerate(flds):
        if not check_avaiable_field(fld):
            continue
        file_name = fld + '_' + time_sys_output + '_' + str(time_interval)
        if eliminate_zero:
            file_name += '_nonzero'
        file_name += '.txt'
        out_file = work_path + os.path.sep + file_name
        with open(out_file, 'w') as f:
            f.write(header_str + '\n')
            f.write('DATETIME,' + fld + '\n')
            for i, v in list(itp_data.items()):
                cur_line = i.strftime('%Y-%m-%d %H:%M:%S') + ',' + str(v[idx]) + '\n'
                f.write(cur_line)
Example #19
0
    def __init__(self, cf):
        # Default arguments
        self.host = '127.0.0.1'  # localhost by default
        self.port = 27017
        self.bin_dir = ''
        self.model_dir = ''
        self.db_name = ''
        self.version = 'OMP'
        self.mpi_bin = None
        self.hosts_opt = None
        self.hostfile = None
        self.nprocess = 1
        self.nthread = 1
        self.lyrmtd = 1
        self.scenario_id = 0
        self.calibration_id = -1
        self.config_dict = dict()

        if 'SEIMS_Model' not in cf.sections():
            raise ValueError(
                "[SEIMS_Model] section MUST be existed in *.ini file.")

        self.host = cf.get('SEIMS_Model', 'hostname')
        self.port = cf.getint('SEIMS_Model', 'port')
        if not StringClass.is_valid_ip_addr(self.host):
            raise ValueError('HOSTNAME defined in [SEIMS_Model] is illegal!')

        self.bin_dir = cf.get('SEIMS_Model', 'bin_dir')
        self.model_dir = cf.get('SEIMS_Model', 'model_dir')
        if not (FileClass.is_dir_exists(self.model_dir)
                and FileClass.is_dir_exists(self.bin_dir)):
            raise IOError('Please Check Directories defined in [SEIMS_Model]. '
                          'BIN_DIR and MODEL_DIR are required!')
        self.db_name = os.path.split(self.model_dir)[1]

        if cf.has_option('SEIMS_Model', 'version'):
            self.version = cf.get('SEIMS_Model', 'version')
        if cf.has_option('SEIMS_Model',
                         'mpi_bin'):  # full path of the executable MPI program
            self.mpi_bin = cf.get('SEIMS_Model', 'mpi_bin')
        if cf.has_option('SEIMS_Model', 'hostopt'):
            self.hosts_opt = cf.get('SEIMS_Model', 'hostopt')
        if cf.has_option('SEIMS_Model', 'hostfile'):
            self.hostfile = cf.get('SEIMS_Model', 'hostfile')
        if cf.has_option('SEIMS_Model', 'processnum'):
            self.nprocess = cf.getint('SEIMS_Model', 'processnum')
        if cf.has_option('SEIMS_Model', 'threadsnum'):
            self.nthread = cf.getint('SEIMS_Model', 'threadsnum')
        if cf.has_option('SEIMS_Model', 'layeringmethod'):
            self.lyrmtd = cf.getint('SEIMS_Model', 'layeringmethod')
        if cf.has_option('SEIMS_Model', 'scenarioid'):
            self.scenario_id = cf.getint('SEIMS_Model', 'scenarioid')
        if cf.has_option('SEIMS_Model', 'calibrationid'):
            self.calibration_id = cf.getint('SEIMS_Model', 'calibrationid')

        if not (cf.has_option('SEIMS_Model', 'sim_time_start')
                and cf.has_option('SEIMS_Model', 'sim_time_end')):
            raise ValueError(
                "Start and end time MUST be specified in [SEIMS_Model].")

        try:
            # UTCTIME
            tstart = cf.get('SEIMS_Model', 'sim_time_start')
            tend = cf.get('SEIMS_Model', 'sim_time_end')
            self.time_start = StringClass.get_datetime(tstart)
            self.time_end = StringClass.get_datetime(tend)
        except ValueError:
            raise ValueError('The time format MUST be "YYYY-MM-DD HH:MM:SS".')
        if self.time_start >= self.time_end:
            raise ValueError("Wrong time settings in [SEIMS_Model]!")
        # Running time counted by time.time() of Python, in case of failed of GetTimespan()
        self.runtime = 0.
Example #20
0
def calculate_95ppu(sim_obs_data, sim_data, outdir, gen_num,
                    vali_sim_obs_data=None, vali_sim_data=None):
    """Calculate 95% prediction uncertainty and plot the hydrographs."""
    plt.rcParams['xtick.direction'] = 'out'
    plt.rcParams['ytick.direction'] = 'out'
    plt.rcParams['font.family'] = 'Times New Roman'
    plt.rcParams['timezone'] = 'UTC'
    plt.rcParams['mathtext.fontset'] = 'custom'
    plt.rcParams['mathtext.it'] = 'STIXGeneral:italic'
    plt.rcParams['mathtext.bf'] = 'STIXGeneral:italic:bold'
    if len(sim_data) < 2:
        return
    var_name = sim_obs_data[0]['var_name']
    for idx, var in enumerate(var_name):
        plot_validation = False
        if vali_sim_obs_data and vali_sim_data and var in vali_sim_obs_data[0]['var_name']:
            plot_validation = True
        ylabel_str = var
        if var in ['Q', 'QI', 'QG', 'QS']:
            ylabel_str += ' (m$^3$/s)'
        elif 'CONC' in var.upper():  # Concentrate
            if 'SED' in var.upper():
                ylabel_str += ' (g/L)'
            else:
                ylabel_str += ' (mg/L)'
        else:  # amount
            ylabel_str += ' (kg)'
        cali_obs_dates = sim_obs_data[0][var]['UTCDATETIME'][:]
        if isinstance(cali_obs_dates[0], str) or isinstance(cali_obs_dates[0], text_type):
            cali_obs_dates = [StringClass.get_datetime(s) for s in cali_obs_dates]
        obs_dates = cali_obs_dates[:]
        if plot_validation:
            vali_obs_dates = vali_sim_obs_data[0][var]['UTCDATETIME']
            if isinstance(vali_obs_dates[0], str) or isinstance(vali_obs_dates[0], text_type):
                vali_obs_dates = [StringClass.get_datetime(s) for s in vali_obs_dates]
            obs_dates += vali_obs_dates
        obs_data = sim_obs_data[0][var]['Obs'][:]
        if plot_validation:
            obs_data += vali_sim_obs_data[0][var]['Obs']

        sim_dates = list(sim_data[0].keys())
        if isinstance(sim_dates[0], str) or isinstance(sim_dates[0], text_type):
            sim_dates = [StringClass.get_datetime(s) for s in sim_dates]
        if plot_validation:
            vali_sim_dates = list(vali_sim_data[0].keys())
            if isinstance(vali_sim_dates[0], str) or isinstance(vali_sim_dates[0], text_type):
                vali_sim_dates = [StringClass.get_datetime(s) for s in vali_sim_dates]
            sim_dates += vali_sim_dates
        sim_data_list = list()
        caliBestIdx = -1
        caliBestNSE = -9999.
        for idx2, ind in enumerate(sim_data):
            tmp = numpy.array(list(ind.values()))
            tmp = tmp[:, idx]
            if sim_obs_data[idx2][var]['NSE'] > caliBestNSE:
                caliBestNSE = sim_obs_data[idx2][var]['NSE']
                caliBestIdx = idx2
            tmpsim = tmp.tolist()
            if plot_validation:
                tmpsim += numpy.array(list(vali_sim_data[idx2].values()))[:, idx].tolist()
            sim_data_list.append(tmpsim)

        sim_best = numpy.array(list(sim_data[caliBestIdx].values()))[:, idx]
        sim_best = sim_best.tolist()
        if plot_validation:
            sim_best += numpy.array(list(vali_sim_data[caliBestIdx].values()))[:, idx].tolist()
        sim_data_list = numpy.array(sim_data_list)
        ylows = numpy.percentile(sim_data_list, 2.5, 0, interpolation='nearest')
        yups = numpy.percentile(sim_data_list, 97.5, 0, interpolation='nearest')

        def calculate_95ppu_efficiency(obs_data_list, obs_dates_list, sim_dates_list):
            count = 0
            ylows_obs = list()
            yups_obs = list()
            for oi, ov in enumerate(obs_data_list):
                try:
                    si = sim_dates_list.index(obs_dates_list[oi])
                    ylows_obs.append(ylows[si])
                    yups_obs.append(yups[si])
                    if ylows[si] <= ov <= yups[si]:
                        count += 1
                except Exception:
                    continue
            p = float(count) / len(obs_data_list)
            ylows_obs = numpy.array(ylows_obs)
            yups_obs = numpy.array(yups_obs)
            r = numpy.mean(yups_obs - ylows_obs) / numpy.std(numpy.array(obs_data_list))
            return p, r

        # concatenate text
        p_value, r_value = calculate_95ppu_efficiency(sim_obs_data[0][var]['Obs'],
                                                      cali_obs_dates,
                                                      list(sim_data[0].keys()))
        txt = 'P-factor: %.2f, R-factor: %.2f\n' % (p_value, r_value)
        txt += 'One of the best simulations:\n' \
               '    $\mathit{NSE}$: %.2f\n' \
               '    $\mathit{RSR}$: %.2f\n' \
               '    $\mathit{PBIAS}$: %.2f%%\n' \
               '    $\mathit{R^2}$: %.2f' % (sim_obs_data[caliBestIdx][var]['NSE'],
                                             sim_obs_data[caliBestIdx][var]['RSR'],
                                             sim_obs_data[caliBestIdx][var]['PBIAS'],
                                             sim_obs_data[caliBestIdx][var]['R-square'])
        # concatenate text of validation if needed
        vali_txt = ''
        if plot_validation:
            p_value, r_value = calculate_95ppu_efficiency(vali_sim_obs_data[0][var]['Obs'],
                                                          vali_obs_dates,
                                                          list(vali_sim_data[0].keys()))
            vali_txt = 'P-factor: %.2f, R-factor: %.2f\n\n' % (p_value, r_value)
            vali_txt += '    $\mathit{NSE}$: %.2f\n' \
                        '    $\mathit{RSR}$: %.2f\n' \
                        '    $\mathit{PBIAS}$: %.2f%%\n' \
                        '    $\mathit{R^2}$: %.2f' % (vali_sim_obs_data[caliBestIdx][var]['NSE'],
                                                      vali_sim_obs_data[caliBestIdx][var]['RSR'],
                                                      vali_sim_obs_data[caliBestIdx][var]['PBIAS'],
                                                      vali_sim_obs_data[caliBestIdx][var][
                                                          'R-square'])
        # plot
        fig, ax = plt.subplots(figsize=(12, 4))
        ax.fill_between(sim_dates, ylows.tolist(), yups.tolist(),
                        color=(0.8, 0.8, 0.8), label='95PPU')
        ax.scatter(obs_dates, obs_data, marker='.', s=20,
                   color='g', label='Observed points')
        ax.plot(sim_dates, sim_best, linestyle='--', color='red',
                label='Best simulation', linewidth=1)
        ax.set_xlim(left=min(sim_dates), right=max(sim_dates))
        ax.set_ylim(bottom=0.)
        date_fmt = mdates.DateFormatter('%m-%d-%y')
        ax.xaxis.set_major_formatter(date_fmt)
        ax.tick_params(axis='x', bottom='on', top='off', length=5, width=2, which='major')
        ax.tick_params(axis='y', left='on', right='off', length=5, width=2, which='major')
        plt.xlabel('Date', fontsize='small')
        plt.ylabel(ylabel_str, fontsize='small')
        # plot separate dash line
        delta_dt = (max(sim_dates) - min(sim_dates)) / 9
        delta_dt2 = (max(sim_dates) - min(sim_dates)) / 35
        sep_time = max(sim_dates) - delta_dt
        ymax, ymin = ax.get_ylim()
        yc = abs(ymax - ymin) * 0.9
        if plot_validation:
            sep_time = vali_sim_dates[0] if vali_sim_dates[0] > sim_dates[0] else sim_dates[0]
            ax.axvline(sep_time, color='black', linestyle='dashed', linewidth=2)
            plt.text(sep_time - delta_dt, yc, 'Calibration',
                     fontdict={'style': 'italic', 'weight': 'bold'}, color='black')
            plt.text(sep_time + delta_dt2, yc, 'Validation',
                     fontdict={'style': 'italic', 'weight': 'bold'}, color='black')

        # add legend
        handles, labels = ax.get_legend_handles_labels()
        order = [labels.index('95PPU'), labels.index('Observed points'),
                 labels.index('Best simulation')]
        ax.legend([handles[idx] for idx in order], [labels[idx] for idx in order],
                  fontsize='medium', loc=2, framealpha=0.8)
        # add text
        plt.text(sep_time - 2 * delta_dt, yc * 0.6, txt, color='red')
        if plot_validation:
            plt.text(sep_time + delta_dt2, yc * 0.6, vali_txt, color='red')
        # fig.autofmt_xdate(rotation=0, ha='center')
        plt.tight_layout()
        save_png_eps(plt, outdir, 'Gen%d_95ppu_%s' % (gen_num, var))
        # close current plot in case of 'figure.max_open_warning'
        plt.cla()
        plt.clf()
        plt.close()
Example #21
0
    def __init__(self, cf):
        # Default arguments
        self.host = '127.0.0.1'  # localhost by default
        self.port = 27017
        self.bin_dir = ''
        self.model_dir = ''
        self.db_name = ''
        self.version = 'OMP'
        self.mpi_bin = None
        self.hosts_opt = None
        self.hostfile = None
        self.nprocess = 1
        self.nthread = 1
        self.lyrmtd = 1
        self.scenario_id = 0
        self.calibration_id = -1
        self.config_dict = dict()

        if 'SEIMS_Model' not in cf.sections():
            raise ValueError("[SEIMS_Model] section MUST be existed in *.ini file.")

        self.host = cf.get('SEIMS_Model', 'hostname')
        self.port = cf.getint('SEIMS_Model', 'port')
        if not StringClass.is_valid_ip_addr(self.host):
            raise ValueError('HOSTNAME defined in [SEIMS_Model] is illegal!')

        self.bin_dir = cf.get('SEIMS_Model', 'bin_dir')
        self.model_dir = cf.get('SEIMS_Model', 'model_dir')
        if not (FileClass.is_dir_exists(self.model_dir)
                and FileClass.is_dir_exists(self.bin_dir)):
            raise IOError('Please Check Directories defined in [SEIMS_Model]. '
                          'BIN_DIR and MODEL_DIR are required!')
        self.db_name = os.path.split(self.model_dir)[1]

        if cf.has_option('SEIMS_Model', 'version'):
            self.version = cf.get('SEIMS_Model', 'version')
        if cf.has_option('SEIMS_Model', 'mpi_bin'):  # full path of the executable MPI program
            self.mpi_bin = cf.get('SEIMS_Model', 'mpi_bin')
        if cf.has_option('SEIMS_Model', 'hostopt'):
            self.hosts_opt = cf.get('SEIMS_Model', 'hostopt')
        if cf.has_option('SEIMS_Model', 'hostfile'):
            self.hostfile = cf.get('SEIMS_Model', 'hostfile')
        if cf.has_option('SEIMS_Model', 'processnum'):
            self.nprocess = cf.getint('SEIMS_Model', 'processnum')
        if cf.has_option('SEIMS_Model', 'threadsnum'):
            self.nthread = cf.getint('SEIMS_Model', 'threadsnum')
        if cf.has_option('SEIMS_Model', 'layeringmethod'):
            self.lyrmtd = cf.getint('SEIMS_Model', 'layeringmethod')
        if cf.has_option('SEIMS_Model', 'scenarioid'):
            self.scenario_id = cf.getint('SEIMS_Model', 'scenarioid')
        if cf.has_option('SEIMS_Model', 'calibrationid'):
            self.calibration_id = cf.getint('SEIMS_Model', 'calibrationid')

        if not (cf.has_option('SEIMS_Model', 'sim_time_start') and
                cf.has_option('SEIMS_Model', 'sim_time_end')):
            raise ValueError("Start and end time MUST be specified in [SEIMS_Model].")

        try:
            # UTCTIME
            tstart = cf.get('SEIMS_Model', 'sim_time_start')
            tend = cf.get('SEIMS_Model', 'sim_time_end')
            self.time_start = StringClass.get_datetime(tstart)
            self.time_end = StringClass.get_datetime(tend)
        except ValueError:
            raise ValueError('The time format MUST be "YYYY-MM-DD HH:MM:SS".')
        if self.time_start >= self.time_end:
            raise ValueError("Wrong time settings in [SEIMS_Model]!")
        # Running time counted by time.time() of Python, in case of failed of GetTimespan()
        self.runtime = 0.