def test_remake():
    'delete automatically remakes internal lists without None'
    oc = OrderedCollection(['p', 'q', 'ab', 'adsf', 'ss'])
    del oc[0]
    del oc[2]
    oc.remake()
    for ix, elem in enumerate(oc._elems):
        assert elem is not None
        assert oc._d_index[s_id(elem)] == ix
Beispiel #2
0
def test_remake():
    'delete automatically remakes internal lists without None'
    oc = OrderedCollection(['p', 'q', 'ab', 'adsf', 'ss'])
    del oc[0]
    del oc[2]
    oc.remake()
    for ix, elem in enumerate(oc._elems):
        assert elem is not None
        assert oc._d_index[s_id(elem)] == ix
def test_remake():
    "remakes internal lists without None enteries"
    oc = OrderedCollection(["p", "q", "ab", "adsf", "ss"])
    del oc[0]
    del oc[3]
    assert oc._elems[0] is None
    assert oc._elems[3] is None
    oc.remake()
    for ix, elem in enumerate(oc._elems):
        assert elem is not None
        assert oc._index[s_id(elem)] == ix
def test_remake_emptyoc():
    "empty OC"
    oc = OrderedCollection(dtype=int)
    oc.remake()
class SpillContainer(SpillContainerData):
    """
    Container class for all spills -- it takes care of capturing the released
    LEs from all the spills, putting them all in a single set of arrays.

    Many of the "fields" associated with a collection of elements are optional,
    or used only by some movers, so only the ones required will be requested
    by each mover.

    The data for the elements is stored in the _data_arrays dict. They can be
    accessed by indexing. For example:

    positions = spill_container['positions'] : returns a (num_LEs, 3) array of
    world_point_types
    """
    def __init__(self, uncertain=False):
        super(SpillContainer, self).__init__(uncertain=uncertain)
        self.spills = OrderedCollection(dtype=gnome.spill.Spill)
        self.rewind()

        # don't want user to add to array_types in middle of run. Since its
        # not possible to throw an error in this case, let's just make it a
        # bit difficult to do.
        # dict must be updated via prepar_for_model_run() only at beginning of
        # run. Make self._array_types an an instance variable
        self._reset_arrays()

    def __setitem__(self, data_name, array):
        """
        Invoke base class __setitem__ method so the _data_array is set
        correctly.  In addition, create the appropriate ArrayType if it wasn't
        created by the user.
        """
        super(SpillContainer, self).__setitem__(data_name, array)
        if data_name not in self._array_types:
            shape = self._data_arrays[data_name].shape[1:]
            dtype = self._data_arrays[data_name].dtype.type

            self._array_types[data_name] = array_types.ArrayType(shape, dtype)

    def _reset_arrays(self):
        'reset _array_types dict so it contains default keys/values'
        gnome.array_types.reset_to_defaults(['spill_num', 'id'])

        self._array_types = {'positions': array_types.positions,
                             'next_positions': array_types.next_positions,
                             'last_water_positions': array_types.last_water_positions,
                             'status_codes': array_types.status_codes,
                             'spill_num': array_types.spill_num,
                             'id': array_types.id,
                             #'rise_vel': array_types.rise_vel,
                             #'droplet_diameter': array_types.droplet_diameter,
                             'mass': array_types.mass,
                             'age': array_types.age}
        self._data_arrays = {}

    @property
    def array_types(self):
        """
        user can modify ArrayType initial_value in middle of run. Changing
        the shape should throw an error. Change the dtype at your own risk.
        This returns a new dict so user cannot add/delete an ArrayType in
        middle of run. Use prepare_for_model_run() to do add an ArrayType.
        """
        return dict(self._array_types)

    def rewind(self):
        """
        In the rewind operation, we:
        - rewind all the spills
        - restore _array_types to contain only defaults
          - movers/weatherers could have been deleted and we don't want to
            carry associated data_arrays
          - prepare_for_model_run() will be called before the next run and
            new arrays can be given

        - purge the data arrays
          - we gather data arrays for each contained spill
          - the stored arrays are cleared, then replaced with appropriate
            empty arrays
        """
        for spill in self.spills:
            spill.rewind()
        # create a full set of zero-sized arrays. If we rewound, something
        # must have changed so let's get back to default _array_types
        self._reset_arrays()
        self.initialize_data_arrays()

    def get_spill_mask(self, spill):
        return self['spill_num'] == self.spills.index(spill)

    def uncertain_copy(self):
        """
        Returns a copy of the spill_container suitable for uncertainty

        It has all the same spills, with the same ids, and the uncertain
        flag set to True
        """
        u_sc = SpillContainer(uncertain=True)
        for sp in self.spills:
            u_sc.spills += sp.uncertain_copy()

        return u_sc

    def prepare_for_model_run(self, array_types={}):
        """
        called when setting up the model prior to 1st time step
        This is considered 0th timestep by model

        Make current_time optional since SpillContainer doesn't require it
        especially for 0th step; however, the model needs to set it because
        it will write_output() after each step. The data_arrays along with
        the current_time_stamp must be set in order to write_output()

        :param model_start_time: model_start_time to initialize
            current_time_stamp. This is the time_stamp associated with 0-th
            step so initial conditions for data arrays
        :param array_types: a dict of additional array_types to append to
            standard array_types attribute. The data_arrays are initialized and
            appended based on the values of array_types attribute

        .. note:: The SpillContainer cycles through each of the keys in
        array_types and checks to see if there is an associated initializer
        in each Spill. If a corresponding initializer is found, it gets the
        array_types from initializer and appends them to its own list. For
        most initializers like 
        """
        # Question - should we purge any new arrays that were added in previous
        # call to prepare_for_model_run()?
        # No! If user made modifications to _array_types before running model,
        # let's keep those. A rewind will reset data_arrays.
        self._array_types.update(array_types)

        # for each array_types, use the key to get the associated initializer
        for key in array_types:
            for spill in self.spills:
                if spill.is_initializer(key):
                    self._array_types.update(
                        spill.get_initializer(key).array_types)
        self.initialize_data_arrays()

        # remake() spills ordered collection
        self.spills.remake()

    def initialize_data_arrays(self):
        """
        initialize_data_arrays() is called without input data during rewind
        and prepare_for_model_run to define all data arrays.
        At this time the arrays are empty.
        """
        for name, atype in self._array_types.iteritems():
            # Initialize data_arrays with 0 elements
            self._data_arrays[name] = atype.initialize_null()

    def _append_data_arrays(self, num_released):
        """
        initialize data arrays once spill has spawned particles
        Data arrays are set to their initial_values

        :param num_released: number of particles released
        :type num_released: int

        """
        for name, atype in self._array_types.iteritems():
            # initialize all arrays even if 0 length
            self._data_arrays[name] = np.r_[self._data_arrays[name],
                                            atype.initialize(num_released)]

    def release_elements(self, time_step, model_time):
        """
        Called at the end of a time step

        This calls release_elements on all of the contained spills, and adds
        the elements to the data arrays
        """
        for sp in self.spills:
            if not sp.on:
                continue

            num_released = sp.num_elements_to_release(model_time, time_step)

            if num_released > 0:
                # update 'spill_num' ArrayType's initial_value so it
                # corresponds with spill number for this set of released
                # particles - just another way to set value of spill_num
                # correctly
                self._array_types['spill_num'].initial_value = \
                                self.spills.index(sp)

                if len(self['spill_num']) > 0:
                    # unique identifier for each new element released
                    # this adjusts the _array_types initial_value since the
                    # initialize function just calls:
                    #  range(initial_value, num_released + initial_value)
                    self._array_types['id'].initial_value = self['id'][-1] + 1
                else:
                    # always reset the value of first particle released to 0
                    # when we have uncertain spills - this will be non-zero
                    # for uncertain spill even if no particles are released
                    # because the certian spill released particles and it gets
                    # incremented. To be safe, always reset to 0 when no
                    # particles are released
                    self._array_types['id'].initial_value = 0

                # append to data arrays
                self._append_data_arrays(num_released)
                sp.set_newparticle_values(num_released, model_time, time_step,
                                          self._data_arrays)

    def model_step_is_done(self):
        '''
        Called at the end of a time step
        Need to remove particles marked as to_be_removed...
        '''
        if len(self._data_arrays) == 0:
            return  # nothing to do - arrays are not yet defined.

        to_be_removed = np.where(self['status_codes'] ==
                                 oil_status.to_be_removed)[0]

        if len(to_be_removed) > 0:
            for key in self._array_types.keys():
                self._data_arrays[key] = np.delete(self[key], to_be_removed,
                                                   axis=0)

    def __str__(self):
        return ('gnome.spill_container.SpillContainer\n'
                'spill LE attributes: {0}'
                .format(sorted(self._data_arrays.keys())))

    __repr__ = __str__

    def spill_by_index(self, index):
        'return the spill object from ordered collection at index'
        return self.spills[index]