"""
Module for handling state variables.
"""
from __future__ import absolute_import
import numpy as nm
from sfepy.base.base import Struct
import six
[docs]class State(Struct):
"""
Class holding/manipulating the state variables and corresponding DOF
vectors.
Manipulating the state class changes the underlying variables, and
hence also the corresponding equations/terms (if any).
Notes
-----
This class allows working with LCBC conditions in time-dependent
problems, as it keeps track of the reduced DOF vector that cannot
be reconstructed from the full DOF vector by using the usual
`variables.strip_state_vector()`.
"""
[docs] @staticmethod
def from_variables(variables):
"""
Create a State instance for the given variables.
The DOF vector is created using the DOF data in `variables`.
Parameters
----------
variables : Variables instance
The variables.
"""
parts = variables.get_state_parts()
vec = variables.create_state_vector()
for key, part in six.iteritems(parts):
indx = variables.get_indx(key)
vec[indx] = part
return State(variables, vec)
def __init__(self, variables, vec=None, preserve_caches=False):
"""
Create a State instance for the given variables.
Parameters
----------
variables : Variables instance
The variables.
vec : array, optional
The (initial) DOF vector corresponding to the variables.
preserve_caches : bool
If True, do not invalidate evaluate caches of variables.
"""
Struct.__init__(self, variables=variables, vec=vec, r_vec=None)
if self.vec is None:
self.vec = variables.create_state_vector()
self.variables.set_data(self.vec, preserve_caches=preserve_caches)
[docs] def copy(self, deep=False, preserve_caches=False):
"""
Copy the state. By default, the new state contains the same
variables, and creates new DOF vectors. If `deep` is True, also
the DOF vectors are copied.
Parameters
----------
deep : bool
If True, make a copy of the DOF vectors.
preserve_caches : bool
If True, do not invalidate evaluate caches of variables.
"""
if deep:
other = State(self.variables, self.vec.copy(), preserve_caches=True)
if self.r_vec is not None:
other.r_vec = self.r_vec.copy()
else:
other = State(self.variables, preserve_caches=True)
return other
[docs] def fill(self, value):
"""
Fill the DOF vector with given value.
"""
if self.r_vec is not None:
self.r_vec.fill(value)
self.vec.fill(value)
[docs] def init_history(self):
"""
Initialize variables with history.
"""
self.variables.init_history()
[docs] def apply_ebc(self, force_values=None):
"""
Apply essential (Dirichlet) boundary conditions to the state.
"""
self.variables.apply_ebc(self.vec, force_values=force_values)
[docs] def has_ebc(self):
"""
Test whether the essential (Dirichlet) boundary conditions have
been applied to the DOF vector.
"""
return self.variables.has_ebc(self.vec)
[docs] def apply_ic(self, force_values=None):
"""
Apply initial conditions to the state.
"""
if self.r_vec is not None:
raise ValueError('cannot re-apply initial conditions with LCBCs!')
self.variables.apply_ic(self.vec, force_values=force_values)
[docs] def get_reduced(self, follow_epbc=False):
"""
Get the reduced DOF vector, with EBC and PBC DOFs removed.
"""
strip = self.variables.strip_state_vector
if self.variables.has_lcbc:
if self.r_vec is None:
r_vec = strip(self.vec, follow_epbc=follow_epbc)
# This just sets the correct vector size (wrong values)!
r_vec = self.variables.mtx_lcbc.T * r_vec
else:
r_vec = self.r_vec
else:
r_vec = strip(self.vec, follow_epbc=follow_epbc)
return r_vec
[docs] def set_reduced(self, r_vec, preserve_caches=False):
"""
Set the reduced DOF vector, with EBC and PBC DOFs removed.
Parameters
----------
r_vec : array
The reduced DOF vector corresponding to the variables.
preserve_caches : bool
If True, do not invalidate evaluate caches of variables.
"""
self.vec = self.variables.make_full_vec(r_vec)
if self.variables.has_lcbc:
self.r_vec = r_vec
self.variables.set_data(self.vec, preserve_caches=preserve_caches)
[docs] def set_full(self, vec, var_name=None, force=False):
"""
Set the full DOF vector (including EBC and PBC DOFs). If
`var_name` is given, set only the DOF sub-vector corresponding
to the given variable. If `force` is True, setting variables
with LCBC DOFs is allowed.
"""
if var_name is None:
if self.variables.has_lcbc and not force:
raise ValueError('cannot set full DOF vector with LCBCs!')
self.vec = vec
self.variables.set_data(self.vec)
else:
var = self.variables[var_name]
if var.has_lcbc and not force:
raise ValueError('cannot set full DOF vector with LCBCs!')
self.variables.set_state_part(self.vec, vec, var_name)
var.set_data(self.vec, self.variables.get_indx(var_name))
def __call__(self, var_name=None):
"""
Get the full DOF vector (including EBC and PBC DOFs). If
`var_name` is given, return only the DOF vector corresponding to
the given variable.
"""
if var_name is None:
out = self.vec
else:
out = self.variables.get_state_part_view(self.vec, var_name)
return out
[docs] def set_parts(self, parts, force=False):
"""
Set parts of the DOF vector corresponding to individual state
variables.
Parameters
----------
parts : dict
The dictionary of the DOF vector parts.
"""
if self.variables.has_lcbc and not force:
raise ValueError('cannot set full DOF vector with LCBCs!')
self.variables.set_data(parts)
for key, part in six.iteritems(parts):
indx = self.variables.get_indx(key)
self.vec[indx] = part
[docs] def get_parts(self):
"""
Return parts of the DOF vector corresponding to individual state
variables.
Returns
-------
out : dict
The dictionary of the DOF vector parts.
"""
return self.variables.get_state_parts(self.vec)
[docs] def get_vec(self, active_only):
if active_only:
vec = self.get_reduced()
else:
vec = self()
return vec
[docs] def set_vec(self, vec, active_only):
if active_only:
self.set_reduced(vec, preserve_caches=True)
else:
self.set_full(vec)
[docs] def create_output_dict(self, fill_value=None, var_info=None,
extend=True, linearization=None):
"""
Transforms state to an output dictionary, that can be
passed as 'out' kwarg to Mesh.write().
Then the dictionary entries are formed by components of the
state vector corresponding to unknown variables according to
kind of linearization given by `linearization`.
Examples
--------
>>> out = state.create_output_dict()
>>> problem.save_state('file.vtk', out=out)
"""
return self.variables.state_to_output(self.vec, fill_value,
var_info, extend,
linearization=linearization)
[docs] def get_weighted_norm(self, vec, weights=None, return_weights=False):
"""
Return the weighted norm of DOF vector `vec`.
By default, each component of `vec` is weighted by the 1/norm of the
corresponding state part, or 1 if the norm is zero. Alternatively, the
weights can be provided explicitly using `weights` argument.
Parameters
----------
vec : array
The DOF vector corresponding to the variables.
weights : dict, optional
If given, the weights are used instead of the norms of the state
parts. Keys of the dictionary must be equal to the names of
variables comprising the DOF vector.
return_weights: bool
If True, return also the used weights.
Returns
-------
norm : float
The weighted norm.
weights : dict, optional
If `return_weights` is True, the used weights.
Examples
--------
>>> err = state0.get_weighted_norm(state() - state0())
"""
if weights is None:
parts = self.get_parts()
weights = {}
for key, part in six.iteritems(parts):
pnorm = nm.linalg.norm(part)
if pnorm < 10.0 * nm.finfo(nm.float64).eps:
pnorm = 1.0
weights[key] = 1.0 / pnorm
else:
if set(weights.keys()) != self.variables.state:
raise ValueError('weights keys have to be in %s!'
% self.variables.state)
wvec = vec.copy()
for key in six.iterkeys(weights):
indx = self.variables.get_indx(key)
wvec[indx] *= weights[key]
norm = nm.linalg.norm(wvec)
if return_weights:
return norm, weights
else:
return norm