Skip to content

Commit 54a64e4

Browse files
mlienorceMartha Økland LienrolfjlRolf Johan Lorentzenkjei
authored
Merge many Ramonco developments (#109)
* GIES and AVO Smeaheia in progress (#72) Co-authored-by: Martha Økland Lien <[email protected]> Co-authored-by: Rolf J. Lorentzen <[email protected]> * Ramon co pet (#73) * GIES and AVO Smeaheia in progress * avo updted, grav included, method for joint grav and avo. calc_pem method in flow_rock that is read by flow_avo * softsand.py represents soft sand rock physics model, added 4D gravity and 4D avo * give overburden velocity values to inactive cells * minor edits --------- Co-authored-by: Martha Økland Lien <[email protected]> Co-authored-by: Rolf J. Lorentzen <[email protected]> * Rename keys variable * Ramon co pet (#74) * GIES and AVO Smeaheia in progress * avo updted, grav included, method for joint grav and avo. calc_pem method in flow_rock that is read by flow_avo * softsand.py represents soft sand rock physics model, added 4D gravity and 4D avo * give overburden velocity values to inactive cells * minor edits * Compute reflection coefficients for active cells --------- Co-authored-by: Martha Økland Lien <[email protected]> Co-authored-by: Rolf J. Lorentzen <[email protected]> * Bug fix of avo and gravity (#77) * Bug fix of avo and gravity * Bug fix * Add functionality for updating pressure and saturations directly (#80) * invert for dynamic variables first iteration (#94) * NE in pipt file is now used instead of the whole loaded ensemble if this number is smaller+bug fix (#95) * subsidence * subsidence (#96) * stash issue resolve * Subsidence (#97) * subsidence * stash issue resolve * Compression also for 2D data (#107) * Smeaheia update and GIES modifications * More updates after merge of Ramonco branch --------- Co-authored-by: Martha Økland Lien <[email protected]> Co-authored-by: Rolf J. Lorentzen <[email protected]> Co-authored-by: Rolf Johan Lorentzen <[email protected]> Co-authored-by: Kjersti <[email protected]>
1 parent d1c2e52 commit 54a64e4

File tree

15 files changed

+4052
-316
lines changed

15 files changed

+4052
-316
lines changed

ensemble/ensemble.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from tqdm.auto import tqdm
1616
from p_tqdm import p_map
1717
import logging
18+
from geostat.decomp import Cholesky # Making realizations
1819

1920
# Internal imports
2021
import pipt.misc_tools.analysis_tools as at
@@ -26,7 +27,6 @@
2627
from misc.system_tools.environ_var import OpenBlasSingleThread # Single threaded OpenBLAS runs
2728

2829

29-
3030
class Ensemble:
3131
"""
3232
Class for organizing misc. variables and simulator for an ensemble-based inversion run. Here, the forecast step
@@ -139,15 +139,24 @@ def __init__(self, keys_en, sim, redund_sim=None):
139139
# individually).
140140
self.state = {key: val for key, val in tmp_load.items()}
141141

142-
# Find the number of ensemble members from state variable
142+
# Find the number of ensemble members from loaded state variables
143143
tmp_ne = []
144144
for tmp_state in self.state.keys():
145145
tmp_ne.extend([self.state[tmp_state].shape[1]])
146-
if max(tmp_ne) != min(tmp_ne):
147-
print('\033[1;33mInput states have different ensemble size\033[1;m')
148-
sys.exit(1)
149-
self.ne = min(tmp_ne)
150-
146+
147+
if 'ne' not in self.keys_en: # NE not specified in input file
148+
if max(tmp_ne) != min(tmp_ne): #Check loaded ensembles are the same size (if more than one state variable)
149+
print('\033[1;33mInput states have different ensemble size\033[1;m')
150+
sys.exit(1)
151+
self.ne = min(tmp_ne) # Use the number of ensemble members in loaded ensemble
152+
else:
153+
# Use the number of ensemble members specified in input file (may be fewer than loaded)
154+
self.ne = int(self.keys_en['ne'])
155+
if self.ne <= min(tmp_ne):
156+
# pick correct number of ensemble members
157+
self.state = {key: val[:,:self.ne] for key, val in self.state.items()}
158+
else:
159+
print('\033[1;33mInput states are smaller than NE\033[1;m')
151160
if 'multilevel' in self.keys_en:
152161
ml_info = extract.extract_multilevel_info(self.keys_en)
153162
self.multilevel, self.tot_level, self.ml_ne, self.ML_error_corr, self.error_comp_scheme, self.ML_corr_done = ml_info
@@ -338,6 +347,20 @@ def calc_prediction(self, input_state=None, save_prediction=None):
338347
# Index list of ensemble members
339348
list_member_index = list(range(self.ne))
340349

350+
# modified by xluo, for including the simulation of the mean reservoir model
351+
# as used in the RLM-MAC algorithm
352+
if 'daalg' in self.keys_en and self.keys_en['daalg'][1] == 'gies':
353+
list_state.append({})
354+
list_member_index.append(self.ne)
355+
356+
for key in self.state.keys():
357+
tmp_state = np.zeros(list_state[0][key].shape[0])
358+
359+
for i in range(self.ne):
360+
tmp_state += list_state[i][key]
361+
362+
list_state[self.ne][key] = tmp_state / self.ne
363+
341364
if no_tot_run==1: # if not in parallel we use regular loop
342365
en_pred = [self.sim.run_fwd_sim(state, member_index) for state, member_index in
343366
tqdm(zip(list_state, list_member_index), total=len(list_state))]
@@ -392,6 +415,7 @@ def calc_prediction(self, input_state=None, save_prediction=None):
392415
else: # Run prediction in parallel using p_map
393416
en_pred = p_map(self.sim.run_fwd_sim, list_state,
394417
list_member_index, num_cpus=no_tot_run, disable=self.disable_tqdm)
418+
395419
# List successful runs and crashes
396420
list_crash = [indx for indx, el in enumerate(en_pred) if el is False]
397421
list_success = [indx for indx, el in enumerate(en_pred) if el is not False]

pipt/loop/assimilation.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,12 @@ def post_process_forecast(self):
529529
for k in pred_data_tmp[i]: # DATATYPE
530530
if vintage < len(self.ensemble.sparse_info['mask']) and \
531531
len(pred_data_tmp[i][k]) == int(np.sum(self.ensemble.sparse_info['mask'][vintage])):
532-
self.ensemble.pred_data[i][k] = np.zeros(
533-
(len(self.ensemble.obs_data[i][k]), self.ensemble.ne))
532+
if self.ensemble.keys_da['daalg'][1] == 'gies':
533+
self.ensemble.pred_data[i][k] = np.zeros(
534+
(len(self.ensemble.obs_data[i][k]), self.ensemble.ne+1))
535+
else:
536+
self.ensemble.pred_data[i][k] = np.zeros(
537+
(len(self.ensemble.obs_data[i][k]), self.ensemble.ne))
534538
for m in range(pred_data_tmp[i][k].shape[1]):
535539
data_array = self.ensemble.compress(pred_data_tmp[i][k][:, m], vintage,
536540
self.ensemble.sparse_info['use_ensemble'])

pipt/loop/ensemble.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,24 @@ def _ext_scaling(self):
540540
self.state_scaling = at.calc_scaling(
541541
self.prior_state, self.list_states, self.prior_info)
542542

543-
self.Am = None
543+
delta_scaled_prior = self.state_scaling[:, None] * \
544+
np.dot(at.aug_state(self.prior_state, self.list_states), self.proj)
545+
546+
u_d, s_d, v_d = np.linalg.svd(delta_scaled_prior, full_matrices=False)
547+
548+
# remove the last singular value/vector. This is because numpy returns all ne values, while the last is actually
549+
# zero. This part is a good place to include eventual additional truncation.
550+
energy = 0
551+
trunc_index = len(s_d) - 1 # inititallize
552+
for c, elem in enumerate(s_d):
553+
energy += elem
554+
if energy / sum(s_d) >= self.trunc_energy:
555+
trunc_index = c # take the index where all energy is preserved
556+
break
557+
u_d, s_d, v_d = u_d[:, :trunc_index +
558+
1], s_d[:trunc_index + 1], v_d[:trunc_index + 1, :]
559+
self.Am = np.dot(u_d, np.eye(trunc_index+1) *
560+
((s_d**(-1))[:, None])) # notation from paper
544561

545562
def save_temp_state_assim(self, ind_save):
546563
"""
@@ -694,7 +711,7 @@ def compress(self, data=None, vintage=0, aug_coeff=None):
694711

695712
data_array = None
696713

697-
elif aug_coeff is None:
714+
elif aug_coeff is None: # compress predicted data
698715

699716
data_array, wdec_rec = self.sparse_data[vintage].compress(data)
700717
rec = self.sparse_data[vintage].reconstruct(
@@ -703,7 +720,7 @@ def compress(self, data=None, vintage=0, aug_coeff=None):
703720
self.data_rec.append([])
704721
self.data_rec[vintage].append(rec)
705722

706-
elif not aug_coeff:
723+
elif not aug_coeff: # compress true data, aug_coeff = false
707724

708725
options = copy(self.sparse_info)
709726
# find the correct mask for the vintage

pipt/misc_tools/analysis_tools.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,9 @@ def calc_objectivefun(pert_obs, pred_data, Cd):
542542
data_misfit : array-like
543543
Nex1 array containing objective function values.
544544
"""
545-
ne = pred_data.shape[1]
546-
r = (pred_data - pert_obs)
545+
#ne = pred_data.shape[1]
546+
ne = pert_obs.shape[1]
547+
r = (pred_data[:, :ne] - pert_obs) # This is necessary to use to gies code that xilu has implemented
547548
if len(Cd.shape) == 1:
548549
precission = Cd**(-1)
549550
data_misfit = np.diag(r.T.dot(r*precission[:, None]))

pipt/misc_tools/extract_tools.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,12 @@ def organize_sparse_representation(info: Union[dict,list]) -> dict:
271271
sparse['dim'] = [dim[2], dim[1], dim[0]]
272272

273273
# Read mask_files
274-
sparse['mask'] = []
275-
for idx, filename in enumerate(info['mask'], start=1):
274+
sparse['mask'] = []
275+
m_info = info['mask']
276+
# allow for one mask with filename given as string
277+
if isinstance(m_info, str):
278+
m_info = [m_info]
279+
for idx, filename in enumerate(m_info, start=1):
276280
if not os.path.exists(filename):
277281
mask = np.ones(sparse['dim'], dtype=bool)
278282
np.savez(f'mask_{idx}.npz', mask=mask)

0 commit comments

Comments
 (0)