Source code for romtools.vector_space.utils.shifter

#
# ************************************************************************
#
#                         ROM Tools and Workflows
# Copyright 2019 National Technology & Engineering Solutions of Sandia,LLC
#                              (NTESS)
#
# Under the terms of Contract DE-NA0003525 with NTESS, the
# U.S. Government retains certain rights in this software.
#
# ROM Tools and Workflows is licensed under BSD-3-Clause terms of use:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Questions? Contact Eric Parish (ejparis@sandia.gov)
#
# ************************************************************************
#

'''
Notes
-----
The vector defining the affine offset for a linear subspace is viewed as a matrix of shape

.. math::

   \\mathbf{u}_{\\mathrm{shift}} \\in \\mathbb{R}^{N_{\\mathrm{vars}} \\times N_{\\mathrm{x}} }

Theory
------

*What is a shift vector, and why would I use it?* In ROMs, we restrict a state to belong to a low-dimensional affine
vector space,
.. math::

   \\mathbf{u} \\approx \\tilde{\\mathbf{u}} \\in \\mathcal{V} + \\mathbf{u}_{\\mathrm{shift}}

where
:math:`\\mathcal{V} \\equiv \\mathrm{range}(\\boldsymbol \\Phi)`. Here :math:`\\mathbf{u}_{\\mathrm{shift}}` defines an
affine offset.
Affine offsets can be useful for a variety of reasons, including satisfying boundary conditions, and satisfying initial
conditions.

The _Shifter class encapsulates the affine offset.

API
---
'''

import sys
from numbers import Number
from typing import Protocol
import numpy as np
import romtools.linalg.linalg as la


[docs] class Shifter(Protocol): '''Interface for the Shifter class.'''
[docs] def apply_shift(self, my_array: np.ndarray) -> None: '''Shifts the snapshot matrix by subtracting a vector generated by the public-facing free functions.''' ...
[docs] def apply_inverse_shift(self, my_array: np.ndarray) -> None: '''Shifts the snapshot matrix by adding a vector generated by the public-facing free functions.''' ...
[docs] def get_shift_vector(self) -> np.ndarray: '''Returns the vector used to shift the data.''' ...
class _Shifter(): ''' Shifts the data by a vector generated by the public-facing free functions. This class conforms to `Shifter` protocol. ''' def __init__(self, shift_vector: np.ndarray) -> None: ''' Constructor Args: shift_vector (np.ndarray): The vector to shift the data by. ''' self.__shift_vector = shift_vector.copy() def apply_shift(self, my_array: np.ndarray) -> None: '''Shifts the input array in place by subtracting the provided shift vector.''' my_array -= self.__shift_vector[..., None] def apply_inverse_shift(self, my_array: np.ndarray) -> None: '''Shifts the input array in place by adding the provided shift vector.''' my_array += self.__shift_vector[..., None] def get_shift_vector(self) -> np.ndarray: '''Returns the shift vector.''' return self.__shift_vector
[docs] def create_noop_shifter(my_array: np.ndarray) -> Shifter: '''No op implementation.''' shift_vector = np.zeros((my_array.shape[0], my_array.shape[1])) shifter = _Shifter(shift_vector) return shifter
[docs] def create_constant_shifter(shift_value, my_array: np.ndarray) -> Shifter: '''Shifts the data by a constant value.''' if isinstance(shift_value, np.ndarray): shift_vector = np.empty((my_array.shape[0], my_array.shape[1],)) assert my_array.shape[0] == shift_value.size for i in range(0, my_array.shape[0]): shift_vector[i] = shift_value[i] elif isinstance(shift_value, Number): shift_vector = np.full((my_array.shape[0], my_array.shape[1],), shift_value) else: sys.exit("Error: shift_value must be either a number or np.ndarray.") shifter = _Shifter(shift_vector) return shifter
[docs] def create_vector_shifter(shift_vector: np.ndarray) -> Shifter: '''Shifts the data by a user-input vector.''' shifter = _Shifter(shift_vector) return shifter
[docs] def create_average_shifter(my_array: np.ndarray) -> Shifter: '''Shifts the data by the average of a data matrix.''' shift_vector = la.mean(my_array, axis=2) return _Shifter(shift_vector)
[docs] def create_firstvec_shifter(my_array: np.ndarray) -> Shifter: '''Shifts the data by the first vector of a data matrix.''' shift_vector = my_array[:, :, 0] shifter = _Shifter(shift_vector) return shifter