pipt.update_schemes.es

ES type schemes

  1"""
  2ES type schemes
  3"""
  4from pipt.update_schemes.enkf import enkf_approx
  5from pipt.update_schemes.enkf import enkf_full
  6from pipt.update_schemes.enkf import enkf_subspace
  7
  8import numpy as np
  9from copy import deepcopy
 10from pipt.misc_tools import analysis_tools as at
 11
 12
 13class esMixIn():
 14    """
 15    This is the straightforward ES analysis scheme. We treat this as a all-data-at-once EnKF step, hence the
 16    calc_analysis method here is identical to that in the `enkf` class. Since, for the moment, ASSIMINDEX is parsed in a
 17    specific manner (or more precise, single rows and columns in the PIPT init. file is parsed to a 1D list), a
 18    `Simultaneous` 'loop' had to be implemented, and `es` will use this to do the inversion. Maybe in the future, we can
 19    make the `enkf` class do simultaneous updating also. The consequence of all this is that we inherit BOTH `enkf` and
 20    `Simultaneous` classes, which is convenient. The `Simultaneous` class is inherited to set up the correct inversion
 21    structure and `enkf` is inherited to get `calc_analysis`, so we do not have to implement it again.
 22    """
 23
 24    def __init__(self, keys_da, keys_fwd, sim):
 25        """
 26        The class is initialized by passing the PIPT init. file upwards in the hierarchy to be read and parsed in
 27        `pipt.input_output.pipt_init.ReadInitFile`.
 28
 29        Parameters
 30        ----------
 31        init_file: str
 32            PIPT init. file containing info. to run the inversion algorithm
 33        """
 34        # Pass init. file to Simultaneous parent class (Python searches parent classes from left to right).
 35        super().__init__(keys_da, keys_fwd, sim)
 36
 37        if self.restart is False:
 38            # At the moment, the iterative loop is threated as an iterative smoother an thus we check if assim. indices
 39            # are given as in the Simultaneous loop.
 40            self.check_assimindex_simultaneous()
 41
 42            # Extract no. assimilation steps from MDA keyword in DATAASSIM part of init. file and set this equal to
 43            # the number of iterations pluss one. Need one additional because the iter=0 is the prior run.
 44            self.max_iter = 2
 45
 46    def check_convergence(self):
 47        """
 48        Calculate the "convergence" of the method. Important to
 49        """
 50        self.prev_data_misfit = self.prior_data_misfit
 51        # only calulate for the final (posterior) estimate
 52        if self.iteration == len(self.keys_da['assimindex']):
 53            assim_index = [self.keys_da['obsname'], list(
 54                np.concatenate(self.keys_da['assimindex']))]
 55            list_datatypes = self.list_datatypes
 56            obs_data_vector, pred_data = at.aug_obs_pred_data(self.obs_data, self.pred_data, assim_index,
 57                                                              list_datatypes)
 58
 59            data_misfit = at.calc_objectivefun(
 60                self.full_real_obs_data, pred_data, self.full_cov_data)
 61            self.data_misfit = np.mean(data_misfit)
 62            self.data_misfit_std = np.std(data_misfit)
 63
 64        else:  # sequential updates not finished. Misfit is not relevant
 65            self.data_misfit = self.prior_data_misfit
 66
 67        # Logical variables for conv. criteria
 68        why_stop = {'rel_data_misfit': 1 - (self.data_misfit / self.prev_data_misfit),
 69                    'data_misfit': self.data_misfit,
 70                    'prev_data_misfit': self.prev_data_misfit}
 71
 72        if self.data_misfit == self.prev_data_misfit:
 73            self.logger.info(
 74                f'ES update {self.iteration} complete!')
 75            self.current_state = deepcopy(self.state)
 76        else:
 77            if self.data_misfit < self.prior_data_misfit:
 78                self.logger.info(
 79                    f'ES update complete! Objective function decreased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
 80            else:
 81                self.logger.info(
 82                    f'ES update complete! Objective function increased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
 83        # Return conv = False, why_stop var.
 84        return False, True, why_stop
 85
 86
 87class es_approx(esMixIn, enkf_approx):
 88    """
 89    Mixin of ES class and approximate update
 90    """
 91    pass
 92
 93
 94class es_full(esMixIn, enkf_full):
 95    """
 96    mixin of ES class and full update.
 97    Note that since we do not iterate there is no difference between is full and approx.
 98    """
 99    pass
100
101
102class es_subspace(esMixIn, enkf_subspace):
103    """
104    mixin of ES class and subspace update.
105    """
106    pass
class esMixIn:
14class esMixIn():
15    """
16    This is the straightforward ES analysis scheme. We treat this as a all-data-at-once EnKF step, hence the
17    calc_analysis method here is identical to that in the `enkf` class. Since, for the moment, ASSIMINDEX is parsed in a
18    specific manner (or more precise, single rows and columns in the PIPT init. file is parsed to a 1D list), a
19    `Simultaneous` 'loop' had to be implemented, and `es` will use this to do the inversion. Maybe in the future, we can
20    make the `enkf` class do simultaneous updating also. The consequence of all this is that we inherit BOTH `enkf` and
21    `Simultaneous` classes, which is convenient. The `Simultaneous` class is inherited to set up the correct inversion
22    structure and `enkf` is inherited to get `calc_analysis`, so we do not have to implement it again.
23    """
24
25    def __init__(self, keys_da, keys_fwd, sim):
26        """
27        The class is initialized by passing the PIPT init. file upwards in the hierarchy to be read and parsed in
28        `pipt.input_output.pipt_init.ReadInitFile`.
29
30        Parameters
31        ----------
32        init_file: str
33            PIPT init. file containing info. to run the inversion algorithm
34        """
35        # Pass init. file to Simultaneous parent class (Python searches parent classes from left to right).
36        super().__init__(keys_da, keys_fwd, sim)
37
38        if self.restart is False:
39            # At the moment, the iterative loop is threated as an iterative smoother an thus we check if assim. indices
40            # are given as in the Simultaneous loop.
41            self.check_assimindex_simultaneous()
42
43            # Extract no. assimilation steps from MDA keyword in DATAASSIM part of init. file and set this equal to
44            # the number of iterations pluss one. Need one additional because the iter=0 is the prior run.
45            self.max_iter = 2
46
47    def check_convergence(self):
48        """
49        Calculate the "convergence" of the method. Important to
50        """
51        self.prev_data_misfit = self.prior_data_misfit
52        # only calulate for the final (posterior) estimate
53        if self.iteration == len(self.keys_da['assimindex']):
54            assim_index = [self.keys_da['obsname'], list(
55                np.concatenate(self.keys_da['assimindex']))]
56            list_datatypes = self.list_datatypes
57            obs_data_vector, pred_data = at.aug_obs_pred_data(self.obs_data, self.pred_data, assim_index,
58                                                              list_datatypes)
59
60            data_misfit = at.calc_objectivefun(
61                self.full_real_obs_data, pred_data, self.full_cov_data)
62            self.data_misfit = np.mean(data_misfit)
63            self.data_misfit_std = np.std(data_misfit)
64
65        else:  # sequential updates not finished. Misfit is not relevant
66            self.data_misfit = self.prior_data_misfit
67
68        # Logical variables for conv. criteria
69        why_stop = {'rel_data_misfit': 1 - (self.data_misfit / self.prev_data_misfit),
70                    'data_misfit': self.data_misfit,
71                    'prev_data_misfit': self.prev_data_misfit}
72
73        if self.data_misfit == self.prev_data_misfit:
74            self.logger.info(
75                f'ES update {self.iteration} complete!')
76            self.current_state = deepcopy(self.state)
77        else:
78            if self.data_misfit < self.prior_data_misfit:
79                self.logger.info(
80                    f'ES update complete! Objective function decreased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
81            else:
82                self.logger.info(
83                    f'ES update complete! Objective function increased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
84        # Return conv = False, why_stop var.
85        return False, True, why_stop

This is the straightforward ES analysis scheme. We treat this as a all-data-at-once EnKF step, hence the calc_analysis method here is identical to that in the enkf class. Since, for the moment, ASSIMINDEX is parsed in a specific manner (or more precise, single rows and columns in the PIPT init. file is parsed to a 1D list), a Simultaneous 'loop' had to be implemented, and es will use this to do the inversion. Maybe in the future, we can make the enkf class do simultaneous updating also. The consequence of all this is that we inherit BOTH enkf and Simultaneous classes, which is convenient. The Simultaneous class is inherited to set up the correct inversion structure and enkf is inherited to get calc_analysis, so we do not have to implement it again.

esMixIn(keys_da, keys_fwd, sim)
25    def __init__(self, keys_da, keys_fwd, sim):
26        """
27        The class is initialized by passing the PIPT init. file upwards in the hierarchy to be read and parsed in
28        `pipt.input_output.pipt_init.ReadInitFile`.
29
30        Parameters
31        ----------
32        init_file: str
33            PIPT init. file containing info. to run the inversion algorithm
34        """
35        # Pass init. file to Simultaneous parent class (Python searches parent classes from left to right).
36        super().__init__(keys_da, keys_fwd, sim)
37
38        if self.restart is False:
39            # At the moment, the iterative loop is threated as an iterative smoother an thus we check if assim. indices
40            # are given as in the Simultaneous loop.
41            self.check_assimindex_simultaneous()
42
43            # Extract no. assimilation steps from MDA keyword in DATAASSIM part of init. file and set this equal to
44            # the number of iterations pluss one. Need one additional because the iter=0 is the prior run.
45            self.max_iter = 2

The class is initialized by passing the PIPT init. file upwards in the hierarchy to be read and parsed in pipt.input_output.pipt_init.ReadInitFile.

Parameters
  • init_file (str): PIPT init. file containing info. to run the inversion algorithm
def check_convergence(self):
47    def check_convergence(self):
48        """
49        Calculate the "convergence" of the method. Important to
50        """
51        self.prev_data_misfit = self.prior_data_misfit
52        # only calulate for the final (posterior) estimate
53        if self.iteration == len(self.keys_da['assimindex']):
54            assim_index = [self.keys_da['obsname'], list(
55                np.concatenate(self.keys_da['assimindex']))]
56            list_datatypes = self.list_datatypes
57            obs_data_vector, pred_data = at.aug_obs_pred_data(self.obs_data, self.pred_data, assim_index,
58                                                              list_datatypes)
59
60            data_misfit = at.calc_objectivefun(
61                self.full_real_obs_data, pred_data, self.full_cov_data)
62            self.data_misfit = np.mean(data_misfit)
63            self.data_misfit_std = np.std(data_misfit)
64
65        else:  # sequential updates not finished. Misfit is not relevant
66            self.data_misfit = self.prior_data_misfit
67
68        # Logical variables for conv. criteria
69        why_stop = {'rel_data_misfit': 1 - (self.data_misfit / self.prev_data_misfit),
70                    'data_misfit': self.data_misfit,
71                    'prev_data_misfit': self.prev_data_misfit}
72
73        if self.data_misfit == self.prev_data_misfit:
74            self.logger.info(
75                f'ES update {self.iteration} complete!')
76            self.current_state = deepcopy(self.state)
77        else:
78            if self.data_misfit < self.prior_data_misfit:
79                self.logger.info(
80                    f'ES update complete! Objective function decreased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
81            else:
82                self.logger.info(
83                    f'ES update complete! Objective function increased from {self.prior_data_misfit:0.1f} to {self.data_misfit:0.1f}.')
84        # Return conv = False, why_stop var.
85        return False, True, why_stop

Calculate the "convergence" of the method. Important to

class es_approx(esMixIn, pipt.update_schemes.enkf.enkf_approx):
88class es_approx(esMixIn, enkf_approx):
89    """
90    Mixin of ES class and approximate update
91    """
92    pass

Mixin of ES class and approximate update

class es_full(esMixIn, pipt.update_schemes.enkf.enkf_full):
 95class es_full(esMixIn, enkf_full):
 96    """
 97    mixin of ES class and full update.
 98    Note that since we do not iterate there is no difference between is full and approx.
 99    """
100    pass

mixin of ES class and full update. Note that since we do not iterate there is no difference between is full and approx.

class es_subspace(esMixIn, pipt.update_schemes.enkf.enkf_subspace):
103class es_subspace(esMixIn, enkf_subspace):
104    """
105    mixin of ES class and subspace update.
106    """
107    pass

mixin of ES class and subspace update.