Skip to content
This repository has been archived by the owner on Jul 19, 2021. It is now read-only.

Commit

Permalink
Merge pull request #247 from andreabrambilla/enkf_no_circular_dep
Browse files Browse the repository at this point in the history
Remove circular dependencies from EnKFMain
  • Loading branch information
joakim-hove authored Feb 28, 2018
2 parents d7e5f62 + 1942efe commit 4f8727e
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 92 deletions.
22 changes: 17 additions & 5 deletions python/python/res/enkf/enkf_fs_manager.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import os.path
import re

from cwrap import BaseCClass
from res.enkf import EnkfFs, StateMap, TimeMap, RealizationStateEnum, EnkfInitModeEnum, EnkfPrototype
from ecl.util.util import StringList, BoolVector
from res.enkf import EnkfFs, StateMap, TimeMap, RealizationStateEnum, EnkfInitModeEnum, EnkfPrototype

import re

def naturalSortKey(s, _nsre=re.compile('([0-9]+)')):
return [int(text) if text.isdigit() else text.lower() for text in re.split(_nsre, s)]
Expand Down Expand Up @@ -97,14 +98,25 @@ def __init__(self, enkf_main, capacity=DEFAULT_CAPACITY):
@type enkf_main: res.enkf.EnKFMain
@type capacity: int
"""
super(EnkfFsManager, self).__init__(enkf_main.from_param(enkf_main).value, parent=enkf_main, is_reference=True)
# enkf_main should be an EnKFMain, get the _RealEnKFMain object
real_enkf_main = enkf_main.parent()

super(EnkfFsManager, self).__init__(
real_enkf_main.from_param(real_enkf_main).value ,
parent=real_enkf_main ,
is_reference=True)

self._fs_rotator = FileSystemRotator(capacity)
self._mount_root = enkf_main.getMountPoint()
self._mount_root = real_enkf_main.getMountPoint()

self._fs_type = enkf_main.getModelConfig().getFSType()
self._fs_type = real_enkf_main.getModelConfig().getFSType()
self._fs_arg = None

def __del__(self):
# This object is a reference, so free() won't be called on it
# Any clean-up must be done here
self.umount()
super(EnkfFsManager, self).__del__()

def _createFullCaseName(self, mount_root, case_name):
return os.path.join(mount_root, case_name)
Expand Down
191 changes: 139 additions & 52 deletions python/python/res/enkf/enkf_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,153 @@
# See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
# for more details.
import ctypes, warnings

from os.path import isfile
from cwrap import BaseCClass
from res.util import Log
from res.util.substitution_list import SubstitutionList

from cwrap import BaseCClass
from res.enkf import (AnalysisConfig, EclConfig, LocalConfig, ModelConfig,
EnsembleConfig, SiteConfig, ResConfig, QueueConfig)
from res.enkf import (EnkfPrototype, EnkfObs, EnKFState, ENKF_LIB,
EnkfSimulationRunner, EnkfFsManager)
from res.enkf import (PlotSettings, ErtWorkflowList, HookManager, HookWorkflow,
ESUpdate)
from res.enkf import (AnalysisConfig, EclConfig, LocalConfig, ModelConfig,
EnsembleConfig, SiteConfig, ResConfig, QueueConfig)
from res.enkf.enums import EnkfInitModeEnum
from res.enkf.key_manager import KeyManager
from res.util import Log
from res.util.substitution_list import SubstitutionList


class EnKFMain(BaseCClass):

TYPE_NAME = "enkf_main"

@classmethod
def createPythonObject(cls, c_pointer):
if c_pointer is not None:
real_enkf_main = _RealEnKFMain.createPythonObject(c_pointer)
new_obj = cls.__new__(cls)
EnKFMain._init_from_real_enkf_main(new_obj, real_enkf_main)
EnKFMain._monkey_patch_methods(new_obj, real_enkf_main)
return new_obj
else:
return None

@classmethod
def createCReference(cls, c_pointer, parent=None):
if c_pointer is not None:
real_enkf_main = _RealEnKFMain.createCReference(c_pointer, parent)
new_obj = cls.__new__(cls)
EnKFMain._init_from_real_enkf_main(new_obj, real_enkf_main)
EnKFMain._monkey_patch_methods(new_obj, real_enkf_main)
return new_obj
else:
return None

def __init__(self, config, strict=True, verbose=True):
""" Initializes an instance of EnkfMain.
Note: @config ought to be the ResConfig instance holding the
configuration. It also accepts that config is the name of a
configuration file, this is however deprecated.
"""

real_enkf_main = _RealEnKFMain(config, strict, verbose)
assert isinstance(real_enkf_main, BaseCClass)
self._init_from_real_enkf_main(real_enkf_main)
self._monkey_patch_methods(real_enkf_main)

def _init_from_real_enkf_main(self, real_enkf_main):
super(EnKFMain, self).__init__(
real_enkf_main.from_param(real_enkf_main).value,
parent=real_enkf_main,
is_reference=True)

self.__simulation_runner = EnkfSimulationRunner(self)
self.__fs_manager = EnkfFsManager(self)
self.__es_update = ESUpdate(self)

def _real_enkf_main(self):
return self.parent()

def getESUpdate(self):
""" @rtype: ESUpdate """
return self.__es_update

def getEnkfSimulationRunner(self):
""" @rtype: EnkfSimulationRunner """
return self.__simulation_runner

def getEnkfFsManager(self):
""" @rtype: EnkfFsManager """
return self.__fs_manager

def umount(self):
if self.__fs_manager is not None:
self.__fs_manager.umount()


# --- Overridden methods --------------------

def _monkey_patch_methods(self, real_enkf_main):
# As a general rule, EnKFMain methods should be implemented on
# _RealEnKFMain because the other references (such as __es_update)
# may need to use them.
# The public methods should be also exposed in this class, forwarding
# the call to the real method on the real_enkf_main object. That's done
# via monkey patching, so we don't need to manually keep the classes
# synchronized
from inspect import getmembers, ismethod
from functools import partial
methods = getmembers(_RealEnKFMain, predicate=ismethod)
dont_patch = [name for name, _ in getmembers(BaseCClass,
predicate=ismethod)]
for name, method in methods:
if name.startswith('_') or name in dont_patch:
continue # skip private methods
setattr(self, name, partial(method, real_enkf_main))

@staticmethod
def createNewConfig(config_file, storage_path, dbase_type, num_realizations):
return _RealEnKFMain.createNewConfig(config_file, storage_path, dbase_type, num_realizations)

def __repr__(self):
repr = self._real_enkf_main().__repr__()
assert repr.startswith('_RealEnKFMain')
return repr[5:]


class _RealEnKFMain(BaseCClass):
""" Access to the C EnKFMain interface.
The python interface of EnKFMain is split between 4 classes, ie
- EnKFMain: main entry point, defined further down
- EnkfSimulationRunner, EnkfFsManager and ESUpdate: access specific
functionalities
EnKFMain owns an instance of each of the last 3 classes. Also, all
of these classes need to access the same underlying C object.
So, in order to avoid circular dependencies, we make _RealEnKF main
the only "owner" of the C object, and all the classes that need to
access it set _RealEnKFMain as parent.
The situation can be summarized as follows (show only EnkfFSManager,
classes EnkfSimulationRunner and ESUpdate are treated analogously)
------------------------------------
| real EnKFMain object in memory |
------------------------------------
^ ^ ^
| | |
(c_ptr) (c_ptr) |
| | |
_RealEnKFMain | |
^ ^ | |
| ^--(parent)-- EnKFMain |
| | |
| (owns) |
(parent) | (c_ptr)
| v |
------------ EnkfFSManager ----
"""

_alloc = EnkfPrototype("void* enkf_main_alloc(res_config, bool, bool)", bind = False)
_create_new_config = EnkfPrototype("void enkf_main_create_new_config(char* , char*, char* , int)", bind = False)

Expand Down Expand Up @@ -77,34 +206,18 @@ class EnKFMain(BaseCClass):


def __init__(self, config, strict=True, verbose=True):
"""
Initializes an instance of EnkfMain.
Note: @config ought to be the ResConfig instance holding the
configuration. It also accepts that config is the name of a
configuration file, this is however deprecated.
"""
""" Please don't use this class directly. See EnKFMain instead """

res_config = self._init_res_config(config)
if res_config is None:
raise TypeError("Failed to construct EnKFMain instance due to invalid res_config.")

c_ptr = self._alloc(res_config, strict, verbose)
if c_ptr:
super(EnKFMain, self).__init__(c_ptr)
super(_RealEnKFMain, self).__init__(c_ptr)
else:
raise ValueError('Failed to construct EnKFMain instance from config %s.' % res_config)

if config is None:
self.__simulation_runner = None
self.__fs_manager = None
self.__es_update = None
else:
self.__simulation_runner = EnkfSimulationRunner(self)
self.__fs_manager = EnkfFsManager(self)
self.__es_update = ESUpdate(self)


self.__key_manager = KeyManager(self)


Expand Down Expand Up @@ -141,18 +254,9 @@ def _init_res_config(self, config):
def get_queue_config(self):
return self._get_queue_config()


@classmethod
def createCReference(cls, c_pointer, parent=None):
obj = super(EnKFMain, cls).createCReference(c_pointer, parent)
obj.__simulation_runner = EnkfSimulationRunner(obj)
obj.__fs_manager = EnkfFsManager(obj)
return obj


@staticmethod
def createNewConfig(config_file, storage_path, dbase_type, num_realizations):
return EnKFMain._create_new_config(config_file, storage_path, dbase_type, num_realizations)
return _RealEnKFMain._create_new_config(config_file, storage_path, dbase_type, num_realizations)

def getRealisation(self , iens):
""" @rtype: EnKFState """
Expand All @@ -161,12 +265,7 @@ def getRealisation(self , iens):
else:
raise IndexError("iens value:%d invalid Valid range: [0,%d)" % (iens , self.getEnsembleSize()))

def umount(self):
if not self.__fs_manager is None:
self.__fs_manager.umount()

def free(self):
self.umount()
self._free( )

def __repr__(self):
Expand Down Expand Up @@ -267,19 +366,6 @@ def get_observations(self, user_key, obs_count, obs_x, obs_y, obs_std):
def get_observation_count(self, user_key):
return self._get_observation_count(user_key)


def getESUpdate(self):
""" @rtype: ESUpdate """
return self.__es_update

def getEnkfSimulationRunner(self):
""" @rtype: EnkfSimulationRunner """
return self.__simulation_runner

def getEnkfFsManager(self):
""" @rtype: EnkfFsManager """
return self.__fs_manager

def getKeyManager(self):
""" :rtype: KeyManager """
return self.__key_manager
Expand Down Expand Up @@ -334,3 +420,4 @@ def getRunpathList(self):

def addNode(self, enkf_config_node):
self._add_node(enkf_config_node)

18 changes: 12 additions & 6 deletions python/python/res/enkf/enkf_simulation_runner.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from cwrap import BaseCClass
from ecl.util.util import BoolVector
from res.enkf import EnkfFs
from res.enkf import EnkfPrototype, ErtRunContext
from res.enkf.enums import EnkfInitModeEnum
from ecl.util.util import BoolVector


class EnkfSimulationRunner(BaseCClass):
Expand All @@ -13,9 +13,15 @@ class EnkfSimulationRunner(BaseCClass):

def __init__(self, enkf_main):
assert isinstance(enkf_main, BaseCClass)
super(EnkfSimulationRunner, self).__init__(enkf_main.from_param(enkf_main).value, parent=enkf_main, is_reference=True)
self.ert = enkf_main
""":type: res.enkf.EnKFMain """
# enkf_main should be an EnKFMain, get the _RealEnKFMain object
real_enkf_main = enkf_main.parent()
super(EnkfSimulationRunner, self).__init__(
real_enkf_main.from_param(real_enkf_main).value ,
parent=real_enkf_main ,
is_reference=True)

def _enkf_main(self):
return self.parent()

def runSimpleStep(self, job_queue, run_context):
""" @rtype: int """
Expand All @@ -32,5 +38,5 @@ def runEnsembleExperiment(self, job_queue, run_context):

def runWorkflows(self , runtime):
""":type res.enkf.enum.HookRuntimeEnum"""
hook_manager = self.ert.getHookManager()
hook_manager.runWorkflows( runtime , self.ert )
hook_manager = self._enkf_main().getHookManager()
hook_manager.runWorkflows(runtime , self._enkf_main())
26 changes: 15 additions & 11 deletions python/python/res/enkf/es_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,35 @@ class ESUpdate(BaseCClass):
TYPE_NAME="es_update"
_smoother_update = EnkfPrototype("bool enkf_main_smoother_update(es_update, enkf_fs, enkf_fs)")

def __init__(self , ert):
assert isinstance(ert , BaseCClass)
super(ESUpdate, self).__init__(ert.from_param(ert).value , parent=ert , is_reference=True)
self.ert = ert
self.analysis_config = self.ert.analysisConfig( )
def __init__(self , enkf_main):
assert isinstance(enkf_main , BaseCClass)

# enkf_main should be an EnKFMain, get the _RealEnKFMain object
real_enkf_main = enkf_main.parent()

super(ESUpdate, self).__init__(
real_enkf_main.from_param(real_enkf_main).value ,
parent=real_enkf_main ,
is_reference=True)

def _analysis_config(self):
return self.parent().analysisConfig()

def hasModule(self, name):
"""
Will check if we have analysis module @name.
"""
return self.analysis_config.hasModule( name )

return self._analysis_config().hasModule(name)

def getModule(self,name):
if self.hasModule( name ):
self.analysis_config.getModule( name )
self._analysis_config().getModule(name)
else:
raise KeyError("No such module:%s " % name)


def setGlobalStdScaling(self , weight):
self.analysis_config.setGlobalStdScaling( weight )


self._analysis_config().setGlobalStdScaling(weight)

def smootherUpdate( self , run_context):
data_fs = run_context.get_sim_fs( )
Expand Down
Loading

0 comments on commit 4f8727e

Please sign in to comment.